Version 2 of 3 · · razie via designer-86designer · Current version

Open the page

---
description: Start your agents as a signed base d2 Page (P-1216): tick the roles, one call makes a fresh one-time onboarding link per role (1 hour), each agent claims its own token; a live dot per role
tags: page, onboarding
---
<!doctype html>
<html lang="en"><head><meta charset="utf-8"><meta name="viewport" content="width=device-width, initial-scale=1, viewport-fit=cover"><meta name="color-scheme" content="light dark"><title>Start your agents</title>
<style>
:root{--paper:#fbfaf7;--card:#fff;--ink:#1d1d1b;--soft:#6b6a66;--line:#e4e1da;--rose:#b23a48;--ok:#3c9a5f;--lv:#6c8cff;--display:'Manrope',system-ui,-apple-system,'Segoe UI',sans-serif;--body:system-ui,-apple-system,'Segoe UI',sans-serif}
@media (prefers-color-scheme:dark){:root{--paper:#141413;--card:#1d1d1b;--ink:#ecebe6;--soft:#9c9a93;--line:#33322f;--rose:#f08a92;--ok:#5cc282}}
*{box-sizing:border-box}html,body{background:var(--paper);color:var(--ink);margin:0}body{font:calc(15px*var(--fz,1))/1.5 var(--body)}
.sp-wrap{max-width:900px;margin:0 auto;padding:6px clamp(12px,3vw,28px) 56px}
h1{font:800 calc(clamp(26px,4vw,34px)*var(--fz,1))/1.1 var(--display);letter-spacing:-.02em;margin:12px 0}
h2{font:800 22px/1.2 var(--display);margin:28px 0 8px}a{color:var(--lv)}.small{font-size:13px;color:var(--soft)}
.sp-opts{display:grid;gap:10px;margin:12px 0 16px;max-width:620px}
.sp-opt{display:flex;gap:10px;align-items:flex-start;cursor:pointer;background:color-mix(in srgb,var(--lv) 9%,var(--card));border:1px solid color-mix(in srgb,var(--lv) 35%,var(--line));border-radius:12px;padding:12px 14px}
.sp-opt input{width:20px;height:20px;margin:2px 0 0;accent-color:var(--lv);flex:none}.sp-opt b{display:block}.sp-opt span{color:var(--soft);font-size:14px}
.sp-go{border:0;cursor:pointer;padding:14px 30px;border-radius:999px;background:var(--lv);color:#111;font:800 1.3em/1.2 var(--body);box-shadow:0 2px 8px rgba(0,0,0,.18)}.sp-go:disabled{opacity:.6;cursor:default}
.sp-say{display:block;margin-top:8px;color:var(--rose);font-size:13px;min-height:1em}
.cards{display:grid;gap:12px;max-width:720px}
.card{background:var(--card);border:1px solid var(--line);border-left:4px solid var(--lv);border-radius:12px;padding:12px 14px}
.card h3{margin:0 0 6px;font:800 17px/1.2 var(--display);display:flex;align-items:center;gap:8px}
.dot{width:10px;height:10px;border-radius:50%;background:var(--line);flex:none}.dot.on{background:var(--ok);box-shadow:0 0 0 3px color-mix(in srgb,var(--ok) 25%,transparent)}
.st{font:400 13px var(--body);color:var(--soft)}
pre{white-space:pre-wrap;word-break:break-all;background:color-mix(in srgb,var(--lv) 7%,var(--paper));border:1px solid var(--line);border-radius:8px;padding:8px 10px;margin:6px 0;font:12.5px/1.4 ui-monospace,Menlo,monospace}
.btn{border:1px solid var(--line);background:var(--card);color:var(--ink);border-radius:999px;padding:6px 14px;cursor:pointer;font:600 13px var(--body);margin-right:6px}
</style>
</head><body><div class="sp-wrap">
<h1>Start your agents</h1>
<p>Each agent is an AI working in one <b>role</b> on your project, in the background. Tick the roles, and you get one fresh link per role. Paste a link into <b>Claude Code</b> (or any AI chat that can reach the web): the agent fetches its start prompt from it, gets its own token, and shows up here.</p>
<div class="sp-opts" id="roles" role="group" aria-label="Roles"></div>
<p><button class="sp-go" id="sp-make" type="button">Create my agents →</button> <span class="sp-say" id="sp-say" role="status"></span></p>
<section id="out" hidden>
<h2>Your agents</h2>
<p class="small">Each link works once, for an hour. No token is ever shown: the agent claims its own. Lost one, or it expired? <b>New link</b>.</p>
<div class="cards" id="cards"></div>
</section>
<p style="margin-top:28px"><button class="sp-go" id="sp-done" type="button">Done →</button></p>
<p class="small">More coders later? They all share one builder token: your dispatcher starts them. Tokens and links: <a href="/ai/tokens" data-go>AI tokens</a>. Your agents: <a href="/agents" data-go>Agents</a>.</p>
</div>
<script>
(()=>{const D2=window.d2||{},$=s=>document.querySelector(s);
const P=Object.assign({},Object.fromEntries(new URLSearchParams(location.search)),D2.params||window.d2params||{});
const LEVEL=P.level==="master"?"master":"pro";
const api=(m,u,b)=>D2.api?Promise.resolve(D2.api(m,u,b)):Promise.reject(new Error("E_BRIDGE: open this page in d2"));
const go=u=>D2.go?D2.go(u):(location.href=u);
const esc=s=>String(s==null?"":s).replace(/[&<>"]/g,c=>({"&":"&amp;","<":"&lt;",">":"&gt;",'"':"&quot;"}[c]));
const msg=e=>String(e&&e.message||e).replace(/^E_[A-Z_]+: /,"");
document.addEventListener("click",e=>{const a=e.target.closest&&e.target.closest("a[data-go]");if(!a)return;e.preventDefault();go(a.getAttribute("href"))});
if(LEVEL==="master")document.documentElement.style.setProperty("--lv","#b07cf0");
// the roles per level (razie 2026-10-10: one builder token shared by every coder; the dispatcher reads designer and builder from the vault)
const ROLES={designer:["Designer","Turns your thoughts into the spec, design, mockups and tasks."],
  builder:["Coder","Builds the tasks from the pipeline. Every coder shares this one."],
  dispatcher:["Dispatcher","Runs the coders, batch after batch, and restarts your designer and coders when needed."],
  tester:["Tester","Checks every release."]};
const PICK=LEVEL==="master"?{designer:1,dispatcher:1,builder:1,tester:1}:{designer:1,builder:P.coder?1:0};
$("#roles").innerHTML=Object.keys(PICK).map(r=>`<label class="sp-opt"><input type="checkbox" value="${r}"${PICK[r]?" checked":""}><div><b>${ROLES[r][0]}</b><span>${ROLES[r][1]}</span></div></label>`).join("");
const links={};let since=new Date().toISOString();
const line=l=>`You're the ${ROLES[l.role][0].toLowerCase()} on this d2 project. Start here: ${l.link}`;
const draw=()=>{$("#out").hidden=!Object.keys(links).length;$("#cards").innerHTML=Object.values(links).map(l=>`<div class="card" data-role="${esc(l.role)}">
<h3><span class="dot${l.up?" on":""}"></span>${esc(ROLES[l.role][0])} <span class="st">${l.up?esc(l.up):"waiting for it to connect"}</span></h3>
<pre>${esc(line(l))}</pre>
<button class="btn" data-copy="${esc(l.role)}">Copy</button><button class="btn" data-new="${esc(l.role)}">New link</button>
<span class="small">works once, until ${esc(new Date(l.expires).toLocaleTimeString([], {hour:"2-digit",minute:"2-digit"}))}</span></div>`).join("")};
// one call for every ticked role: POST /api/v2/onboard/roles (P-1216; signed d2 pages and the session only)
const make=async roles=>{const r=await api("POST","/api/v2/onboard/roles",{roles});(r&&r.links||[]).forEach(l=>links[l.role]=Object.assign(links[l.role]||{},l,{up:""}));draw()};
$("#sp-make").onclick=async()=>{const b=$("#sp-make"),say=$("#sp-say"),roles=[...document.querySelectorAll("#roles input:checked")].map(i=>i.value);
  say.textContent="";if(!roles.length){say.textContent="Tick at least one role.";return}
  b.disabled=true;since=new Date().toISOString();
  try{await make(roles);b.textContent="Make the links again →"}
  catch(e){say.textContent="Couldn't make the links: "+msg(e)}
  b.disabled=false};
$("#cards").addEventListener("click",async e=>{const c=e.target.dataset.copy,n=e.target.dataset.new;
  if(c){try{await navigator.clipboard.writeText(line(links[c]));e.target.textContent="Copied"}catch(_){e.target.textContent="Select and copy"}}
  if(n){e.target.disabled=true;try{await make([n])}catch(x){$("#sp-say").textContent=msg(x)}}});
// the live dot: an agent of that role posted a status since the links were made
const poll=async()=>{if(!Object.keys(links).length)return;try{const a=await api("GET","/api/v2/agents");const rows=(a&&(a.data||a.agents))||[];
  let ch=0;rows.forEach(s=>{const r=s.role==="coder"?"builder":s.role;const l=links[r];if(l&&!l.up&&s.at>=since&&s.state!=="gone"){l.up=(s.agent||"")+" · "+s.state;ch=1}});if(ch)draw()}catch(_){}};
setInterval(poll,10000);
// Done: back to Setup, to its last step (members, then the dashboard)
$("#sp-done").onclick=()=>go("/page/Setup"+(LEVEL==="master"?"Master":"Pro")+"#members");
})();
</script>
</body></html>