bostrat-node 0.2.0 → 0.2.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cli.js +41 -39
- package/dist/index.js +112 -97
- package/fix-pty.js +22 -0
- package/package.json +4 -1
package/dist/index.js
CHANGED
|
@@ -1,86 +1,88 @@
|
|
|
1
|
-
var
|
|
1
|
+
var Gh=Object.defineProperty;var Yc=(t,e)=>{for(var n in e)Gh(t,n,{get:e[n],enumerable:!0})};import ct from"node:path";import It from"node:fs";import aA from"node:readline";import{fileURLToPath as iA}from"node:url";import de from"express";import cA from"node:http";import lA from"node:https";import zc from"node:os";import{WebSocketServer as uA}from"ws";import{execFile as ew}from"node:child_process";import{promisify as nw}from"node:util";import Ps from"node:fs";import{randomUUID as rw}from"node:crypto";import gn from"node:fs/promises";import Zc from"node:os";import Le from"node:path";function Qr(){return/^(1|true|yes|on)$/i.test(process.env.BOSTRAT_CLAUDE_USE_API_KEY||"")&&process.env.ANTHROPIC_API_KEY?.trim()||null}function Kh(){return process.env.CLAUDE_CONFIG_DIR?Le.join(process.env.CLAUDE_CONFIG_DIR,".claude.json"):Le.join(Zc.homedir(),".claude.json")}function Vh(){return process.env.CLAUDE_CONFIG_DIR?Le.join(process.env.CLAUDE_CONFIG_DIR,"settings.json"):Le.join(Zc.homedir(),".claude","settings.json")}async function fe(t){if(!t||typeof t!="string")return;let e=Le.resolve(t),n=Kh(),r=Vh(),o={};try{o=JSON.parse(await gn.readFile(n,"utf8"))}catch{}o.hasCompletedOnboarding=!0,o.theme||(o.theme="dark"),o.projects=o.projects||{},o.projects[e]=o.projects[e]||{};let s=o.projects[e];Array.isArray(s.allowedTools)||(s.allowedTools=[]),s.hasTrustDialogAccepted=!0,s.hasCompletedProjectOnboarding=!0;let a=process.env.ANTHROPIC_API_KEY;if(a){let c=a.slice(-20);o.customApiKeyResponses=o.customApiKeyResponses||{},o.customApiKeyResponses.approved=o.customApiKeyResponses.approved||[],o.customApiKeyResponses.rejected=o.customApiKeyResponses.rejected||[],o.customApiKeyResponses.approved.includes(c)||o.customApiKeyResponses.approved.push(c)}await gn.mkdir(Le.dirname(n),{recursive:!0}),await gn.writeFile(n,JSON.stringify(o,null,2));let i={};try{i=JSON.parse(await gn.readFile(r,"utf8"))}catch{}if(i.skipDangerousModePermissionPrompt=!0,process.env.BOSTRAT_HOOKS!=="off"){let l=`curl -sS -m 3 -X POST ${process.env.BOSTRAT_HOOK_URL||`http://127.0.0.1:${process.env.PORT||3362}/api/hook`} -H 'content-type: application/json' --data-binary @- >/dev/null 2>&1 || true`,u=[{hooks:[{type:"command",command:l}]}];i.hooks=i.hooks||{};for(let d of["SessionStart","UserPromptSubmit","Stop"])i.hooks[d]=u;i.hooks.PostToolUse=[{matcher:"*",hooks:[{type:"command",command:l}]}]}await gn.mkdir(Le.dirname(r),{recursive:!0}),await gn.writeFile(r,JSON.stringify(i,null,2))}import Cs from"node:fs/promises";import Jh from"node:os";import to from"node:path";function zh(){let t=process.env.CODEX_HOME||to.join(Jh.homedir(),".codex");return to.join(t,"config.toml")}function Qc(t){return`[projects."${t}"]`}function Xh(t,e){let n=Qc(e),r=t.indexOf(n);if(r===-1)return!1;let o=t.slice(r+n.length),s=o.search(/^\s*\[/m),a=s===-1?o:o.slice(0,s);return/trust_level\s*=\s*"trusted"/.test(a)}async function yn(t){if(!t||typeof t!="string")return;let e=to.resolve(t),n=zh(),r="";try{r=await Cs.readFile(n,"utf8")}catch{}if(Xh(r,e))return;let o=Qc(e);if(r.includes(o)){let a=r.indexOf(o)+o.length,c=r.slice(a).search(/^\s*\[/m),l=c===-1?r.length:a+c,u=r.slice(a,l),d=/trust_level\s*=/.test(u)?u.replace(/trust_level\s*=\s*"[^"]*"/,'trust_level = "trusted"'):`${u.replace(/\s*$/,"")}
|
|
2
2
|
trust_level = "trusted"
|
|
3
3
|
`;r=r.slice(0,a)+d+r.slice(l)}else{let s=`
|
|
4
4
|
${o}
|
|
5
5
|
trust_level = "trusted"
|
|
6
6
|
`;r=r.trimEnd()+s+(r.length&&!r.endsWith(`
|
|
7
7
|
`)?`
|
|
8
|
-
`:"")}try{await
|
|
9
|
-
`,"utf8")}import
|
|
10
|
-
`,"utf8");let n=(await
|
|
11
|
-
`).filter(Boolean);n.length>
|
|
8
|
+
`:"")}try{await Cs.mkdir(to.dirname(n),{recursive:!0}),await Cs.writeFile(n,r)}catch(s){console.warn(`[codex-config] could not seed folder trust for ${e}: ${s?.message||s}`)}}import tl from"node:fs/promises";import Yh from"node:os";import or from"node:path";function Zh(t){let n=or.resolve(t).slice(1).replace(/\//g,"-").replace(/\./g,""),r=process.env.CURSOR_CONFIG_DIR||or.join(Yh.homedir(),".cursor");return or.join(r,"projects",n)}async function bn(t){if(!t||typeof t!="string")return;let e=or.resolve(t),n=Zh(e),r=or.join(n,".workspace-trusted");await tl.mkdir(n,{recursive:!0}),await tl.writeFile(r,`${JSON.stringify({trustedAt:new Date().toISOString(),workspacePath:e},null,2)}
|
|
9
|
+
`,"utf8")}import sr from"node:fs/promises";import Qh from"node:os";import nl from"node:path";var Ms=process.env.BOSTRAT_DEBUG_DIR||nl.join(Qh.homedir(),".bostrat","debug"),kn=nl.join(Ms,"handoff.jsonl"),el=Number(process.env.BOSTRAT_HANDOFF_DEBUG_MAX)||500;function eo(){let t=process.env.BOSTRAT_HANDOFF_DEBUG;return t==="1"||t==="true"}async function tw(t){try{await sr.mkdir(Ms,{recursive:!0}),await sr.appendFile(kn,`${JSON.stringify(t)}
|
|
10
|
+
`,"utf8");let n=(await sr.readFile(kn,"utf8")).split(`
|
|
11
|
+
`).filter(Boolean);n.length>el&&await sr.writeFile(kn,`${n.slice(-el).join(`
|
|
12
12
|
`)}
|
|
13
|
-
`,"utf8")}catch(e){console.warn(`[handoff-debug] failed to write ${
|
|
14
|
-
`).filter(Boolean).slice(-t).map(r=>{try{return JSON.parse(r)}catch{return{raw:r}}})}catch{return[]}}var
|
|
15
|
-
`).filter(Boolean).map(r=>{let[o,s,a,i,c,l,u,d,f]=r.split("|"),w=Number(i);return{name:o,title:u||null,windows:Number(s),attached:Number(a),lastActivityMs:Number.isFinite(w)?n-w*1e3:null,cwd:c||null,isBostrat:l==="1",sessionId:d||null,agent:f||(l==="1"?"claude":null)}}).filter(r=>!
|
|
16
|
-
`).map(n=>n.trim()).includes(t)}catch{return!1}}async function Ue(t,e=
|
|
17
|
-
`).filter(Boolean).length}catch{return 0}}async function Fe(t,{cols:e=
|
|
18
|
-
`);for(let n=e.length-1;n>=0;n--){let r=e[n].trim();if(!r||r[0]!=="{")continue;let o;try{o=JSON.parse(r)}catch{continue}let s=o?.message?.usage;if(!s)continue;let a=(s.input_tokens||0)+(s.cache_creation_input_tokens||0)+(s.cache_read_input_tokens||0);if(a>0)return{occupancy:a,model:o.message.model||o.model||null}}return null}async function
|
|
19
|
-
`).trim():""}function
|
|
20
|
-
`)){let i=a.trim();if(!i||i[0]!=="{")continue;let c;try{c=JSON.parse(i)}catch{continue}if(c.type==="attachment"){let
|
|
21
|
-
|
|
22
|
-
`):""}import
|
|
23
|
-
`))
|
|
24
|
-
`,n!=null?{mode:n}:void 0),await
|
|
25
|
-
`){e.push(n),n="";continue}n+=s}return e.push(n),e.map(o=>o.trim()).filter(Boolean)}function
|
|
26
|
-
`,n);i!==-1&&(e=e.slice(0,i))}let r=
|
|
27
|
-
`).map(n=>n.trim()).find(Boolean)||t;return e.length>88?`${e.slice(0,88).trimEnd()}\u2026`:e}function ye(t,e=""){let n=e.trim();return n||
|
|
28
|
-
`)){let a=s.trim();if(!a||a[0]!=="{")continue;let i;try{i=JSON.parse(a)}catch{continue}let c=i.message;if(!c)continue;let l=[];if(
|
|
29
|
-
`)){let c=i.trim();if(!c||c[0]!=="{")continue;let l;try{l=JSON.parse(c)}catch{continue}if(l.isSidechain)continue;let u=l.message?.content;if(!Array.isArray(u))continue;let d=l.timestamp?Date.parse(l.timestamp):null;for(let f of u)if(!(!f||typeof f!="object"))if(f.type==="tool_use"&&f.id){if(f.name==="Agent"){let w=f.input||{};o.set(f.id,{id:f.id,kind:"agent",name:String(w.description||"").trim()||null,detail:String(w.subagent_type||"").trim()||null,ts:d,proven:!1})}else if(f.name==="Bash"&&f.input?.run_in_background===!0){let w=String(f.input.command||"").trim();o.set(f.id,{id:f.id,kind:"command",name:String(f.input.description||"").trim()||(w?
|
|
30
|
-
`).trim():"",
|
|
31
|
-
`);if(i!==-1)return o.slice(0,i);if(s+=a,o.length>=e)break}return o}finally{await n.close()}}async function
|
|
32
|
-
`).trim():"";async function
|
|
33
|
-
`)){let w=f.trim();if(!w||w[0]!=="{")continue;let
|
|
34
|
-
`;else if(s==="t")n+=" ";else if(s==="r")n+="\r";else if(s==="u")if(t[r+1]==="{"){let a=t.indexOf("}",r+2);if(a===-1)return null;n+=String.fromCodePoint(parseInt(t.slice(r+2,a),16)),r=a}else n+=String.fromCharCode(parseInt(t.slice(r+1,r+5),16)),r+=4;else n+=s}else{if(o===e)return n;if(e==="`"&&o==="$"&&t[r+1]==="{")return null;n+=o}}return null}function
|
|
35
|
-
`)){let c=i.trim();if(!c||c[0]!=="{")continue;let l;try{l=JSON.parse(c)}catch{continue}if(l.type!=="response_item")continue;let u=l.payload;if(!u)continue;let d=[];if(u.type==="function_call"&&
|
|
36
|
-
`);if(!
|
|
37
|
-
`),o=[],s=-1;for(let l=0;l<r.length;l++){let u=r[l].match(
|
|
38
|
-
`),o=r.findIndex(i=>
|
|
39
|
-
`),o=-1;for(let u=r.length-1;u>=0;u--)if(/^\s*Switch model\?\s*$/.test(r[u])){o=u;break}if(o<0)return null;let s=[],a=[];for(let u=o+1;u<r.length;u++){let d=r[u].match(
|
|
40
|
-
`),
|
|
41
|
-
`),o
|
|
42
|
-
`),o=
|
|
43
|
-
`),o=
|
|
44
|
-
`),o=
|
|
45
|
-
`)
|
|
46
|
-
`)
|
|
47
|
-
`),o
|
|
48
|
-
`),o
|
|
49
|
-
`)
|
|
50
|
-
`).
|
|
51
|
-
`).
|
|
52
|
-
`).map(
|
|
53
|
-
`)
|
|
54
|
-
`)
|
|
55
|
-
`);for(let n
|
|
56
|
-
`),n=e.findIndex(
|
|
57
|
-
`)
|
|
58
|
-
`))if(a.trim())try{s.push(JSON.parse(a))}catch{}return s}function Zu(t,e){let n=`track:${t}`;return Mt.on(n,e),()=>Mt.off(n,e)}var $t=t=>t||null;function ko(t){let e=t||{},n=ob(e);return n&&(n.payload={ts:e.at,...n.payload}),n}function ob(t){switch(t.type){case"said":return{type:"turn.user",actor:"user",source:"orchestrator",boxId:t.box,provider:$t(t.agent),payload:{text:t.text,boxRole:t.boxRole,...t.kind?{kind:t.kind}:{},...t.attachments?.length?{attachments:t.attachments}:{}}};case"unsaid":return{type:"turn.retracted",actor:"user",source:"orchestrator",boxId:t.box,provider:$t(t.agent),payload:{saidTs:t.saidTs,text:t.text,boxRole:t.boxRole}};case"orchestration-prompt":return{type:"turn.user",actor:"orchestrator",source:"orchestrator",boxId:t.box,provider:$t(t.agent),payload:{text:t.prompt,kind:t.kind,boxRole:t.boxRole}};case"lead-handoff":return{type:"handoff.done",actor:"orchestrator",source:"orchestrator",boxId:t.to,provider:$t(t.toAgent),payload:{from:t.from,fromAgent:t.fromAgent,to:t.to,toAgent:t.toAgent,reason:t.reason,handoffStartedAt:t.handoffStartedAt,fromSessionId:t.fromSessionId,toSessionId:t.toSessionId,epoch:t.epoch,singleBox:t.singleBox,forced:t.forced,recovered:t.recovered}};case"lead-handoff-failed":return{type:"handoff.failed",actor:"orchestrator",source:"orchestrator",boxId:t.box,provider:$t(t.toAgent),payload:{box:t.box,fromAgent:t.fromAgent,toAgent:t.toAgent,reason:t.reason,error:t.error,rolledBack:t.rolledBack,handoffStartedAt:t.handoffStartedAt}};case"subtask-delegated":return{type:"subtask.delegated",actor:"orchestrator",source:"orchestrator",boxId:t.to,provider:$t(t.toAgent),payload:{task:t.task,subtaskId:t.subtaskId,reason:t.reason}};case"subtask-merged":return{type:"subtask.merged",actor:"orchestrator",source:"orchestrator",boxId:t.box||t.from,provider:$t(t.agent),payload:{...gr(t)}};case"subtask-failed":return{type:"error",actor:"system",source:"orchestrator",boxId:t.box||t.to,provider:$t(t.agent||t.toAgent),payload:{kind:"subtask-failed",...gr(t)}};case"subtask-resumed":return{type:"box.state.changed",actor:"orchestrator",source:"orchestrator",boxId:t.box,provider:$t(t.agent),payload:{change:"subtask-resumed",...gr(t)}};case"model-change":return{type:"box.state.changed",actor:"user",source:"orchestrator",boxId:t.box,payload:{change:"model",model:t.to,from:t.from??null,verified:t.verified??null}};case"codex-update":return{type:"box.state.changed",actor:"orchestrator",source:"orchestrator",boxId:t.box,provider:"codex",payload:{change:"cli-update",from:t.from??null,to:t.to??null,status:t.status,...t.error?{error:t.error}:{}}};case"opened":return{type:"box.state.changed",actor:"user",source:"orchestrator",boxId:t.box,payload:{change:"opened",...gr(t)}};case"archived":case"unarchived":case"track-deleted":return{type:"box.state.changed",actor:"user",source:"orchestrator",payload:{change:t.type,...gr(t)}};case"rehydrated":return{type:"box.state.changed",actor:"orchestrator",source:"orchestrator",boxId:t.box,provider:$t(t.agent),payload:{change:"rehydrated"}};case"rehydrate-failed":return{type:"error",actor:"system",source:"orchestrator",boxId:t.box,provider:$t(t.agent),payload:{kind:"rehydrate-failed",reason:t.reason}};default:return null}}var bo=Promise.resolve(),Fa=t=>(bo=bo.then(t).catch(()=>{}),bo),td=()=>bo;function ed(t,e){let n=ko(e);if(!n)return;let r=e?.type==="track-deleted"?{id:t.id}:t;Fa(()=>R(r,n))}var Qu=new Set;async function nd(t){if(!(!t?.id||Qu.has(t.id))){Qu.add(t.id);try{let e=await P(t),n=new Set(e.filter(r=>r.source==="orchestrator").map(r=>`${r.type}|${r.payload?.ts??"?"}`));for(let r of t.events||[]){let o=ko(r);!o||n.has(`${o.type}|${o.payload?.ts??"?"}`)||await R(t,o)}}catch{}}}function gr(t){let{type:e,at:n,...r}=t;return r}var Ua=new Map;function rd(t,e,n){!n?.promptId||Ua.get(t.id)===n.promptId||(Ua.set(t.id,n.promptId),Fa(()=>R(t,{type:"prompt.raised",actor:"agent",source:"pane",boxId:e?.name,provider:$t(n.provider||e?.agent),payload:{promptId:n.promptId,question:n.question||n.title||null,options:(n.options||[]).map(r=>r?.label??r).slice(0,12)}})))}function od(t,e,n,r={}){Ua.delete(t.id),Fa(()=>R(t,{type:"prompt.answered",actor:"user",source:"orchestrator",boxId:e?.name,provider:$t(n?.provider||e?.agent),payload:{promptId:n?.promptId,...r}}))}import{randomUUID as Mb}from"node:crypto";var sd=/(?<!\b(?:a|an|the|this|that|another|one|each|any|no|separate|single|new|initial|final|first|second|third|last|later|next|subsequent|extra|additional|amend(?:ed)?|squash|merge|follow[\s-]?up|clean[\s-]?up)\s)commit(?!s\b)(?!\s+(?:history|log|message|hash|sha)s?\b)/.source,ad=/(?<!\b(?:a|an|the|this|that|another|one|each|any|no|separate|single|new|initial|final|first|second|third|last|later|next|subsequent|extra|additional)\s)push(?!\s+notifications?\b)(?:\s+(?:it|this|that|them|everything|the\s+\w+))?\s*(?:up)?/.source,id=/open(?:\s+up)?\s+(?:a|the|that)\s+(?:draft\s+)?(?:pr|pull\s+request)|creat(?:e|ing)\s+(?:a|the)\s+(?:draft\s+)?(?:pr|pull\s+request)|rais(?:e|ing)\s+(?:a|the)\s+(?:pr|pull\s+request)|put\s+up\s+(?:a|the)\s+(?:pr|pull\s+request)|send\s+(?:up\s+)?(?:a|the)\s+(?:pr|pull\s+request)/.source,sb=new RegExp(`\\b(?:${sd}|${ad}|${id})\\b`,"i"),ab=/\b(?:want\s+me\s+to|would\s+you\s+like(?:\s+me)?(?:\s+to)?|should\s+i|shall\s+i|do\s+you\s+want(?:\s+me)?(?:\s+to)?|happy\s+to|ready\s+for\s+me\s+to|let\s+me\s+know\s+(?:if|when|whether|and)|say\s+the\s+word|give\s+me\s+the\s+(?:word|go[\s-]?ahead)|if\s+you(?:'d|\s+would)?\s+(?:like|want|prefer)|just\s+say\s+(?:so|yes|go))\b/i,ib=/^(?:commit|push|open|create|raise|ready|ok(?:ay)?\s+to|good\s+to)\b/i,cb=90;function lb(t){return String(t).split(/(?<=[.?!])\s+|\n+/).map(e=>e.trim()).filter(Boolean)}function cd(t){let e=String(t||"").trim();if(!e)return null;let n=e.slice(-600);for(let r of lb(n).reverse()){if(!sb.test(r))continue;let o=r.endsWith("?")&&r.length<=cb&&ib.test(r);if(ab.test(r)||o)return r.length>200?r.slice(0,197)+"\u2026":r}return null}var ld=["commit","push","pr"],ub=new RegExp(`\\b(?:${sd})\\b`,"i"),db=new RegExp(`\\b(?:${ad})\\b`,"i"),fb=new RegExp(`\\b(?:${id})\\b`,"i"),pb=/\b(?:to|into|onto|on|updat(?:e|es|ing))\s+(?:the|that|this)\s+(?:open\s+|existing\s+|current\s+|same\s+)?(?:draft\s+)?(?:pr|pull\s+request)\b/i;function ud(t){let e=String(t||""),n=[];ub.test(e)&&n.push("commit"),db.test(e)&&n.push("push");let r=pb.test(e);!r&&fb.test(e)&&n.push("pr");let o=null;if(r){let s=e.match(/(?:pr|pull\s+request)\s*\(?\s*#(\d+)\)?/i)||e.match(/#(\d+)/);o={number:s?Number(s[1]):null}}return{offered:n.length?n:null,toPr:o}}function xo(t,{branch:e=null,base:n=null,toPr:r=null}={}){Array.isArray(t)||(t=t==="push"?["commit","push"]:["commit","push","pr"]);let o=new Set(t),s=e?` on this branch (${e})`:"",a=[];if(o.has("commit")&&a.push(`commit the work${s} with a clear commit message`),o.has("push")||o.has("pr")){let l=r&&!o.has("pr")?` (the open PR${r.number?` #${r.number}`:""} picks it up)`:"";a.push(o.has("commit")?`push it to the remote${l}`:`push this branch${s} to the remote${l}`)}o.has("pr")&&a.push(`open a pull request${n?` against ${n}`:""} with a concise title and a description of what changed and why (use \`gh pr create\`)`);let i=[];o.has("commit")||i.push("don't create any new commits \u2014 ship what's already committed"),!o.has("push")&&!o.has("pr")&&i.push("don't push"),o.has("pr")||i.push("don't open a pull request");let c=`Yes \u2014 go ahead: ${a.join(", then ")}.`;return i.length&&(c+=` ${i.join(", and ").replace(/^./,l=>l.toUpperCase())}.`),o.has("pr")&&(c+=" If a PR for this branch is already open, just push the update instead."),c}var mb=3e4;function dd(t,e=Date.now()){let n=[],r=null;for(let o of t||[]){let s=o.payload||{},a=s.ts??o.ts;if(o.type==="pr.offer.answered"&&s.action==="pr"){r={box:o.boxId,from:a,lastActivity:a};continue}!r||a==null||a<r.from||(o.type==="pr.offer.answered"?r=null:o.type==="pr.state.changed"&&s.change==="opened"?(n.push({box:r.box,from:r.from,to:a,settled:!0}),r=null):o.type==="turn.user"&&o.source!=="hook"&&!s.kind&&String(s.text||"").trim()?r=null:o.boxId===r.box&&/^(?:turn|tool)\./.test(o.type)&&(r.lastActivity=Math.max(r.lastActivity,a)))}return r&&e-r.lastActivity<=mb&&n.push({box:r.box,from:r.from,to:e+1e12}),n}function fd(t,e){let n=new Set;for(let r of e||[]){if(!r.settled)continue;let o=null;for(let s of t||[])s.kind!=="message"||s.role!=="assistant"||s.box!==r.box||s.ts==null||s.ts<r.from||s.ts>r.to||(!o||s.ts>=o.ts)&&(o=s);o&&n.add(o)}return n}import{execFile as gb}from"node:child_process";import{promisify as yb}from"node:util";import So from"node:fs/promises";import pd from"node:path";import hb from"node:os";var md=process.env.BOSTRAT_STATE_DIR||pd.join(process.env.HOME||hb.tmpdir(),".bostrat"),Wa=pd.join(md,"pr-links.json"),kt=(t,e)=>`${t}#${Number(e)}`,vo=null,Ha=null,L=Promise.resolve();async function vt(){return vo||(Ha??=So.readFile(Wa,"utf8").then(t=>JSON.parse(t)?.links??{}).catch(()=>({})).then(t=>vo=t).finally(()=>Ha=null),Ha)}async function nt(){let t=JSON.stringify({links:vo},null,2),e=`${Wa}.${process.pid}.tmp`;await So.mkdir(md,{recursive:!0}),await So.writeFile(e,t),await So.rename(e,Wa)}var wb=new Set(["api","observed","reconciled"]);async function Nt({repo:t,number:e,trackId:n,url:r=null,headRef:o=null,title:s=null,source:a="matched"}){if(!t||!Number.isInteger(Number(e))||!n)return{link:null,created:!1};let i=await vt(),c=kt(t,e);return i[c]?{link:i[c],created:!1}:(i[c]={repo:t,number:Number(e),trackId:n,url:r,headRef:o,title:s,source:a,announced:wb.has(a),firstSeen:Date.now()},L=L.then(nt,nt),await L.catch(()=>{}),{link:i[c],created:!0})}async function yr(t,e){if(!t||e==null)return null;let n=(await vt())[kt(t,e)];return!n||n.announced?null:(n.announced=!0,L=L.then(nt,nt),await L.catch(()=>{}),n)}async function Pt(t,e){return!t||e==null?null:(await vt())[kt(t,e)]??null}async function ze(t,e,n){if(!t||e==null)return null;let r=(await vt())[kt(t,e)];return r?(r.state=n,r.resolvedAt=Date.now(),delete r.armedAt,delete r.autoMerge,L=L.then(nt,nt),await L.catch(()=>{}),r):null}async function hd(t,e,{headSha:n=null,trackId:r=null}={}){if(!t||e==null)return null;let o=(await vt())[kt(t,e)];return o?(o.ciFix={delegatedAt:Date.now(),headSha:n,trackId:r},L=L.then(nt,nt),await L.catch(()=>{}),o):null}async function wd(t,e,{headSha:n=null,trackId:r=null}={}){if(!t||e==null)return null;let o=(await vt())[kt(t,e)];return o?(o.reconcile={delegatedAt:Date.now(),headSha:n,trackId:r},L=L.then(nt,nt),await L.catch(()=>{}),o):null}async function To(t,e,{headSha:n=null}={}){if(!t||e==null)return null;let r=(await vt())[kt(t,e)];return!r||!r.ciFix?null:(r.ciFixAwait={since:Date.now(),trackId:r.ciFix.trackId??null,headSha:n},delete r.ciFix,L=L.then(nt,nt),await L.catch(()=>{}),r)}async function Ao(t,e){if(!t||e==null)return null;let n=(await vt())[kt(t,e)];return!n||!n.ciFixAwait?null:(delete n.ciFixAwait,L=L.then(nt,nt),await L.catch(()=>{}),n)}async function _o(t,e,{headSha:n=null}={}){if(!t||e==null)return null;let r=(await vt())[kt(t,e)];return!r||!r.reconcile?null:(r.reconcileAwait={since:Date.now(),trackId:r.reconcile.trackId??null,headSha:n},delete r.reconcile,L=L.then(nt,nt),await L.catch(()=>{}),r)}async function Eo(t,e){if(!t||e==null)return null;let n=(await vt())[kt(t,e)];return!n||!n.reconcileAwait?null:(delete n.reconcileAwait,L=L.then(nt,nt),await L.catch(()=>{}),n)}async function gd(t,e,n){if(!t||e==null||!n)return null;let r=(await vt())[kt(t,e)];return r?(r.lastHeadSha=n,L=L.then(nt,nt),await L.catch(()=>{}),r):null}async function Io(t,e,{merge:n=null}={}){if(!t||e==null)return null;let r=(await vt())[kt(t,e)];return r?(r.armedAt=Date.now(),n&&(r.autoMerge={method:n.method||"merge",armedAt:r.armedAt}),L=L.then(nt,nt),await L.catch(()=>{}),r):null}async function Ro(t,e){if(!t||e==null)return null;let n=(await vt())[kt(t,e)];return!n||!n.armedAt&&!n.autoMerge?null:(delete n.armedAt,delete n.autoMerge,L=L.then(nt,nt),await L.catch(()=>{}),n)}async function yd(){return Object.values(await vt()).filter(t=>t.state!=="merged"&&t.state!=="closed")}async function qa(t,e){let n={working:!1,prompted:!1};if(!t)return n;let r=await pt(t,{lines:It}).catch(()=>"");if(!ir(e,r))return wl(e,r)?{working:!0,prompted:!1}:n;if($n(e,r)){let o=await me(t).catch(()=>r);if(Ve({name:t,agent:e},o))return{working:!1,prompted:!0}}return{working:!0,prompted:!1}}async function $o(t,e){return(await qa(t,e)).working}var bd=yb(gb),kd=[4e3,15e3,45e3],bb=2500,kb=5*6e4;async function Ga(t,e,{midTurn:n=$o,pollMs:r=bb,capMs:o=kb}={}){let s=Date.now()+o;for(;await n(t,e).catch(()=>!1);){if(Date.now()>=s)return!1;await new Promise(a=>setTimeout(a,r))}return!0}var xb=6e4,Sb=9e4,vb=2e3,Tb=/(?:^|[;&|]\s*|\)\s*)gh\s+(?:[^\s;&|"'`$]+\s+)*?pr\s+create\b/,Ab=/(?:^|[;&|]\s*|\)\s*)gh\s+(?:[^\s;&|"'`$]+\s+)*?pr\s+merge\b/,_b=/\s--web\b/,xd=/(?:--repo|-R)[\s=]+([\w.-]+\/[\w.-]+)/;function Ka(t,e){return t.slice(e).replace(/^[\s;&|)]+/,"").replace(/"[^"]*"|'[^']*'/g,'""').split(/[;&|]/)[0]}function Sd(t){let e=String(t||""),n=e.match(Tb);if(!n)return null;let r=Ka(e,n.index);return _b.test(r)?null:{repo:r.match(xd)?.[1]??null}}var Eb=new Set(["--subject","-t","--body","-b","--body-file","-F","--match-head-commit","--author-email","-A","--repo","-R"]);function vd(t){let e=String(t||""),n=e.match(Ab);if(!n)return null;let r=Ka(e,n.index),o=Ka(e,n.index+n[0].length),s=null,a=o.trim().split(/\s+/).filter(Boolean);for(let i=0;i<a.length;i++){let c=a[i];if(c.startsWith("-")){Eb.has(c)&&i++;continue}s=c.replace(/^["']+|["']+$/g,"")||null;break}return{repo:r.match(xd)?.[1]??null,selector:s,auto:/\s--auto\b/.test(r)}}var Ib="number,url,title,headRefName,createdAt";async function Rb(t,e){let n=async s=>{let{stdout:a}=await bd("gh",["pr","list","--repo",t,"--author","@me","--state","all","--limit","10","--json",Ib],{timeout:3e4,maxBuffer:4194304,env:{...process.env,...s}});return JSON.parse(a||"[]")},r=await Gt({slug:t}),o=await n(r);return!o.length&&r.GH_TOKEN&&(o=await n({}).catch(()=>[])),o.filter(s=>Date.parse(s.createdAt)>=e-xb).sort((s,a)=>Date.parse(a.createdAt)-Date.parse(s.createdAt))}async function $b(t,e){if(e)return[e];let n=[],r=await Z(t.workspace?.base||t.cwd).catch(()=>null);r&&n.push(r);for(let o of await oe().catch(()=>[])){let s=await Z(o.path).catch(()=>null);s&&!n.includes(s)&&n.push(s)}return n}async function Ob(t,e,n){let r=t.workspace?.branch,o=[];for(let s of await $b(t,e)){let a;try{a=await Rb(s,n)}catch{continue}for(let i of a){let c=await Pt(s,i.number);if(!(c&&(c.trackId!==t.id||c.announced))){if(r&&i.headRefName===r)return{slug:s,pr:i};o.push({slug:s,pr:i})}}}return o.length?(o.sort((s,a)=>Date.parse(a.pr.createdAt)-Date.parse(s.pr.createdAt)),o[0]):null}var ke=new Set;function Td(t,e,{delays:n=kd,turnEnd:r}={}){let o=Sd(e.command);if(!o)return null;let s=!!vd(e.command),a=`${t.id}|${e.id??e.ts??e.command}`;if(ke.has(a))return null;ke.add(a);let i=e.ts??Date.now(),c=(async()=>{try{for(let l of n){await new Promise(g=>setTimeout(g,l));let u=await Ob(t,o.repo,i).catch(()=>null);if(!u)continue;let{slug:d,pr:f}=u,{link:w,created:h}=await Nt({repo:d,number:f.number,trackId:t.id,url:f.url,headRef:f.headRefName,title:f.title,source:"observed"});return!h&&!(w?.trackId===t.id&&await yr(d,f.number))?null:(await Ga(e.box,e.agent,r),await R({id:t.id,cwd:t.cwd},{type:"pr.state.changed",actor:"agent",source:"transcript",boxId:e.box??null,provider:e.agent??null,payload:{ts:Date.now(),change:"opened",number:f.number,url:f.url,title:f.title,branch:f.headRefName,repo:d,...s?{selfMerge:!0}:{}}}),f)}return null}finally{ke.delete(a)}})();return c.catch(()=>ke.delete(a)),c}var Cb="number,url,title,headRefName,state";function Ad(t,e,{delays:n=kd,turnEnd:r,openedWait:o}={}){let s=vd(e.command);if(!s)return null;let a=`merge|${t.id}|${e.id??e.ts??e.command}`;if(ke.has(a))return null;ke.add(a);let i=(async()=>{try{let c=s.repo,l=s.selector,u=l?.match(/github\.com\/([\w.-]+\/[\w.-]+)\/pull\/(\d+)/);if(u&&([,c,l]=u),c||=await Z(t.workspace?.base||t.cwd).catch(()=>null),l||=t.workspace?.branch,!c||!l)return null;let d=async(w,h)=>{let g=await Pt(c,w.number);return g&&g.trackId!==t.id?null:(g||await Nt({repo:c,number:w.number,trackId:t.id,url:w.url,headRef:w.headRefName,title:w.title,source:"merge-observed"}),await Io(c,w.number),h&&((await P(t).catch(()=>[])).some(S=>S.type==="pr.state.changed"&&(S.payload?.change==="auto-armed"||S.payload?.change==="merged")&&S.payload?.number===w.number)||(await Ga(e.box,e.agent,r),await R({id:t.id,cwd:t.cwd},{type:"pr.state.changed",actor:"agent",source:"transcript",boxId:e.box??null,provider:e.agent??null,payload:{ts:Date.now(),change:"auto-armed",number:w.number,url:w.url,title:w.title,branch:w.headRefName,repo:c}}))),w)},f=null;for(let w of n){await new Promise(v=>setTimeout(v,w));let h;try{let{stdout:v}=await bd("gh",["pr","view",String(l),"--repo",c,"--json",Cb],{timeout:3e4,maxBuffer:4194304,env:{...process.env,...await Gt({slug:c})}});h=JSON.parse(v||"null")}catch{continue}if(!h?.number)continue;if(h.state==="CLOSED")return null;if(h.state!=="MERGED"){if(f=h,s.auto)return d(h,!0);continue}let g=await Pt(c,h.number);if(g&&g.trackId!==t.id)return null;g||await Nt({repo:c,number:h.number,trackId:t.id,url:h.url,headRef:h.headRefName,title:h.title,source:"merge-observed"}),await Ga(e.box,e.agent,r);let x=await P(t).catch(()=>[]);if(x.some(v=>v.type==="pr.state.changed"&&v.payload?.change==="merged"&&v.payload?.number===h.number))return null;let A=v=>v.some(E=>E.type==="pr.state.changed"&&E.payload?.change==="opened"&&E.payload?.number===h.number),S=0;if(!A(x)){if(Sd(e.command)&&await yr(c,h.number)){let v=Date.now();S=v+1,await R({id:t.id,cwd:t.cwd},{type:"pr.state.changed",actor:"agent",source:"transcript",boxId:e.box??null,provider:e.agent??null,payload:{ts:v,change:"opened",number:h.number,url:h.url,title:h.title,branch:h.headRefName,repo:c,selfMerge:!0}})}else if((await Pt(c,h.number))?.announced){let v=o?.capMs??Sb,E=o?.pollMs??vb,J=Date.now()+v;for(;Date.now()<J&&(await new Promise(I=>setTimeout(I,E)),!A(await P(t).catch(()=>[]))););}}return await R({id:t.id,cwd:t.cwd},{type:"pr.state.changed",actor:"agent",source:"transcript",boxId:e.box??null,provider:e.agent??null,payload:{ts:Math.max(Date.now(),S),change:"merged",number:h.number,url:h.url,title:h.title,branch:h.headRefName,repo:c}}),await ze(c,h.number,"merged"),h}return f?d(f,!1):null}finally{ke.delete(a)}})();return i.catch(()=>ke.delete(a)),i}function Ja(t){return t.sourceId?`${t.role}|id:${t.sourceId}`:`${t.role}|${t.ts??"?"}|${String(t.text||"").slice(0,40)}`}function _d(t){return t.id?`cmd|${t.box}|id:${t.id}`:`cmd|${t.box}|${t.ts??"?"}|${String(t.command||"").slice(0,40)}`}function Ed(t){return`job|${t.box}|id:${t.id}`}var Va=new Map,Oo=new Map;async function za(t){return Va.has(t.id)?Va.get(t.id):(Oo.has(t.id)||Oo.set(t.id,P(t).then(e=>{let n={keys:new Set,cmdN:0};for(let r of e)if((r.type==="turn.user"||r.type==="turn.agent")&&r.payload?.text!=null){let o=r.type==="turn.user"?"user":"assistant";r.payload.sourceId&&n.keys.add(Ja({role:o,sourceId:r.payload.sourceId})),n.keys.add(Ja({role:o,ts:r.payload.ts,text:r.payload.text}))}else if(r.type==="tool.called"&&r.source==="transcript"&&r.payload?.command){let o=r.payload;n.keys.add(_d({id:o.id,box:r.boxId,ts:o.ts,command:o.command})),Number(o.n)>n.cmdN&&(n.cmdN=Number(o.n))}else r.type==="job.started"&&r.payload?.id&&n.keys.add(Ed({id:r.payload.id,box:r.boxId}));return Va.set(t.id,n),n}).finally(()=>Oo.delete(t.id))),Oo.get(t.id))}async function Id(t,e){try{let n=(await za(t)).keys;for(let r of e||[]){if(!r||r.ts==null||!String(r.text||"").trim()&&!r.images?.length)continue;let o=Ja(r);if(!n.has(o)&&(n.add(o),await R(t,{type:r.role==="user"?"turn.user":"turn.agent",actor:r.role==="user"?"user":"agent",source:"transcript",boxId:r.box,provider:r.agent||null,payload:{text:r.text,ts:r.ts,model:r.model||null,tools:r.tools||void 0,boxRole:r.boxRole,sourceId:r.sourceId||void 0,note:r.note||void 0,images:r.images?.length?r.images:void 0}}),r.role==="assistant"&&r.box===t.leadBox&&t.workspace?.branch)){let s=cd(r.text);if(s){let{offered:a,toPr:i}=ud(s);await R(t,{type:"pr.offered",actor:"agent",source:"transcript",boxId:r.box,provider:r.agent||null,payload:{offerId:Mb(),ts:r.ts,excerpt:s,branch:t.workspace.branch,base:t.workspace.baseRef?.replace(/^origin\//,"")||null,boxRole:r.boxRole,offered:a||void 0,toPr:i||void 0}})}}}}catch{}}async function Rd(t,e){try{let n=await za(t),r=[...e||[]].filter(o=>o&&String(o.command||"").trim()).sort((o,s)=>(o.ts||0)-(s.ts||0));for(let o of r){let s=_d(o);n.keys.has(s)||(n.keys.add(s),n.cmdN+=1,await R(t,{type:"tool.called",actor:"agent",source:"transcript",boxId:o.box,provider:o.agent||null,payload:{id:o.id??null,tool:o.tool||null,command:o.command,description:o.description||"",summary:o.summary||o.command,ts:o.ts??null,boxRole:o.boxRole,n:n.cmdN}}),Td(t,o),Ad(t,o))}}catch{}}async function $d(t,e){try{let n=await za(t),r=[...e||[]].filter(o=>o&&o.id).sort((o,s)=>(o.ts||0)-(s.ts||0));for(let o of r){let s=Ed(o);n.keys.has(s)||(n.keys.add(s),await R(t,{type:"job.started",actor:"agent",source:"transcript",boxId:o.box,provider:o.agent||null,payload:{id:o.id,kind:o.kind||"agent",name:o.name||null,detail:o.detail||null,ts:o.ts??null,boxRole:o.boxRole}}))}}catch{}}var Nb=/^Base directory for this skill:\s*(\S+)/,Pb=/^Run the "([^"]+)" workflow\./,Bb=/<command-name>\s*\/?([a-z0-9:_-]+)\s*<\/command-name>/i,Db=/^<local-command-(?:stdout|caveat)>/i,jb=/^\[Image: original \d+x\d+, displayed at \d+x\d+\.\s*Multiply coordinates by [\d.]+ to map to original image\.\]$/,Lb=/^<task-notification>/i,Ub=/<summary>([\s\S]*?)<\/summary>/i,Fb=/<task-id>([\s\S]*?)<\/task-id>/i,Hb=/<tool-use-id>([\s\S]*?)<\/tool-use-id>/i,Wb=/<status>([\s\S]*?)<\/status>/i,qb=/<result>([\s\S]*?)(?:<\/result>|$)/i;var Gb=new Set(["model","clear"]);function Co(t){let e=String(t||"").trim();if(!e)return null;let n=e.match(Nb);if(n)return{kind:"skill",name:n[1].split("/").filter(Boolean).pop()||"skill"};let r=e.match(Pb);if(r)return{kind:"skill",name:r[1]};let o=e.match(Bb);if(o){let s=o[1].toLowerCase();return Gb.has(s)?{kind:"suppress"}:{kind:"command",command:`/${s}`}}if(Db.test(e))return{kind:"suppress"};if(jb.test(e))return{kind:"suppress"};if(Lb.test(e)){let s=i=>Kb(e.match(i)?.[1].trim()||"")||null,a=s(qb);return{kind:"task",summary:s(Ub)||"",taskId:s(Fb),toolUseId:s(Hb),status:s(Wb)?.toLowerCase()||null,resultHead:a?a.length>600?`${a.slice(0,600).trimEnd()}\u2026`:a:null}}return null}function Kb(t){return t.replace(/</g,"<").replace(/>/g,">").replace(/"/g,'"').replace(/'/g,"'").replace(/&/g,"&")}function Od(t){return t?.kind==="skill"?`Ran the ${t.name} skill`:t?.kind==="command"?`Ran ${t.command}`:t?.kind==="task"?t.summary||"A background task finished":""}function Cd(t){return t?.kind==="skill"?"skill-run":t?.kind==="task"?"task-done":"command-run"}var at=t=>t.payload?.ts??t.ts;function Mo(t){let e=[],n=new Set,r=new Set,o=new Map,s=new Map,a=new Set,i=new Map,c=new Set,l=new Map,u=/^Agent "([\s\S]+)" finished$/,d=new Map;for(let m of t||[]){if(m.type!=="tool.called"||m.source!=="transcript")continue;let p=String(m.payload?.command||"");if(/\bbostrat-mock\s+(?:add|update)\b/.test(p))for(let b of p.matchAll(/[\w./~-]+\.(?:png|jpe?g|gif|webp|bmp|svg)\b/gi)){let T=b[0].split("/").pop().toLowerCase();d.has(T)||d.set(T,[]),d.get(T).push(m.payload?.ts??m.ts)}}let f=3*6e4,w=20*6e4,h=(m,p)=>!!(m?.name&&d.get(String(m.name).toLowerCase()))?.some(T=>T>=p-f&&T<=p+w);for(let m of t||[])if(m.type==="turn.retracted"){r.add(`${m.boxId}|${m.payload?.saidTs}`);let p=Je(m.payload?.text);p&&(o.has(m.boxId)||o.set(m.boxId,[]),o.get(m.boxId).push(p))}else if(m.type==="turn.user"&&m.source==="orchestrator"){let p=Je(m.payload?.text);p&&(s.has(m.boxId)||s.set(m.boxId,new Set),s.get(m.boxId).add(p))}else if(m.type==="job.started"&&m.payload?.id)a.add(m.payload.id),(m.payload.kind||"agent")==="agent"&&m.payload.name&&c.add(m.payload.name);else if(m.type==="turn.user"&&m.payload?.note?.kind==="task"){let p=m.payload.note,b={status:p.status||null,summary:p.summary||null,resultHead:p.resultHead??null,ts:at(m)};p.toolUseId&&i.set(p.toolUseId,b);let T=String(p.summary||"").match(u);T&&l.set(T[1],b)}let g=new Map,x=new Map,A=new Map,S=new Map,v=new Map,E=new Map,J=new Map,I=new Map,M=(m,p)=>{let b=o.get(m);if(!b?.length)return!1;let T=Je(p),ot=s.get(m);return b.some($=>{if(!T.startsWith($))return!1;let z=Je(T.slice($.length));return!!z&&!!ot?.has(z)})};for(let m of t||[]){let p=m.payload||{};switch(m.type){case"turn.user":{if(m.source==="hook"||!String(p.text||"").trim()&&!p.images?.length)break;{let b=p.note||Co(p.text);if(b){if(b.kind==="task"){if(b.toolUseId&&a.has(b.toolUseId))break;let T=String(b.summary||"").match(u);if(!b.toolUseId&&T&&c.has(T[1]))break}b.kind!=="suppress"&&e.push({id:m.id,kind:"event",type:Cd(b),name:b.name??null,command:b.command??null,summary:b.summary??null,ts:at(m),box:m.boxId,agent:m.provider});break}}if(r.has(`${m.boxId}|${at(m)}`)||m.source==="transcript"&&M(m.boxId,p.text)||p.kind==="mock-picked"||p.kind==="ci-fix"||p.kind==="reconcile"||p.kind==="pr-go"||p.kind==="push-go"||p.kind==="spin-up-view")break;for(let b of g.values())b.status="stale";g.clear(),e.push({id:m.id,kind:"message",role:"user",text:p.text,ts:at(m),box:m.boxId,agent:m.provider,boxRole:p.boxRole,...m.actor==="orchestrator"?{orchestration:p.kind||!0}:{},...p.sourceId?{sourceId:p.sourceId}:{},...p.attachments?.length?{attachments:p.attachments}:{},...p.images?.length?{images:p.images}:{}});break}case"turn.agent":{J.clear();let b=(p.images||[]).filter(ot=>!h(ot,at(m)));if(!String(p.text||"").trim()&&!b.length)break;if(m.provider==="cursor"){let ot=`${m.boxId}|${p.text}`;if(n.has(ot))break;n.add(ot)}let T=m.provider==="codex"?(p.tools||[]).filter(ot=>!ya.has(ot)&&!pr.has(ot)):p.tools||[];e.push({id:m.id,kind:"message",role:"assistant",text:p.text,ts:at(m),box:m.boxId,agent:m.provider,boxRole:p.boxRole,...T.length?{tools:T}:{},...p.model?{model:p.model}:{},...p.sourceId?{sourceId:p.sourceId}:{},...b.length?{images:b}:{}});break}case"job.started":{if(!p.id)break;let b=at(m),T=i.get(p.id);if(!T&&(p.kind||"agent")==="agent"&&p.name){let N=l.get(p.name);N&&N.ts>=b&&(T=N)}let ot={jobId:p.id,jobKind:p.kind||"agent",name:p.name||null,detail:p.detail||null,status:T?T.status&&T.status!=="completed"?"failed":"done":"running",startedTs:b,finishedTs:T?.ts??null,summary:T?.summary??null,resultHead:T?.resultHead??null},$=I.get(m.boxId);if($&&$.jobs.some(N=>(N.finishedTs??1/0)>=b))$.jobs.push(ot);else{let N={id:m.id,kind:"job-dock",dockId:p.id,ts:b,box:m.boxId,agent:m.provider,jobs:[ot]};I.set(m.boxId,N),e.push(N)}break}case"handoff.done":e.push(xe(m,"lead-handoff"));break;case"subtask.delegated":e.push({...xe(m,"subtask-delegated"),to:m.boxId,toAgent:m.provider});break;case"subtask.merged":e.push(xe(m,"subtask-merged"));break;case"pr.state.changed":if(!p.change)break;if(p.change==="opened"){for(let b of g.values())b.status="stale";g.clear()}e.push(xe(m,`pr-${p.change}`));break;case"pr.offered":{if(!p.offerId)break;for(let T of g.values())T.status="stale";g.clear();let b={id:m.id,kind:"pr-offer",offerId:p.offerId,excerpt:p.excerpt||null,branch:p.branch||null,base:p.base||null,offered:Array.isArray(p.offered)&&p.offered.length?p.offered:null,toPr:p.toPr&&typeof p.toPr=="object"?{number:p.toPr.number??null}:null,status:"open",ts:at(m),box:m.boxId,agent:m.provider};g.set(p.offerId,b),x.set(p.offerId,b),e.push(b);break}case"pr.offer.answered":{let b=x.get(p.offerId);b&&(b.status=p.action||"dismiss",b.actions=Array.isArray(p.actions)?p.actions:null,g.delete(p.offerId));break}case"box.state.changed":{let b=p.change==="model"?"model-change":p.change;if(!b)break;let T=xe(m,b);b==="model-change"&&(T.to=p.model),e.push(T);break}case"view.state.changed":{if(p.change!=="created"||p.restarted)break;e.push({id:m.id,kind:"view-card",viewId:p.viewId,name:p.name,slug:p.slug,port:p.port,origin:p.origin,ts:at(m),box:m.boxId,agent:m.provider});break}case"view.spinup.failed":{e.push({id:m.id,kind:"view-spinup-failed",name:p.name||null,reason:p.reason||"no-view",ts:at(m),box:m.boxId,agent:m.provider});break}case"mock.state.changed":{if(p.change==="picked"){e.push({id:m.id,kind:"mock-picked",mockId:p.mockId,name:p.name,group:p.group??null,version:p.version??null,siblings:p.siblings??[],ts:at(m),box:m.boxId,agent:m.provider});break}if(p.change!=="created")break;e.push({id:m.id,kind:"mock-card",mockId:p.mockId,name:p.name,group:p.group??null,ts:at(m),box:m.boxId,agent:m.provider});break}case"runbook.state.changed":{if(p.change!=="created")break;e.push({id:m.id,kind:"runbook-card",runbookId:p.runbookId,name:p.name,ts:at(m),box:m.boxId,agent:m.provider});break}case"ticket.linked":{e.push({id:m.id,kind:"ticket-card",provider:p.provider||"linear",key:p.key,url:p.url,title:p.title??null,linkSource:p.linkSource??null,ts:at(m),box:m.boxId,agent:m.provider});break}case"ticket.move.offered":{if(!p.offerId)break;for(let T of A.values())T.key===p.key&&(T.status="stale",A.delete(T.offerId));let b={id:m.id,kind:"ticket-offer",offerId:p.offerId,provider:p.provider||"linear",key:p.key,url:p.url??null,phase:p.phase??null,from:p.from??null,to:p.to?.name??null,prNumber:p.prNumber??null,status:"open",ts:at(m),box:m.boxId,agent:m.provider};A.set(p.offerId,b),S.set(p.offerId,b),e.push(b);break}case"ticket.move.answered":{let b=S.get(p.offerId);b&&(b.status=p.action==="moved"?"moved":"dismiss",A.delete(p.offerId));break}case"ticket.state.changed":{for(let b of A.values())b.key===p.key&&b.to===p.to&&(b.status="stale",A.delete(b.offerId));if(p.mode==="offer")break;e.push(xe(m,"ticket-state"));break}case"ticket.commented":{e.push({id:m.id,kind:"ticket-comment",key:p.key,url:p.url??null,excerpt:p.excerpt??null,ts:at(m),box:m.boxId,agent:m.provider});break}case"ticket.flow.changed":e.push(xe(m,"ticket-flow"));break;case"ticket.create.offered":{if(!p.proposalId)break;for(let T of v.values())T.status="stale";v.clear();let b={id:m.id,kind:"ticket-create-offer",proposalId:p.proposalId,provider:p.provider||"linear",epic:p.epic??null,issues:Array.isArray(p.issues)?p.issues:[],team:p.team??null,needsTeam:!!p.needsTeam,linkCurrentTrack:!!p.linkCurrentTrack,status:"open",ts:at(m),box:m.boxId,agent:m.provider};v.set(p.proposalId,b),E.set(p.proposalId,b),e.push(b);break}case"ticket.create.answered":{let b=E.get(p.proposalId);b&&(b.status=p.action==="created"?"created":"dismiss",b.createdCount=p.createdCount??null,b.failedCount=p.failedCount??null,v.delete(p.proposalId));break}case"ticket.created":{e.push({id:m.id,kind:"ticket-created",items:Array.isArray(p.items)?p.items:[],failed:Array.isArray(p.failed)?p.failed:[],team:p.team??null,ts:at(m),box:m.boxId,agent:m.provider});break}case"error":{if(!p.kind)break;e.push(xe(m,p.kind));break}case"provider.connected":{e.push({id:m.id,kind:"event",type:"provider-connected",provider:p.provider||m.provider,ts:at(m),at:at(m),box:m.boxId,agent:m.provider});break}case"prompt.answered":{if(p.control||!p.question)break;let b=p.groupId||p.promptId;if(!b)break;let T=J.get(b);T||(T={id:m.id,kind:"prompt",groupId:b,provider:m.provider,box:m.boxId,agent:m.provider,questionTotal:p.questionTotal??1,questions:[],ts:at(m)},J.set(b,T));let ot=p.questionIndex??0,$={index:ot,header:p.header||"",question:p.question,multiSelect:!!p.multiSelect,chosen:Array.isArray(p.chosen)?p.chosen:[]},z=T.questions.findIndex(H=>H.index===ot);z>=0?T.questions[z]=$:T.questions.push($),T.questions.sort((H,_)=>H.index-_.index),T.questionTotal=Math.max(T.questionTotal,p.questionTotal??1),new Set(T.questions.map(H=>H.index)).size>=T.questionTotal&&(e.push(T),J.delete(b));break}default:break}}return Jb(e.sort((m,p)=>(m.ts??0)-(p.ts??0)))}var Vb=2*6e4;function Jb(t){let e=new Map,n=[];for(let r of t){if(r.kind!=="message"||!r.box){n.push(r);continue}let o=r.role==="assistant"&&!String(r.text||"").trim()&&r.images?.length,s=e.get(r.box);if(o&&s&&(r.ts??0)-s.lastTs<=Vb){s.item.images.push(...r.images),s.lastTs=r.ts??s.lastTs;continue}if(o){let a={...r,images:[...r.images]};e.set(r.box,{item:a,lastTs:r.ts??0}),n.push(a)}else e.delete(r.box),n.push(r)}return n}function Xa(t,e=null){let n=[];for(let o of t||[]){if(o.type!=="tool.called"||o.source!=="transcript")continue;let s=o.payload||{};String(s.command||"").trim()&&n.push({id:s.id??null,tool:s.tool||null,command:s.command,description:s.description||"",summary:zb(s),ts:s.ts??o.ts,n:s.n,box:o.boxId,agent:o.provider,boxRole:s.boxRole})}if(!n.length)return[];let r=(e??Mo(t)).filter(o=>o.kind==="message");return Vl(n,r)}function zb(t){let e=String(t.summary||"").trim();return e&&e!==t.command&&e!==vn(t.command)?e:ye(t.command,t.description||"")}function xe(t,e){let{ts:n,change:r,...o}=t.payload||{};return{id:t.id,kind:"event",type:e,...o,at:at(t),ts:at(t)}}var Ya={"claude-opus-4-8":{input:5,output:25,cacheWrite:6.25,cacheRead:.5},"claude-sonnet-5":{input:3,output:15,cacheWrite:3.75,cacheRead:.3},"claude-sonnet-4-6":{input:3,output:15,cacheWrite:3.75,cacheRead:.3},"claude-haiku-4-5":{input:1,output:5,cacheWrite:1.25,cacheRead:.1},"claude-fable-5":{input:10,output:50,cacheWrite:12.5,cacheRead:1},"gpt-5.5":{input:5,output:30,cacheWrite:5,cacheRead:.5},"gpt-5.6-sol":{input:5,output:30,cacheWrite:6.25,cacheRead:.5},"gpt-5.6-terra":{input:2.5,output:15,cacheWrite:3.125,cacheRead:.25},"gpt-5.6-luna":{input:1,output:6,cacheWrite:1.25,cacheRead:.1}},Md={opus:"claude-opus-4-8",opusplan:"claude-opus-4-8",default:"claude-opus-4-8",sonnet:"claude-sonnet-4-6",haiku:"claude-haiku-4-5",fable:"claude-fable-5","gpt-5.6":"gpt-5.6-sol"};function Nd(t){if(!t)return null;let e=String(t).trim();return Ya[e]?e:Md[e]?Md[e]:Object.keys(Ya).find(r=>e.startsWith(r))||null}var No=t=>Nd(t)!==null;function Kt(t,e={}){let n=Ya[Nd(t)];return n?((e.input||0)*n.input+(e.output||0)*n.output+(e.cacheWrite||0)*n.cacheWrite+(e.cacheRead||0)*n.cacheRead)/1e6:0}function Xb(t){let e=[],n=null;for(let r of String(t||"").split(`
|
|
59
|
-
`)){let o=r.trim();if(!o||o[0]!=="{")continue;let s;try{s=JSON.parse(o)}catch{continue}let a=s.payload||{};if(s.type==="turn_context"&&a.model&&(n=a.model),s.type==="event_msg"&&a.type==="token_count"&&a.info?.last_token_usage){let i=a.info.last_token_usage;e.push({ts:s.timestamp?Date.parse(s.timestamp):null,model:n,last:{input:i.input_tokens||0,cachedInput:i.cached_input_tokens||0,output:i.output_tokens||0,reasoningOutput:i.reasoning_output_tokens||0},occupancy:i.total_tokens??null,contextWindow:a.info.model_context_window??null})}}return e}var Za=new Map;async function Yb(t,e){let n=`${t.id}:${e.name}`;if(!Za.has(n)){let r=await P(t),o=new Set;for(let s of r)s.type==="cost.recorded"&&s.boxId===e.name&&s.payload?.codexTs!=null&&o.add(s.payload.codexTs);Za.set(n,o)}return Za.get(n)}async function Pd(t,e){try{if(e?.agent!=="codex"||!e.cwd)return;let n=await Ge(e.cwd,e.pinned?e.sessionId:null);if(!n)return;let r=await Yb(t,e),o=Xb(await St(n.file,262144)),s=o[o.length-1];for(let a of o){if(a.ts==null||r.has(a.ts))continue;r.add(a.ts);let i={input:Math.max(0,a.last.input-a.last.cachedInput),output:a.last.output,cacheRead:a.last.cachedInput,cacheWrite:0},c=a.model||e.model||"gpt-5.5";await R(t,{type:"cost.recorded",actor:"agent",source:"transcript",boxId:e.name,provider:"codex",payload:{usd:Kt(c,i),model:c,tokens:i,reasoningOutput:a.last.reasoningOutput||void 0,codexTs:a.ts,ts:a.ts}}),a===s&&a.contextWindow&&await R(t,{type:"context.usage",actor:"agent",source:"transcript",boxId:e.name,provider:"codex",payload:{occupancy:a.occupancy,window:a.contextWindow,pct:a.occupancy!=null?a.occupancy/a.contextWindow:null,model:a.model||null,ts:a.ts}})}}catch{}}import Po from"node:fs";import ti from"node:path";import Zb from"node:os";var Qb=process.env.BOSTRAT_STATE_DIR||ti.join(process.env.HOME||Zb.tmpdir(),".bostrat"),Dd=(t=process.env)=>!!t.BOSTRAT_CURSOR_USAGE_COOKIE,tk="https://cursor.com/api/dashboard/get-filtered-usage-events",Bd=100;async function ek({sinceMs:t,cookie:e,fetchImpl:n=fetch}){let r=[];for(let o=1;o<=20;o++){let s=await n(tk,{method:"POST",headers:{"content-type":"application/json",origin:"https://cursor.com",cookie:`WorkosCursorSessionToken=${e}`},body:JSON.stringify({teamId:0,startDate:String(t),endDate:String(Date.now()),page:o,pageSize:Bd})});if(!s.ok)throw new Error(`cursor usage feed: HTTP ${s.status}`);let a=await s.json(),i=a?.usageEventsDisplay||a?.usageEvents||[];if(r.push(...i),i.length<Bd)break}return r}function nk(t){if(!t)return null;let e=Number(t.timestamp)||null,n=t.tokenUsage||null,r=n?{input:n.inputTokens||0,output:n.outputTokens||0,cacheRead:n.cacheReadTokens||0,cacheWrite:n.cacheWriteTokens||0}:null,o=t.conversationId||null;return{id:[o||"?",e??"?",r?`${r.input}.${r.output}.${r.cacheRead}.${r.cacheWrite}`:"?"].join(":"),conversationId:o,ts:e,model:t.model||t.modelIntent||null,kind:t.kindLabel||t.kind||null,isHeadless:t.isHeadless===!0,tokens:r,vendorCents:n?.totalCents??null,chargedCents:t.chargedCents??null}}var rk=5*6e4,Se={at:0,rows:null,error:null},Bo=()=>ti.join(Qb,"cursor-usage.json");function jd(){try{return JSON.parse(Po.readFileSync(Bo(),"utf8"))}catch{return{sinceMs:0,seen:[]}}}function ok(t){try{Po.mkdirSync(ti.dirname(Bo()),{recursive:!0});let e=Bo()+".tmp";Po.writeFileSync(e,JSON.stringify(t)),Po.renameSync(e,Bo())}catch{}}var sk=5e3;async function ak({fetchImpl:t,now:e=Date.now()}={}){if(Se.rows&&e-Se.at<rk)return Se.rows;let n=process.env.BOSTRAT_CURSOR_USAGE_COOKIE;if(!n)return null;let r=jd(),o=Math.max(r.sinceMs-6e4,e-24*36e5);try{let s=await ek({sinceMs:o,cookie:n,fetchImpl:t});Se={at:e,rows:s.map(nk).filter(Boolean),error:null}}catch(s){Se={at:e,rows:null,error:String(s?.message||s)}}return Se.rows}var Ld=()=>({enabled:Dd(),lastPollAt:Se.at||null,error:Se.error}),Qa=new Map;async function ik(t,e){let n=`${t.id}:${e.name}`;if(!Qa.has(n)){let r=await P(t),o=new Set;for(let s of r)s.type==="cost.recorded"&&s.boxId===e.name&&s.payload?.cursorEventId&&o.add(s.payload.cursorEventId);Qa.set(n,o)}return Qa.get(n)}async function Ud(t,e,{fetchImpl:n}={}){try{if(e?.agent!=="cursor"||!e.cwd||!Dd())return;let r=await ak({fetchImpl:n});if(!r||!r.length)return;let o=await nu(e.cwd,e.pinned?e.sessionId:null);if(!o?.chatId)return;let s=await ik(t,e),a=jd(),i=new Set(a.seen||[]),c=a.sinceMs||0;for(let l of r){if(!l.conversationId||l.conversationId!==o.chatId||s.has(l.id)||i.has(`${t.id}:${e.name}:${l.id}`))continue;s.add(l.id),i.add(`${t.id}:${e.name}:${l.id}`),l.ts&&l.ts>c&&(c=l.ts);let u=l.model||o.models[o.models.length-1]||"cursor-unknown",d=l.tokens||null,f=l.vendorCents!=null?l.vendorCents/100:0;!f&&d&&(f=Kt(u,d)),!f&&l.chargedCents!=null&&(f=l.chargedCents/100),await R(t,{type:"cost.recorded",actor:"agent",source:"vendor",boxId:e.name,provider:"cursor",payload:{usd:f,model:u,...d?{tokens:d}:{},...l.vendorCents!=null?{vendorCents:l.vendorCents}:{},...l.chargedCents!=null?{chargedCents:l.chargedCents}:{},...l.kind?{kind:l.kind}:{},conversationId:l.conversationId,cursorEventId:l.id,ts:l.ts??void 0}})}ok({sinceMs:c,seen:[...i].slice(-sk)})}catch{}}import kr from"node:fs/promises";import Tt from"node:path";import ck from"node:os";import lk from"node:net";import{randomUUID as uk}from"node:crypto";import{execFile as dk}from"node:child_process";import{promisify as fk}from"node:util";var Jt=fk(dk),pk=process.env.BOSTRAT_STATE_DIR||Tt.join(process.env.HOME||ck.tmpdir(),".bostrat"),Gd=Tt.join(pk,"views.json"),[Fd,Hd]=(process.env.BOSTRAT_VIEW_PORTS||"4300-4349").split("-").map(t=>Number(t)||0),ve=Number(process.env.BOSTRAT_VIEW_MAX)||3,mk="bostrat-view-",Mn=t=>`${mk}${t}`,Wd=new Set(["bostrat","www","api","views","app"]),Do=t=>Object.assign(new Error(t),{status:400}),hk=new Set(["npm","pnpm","yarn","bun"]),ri="package.json",wk=/[|;&`]|\$\(/,gk=new Set(["sh","bash","zsh","dash","env","exec","nohup","time"]),yk=/(^|\s)(-C|--cwd|--manifest-path|--dir)(=|\s)/,Kd=t=>String(t).trim().split(/\s+/).filter(Boolean),bk=/^[A-Za-z_][A-Za-z0-9_]*=/;function kk(t){for(let e of Kd(t))if(!bk.test(e))return e;return null}function Vd(t){let e=Kd(t),n=e.indexOf("--");return n===-1?e:e.slice(0,n)}function xk(t,e){let n=Vd(e);for(let r=0;r<n.length;r++){let o=/^--prefix=(.+)$/.exec(n[r]);if(o)return Tt.resolve(t,o[1]);if(n[r]==="--prefix"&&n[r+1])return Tt.resolve(t,n[r+1])}return t}var Sk=t=>kr.stat(t).then(e=>e.isDirectory(),()=>!1),Jd=t=>kr.stat(t).then(e=>e.isFile(),()=>!1),jo=t=>kr.realpath(t).catch(()=>Tt.resolve(t));async function vk(t,e){if(Tt.resolve(t)===Tt.resolve(e))return!0;let[n,r]=await Promise.all([jo(t),jo(e)]);return n===r}async function Tk(t){let n=(await kr.readdir(t,{withFileTypes:!0}).catch(()=>[])).filter(o=>o.isDirectory()&&!o.name.startsWith(".")&&o.name!=="node_modules");return(await Promise.all(n.map(async o=>await Jd(Tt.join(t,o.name,ri))?`./${o.name}`:null))).filter(Boolean)}async function Ak(t,e){let[n,r]=await Promise.all([jo(t),jo(e)]),o=Tt.relative(n,r);if(o&&(o.startsWith("..")||Tt.isAbsolute(o)))throw Do(`serve directory escapes the Track workspace: ${e}`)}async function _k(t,e){if(!await Sk(t))throw Do(`serve directory does not exist: ${t}`);let n=kk(e);if(!n)return;if(!n.includes("/")&&!await Ps(n,{loginShell:!0,failOpen:!0}))throw Do(`\`${n}\` is not on the box's PATH \u2014 install it in the box first`);if(wk.test(e)||gk.has(n)||!hk.has(n)||!Vd(e).includes("run")||yk.test(e))return;let r=xk(t,e);if(await Jd(Tt.join(r,ri)))return;let o=await Tk(r),s=o.length?` \u2014 found one in ${o.join(", ")}; run bostrat-view from there`:"";throw Do(`no ${ri} in ${r}${s}`)}var Vt=null,ei=null;async function ie(){return Vt||(ei||=(async()=>{try{Vt=JSON.parse(await kr.readFile(Gd,"utf8"))}catch{Vt={views:[],adoptWatermarks:{}}}finally{ei=null}return Vt.views||=[],Vt.adoptWatermarks||={},Vt})(),ei)}async function zt(){await qt(Gd,Vt)}function ae(t,e,n={}){return R({id:t.trackId,cwd:t.cwd},{type:"view.state.changed",actor:"orchestrator",source:"orchestrator",boxId:t.box||null,payload:{ts:Date.now(),change:e,viewId:t.id,name:t.name,slug:t.slug,port:t.port,...n}}).catch(()=>{})}var br=new Map,ai=Number(process.env.BOSTRAT_VIEW_JOB_TIMEOUT)||3e5,zd=12e3;function ii(t){return br.get(t)||null}function Xd(t,{name:e,box:n}={}){let r=br.get(t);if(r&&!r.finishedAt)throw new Error("a view is already being spun up for this Track");let o={trackId:t,name:e||"preview",box:n||null,step:"spinning-up",startedAt:Date.now(),finishedAt:null,viewId:null,error:null};return br.set(t,o),o}function oi(t,e){let n=br.get(t);n&&!n.finishedAt&&!n.viewId&&(n.viewId=e,n.step="booting")}function Lo(t,{error:e=null}={}){let n=br.get(t);return!n||n.finishedAt?null:(n.finishedAt=Date.now(),e&&(n.error=e),n)}function Yd(t){let e=[];for(let n of t?.events||[])n.type!=="view-spinup"||!n.box||!n.startedAt||!n.endedAt||e.push({box:n.box,from:n.startedAt,to:n.endedAt});return e}function si(t){return String(t||"").trim().toLowerCase().replace(/[^a-z0-9]+/g,"-").replace(/^-+|-+$/g,"").slice(0,40).replace(/-+$/g,"")||"view"}function Ek(t,e){let n=si(e),r=new Set(t.views.map(o=>o.slug));if(!r.has(n)&&!Wd.has(n))return n;for(let o=2;;o++){let s=`${n}-${o}`;if(!r.has(s)&&!Wd.has(s))return s}}function xr(t,{timeoutMs:e=1200}={}){return new Promise(n=>{let r=lk.connect({port:t,host:"127.0.0.1"}),o=s=>{r.destroy(),n(s)};r.setTimeout(e,()=>o(!1)),r.once("connect",()=>o(!0)),r.once("error",()=>o(!1))})}async function ci(t){if(process.platform!=="darwin"){await Jt("fuser",["-k",`${t}/tcp`]).catch(()=>{});return}let e="";try{({stdout:e}=await Jt("lsof",["-t","-sTCP:LISTEN","-i",`tcp:${t}`]))}catch{return}for(let n of e.split(/\s+/).filter(Boolean)){let r=Number(n);if(!(!r||r===process.pid))try{process.kill(r,"SIGKILL")}catch{}}}async function Ik(t,e){let n=new Set(t.views.map(r=>r.port));if(e){let r=Number(e);if(!Number.isInteger(r)||r<1024||r>65535)throw new Error("invalid port");if(n.has(r))throw new Error(`port ${r} is already owned by a view`);return r}for(let r=Fd;r<=Hd;r++)if(!n.has(r)&&!await xr(r,{timeoutMs:250}))return r;throw new Error(`no free view port in ${Fd}-${Hd}`)}async function Rk(t){try{let{stdout:e}=await Jt(O,C("list-sessions","-F","#{session_name}"));return e.split(`
|
|
60
|
-
`).some(n=>n.trim()===Mn(t))}catch{return!1}}async function Sr(t){try{await Jt(O,C("kill-session","-t",Mn(t)))}catch{}}async function li(t){if(!await ee())throw new Error("host tmux server not running \u2014 cannot spawn a view server");let e=Mn(t.slug),n=t.serveCwd??t.cwd,r=Tt.dirname(process.execPath);await Jt(O,C("new-session","-d","-s",e,"-c",n)),await Jt(O,C("send-keys","-t",e,"-l",`export PORT=${t.port} PATH="${r}:$PATH"`)),await Jt(O,C("send-keys","-t",e,"Enter")),await Jt(O,C("send-keys","-t",e,"-l",t.cmd)),await Jt(O,C("send-keys","-t",e,"Enter"))}async function qd(t,e=12){try{let{stdout:n}=await Jt(O,C("capture-pane","-t",Mn(t),"-p"));return String(n||"").trimEnd().split(`
|
|
13
|
+
`,"utf8")}catch(e){console.warn(`[handoff-debug] failed to write ${kn}: ${e?.message||e}`)}}async function L(t={}){let e={at:new Date().toISOString(),ts:Date.now(),...t},n=[e.flow||"handoff",e.phase||e.step||"?",e.trackId?`track=${e.trackId}`:"",e.sessionName?`box=${e.sessionName}`:"",e.error?`ERR=${e.error}`:""].filter(Boolean).join(" \xB7 ");if(console.log(`[handoff-debug] ${n}`),e.sessionName&&eo())try{e.paneTail=(await pt(e.sessionName,{lines:35})).slice(-4e3)}catch{e.paneTail=null}(eo()||e.error||/^(checkpoint|spawn|hydrate|commit|done|summarize|clear|resume|ready|send|failed)/.test(String(e.phase||e.step||"")))&&await tw(e)}async function rl({limit:t=80}={}){try{return(await sr.readFile(kn,"utf8")).split(`
|
|
14
|
+
`).filter(Boolean).slice(-t).map(r=>{try{return JSON.parse(r)}catch{return{raw:r}}})}catch{return[]}}var ol={DEBUG_DIR:Ms,LOG_FILE:kn};var tt=nw(ew);function ar(t){return new Promise(e=>setTimeout(e,t))}function Ns(t,e){if(!e)return t;let n=o=>typeof o=="string"?o.split(e).join("<redacted>"):o,r=new Error(n(t?.message)||"command failed");return t?.code!==void 0&&(r.code=t.code),t?.stderr!==void 0&&(r.stderr=n(t.stderr)),r}var O=process.env.BOSTRAT_TMUX_BIN||"/usr/bin/tmux",Rt=process.env.BOSTRAT_TMUX_SOCKET||"/tmp/tmux-1000/default",ow=process.env.BOSTRAT_DEFAULT_CWD||process.env.HOME||"/",Bs=process.env.BOSTRAT_AGENT_CMD||"claude --dangerously-skip-permissions",Ds="bostrat-usage-probe",sw=new Set([Ds]);function js(t,e){let n=xn(t),r=/(^|\s)(--session-id|--resume|--continue|-r|-c)(\s|=|$)/.test(t);return!n||r?t:`${t} --session-id ${e}`}function xn(t){return/(^|\/|\s)claude(\s|$)/.test(String(t||""))}function Sn(t){return/(^|\/|\s)claude(\s|$)/.test(t)?"claude":/cursor/.test(t)?"cursor":/codex/.test(t)?"codex":/gemini/.test(t)?"gemini":"agent"}async function Ls(t,{loginShell:e=!1,failOpen:n=!1}={}){if(!t)return!1;let r=e?process.env.SHELL||"/bin/sh":"sh",o=e?"-lic":"-c";try{return await tt(r,[o,'command -v "$1" >/dev/null 2>&1',"_",t],{timeout:5e3}),!0}catch(s){return s?.code===1?!1:n}}async function Us(t){return Ls(String(t||"").trim().split(/\s+/)[0])}function C(...t){return["-S",Rt,...t]}async function pe(){if(!Ps.existsSync(Rt))return!1;try{return await tt(O,C("list-sessions")),!0}catch(t){let e=t&&t.stderr?String(t.stderr):"";return!/no server running/i.test(e)}}async function ir(){if(await pe())return!0;try{Ps.mkdirSync(Rt.replace(/\/[^/]+$/,""),{recursive:!0}),await tt(O,C("new-session","-d","-s","keeper","sleep 2147483647"))}catch{}return await pe()}var aw=/^[A-Za-z0-9_.-]{1,64}$/;function ut(t){return typeof t=="string"&&aw.test(t)}async function vn(){if(!Ps.existsSync(Rt))return[];try{let t="#{session_name}|#{session_windows}|#{session_attached}|#{session_activity}|#{pane_current_path}|#{@bostrat-session}|#{@bostrat-title}|#{@bostrat-sid}|#{@bostrat-agent}",{stdout:e}=await tt(O,C("list-sessions","-F",t)),n=Date.now();return e.trim().split(`
|
|
15
|
+
`).filter(Boolean).map(r=>{let[o,s,a,i,c,l,u,d,f]=r.split("|"),w=Number(i);return{name:o,title:u||null,windows:Number(s),attached:Number(a),lastActivityMs:Number.isFinite(w)?n-w*1e3:null,cwd:c||null,isBostrat:l==="1",sessionId:d||null,agent:f||(l==="1"?"claude":null)}}).filter(r=>!sw.has(r.name)&&!r.name.startsWith("bostrat-view-"))}catch{return[]}}async function G(t){return(await vn()).find(n=>n.name===t)||null}var iw=/^[A-Za-z0-9 ._-]+$/,cw=64;function lw(t){if(typeof t!="string")return!1;let e=t.trim();return e.length>=1&&e.length<=cw&&iw.test(e)}function Fs(t){return t.trim().replace(/\s+/g,"-").toLowerCase().replace(/[^A-Za-z0-9_.-]/g,"")}async function uw(t){try{let{stdout:e}=await tt(O,C("list-sessions","-F","#{session_name}"));return e.split(`
|
|
16
|
+
`).map(n=>n.trim()).includes(t)}catch{return!1}}async function Ue(t,e=ow,n={}){if(!lw(t))throw new Error("displayName must be 1\u201364 chars (letters, digits, space . _ -)");let r=t.trim(),o=Fs(r);if(!ut(o))throw new Error("resulting tmux session name is invalid");if(!await ir())throw new Error(`host tmux server not running on ${Rt} and auto-start failed \u2014 start one: ${O} -S ${Rt} new-session -d -s keeper "sleep 2147483647"`);if(await uw(o))throw new Error(`tmux session "${o}" already exists`);let s=n.agentCmd||Bs,a=n.agent||Sn(s),i=!!n.resumeSessionId,c=n.resumeSessionId||rw(),l=i?s:js(s,c),u=i?n.pinned===!0:/(^|\s)--session-id(\s|=)/.test(l),d=a==="claude"&&xn(s),f=n.firstRun===!0;f||(d&&await fe(e),a==="codex"&&await yn(e),a==="cursor"&&await bn(e));let w=d&&!f?Qr():null,m=w?["-e",`ANTHROPIC_API_KEY=${w}`]:[];try{await tt(O,C("new-session","-d","-s",o,"-c",e,...m))}catch(g){throw w?Ns(g,w):g}return await Fe(o),await tt(O,C("set-option","-t",o,"@bostrat-session","1")),await tt(O,C("set-option","-t",o,"@bostrat-title",r)),await tt(O,C("set-option","-t",o,"@bostrat-sid",c)),await tt(O,C("set-option","-t",o,"@bostrat-agent",a)),await tt(O,C("send-keys","-t",o,l,"Enter")),{name:o,title:r,cwd:e,sessionId:c,agent:a,pinned:u}}async function Wt(t){if(!ut(t))throw new Error("invalid session name");await tt(O,C("kill-session","-t",t))}async function sl(t,e){if(ut(t))try{let n=e?C("set-option","-t",t,"@bostrat-sid",e):C("set-option","-u","-t",t,"@bostrat-sid");await tt(O,n)}catch{}}async function pt(t,{lines:e=40}={}){if(!ut(t))return"";try{let n=C("capture-pane","-t",t,"-p");Number.isFinite(e)&&e>0&&n.push("-S",`-${Math.floor(e)}`);let{stdout:r}=await tt(O,n);return String(r||"")}catch{return""}}var dw=Number(process.env.BOSTRAT_PROMPT_CAPTURE_COLS)||100,fw=Number(process.env.BOSTRAT_PROMPT_CAPTURE_ROWS)||40,$t=Number(process.env.BOSTRAT_PROMPT_CAPTURE_LINES)||80,pw=Number(process.env.BOSTRAT_BOX_COLS)||120,mw=Number(process.env.BOSTRAT_BOX_ROWS)||40;async function hw(t){if(!ut(t))return 0;try{let{stdout:e}=await tt(O,C("list-clients","-t",t,"-F","1"));return String(e||"").trim().split(`
|
|
17
|
+
`).filter(Boolean).length}catch{return 0}}async function Fe(t,{cols:e=pw,rows:n=mw}={}){if(ut(t)&&!await hw(t))try{let r=await il(t);if(r.cols===e&&r.rows===n)return;await tt(O,C("resize-window","-t",t,"-x",String(e),"-y",String(n))),await tt(O,C("set-window-option","-t",t,"window-size","latest")),await ar(80)}catch{}}async function al(t){if(!ut(t))return 0;try{let{stdout:e}=await tt(O,C("display-message","-t",t,"-p","#{pane_pid}"));return Number(String(e||"").trim())||0}catch{return 0}}async function il(t){if(!ut(t))return{cols:0,rows:0};try{let{stdout:e}=await tt(O,C("display-message","-t",t,"-p","#{window_width} #{window_height}")),[n,r]=String(e||"").trim().split(/\s+/).map(o=>Number(o)||0);return{cols:n,rows:r}}catch{return{cols:0,rows:0}}}async function no(t,{cols:e=dw,rows:n=fw}={}){if(ut(t))try{let r=await il(t),o=Math.max(r.cols,e),s=Math.max(r.rows,n);(o>r.cols||s>r.rows)&&(await tt(O,C("resize-window","-t",t,"-x",String(o),"-y",String(s))),await ar(80))}catch{}}async function me(t){return await no(t),pt(t,{lines:$t})}var ww=new Set(["Up","Down","Left","Right","Space","Enter","Tab","BTab","Escape","BSpace"]);async function gt(t,e,{settleMs:n=40,kind:r}={}){if(!ut(t))throw new Error("invalid session name");let o=(Array.isArray(e)?e:[e]).filter(s=>ww.has(s));for(let s=0;s<o.length;s++)await tt(O,C("send-keys","-t",t,o[s])),n>0&&s<o.length-1&&await ar(n);return L({flow:"tmux",phase:"send-keys",sessionName:t,kind:r||"keys",keys:o.join(" ")}).catch(()=>{}),o.length}async function ro(t,e,{kind:n}={}){if(!ut(t))throw new Error("invalid session name");let r=Math.max(0,Math.floor(Number(e)||0));return r>0&&await tt(O,C("send-keys","-t",t,"-N",String(r),"BSpace")),L({flow:"tmux",phase:"send-keys",sessionName:t,kind:n||"wipe",keys:`BSpace x${r}`}).catch(()=>{}),r}async function oo(t,e,{perCharDelayMs:n=120,kind:r}={}){if(!ut(t))throw new Error("invalid session name");let o=[...String(e)];for(let s of o)await tt(O,C("send-keys","-t",t,"-l",s)),n>0&&await ar(n);L({flow:"tmux",phase:"type",sessionName:t,kind:r||"type",textLen:o.length,textHead:String(e).slice(0,120)}).catch(()=>{})}async function yt(t,e,{enter:n=!0,submitDelayMs:r=0,kind:o}={}){if(!ut(t))throw new Error("invalid session name");if(typeof e=="string"&&e.length>0&&await tt(O,C("send-keys","-t",t,"-l",e)),n){let s=Number(r);Number.isFinite(s)&&s>0&&await ar(s),await tt(O,C("send-keys","-t",t,"Enter"))}L({flow:"tmux",phase:"send",sessionName:t,kind:o||"text",textLen:typeof e=="string"?e.length:0,textHead:typeof e=="string"?e.slice(0,120):"",enter:n,submitDelayMs:r}).catch(()=>{})}import gw from"node-pty";async function ll(t,e,n={}){if(!ut(e)){so(t,`invalid session name: ${e}`),t.close(1008,"invalid session name");return}if(!await pe()){so(t,`host tmux server not running on ${Rt}`),t.close(1011,"tmux server unavailable");return}if(!await G(e)){so(t,`session "${e}" not found`),t.close(1008,"session not found");return}let r=cl(n.cols,120,ul),o=cl(n.rows,32,dl),s;try{s=gw.spawn(O,["-S",Rt,"attach-session","-d","-t",e],{name:"xterm-256color",cols:r,rows:o,cwd:process.env.HOME||"/",env:{...process.env,TERM:"xterm-256color"}})}catch(a){so(t,`failed to spawn tmux attach: ${a.message}`),t.close(1011,"spawn failed");return}s.onData(a=>{t.readyState===t.OPEN&&Hs(t,{type:"data",data:a})}),s.onExit(({exitCode:a,signal:i})=>{t.readyState===t.OPEN&&(Hs(t,{type:"exit",code:a??null,signal:i??null}),t.close(1e3,"tmux exited"))}),t.on("message",a=>{let i;try{i=JSON.parse(a.toString())}catch{return}if(!(!i||typeof i!="object")){if(i.type==="data"&&typeof i.data=="string")s.write(i.data);else if(i.type==="resize"&&yw(i.cols,i.rows))try{s.resize(Math.min(500,i.cols|0),Math.min(500,i.rows|0))}catch{}}}),t.on("close",()=>{try{s.kill()}catch{}Fe(e).catch(()=>{})})}var ul=20,dl=6;function yw(t,e){return Number.isFinite(Number(t))&&Number.isFinite(Number(e))&&Number(t)>=ul&&Number(e)>=dl}function cl(t,e,n){let r=Number(t);return!Number.isFinite(r)||r<n?e:Math.min(500,r|0)}function Hs(t,e){try{t.send(JSON.stringify(e))}catch{}}function so(t,e){Hs(t,{type:"error",message:e})}function fl(t){let e=typeof t=="string"&&t?t:"/",n=e.indexOf("?"),r=n===-1?e:e.slice(0,n),o=n===-1?"":e.slice(n+1),s=r.match(/^\/api\/attach\/([^/]+)$/);if(!s)return{ok:!1,status:404};let a;try{a=decodeURIComponent(s[1])}catch{return{ok:!1,status:400}}let i=new URLSearchParams(o);return{ok:!0,sessionName:a,cols:Number(i.get("cols"))||void 0,rows:Number(i.get("rows"))||void 0}}import cr from"node:fs";import bw from"node:fs/promises";import Tn from"node:path";import kw from"node:os";var pl={"claude-opus-4-8":1e6,"claude-opus-4-7":1e6,"claude-opus-4-6":1e6,"claude-sonnet-4-6":1e6,"claude-haiku-4-5":2e5,"claude-fable-5":1e6},xw=2e5,Ws=process.env.BOSTRAT_CLAUDE_PROJECTS||Tn.join(kw.homedir(),".claude","projects");function qs(t){return t.replace(/[^A-Za-z0-9]/g,"-")}function Gs(t){let e=Tn.join(Ws,qs(t)),n;try{n=cr.readdirSync(e).filter(o=>o.endsWith(".jsonl"))}catch{return null}let r=null;for(let o of n){let s=Tn.join(e,o);try{let{mtimeMs:a}=cr.statSync(s);(!r||a>r.mtimeMs)&&(r={full:s,mtimeMs:a})}catch{}}return r?r.full:null}function ee(t,e){if(!e)return null;let n=Tn.join(Ws,qs(t),`${e}.jsonl`);return cr.existsSync(n)?n:null}function ml(t){if(!t)return[];let e=Tn.join(Ws,qs(t)),n;try{n=cr.readdirSync(e).filter(o=>o.endsWith(".jsonl"))}catch{return[]}let r=[];for(let o of n)try{let s=cr.statSync(Tn.join(e,o));r.push({id:o.slice(0,-6),birthMs:s.birthtimeMs||s.ctimeMs||s.mtimeMs})}catch{}return r}async function vt(t,e=131072){let n=await bw.open(t,"r");try{let{size:r}=await n.stat(),o=Math.max(0,r-e),s=r-o;if(s===0)return"";let a=Buffer.alloc(s);return await n.read(a,0,s,o),a.toString("utf8")}finally{await n.close()}}function Sw(t){let e=t.split(`
|
|
18
|
+
`);for(let n=e.length-1;n>=0;n--){let r=e[n].trim();if(!r||r[0]!=="{")continue;let o;try{o=JSON.parse(r)}catch{continue}let s=o?.message?.usage;if(!s)continue;let a=(s.input_tokens||0)+(s.cache_creation_input_tokens||0)+(s.cache_read_input_tokens||0);if(a>0)return{occupancy:a,model:o.message.model||o.model||null}}return null}async function lr(t,e=null,{fallbackNewest:n=!0}={}){if(!t)return null;let r,o;if(e){if(r=ee(t,e),o=!0,!r)return null}else if(n){if(r=Gs(t),o=!1,!r)return null}else return null;let s;try{s=Sw(await vt(r))}catch{return null}if(!s)return null;let a=Object.prototype.hasOwnProperty.call(pl,s.model),i=pl[s.model]||xw;return{source:"transcript",exact:o,model:s.model,windowKnown:a,window:i,occupancy:s.occupancy,pct:Math.min(1,s.occupancy/i),transcript:r}}function vw(t,e){return!t||!e?null:ee(t,e)}function Tw(t){return typeof t=="string"?t.trim():Array.isArray(t)?t.filter(e=>e&&e.type==="text"&&typeof e.text=="string").map(e=>e.text).join(`
|
|
19
|
+
`).trim():""}function Aw(t){if(!Array.isArray(t))return null;let e=t.filter(n=>n&&n.type==="tool_use").map(n=>n.name).filter(Boolean);return e.length?e:null}function _w(t){return Array.isArray(t)&&t.length>0&&t.every(e=>e&&e.type==="tool_result")}function Ks(t){let e=[],n=t?.message?.content;if(!Array.isArray(n))return e;for(let r of n)if(r){if(r.type==="image"&&r.source?.type==="base64"&&r.source.data)e.push({mediaType:r.source.media_type||"image/png",data:r.source.data,toolUseId:null});else if(r.type==="tool_result"&&Array.isArray(r.content))for(let o of r.content)o?.type==="image"&&o.source?.type==="base64"&&o.source.data&&e.push({mediaType:o.source.media_type||"image/png",data:o.source.data,toolUseId:r.tool_use_id||null})}return e}function hl(t,e){if(!t.uuid)return null;let n=Ks(t).map((r,o)=>({uuid:t.uuid,index:o,mediaType:r.mediaType,name:r.toolUseId&&e.get(r.toolUseId)||null}));return n.length?n:null}var Ew=/\.(png|jpe?g|gif|webp|bmp|svg|tiff?|avif|heic|heif|ico)$/i;async function Vs(t,{maxBytes:e=524288,limit:n=80}={}){let r;try{r=await vt(t,e)}catch{return[]}let o=[],s=new Map;for(let a of r.split(`
|
|
20
|
+
`)){let i=a.trim();if(!i||i[0]!=="{")continue;let c;try{c=JSON.parse(i)}catch{continue}if(c.type==="attachment"){let m=c.attachment;m?.type==="queued_command"&&m.commandMode==="task-notification"&&typeof m.prompt=="string"&&m.prompt.trim()&&o.push({role:"user",text:m.prompt.trim(),ts:c.timestamp?Date.parse(c.timestamp):null,model:null});continue}let l=c.message;if(!l)continue;let u=l.role||c.type;if(u!=="user"&&u!=="assistant")continue;if(u==="assistant"&&Array.isArray(l.content))for(let m of l.content){let g=m?.type==="tool_use"?m.input?.file_path||m.input?.path:null;typeof g=="string"&&Ew.test(g)&&s.set(m.id,g.split("/").pop())}if(_w(l.content)){let m=hl(c,s);m&&o.push({role:"assistant",text:"",images:m,ts:c.timestamp?Date.parse(c.timestamp):null,model:null});continue}let d=Tw(l.content),f=u==="assistant"?Aw(l.content):null,w=u==="user"?hl(c,s):null;!d&&!f&&!w||o.push({role:u,text:d,tools:f||void 0,images:w||void 0,ts:c.timestamp?Date.parse(c.timestamp):null,model:l.model||null})}return o.slice(-n)}var Iw=(t,e)=>t.length>e?`${t.slice(0,e).trimEnd()}\u2026`:t;async function wl(t,e,{turns:n=16}={}){let r=vw(t,e);if(!r)return"";let o=await Vs(r,{limit:n});return o.length?o.map(s=>{let a=s.role==="user"?"User":"Lead agent",i=s.tools?`_(also ran: ${s.tools.join(", ")})_`:"",c=s.images?.length?"(viewed an image)":"(took an action)";return`**${a}:** ${Iw(s.text||c,700)} ${i}`.trim()}).join(`
|
|
21
|
+
|
|
22
|
+
`):""}import Kw from"node:fs";import na from"node:fs/promises";import uo from"node:path";import Rw from"node:fs/promises";import{execFile as $w}from"node:child_process";import{promisify as Ow}from"node:util";var Js=Ow($w),Cw=Number(process.env.BOSTRAT_CODEX_UPDATE_TIMEOUT_MS)||5*6e4,gl=new Set,yl=Promise.resolve(),zs=null;function kl(t){zs=typeof t=="function"?t:null}function Mw(t){let e=String(t||""),n=e.indexOf("/lib/node_modules/");return n>0?e.slice(0,n):null}function xl({sessionName:t,from:e,to:n}={}){let r=String(n||"").trim();!r||gl.has(r)||(gl.add(r),yl=yl.then(()=>Pw({sessionName:t,from:e,to:r})).catch(()=>{}))}async function Pw({sessionName:t,from:e,to:n}){await L({flow:"codex-update",phase:"start",sessionName:t,from:e,to:n}).catch(()=>{});try{let r=(await Js("/bin/sh",["-c","command -v codex"])).stdout.trim();if(!r)throw new Error("codex not on PATH");let o=await Rw.realpath(r),s=Mw(o),a=s?{...process.env,PATH:`${s}/bin:${process.env.PATH||""}`}:process.env;await Js("npm",["install","-g","@openai/codex"],{env:a,timeout:Cw});let i=(await Js(r,["--version"])).stdout.trim().replace(/^codex-cli\s*/,"")||n;await L({flow:"codex-update",phase:"done",sessionName:t,from:e,to:n,version:i}).catch(()=>{}),await bl({sessionName:t,from:e,to:n,version:i,status:"updated"})}catch(r){let o=String(r?.message||r).slice(0,300);await L({flow:"codex-update",phase:"failed",sessionName:t,from:e,to:n,error:o}).catch(()=>{}),await bl({sessionName:t,from:e,to:n,status:"failed",error:o})}}async function bl(t){if(zs)try{await zs(t)}catch{}}var Nw=()=>Number(process.env.BOSTRAT_AGENT_READY_TIMEOUT_MS)||9e4,Xs=Number(process.env.BOSTRAT_AGENT_READY_POLL_MS)||500,ur=Number(process.env.BOSTRAT_AGENT_READY_SETTLE_MS)||800,Ot={claude:{up:/shift\+tab to cycle|⏵⏵ bypass permissions on|⏸ manual mode on/,busy:/esc to interrupt/,auth:/Select login method:|Paste code here if prompted|OAuth error:|Choose the text style that looks best|Quick safety check: Is this a project you created/},codex:{up:/(^|\n)\s*›/,busy:/esc to interrupt|\bWorking \(/,block:/Update available![\s\S]*\b(?:Update now|Skip until next version)\b[\s\S]*Press enter to continue/,auth:/Sign in with ChatGPT[\s\S]*Press enter to continue|Finish signing in via your browser|Paste or type your API key/},cursor:{up:/Run Everything|Add a follow-up|Plan, search, build anything/,busy:/ctrl\+c to stop|^\s*[⠁-⣿]+\s+\S/m,block:/Workspace Trust Required|Trust this workspace|\[a\]\s*Trust|Trusting workspace/}},Bw=/Trust this workspace|\[a\]\s*Trust/,Dw=/Update available!\s*([\d][\w.-]*)\s*->\s*([\d][\w.-]*)/;function jw(t){let e=String(t||"");if(!Ot.codex.block.test(e))return null;let n=e.match(Dw);return{from:n?.[1]||null,to:n?.[2]||null}}function he(t){return new Promise(e=>setTimeout(e,t))}var Lw=()=>Number(process.env.BOSTRAT_GATE_RESEND_MS)||3e3,ao=new Map;function Sl(t,e){let n=Date.now(),r=Lw();for(let[s,a]of ao)n-a>=r&&ao.delete(s);let o=`${t}::${e}`;return ao.has(o)?!1:(ao.set(o,n),!0)}var Uw=68,Fw=/^\s*[─━]{10,}\s*$/;function Hw(t){let e=0;for(let n of String(t).split(`
|
|
23
|
+
`))Fw.test(n)&&(e=Math.max(e,n.trimEnd().length));return e}function Ys(t,e){let n=Ot[t];if(!n?.busy)return"idle";if(Zs(t,e))return"unknown";if(n.up&&!n.up.test(e))return"idle";if(n.busy.test(e))return"busy";let r=Hw(e);return t==="claude"&&r>0&&r<Uw?"unknown":"idle"}var Ww=/\bWaiting for \d+\b[^\n]*\bto finish\b|\/workflows to (?:stop|monitor)\b/;function Tl(t,e){return t==="claude"&&Ww.test(String(e||""))}function dr(t,e){return Ys(t,e)==="busy"}function io(t,e){return Ys(t,e)!=="idle"}function co(t,e){let n=Ot[t];return n?.up?n.up.test(e):!0}function qw(t,e){let n=Ot[t];return n?Zs(t,e)?!1:n.up.test(e)&&Ys(t,e)==="idle":!0}function Zs(t,e){let n=Ot[t];return n?n.auth?.test(e)&&!n.up.test(e)?!0:n.block?.test(e)?t==="cursor"?!n.up.test(e):!0:!1:!1}async function vl(t,e,n){let r=Date.now()+n,o=0;for(await Fe(t).catch(()=>{});;){let s=await pt(t);if(qw(e,s)){await he(ur);return}if(e==="cursor"&&Bw.test(s)&&!Ot.cursor.up.test(s)&&o<3){if(!Sl(t,"cursor-trust")){await he(Xs);continue}o+=1,await L({flow:"agent-ready",phase:"gate-accept",sessionName:t,agent:e,attempt:o}).catch(()=>{}),await yt(t,"a",{enter:!1,kind:"cursor-trust-accept"}),await he(ur);continue}if(e==="codex"&&o<3){let a=jw(s);if(a){if(!Sl(t,`codex-update:${a.from}->${a.to}`)){await he(Xs);continue}o+=1,await L({flow:"agent-ready",phase:"gate-accept",sessionName:t,agent:e,attempt:o,update:`${a.from} -> ${a.to}`}).catch(()=>{}),await yt(t,"3",{enter:!1,kind:"codex-update-skip"}),xl({sessionName:t,from:a.from,to:a.to}),await he(ur);continue}}if(Date.now()>r){if(Zs(e,s)){let a=`${e} is blocked on an interactive prompt`;throw e==="codex"?a=Ot.codex.auth.test(s)?"codex isn't signed in on this box \u2014 answer the Sign in card in chat (Device Code works from any device) or finish signing in from the Terminal tab, then send again":"codex is stuck on its update prompt (auto-skip didn't clear it) \u2014 open the terminal and choose Skip, then send again":e==="claude"&&Ot.claude.auth.test(s)&&(a="claude isn't signed in on this box \u2014 answer the setup cards in chat (or finish signing in from the Terminal tab), then send again"),L({flow:"agent-ready",phase:"blocked",sessionName:t,agent:e,timeoutMs:n,gateAccepts:o,error:a}).catch(()=>{}),new Error(a)}console.warn(`[agent-ready] ${e} readiness unconfirmed in ${t} after ${n}ms \u2014 driving the box anyway.`),L({flow:"agent-ready",phase:"timeout",sessionName:t,agent:e,timeoutMs:n,gateAccepts:o,paneBlocked:!!Ot[e]?.block?.test(s),paneUp:!!Ot[e]?.up?.test(s),paneBusy:!!Ot[e]?.busy?.test(s)}).catch(()=>{}),await he(ur);return}await he(Xs)}}function Al({agent:t,agentCmd:e}={}){let n=e?Sn(e):Sn(t||"");return{label:n,realClaude:e?xn(e):n==="claude",realCodex:e?/codex/.test(e)&&!/fake-codex|fake-lead/.test(e):n==="codex",realCursor:e?/cursor/.test(e)&&!/fake-cursor|fake-box/.test(e):n==="cursor"}}function lo({cwd:t,sessionName:e,agent:n,agentCmd:r}={}){let{realCodex:o,realCursor:s}=Al({agent:n,agentCmd:r});return!e||!o&&!s?null:we({cwd:t,sessionName:e,agent:n,agentCmd:r}).catch(()=>{})}async function we({cwd:t,sessionId:e,sessionName:n,agent:r,agentCmd:o,timeoutMs:s=Nw()}={}){let{label:a,realClaude:i,realCodex:c,realCursor:l}=Al({agent:r,agentCmd:o});if(i&&t&&await fe(t),c&&t&&await yn(t),l&&t&&await bn(t),(i||c||l)&&n){await vl(n,a,s);return}let u=Ot[r]?r:Ot[a]?a:null;if(process.env.BOSTRAT_FAKE_POLL_READY==="1"&&n&&u){await vl(n,u,s);return}await he(ur)}var Gw=(()=>{let t=Number(process.env.BOSTRAT_CODEX_SUBMIT_SETTLE_MS??120);return Number.isFinite(t)&&t>=0?t:120})();function ne(t){return t==="codex"?{enter:!0,submitDelayMs:Gw}:{enter:!0}}var _l=".bostrat/handoff",El="latest.md",Il="Checkpoint: write a work-in-progress handoff",Rl="HANDOFF WRITTEN",$l="Resume from .bostrat/handoff/latest.md",Vw="/clear",Jw=t=>`${Il} to the EXACT absolute path ${t} (write it there specifically \u2014 NOT into any sub-project or other directory; create the directory if needed, overwrite the file) so a FRESH Claude context with none of this conversation can resume seamlessly. Use these sections: Objective; Done (with file paths); In progress (exact state); Next steps (ordered); Key files & commands; Open decisions/blockers; Resume instruction (one line). Be concrete and self-contained \u2014 assume the reader has none of this chat. When the file is written, reply only with ${Rl} and take no further action.`,zw=`${$l}: read it, restate the Objective and the single next step in one line, then continue the work from 'Next steps'.`;function Xw(t,e){let n=(e||"").trim();return n?t==="assistant"?n===Rl:n.startsWith(Il)||n===Vw||n.startsWith($l):!1}var Yw=6e5;function Ol(t){let e=[];for(let r of t?.events||[]){if(r.type!=="lead-handoff"||!r.from)continue;let o=r.at??Date.now(),s=r.handoffStartedAt??o-Yw;e.push({box:r.from,from:s,to:o}),r.to&&r.to!==r.from&&e.push({box:r.to,from:s,to:o})}let n=t?.handoff;return n?.fromBox&&n.startedAt&&n.finishedAt&&e.push({box:n.fromBox,from:n.startedAt,to:n.finishedAt}),n?.toBox&&n.startedAt&&n.finishedAt&&e.push({box:n.toBox,from:n.startedAt,to:n.finishedAt}),e}function Cl(t,e){let n=[];for(let r of t?.boxes||[]){let o=e?.(r.name);o?.startedAt&&n.push({box:r.name,from:o.startedAt,to:o.finishedAt||Date.now()+1e12})}return n}function Ml(t,{windows:e=[]}={}){if(Xw(t.role,t.text))return!0;if(t.role!=="assistant"||!t.ts)return!1;for(let n of e)if(t.box===n.box&&t.ts>=n.from&&t.ts<=n.to)return!0;return!1}var Zw=3500,Qw=3e5,Qs=new Map;function fo(t){return Qs.get(t)||null}async function Pl(t){let e=Qs.get(t);if(e&&!e.finishedAt)throw new Error("a handoff is already running for this session");let n=await G(t);if(!n)throw new Error(`session "${t}" not found`);if(!n.cwd)throw new Error(`can't resolve cwd for "${t}"`);let r={name:t,step:"summarize",startedAt:Date.now(),finishedAt:null,error:null,file:uo.join(n.cwd,_l,El)};return Qs.set(t,r),tg(r,n.cwd).catch(o=>{r.error=o?.message||String(o),r.finishedAt=Date.now()}),r}async function tg(t,e){t.step="summarize",await Nl(t.name,e),t.step="clear";let n=await G(t.name);await ea(t.name,n,"/clear"),await Dl(Zw),t.step="resume",await ea(t.name,n,zw),t.step="done",t.finishedAt=Date.now()}async function Nl(t,e){let n=uo.join(e,_l),r=uo.join(n,El);await na.mkdir(n,{recursive:!0});let o=await ta(r),s=await G(t);return await L({flow:"checkpoint",phase:"summarize-start",sessionName:t,cwd:e,file:r,beforeMtime:o}),await ea(t,s,Jw(r)),await eg(r,o,Qw),await L({flow:"checkpoint",phase:"summarize-done",sessionName:t,file:r,afterMtime:await ta(r)}),await ng(n,r),r}async function Bl(t){let e=await G(t);if(!e)throw new Error(`session "${t}" not found`);if(!e.cwd)throw new Error(`can't resolve cwd for "${t}"`);return Nl(t,e.cwd)}async function ta(t){try{return(await na.stat(t)).mtimeMs}catch{return 0}}async function eg(t,e,n){let r=Date.now()+n;for(;;){if(await ta(t)>e)return;if(Date.now()>r)throw new Error(`timed out waiting for the handoff file to be written (${t})`);await Dl(2e3)}}async function ng(t,e){try{if(!Kw.existsSync(e))return;let n=new Date().toISOString().replace(/[:.]/g,"-");await na.copyFile(e,uo.join(t,`${n}.md`))}catch{}}function Dl(t){return new Promise(e=>setTimeout(e,t))}async function ea(t,e,n){if(e){let o=e.agent==="claude"?process.env.BOSTRAT_AGENT_CMD||"claude":e.agent==="cursor"?process.env.BOSTRAT_CURSOR_CMD||"cursor-agent":e.agent==="codex"?process.env.BOSTRAT_CODEX_CMD||"codex":"";await we({cwd:e.cwd,sessionId:e.sessionId,sessionName:t,agent:e.agent,agentCmd:o})}let r=await G(t);if(!r)throw new Error(`session "${t}" disappeared before the orchestration prompt could be sent`);await yt(t,n,{kind:"orchestration-drive",...ne(r.agent)})}import P from"node:fs/promises";import U from"node:path";import{randomUUID as ts}from"node:crypto";import{execFile as jx}from"node:child_process";import{promisify as Lx}from"node:util";import ra from"node:fs/promises";import rg from"node:path";var og=0;async function qt(t,e,{mode:n}={}){await ra.mkdir(rg.dirname(t),{recursive:!0});let r=`${t}.${process.pid}.${++og}.tmp`;await ra.writeFile(r,JSON.stringify(e,null,2)+`
|
|
24
|
+
`,n!=null?{mode:n}:void 0),await ra.rename(r,t)}import{execFile as sg}from"node:child_process";import{promisify as ag}from"node:util";import{randomUUID as ig}from"node:crypto";var He=ag(sg),po=t=>new Promise(e=>setTimeout(e,t)),cg=Number(process.env.BOSTRAT_SWAP_EXIT_TIMEOUT_MS)||15e3,jl=Number(process.env.BOSTRAT_SWAP_SHELL_TIMEOUT_MS)||8e3,lg=250,ug=400,Ll={claude:{escape:!0,wipe:!0,text:"/exit"},codex:{escape:!0,wipe:!0,text:"/quit"},cursor:{escape:!0,wipe:!0,text:"/quit"},default:{escape:!1,wipe:!1,text:"exit"}};async function Fl(t){let e=await al(t);if(!e)return!1;try{return await He("pgrep",["-P",String(e)]),!1}catch(n){return n?.code===1}}async function oa(t,e){let n=Date.now()+e;for(;;){if(await Fl(t))return!0;if(Date.now()>n)return!1;await po(lg)}}async function dg(t,e){let n=Ll[e]||Ll.default;n.escape&&(await gt(t,["Escape"],{kind:"swap-exit"}),await po(150)),n.wipe&&await ro(t,400,{kind:"swap-exit"}),await oo(t,n.text,{perCharDelayMs:90,kind:"swap-exit"});let r=ne(e).submitDelayMs||0;await po(Math.max(r,250)),await gt(t,["Enter"],{kind:"swap-exit"})}async function fg(t,e){try{let{stdout:n}=await He(O,C("show-environment","-t",t,e));return!String(n||"").trimStart().startsWith("-")}catch{return!1}}async function Ul(t){await He(O,C("respawn-pane","-k","-t",t))}async function sa(t,e={}){if(!ut(t))throw new Error("invalid session name");let n=String(e.agentCmd||"").trim();if(!n)throw new Error("agentCmd is required");let r=await G(t);if(!r)throw new Error(`session "${t}" not found`);if(!r.cwd)throw new Error(`can't resolve cwd for "${t}"`);let o=r.agent||"agent",s=e.agent||Sn(n),a=Number(e.exitTimeoutMs)||cg;await L({flow:"swap",phase:"start",sessionName:t,from:o,to:s,cwd:r.cwd});let i=!1;if(!await Fl(t)&&(await dg(t,o),!await oa(t,a)&&(i=!0,await L({flow:"swap",phase:"respawn-forced",sessionName:t,from:o}),await Ul(t),!await oa(t,jl))))throw new Error(`pane shell did not come back after respawn-pane in "${t}"`);let c=s==="claude"&&xn(n),l=c?Qr():null,u=await fg(t,"ANTHROPIC_API_KEY"),d=!1;try{l?(await He(O,C("set-environment","-t",t,"ANTHROPIC_API_KEY",l)),d=!0):u&&(await He(O,C("set-environment","-u","-t",t,"ANTHROPIC_API_KEY")),d=!0)}catch(x){throw l?Ns(x,l):x}if(d&&!i&&(await Ul(t),!await oa(t,jl)))throw new Error(`pane shell did not come back after env respawn in "${t}"`);let f=!!e.resumeSessionId,w=e.resumeSessionId||ig(),m=f?n:js(n,w),g=f?e.pinned===!0:/(^|\s)--session-id(\s|=)/.test(m);return c&&await fe(r.cwd),s==="codex"&&await yn(r.cwd),s==="cursor"&&await bn(r.cwd),await Fe(t),await He(O,C("set-option","-t",t,"@bostrat-sid",w)),await He(O,C("set-option","-t",t,"@bostrat-agent",s)),await po(ug),await yt(t,m,{kind:"swap-launch"}),await L({flow:"swap",phase:"done",sessionName:t,from:o,to:s,sid:w,forced:i,envRespawned:d}),{name:t,cwd:r.cwd,sessionId:w,agent:s,pinned:g,forced:i,envRespawned:d}}import vg from"node:fs";import Wl from"node:fs/promises";import fr from"node:path";import Tg from"node:os";import{fileURLToPath as Ag}from"node:url";import{execFile as _g}from"node:child_process";import{promisify as Eg}from"node:util";import ia from"node:fs/promises";import ge from"node:path";import{execFile as pg}from"node:child_process";import{promisify as mg}from"node:util";var hg=mg(pg),Hl=process.env.HOME||"/home/globo",wg=ge.join(process.env.BOSTRAT_STATE_DIR||ge.join(Hl,".bostrat"),"repos"),gg=[...(process.env.BOSTRAT_REPOS_ROOT||ge.join(Hl,"Documents")).split(","),wg].map(t=>t.trim()).filter(Boolean),yg=Number(process.env.BOSTRAT_REPOS_DEPTH)||3,bg=new Set(["node_modules",".git",".cache","dist","build",".next","vendor","__pycache__","venv",".venv","target"]);async function kg(t){try{return(await ia.stat(ge.join(t,".git"))).isDirectory()?"repo":"worktree"}catch{return null}}async function xg(t){let e=i=>ia.readFile(ge.join(t,".git",i),"utf8").then(c=>c.trim()).catch(()=>null),r=(await e("refs/remotes/origin/HEAD"))?.match(/^ref:\s*refs\/remotes\/origin\/(.+)/);if(r)return r[1].trim();let o;for(let i of["main","master"])if(await e(`refs/heads/${i}`)||(o??=await e("packed-refs")||"",new RegExp(`^[0-9a-f]{40} refs/heads/${i}$`,"m").test(o)))return i;let a=(await e("HEAD"))?.match(/^ref:\s*refs\/heads\/(.+)/);return a?a[1].trim():null}async function re(){let t=new Map;async function e(n,r){if(t.has(n))return;let o=await kg(n);if(o==="worktree")return;if(o==="repo"){t.set(n,{path:n,name:ge.basename(n),branch:await xg(n)});return}if(r>=yg)return;let s;try{s=await ia.readdir(n,{withFileTypes:!0})}catch{return}for(let a of s)!a.isDirectory()||a.name.startsWith(".")||bg.has(a.name)||await e(ge.join(n,a.name),r+1)}for(let n of gg)await e(ge.resolve(n),0);return[...t.values()].sort((n,r)=>n.name.localeCompare(r.name))}function Sg(t){let e=String(t||"").trim().match(/github\.com[:/]+([^/\s]+)\/([^/\s]+?)(?:\.git)?\/?$/i);return e?`${e[1]}/${e[2]}`:null}var aa=new Map;async function Y(t){if(!t)return null;if(aa.has(t))return aa.get(t);let e=null;try{let{stdout:n}=await hg("git",["-C",t,"config","--get","remote.origin.url"],{timeout:5e3});e=Sg(n)}catch{e=null}return aa.set(t,e),e}var ql=Eg(_g),Ig=process.env.BOSTRAT_STATE_DIR||fr.join(process.env.HOME||Tg.tmpdir(),".bostrat"),Rg=fr.join(Ig,"repos"),Gl=/^[^/\s]+\/[^/\s]+$/,$g=5*6e4,Og=6e4,Cg=10*6e4,Kl=fetch,ca=new Map,We=null;function la(t=process.env){let e=t.BOSTRAT_PLANE_URL||"";if(!e)return null;try{let n=new URL(e);return n.protocol=n.protocol==="ws:"?"http:":"https:",n.origin}catch{return null}}function mo(t=process.env){return!!(la(t)&&t.BOSTRAT_PLANE_TOKEN)}var Vl=t=>({authorization:`Bearer ${t.BOSTRAT_PLANE_TOKEN||""}`}),An=(t,e)=>Object.assign(new Error(t),{status:e});async function ho(t,{env:e=process.env}={}){let n=String(t||"").toLowerCase();if(!Gl.test(n))throw An("repo must be owner/name",400);let r=ca.get(n);if(r&&r.expiresAt-Date.now()>$g)return r;if(!mo(e))throw An("node is not plane-linked \u2014 GitHub auth unavailable",503);let o;try{o=await Kl(`${la(e)}/v1/github/token?repo=${encodeURIComponent(t)}`,{headers:Vl(e)})}catch(i){if(r&&r.expiresAt>Date.now())return r;throw An(`plane unreachable \u2014 GitHub auth unavailable (${i?.message||i})`,503)}let s=await o.json().catch(()=>({}));if(!o.ok){if(o.status>=500&&r&&r.expiresAt>Date.now())return r;throw An(s?.error||`plane replied ${o.status}`,o.status)}let a={token:s.token,expiresAt:Number(s.expiresAt)||Date.now()+55*6e4,username:s.username??null,installationId:s.installationId??null};return ca.set(n,a),a}async function pr({env:t=process.env,force:e=!1}={}){if(!mo(t))return{configured:!1,repos:[],username:null};if(!e&&We&&Date.now()-We.at<Og)return We.data;try{let n=await Kl(`${la(t)}/v1/github/repos`,{headers:Vl(t)});if(!n.ok)throw new Error(`plane replied ${n.status}`);let r=await n.json();return We={at:Date.now(),data:r},r}catch(n){return We?We.data:{configured:!0,repos:[],username:null,error:n?.message||String(n)}}}function Jl(){ca.clear(),We=null}async function Gt({cwd:t=null,slug:e=null,env:n=process.env}={}){try{let r=e||(t?await Y(t):null);if(!r)return{};let{token:o}=await ho(r,{env:n});return o?{GH_TOKEN:o}:{}}catch{return{}}}async function ua({env:t=process.env}={}){return(await pr({env:t})).username??null}var Mg=()=>Ag(new URL("../bin/bostrat-git-credential",import.meta.url)),Pg=(t=process.env)=>`http://127.0.0.1:${Number(t.PORT)||3362}`;function Ng(t){let[e,n]=String(t).split("/");return fr.join(Rg,e,n)}async function da(t,{env:e=process.env}={}){let n=(...r)=>ql("git",["-C",t,...r],{timeout:1e4});await n("config","--replace-all","credential.helper",""),await n("config","--add","credential.helper",`!"${Mg()}" ${Pg(e)}`),await n("config","credential.useHttpPath","true")}async function fa(t,{env:e=process.env,baseUrl:n="https://github.com"}={}){if(!Gl.test(String(t||"")))throw An(`bad repo slug "${t}"`,400);let r=Ng(t);if(vg.existsSync(fr.join(r,".git")))return r;let{token:o}=await ho(t,{env:e});await Wl.mkdir(fr.dirname(r),{recursive:!0});let s="!f() { echo username=x-access-token; echo password=$BOSTRAT_GIT_TOKEN; }; f";try{await ql("git",["-c","credential.helper=","-c",`credential.helper=${s}`,"clone","--no-tags","-q",`${n}/${t}.git`,r],{timeout:Cg,env:{...e,BOSTRAT_GIT_TOKEN:o,GIT_TERMINAL_PROMPT:"0"}})}catch(a){throw await Wl.rm(r,{recursive:!0,force:!0}).catch(()=>{}),An(`clone of ${t} failed: ${a?.stderr?.trim?.()||a?.message||a}`,502)}return await da(r,{env:e}),r}import my from"node:fs/promises";function Bg(t){let e=[],n="",r=null;for(let o=0;o<t.length;o++){let s=t[o];if(r){n+=s,s===r&&t[o-1]!=="\\"&&(r=null);continue}if(s==="'"||s==='"'||s==="`"){r=s,n+=s;continue}if(s==="&"&&t[o+1]==="&"){e.push(n),n="",o++;continue}if(s==="|"&&t[o+1]==="|"){e.push(n),n="",o++;continue}if(s==="|"||s===";"||s===`
|
|
25
|
+
`){e.push(n),n="";continue}n+=s}return e.push(n),e.map(o=>o.trim()).filter(Boolean)}function mr(t){let e=[],n="",r=null,o=!1,s=()=>{if(o){if(/^\d*[<>]/.test(n)||n.startsWith(">")){n="",o=!1;return}e.push(n),n="",o=!1}};for(let a=0;a<t.length;a++){let i=t[a];if(r){i===r&&t[a-1]!=="\\"?r=null:n+=i,o=!0;continue}if(i==="'"||i==='"'){r=i,o=!0;continue}if(i==="<"&&t[a+1]==="<")return s(),e.push("<<"),e;if(/\s/.test(i)){s();continue}n+=i,o=!0}return s(),e}function hr(t){let e=t;for(;e.length&&/^[A-Za-z_][A-Za-z0-9_]*=/.test(e[0]);)e=e.slice(1);for(;e.length&&(e[0]==="sudo"||e[0]==="time"||e[0]==="env");)e=e.slice(1);return e}var En=(t,e)=>t.length>e?`${t.slice(0,e).trimEnd()}\u2026`:t,kt=t=>t&&!/[$`]/.test(t)?t:null,st=t=>t.replace(/\/+$/,"").split("/").pop()||t,_n=t=>{let e=t.replace(/\/+$/,"").split("/").filter(Boolean);return e.length>3?e.slice(-2).join("/"):t},Yl=t=>t.startsWith("-"),zl=new Set(["-g","--glob","-t","--type","-A","-B","-C","-m","--max-count","-e","--regexp","-f","--file"]);function V(t,e=new Set){let n=[];for(let r=1;r<t.length;r++){let o=t[r];if(o==="<<")break;if(Yl(o)){e.has(o)&&r++;continue}n.push(o)}return n}var Dg={status:()=>"Check git status",diff:()=>"Review changes",log:()=>"Review commit history",show:()=>"Inspect a commit",add:()=>"Stage files",commit:()=>"Commit changes",push:()=>"Push to remote",pull:()=>"Pull from remote",fetch:()=>"Fetch from remote",merge:()=>"Merge branches",rebase:()=>"Rebase branch",stash:()=>"Stash changes",clone:t=>t[1]?`Clone ${st(t[1])}`:"Clone repository",checkout:()=>"Switch branches",switch:()=>"Switch branches",restore:()=>"Restore files",reset:()=>"Reset git state",branch:(t,e)=>e.includes("--show-current")?"Check current branch":"List branches",remote:()=>"Check git remotes","rev-parse":()=>"Resolve a git ref",worktree:()=>"Manage git worktrees","merge-base":()=>"Find the merge base","ls-files":()=>"List tracked files"},jg={pr:t=>({create:"Create a pull request",list:"List pull requests",view:"View a pull request",merge:"Merge a pull request",checks:"Check PR status"})[t[1]]||"Run a gh pr command",auth:()=>"Authenticate GitHub CLI",api:()=>"Call the GitHub API",repo:()=>"Inspect the GitHub repo",issue:()=>"Work with GitHub issues"};function Zl(t){let e=t.indexOf("-n"),n=e!==-1?t[e+1]:null;if(!n)return null;let r=/^(\d+),(\d+)p$/.exec(n)||/^(\d+)p$/.exec(n);return r?r[2]?{from:r[1],to:r[2]}:{from:r[1],to:r[1]}:null}function Lg(t){let e=Zl(t),n=kt(V(t).find(r=>!/^\d+(,\d+)?p$/.test(r)));return e&&n?e.from===e.to?`Read line ${e.from} of ${st(n)}`:`Read lines ${e.from}\u2013${e.to} of ${st(n)}`:t.includes("-i")&&n?`Edit ${st(n)}`:null}function pa(t){if(t.includes("--files")){let s=kt(V(t,zl)[0]);return s&&s!=="."?`List files in ${_n(s)}`:"List files"}let e=V(t,zl),n=e[0];if(!n)return null;let r=kt(e.slice(1).find(s=>s!==".")),o=En(n,40);return r?`Search for \u201C${o}\u201D in ${_n(r)}`:`Search for \u201C${o}\u201D`}function ma(t){let e=V(t,new Set(["--prefix"])),n=e[0];return n?n==="test"||n==="t"?"Run tests":n==="run"||n==="run-script"?e[1]?`Run ${e[1]}`:null:n==="ci"||n==="install"||n==="i"||n==="add"?"Install dependencies":null:null}function ha(t,e){if(t.includes("-e")||t.includes("--eval")||t.includes("-c")||t[1]==="-"||t.includes("<<"))return`Run an inline ${e} script`;if(t.includes("--test"))return"Run tests";let n=V(t)[0];return n?`Run ${st(n)}`:null}function Ug(t){let e=V(t),n=e[0],r=e.find(a=>/\.(service|timer|socket|target)$/.test(a))||e[1],o=r?r.replace(/\.service$/,""):"a service";return{status:`Check ${o} status`,show:`Inspect ${o}`,restart:`Restart ${o}`,start:`Start ${o}`,stop:`Stop ${o}`,"daemon-reload":"Reload systemd"}[n]||null}function Xl(t){return t.find(e=>/^https?:\/\//.test(e))||null}var Fg={git:t=>{let e=V(t,new Set(["-C","-c"])),n=Dg[e[0]];return n?n(e,t):null},gh:t=>{let e=V(t),n=jg[e[0]];return n?n(e):null},sed:Lg,rg:pa,grep:pa,egrep:pa,nl:t=>{let e=kt(V(t)[0]);return e?`Read ${st(e)}`:null},cat:t=>{let e=kt(V(t)[0]);return e?`Read ${st(e)}`:null},head:t=>{let e=kt(V(t,new Set(["-n","-c"])).find(n=>!/^\d+$/.test(n)));return e?`Read the start of ${st(e)}`:null},tail:t=>{let e=kt(V(t,new Set(["-n","-c"])).find(n=>!/^\d+$/.test(n)));return e?`Read the end of ${st(e)}`:null},wc:t=>{let e=kt(V(t)[0]);return e?`Count lines in ${st(e)}`:"Count input lines"},ls:t=>{let e=kt(V(t)[0]);return e&&e!=="."?`List files in ${_n(e)}`:"List files"},find:t=>kt(t[1])&&!Yl(t[1])&&t[1]!=="."?`Find files in ${_n(t[1])}`:"Find files",tree:()=>"List the directory tree",mkdir:t=>{let e=kt(V(t)[0]);return e?`Create directory ${_n(e)}`:null},touch:t=>{let e=kt(V(t)[0]);return e?`Create ${st(e)}`:null},rm:t=>{let e=kt(V(t)[0]);return e?`Delete ${_n(e)}`:null},mv:t=>{let e=kt(V(t)[0]);return e?`Move ${st(e)}`:null},cp:t=>{let e=kt(V(t)[0]);return e?`Copy ${st(e)}`:null},chmod:t=>{let e=kt(V(t)[1]);return e?`Change permissions on ${st(e)}`:null},diff:t=>{let e=V(t);return e.length>=2?`Compare ${st(e[0])} and ${st(e[1])}`:null},curl:t=>{let e=Xl(t);return e?`Fetch ${En(e,56)}`:"Fetch a URL"},wget:t=>{let e=Xl(t);return e?`Fetch ${En(e,56)}`:"Fetch a URL"},npm:ma,pnpm:ma,yarn:ma,npx:t=>{let e=V(t)[0];return e?`Run ${e}`:null},node:t=>ha(t,"Node"),python:t=>ha(t,"Python"),python3:t=>ha(t,"Python"),make:t=>{let e=V(t)[0];return e?`Run make ${e}`:"Run make"},systemctl:Ug,journalctl:t=>{let e=t.findIndex(r=>r==="-u"||r==="--unit"||r==="--user-unit"),n=e!==-1?t[e+1]:null;return n?`Read ${n.replace(/\.service$/,"")} logs`:"Read system logs"},tmux:t=>{let e=V(t,new Set(["-S","-L","-t","-f"]))[0];return{ls:"List tmux sessions","list-sessions":"List tmux sessions","kill-session":"Kill a tmux session","send-keys":"Send keys to a tmux session","capture-pane":"Capture a tmux pane","new-session":"Start a tmux session"}[e]||null},ps:()=>"List processes",which:t=>{let e=V(t)[0];return e?`Locate ${e}`:null},command:t=>t[1]==="-v"&&t[2]?`Locate ${t[2]}`:null,fly:t=>{let e=V(t)[0];return e==="deploy"?"Deploy to Fly":e?`Run fly ${e}`:null}},Hg=new Set(["pwd","echo","cd","sleep","true","false","test","[","set","export","exit","read"]);function Wg(t){let e=hr(mr(t));if(!e.length)return null;if(/^(ba|z)?sh$/.test(st(e[0]))&&(e[1]==="-lc"||e[1]==="-c")&&e[2])return wa(e[2]);let n=st(e[0]);if(Hg.has(n))return null;if(e.includes("--version"))return`Check ${n} version`;if(n.endsWith(".sh"))return`Run ${n}`;let r=Fg[n];return r?r(e.map((o,s)=>s===0?n:o)):null}function wa(t){let e=String(t||"").trim();if(!e)return null;let n=e.indexOf("<<");if(n!==-1){let i=e.indexOf(`
|
|
26
|
+
`,n);i!==-1&&(e=e.slice(0,i))}let r=Bg(e);for(let i=0;i<r.length-1;i++){let c=hr(mr(r[i])),l=hr(mr(r[i+1]));if(!(!c.length||!l.length)&&["nl","cat"].includes(st(c[0]))&&st(l[0])==="sed"){let u=Zl(l),d=V(c)[0];if(u&&d)return En(u.from===u.to?`Read line ${u.from} of ${st(d)}`:`Read lines ${u.from}\u2013${u.to} of ${st(d)}`,88)}}let o=[];for(let i of r){let c=Wg(i);c&&o.push({d:c,head:st(hr(mr(i))[0]||""),sub:V(hr(mr(i)))[0]})}if(!o.length)return null;let s=o.find(i=>i.head==="git"&&i.sub==="commit"),a=o.find(i=>i.head==="git"&&i.sub==="push");return s&&a?"Commit and push changes":En(s?s.d:o[0].d,88)}var ya=new Set(["Bash"]);function In(t){let e=t.split(`
|
|
27
|
+
`).map(n=>n.trim()).find(Boolean)||t;return e.length>88?`${e.slice(0,88).trimEnd()}\u2026`:e}function ye(t,e=""){let n=e.trim();return n||wa(t)||In(t)}function ga(t,e){if(t){if(Array.isArray(t)){for(let n of t)ga(n,e);return}if(typeof t=="object"){if(t.type==="tool_use"&&ya.has(t.name)){let n=String(t.input?.command||"").trim();n&&e.push({id:t.id||null,tool:t.name,command:n,description:String(t.input?.description||"").trim()})}for(let n of Object.values(t))ga(n,e)}}}async function Ql(t,{maxBytes:e=1048576,limit:n=400}={}){let r;try{r=await vt(t,e)}catch{return[]}let o=[];for(let s of r.split(`
|
|
28
|
+
`)){let a=s.trim();if(!a||a[0]!=="{")continue;let i;try{i=JSON.parse(a)}catch{continue}let c=i.message;if(!c)continue;let l=[];if(ga(c.content,l),!l.length)continue;let u=i.timestamp?Date.parse(i.timestamp):null;for(let d of l)o.push({...d,summary:ye(d.command,d.description),ts:u})}return o.slice(-n)}function tu(t,e){let n=e.filter(l=>l.role==="assistant"&&(l.text||"").trim()).sort((l,u)=>(l.ts||0)-(u.ts||0)),r=[...t].sort((l,u)=>(l.ts||0)-(u.ts||0)),o=[],s=0,a=0,i=l=>{let u=l.n??a+1;return u>a&&(a=u),u};for(let l of n){let u=r.findIndex((m,g)=>g>=s&&(m.ts||0)>(l.ts||0)),d=u===-1?r.length:u,f=r.slice(s,d);if(s=d,!f.length)continue;let w=f.map(m=>({...m,n:i(m)}));o.push({id:`batch-${l.ts}-${l.box}`,anchorTs:l.ts,anchorBox:l.box,agent:l.agent,boxRole:l.boxRole,commands:w,ts:Math.max(...w.map(m=>m.ts||0)),pending:!1})}let c=r.slice(s);if(c.length){let l=c.map(u=>({...u,n:i(u)}));o.push({id:`batch-pending-${l[0]?.box||"lead"}`,anchorTs:null,anchorBox:l[l.length-1]?.box??null,agent:l[l.length-1]?.agent??"claude",boxRole:l[l.length-1]?.boxRole??"lead",commands:l,ts:Math.max(...l.map(u=>u.ts||0)),pending:!0})}return o}var qg=/^Async (?:agent|task) launched/i,Gg=t=>{if(typeof t=="string")return t.trim();if(!Array.isArray(t))return"";let e=t.find(n=>n&&n.type==="text"&&typeof n.text=="string");return e?e.text.trim():""};async function eu(t,{maxBytes:e=2097152,limit:n=100}={}){let r;try{r=await vt(t,e)}catch{return[]}let o=new Map,s=new Set;for(let i of r.split(`
|
|
29
|
+
`)){let c=i.trim();if(!c||c[0]!=="{")continue;let l;try{l=JSON.parse(c)}catch{continue}if(l.isSidechain)continue;let u=l.message?.content;if(!Array.isArray(u))continue;let d=l.timestamp?Date.parse(l.timestamp):null;for(let f of u)if(!(!f||typeof f!="object"))if(f.type==="tool_use"&&f.id){if(f.name==="Agent"){let w=f.input||{};o.set(f.id,{id:f.id,kind:"agent",name:String(w.description||"").trim()||null,detail:String(w.subagent_type||"").trim()||null,ts:d,proven:!1})}else if(f.name==="Bash"&&f.input?.run_in_background===!0){let w=String(f.input.command||"").trim();o.set(f.id,{id:f.id,kind:"command",name:String(f.input.description||"").trim()||(w?In(w):null),detail:w?In(w):null,ts:d,proven:!0})}}else f.type==="tool_result"&&f.tool_use_id&&qg.test(Gg(f.content))&&s.add(f.tool_use_id)}let a=[];for(let i of o.values()){if(!i.proven&&!s.has(i.id))continue;let{proven:c,...l}=i;a.push(l)}return a.slice(-n)}import wr from"node:fs";import Vg from"node:fs/promises";import qe from"node:path";import Jg from"node:os";import zg from"node:crypto";import Xg from"node:zlib";var wo=new Set(["Shell","Read","Grep","Glob","StrReplace","Write","ReadLints","Delete","ApplyPatch","Await"]),Lt=(t,e=88)=>t.length>e?`${t.slice(0,e).trimEnd()}\u2026`:t,Rn=t=>String(t).replace(/\/+$/,"").split("/").pop()||String(t);function be(t,e){if(!t)return null;let n=String(t);if(e){let o=e.endsWith("/")?e:`${e}/`;if(n===e)return".";if(n.startsWith(o))return n.slice(o.length)||"."}let r=n.split("/").filter(Boolean);return n.startsWith("/")&&r.length>2?r.slice(-2).join("/"):n}var ba=t=>`"${String(t).replace(/"/g,'\\"')}"`;function Kg(t){let e=typeof t=="string"?t:String(t?.patch??t?.input??""),n=[];for(let r of e.matchAll(/^\*{3} (?:Update|Add|Delete) File: (.+)$/gm))n.push(r[1].trim());return n}function nu(t,e,n){switch(t){case"Shell":{let r=String(e?.command||"").trim();return r?{command:r,description:String(e?.description||"").trim()}:null}case"Read":{let r=e?.path;if(!r)return null;let o=Number(e?.offset)||null,s=o&&Number(e?.limit)?o+Number(e.limit)-1:null;return{command:Lt(`read ${be(r,n)}${o?` (lines ${o}\u2013${s??"end"})`:""}`,140),description:o?`Read lines ${o}\u2013${s??"end"} of ${Rn(r)}`:`Read ${Rn(r)}`}}case"Grep":{let r=e?.pattern;if(!r)return null;let o=e?.path?be(e.path,n):null,s=e?.glob?` --glob ${ba(e.glob)}`:"",a=Lt(String(r),40);return{command:Lt(`grep ${ba(r)}${o&&o!=="."?` ${o}`:""}${s}`,140),description:o&&o!=="."?`Search for \u201C${a}\u201D in ${o}`:`Search for \u201C${a}\u201D`}}case"Glob":{let r=e?.glob_pattern;if(!r)return null;let o=e?.target_directory?be(e.target_directory,n):null;return{command:Lt(`glob ${ba(r)}${o&&o!=="."?` ${o}`:""}`,140),description:`Find files matching \u201C${Lt(String(r),40)}\u201D`}}case"StrReplace":return e?.path?{command:Lt(`edit ${be(e.path,n)}`,140),description:`Edit ${Rn(e.path)}`}:null;case"Write":return e?.path?{command:Lt(`write ${be(e.path,n)}`,140),description:`Write ${Rn(e.path)}`}:null;case"Delete":return e?.path?{command:Lt(`delete ${be(e.path,n)}`,140),description:`Delete ${Rn(e.path)}`}:null;case"ReadLints":{let o=(Array.isArray(e?.paths)?e.paths:[]).map(s=>be(s,n)).filter(Boolean);return{command:Lt(o.length?`lints ${o.join(" ")}`:"lints",140),description:"Check lints"}}case"ApplyPatch":{let r=Kg(e);if(!r.length)return null;let o=r.length>1?` (+${r.length-1} more)`:"";return{command:Lt(`apply_patch ${be(r[0],n)}${o}`,140),description:r.length>1?`Patch ${r.length} files`:`Patch ${Rn(r[0])}`}}case"Await":{let r=e?.shell_id??e?.task_id;return{command:Lt(`await ${r??"background output"}`,140),description:"Wait for a background command"}}default:return null}}var Yg=process.env.BOSTRAT_CURSOR_CHATS||qe.join(Jg.homedir(),".cursor","chats");function su(t){let e=zg.createHash("md5").update(String(t)).digest("hex");return qe.join(Yg,e)}function au(t){try{let e=JSON.parse(wr.readFileSync(qe.join(t,"meta.json"),"utf8"));return{createdAtMs:Number(e.createdAtMs)||null,updatedAtMs:Number(e.updatedAtMs)||Number(e.createdAtMs)||null,hasConversation:e.hasConversation===!0}}catch{return null}}function Zg(t){let e=su(t),n;try{n=wr.readdirSync(e,{withFileTypes:!0}).filter(o=>o.isDirectory())}catch{return null}let r=null;for(let o of n){let s=qe.join(e,o.name),a=qe.join(s,"store.db");if(!wr.existsSync(a))continue;let i=au(s);if(!i)continue;let c={id:o.name,dir:s,store:a,...i};(!r||c.hasConversation&&!r.hasConversation||c.hasConversation===r.hasConversation&&(c.updatedAtMs||0)>(r.updatedAtMs||0))&&(r=c)}return r}function go(t,e){if(!t)return null;if(e){let n=qe.join(su(t),e),r=qe.join(n,"store.db");if(wr.existsSync(r))return{id:e,dir:n,store:r,...au(n)||{createdAtMs:null,updatedAtMs:null}}}return Zg(t)}function ru(t,e){let n=t.prepare("SELECT data FROM blobs WHERE id = ?").get(e);return n?Buffer.from(n.data):null}function Qg(t){if(!t)return null;let e=t;if(e.length>1&&e[0]===31&&e[1]===139)try{e=Xg.gunzipSync(e)}catch{}try{return JSON.parse(e.toString("utf8"))}catch{return null}}function ty(t){let e=[],n=0,r=t.length,o=()=>{let s=0,a=0;for(;n<r;){let i=t[n++];if(s+=(i&127)*2**a,!(i&128))break;a+=7}return s};for(;n<r;){let s=o(),a=s>>>3,i=s&7;if(i===2){let c=o(),l=t.subarray(n,n+c);n+=c,a===1&&c===32&&e.push(l.toString("hex"))}else if(i===0)o();else if(i===5)n+=4;else if(i===1)n+=8;else break}return e}function ey(t,e){if(!Array.isArray(t))return[];let n=[];for(let r of t){if(!r||r.type!=="tool-call"||!wo.has(r.toolName))continue;let o=nu(r.toolName,r.args,e);!o||!o.command||n.push({id:r.toolCallId||null,tool:r.toolName,command:o.command,description:o.description||""})}return n}var ny=t=>typeof t=="string"?t.trim():Array.isArray(t)?t.filter(e=>e&&e.type==="text"&&typeof e.text=="string").map(e=>e.text).join(`
|
|
30
|
+
`).trim():"",ry=t=>Array.isArray(t)?t.filter(e=>e&&e.type==="tool-call"&&e.toolName).map(e=>e.toolName):[],ou=process.getBuiltinModule?.("node:sqlite"),$n={turns:[],trailingCommands:[],model:null,requestIds:[]};async function ka(t,e){if(!ou)return $n;let{DatabaseSync:n}=ou,r;try{r=new n(t,{readOnly:!0})}catch{return $n}try{let o=r.prepare("SELECT value FROM meta WHERE key = '0'").get()||r.prepare("SELECT value FROM meta LIMIT 1").get();if(!o)return $n;let s;try{s=JSON.parse(Buffer.from(String(o.value),"hex").toString("utf8"))}catch{return $n}let a=s.lastUsedModel||null,i=s.latestRootBlobId?ru(r,s.latestRootBlobId):null;if(!i)return{...$n,model:a};let c=[],l=[],u=[],d=null,f=[];for(let w of ty(i)){let m=Qg(ru(r,w));if(!m)continue;let g=m.providerOptions?.cursor;if(g?.requestId&&f.push(g.requestId),g?.modelProviderMessageId&&f.push(g.modelProviderMessageId),m.role!=="assistant")continue;if(Array.isArray(m.content))for(let S of m.content){let T=S?.providerOptions?.cursor?.modelName;T&&(d=T)}l.push(...ey(m.content,e).map((S,T)=>({...S,id:S.id||`${w}:${T}`}))),u.push(...ry(m.content));let x=ny(m.content);if(!x)continue;let A=[...new Set(u)];u=[],c.push({id:w,text:x,tools:A.length?A:void 0,commands:l,model:d}),l=[]}return{turns:c,trailingCommands:l,model:a,requestIds:f}}catch{return $n}finally{try{r.close()}catch{}}}function iu(t,e){let n=t.updatedAtMs;if(!n)try{n=Math.round(wr.statSync(t.store).mtimeMs)}catch{n=Date.now()}let r=t.createdAtMs;(!r||r>n)&&(r=n-e*1e3);let o=Math.max(0,n-r);return{end:n,tsAt:s=>Math.round(r+o*(s+1)/e)}}async function cu(t,e,{limit:n=80}={}){let r=go(t,e);if(!r)return[];let{turns:o,model:s}=await ka(r.store,t);if(!o.length)return[];let a=o.slice(-n),{tsAt:i}=iu(r,a.length);return a.map((c,l)=>({role:"assistant",text:c.text,tools:c.tools,ts:i(l),model:c.model||s,sourceId:c.id}))}async function lu(t,e){let n=go(t,e);if(!n)return null;let{turns:r,requestIds:o,model:s}=await ka(n.store,t);return{chatId:n.id,requestIds:o,models:[...new Set(r.map(a=>a.model).filter(Boolean))],configuredModel:s,updatedAtMs:n.updatedAtMs||null}}async function uu(t,e,{limit:n=80}={}){let r=go(t,e);if(!r)return[];let{turns:o,trailingCommands:s}=await ka(r.store,t);if(!o.length&&!s.length)return[];let a=o.slice(-n),{end:i,tsAt:c}=iu(r,a.length||1),l=[];a.forEach((u,d)=>{for(let f of u.commands)l.push({...f,summary:ye(f.command,f.description),ts:c(d)})});for(let u of s)l.push({...u,summary:ye(u.command,u.description),ts:i+1});return l}async function du(t,e){let n=go(t,e);if(!n)return null;if(n.updatedAtMs)return n.updatedAtMs;try{return Math.round((await Vg.stat(n.store)).mtimeMs)}catch{return null}}import fu from"node:fs";import Sa from"node:fs/promises";import xa from"node:path";import oy from"node:os";var sy=process.env.BOSTRAT_CODEX_SESSIONS||xa.join(process.env.CODEX_HOME||xa.join(oy.homedir(),".codex"),"sessions");async function pu(t){try{return await Sa.realpath(t)}catch{return t}}function ay(t=sy){let e=[],n=r=>{let o;try{o=fu.readdirSync(r,{withFileTypes:!0})}catch{return}for(let s of o){let a=xa.join(r,s.name);if(s.isDirectory())n(a);else if(s.isFile()&&s.name.startsWith("rollout-")&&s.name.endsWith(".jsonl")){let i=/-([0-9a-f-]{36})\.jsonl$/i.exec(s.name);try{e.push({id:i?i[1]:null,file:a,mtimeMs:fu.statSync(a).mtimeMs})}catch{}}}};return n(t),e.sort((r,o)=>o.mtimeMs-r.mtimeMs)}async function iy(t,e=1048576){let n;try{n=await Sa.open(t,"r")}catch{return""}try{let r=Buffer.alloc(Math.min(e,65536)),o="",s=0;for(;;){let{bytesRead:a}=await n.read(r,0,r.length,s);if(!a)break;o+=r.toString("utf8",0,a);let i=o.indexOf(`
|
|
31
|
+
`);if(i!==-1)return o.slice(0,i);if(s+=a,o.length>=e)break}return o}finally{await n.close()}}async function cy(t){let e=await iy(t);if(!e)return null;let n;try{n=JSON.parse(e)}catch{return null}return n?.type==="session_meta"&&n?.payload?.cwd||null}async function Ge(t,e){if(!t&&!e)return null;let n=ay();if(e){let o=n.find(s=>s.id===e);return o||null}let r=await pu(t);for(let o of n){let s=await cy(o.file);if(s&&(s===t||await pu(s)===r))return o}return null}var ly=new Set(["output_text","text"]),uy=t=>typeof t=="string"?t.trim():Array.isArray(t)?t.filter(e=>e&&typeof e.text=="string"&&ly.has(e.type)).map(e=>e.text).join(`
|
|
32
|
+
`).trim():"";async function mu(t,e,{limit:n=80,maxBytes:r=524288}={}){let o=await Ge(t,e);if(!o)return[];let s;try{s=await vt(o.file,r)}catch{return[]}let a=[],i=null,c=[],l=!1,u=!1,d=null;for(let f of s.split(`
|
|
33
|
+
`)){let w=f.trim();if(!w||w[0]!=="{")continue;let m;try{m=JSON.parse(w)}catch{continue}if((m.type==="turn_context"&&m.payload?.model||m.type==="session_meta"&&m.payload?.model)&&(i=m.payload.model),m.type==="event_msg"){let S=m.payload?.type;S==="task_started"?(l=!0,u=!1,d=m.payload?.turn_id||null):S==="task_complete"&&l&&(l=!1,!u&&m.payload?.last_agent_message==null&&a.push({role:"assistant",failed:!0,text:Ta,sourceId:m.payload?.turn_id||d||void 0,ts:m.timestamp?Date.parse(m.timestamp):null,model:i}));continue}if(m.type!=="response_item")continue;let g=m.payload;if(!g)continue;if((g.type==="function_call"||g.type==="custom_tool_call")&&g.name){u=!0,va.has(g.name)||c.push(g.name);continue}if(g.type!=="message"||g.role!=="assistant")continue;u=!0;let x=uy(g.content),A=c.length?c.slice():null;c=[],!(!x&&!A)&&a.push({role:"assistant",text:x,tools:A||void 0,ts:m.timestamp?Date.parse(m.timestamp):null,model:i})}if(i)for(let f of a)f.model||(f.model=i);return a.slice(-n)}var gr=new Set(["exec_command","shell","exec"]),va=new Set(["wait","write_stdin"]),Ta="Codex ended the turn without a response \u2014 it hit an error the CLI shows only in the terminal (e.g. quota or billing). Check the Terminal tab.";function dy(t){if(typeof t?.cmd=="string"&&t.cmd.trim())return t.cmd.trim();let e=t?.command;return typeof e=="string"&&e.trim()?e.trim():Array.isArray(e)&&e.length?e.length===3&&/^(ba)?sh$/.test(e[0])&&e[1]==="-lc"?String(e[2]).trim():e.map(String).join(" ").trim():null}function fy(t){let e=t[0];if(e!=='"'&&e!=="'"&&e!=="`")return null;let n="";for(let r=1;r<t.length;r++){let o=t[r];if(o==="\\"){let s=t[++r];if(s===void 0)return null;if(s==="n")n+=`
|
|
34
|
+
`;else if(s==="t")n+=" ";else if(s==="r")n+="\r";else if(s==="u")if(t[r+1]==="{"){let a=t.indexOf("}",r+2);if(a===-1)return null;n+=String.fromCodePoint(parseInt(t.slice(r+2,a),16)),r=a}else n+=String.fromCharCode(parseInt(t.slice(r+1,r+5),16)),r+=4;else n+=s}else{if(o===e)return n;if(e==="`"&&o==="$"&&t[r+1]==="{")return null;n+=o}}return null}function py(t){let e=String(t||""),n=[],r=/exec_command\s*\(/g;for(let s;s=r.exec(e);)n.push(s.index+s[0].length);let o=[];return n.forEach((s,a)=>{let i=e.slice(s,a+1<n.length?n[a+1]:e.length),c=/(?:^|[{,\s])["'`]?cmd["'`]?\s*:\s*/.exec(i);if(!c)return;let l=fy(i.slice(c.index+c[0].length).trimStart());l&&l.trim()&&o.push(l.trim())}),o}async function hu(t,e,{limit:n=400,maxBytes:r=524288}={}){let o=await Ge(t,e);if(!o)return[];let s;try{s=await vt(o.file,r)}catch{return[]}let a=[];for(let i of s.split(`
|
|
35
|
+
`)){let c=i.trim();if(!c||c[0]!=="{")continue;let l;try{l=JSON.parse(c)}catch{continue}if(l.type!=="response_item")continue;let u=l.payload;if(!u)continue;let d=[];if(u.type==="function_call"&&gr.has(u.name)){let w;try{w=JSON.parse(u.arguments||"{}")}catch{continue}let m=dy(w);m&&(d=[m])}else u.type==="custom_tool_call"&&u.name==="exec"&&(d=py(u.input));if(!d.length)continue;let f=u.call_id||u.id||null;d.forEach((w,m)=>{a.push({id:f?m?`${f}:${m}`:f:null,tool:u.name,command:w,description:"",summary:ye(w),ts:l.timestamp?Date.parse(l.timestamp):null})})}return a.slice(-n)}async function wu(t,e){let n=await Ge(t,e);if(!n)return null;try{return Math.round((await Sa.stat(n.file)).mtimeMs)}catch{return n.mtimeMs||null}}var On=class{constructor(e,n=new Set){this.agent=e,this.cliTools=n}async readMessages(){return[]}async readCommands(){return[]}async readJobs(){return[]}async updatedAt(){return null}},Aa=class extends On{transcript(e){return!e?.cwd||!e?.sessionId?null:ee(e.cwd,e.sessionId)}async readMessages(e,n){let r=this.transcript(e);return r?Vs(r,n):[]}async readCommands(e,n){let r=this.transcript(e);return r?Ql(r,n):[]}async readJobs(e,n){let r=this.transcript(e);return r?eu(r,n):[]}async updatedAt(e){let n=this.transcript(e);if(!n)return null;try{return Math.round((await my.stat(n)).mtimeMs)}catch{return null}}},_a=class extends On{async readMessages(e,n){return cu(e.cwd,e.sessionId,n)}async readCommands(e,n){return uu(e.cwd,e.sessionId,n)}async updatedAt(e){return du(e.cwd,e.sessionId)}},Ea=class extends On{sessionId(e){return e?.pinned?e.sessionId:null}async readMessages(e,n){return mu(e.cwd,this.sessionId(e),n)}async readCommands(e,n){return hu(e.cwd,this.sessionId(e),n)}async updatedAt(e){return wu(e.cwd,this.sessionId(e))}},hy=new On("unknown"),gu=new Map;function Ia(t){return gu.set(t.agent,t),t}function yr(t){return gu.get(t)||hy}Ia(new Aa("claude",ya));Ia(new _a("cursor",wo));Ia(new Ea("codex",gr));var Ra=t=>e=>({...e,box:t.name,agent:t.agent,boxRole:t.role});async function yu(t,e={}){return(await yr(t.agent).readMessages(t,e)).map(Ra(t))}async function bu(t,e={}){return(await yr(t.agent).readCommands(t,e)).map(Ra(t))}async function ku(t,e={}){return(await yr(t.agent).readJobs(t,e)).map(Ra(t))}var wy=/^\s*[›>*\-❯]?\s*(\d+)[.)]\s+(.+?)\s*$/,xu=/\b(allow|approve|apply (this )?patch|run (this )?command|do you want to run|grant)\b/i;function gy(t,{provider:e,box:n}){let r=String(t).replace(/\r/g,"").split(`
|
|
36
|
+
`);if(!xu.test(t))return null;let o=[],s=!1;for(let l=r.length-1;l>=0;l--){let u=r[l].match(wy);if(u)o.unshift({n:Number(u[1]),label:u[2]}),s=!0;else{if(s&&r[l].trim()==="")continue;if(s){var a=r[l].trim();break}}}if(o.length<2)return null;let i=o.sort((l,u)=>l.n-u.n);if(i.some((l,u)=>l.n!==u+1))return null;let c=typeof a=="string"&&xu.test(a)?a:"The agent is asking for approval";return{provider:e,box:n,promptId:`${e}:${c}:${i.map(l=>l.label).join("|")}`.slice(0,200),questions:[{question:c,header:"Approval",multiSelect:!1,options:i.map(l=>({label:l.label,description:""}))}]}}var yo=/(^|\n)\s*›/,Su=/^\s*(>)?\s*(\d+)\.\s+(.+?)\s*$/;function yy(t,e=""){let n=String(t);if(!/Sign in with ChatGPT/.test(n)||!/Press enter to continue/.test(n)||yo.test(n))return null;let r=n.replace(/\r/g,"").split(`
|
|
37
|
+
`),o=[],s=-1;for(let l=0;l<r.length;l++){let u=r[l].match(Su);if(!u)continue;s<0&&(s=l);let d="";for(let f=l+1;f<Math.min(l+3,r.length);f++){let w=r[f].trim();if(w){if(Su.test(r[f])||/^Press enter/.test(w))break;d=w;break}}o.push({n:Number(u[2]),cursor:!!u[1],label:u[3].trim(),desc:d})}let a=o.sort((l,u)=>l.n-u.n);if(a.length<2||a.some((l,u)=>l.n!==u+1)||!a.some(l=>/Sign in with ChatGPT/.test(l.label)))return null;let i=[];for(let l=0;l<s;l++){let u=r[l].trim();u&&!/Welcome to Codex/.test(u)&&i.push(u)}let c=i.join(" ")||"Codex isn't signed in on this box \u2014 choose a sign-in method";return{provider:"codex",box:e,multiSelect:!1,cursorIndex:Math.max(0,a.findIndex(l=>l.cursor)),answerMode:"keys",promptId:`codex:signin:${a.map(l=>l.label).join("|")}`.slice(0,200),questions:[{question:c,header:"Sign in",multiSelect:!1,options:a.map(l=>({label:l.label,description:l.desc}))}]}}var vu=/Enter this one-time code/,by=/^\s*([A-Z0-9]{2,10}-[A-Z0-9]{2,10})\s*$/;function ky(t,e=""){let n=String(t);if(!vu.test(n)||yo.test(n))return null;let r=n.replace(/\r/g,"").split(`
|
|
38
|
+
`),o=r.findIndex(i=>vu.test(i)),s="";for(let i=o+1;i<r.length;i++){let c=r[i].match(by);if(c){s=c[1];break}}if(!s)return null;let a=(n.match(/https:\/\/\S+/)||[])[0]||"https://auth.openai.com/codex/device";return{provider:"codex",box:e,multiSelect:!1,cursorIndex:0,answerMode:"esc",promptId:`codex:device-code:${s}`,questions:[{question:`Codex is waiting for you to finish signing in: open ${a} and enter code ${s} (expires in 15 minutes). The code goes straight to OpenAI \u2014 bostrat never sees your login. This card clears once OpenAI confirms.`,header:"Sign in",multiSelect:!1,options:[{label:"Cancel sign-in",description:"Go back to the sign-in options"}]}]}}function xy(t,e=""){let n=String(t);return!/open the following link to authenticate/.test(n)||yo.test(n)?null:{provider:"codex",box:e,multiSelect:!1,cursorIndex:0,answerMode:"esc",promptId:"codex:signin-browser",questions:[{question:"Codex started its browser sign-in, which opens on the box itself and can't complete from another device. Go back and choose \u201CSign in with Device Code\u201D instead.",header:"Sign in",multiSelect:!1,options:[{label:"Go back to sign-in options",description:""}]}]}}function Sy(t,e=""){let n=String(t);return!/Paste or type your API key/.test(n)||yo.test(n)?null:{provider:"codex",box:e,multiSelect:!1,cursorIndex:0,answerMode:"esc",promptId:"codex:signin-apikey",questions:[{question:"Codex is asking for an OpenAI API key. For safety a key can only be typed in the Terminal tab, never through chat. To use your ChatGPT subscription instead, go back and pick a sign-in option.",header:"Sign in",multiSelect:!1,options:[{label:"Go back to sign-in options",description:""}]}]}}function Tu(t,e,n=""){if(t!=="cursor"&&t!=="codex")return null;if(t==="codex"){let r=ky(e,n)||xy(e,n)||Sy(e,n)||yy(e,n);if(r)return r}return gy(e,{provider:t,box:n})}function Au(t,e){let n=Array.isArray(e?.[0])?e[0]:[],o=(Number.isInteger(n[0])&&n[0]>=0?n[0]:0)+1;return String(o)}var Iu=/to navigate\b.*\bEsc to cancel|Enter to select\b/,Ca=/Chat about this/i,Ke=/^\s*(❯)?\s*(\d+)\.\s+(?:\[([ xX✔])\]\s+)?(.+?)\s*$/,_u=/^(type something|chat about this|submit)\b/i,Ve=/^[─—-]{6,}\s*$/,Ma=/[\u2500-\u257F\u2550-\u256F┌┐└┘├┤│┬┴┼─✂]/;function Je(t){let e=t.search(Ma);return(e>=0?t.slice(0,e):t).trim()}function vy(t){if(!t)return"";let e=t.replace(/[│┌└├─┘┤┬┴┼─✂].*$/,"").trim();return!e||/hidden|\d+\s+lines?/i.test(e)?"":e}function Ty(t){let e=String(t).trim();return!!(!e||Ve.test(t)||Ma.test(e)||/hidden|\d+\s+lines?/i.test(e)||/notespress|press n to add/i.test(e)||/^\s*notes?\s*$/i.test(e))}function Ay(t,e=""){let n=[],r=/([☐☒])\s+([^☐☒←→✔]+)/g,o;for(;(o=r.exec(t))!==null;)n.push({label:o[2].trim(),answered:o[1]==="\u2612",current:!1});if(!n.length)return null;let s=_y(n,e);return n.forEach((a,i)=>{a.current=i===s}),{tabs:n,questionIndex:s,questionTotal:n.length,currentTabLabel:n[s].label}}function _y(t,e){let n=String(e).toLowerCase();if(n){let o=t.map((s,a)=>({i:a,label:s.label.toLowerCase()})).sort((s,a)=>a.label.length-s.label.length);for(let{i:s,label:a}of o){if(a.split(/\s+/).filter(l=>l.length>3).some(l=>n.includes(l)))return s;let c=a.slice(0,8).trim();if(c.length>=4&&n.includes(c))return s}}let r=t.findIndex(o=>!o.answered);return r>=0?r:t.length-1}function Eu(t){return t.replace(/[←→✔☐☒●▶]/g," ").replace(/\bSubmit\b/g," ").replace(/\s+/g," ").trim()}var Ru=/^\s*Switch model\?\s*$/m;function Ey(t,e=""){let n=String(t);if(!Ru.test(n))return null;let r=n.replace(/\r/g,"").split(`
|
|
39
|
+
`),o=-1;for(let u=r.length-1;u>=0;u--)if(/^\s*Switch model\?\s*$/.test(r[u])){o=u;break}if(o<0)return null;let s=[],a=[];for(let u=o+1;u<r.length;u++){let d=r[u].match(Ke);if(d)s.push({cursor:!!d[1],label:d[4].trim()});else if(s.length){if(r[u].trim()==="")break}else{let f=r[u].trim();f&&!Ve.test(r[u])&&a.push(f)}}if(s.length<2)return null;let i=s.map(u=>({label:Je(u.label),description:""})),c=Math.max(0,s.findIndex(u=>u.cursor)),l=["Switch model?",...a].join(" ");return{provider:"claude",box:e,multiSelect:!1,cursorIndex:c,promptId:`claude:model-confirm:${i.map(u=>u.label).join("|")}`.slice(0,240),questions:[{question:l,header:"Model",multiSelect:!1,options:i}]}}var $u=/^\s*Enter to confirm\s*·\s*Esc to cancel\s*$/,Iy=/^\s*[─—▔-]{6,}\s*$/;function Ry(t,e=""){let n=String(t).replace(/\r/g,"").split(`
|
|
40
|
+
`),r=n.length-1;for(;r>=0&&n[r].trim()==="";)r--;if(r<0||!$u.test(n[r]))return null;let o=r-1;for(;o>=0&&n[o].trim()==="";)o--;let s=[];for(;o>=0;o--){let c=n[o].match(Ke);if(!c)break;s.unshift({cursor:!!c[1],n:Number(c[2]),label:c[4].trim()})}if(s.length<2||s.some((c,l)=>c.n!==l+1))return null;let a=[];for(let c=o;c>=0&&a.length<6&&!(Iy.test(n[c])||Ve.test(n[c]));c--){let l=n[c].trim();l&&a.unshift(l)}let i=s.map(c=>({label:Je(c.label),description:""}));return{provider:"claude",box:e,multiSelect:!1,cursorIndex:Math.max(0,s.findIndex(c=>c.cursor)),promptId:`claude:confirm-gate:${i.map(c=>c.label).join("|")}`.slice(0,240),questions:[{question:a.join(" ")||"Claude Code is asking you to confirm",header:"Confirm",multiSelect:!1,options:i}]}}function $y(t,e=""){let n=String(t);if(!/ready to submit your answers/i.test(n))return null;let r=n.replace(/\r/g,"").split(`
|
|
41
|
+
`),o=[];for(let c=0;c<r.length;c++){let l=r[c].match(Ke);l&&o.push({cursor:!!l[1],label:l[4].trim()})}let s=o.filter(c=>/^(submit answers|cancel)$/i.test(c.label));if(s.length<2)return null;let a=s.map(c=>({label:c.label,description:""})),i=Math.max(0,s.findIndex(c=>c.cursor));return{provider:"claude",box:e,multiSelect:!1,cursorIndex:i,review:!0,promptId:"claude:review:submit-answers",questions:[{question:"Ready to submit your answers?",header:"Review",multiSelect:!1,options:a}]}}var $a=/Resume from summary/i,Oy=/^\s*This session is\b/;function Cy(t,e=""){let n=String(t);if(!$a.test(n))return null;let r=n.replace(/\r/g,"").split(`
|
|
42
|
+
`),o=-1;for(let u=r.length-1;u>=0;u--)if(Oy.test(r[u])){o=u;break}if(o<0)return null;let s=[],a=[r[o].trim()];for(let u=o+1;u<r.length;u++){let d=r[u].match(Ke);if(d)s.push({cursor:!!d[1],label:d[4].trim()});else if(s.length){if(r[u].trim()==="")break}else{let f=r[u].trim();f&&!Ve.test(r[u])&&a.push(f)}}if(s.length<2||!s.some(u=>$a.test(u.label)))return null;let i=s.map(u=>({label:Je(u.label),description:""})),c=Math.max(0,s.findIndex(u=>u.cursor)),l=a.join(" ");return{provider:"claude",box:e,multiSelect:!1,cursorIndex:c,promptId:`claude:resume-confirm:${i.map(u=>u.label).join("|")}`.slice(0,240),questions:[{question:l,header:"Resume",multiSelect:!1,options:i}]}}var ze=/shift\+tab to cycle|⏵⏵ bypass permissions on|⏸ manual mode on/;function Pa(t){let e=[];for(let r=0;r<t.length;r++){let o=t[r].match(Ke);o&&e.push({line:r,cursor:!!o[1],n:Number(o[2]),label:o[4].trim()})}let n=e.sort((r,o)=>r.n-o.n);return n.length<2||n.some((r,o)=>r.n!==o+1)?null:n}var Ou=/Choose the text style that looks best with your terminal/,Cu=/Let's get started\./;function My(t,e=""){let n=String(t);if(!Ou.test(n)||!Cu.test(n)||ze.test(n))return null;let r=n.replace(/\r/g,"").split(`
|
|
43
|
+
`),o=Pa(r);return o?{provider:"claude",box:e,multiSelect:!1,cursorIndex:Math.max(0,o.findIndex(s=>s.cursor)),setup:"theme",promptId:`claude:setup:theme:${o.map(s=>s.label).join("|")}`.slice(0,200),questions:[{question:"Claude Code asks: choose the text style that looks best with your terminal. You can change it later with /theme.",header:"Theme",multiSelect:!1,options:o.map(s=>({label:Je(s.label),description:""}))}]}:null}var Mu=/Select login method:/;function Py(t,e=""){let n=String(t);if(!Mu.test(n)||ze.test(n))return null;let r=n.replace(/\r/g,"").split(`
|
|
44
|
+
`),o=Pa(r);if(!o)return null;let s=o.map(a=>{let[i,...c]=a.label.split("\xB7");return{label:Je(i),description:c.join("\xB7").trim()}});return{provider:"claude",box:e,multiSelect:!1,cursorIndex:Math.max(0,o.findIndex(a=>a.cursor)),setup:"login-method",promptId:`claude:setup:login-method:${s.map(a=>a.label).join("|")}`.slice(0,200),questions:[{question:"How do you want to sign in to Claude? A Claude subscription (Pro/Max/Team) uses your existing plan; a Console account bills API usage. Your login stays on this box \u2014 bostrat never sees it.",header:"Log in",multiSelect:!1,options:s}]}}var Na=/Paste code here if prompted/,Ny=/^[A-Za-z0-9%&=+._~#?/:\-]+$/;function By(t,e){let n=t[e].trim();for(let r=e+1;r<t.length;r++){let o=t[r].trim();if(!o||!Ny.test(o)||Na.test(t[r]))break;n+=o}return n}function Dy(t,e=""){let n=String(t);if(!Na.test(n)||ze.test(n))return null;let r=n.replace(/\r/g,"").split(`
|
|
45
|
+
`),o="";for(let s=0;s<r.length;s++)if(/^\s*https:\/\//.test(r[s])){o=By(r,s);break}return o?{provider:"claude",box:e,multiSelect:!1,cursorIndex:0,answerMode:"text",setup:"oauth",url:o,inputLabel:"Paste your sign-in code",promptId:"claude:setup:oauth",questions:[{question:"Open the sign-in link, approve access, then paste the code you're shown back here. The code goes straight to the Claude CLI on your box \u2014 bostrat relays the keystrokes and never stores it.",header:"Log in",multiSelect:!1,options:[]}]}:null}var Oa=/OAuth error:/,jy=/Press Enter to retry\./;function Ly(t,e=""){let n=String(t);if(!Oa.test(n)||!jy.test(n)||ze.test(n))return null;let s=([...n.replace(/\r/g,"").split(`
|
|
46
|
+
`)].reverse().find(a=>Oa.test(a))||"OAuth error").trim();return{provider:"claude",box:e,multiSelect:!1,cursorIndex:0,setup:"oauth-error",promptId:`claude:setup:oauth-error:${s}`.slice(0,200),questions:[{question:`${s} \u2014 retry to get a fresh sign-in link.`,header:"Log in",multiSelect:!1,options:[{label:"Retry sign-in",description:"Mints a new sign-in link and code"}]}]}}var Pu=/Quick safety check: Is this a project you created or one you trust/;function Uy(t,e=""){let n=String(t);if(!Pu.test(n)||ze.test(n))return null;let r=n.replace(/\r/g,"").split(`
|
|
47
|
+
`),o=Pa(r);return o?{provider:"claude",box:e,multiSelect:!1,cursorIndex:Math.max(0,o.findIndex(s=>s.cursor)),setup:"trust",promptId:`claude:setup:trust:${o.map(s=>s.label).join("|")}`.slice(0,200),questions:[{question:"Claude Code asks whether it can read, edit, and run files in this workspace. This box's workspace is bostrat-managed scratch space, so trusting it is safe.",header:"Folder trust",multiSelect:!1,options:o.map(s=>({label:Je(s.label),description:""}))}]}:null}var Nu=/^\s*Press Enter to continue\s*$/;function Fy(t,e=""){let n=String(t);if(ze.test(n))return null;let r=n.replace(/\r/g,"").split(`
|
|
48
|
+
`),o=-1;for(let i=r.length-1;i>=0;i--)if(r[i].trim()!==""){o=i;break}if(o<0||!Nu.test(r[o]))return null;let s=[];for(let i=o-1;i>=0&&s.length<3;i--){let c=r[i].trim();if(!c){if(s.length)break;continue}if(Ve.test(r[i])||Ma.test(c))break;s.unshift(c)}let a=s.join(" ")||"Claude Code is waiting for you to continue.";return{provider:"claude",box:e,multiSelect:!1,cursorIndex:0,setup:"continue",promptId:`claude:setup:continue:${a}`.slice(0,200),questions:[{question:a,header:"Continue",multiSelect:!1,options:[{label:"Continue",description:""}]}]}}function Hy(t){let e=-1;for(let r=t.length-1;r>=0;r--)if(Ca.test(t[r])){e=r;break}if(e<0)return null;let n=-1;for(let r=e-1;r>=0;r--)if(/[☐☒]/.test(t[r])){n=r;break}return n<0?null:{headerLine:n,chatLine:e,optStart:n+1,optEnd:e}}function Wy(t,e=""){let n=String(t);if(!Iu.test(n)||!Ca.test(n))return null;let r=n.replace(/\r/g,"").split(`
|
|
49
|
+
`),o=Hy(r);if(!o)return null;let{headerLine:s,chatLine:a,optStart:i,optEnd:c}=o,l=[],u=-1;for(let h=i;h<c;h++){let p=r[h].match(Ke);p&&(u<0&&(u=h),l.push({line:h,cursor:!!p[1],checked:p[3]!=null&&p[3]!==" ",hasCheckbox:p[3]!=null,label:p[4].trim(),desc:""}))}if(!l.length)return null;for(let h of l)for(let p=h.line+1;p<Math.min(r.length,h.line+4)&&!(Ke.test(r[p])||Ve.test(r[p]));p++){let b=r[p].trim();if(!(!b||Ty(r[p])||_u.test(b))){h.desc=vy(b);break}}let d=l.filter(h=>!_u.test(h.label));if(!d.length)return null;let f=d.some(h=>h.hasCheckbox),w=Math.max(0,d.findIndex(h=>h.cursor)),m=s>=0?s+1:0,g=[];for(let h=m;h<u;h++){let p=r[h].trim();p&&!Ve.test(r[h])&&!/[☐☒]/.test(r[h])&&g.push(p)}let x=g.join(" ")||(s>=0?Eu(r[s]):"")||"The agent is asking a question",A=s>=0?Ay(r[s],x):null,S=A?.currentTabLabel||(s>=0?Eu(r[s]):""),T=d.map(h=>({label:Je(h.label),description:h.desc})),E=T.map(h=>h.label).join("|"),M=`claude:${A?`q${A.questionIndex+1}/${A.questionTotal}`:"q1/1"}:${S}:${x}:${E}`.slice(0,240),I=A?`claude:ask:${A.tabs.map(h=>h.label).join("|")}`.slice(0,240):M;return{provider:"claude",box:e,multiSelect:f,cursorIndex:w,promptId:M,groupId:I,...A?{questionIndex:A.questionIndex,questionTotal:A.questionTotal,currentTabLabel:A.currentTabLabel,tabs:A.tabs}:{},questions:[{question:x,header:S,multiSelect:f,options:T}]}}function Cn(t,e){let n=String(e||"");return n?t==="claude"?Iu.test(n)&&Ca.test(n)||Ru.test(n)||$a.test(n)||$u.test(n.trimEnd().split(`
|
|
50
|
+
`).pop()||"")?!0:ze.test(n)?!1:Ou.test(n)&&Cu.test(n)||Mu.test(n)||Na.test(n)||Oa.test(n)||Pu.test(n)||Nu.test(n.trimEnd().split(`
|
|
51
|
+
`).pop()||""):t==="codex"&&/Sign in with ChatGPT|one-time code|Paste or type your API key|link to authenticate/i.test(n)?!0:t==="cursor"||t==="codex"?/\b(allow|approve|apply (this )?patch|run (this )?command|do you want to run|grant)\b/i.test(n):!1:!1}function Xe(t,e){return!t||!e?null:t.agent==="claude"?$y(e,t.name)||Wy(e,t.name)||Ey(e,t.name)||Cy(e,t.name)||Ly(e,t.name)||Dy(e,t.name)||Py(e,t.name)||My(e,t.name)||Uy(e,t.name)||Fy(e,t.name)||Ry(e,t.name):t.agent==="cursor"||t.agent==="codex"?Tu(t.agent,e,t.name):null}async function Ba(t){if(!t)return null;let e=await me(t.name).catch(()=>"");return Xe(t,e)}function Bu(t,e){let n=Array.isArray(e?.[0])?e[0].filter(a=>Number.isInteger(a)&&a>=0):[],r=Number.isInteger(t?.cursorIndex)?t.cursorIndex:0,o=(a,i)=>{let c=i-a;return Array.from({length:Math.abs(c)},()=>c>0?"Down":"Up")},s=[];if(t?.multiSelect){let a=r;for(let i of[...n].sort((c,l)=>c-l))s.push(...o(a,i),"Enter"),a=i;s.push("Tab","Enter")}else{let a=n.length?n[0]:0;s.push(...o(r,a),"Enter")}return s}function Du(t,e){let n=Number.isInteger(t?.questionIndex)?t.questionIndex:0,o=(Number.isInteger(e)?e:n)-n;if(!o)return[];let s=o>0?"Tab":"BTab";return Array.from({length:Math.abs(o)},()=>s)}function Mn(t){return typeof t=="string"?t.trim():""}var ju=/^\s*─{8,}\s*$/,qy=/^❯[ ]?(.*)$/;function Da(t,e){if(t!=="claude")return null;let n=String(e).replace(/\r/g,"").split(`
|
|
52
|
+
`).map(c=>c.replace(/\s+$/,"")),r=-1;for(let c=n.length-1;c>=0;c--)if(ju.test(n[c])){r=c;break}if(r<=0)return null;let o=-1;for(let c=r-1;c>=0;c--)if(ju.test(n[c])){o=c;break}if(o<0)return null;let s=n.slice(o+1,r);if(!s.length)return null;let a=s[0].match(qy);if(!a)return null;let i=[a[1].trim(),...s.slice(1).map(c=>c.trim())].filter(Boolean);return i.length?i.join(" "):null}function Ye(t){return String(t||"").replace(/\s+/g," ").trim()}function Lu(t,e){let n=Ye(t),r=Ye(e).replace(/^…\s*/,"").replace(/\s*…$/,"");return!n||!r||r.length<Math.min(n.length,12)?!1:n===r||n.includes(r)}import{execFile as Gy}from"node:child_process";import{promisify as Ky}from"node:util";var La=Ky(Gy),Uu=64*1024*1024;async function mt(t,e,{input:n}={}){let r=La("git",["-C",t,...e],{maxBuffer:Uu});return n!==void 0&&r.child.stdin&&(r.child.stdin.on("error",()=>{}),r.child.stdin.end(n)),r}async function Ua(t){if(!t)return!1;try{let{stdout:e}=await mt(t,["rev-parse","--is-inside-work-tree"]);return e.trim()==="true"}catch{return!1}}async function Fu(t){let{stdout:e}=await mt(t,["rev-parse","--show-toplevel"]);return e.trim()}async function Vy(t){try{return await mt(t,["rev-parse","--verify","HEAD"]),!0}catch{return!1}}async function Hu(t,e="bostrat: subtask snapshot"){let{stdout:n}=await mt(t,["rev-parse","--absolute-git-dir"]),r=`${n.trim()}/bostrat-snapshot-index`,o={...process.env,GIT_INDEX_FILE:r},s=(a,i)=>La("git",["-C",t,...a],{maxBuffer:Uu,env:o,...i});try{let a=await Vy(t);a&&await s(["read-tree","HEAD"]),await s(["add","-A"]);let{stdout:i}=await s(["write-tree"]),c=i.trim(),l=a?["commit-tree",c,"-p","HEAD","-m",e]:["commit-tree",c,"-m",e],{stdout:u}=await s(l);return u.trim()}finally{try{await La("rm",["-f",r])}catch{}}}async function Wu(t,e){let{stdout:n}=await mt(t,["status","--porcelain"]);return n.trim()?(await mt(t,["add","-A"]),await mt(t,["-c","user.email=agent@bostrat.local","-c","user.name=bostrat","commit","-m",e]),!0):!1}async function bo(t,e,n,r){return await mt(t,["worktree","add","--no-track","-b",n,e,r]),e}async function Fa(t){if(process.env.BOSTRAT_TRACK_BASE_REF)return process.env.BOSTRAT_TRACK_BASE_REF;try{let{stdout:e}=await mt(t,["symbolic-ref","--short","refs/remotes/origin/HEAD"]);if(e.trim())return e.trim()}catch{}for(let[e,n]of[["refs/remotes/origin/main","origin/main"],["refs/remotes/origin/master","origin/master"],["refs/heads/main","main"],["refs/heads/master","master"]])try{return await mt(t,["rev-parse","--verify","--quiet",e]),n}catch{}return"HEAD"}async function qu(t){await mt(t,["init","-q"]),await mt(t,["-c","user.email=agent@bostrat.local","-c","user.name=bostrat","commit","--allow-empty","-qm","bostrat: track workspace"])}async function Ze(t,e){try{await mt(t,["worktree","remove","--force",e])}catch{try{await mt(t,["worktree","prune"])}catch{}}}function Jy(t){let e=/(\d+) files? changed/.exec(t),n=/(\d+) insertions?\(\+\)/.exec(t),r=/(\d+) deletions?\(-\)/.exec(t);return{files:e?Number(e[1]):0,insertions:n?Number(n[1]):0,deletions:r?Number(r[1]):0}}function Ha(t,e){return!e||!e.length?t:[...t,"--",".",...e.map(n=>`:(exclude)${n}`)]}async function Gu(t,e,n,r){let{stdout:o}=await mt(t,Ha(["diff","--shortstat",e,n],r));return Jy(o)}async function Ku(t,e,n,r){let{stdout:o}=await mt(t,Ha(["diff","--name-only",e,n],r));return o.split(`
|
|
53
|
+
`).map(s=>s.trim()).filter(Boolean)}async function Vu(t,e,n,r){let{stdout:o}=await mt(t,Ha(["diff","--binary",e,n],r));if(!o.trim())return{applied:!0,empty:!0,conflicts:[]};if((await ja(t,o,["--whitespace=nowarn"])).ok)return{applied:!0,empty:!1,conflicts:[]};let s=await ja(t,o,["--3way","--whitespace=nowarn"]),a=await zy(t);return a.length>0?{applied:!1,empty:!1,conflicts:a}:s.ok?{applied:!0,empty:!1,conflicts:[]}:(await ja(t,o,["--reject","--whitespace=nowarn"]),{applied:!1,empty:!1,conflicts:[`patch did not apply cleanly: ${s.err}`]})}async function ja(t,e,n){try{return await mt(t,["apply",...n,"-"],{input:e}),{ok:!0}}catch(r){return{ok:!1,err:r?.stderr?.trim()||r?.message||String(r)}}}async function zy(t){try{let{stdout:e}=await mt(t,["diff","--name-only","--diff-filter=U"]);return e.split(`
|
|
54
|
+
`).map(n=>n.trim()).filter(Boolean)}catch{return[]}}import Xy from"node:fs/promises";import Yy from"node:os";import Ju from"node:path";var ko=t=>new Promise(e=>setTimeout(e,t)),Zy=Number(process.env.BOSTRAT_MODEL_PICKER_TIMEOUT_MS)||8e3,Qy=300,Zu=[{alias:"default",label:"Default"},{alias:"opus",label:"Opus 4.8"},{alias:"sonnet",label:"Sonnet 4.6"},{alias:"haiku",label:"Haiku 4.5"},{alias:"fable",label:"Fable 5"},{alias:"opusplan",label:"Opus Plan"}],tb=new Set([...Zu.map(t=>t.alias),"best","opus[1m]","sonnet[1m]","claude-opus-4-8","claude-sonnet-4-6","claude-haiku-4-5","claude-fable-5"]),eb=[{alias:"gpt-5.5",label:"GPT-5.5"},{alias:"gpt-5.4",label:"GPT-5.4"},{alias:"gpt-5.4-mini",label:"GPT-5.4 Mini"},{alias:"gpt-5.3-codex",label:"Codex 5.3"},{alias:"gpt-5.2",label:"GPT-5.2"}],Qu=new Map([["low","Low"],["medium","Medium"],["high","High"],["xhigh","Extra high"]]),td=[{alias:"auto",label:"Auto"},{alias:"composer-2.5",label:"Composer 2.5"},{alias:"claude-opus-4-8",label:"Opus 4.8"},{alias:"claude-fable-5",label:"Fable 5"},{alias:"claude-sonnet-5",label:"Sonnet 5"},{alias:"claude-sonnet-4-6",label:"Sonnet 4.6"},{alias:"gpt-5.5",label:"GPT-5.5"},{alias:"gpt-5.3-codex",label:"Codex 5.3"}];function ed(t){return t==="codex"?eb:t==="cursor"?td:Zu}function zu(t){let e=String(t||"").split(`
|
|
55
|
+
`),n=e.findIndex(o=>/Select Model and Effort/i.test(o));if(n<0)return null;let r=[];for(let o=n+1;o<e.length;o++){let s=e[o].match(/^\s*(›\s*)?(\d+)\.\s+(\S+?)(?:\s+\((current|default)\))?(?:\s{2,}|\s*$)/);if(s)r.push({n:Number(s[2]),id:s[3],current:s[4]==="current",active:!!s[1]});else if(r.length&&/Press enter|esc to go back/i.test(e[o]))break}return r.length?r:null}function Xu(t){let e=String(t||"").split(`
|
|
56
|
+
`),n=e.findIndex(o=>/Select Reasoning Level/i.test(o));if(n<0)return null;let r=[];for(let o=n+1;o<e.length;o++){let s=e[o].match(/^\s*(›\s*)?(\d+)\.\s+([A-Za-z ]+?)(?:\s+\((default|current)\))?(?:\s{2,}|\s*$)/);if(s)r.push({n:Number(s[2]),label:s[3].trim(),isDefault:!!s[4],active:!!s[1]});else if(r.length&&/Press enter|esc to go back/i.test(e[o]))break}return r.length?r:null}function nb(t){let e=String(t||"").split(`
|
|
57
|
+
`);for(let n=e.length-1;n>=0;n--){let r=e[n].match(/Model changed to\s+(\S+)(?:\s+(\S+))?\s*$/i);if(r)return{model:r[1],effort:r[2]||null}}return null}function Yu(t){let e=String(t||"").split(`
|
|
58
|
+
`),n=e.findIndex(s=>/Available models|Models matching/i.test(s));if(n<0)return null;let r=/Models matching/i.test(e[n]),o=[];for(let s=n+1;s<e.length;s++){let a=e[s];if(/^\s*Filter:/i.test(a)||!a.trim())continue;if(/to filter|Enter to select|^\s*\d+-\d+ of \d+/i.test(a))break;let i=a.match(/^\s*(→\s+)?(.+?)\s*$/);if(!i)continue;let c=i[2].replace(/\s*\(Tab to modify\)\s*$/i,""),l=null,u=c.split(/\s{2,}/);u.length>1&&(c=u[0],l=u.slice(1).join(" ").trim()||null),c&&o.push({label:c.trim(),params:l,active:!!i[1]})}return o.length?{filtered:r,rows:o}:null}function nd(t,e){if(typeof e!="string"||!e.trim())throw new Error("model (string) required");let n=e.trim();if(t==="codex"){let[r,o]=n.split(/[\s:]+/);if(o&&!Qu.has(o))throw new Error(`unknown codex effort "${o}" (low | medium | high | xhigh)`);return{model:r,effort:o||null}}if(t==="cursor"){let r=td.find(o=>o.alias===n);return{model:n,filter:r?r.label:n}}if(!tb.has(n))throw new Error(`unknown model "${n}"`);return{model:n}}async function br(t,e,{timeoutMs:n=Zy}={}){let r=Date.now()+n;for(;;){let o=await pt(t,{lines:80}).catch(()=>""),s=e(o);if(s)return s;if(Date.now()>r)return null;await ko(Qy)}}async function rb(t,e){return await yt(t,`/model ${e.model}`,ne("claude")),{verified:!1,note:"confirmed when the next turn's transcript model flips"}}async function ob(t,e){await no(t),await yt(t,"/model",{enter:!1}),await ko(250),await gt(t,["Enter"],{kind:"model-switch"});let n=await br(t,zu);if(!n)throw new Error("codex model picker didn't open");let r=n.find(i=>i.id===e.model);if(!r)throw await gt(t,["Escape"],{kind:"model-switch"}).catch(()=>{}),new Error(`unknown model "${e.model}" \u2014 codex offers: ${n.map(i=>i.id).join(", ")}`);let o=i=>{let c=nb(i);return!c||c.model!==e.model||e.effort&&c.effort&&c.effort.toLowerCase()!==e.effort.toLowerCase()?null:c};await yt(t,String(r.n),{enter:!1,kind:"model-switch"});let s=await br(t,i=>Xu(i)||!zu(i)&&o(i)&&{done:o(i)});if(!s)throw await gt(t,["Escape"],{kind:"model-switch"}).catch(()=>{}),new Error("codex reasoning-level picker didn't open");if(!s.done){let i=e.effort?Qu.get(e.effort):null,c=i?s.find(l=>l.label.toLowerCase()===i.toLowerCase()):s.find(l=>l.isDefault)||s[0];if(!c)throw await gt(t,["Escape","Escape"],{kind:"model-switch"}).catch(()=>{}),new Error(`codex doesn't offer effort "${e.effort}" for ${e.model}`);await yt(t,String(c.n),{enter:!1,kind:"model-switch"})}let a=s.done||await br(t,i=>!Xu(i)&&o(i));if(!a)throw new Error("codex never confirmed the model change");return{verified:!0,model:a.model,effort:a.effort}}async function sb(){let t=Ju.join(process.env.CURSOR_CONFIG_DIR||Ju.join(Yy.homedir(),".cursor"),"cli-config.json");try{return JSON.parse(await Xy.readFile(t,"utf8"))?.model||null}catch{return null}}async function ab(t,e){await no(t),await oo(t,`/model ${e.filter}`,{kind:"model-switch"});let n=await br(t,Yu),r=c=>String(c||"").toLowerCase(),o=c=>r(c.label).startsWith(r(e.filter)),s=n?.rows.find(c=>c.active)||null;if(n&&!(s&&o(s)))for(let c=0;c<12;c++){let l=await br(t,Yu,{timeoutMs:1500});if(s=l?.rows.find(u=>u.active)||null,s&&o(s))break;if(!l||!l.rows.some(o)){s=null;break}await gt(t,["Down"],{kind:"model-switch"}),await ko(250)}if(!n||!s||!o(s)){await gt(t,["Escape"],{kind:"model-switch"}).catch(()=>{});let c=Array(`/model ${e.filter}`.length).fill("BSpace");throw await gt(t,c,{settleMs:15,kind:"model-switch"}).catch(()=>{}),new Error(n?`cursor picker has no model matching "${e.filter}"`:"cursor model picker didn't open")}await gt(t,["Enter"],{kind:"model-switch"}),await ko(800);let a=await sb();return{verified:!!a&&(a.modelId===e.model||a.displayModelId===e.model||r(a.displayName).startsWith(r(e.filter))),model:a?.modelId||null,note:"cursor applies this as the global default for new sessions too"}}async function rd(t,e,n){return t==="codex"?ob(e,n):t==="cursor"?ab(e,n):rb(e,n)}import Wa from"node:fs/promises";import Pn from"node:path";import ib from"node:os";import{EventEmitter as cb}from"node:events";import{randomUUID as lb}from"node:crypto";var ub=new Set(["session.ready","turn.user","turn.agent","turn.retracted","tool.called","tool.result","prompt.raised","prompt.answered","context.usage","cost.recorded","handoff.started","handoff.step","handoff.done","handoff.failed","subtask.delegated","subtask.merged","job.started","pr.state.changed","pr.offered","pr.offer.answered","box.state.changed","view.state.changed","view.spinup.failed","mock.state.changed","runbook.state.changed","ticket.linked","ticket.sync.failed","ticket.move.offered","ticket.move.answered","ticket.state.changed","ticket.commented","ticket.flow.changed","ticket.create.offered","ticket.create.answered","ticket.created","provider.connected","stall.detected","error"]),db=new Set(["user","agent","orchestrator","system"]),fb=new Set(["hook","otel","statusline","transcript","pane","orchestrator","vendor"]),Nt=new cb;Nt.setMaxListeners(0);var qa=process.env.BOSTRAT_STATE_DIR||Pn.join(process.env.HOME||ib.tmpdir(),".bostrat");function So(t){return t?.cwd?Pn.join(t.cwd,".bostrat","events"):Pn.join(qa,"events")}var od=(t,e)=>Pn.join(t,`${e}.ndjson`),xo=new Map;async function pb(t,e){if(!xo.has(e)){let o=(await B(t,e)).reduce((s,a)=>a.seq>s?a.seq:s,0);xo.set(e,o)}let n=xo.get(e)+1;return xo.set(e,n),n}function mb(t,e){let n=Array.isArray(t?.epochs)?t.epochs:null;if(!(!n||n.length===0)){if(!Number.isFinite(e))return n.length-1;for(let r=n.length-1;r>=0;r--){let o=n[r].startedAt;if(o==null||o<=e)return r}return 0}}async function R(t,e){let n=typeof t=="string"?t:t?.id;if(!n)throw new Error("appendEvent: a track (or trackId) is required");if(!e||!ub.has(e.type))throw new Error(`appendEvent: unknown event type "${e?.type}"`);if(e.actor&&!db.has(e.actor))throw new Error(`appendEvent: bad actor "${e.actor}"`);if(e.source&&!fb.has(e.source))throw new Error(`appendEvent: bad source "${e.source}"`);let r=typeof t=="string"?Pn.join(qa,"events"):So(t),o=e.epoch;if(o==null&&typeof t=="object"&&e.boxId&&Array.isArray(t?.boxes)){let c=t.boxes.find(l=>l.name===e.boxId);o=mb(c,Number(e.payload?.ts??e.ts)||Date.now())}let s=await pb(r,n),a=e.userId??process.env.BOSTRAT_OWNER??null,i={id:e.id||lb(),trackId:n,boxId:e.boxId??null,...o!=null?{epoch:o}:{},...a?{userId:a}:{},seq:s,ts:e.ts??Date.now(),type:e.type,actor:e.actor??"system",provider:e.provider??null,source:e.source??"orchestrator",payload:e.payload??{},...e.causationId?{causationId:e.causationId}:{},...e.correlationId?{correlationId:e.correlationId}:{}};return await Wa.mkdir(r,{recursive:!0}),await Wa.appendFile(od(r,n),JSON.stringify(i)+`
|
|
59
|
+
`),Nt.emit("event",i),Nt.emit(`track:${n}`,i),i}async function B(t,e){let n,r;typeof t=="string"&&e?(n=t,r=e):(r=typeof t=="string"?t:t?.id,n=typeof t=="string"?Pn.join(qa,"events"):So(t));let o;try{o=await Wa.readFile(od(n,r),"utf8")}catch{return[]}let s=[];for(let a of o.split(`
|
|
60
|
+
`))if(a.trim())try{s.push(JSON.parse(a))}catch{}return s}function sd(t,e){let n=`track:${t}`;return Nt.on(n,e),()=>Nt.off(n,e)}var Ct=t=>t||null;function To(t){let e=t||{},n=hb(e);return n&&(n.payload={ts:e.at,...n.payload}),n}function hb(t){switch(t.type){case"said":return{type:"turn.user",actor:"user",source:"orchestrator",boxId:t.box,provider:Ct(t.agent),payload:{text:t.text,boxRole:t.boxRole,...t.kind?{kind:t.kind}:{},...t.attachments?.length?{attachments:t.attachments}:{}}};case"unsaid":return{type:"turn.retracted",actor:"user",source:"orchestrator",boxId:t.box,provider:Ct(t.agent),payload:{saidTs:t.saidTs,text:t.text,boxRole:t.boxRole}};case"orchestration-prompt":return{type:"turn.user",actor:"orchestrator",source:"orchestrator",boxId:t.box,provider:Ct(t.agent),payload:{text:t.prompt,kind:t.kind,boxRole:t.boxRole}};case"lead-handoff":return{type:"handoff.done",actor:"orchestrator",source:"orchestrator",boxId:t.to,provider:Ct(t.toAgent),payload:{from:t.from,fromAgent:t.fromAgent,to:t.to,toAgent:t.toAgent,reason:t.reason,handoffStartedAt:t.handoffStartedAt,fromSessionId:t.fromSessionId,toSessionId:t.toSessionId,epoch:t.epoch,singleBox:t.singleBox,forced:t.forced,recovered:t.recovered}};case"lead-handoff-failed":return{type:"handoff.failed",actor:"orchestrator",source:"orchestrator",boxId:t.box,provider:Ct(t.toAgent),payload:{box:t.box,fromAgent:t.fromAgent,toAgent:t.toAgent,reason:t.reason,error:t.error,rolledBack:t.rolledBack,handoffStartedAt:t.handoffStartedAt}};case"subtask-delegated":return{type:"subtask.delegated",actor:"orchestrator",source:"orchestrator",boxId:t.to,provider:Ct(t.toAgent),payload:{task:t.task,subtaskId:t.subtaskId,reason:t.reason}};case"subtask-merged":return{type:"subtask.merged",actor:"orchestrator",source:"orchestrator",boxId:t.box||t.from,provider:Ct(t.agent),payload:{...kr(t)}};case"subtask-failed":return{type:"error",actor:"system",source:"orchestrator",boxId:t.box||t.to,provider:Ct(t.agent||t.toAgent),payload:{kind:"subtask-failed",...kr(t)}};case"subtask-resumed":return{type:"box.state.changed",actor:"orchestrator",source:"orchestrator",boxId:t.box,provider:Ct(t.agent),payload:{change:"subtask-resumed",...kr(t)}};case"model-change":return{type:"box.state.changed",actor:"user",source:"orchestrator",boxId:t.box,payload:{change:"model",model:t.to,from:t.from??null,verified:t.verified??null}};case"codex-update":return{type:"box.state.changed",actor:"orchestrator",source:"orchestrator",boxId:t.box,provider:"codex",payload:{change:"cli-update",from:t.from??null,to:t.to??null,status:t.status,...t.error?{error:t.error}:{}}};case"opened":return{type:"box.state.changed",actor:"user",source:"orchestrator",boxId:t.box,payload:{change:"opened",...kr(t)}};case"archived":case"unarchived":case"track-deleted":return{type:"box.state.changed",actor:"user",source:"orchestrator",payload:{change:t.type,...kr(t)}};case"rehydrated":return{type:"box.state.changed",actor:"orchestrator",source:"orchestrator",boxId:t.box,provider:Ct(t.agent),payload:{change:"rehydrated"}};case"rehydrate-failed":return{type:"error",actor:"system",source:"orchestrator",boxId:t.box,provider:Ct(t.agent),payload:{kind:"rehydrate-failed",reason:t.reason}};default:return null}}var vo=Promise.resolve(),Ka=t=>(vo=vo.then(t).catch(()=>{}),vo),id=()=>vo;function cd(t,e){let n=To(e);if(!n)return;let r=e?.type==="track-deleted"?{id:t.id}:t;Ka(()=>R(r,n))}var ad=new Set;async function ld(t){if(!(!t?.id||ad.has(t.id))){ad.add(t.id);try{let e=await B(t),n=new Set(e.filter(r=>r.source==="orchestrator").map(r=>`${r.type}|${r.payload?.ts??"?"}`));for(let r of t.events||[]){let o=To(r);!o||n.has(`${o.type}|${o.payload?.ts??"?"}`)||await R(t,o)}}catch{}}}function kr(t){let{type:e,at:n,...r}=t;return r}var Ga=new Map;function ud(t,e,n){!n?.promptId||Ga.get(t.id)===n.promptId||(Ga.set(t.id,n.promptId),Ka(()=>R(t,{type:"prompt.raised",actor:"agent",source:"pane",boxId:e?.name,provider:Ct(n.provider||e?.agent),payload:{promptId:n.promptId,question:n.question||n.title||null,options:(n.options||[]).map(r=>r?.label??r).slice(0,12)}})))}function dd(t,e,n,r={}){Ga.delete(t.id),Ka(()=>R(t,{type:"prompt.answered",actor:"user",source:"orchestrator",boxId:e?.name,provider:Ct(n?.provider||e?.agent),payload:{promptId:n?.promptId,...r}}))}import{randomUUID as qb}from"node:crypto";var fd=/(?<!\b(?:a|an|the|this|that|another|one|each|any|no|separate|single|new|initial|final|first|second|third|last|later|next|subsequent|extra|additional|amend(?:ed)?|squash|merge|follow[\s-]?up|clean[\s-]?up)\s)commit(?!s\b)(?!\s+(?:history|log|message|hash|sha)s?\b)/.source,pd=/(?<!\b(?:a|an|the|this|that|another|one|each|any|no|separate|single|new|initial|final|first|second|third|last|later|next|subsequent|extra|additional)\s)push(?!\s+notifications?\b)(?:\s+(?:it|this|that|them|everything|the\s+\w+))?\s*(?:up)?/.source,md=/open(?:\s+up)?\s+(?:a|the|that)\s+(?:draft\s+)?(?:pr|pull\s+request)|creat(?:e|ing)\s+(?:a|the)\s+(?:draft\s+)?(?:pr|pull\s+request)|rais(?:e|ing)\s+(?:a|the)\s+(?:pr|pull\s+request)|put\s+up\s+(?:a|the)\s+(?:pr|pull\s+request)|send\s+(?:up\s+)?(?:a|the)\s+(?:pr|pull\s+request)/.source,wb=new RegExp(`\\b(?:${fd}|${pd}|${md})\\b`,"i"),gb=/\b(?:want\s+me\s+to|would\s+you\s+like(?:\s+me)?(?:\s+to)?|should\s+i|shall\s+i|do\s+you\s+want(?:\s+me)?(?:\s+to)?|happy\s+to|ready\s+for\s+me\s+to|let\s+me\s+know\s+(?:if|when|whether|and)|say\s+the\s+word|give\s+me\s+the\s+(?:word|go[\s-]?ahead)|if\s+you(?:'d|\s+would)?\s+(?:like|want|prefer)|just\s+say\s+(?:so|yes|go))\b/i,yb=/^(?:commit|push|open|create|raise|ready|ok(?:ay)?\s+to|good\s+to)\b/i,bb=90;function kb(t){return String(t).split(/(?<=[.?!])\s+|\n+/).map(e=>e.trim()).filter(Boolean)}function hd(t){let e=String(t||"").trim();if(!e)return null;let n=e.slice(-600);for(let r of kb(n).reverse()){if(!wb.test(r))continue;let o=r.endsWith("?")&&r.length<=bb&&yb.test(r);if(gb.test(r)||o)return r.length>200?r.slice(0,197)+"\u2026":r}return null}var wd=["commit","push","pr"],xb=new RegExp(`\\b(?:${fd})\\b`,"i"),Sb=new RegExp(`\\b(?:${pd})\\b`,"i"),vb=new RegExp(`\\b(?:${md})\\b`,"i"),Tb=/\b(?:to|into|onto|on|updat(?:e|es|ing))\s+(?:the|that|this)\s+(?:open\s+|existing\s+|current\s+|same\s+)?(?:draft\s+)?(?:pr|pull\s+request)\b/i;function gd(t){let e=String(t||""),n=[];xb.test(e)&&n.push("commit"),Sb.test(e)&&n.push("push");let r=Tb.test(e);!r&&vb.test(e)&&n.push("pr");let o=null;if(r){let s=e.match(/(?:pr|pull\s+request)\s*\(?\s*#(\d+)\)?/i)||e.match(/#(\d+)/);o={number:s?Number(s[1]):null}}return{offered:n.length?n:null,toPr:o}}function Ao(t,{branch:e=null,base:n=null,toPr:r=null}={}){Array.isArray(t)||(t=t==="push"?["commit","push"]:["commit","push","pr"]);let o=new Set(t),s=e?` on this branch (${e})`:"",a=[];if(o.has("commit")&&a.push(`commit the work${s} with a clear commit message`),o.has("push")||o.has("pr")){let l=r&&!o.has("pr")?` (the open PR${r.number?` #${r.number}`:""} picks it up)`:"";a.push(o.has("commit")?`push it to the remote${l}`:`push this branch${s} to the remote${l}`)}o.has("pr")&&a.push(`open a pull request${n?` against ${n}`:""} with a concise title and a description of what changed and why (use \`gh pr create\`)`);let i=[];o.has("commit")||i.push("don't create any new commits \u2014 ship what's already committed"),!o.has("push")&&!o.has("pr")&&i.push("don't push"),o.has("pr")||i.push("don't open a pull request");let c=`Yes \u2014 go ahead: ${a.join(", then ")}.`;return i.length&&(c+=` ${i.join(", and ").replace(/^./,l=>l.toUpperCase())}.`),o.has("pr")&&(c+=" If a PR for this branch is already open, just push the update instead."),c}var Ab=3e4;function yd(t,e=Date.now()){let n=[],r=null;for(let o of t||[]){let s=o.payload||{},a=s.ts??o.ts;if(o.type==="pr.offer.answered"&&s.action==="pr"){r={box:o.boxId,from:a,lastActivity:a};continue}!r||a==null||a<r.from||(o.type==="pr.offer.answered"?r=null:o.type==="pr.state.changed"&&s.change==="opened"?(n.push({box:r.box,from:r.from,to:a,settled:!0}),r=null):o.type==="turn.user"&&o.source!=="hook"&&!s.kind&&String(s.text||"").trim()?r=null:o.boxId===r.box&&/^(?:turn|tool)\./.test(o.type)&&(r.lastActivity=Math.max(r.lastActivity,a)))}return r&&e-r.lastActivity<=Ab&&n.push({box:r.box,from:r.from,to:e+1e12}),n}function bd(t,e){let n=new Set;for(let r of e||[]){if(!r.settled)continue;let o=null;for(let s of t||[])s.kind!=="message"||s.role!=="assistant"||s.box!==r.box||s.ts==null||s.ts<r.from||s.ts>r.to||(!o||s.ts>=o.ts)&&(o=s);o&&n.add(o)}return n}import{execFile as Ib}from"node:child_process";import{promisify as Rb}from"node:util";import _o from"node:fs/promises";import kd from"node:path";import _b from"node:os";var xd=process.env.BOSTRAT_STATE_DIR||kd.join(process.env.HOME||_b.tmpdir(),".bostrat"),Ja=kd.join(xd,"pr-links.json"),bt=(t,e)=>`${t}#${Number(e)}`,Eo=null,Va=null,D=Promise.resolve();async function xt(){return Eo||(Va??=_o.readFile(Ja,"utf8").then(t=>JSON.parse(t)?.links??{}).catch(()=>({})).then(t=>Eo=t).finally(()=>Va=null),Va)}async function Z(){let t=JSON.stringify({links:Eo},null,2),e=`${Ja}.${process.pid}.tmp`;await _o.mkdir(xd,{recursive:!0}),await _o.writeFile(e,t),await _o.rename(e,Ja)}var Eb=new Set(["api","observed","reconciled"]);async function Bt({repo:t,number:e,trackId:n,url:r=null,headRef:o=null,title:s=null,source:a="matched"}){if(!t||!Number.isInteger(Number(e))||!n)return{link:null,created:!1};let i=await xt(),c=bt(t,e);return i[c]?{link:i[c],created:!1}:(i[c]={repo:t,number:Number(e),trackId:n,url:r,headRef:o,title:s,source:a,announced:Eb.has(a),firstSeen:Date.now()},D=D.then(Z,Z),await D.catch(()=>{}),{link:i[c],created:!0})}async function xr(t,e){if(!t||e==null)return null;let n=(await xt())[bt(t,e)];return!n||n.announced?null:(n.announced=!0,D=D.then(Z,Z),await D.catch(()=>{}),n)}async function Tt(t,e){return!t||e==null?null:(await xt())[bt(t,e)]??null}async function Qe(t,e,n){if(!t||e==null)return null;let r=(await xt())[bt(t,e)];return r?(r.state=n,r.resolvedAt=Date.now(),delete r.armedAt,delete r.autoMerge,delete r.archiveOnMerge,D=D.then(Z,Z),await D.catch(()=>{}),r):null}async function Sd(t,e,{headSha:n=null,trackId:r=null}={}){if(!t||e==null)return null;let o=(await xt())[bt(t,e)];return o?(o.ciFix={delegatedAt:Date.now(),headSha:n,trackId:r},D=D.then(Z,Z),await D.catch(()=>{}),o):null}async function vd(t,e,{headSha:n=null,trackId:r=null}={}){if(!t||e==null)return null;let o=(await xt())[bt(t,e)];return o?(o.reconcile={delegatedAt:Date.now(),headSha:n,trackId:r},D=D.then(Z,Z),await D.catch(()=>{}),o):null}async function Io(t,e,{headSha:n=null}={}){if(!t||e==null)return null;let r=(await xt())[bt(t,e)];return!r||!r.ciFix?null:(r.ciFixAwait={since:Date.now(),trackId:r.ciFix.trackId??null,headSha:n},delete r.ciFix,D=D.then(Z,Z),await D.catch(()=>{}),r)}async function Ro(t,e){if(!t||e==null)return null;let n=(await xt())[bt(t,e)];return!n||!n.ciFixAwait?null:(delete n.ciFixAwait,D=D.then(Z,Z),await D.catch(()=>{}),n)}async function $o(t,e,{headSha:n=null}={}){if(!t||e==null)return null;let r=(await xt())[bt(t,e)];return!r||!r.reconcile?null:(r.reconcileAwait={since:Date.now(),trackId:r.reconcile.trackId??null,headSha:n},delete r.reconcile,D=D.then(Z,Z),await D.catch(()=>{}),r)}async function Oo(t,e){if(!t||e==null)return null;let n=(await xt())[bt(t,e)];return!n||!n.reconcileAwait?null:(delete n.reconcileAwait,D=D.then(Z,Z),await D.catch(()=>{}),n)}async function Td(t,e,n){if(!t||e==null||!n)return null;let r=(await xt())[bt(t,e)];return r?(r.lastHeadSha=n,D=D.then(Z,Z),await D.catch(()=>{}),r):null}async function Co(t,e,{merge:n=null}={}){if(!t||e==null)return null;let r=(await xt())[bt(t,e)];return r?(r.armedAt=Date.now(),n&&(r.autoMerge={method:n.method||"merge",armedAt:r.armedAt}),D=D.then(Z,Z),await D.catch(()=>{}),r):null}async function za(t,e,n=!0){if(!t||e==null)return null;let r=(await xt())[bt(t,e)];return r?(n?r.archiveOnMerge=!0:delete r.archiveOnMerge,D=D.then(Z,Z),await D.catch(()=>{}),r):null}async function Mo(t,e){if(!t||e==null)return null;let n=(await xt())[bt(t,e)];return!n||!n.armedAt&&!n.autoMerge?null:(delete n.armedAt,delete n.autoMerge,delete n.archiveOnMerge,D=D.then(Z,Z),await D.catch(()=>{}),n)}async function Ad(){return Object.values(await xt()).filter(t=>t.state!=="merged"&&t.state!=="closed")}async function Xa(t,e){let n={working:!1,prompted:!1};if(!t)return n;let r=await pt(t,{lines:$t}).catch(()=>"");if(!dr(e,r))return Tl(e,r)?{working:!0,prompted:!1}:n;if(Cn(e,r)){let o=await me(t).catch(()=>r);if(Xe({name:t,agent:e},o))return{working:!1,prompted:!0}}return{working:!0,prompted:!1}}async function Po(t,e){return(await Xa(t,e)).working}var _d=Rb(Ib),Ed=[4e3,15e3,45e3],$b=2500,Ob=5*6e4;async function Ya(t,e,{midTurn:n=Po,pollMs:r=$b,capMs:o=Ob}={}){let s=Date.now()+o;for(;await n(t,e).catch(()=>!1);){if(Date.now()>=s)return!1;await new Promise(a=>setTimeout(a,r))}return!0}var Cb=6e4,Mb=9e4,Pb=2e3,Nb=/(?:^|[;&|]\s*|\)\s*)gh\s+(?:[^\s;&|"'`$]+\s+)*?pr\s+create\b/,Bb=/(?:^|[;&|]\s*|\)\s*)gh\s+(?:[^\s;&|"'`$]+\s+)*?pr\s+merge\b/,Db=/\s--web\b/,Id=/(?:--repo|-R)[\s=]+([\w.-]+\/[\w.-]+)/;function Za(t,e){return t.slice(e).replace(/^[\s;&|)]+/,"").replace(/"[^"]*"|'[^']*'/g,'""').split(/[;&|]/)[0]}function Rd(t){let e=String(t||""),n=e.match(Nb);if(!n)return null;let r=Za(e,n.index);return Db.test(r)?null:{repo:r.match(Id)?.[1]??null}}var jb=new Set(["--subject","-t","--body","-b","--body-file","-F","--match-head-commit","--author-email","-A","--repo","-R"]);function $d(t){let e=String(t||""),n=e.match(Bb);if(!n)return null;let r=Za(e,n.index),o=Za(e,n.index+n[0].length),s=null,a=o.trim().split(/\s+/).filter(Boolean);for(let i=0;i<a.length;i++){let c=a[i];if(c.startsWith("-")){jb.has(c)&&i++;continue}s=c.replace(/^["']+|["']+$/g,"")||null;break}return{repo:r.match(Id)?.[1]??null,selector:s,auto:/\s--auto\b/.test(r)}}var Lb="number,url,title,headRefName,createdAt";async function Ub(t,e){let n=async s=>{let{stdout:a}=await _d("gh",["pr","list","--repo",t,"--author","@me","--state","all","--limit","10","--json",Lb],{timeout:3e4,maxBuffer:4194304,env:{...process.env,...s}});return JSON.parse(a||"[]")},r=await Gt({slug:t}),o=await n(r);return!o.length&&r.GH_TOKEN&&(o=await n({}).catch(()=>[])),o.filter(s=>Date.parse(s.createdAt)>=e-Cb).sort((s,a)=>Date.parse(a.createdAt)-Date.parse(s.createdAt))}async function Fb(t,e){if(e)return[e];let n=[],r=await Y(t.workspace?.base||t.cwd).catch(()=>null);r&&n.push(r);for(let o of await re().catch(()=>[])){let s=await Y(o.path).catch(()=>null);s&&!n.includes(s)&&n.push(s)}return n}async function Hb(t,e,n){let r=t.workspace?.branch,o=[];for(let s of await Fb(t,e)){let a;try{a=await Ub(s,n)}catch{continue}for(let i of a){let c=await Tt(s,i.number);if(!(c&&(c.trackId!==t.id||c.announced))){if(r&&i.headRefName===r)return{slug:s,pr:i};o.push({slug:s,pr:i})}}}return o.length?(o.sort((s,a)=>Date.parse(a.pr.createdAt)-Date.parse(s.pr.createdAt)),o[0]):null}var ke=new Set;function Od(t,e,{delays:n=Ed,turnEnd:r}={}){let o=Rd(e.command);if(!o)return null;let s=!!$d(e.command),a=`${t.id}|${e.id??e.ts??e.command}`;if(ke.has(a))return null;ke.add(a);let i=e.ts??Date.now(),c=(async()=>{try{for(let l of n){await new Promise(g=>setTimeout(g,l));let u=await Hb(t,o.repo,i).catch(()=>null);if(!u)continue;let{slug:d,pr:f}=u,{link:w,created:m}=await Bt({repo:d,number:f.number,trackId:t.id,url:f.url,headRef:f.headRefName,title:f.title,source:"observed"});return!m&&!(w?.trackId===t.id&&await xr(d,f.number))?null:(await Ya(e.box,e.agent,r),await R({id:t.id,cwd:t.cwd},{type:"pr.state.changed",actor:"agent",source:"transcript",boxId:e.box??null,provider:e.agent??null,payload:{ts:Date.now(),change:"opened",number:f.number,url:f.url,title:f.title,branch:f.headRefName,repo:d,...s?{selfMerge:!0}:{}}}),f)}return null}finally{ke.delete(a)}})();return c.catch(()=>ke.delete(a)),c}var Wb="number,url,title,headRefName,state";function Cd(t,e,{delays:n=Ed,turnEnd:r,openedWait:o}={}){let s=$d(e.command);if(!s)return null;let a=`merge|${t.id}|${e.id??e.ts??e.command}`;if(ke.has(a))return null;ke.add(a);let i=(async()=>{try{let c=s.repo,l=s.selector,u=l?.match(/github\.com\/([\w.-]+\/[\w.-]+)\/pull\/(\d+)/);if(u&&([,c,l]=u),c||=await Y(t.workspace?.base||t.cwd).catch(()=>null),l||=t.workspace?.branch,!c||!l)return null;let d=async(w,m)=>{let g=await Tt(c,w.number);return g&&g.trackId!==t.id?null:(g||await Bt({repo:c,number:w.number,trackId:t.id,url:w.url,headRef:w.headRefName,title:w.title,source:"merge-observed"}),await Co(c,w.number),m&&((await B(t).catch(()=>[])).some(S=>S.type==="pr.state.changed"&&(S.payload?.change==="auto-armed"||S.payload?.change==="merged")&&S.payload?.number===w.number)||(await Ya(e.box,e.agent,r),await R({id:t.id,cwd:t.cwd},{type:"pr.state.changed",actor:"agent",source:"transcript",boxId:e.box??null,provider:e.agent??null,payload:{ts:Date.now(),change:"auto-armed",number:w.number,url:w.url,title:w.title,branch:w.headRefName,repo:c}}))),w)},f=null;for(let w of n){await new Promise(T=>setTimeout(T,w));let m;try{let{stdout:T}=await _d("gh",["pr","view",String(l),"--repo",c,"--json",Wb],{timeout:3e4,maxBuffer:4194304,env:{...process.env,...await Gt({slug:c})}});m=JSON.parse(T||"null")}catch{continue}if(!m?.number)continue;if(m.state==="CLOSED")return null;if(m.state!=="MERGED"){if(f=m,s.auto)return d(m,!0);continue}let g=await Tt(c,m.number);if(g&&g.trackId!==t.id)return null;g||await Bt({repo:c,number:m.number,trackId:t.id,url:m.url,headRef:m.headRefName,title:m.title,source:"merge-observed"}),await Ya(e.box,e.agent,r);let x=await B(t).catch(()=>[]);if(x.some(T=>T.type==="pr.state.changed"&&T.payload?.change==="merged"&&T.payload?.number===m.number))return null;let A=T=>T.some(E=>E.type==="pr.state.changed"&&E.payload?.change==="opened"&&E.payload?.number===m.number),S=0;if(!A(x)){if(Rd(e.command)&&await xr(c,m.number)){let T=Date.now();S=T+1,await R({id:t.id,cwd:t.cwd},{type:"pr.state.changed",actor:"agent",source:"transcript",boxId:e.box??null,provider:e.agent??null,payload:{ts:T,change:"opened",number:m.number,url:m.url,title:m.title,branch:m.headRefName,repo:c,selfMerge:!0}})}else if((await Tt(c,m.number))?.announced){let T=o?.capMs??Mb,E=o?.pollMs??Pb,J=Date.now()+T;for(;Date.now()<J&&(await new Promise(M=>setTimeout(M,E)),!A(await B(t).catch(()=>[]))););}}return await R({id:t.id,cwd:t.cwd},{type:"pr.state.changed",actor:"agent",source:"transcript",boxId:e.box??null,provider:e.agent??null,payload:{ts:Math.max(Date.now(),S),change:"merged",number:m.number,url:m.url,title:m.title,branch:m.headRefName,repo:c}}),await tn(c,m.number).catch(()=>{}),await Qe(c,m.number,"merged"),m}return f?d(f,!1):null}finally{ke.delete(a)}})();return i.catch(()=>ke.delete(a)),i}function ti(t){return t.sourceId?`${t.role}|id:${t.sourceId}`:`${t.role}|${t.ts??"?"}|${String(t.text||"").slice(0,40)}`}function Md(t){return t.id?`cmd|${t.box}|id:${t.id}`:`cmd|${t.box}|${t.ts??"?"}|${String(t.command||"").slice(0,40)}`}function Pd(t){return`job|${t.box}|id:${t.id}`}var Qa=new Map,No=new Map;async function ei(t){return Qa.has(t.id)?Qa.get(t.id):(No.has(t.id)||No.set(t.id,B(t).then(e=>{let n={keys:new Set,cmdN:0};for(let r of e)if((r.type==="turn.user"||r.type==="turn.agent")&&r.payload?.text!=null){let o=r.type==="turn.user"?"user":"assistant";r.payload.sourceId&&n.keys.add(ti({role:o,sourceId:r.payload.sourceId})),n.keys.add(ti({role:o,ts:r.payload.ts,text:r.payload.text}))}else if(r.type==="tool.called"&&r.source==="transcript"&&r.payload?.command){let o=r.payload;n.keys.add(Md({id:o.id,box:r.boxId,ts:o.ts,command:o.command})),Number(o.n)>n.cmdN&&(n.cmdN=Number(o.n))}else r.type==="job.started"&&r.payload?.id&&n.keys.add(Pd({id:r.payload.id,box:r.boxId}));return Qa.set(t.id,n),n}).finally(()=>No.delete(t.id))),No.get(t.id))}async function Nd(t,e){try{let n=(await ei(t)).keys;for(let r of e||[]){if(!r||r.ts==null||!String(r.text||"").trim()&&!r.images?.length)continue;let o=ti(r);if(!n.has(o)&&(n.add(o),await R(t,{type:r.role==="user"?"turn.user":"turn.agent",actor:r.role==="user"?"user":"agent",source:"transcript",boxId:r.box,provider:r.agent||null,payload:{text:r.text,ts:r.ts,model:r.model||null,tools:r.tools||void 0,boxRole:r.boxRole,sourceId:r.sourceId||void 0,note:r.note||void 0,images:r.images?.length?r.images:void 0}}),r.role==="assistant"&&r.box===t.leadBox&&t.workspace?.branch)){let s=hd(r.text);if(s){let{offered:a,toPr:i}=gd(s);await R(t,{type:"pr.offered",actor:"agent",source:"transcript",boxId:r.box,provider:r.agent||null,payload:{offerId:qb(),ts:r.ts,excerpt:s,branch:t.workspace.branch,base:t.workspace.baseRef?.replace(/^origin\//,"")||null,boxRole:r.boxRole,offered:a||void 0,toPr:i||void 0}})}}}}catch{}}async function Bd(t,e){try{let n=await ei(t),r=[...e||[]].filter(o=>o&&String(o.command||"").trim()).sort((o,s)=>(o.ts||0)-(s.ts||0));for(let o of r){let s=Md(o);n.keys.has(s)||(n.keys.add(s),n.cmdN+=1,await R(t,{type:"tool.called",actor:"agent",source:"transcript",boxId:o.box,provider:o.agent||null,payload:{id:o.id??null,tool:o.tool||null,command:o.command,description:o.description||"",summary:o.summary||o.command,ts:o.ts??null,boxRole:o.boxRole,n:n.cmdN}}),Od(t,o),Cd(t,o))}}catch{}}async function Dd(t,e){try{let n=await ei(t),r=[...e||[]].filter(o=>o&&o.id).sort((o,s)=>(o.ts||0)-(s.ts||0));for(let o of r){let s=Pd(o);n.keys.has(s)||(n.keys.add(s),await R(t,{type:"job.started",actor:"agent",source:"transcript",boxId:o.box,provider:o.agent||null,payload:{id:o.id,kind:o.kind||"agent",name:o.name||null,detail:o.detail||null,ts:o.ts??null,boxRole:o.boxRole}}))}}catch{}}var Gb=/^Base directory for this skill:\s*(\S+)/,Kb=/^Run the "([^"]+)" workflow\./,Vb=/<command-name>\s*\/?([a-z0-9:_-]+)\s*<\/command-name>/i,Jb=/^<local-command-(?:stdout|caveat)>/i,zb=/^\[Image: original \d+x\d+, displayed at \d+x\d+\.\s*Multiply coordinates by [\d.]+ to map to original image\.\]$/,Xb=/^<task-notification>/i,Yb=/<summary>([\s\S]*?)<\/summary>/i,Zb=/<task-id>([\s\S]*?)<\/task-id>/i,Qb=/<tool-use-id>([\s\S]*?)<\/tool-use-id>/i,tk=/<status>([\s\S]*?)<\/status>/i,ek=/<result>([\s\S]*?)(?:<\/result>|$)/i;var ni=new Set(["model","clear","compact","login"]);function jd(t){return ni.has(String(t||"").replace(/^\//,"").toLowerCase())}var nk=/^\/([a-z0-9:_-]+)$/i;function Bo(t){let e=String(t||"").trim();if(!e)return null;let n=e.match(Gb);if(n)return{kind:"skill",name:n[1].split("/").filter(Boolean).pop()||"skill"};let r=e.match(Kb);if(r)return{kind:"skill",name:r[1]};let o=e.match(Vb);if(o){let a=o[1].toLowerCase();return ni.has(a)?{kind:"suppress"}:{kind:"command",command:`/${a}`}}let s=e.match(nk);if(s&&ni.has(s[1].toLowerCase()))return{kind:"suppress"};if(Jb.test(e))return{kind:"suppress"};if(zb.test(e))return{kind:"suppress"};if(Xb.test(e)){let a=c=>rk(e.match(c)?.[1].trim()||"")||null,i=a(ek);return{kind:"task",summary:a(Yb)||"",taskId:a(Zb),toolUseId:a(Qb),status:a(tk)?.toLowerCase()||null,resultHead:i?i.length>600?`${i.slice(0,600).trimEnd()}\u2026`:i:null}}return null}function rk(t){return t.replace(/</g,"<").replace(/>/g,">").replace(/"/g,'"').replace(/'/g,"'").replace(/&/g,"&")}function Ld(t){return t?.kind==="skill"?`Ran the ${t.name} skill`:t?.kind==="command"?`Ran ${t.command}`:t?.kind==="task"?t.summary||"A background task finished":""}function Ud(t){return t?.kind==="skill"?"skill-run":t?.kind==="task"?"task-done":"command-run"}var at=t=>t.payload?.ts??t.ts;function Do(t){let e=[],n=new Set,r=new Set,o=new Map,s=new Map,a=new Set,i=new Map,c=new Set,l=new Map,u=/^Agent "([\s\S]+)" finished$/,d=new Map;for(let h of t||[]){if(h.type!=="tool.called"||h.source!=="transcript")continue;let p=String(h.payload?.command||"");if(/\bbostrat-mock\s+(?:add|update)\b/.test(p))for(let b of p.matchAll(/[\w./~-]+\.(?:png|jpe?g|gif|webp|bmp|svg)\b/gi)){let v=b[0].split("/").pop().toLowerCase();d.has(v)||d.set(v,[]),d.get(v).push(h.payload?.ts??h.ts)}}let f=3*6e4,w=20*6e4,m=(h,p)=>!!(h?.name&&d.get(String(h.name).toLowerCase()))?.some(v=>v>=p-f&&v<=p+w);for(let h of t||[])if(h.type==="turn.retracted"){r.add(`${h.boxId}|${h.payload?.saidTs}`);let p=Ye(h.payload?.text);p&&(o.has(h.boxId)||o.set(h.boxId,[]),o.get(h.boxId).push(p))}else if(h.type==="turn.user"&&h.source==="orchestrator"){let p=Ye(h.payload?.text);p&&(s.has(h.boxId)||s.set(h.boxId,new Set),s.get(h.boxId).add(p))}else if(h.type==="job.started"&&h.payload?.id)a.add(h.payload.id),(h.payload.kind||"agent")==="agent"&&h.payload.name&&c.add(h.payload.name);else if(h.type==="turn.user"&&h.payload?.note?.kind==="task"){let p=h.payload.note,b={status:p.status||null,summary:p.summary||null,resultHead:p.resultHead??null,ts:at(h)};p.toolUseId&&i.set(p.toolUseId,b);let v=String(p.summary||"").match(u);v&&l.set(v[1],b)}let g=new Map,x=new Map,A=new Map,S=new Map,T=new Map,E=new Map,J=new Map,M=new Map,I=(h,p)=>{let b=o.get(h);if(!b?.length)return!1;let v=Ye(p),ot=s.get(h);return b.some($=>{if(!v.startsWith($))return!1;let z=Ye(v.slice($.length));return!!z&&!!ot?.has(z)})};for(let h of t||[]){let p=h.payload||{};switch(h.type){case"turn.user":{if(h.source==="hook"||!String(p.text||"").trim()&&!p.images?.length)break;{let b=p.note||Bo(p.text);if(b){if(b.kind==="task"){if(b.toolUseId&&a.has(b.toolUseId))break;let v=String(b.summary||"").match(u);if(!b.toolUseId&&v&&c.has(v[1]))break}if(b.kind==="command"&&jd(b.command))break;b.kind!=="suppress"&&e.push({id:h.id,kind:"event",type:Ud(b),name:b.name??null,command:b.command??null,summary:b.summary??null,ts:at(h),box:h.boxId,agent:h.provider});break}}if(r.has(`${h.boxId}|${at(h)}`)||h.source==="transcript"&&I(h.boxId,p.text)||p.kind==="mock-picked"||p.kind==="ci-fix"||p.kind==="reconcile"||p.kind==="pr-go"||p.kind==="push-go"||p.kind==="spin-up-view")break;for(let b of g.values())b.status="stale";g.clear(),e.push({id:h.id,kind:"message",role:"user",text:p.text,ts:at(h),box:h.boxId,agent:h.provider,boxRole:p.boxRole,...h.actor==="orchestrator"?{orchestration:p.kind||!0}:{},...p.sourceId?{sourceId:p.sourceId}:{},...p.attachments?.length?{attachments:p.attachments}:{},...p.images?.length?{images:p.images}:{}});break}case"turn.agent":{J.clear();let b=(p.images||[]).filter(ot=>!m(ot,at(h)));if(!String(p.text||"").trim()&&!b.length)break;if(h.provider==="cursor"){let ot=`${h.boxId}|${p.text}`;if(n.has(ot))break;n.add(ot)}let v=h.provider==="codex"?(p.tools||[]).filter(ot=>!va.has(ot)&&!gr.has(ot)):p.tools||[];e.push({id:h.id,kind:"message",role:"assistant",text:p.text,ts:at(h),box:h.boxId,agent:h.provider,boxRole:p.boxRole,...v.length?{tools:v}:{},...p.model?{model:p.model}:{},...p.sourceId?{sourceId:p.sourceId}:{},...b.length?{images:b}:{}});break}case"job.started":{if(!p.id)break;let b=at(h),v=i.get(p.id);if(!v&&(p.kind||"agent")==="agent"&&p.name){let N=l.get(p.name);N&&N.ts>=b&&(v=N)}let ot={jobId:p.id,jobKind:p.kind||"agent",name:p.name||null,detail:p.detail||null,status:v?v.status&&v.status!=="completed"?"failed":"done":"running",startedTs:b,finishedTs:v?.ts??null,summary:v?.summary??null,resultHead:v?.resultHead??null},$=M.get(h.boxId);if($&&$.jobs.some(N=>(N.finishedTs??1/0)>=b))$.jobs.push(ot);else{let N={id:h.id,kind:"job-dock",dockId:p.id,ts:b,box:h.boxId,agent:h.provider,jobs:[ot]};M.set(h.boxId,N),e.push(N)}break}case"handoff.done":e.push(xe(h,"lead-handoff"));break;case"subtask.delegated":e.push({...xe(h,"subtask-delegated"),to:h.boxId,toAgent:h.provider});break;case"subtask.merged":e.push(xe(h,"subtask-merged"));break;case"pr.state.changed":if(!p.change)break;if(p.change==="opened"){for(let b of g.values())b.status="stale";g.clear()}e.push(xe(h,`pr-${p.change}`));break;case"pr.offered":{if(!p.offerId)break;for(let v of g.values())v.status="stale";g.clear();let b={id:h.id,kind:"pr-offer",offerId:p.offerId,excerpt:p.excerpt||null,branch:p.branch||null,base:p.base||null,offered:Array.isArray(p.offered)&&p.offered.length?p.offered:null,toPr:p.toPr&&typeof p.toPr=="object"?{number:p.toPr.number??null}:null,status:"open",ts:at(h),box:h.boxId,agent:h.provider};g.set(p.offerId,b),x.set(p.offerId,b),e.push(b);break}case"pr.offer.answered":{let b=x.get(p.offerId);b&&(b.status=p.action||"dismiss",b.actions=Array.isArray(p.actions)?p.actions:null,g.delete(p.offerId));break}case"box.state.changed":{let b=p.change==="model"?"model-change":p.change;if(!b)break;let v=xe(h,b);b==="model-change"&&(v.to=p.model),e.push(v);break}case"view.state.changed":{if(p.change!=="created"||p.restarted)break;e.push({id:h.id,kind:"view-card",viewId:p.viewId,name:p.name,slug:p.slug,port:p.port,origin:p.origin,ts:at(h),box:h.boxId,agent:h.provider});break}case"view.spinup.failed":{e.push({id:h.id,kind:"view-spinup-failed",name:p.name||null,reason:p.reason||"no-view",ts:at(h),box:h.boxId,agent:h.provider});break}case"mock.state.changed":{if(p.change==="picked"){e.push({id:h.id,kind:"mock-picked",mockId:p.mockId,name:p.name,group:p.group??null,version:p.version??null,siblings:p.siblings??[],ts:at(h),box:h.boxId,agent:h.provider});break}if(p.change!=="created")break;e.push({id:h.id,kind:"mock-card",mockId:p.mockId,name:p.name,group:p.group??null,ts:at(h),box:h.boxId,agent:h.provider});break}case"runbook.state.changed":{if(p.change!=="created")break;e.push({id:h.id,kind:"runbook-card",runbookId:p.runbookId,name:p.name,ts:at(h),box:h.boxId,agent:h.provider});break}case"ticket.linked":{e.push({id:h.id,kind:"ticket-card",provider:p.provider||"linear",key:p.key,url:p.url,title:p.title??null,linkSource:p.linkSource??null,ts:at(h),box:h.boxId,agent:h.provider});break}case"ticket.move.offered":{if(!p.offerId)break;for(let v of A.values())v.key===p.key&&(v.status="stale",A.delete(v.offerId));let b={id:h.id,kind:"ticket-offer",offerId:p.offerId,provider:p.provider||"linear",key:p.key,url:p.url??null,phase:p.phase??null,from:p.from??null,to:p.to?.name??null,prNumber:p.prNumber??null,status:"open",ts:at(h),box:h.boxId,agent:h.provider};A.set(p.offerId,b),S.set(p.offerId,b),e.push(b);break}case"ticket.move.answered":{let b=S.get(p.offerId);b&&(b.status=p.action==="moved"?"moved":"dismiss",A.delete(p.offerId));break}case"ticket.state.changed":{for(let b of A.values())b.key===p.key&&b.to===p.to&&(b.status="stale",A.delete(b.offerId));if(p.mode==="offer")break;e.push(xe(h,"ticket-state"));break}case"ticket.commented":{e.push({id:h.id,kind:"ticket-comment",key:p.key,url:p.url??null,excerpt:p.excerpt??null,ts:at(h),box:h.boxId,agent:h.provider});break}case"ticket.flow.changed":e.push(xe(h,"ticket-flow"));break;case"ticket.create.offered":{if(!p.proposalId)break;for(let v of T.values())v.status="stale";T.clear();let b={id:h.id,kind:"ticket-create-offer",proposalId:p.proposalId,provider:p.provider||"linear",epic:p.epic??null,issues:Array.isArray(p.issues)?p.issues:[],team:p.team??null,needsTeam:!!p.needsTeam,linkCurrentTrack:!!p.linkCurrentTrack,status:"open",ts:at(h),box:h.boxId,agent:h.provider};T.set(p.proposalId,b),E.set(p.proposalId,b),e.push(b);break}case"ticket.create.answered":{let b=E.get(p.proposalId);b&&(b.status=p.action==="created"?"created":"dismiss",b.createdCount=p.createdCount??null,b.failedCount=p.failedCount??null,T.delete(p.proposalId));break}case"ticket.created":{e.push({id:h.id,kind:"ticket-created",items:Array.isArray(p.items)?p.items:[],failed:Array.isArray(p.failed)?p.failed:[],team:p.team??null,ts:at(h),box:h.boxId,agent:h.provider});break}case"error":{if(!p.kind)break;e.push(xe(h,p.kind));break}case"provider.connected":{e.push({id:h.id,kind:"event",type:"provider-connected",provider:p.provider||h.provider,ts:at(h),at:at(h),box:h.boxId,agent:h.provider});break}case"prompt.answered":{if(p.control||!p.question)break;let b=p.groupId||p.promptId;if(!b)break;let v=J.get(b);v||(v={id:h.id,kind:"prompt",groupId:b,provider:h.provider,box:h.boxId,agent:h.provider,questionTotal:p.questionTotal??1,questions:[],ts:at(h)},J.set(b,v));let ot=p.questionIndex??0,$={index:ot,header:p.header||"",question:p.question,multiSelect:!!p.multiSelect,chosen:Array.isArray(p.chosen)?p.chosen:[]},z=v.questions.findIndex(W=>W.index===ot);z>=0?v.questions[z]=$:v.questions.push($),v.questions.sort((W,_)=>W.index-_.index),v.questionTotal=Math.max(v.questionTotal,p.questionTotal??1),new Set(v.questions.map(W=>W.index)).size>=v.questionTotal&&(e.push(v),J.delete(b));break}default:break}}return sk(e.sort((h,p)=>(h.ts??0)-(p.ts??0)))}var ok=2*6e4;function sk(t){let e=new Map,n=[];for(let r of t){if(r.kind!=="message"||!r.box){n.push(r);continue}let o=r.role==="assistant"&&!String(r.text||"").trim()&&r.images?.length,s=e.get(r.box);if(o&&s&&(r.ts??0)-s.lastTs<=ok){s.item.images.push(...r.images),s.lastTs=r.ts??s.lastTs;continue}if(o){let a={...r,images:[...r.images]};e.set(r.box,{item:a,lastTs:r.ts??0}),n.push(a)}else e.delete(r.box),n.push(r)}return n}function ri(t,e=null){let n=[];for(let o of t||[]){if(o.type!=="tool.called"||o.source!=="transcript")continue;let s=o.payload||{};String(s.command||"").trim()&&n.push({id:s.id??null,tool:s.tool||null,command:s.command,description:s.description||"",summary:ak(s),ts:s.ts??o.ts,n:s.n,box:o.boxId,agent:o.provider,boxRole:s.boxRole})}if(!n.length)return[];let r=(e??Do(t)).filter(o=>o.kind==="message");return tu(n,r)}function ak(t){let e=String(t.summary||"").trim();return e&&e!==t.command&&e!==In(t.command)?e:ye(t.command,t.description||"")}function xe(t,e){let{ts:n,change:r,...o}=t.payload||{};return{id:t.id,kind:"event",type:e,...o,at:at(t),ts:at(t)}}var oi={"claude-opus-4-8":{input:5,output:25,cacheWrite:6.25,cacheRead:.5},"claude-sonnet-5":{input:3,output:15,cacheWrite:3.75,cacheRead:.3},"claude-sonnet-4-6":{input:3,output:15,cacheWrite:3.75,cacheRead:.3},"claude-haiku-4-5":{input:1,output:5,cacheWrite:1.25,cacheRead:.1},"claude-fable-5":{input:10,output:50,cacheWrite:12.5,cacheRead:1},"gpt-5.5":{input:5,output:30,cacheWrite:5,cacheRead:.5},"gpt-5.6-sol":{input:5,output:30,cacheWrite:6.25,cacheRead:.5},"gpt-5.6-terra":{input:2.5,output:15,cacheWrite:3.125,cacheRead:.25},"gpt-5.6-luna":{input:1,output:6,cacheWrite:1.25,cacheRead:.1}},Fd={opus:"claude-opus-4-8",opusplan:"claude-opus-4-8",default:"claude-opus-4-8",sonnet:"claude-sonnet-4-6",haiku:"claude-haiku-4-5",fable:"claude-fable-5","gpt-5.6":"gpt-5.6-sol"};function Hd(t){if(!t)return null;let e=String(t).trim();return oi[e]?e:Fd[e]?Fd[e]:Object.keys(oi).find(r=>e.startsWith(r))||null}var jo=t=>Hd(t)!==null;function Kt(t,e={}){let n=oi[Hd(t)];return n?((e.input||0)*n.input+(e.output||0)*n.output+(e.cacheWrite||0)*n.cacheWrite+(e.cacheRead||0)*n.cacheRead)/1e6:0}function ik(t){let e=[],n=null;for(let r of String(t||"").split(`
|
|
61
|
+
`)){let o=r.trim();if(!o||o[0]!=="{")continue;let s;try{s=JSON.parse(o)}catch{continue}let a=s.payload||{};if(s.type==="turn_context"&&a.model&&(n=a.model),s.type==="event_msg"&&a.type==="token_count"&&a.info?.last_token_usage){let i=a.info.last_token_usage;e.push({ts:s.timestamp?Date.parse(s.timestamp):null,model:n,last:{input:i.input_tokens||0,cachedInput:i.cached_input_tokens||0,output:i.output_tokens||0,reasoningOutput:i.reasoning_output_tokens||0},occupancy:i.total_tokens??null,contextWindow:a.info.model_context_window??null})}}return e}var si=new Map;async function ck(t,e){let n=`${t.id}:${e.name}`;if(!si.has(n)){let r=await B(t),o=new Set;for(let s of r)s.type==="cost.recorded"&&s.boxId===e.name&&s.payload?.codexTs!=null&&o.add(s.payload.codexTs);si.set(n,o)}return si.get(n)}async function Wd(t,e){try{if(e?.agent!=="codex"||!e.cwd)return;let n=await Ge(e.cwd,e.pinned?e.sessionId:null);if(!n)return;let r=await ck(t,e),o=ik(await vt(n.file,262144)),s=o[o.length-1];for(let a of o){if(a.ts==null||r.has(a.ts))continue;r.add(a.ts);let i={input:Math.max(0,a.last.input-a.last.cachedInput),output:a.last.output,cacheRead:a.last.cachedInput,cacheWrite:0},c=a.model||e.model||"gpt-5.5";await R(t,{type:"cost.recorded",actor:"agent",source:"transcript",boxId:e.name,provider:"codex",payload:{usd:Kt(c,i),model:c,tokens:i,reasoningOutput:a.last.reasoningOutput||void 0,codexTs:a.ts,ts:a.ts}}),a===s&&a.contextWindow&&await R(t,{type:"context.usage",actor:"agent",source:"transcript",boxId:e.name,provider:"codex",payload:{occupancy:a.occupancy,window:a.contextWindow,pct:a.occupancy!=null?a.occupancy/a.contextWindow:null,model:a.model||null,ts:a.ts}})}}catch{}}import Lo from"node:fs";import ii from"node:path";import lk from"node:os";var uk=process.env.BOSTRAT_STATE_DIR||ii.join(process.env.HOME||lk.tmpdir(),".bostrat"),Gd=(t=process.env)=>!!t.BOSTRAT_CURSOR_USAGE_COOKIE,dk="https://cursor.com/api/dashboard/get-filtered-usage-events",qd=100;async function fk({sinceMs:t,cookie:e,fetchImpl:n=fetch}){let r=[];for(let o=1;o<=20;o++){let s=await n(dk,{method:"POST",headers:{"content-type":"application/json",origin:"https://cursor.com",cookie:`WorkosCursorSessionToken=${e}`},body:JSON.stringify({teamId:0,startDate:String(t),endDate:String(Date.now()),page:o,pageSize:qd})});if(!s.ok)throw new Error(`cursor usage feed: HTTP ${s.status}`);let a=await s.json(),i=a?.usageEventsDisplay||a?.usageEvents||[];if(r.push(...i),i.length<qd)break}return r}function pk(t){if(!t)return null;let e=Number(t.timestamp)||null,n=t.tokenUsage||null,r=n?{input:n.inputTokens||0,output:n.outputTokens||0,cacheRead:n.cacheReadTokens||0,cacheWrite:n.cacheWriteTokens||0}:null,o=t.conversationId||null;return{id:[o||"?",e??"?",r?`${r.input}.${r.output}.${r.cacheRead}.${r.cacheWrite}`:"?"].join(":"),conversationId:o,ts:e,model:t.model||t.modelIntent||null,kind:t.kindLabel||t.kind||null,isHeadless:t.isHeadless===!0,tokens:r,vendorCents:n?.totalCents??null,chargedCents:t.chargedCents??null}}var mk=5*6e4,Se={at:0,rows:null,error:null},Uo=()=>ii.join(uk,"cursor-usage.json");function Kd(){try{return JSON.parse(Lo.readFileSync(Uo(),"utf8"))}catch{return{sinceMs:0,seen:[]}}}function hk(t){try{Lo.mkdirSync(ii.dirname(Uo()),{recursive:!0});let e=Uo()+".tmp";Lo.writeFileSync(e,JSON.stringify(t)),Lo.renameSync(e,Uo())}catch{}}var wk=5e3;async function gk({fetchImpl:t,now:e=Date.now()}={}){if(Se.rows&&e-Se.at<mk)return Se.rows;let n=process.env.BOSTRAT_CURSOR_USAGE_COOKIE;if(!n)return null;let r=Kd(),o=Math.max(r.sinceMs-6e4,e-24*36e5);try{let s=await fk({sinceMs:o,cookie:n,fetchImpl:t});Se={at:e,rows:s.map(pk).filter(Boolean),error:null}}catch(s){Se={at:e,rows:null,error:String(s?.message||s)}}return Se.rows}var Vd=()=>({enabled:Gd(),lastPollAt:Se.at||null,error:Se.error}),ai=new Map;async function yk(t,e){let n=`${t.id}:${e.name}`;if(!ai.has(n)){let r=await B(t),o=new Set;for(let s of r)s.type==="cost.recorded"&&s.boxId===e.name&&s.payload?.cursorEventId&&o.add(s.payload.cursorEventId);ai.set(n,o)}return ai.get(n)}async function Jd(t,e,{fetchImpl:n}={}){try{if(e?.agent!=="cursor"||!e.cwd||!Gd())return;let r=await gk({fetchImpl:n});if(!r||!r.length)return;let o=await lu(e.cwd,e.pinned?e.sessionId:null);if(!o?.chatId)return;let s=await yk(t,e),a=Kd(),i=new Set(a.seen||[]),c=a.sinceMs||0;for(let l of r){if(!l.conversationId||l.conversationId!==o.chatId||s.has(l.id)||i.has(`${t.id}:${e.name}:${l.id}`))continue;s.add(l.id),i.add(`${t.id}:${e.name}:${l.id}`),l.ts&&l.ts>c&&(c=l.ts);let u=l.model||o.models[o.models.length-1]||"cursor-unknown",d=l.tokens||null,f=l.vendorCents!=null?l.vendorCents/100:0;!f&&d&&(f=Kt(u,d)),!f&&l.chargedCents!=null&&(f=l.chargedCents/100),await R(t,{type:"cost.recorded",actor:"agent",source:"vendor",boxId:e.name,provider:"cursor",payload:{usd:f,model:u,...d?{tokens:d}:{},...l.vendorCents!=null?{vendorCents:l.vendorCents}:{},...l.chargedCents!=null?{chargedCents:l.chargedCents}:{},...l.kind?{kind:l.kind}:{},conversationId:l.conversationId,cursorEventId:l.id,ts:l.ts??void 0}})}hk({sinceMs:c,seen:[...i].slice(-wk)})}catch{}}import vr from"node:fs/promises";import At from"node:path";import bk from"node:os";import kk from"node:net";import{randomUUID as xk}from"node:crypto";import{execFile as Sk}from"node:child_process";import{promisify as vk}from"node:util";var Jt=vk(Sk),Tk=process.env.BOSTRAT_STATE_DIR||At.join(process.env.HOME||bk.tmpdir(),".bostrat"),Qd=At.join(Tk,"views.json"),[zd,Xd]=(process.env.BOSTRAT_VIEW_PORTS||"4300-4349").split("-").map(t=>Number(t)||0),ve=Number(process.env.BOSTRAT_VIEW_MAX)||3,Ak="bostrat-view-",Nn=t=>`${Ak}${t}`,Yd=new Set(["bostrat","www","api","views","app"]),Fo=t=>Object.assign(new Error(t),{status:400}),_k=new Set(["npm","pnpm","yarn","bun"]),ui="package.json",Ek=/[|;&`]|\$\(/,Ik=new Set(["sh","bash","zsh","dash","env","exec","nohup","time"]),Rk=/(^|\s)(-C|--cwd|--manifest-path|--dir)(=|\s)/,tf=t=>String(t).trim().split(/\s+/).filter(Boolean),$k=/^[A-Za-z_][A-Za-z0-9_]*=/;function Ok(t){for(let e of tf(t))if(!$k.test(e))return e;return null}function ef(t){let e=tf(t),n=e.indexOf("--");return n===-1?e:e.slice(0,n)}function Ck(t,e){let n=ef(e);for(let r=0;r<n.length;r++){let o=/^--prefix=(.+)$/.exec(n[r]);if(o)return At.resolve(t,o[1]);if(n[r]==="--prefix"&&n[r+1])return At.resolve(t,n[r+1])}return t}var Mk=t=>vr.stat(t).then(e=>e.isDirectory(),()=>!1),nf=t=>vr.stat(t).then(e=>e.isFile(),()=>!1),Ho=t=>vr.realpath(t).catch(()=>At.resolve(t));async function Pk(t,e){if(At.resolve(t)===At.resolve(e))return!0;let[n,r]=await Promise.all([Ho(t),Ho(e)]);return n===r}async function Nk(t){let n=(await vr.readdir(t,{withFileTypes:!0}).catch(()=>[])).filter(o=>o.isDirectory()&&!o.name.startsWith(".")&&o.name!=="node_modules");return(await Promise.all(n.map(async o=>await nf(At.join(t,o.name,ui))?`./${o.name}`:null))).filter(Boolean)}async function Bk(t,e){let[n,r]=await Promise.all([Ho(t),Ho(e)]),o=At.relative(n,r);if(o&&(o.startsWith("..")||At.isAbsolute(o)))throw Fo(`serve directory escapes the Track workspace: ${e}`)}async function Dk(t,e){if(!await Mk(t))throw Fo(`serve directory does not exist: ${t}`);let n=Ok(e);if(!n)return;if(!n.includes("/")&&!await Ls(n,{loginShell:!0,failOpen:!0}))throw Fo(`\`${n}\` is not on the box's PATH \u2014 install it in the box first`);if(Ek.test(e)||Ik.has(n)||!_k.has(n)||!ef(e).includes("run")||Rk.test(e))return;let r=Ck(t,e);if(await nf(At.join(r,ui)))return;let o=await Nk(r),s=o.length?` \u2014 found one in ${o.join(", ")}; run bostrat-view from there`:"";throw Fo(`no ${ui} in ${r}${s}`)}var Vt=null,ci=null;async function ae(){return Vt||(ci||=(async()=>{try{Vt=JSON.parse(await vr.readFile(Qd,"utf8"))}catch{Vt={views:[],adoptWatermarks:{}}}finally{ci=null}return Vt.views||=[],Vt.adoptWatermarks||={},Vt})(),ci)}async function zt(){await qt(Qd,Vt)}function se(t,e,n={}){return R({id:t.trackId,cwd:t.cwd},{type:"view.state.changed",actor:"orchestrator",source:"orchestrator",boxId:t.box||null,payload:{ts:Date.now(),change:e,viewId:t.id,name:t.name,slug:t.slug,port:t.port,...n}}).catch(()=>{})}var Sr=new Map,pi=Number(process.env.BOSTRAT_VIEW_JOB_TIMEOUT)||3e5,rf=12e3;function mi(t){return Sr.get(t)||null}function of(t,{name:e,box:n}={}){let r=Sr.get(t);if(r&&!r.finishedAt)throw new Error("a view is already being spun up for this Track");let o={trackId:t,name:e||"preview",box:n||null,step:"spinning-up",startedAt:Date.now(),finishedAt:null,viewId:null,error:null};return Sr.set(t,o),o}function di(t,e){let n=Sr.get(t);n&&!n.finishedAt&&!n.viewId&&(n.viewId=e,n.step="booting")}function Wo(t,{error:e=null}={}){let n=Sr.get(t);return!n||n.finishedAt?null:(n.finishedAt=Date.now(),e&&(n.error=e),n)}function sf(t){let e=[];for(let n of t?.events||[])n.type!=="view-spinup"||!n.box||!n.startedAt||!n.endedAt||e.push({box:n.box,from:n.startedAt,to:n.endedAt});return e}function fi(t){return String(t||"").trim().toLowerCase().replace(/[^a-z0-9]+/g,"-").replace(/^-+|-+$/g,"").slice(0,40).replace(/-+$/g,"")||"view"}function jk(t,e){let n=fi(e),r=new Set(t.views.map(o=>o.slug));if(!r.has(n)&&!Yd.has(n))return n;for(let o=2;;o++){let s=`${n}-${o}`;if(!r.has(s)&&!Yd.has(s))return s}}function Tr(t,{timeoutMs:e=1200}={}){return new Promise(n=>{let r=kk.connect({port:t,host:"127.0.0.1"}),o=s=>{r.destroy(),n(s)};r.setTimeout(e,()=>o(!1)),r.once("connect",()=>o(!0)),r.once("error",()=>o(!1))})}async function hi(t){if(process.platform!=="darwin"){await Jt("fuser",["-k",`${t}/tcp`]).catch(()=>{});return}let e="";try{({stdout:e}=await Jt("lsof",["-t","-sTCP:LISTEN","-i",`tcp:${t}`]))}catch{return}for(let n of e.split(/\s+/).filter(Boolean)){let r=Number(n);if(!(!r||r===process.pid))try{process.kill(r,"SIGKILL")}catch{}}}async function Lk(t,e){let n=new Set(t.views.map(r=>r.port));if(e){let r=Number(e);if(!Number.isInteger(r)||r<1024||r>65535)throw new Error("invalid port");if(n.has(r))throw new Error(`port ${r} is already owned by a view`);return r}for(let r=zd;r<=Xd;r++)if(!n.has(r)&&!await Tr(r,{timeoutMs:250}))return r;throw new Error(`no free view port in ${zd}-${Xd}`)}async function Uk(t){try{let{stdout:e}=await Jt(O,C("list-sessions","-F","#{session_name}"));return e.split(`
|
|
62
|
+
`).some(n=>n.trim()===Nn(t))}catch{return!1}}async function Ar(t){try{await Jt(O,C("kill-session","-t",Nn(t)))}catch{}}async function wi(t){if(!await ir())throw new Error("host tmux server not running and auto-start failed \u2014 cannot spawn a view server");let e=Nn(t.slug),n=t.serveCwd??t.cwd,r=At.dirname(process.execPath);await Jt(O,C("new-session","-d","-s",e,"-c",n)),await Jt(O,C("send-keys","-t",e,"-l",`export PORT=${t.port} PATH="${r}:$PATH"`)),await Jt(O,C("send-keys","-t",e,"Enter")),await Jt(O,C("send-keys","-t",e,"-l",t.cmd)),await Jt(O,C("send-keys","-t",e,"Enter"))}async function Zd(t,e=12){try{let{stdout:n}=await Jt(O,C("capture-pane","-t",Nn(t),"-p"));return String(n||"").trimEnd().split(`
|
|
61
63
|
`).filter(Boolean).slice(-e).join(`
|
|
62
|
-
`)}catch{return""}}var se=new Map,$k=12e4;async function Ok(t){if(t.state==="stopped"||t.state==="error")return!1;let e=t.origin!=="detected",n=e?await Rk(t.slug):!0;if(e&&!n){let s=t.state==="starting";return t.state=s?"error":"stopped",s&&(t.error=await qd(t.slug)||"server exited before listening"),se.delete(t.id),await ae(t,t.state,t.error?{error:t.error}:{}),!0}if(await xr(t.port))return se.delete(t.id),t.lastHealthyAt=Date.now(),t.state!=="live"?(t.state="live",t.error=null,await ae(t,"live"),!0):!1;if(t.state==="starting")return Date.now()-t.createdAt>$k?(t.state="error",t.error=await qd(t.slug)||"never started listening",await ae(t,"error",{error:t.error}),!0):!1;let o=(se.get(t.id)||0)+1;return se.set(t.id,o),o>=3&&t.state==="live"?(t.state="unhealthy",!0):!1}var ni=!1;async function Uo(){if(!ni){ni=!0;try{let t=await ie(),e=!1;for(let n of t.views)await Ok(n)&&(e=!0);e&&await zt()}finally{ni=!1}}}async function Zd({trackId:t}={}){await Uo();let e=await ie();return(t?e.views.filter(r=>r.trackId===t):e.views).map(r=>({...r}))}async function Qd(t){let n=(await ie()).views.find(r=>r.id===t);return n?{...n}:null}function ui(t){return Vt&&Vt.views.find(e=>e.slug===t)||null}async function di({trackId:t,name:e,cmd:n,cwd:r,serveCwd:o,port:s,box:a,origin:i="agent",branch:c=null}){if(!t)throw new Error("trackId required");if(typeof e!="string"||!e.trim())throw new Error("name (string) required");if(i!=="detected"&&(typeof n!="string"||!n.trim()))throw new Error("cmd (string) required");if(!r)throw new Error("cwd required");let l=o!=null,u=Tt.resolve(r,o??"."),d=i==="detected"?null:async()=>{await Ak(r,u),await _k(u,n)},f=await ie();if(i!=="detected"){let g=si(e),x=String(n).trim(),A=f.views.find(S=>S.trackId===t&&(S.slug===g||si(S.name)===g||S.cmd===x&&Tt.resolve(S.serveCwd??S.cwd)===u));if(A)return Ck(f,A,{cmd:x,box:a,branch:c,serveCwd:l?u:void 0,validate:d})}if(d&&await d(),f.views.filter(g=>g.state==="live"||g.state==="starting").length>=ve)throw new Error(`view cap reached (${ve} live) \u2014 stop one first`);let h={id:uk().slice(0,8),trackId:t,box:a||null,name:e.trim(),slug:Ek(f,e),cmd:n?String(n).trim():null,cwd:r,serveCwd:u,branch:c,port:await Ik(f,s),origin:i,state:"starting",error:null,createdAt:Date.now(),lastHealthyAt:null};return i!=="detected"&&await li(h),f.views.push(h),await zt(),await ae(h,"created",{origin:i}),oi(t,h.id),i==="detected"&&await xr(h.port)&&(h.state="live",h.lastHealthyAt=Date.now(),await zt(),await ae(h,"live")),{...h}}async function Ck(t,e,{cmd:n,box:r,branch:o,serveCwd:s,validate:a}){r&&(e.box=r),o&&(e.branch=o);let i=e.cmd!==n,c=s!==void 0&&!await vk(e.serveCwd??e.cwd,s);if((e.state==="live"||e.state==="starting")&&!i&&!c&&await xr(e.port))return await zt(),oi(e.trackId,e.id),{...e,reused:!0};if(a&&await a(),t.views.filter(d=>d.id!==e.id&&(d.state==="live"||d.state==="starting")).length>=ve)throw new Error(`view cap reached (${ve} live) \u2014 stop one first`);return e.origin==="detected"?(await ci(e.port),e.origin="agent"):await Sr(e.slug),e.cmd=n,s!==void 0&&(e.serveCwd=s),e.state="starting",e.error=null,e.createdAt=Date.now(),se.delete(e.id),await li(e),await zt(),await ae(e,"created",{restarted:!0,reused:!0}),oi(e.trackId,e.id),{...e,reused:!0}}async function tf(t){let n=(await ie()).views.find(r=>r.id===t);if(!n)throw new Error("view not found");return n.origin==="detected"?await ci(n.port):await Sr(n.slug),n.state!=="stopped"&&(n.state="stopped",se.delete(n.id),await zt(),await ae(n,"stopped")),{...n}}async function ef(t){let e=await ie(),n=e.views.find(o=>o.id===t);if(!n)throw new Error("view not found");if(!n.cmd)throw new Error("a detected view has no known command \u2014 recreate it as a managed view");if(e.views.filter(o=>o.id!==t&&(o.state==="live"||o.state==="starting")).length>=ve)throw new Error(`view cap reached (${ve} live) \u2014 stop one first`);return await Sr(n.slug),n.state="starting",n.error=null,n.createdAt=Date.now(),se.delete(n.id),await li(n),await zt(),await ae(n,"created",{restarted:!0}),{...n}}async function nf(t){let e=await ie(),n=e.views.findIndex(o=>o.id===t);if(n===-1)throw new Error("view not found");let r=e.views[n];return r.origin==="detected"?await ci(r.port):await Sr(r.slug),e.views.splice(n,1),se.delete(r.id),await zt(),await ae(r,"deleted"),{ok:!0,id:t,slug:r.slug}}async function rf(t){let e=await ie(),n=e.views.filter(r=>r.trackId===t);for(let r of n)r.origin!=="detected"&&await Sr(r.slug),se.delete(r.id);return n.length&&(e.views=e.views.filter(r=>r.trackId!==t),await zt()),n.length}var Mk=/https?:\/\/(?:localhost|127\.0\.0\.1|0\.0\.0\.0)(?::(\d{2,5}))?(?:\/|\b)/g;async function of(t,e){if(!t?.id||!Array.isArray(e)||!e.length)return[];let n=await ie(),r=n.adoptWatermarks[t.id]??null,o=e.filter(c=>c.role==="assistant"&&c.ts!=null&&(r==null?!1:c.ts>r)),s=e.reduce((c,l)=>l.ts>c?l.ts:c,r??0);if(n.adoptWatermarks[t.id]!==s&&(n.adoptWatermarks[t.id]=s,await zt()),!o.length)return[];let a=new Set;for(let c of o)for(let l of String(c.text||"").matchAll(Mk)){let u=Number(l[1]||80);u>=1024&&u<=65535&&a.add(u)}if(!a.size)return[];let i=[];for(let c of a)if(c!==Number(process.env.PORT||3362)&&!n.views.some(l=>l.port===c)&&await xr(c))try{i.push(await di({trackId:t.id,name:`${t.name} preview`,cmd:null,cwd:t.cwd,port:c,origin:"detected",branch:t.workspace?.branch??null}))}catch{}return i}import{execFile as Nk}from"node:child_process";import{promisify as Pk}from"node:util";var Wo=Pk(Nk),Bk={timeout:3e4,maxBuffer:8*1024*1024};async function Lt(t,e,n={}){let r=await Gt({cwd:t}),{stdout:o}=await Wo("gh",e,{cwd:t,...Bk,...n,env:{...process.env,...r,...n.env||{}}});return String(o||"")}var Fo=null;async function mi(){if(Fo&&Date.now()-Fo.at<6e4)return Fo.data;let t;try{let e=await Wo("gh",["auth","status"],{timeout:1e4});t={installed:!0,authenticated:!0,login:`${e.stdout||""}${e.stderr||""}`.match(/account\s+(\S+)/)?.[1]??null}}catch(e){t=e?.code==="ENOENT"?{installed:!1,authenticated:!1,login:null}:{installed:!0,authenticated:!1,login:null}}if(t.installed&&!t.authenticated)try{let e=await lr();e.configured&&(t={installed:!0,authenticated:!0,login:e.username,via:"broker"})}catch{}return Fo={at:Date.now(),data:t},t}async function Dk(t){try{let{stdout:e}=await Wo("git",["-C",t,"config","--get","remote.origin.url"],{timeout:5e3});return/github\.com/.test(e)}catch{return!1}}function af(t){let e=t.name||t.context||"check",n=t.detailsUrl||t.targetUrl||null,r;return t.__typename==="StatusContext"?r=t.state==="SUCCESS"?"pass":t.state==="PENDING"||t.state==="EXPECTED"?"pending":"fail":t.status&&t.status!=="COMPLETED"?r="pending":["SUCCESS","NEUTRAL","SKIPPED"].includes(t.conclusion)?r="pass":r="fail",{name:e,state:r,url:n}}function fi(t){let e=(t||[]).map(af);return{total:e.length,passed:e.filter(n=>n.state==="pass").length,failed:e.filter(n=>n.state==="fail").length,pending:e.filter(n=>n.state==="pending").length,failing:e.filter(n=>n.state==="fail").map(({name:n,url:r})=>({name:n,url:r}))}}var hi="number,title,url,headRefName,headRefOid,baseRefName,isDraft,author,reviewDecision,mergeable,mergeStateStatus,statusCheckRollup,autoMergeRequest,additions,deletions,createdAt,updatedAt",cf=hi.replace("statusCheckRollup,","");function wi(t){return/Resource not accessible by personal access token/.test(String(t?.stderr||t?.message||""))}async function lf(t,e){if(!e)return null;let n=await Z(t);if(!n)return null;try{let r=await Lt(t,["api",`repos/${n}/actions/runs?head_sha=${encodeURIComponent(e)}&per_page=100`,"--jq",".workflow_runs"]),o=new Map;for(let s of JSON.parse(r||"[]"))o.has(s.name)||o.set(s.name,s);return[...o.values()]}catch{return null}}function uf(t){return(t||[]).map(e=>({name:e.name,detailsUrl:e.html_url||null,status:String(e.status||"").toUpperCase(),conclusion:e.conclusion?String(e.conclusion).toUpperCase():null}))}async function df(t,e){let n=await lf(t,e);return n===null?{...fi([]),unavailable:!0}:fi(uf(n))}function Ho(t,e=fi(t.statusCheckRollup)){return{number:t.number,title:t.title,url:t.url,author:t.author?.login??null,headRefName:t.headRefName,headSha:t.headRefOid??null,baseRefName:t.baseRefName,isDraft:!!t.isDraft,reviewDecision:t.reviewDecision||null,mergeable:t.mergeable||"UNKNOWN",mergeStateStatus:t.mergeStateStatus||"UNKNOWN",additions:t.additions??0,deletions:t.deletions??0,createdAt:t.createdAt,updatedAt:t.updatedAt,checks:e,green:e.failed===0&&e.pending===0&&t.mergeStateStatus==="CLEAN",autoMergeArmed:!!t.autoMergeRequest,mergeabilityPending:!1}}var jk=9e4,Nn=new Map;function Lk(t,e,n){let r=new Set;for(let o of e){let s=`${t}#${o.number}`;if(o.mergeable!=="UNKNOWN"&&o.mergeStateStatus!=="UNKNOWN"){Nn.delete(s);continue}r.add(s),Nn.has(s)||Nn.set(s,n),o.mergeabilityPending=n-Nn.get(s)<jk}for(let o of Nn.keys())o.startsWith(`${t}#`)&&!r.has(o)&&Nn.delete(o);return e}async function Tr(t,e){let n=await Te(t),r=Number(e);if(!Number.isInteger(r)||r<=0)throw new Error("invalid PR number");try{let o=await Lt(n.path,["pr","view",String(r),"--json",hi]);return Ho(JSON.parse(o))}catch(o){if(!wi(o))throw o;let s=await Lt(n.path,["pr","view",String(r),"--json",cf]),a=JSON.parse(s);return Ho(a,await df(n.path,a.headRefOid))}}var sf=(t,e)=>e.number-t.number;async function vr(t){try{let e=await Lt(t,["pr","list","--json",hi,"--limit","50"]);return JSON.parse(e||"[]").map(n=>Ho(n)).sort(sf)}catch(e){if(!wi(e))throw e;let n=await Lt(t,["pr","list","--json",cf,"--limit","50"]);return(await Promise.all(JSON.parse(n||"[]").map(async r=>Ho(r,await df(t,r.headRefOid))))).sort(sf)}}var Uk=2e4,Fk=1500,Pn=null;function Xe(){Pn=null}async function Hk(t,e){if(!t)return e;for(let n of e){let r=await Pt(t,n.number).catch(()=>null);if(!r)continue;r.autoMerge&&(n.autoMergeArmed=!0);let o=s=>n.headSha&&s.headSha&&n.headSha!==s.headSha;r.ciFix&&(o(r.ciFix)?await To(t,n.number,{headSha:n.headSha}).catch(()=>{}):n.ciFix={delegatedAt:r.ciFix.delegatedAt,trackId:r.ciFix.trackId??null}),r.reconcile&&(o(r.reconcile)?await _o(t,n.number,{headSha:n.headSha}).catch(()=>{}):n.reconcile={delegatedAt:r.reconcile.delegatedAt,trackId:r.reconcile.trackId??null})}return e}async function ff(){let t=await mi();if(!t.authenticated)return{auth:t,repos:[]};let e=Pn?.pending?Fk:Uk;if(Pn&&Date.now()-Pn.at<e)return{auth:t,repos:Pn.data};let n=[];for(let a of await oe())await Dk(a.path)&&n.push(a);let r=Date.now(),o=await Promise.all(n.map(async a=>{try{let i=Lk(a.path,await vr(a.path),r);return await Hk(await Z(a.path).catch(()=>null),i),{name:a.name,path:a.path,prs:i}}catch(i){return{name:a.name,path:a.path,prs:[],error:ce(i)}}})),s=o.some(a=>a.prs.some(i=>i.mergeabilityPending||i.checks.pending>0||i.autoMergeArmed));return Pn={at:Date.now(),data:o,pending:s},{auth:t,repos:o}}async function Te(t){let e=(await oe()).find(n=>n.path===t);if(!e)throw new Error(`unknown repo: ${t}`);return e}async function gi(t){if(!t)return null;for(let e of await oe())if(await Z(e.path).catch(()=>null)===t)return e.path;return null}var qo=new Set(["squash","merge","rebase"]);async function Ar(t,e,{method:n="squash"}={}){let r=await Te(t);if(!qo.has(n))throw new Error(`invalid merge method: ${n}`);let o=Number(e);if(!Number.isInteger(o)||o<=0)throw new Error("invalid PR number");let s=["pr","merge",String(o),`--${n}`];if(n!=="rebase"&&(await Gt({cwd:r.path})).GH_TOKEN){let a=await aa().catch(()=>null);a&&s.push("--body",`Merged by bostrat on behalf of @${a}`)}try{await Lt(r.path,s)}catch(a){throw new Error(ce(a))}finally{Xe()}return{ok:!0,number:o}}async function pf(t,e){let n=await Te(t),r=Number(e);if(!Number.isInteger(r)||r<=0)throw new Error("invalid PR number");try{await Lt(n.path,["pr","merge",String(r),"--disable-auto"])}catch(o){throw new Error(ce(o))}finally{Xe()}return{ok:!0,number:r}}var Wk=2500,qk=9e4,Gk=t=>new Promise(e=>setTimeout(e,t));function mf(t){return t.green&&!t.isDraft}function Kk(t){return t.isDraft?"draft":t.mergeable==="CONFLICTING"?`conflicts with ${t.baseRefName}`:t.checks.failed>0?"checks failing":t.checks.pending>0?"checks running":t.mergeable==="UNKNOWN"||t.mergeStateStatus==="UNKNOWN"?"mergeability unresolved":t.mergeStateStatus==="BEHIND"?`behind ${t.baseRefName}`:t.mergeStateStatus==="BLOCKED"?"blocked by branch protection":`not clean (${t.mergeStateStatus})`}var pi=new Map;function hf(){let t={};for(let[e,n]of pi)t[e]={...n};return t}async function wf(t,{method:e="merge",onMerged:n=null}={}){let r=await Te(t);if(!qo.has(e))throw new Error(`invalid merge method: ${e}`);if(pi.get(r.path)?.running)throw new Error("a merge-all is already running for this repo");let o=(await vr(r.path)).filter(mf).sort((a,i)=>Date.parse(a.createdAt)-Date.parse(i.createdAt));if(!o.length)throw new Error("no PRs ready to merge");let s={running:!0,startedAt:Date.now(),finishedAt:null,method:e,total:o.length,current:o[0].number,queue:o.map(a=>({number:a.number,title:a.title})),merged:[],skipped:[],failed:[]};return pi.set(r.path,s),Jk(s,o,{listPrs:()=>vr(r.path),merge:a=>Ar(r.path,a,{method:e}),sleep:Gk,onMerged:n}).catch(()=>{}),{ok:!0,repo:r.path,total:s.total,queue:o.map(a=>a.number)}}async function Vk(t,e){let n=Date.now()+(e.settleMaxMs??qk),r=null;for(;;){let o=null;try{if(o=await e.listPrs(),r=null,!o.some(a=>t.includes(a.number)&&(a.mergeable==="UNKNOWN"||a.mergeStateStatus==="UNKNOWN")))return o}catch(s){r=s}if(Date.now()>=n){if(o)return o;if(r)throw r;return[]}await e.sleep(Wk)}}async function Jk(t,e,n){try{let r=e;for(let o=0;o<e.length;o++){let s=e[o];if(t.current=s.number,o>0)try{r=await Vk(e.slice(o).map(i=>i.number),n)}catch(i){for(let c of e.slice(o))t.failed.push({number:c.number,title:c.title,reason:`couldn't re-check PR state: ${ce(i)}`});break}let a=r.find(i=>i.number===s.number);if(!a){t.skipped.push({number:s.number,title:s.title,reason:"no longer open"});continue}if(!mf(a)){t.skipped.push({number:a.number,title:a.title,reason:Kk(a)});continue}try{await n.merge(a.number),t.merged.push({number:a.number,title:a.title});try{await n.onMerged?.(a)}catch{}}catch(i){t.failed.push({number:a.number,title:a.title,reason:ce(i)})}}}finally{t.current=null,t.running=!1,t.finishedAt=Date.now(),Xe()}}async function gf(t,{title:e,body:n="",base:r=null}={}){if(typeof e!="string"||!e.trim())throw new Error("title required");try{await Wo("git",["-C",t,"push","-u","origin","HEAD"],{timeout:12e4,maxBuffer:4*1024*1024})}catch(l){throw new Error(`push failed: ${ce(l)}`)}let o=n||"";if((await Gt({cwd:t})).GH_TOKEN){let l=await aa().catch(()=>null);l&&(o=`${o?`${o}
|
|
64
|
+
`)}catch{return""}}var oe=new Map,Fk=12e4;async function Hk(t){if(t.state==="stopped"||t.state==="error")return!1;let e=t.origin!=="detected",n=e?await Uk(t.slug):!0;if(e&&!n){let s=t.state==="starting";return t.state=s?"error":"stopped",s&&(t.error=await Zd(t.slug)||"server exited before listening"),oe.delete(t.id),await se(t,t.state,t.error?{error:t.error}:{}),!0}if(await Tr(t.port))return oe.delete(t.id),t.lastHealthyAt=Date.now(),t.state!=="live"?(t.state="live",t.error=null,await se(t,"live"),!0):!1;if(t.state==="starting")return Date.now()-t.createdAt>Fk?(t.state="error",t.error=await Zd(t.slug)||"never started listening",await se(t,"error",{error:t.error}),!0):!1;let o=(oe.get(t.id)||0)+1;return oe.set(t.id,o),o>=3&&t.state==="live"?(t.state="unhealthy",!0):!1}var li=!1;async function qo(){if(!li){li=!0;try{let t=await ae(),e=!1;for(let n of t.views)await Hk(n)&&(e=!0);e&&await zt()}finally{li=!1}}}async function af({trackId:t}={}){await qo();let e=await ae();return(t?e.views.filter(r=>r.trackId===t):e.views).map(r=>({...r}))}async function cf(t){let n=(await ae()).views.find(r=>r.id===t);return n?{...n}:null}function gi(t){return Vt&&Vt.views.find(e=>e.slug===t)||null}async function yi({trackId:t,name:e,cmd:n,cwd:r,serveCwd:o,port:s,box:a,origin:i="agent",branch:c=null}){if(!t)throw new Error("trackId required");if(typeof e!="string"||!e.trim())throw new Error("name (string) required");if(i!=="detected"&&(typeof n!="string"||!n.trim()))throw new Error("cmd (string) required");if(!r)throw new Error("cwd required");let l=o!=null,u=At.resolve(r,o??"."),d=i==="detected"?null:async()=>{await Bk(r,u),await Dk(u,n)},f=await ae();if(i!=="detected"){let g=fi(e),x=String(n).trim(),A=f.views.find(S=>S.trackId===t&&(S.slug===g||fi(S.name)===g||S.cmd===x&&At.resolve(S.serveCwd??S.cwd)===u));if(A)return Wk(f,A,{cmd:x,box:a,branch:c,serveCwd:l?u:void 0,validate:d})}if(d&&await d(),f.views.filter(g=>g.state==="live"||g.state==="starting").length>=ve)throw new Error(`view cap reached (${ve} live) \u2014 stop one first`);let m={id:xk().slice(0,8),trackId:t,box:a||null,name:e.trim(),slug:jk(f,e),cmd:n?String(n).trim():null,cwd:r,serveCwd:u,branch:c,port:await Lk(f,s),origin:i,state:"starting",error:null,createdAt:Date.now(),lastHealthyAt:null};return i!=="detected"&&await wi(m),f.views.push(m),await zt(),await se(m,"created",{origin:i}),di(t,m.id),i==="detected"&&await Tr(m.port)&&(m.state="live",m.lastHealthyAt=Date.now(),await zt(),await se(m,"live")),{...m}}async function Wk(t,e,{cmd:n,box:r,branch:o,serveCwd:s,validate:a}){r&&(e.box=r),o&&(e.branch=o);let i=e.cmd!==n,c=s!==void 0&&!await Pk(e.serveCwd??e.cwd,s);if((e.state==="live"||e.state==="starting")&&!i&&!c&&await Tr(e.port))return await zt(),di(e.trackId,e.id),{...e,reused:!0};if(a&&await a(),t.views.filter(d=>d.id!==e.id&&(d.state==="live"||d.state==="starting")).length>=ve)throw new Error(`view cap reached (${ve} live) \u2014 stop one first`);return e.origin==="detected"?(await hi(e.port),e.origin="agent"):await Ar(e.slug),e.cmd=n,s!==void 0&&(e.serveCwd=s),e.state="starting",e.error=null,e.createdAt=Date.now(),oe.delete(e.id),await wi(e),await zt(),await se(e,"created",{restarted:!0,reused:!0}),di(e.trackId,e.id),{...e,reused:!0}}async function lf(t){let n=(await ae()).views.find(r=>r.id===t);if(!n)throw new Error("view not found");return n.origin==="detected"?await hi(n.port):await Ar(n.slug),n.state!=="stopped"&&(n.state="stopped",oe.delete(n.id),await zt(),await se(n,"stopped")),{...n}}async function uf(t){let e=await ae(),n=e.views.find(o=>o.id===t);if(!n)throw new Error("view not found");if(!n.cmd)throw new Error("a detected view has no known command \u2014 recreate it as a managed view");if(e.views.filter(o=>o.id!==t&&(o.state==="live"||o.state==="starting")).length>=ve)throw new Error(`view cap reached (${ve} live) \u2014 stop one first`);return await Ar(n.slug),n.state="starting",n.error=null,n.createdAt=Date.now(),oe.delete(n.id),await wi(n),await zt(),await se(n,"created",{restarted:!0}),{...n}}async function df(t){let e=await ae(),n=e.views.findIndex(o=>o.id===t);if(n===-1)throw new Error("view not found");let r=e.views[n];return r.origin==="detected"?await hi(r.port):await Ar(r.slug),e.views.splice(n,1),oe.delete(r.id),await zt(),await se(r,"deleted"),{ok:!0,id:t,slug:r.slug}}async function ff(t){let e=await ae(),n=e.views.filter(r=>r.trackId===t);for(let r of n)r.origin!=="detected"&&await Ar(r.slug),oe.delete(r.id);return n.length&&(e.views=e.views.filter(r=>r.trackId!==t),await zt()),n.length}var qk=/https?:\/\/(?:localhost|127\.0\.0\.1|0\.0\.0\.0)(?::(\d{2,5}))?(?:\/|\b)/g;async function pf(t,e){if(!t?.id||!Array.isArray(e)||!e.length)return[];let n=await ae(),r=n.adoptWatermarks[t.id]??null,o=e.filter(c=>c.role==="assistant"&&c.ts!=null&&(r==null?!1:c.ts>r)),s=e.reduce((c,l)=>l.ts>c?l.ts:c,r??0);if(n.adoptWatermarks[t.id]!==s&&(n.adoptWatermarks[t.id]=s,await zt()),!o.length)return[];let a=new Set;for(let c of o)for(let l of String(c.text||"").matchAll(qk)){let u=Number(l[1]||80);u>=1024&&u<=65535&&a.add(u)}if(!a.size)return[];let i=[];for(let c of a)if(c!==Number(process.env.PORT||3362)&&!n.views.some(l=>l.port===c)&&await Tr(c))try{i.push(await yi({trackId:t.id,name:`${t.name} preview`,cmd:null,cwd:t.cwd,port:c,origin:"detected",branch:t.workspace?.branch??null}))}catch{}return i}import{execFile as Gk}from"node:child_process";import{promisify as Kk}from"node:util";var Vo=Kk(Gk),Vk={timeout:3e4,maxBuffer:8*1024*1024};async function Ut(t,e,n={}){let r=await Gt({cwd:t}),{stdout:o}=await Vo("gh",e,{cwd:t,...Vk,...n,env:{...process.env,...r,...n.env||{}}});return String(o||"")}var Go=null;async function xi(){if(Go&&Date.now()-Go.at<6e4)return Go.data;let t;try{let e=await Vo("gh",["auth","status"],{timeout:1e4});t={installed:!0,authenticated:!0,login:`${e.stdout||""}${e.stderr||""}`.match(/account\s+(\S+)/)?.[1]??null}}catch(e){t=e?.code==="ENOENT"?{installed:!1,authenticated:!1,login:null}:{installed:!0,authenticated:!1,login:null}}if(t.installed&&!t.authenticated)try{let e=await pr();e.configured&&(t={installed:!0,authenticated:!0,login:e.username,via:"broker"})}catch{}return Go={at:Date.now(),data:t},t}async function Jk(t){try{let{stdout:e}=await Vo("git",["-C",t,"config","--get","remote.origin.url"],{timeout:5e3});return/github\.com/.test(e)}catch{return!1}}function hf(t){let e=t.name||t.context||"check",n=t.detailsUrl||t.targetUrl||null,r;return t.__typename==="StatusContext"?r=t.state==="SUCCESS"?"pass":t.state==="PENDING"||t.state==="EXPECTED"?"pending":"fail":t.status&&t.status!=="COMPLETED"?r="pending":["SUCCESS","NEUTRAL","SKIPPED"].includes(t.conclusion)?r="pass":r="fail",{name:e,state:r,url:n}}function bi(t){let e=(t||[]).map(hf);return{total:e.length,passed:e.filter(n=>n.state==="pass").length,failed:e.filter(n=>n.state==="fail").length,pending:e.filter(n=>n.state==="pending").length,failing:e.filter(n=>n.state==="fail").map(({name:n,url:r})=>({name:n,url:r}))}}var Si="number,title,url,headRefName,headRefOid,baseRefName,isDraft,author,reviewDecision,mergeable,mergeStateStatus,statusCheckRollup,autoMergeRequest,additions,deletions,createdAt,updatedAt",wf=Si.replace("statusCheckRollup,","");function vi(t){return/Resource not accessible by personal access token/.test(String(t?.stderr||t?.message||""))}async function gf(t,e){if(!e)return null;let n=await Y(t);if(!n)return null;try{let r=await Ut(t,["api",`repos/${n}/actions/runs?head_sha=${encodeURIComponent(e)}&per_page=100`,"--jq",".workflow_runs"]),o=new Map;for(let s of JSON.parse(r||"[]"))o.has(s.name)||o.set(s.name,s);return[...o.values()]}catch{return null}}function yf(t){return(t||[]).map(e=>({name:e.name,detailsUrl:e.html_url||null,status:String(e.status||"").toUpperCase(),conclusion:e.conclusion?String(e.conclusion).toUpperCase():null}))}async function bf(t,e){let n=await gf(t,e);return n===null?{...bi([]),unavailable:!0}:bi(yf(n))}function Ko(t,e=bi(t.statusCheckRollup)){return{number:t.number,title:t.title,url:t.url,author:t.author?.login??null,headRefName:t.headRefName,headSha:t.headRefOid??null,baseRefName:t.baseRefName,isDraft:!!t.isDraft,reviewDecision:t.reviewDecision||null,mergeable:t.mergeable||"UNKNOWN",mergeStateStatus:t.mergeStateStatus||"UNKNOWN",additions:t.additions??0,deletions:t.deletions??0,createdAt:t.createdAt,updatedAt:t.updatedAt,checks:e,green:e.failed===0&&e.pending===0&&t.mergeStateStatus==="CLEAN",autoMergeArmed:!!t.autoMergeRequest,mergeabilityPending:!1}}var zk=9e4,Bn=new Map;function Xk(t,e,n){let r=new Set;for(let o of e){let s=`${t}#${o.number}`;if(o.mergeable!=="UNKNOWN"&&o.mergeStateStatus!=="UNKNOWN"){Bn.delete(s);continue}r.add(s),Bn.has(s)||Bn.set(s,n),o.mergeabilityPending=n-Bn.get(s)<zk}for(let o of Bn.keys())o.startsWith(`${t}#`)&&!r.has(o)&&Bn.delete(o);return e}async function Er(t,e){let n=await Te(t),r=Number(e);if(!Number.isInteger(r)||r<=0)throw new Error("invalid PR number");try{let o=await Ut(n.path,["pr","view",String(r),"--json",Si]);return Ko(JSON.parse(o))}catch(o){if(!vi(o))throw o;let s=await Ut(n.path,["pr","view",String(r),"--json",wf]),a=JSON.parse(s);return Ko(a,await bf(n.path,a.headRefOid))}}var mf=(t,e)=>e.number-t.number;async function _r(t){try{let e=await Ut(t,["pr","list","--json",Si,"--limit","50"]);return JSON.parse(e||"[]").map(n=>Ko(n)).sort(mf)}catch(e){if(!vi(e))throw e;let n=await Ut(t,["pr","list","--json",wf,"--limit","50"]);return(await Promise.all(JSON.parse(n||"[]").map(async r=>Ko(r,await bf(t,r.headRefOid))))).sort(mf)}}var Yk=2e4,Zk=1500,Dn=null;function en(){Dn=null}async function Qk(t,e){if(!t)return e;for(let n of e){let r=await Tt(t,n.number).catch(()=>null);if(!r)continue;r.autoMerge&&(n.autoMergeArmed=!0);let o=s=>n.headSha&&s.headSha&&n.headSha!==s.headSha;r.ciFix&&(o(r.ciFix)?await Io(t,n.number,{headSha:n.headSha}).catch(()=>{}):n.ciFix={delegatedAt:r.ciFix.delegatedAt,trackId:r.ciFix.trackId??null}),r.reconcile&&(o(r.reconcile)?await $o(t,n.number,{headSha:n.headSha}).catch(()=>{}):n.reconcile={delegatedAt:r.reconcile.delegatedAt,trackId:r.reconcile.trackId??null})}return e}async function kf(){let t=await xi();if(!t.authenticated)return{auth:t,repos:[]};let e=Dn?.pending?Zk:Yk;if(Dn&&Date.now()-Dn.at<e)return{auth:t,repos:Dn.data};let n=[];for(let a of await re())await Jk(a.path)&&n.push(a);let r=Date.now(),o=await Promise.all(n.map(async a=>{try{let i=Xk(a.path,await _r(a.path),r);return await Qk(await Y(a.path).catch(()=>null),i),{name:a.name,path:a.path,prs:i}}catch(i){return{name:a.name,path:a.path,prs:[],error:ie(i)}}})),s=o.some(a=>a.prs.some(i=>i.mergeabilityPending||i.checks.pending>0||i.autoMergeArmed));return Dn={at:Date.now(),data:o,pending:s},{auth:t,repos:o}}async function Te(t){let e=(await re()).find(n=>n.path===t);if(!e)throw new Error(`unknown repo: ${t}`);return e}async function Ti(t){if(!t)return null;for(let e of await re())if(await Y(e.path).catch(()=>null)===t)return e.path;return null}var Jo=new Set(["squash","merge","rebase"]);async function Ir(t,e,{method:n="squash"}={}){let r=await Te(t);if(!Jo.has(n))throw new Error(`invalid merge method: ${n}`);let o=Number(e);if(!Number.isInteger(o)||o<=0)throw new Error("invalid PR number");let s=["pr","merge",String(o),`--${n}`];if(n!=="rebase"&&(await Gt({cwd:r.path})).GH_TOKEN){let a=await ua().catch(()=>null);a&&s.push("--body",`Merged by bostrat on behalf of @${a}`)}try{await Ut(r.path,s)}catch(a){throw new Error(ie(a))}finally{en()}return{ok:!0,number:o}}async function xf(t,e){let n=await Te(t),r=Number(e);if(!Number.isInteger(r)||r<=0)throw new Error("invalid PR number");try{await Ut(n.path,["pr","merge",String(r),"--disable-auto"])}catch(o){throw new Error(ie(o))}finally{en()}return{ok:!0,number:r}}var tx=2500,ex=9e4,nx=t=>new Promise(e=>setTimeout(e,t));function Sf(t){return t.green&&!t.isDraft}function rx(t){return t.isDraft?"draft":t.mergeable==="CONFLICTING"?`conflicts with ${t.baseRefName}`:t.checks.failed>0?"checks failing":t.checks.pending>0?"checks running":t.mergeable==="UNKNOWN"||t.mergeStateStatus==="UNKNOWN"?"mergeability unresolved":t.mergeStateStatus==="BEHIND"?`behind ${t.baseRefName}`:t.mergeStateStatus==="BLOCKED"?"blocked by branch protection":`not clean (${t.mergeStateStatus})`}var ki=new Map;function vf(){let t={};for(let[e,n]of ki)t[e]={...n};return t}async function Tf(t,{method:e="merge",onMerged:n=null}={}){let r=await Te(t);if(!Jo.has(e))throw new Error(`invalid merge method: ${e}`);if(ki.get(r.path)?.running)throw new Error("a merge-all is already running for this repo");let o=(await _r(r.path)).filter(Sf).sort((a,i)=>Date.parse(a.createdAt)-Date.parse(i.createdAt));if(!o.length)throw new Error("no PRs ready to merge");let s={running:!0,startedAt:Date.now(),finishedAt:null,method:e,total:o.length,current:o[0].number,queue:o.map(a=>({number:a.number,title:a.title})),merged:[],skipped:[],failed:[]};return ki.set(r.path,s),sx(s,o,{listPrs:()=>_r(r.path),merge:a=>Ir(r.path,a,{method:e}),sleep:nx,onMerged:n}).catch(()=>{}),{ok:!0,repo:r.path,total:s.total,queue:o.map(a=>a.number)}}async function ox(t,e){let n=Date.now()+(e.settleMaxMs??ex),r=null;for(;;){let o=null;try{if(o=await e.listPrs(),r=null,!o.some(a=>t.includes(a.number)&&(a.mergeable==="UNKNOWN"||a.mergeStateStatus==="UNKNOWN")))return o}catch(s){r=s}if(Date.now()>=n){if(o)return o;if(r)throw r;return[]}await e.sleep(tx)}}async function sx(t,e,n){try{let r=e;for(let o=0;o<e.length;o++){let s=e[o];if(t.current=s.number,o>0)try{r=await ox(e.slice(o).map(i=>i.number),n)}catch(i){for(let c of e.slice(o))t.failed.push({number:c.number,title:c.title,reason:`couldn't re-check PR state: ${ie(i)}`});break}let a=r.find(i=>i.number===s.number);if(!a){t.skipped.push({number:s.number,title:s.title,reason:"no longer open"});continue}if(!Sf(a)){t.skipped.push({number:a.number,title:a.title,reason:rx(a)});continue}try{await n.merge(a.number),t.merged.push({number:a.number,title:a.title});try{await n.onMerged?.(a)}catch{}}catch(i){t.failed.push({number:a.number,title:a.title,reason:ie(i)})}}}finally{t.current=null,t.running=!1,t.finishedAt=Date.now(),en()}}async function Af(t,{title:e,body:n="",base:r=null}={}){if(typeof e!="string"||!e.trim())throw new Error("title required");try{await Vo("git",["-C",t,"push","-u","origin","HEAD"],{timeout:12e4,maxBuffer:4*1024*1024})}catch(l){throw new Error(`push failed: ${ie(l)}`)}let o=n||"";if((await Gt({cwd:t})).GH_TOKEN){let l=await ua().catch(()=>null);l&&(o=`${o?`${o}
|
|
63
65
|
|
|
64
|
-
`:""}Opened by bostrat on behalf of @${l}`)}let s=["pr","create","--title",e.trim(),"--body",o];r&&s.push("--base",r);let a;try{a=await
|
|
66
|
+
`:""}Opened by bostrat on behalf of @${l}`)}let s=["pr","create","--title",e.trim(),"--body",o];r&&s.push("--base",r);let a;try{a=await Ut(t,s)}catch(l){throw new Error(ie(l))}finally{en()}let i=a.match(/https:\/\/\S+\/pull\/\d+/)?.[0]??null,c=i?Number(i.match(/\/pull\/(\d+)/)?.[1]):null;return{ok:!0,url:i,number:c}}async function _f(t,e){let n=await Te(t),r=Number(e);if(!Number.isInteger(r)||r<=0)throw new Error("invalid PR number");let o;try{let i;try{i=await Ut(n.path,["pr","checks",String(r),"--json","name,state,link,bucket"])}catch(c){if(i=String(c?.stdout||""),!i.trim())throw new Error(ie(c))}o=JSON.parse(i||"[]").filter(c=>c.bucket==="fail")}catch(i){if(!vi(i))throw i;let c=await Ut(n.path,["pr","view",String(r),"--json","headRefOid"]),l=await gf(n.path,JSON.parse(c).headRefOid);if(l===null)throw new Error(ie(i));o=yf(l).map(hf).filter(u=>u.state==="fail").map(u=>({name:u.name,link:u.url}))}let s=new Set,a=[];for(let i of o){let c=ax(i.link),l=null;c&&!s.has(c)&&(s.add(c),l=await Ut(n.path,["run","view",c,"--log-failed"]).then(u=>ix(u)).catch(()=>null)),a.push({name:i.name,link:i.link||null,log:l})}return a}function ax(t){return String(t||"").match(/\/actions\/runs\/(\d+)/)?.[1]??null}function ix(t,{maxLines:e=400,maxBytes:n=64*1024}={}){let r=String(t||""),o=r.split(`
|
|
65
67
|
`);return o.length>e&&(r=`\u2026 (${o.length-e} earlier lines truncated)
|
|
66
68
|
`+o.slice(-e).join(`
|
|
67
69
|
`)),Buffer.byteLength(r)>n&&(r=`\u2026 (truncated)
|
|
68
|
-
`+r.slice(-n)),r}function
|
|
69
|
-
`).find(n=>n.trim())||"gh failed"}var
|
|
70
|
+
`+r.slice(-n)),r}function ie(t){return String(t?.stderr||t?.message||t||"").trim().split(`
|
|
71
|
+
`).find(n=>n.trim())||"gh failed"}var cx=6e3,zo=new Map;function Ai(t){let e=null;for(let n of t||[]){let r=n.payload?.ts??n.ts;n.type==="pr.offer.answered"?e=n.payload?.action==="pr"?{ts:r,offerId:n.payload?.offerId??null,box:n.boxId??null}:null:n.type==="pr.state.changed"&&n.payload?.change==="opened"&&e!=null&&r>=e.ts&&(e=null)}return e}async function Ef(t){let e=t?.workspace?.branch;if(!e)return;let n;try{n=await B(t)}catch{return}if(Ai(n)==null)return;let r=(t.boxes||[]).find(c=>c.name===t.leadBox);if(await Po(t.leadBox,r?.agent).catch(()=>!1))return;let o=Date.now();if(o-(zo.get(t.id)||0)<cx)return;zo.set(t.id,o);let s;try{s=await _r(t.cwd)}catch{return}let a=(s||[]).find(c=>c.headRefName===e);if(!a)return;let i=await Y(t.cwd).catch(()=>null);if(i){let{link:c,created:l}=await Bt({repo:i,number:a.number,trackId:t.id,url:a.url,headRef:e,title:a.title,source:"reconciled"});if(!l&&!(c?.trackId===t.id&&await xr(i,a.number))){zo.delete(t.id);return}}await R(t,{type:"pr.state.changed",actor:"agent",source:"orchestrator",boxId:t.leadBox,payload:{ts:Date.now(),change:"opened",number:a.number,url:a.url,title:a.title,branch:e}}).catch(()=>{}),zo.delete(t.id)}import ht from"node:fs/promises";import _t from"node:path";import Rr from"node:os";import{randomUUID as lx}from"node:crypto";import{execFile as ux}from"node:child_process";import{promisify as dx}from"node:util";import{chromium as fx}from"playwright-core";var Rf=dx(ux),$f=process.env.BOSTRAT_STATE_DIR||_t.join(process.env.HOME||Rr.tmpdir(),".bostrat"),Of=_t.join($f,"mocks.json"),px=process.env.BOSTRAT_MOCK_ENGINE||"chromium",mx=process.env.BOSTRAT_CHROMIUM_BIN||"/usr/bin/chromium",hx=process.env.BOSTRAT_FIREFOX_BIN||"firefox",wx=1300,jn=2,gx=32,Cf=1500,Ln=Number(process.env.BOSTRAT_MOCK_TIMEOUT_MS)||75e3,yx=700*1024*1024;async function bx(){if(process.platform!=="darwin")return Rr.freemem();try{let{stdout:t}=await Rf("vm_stat",[],{timeout:3e3}),e=Number(/page size of (\d+) bytes/.exec(t)?.[1])||4096,n=o=>Number(new RegExp(`Pages ${o}:\\s+(\\d+)\\.`).exec(t)?.[1])||0,r=(n("free")+n("inactive")+n("speculative"))*e;return r>0?r:Rr.freemem()}catch{return Rr.freemem()}}var Ft=class extends Error{constructor(e,n=""){super(n?`${e}
|
|
70
72
|
${n.trim().split(`
|
|
71
73
|
`).slice(-6).join(`
|
|
72
|
-
`)}`:e),this.name="MockRenderError"}},Ye=null,bi=null;async function le(){return Ye||(bi||=(async()=>{try{Ye=JSON.parse(await ht.readFile(vf,"utf8"))}catch{Ye={mocks:[]}}finally{bi=null}return Ye.mocks||=[],Ye})(),bi)}async function Xt(){await qt(vf,Ye)}function Ft(t,e,n={}){return R({id:t.trackId,cwd:t.cwd},{type:"mock.state.changed",actor:"orchestrator",source:"orchestrator",boxId:t.box||null,provider:t.agent||null,payload:{ts:Date.now(),change:e,mockId:t.id,name:t.name,...t.group?{group:t.group}:{},...n}}).catch(()=>{})}var Ko=t=>At.join(t,".bostrat","mocks");function Bt(t,e){let n=Ko(t.cwd);return{html:At.join(n,`${t.id}.v${e}.html`),png:At.join(n,`${t.id}.v${e}.png`)}}function Ze(t,e=null){let n=t.versions||[];return e!=null?n.find(r=>r.v===Number(e))||null:n.length?n[n.length-1]:null}function xi(t){return t.length<24||t.readUInt32BE(12)!==1229472850?null:{width:t.readUInt32BE(16),height:t.readUInt32BE(20)}}var lx=Buffer.from([137,80,78,71,13,10,26,10]);function Af(t){return Buffer.isBuffer(t)&&t.length>24&&t.subarray(0,8).equals(lx)}async function ux(t){let e=Buffer.alloc(24),n=await ht.open(t,"r");try{await n.read(e,0,24,0)}finally{await n.close()}return e}function dx({imgW:t,imgH:e,bbox:n,pad:r,align:o=1}){if(!n||!(t>0)||!(e>0))return null;let s=n.right-n.left+1,a=n.bottom-n.top+1;if(s<=0||a<=0||s>=t*.9&&a>=e*.9)return null;let i=s+2*r,c=a+2*r;return i+=(o-i%o)%o,c+=(o-c%o)%o,{sx:n.left,sy:n.top,sw:s,sh:a,outW:i,outH:c,dx:Math.floor((i-s)/2),dy:Math.floor((c-a)/2)}}async function fx(t,e){return t.evaluate(async n=>{let r=new Image;await new Promise((h,g)=>{r.onload=h,r.onerror=()=>g(new Error("decode failed")),r.src=`data:image/png;base64,${n}`});let o=r.naturalWidth,s=r.naturalHeight;if(!o||!s||o*s>24e6)return null;let a=document.createElement("canvas");a.width=o,a.height=s;let i=a.getContext("2d",{willReadFrequently:!0});i.drawImage(r,0,0);let c=i.getImageData(0,0,o,s).data,l=[c[0],c[1],c[2]],u=o,d=s,f=-1,w=-1;for(let h=0;h<s;h++)for(let g=h*o*4,x=0;x<o;x++,g+=4)Math.abs(c[g]-l[0])+Math.abs(c[g+1]-l[1])+Math.abs(c[g+2]-l[2])>24&&(x<u&&(u=x),x>f&&(f=x),h<d&&(d=h),w=h);return f<0?null:{imgW:o,imgH:s,bg:l,bbox:{left:u,top:d,right:f,bottom:w}}},e.toString("base64"))}async function px(t,e,{bg:n,crop:r}){let o=await t.evaluate(async({b64:s,bg:a,crop:i})=>{let c=new Image;await new Promise((d,f)=>{c.onload=d,c.onerror=()=>f(new Error("decode failed")),c.src=`data:image/png;base64,${s}`});let l=document.createElement("canvas");l.width=i.outW,l.height=i.outH;let u=l.getContext("2d");return u.fillStyle=`rgb(${a[0]},${a[1]},${a[2]})`,u.fillRect(0,0,i.outW,i.outH),u.drawImage(c,i.sx,i.sy,i.sw,i.sh,i.dx,i.dy,i.sw,i.sh),l.toDataURL("image/png").split(",")[1]},{b64:e.toString("base64"),bg:n,crop:r});return Buffer.from(o,"base64")}async function mx({htmlPath:t,outPath:e,width:n}){let r=Date.now(),o;try{o=await ex.launch({executablePath:rx,args:["--disable-gpu"],timeout:3e4})}catch(s){throw new Ut("chromium failed to launch",s?.message||"")}try{let a=await(await o.newContext({viewport:{width:n,height:800},deviceScaleFactor:Bn})).newPage();a.setDefaultTimeout(Dn),await a.goto(`file://${t}`,{waitUntil:"load",timeout:Dn}),await a.waitForTimeout(250);let i;for(let l=1;;l++)try{i=await a.screenshot({fullPage:!0,timeout:Dn});break}catch(u){if(l>=3||!/captureScreenshot|Unable to capture/i.test(u?.message||""))throw u;await a.waitForTimeout(500)}try{let l=await fx(a,i),u=l&&dx({...l,pad:ax*Bn,align:Bn});u&&(i=await px(a,i,{bg:l.bg,crop:u}))}catch{}if(i.length<Tf)throw new Ut(`capture produced a ${i.length}-byte image`);let c=xi(i);if(!c)throw new Ut("output is not a PNG");return await ht.writeFile(e,i),{width:Math.round(c.width/Bn),height:Math.round(c.height/Bn),dpr:Bn,renderMs:Date.now()-r}}catch(s){throw s instanceof Ut?s:new Ut(/timeout/i.test(s?.message||"")?`render timed out after ${Dn}ms`:"chromium render failed",s?.message||"")}finally{await o.close().catch(()=>{})}}async function kf({htmlPath:t,outPath:e,width:n}){let r=await ht.mkdtemp(At.join(_r.tmpdir(),"bostrat-mock-profile-")),o=Date.now(),s="";try{try{s=(await xf(ox,["--headless","--no-remote","--profile",r,`--window-size=${n}`,"--screenshot",e,`file://${t}`],{env:{...process.env,MOZ_HEADLESS:"1"},timeout:Dn,killSignal:"SIGKILL",maxBuffer:4194304})).stderr||""}catch(c){throw s=c?.stderr||"",new Ut(c?.killed?`render timed out after ${Dn}ms`:"firefox exited non-zero",s)}let a=await ht.stat(e).catch(()=>null);if(!a||a.size<Tf)throw new Ut(`render produced ${a?`a ${a.size}-byte file`:"no file"} (silent no-op \u2014 another firefox holding the profile?)`,s);let i=xi(await ux(e));if(!i)throw new Ut("output is not a PNG",s);return{width:i.width,height:i.height,dpr:1,renderMs:Date.now()-o}}finally{await ht.rm(r,{recursive:!0,force:!0}).catch(()=>{})}}async function hx(t){if(nx==="firefox")return kf(t);try{return await mx(t)}catch(e){if(/failed to launch/i.test(e?.message||""))return kf(t);throw e}}var wx=hx;var ki=Promise.resolve();function _f(t){return ki=ki.then(t,t),ki}async function Ef(t,e){let n=await le(),r=n.mocks.find(o=>o.id===t);if(r){r.state="rendering",await Xt(),await Ft(r,"rendering",{version:e});try{let o=await cx();if(o<ix)throw new Ut(`box is low on memory (${Math.round(o/1e6)} MB available) \u2014 not spawning a browser`);let s=Bt(r,e),a=await wx({htmlPath:s.html,outPath:s.png,width:r.width});if(!n.mocks.some(i=>i.id===t)){await ht.rm(s.png,{force:!0}).catch(()=>{});return}If(r,{v:e,source:"html",htmlRel:At.relative(r.cwd,s.html),pngRel:At.relative(r.cwd,s.png),width:a.width,height:a.height,dpr:a.dpr,renderMs:a.renderMs,createdAt:Date.now()}),await Xt(),await Ft(r,"ready",{version:e,width:a.width,height:a.height,dpr:a.dpr})}catch(o){if(!n.mocks.some(s=>s.id===t))return;r.state="error",r.error=o?.message||String(o),await Xt(),await Ft(r,"error",{version:e,error:r.error})}}}function If(t,e){t.versions=(t.versions||[]).filter(r=>r.v!==e.v).concat(e),t.versions.sort((r,o)=>r.v-o.v);let n=t.versions[t.versions.length-1];t.readyVersion=n.v,t.height=n.height,t.dpr=n.dpr,t.renderMs=n.renderMs??null,n.v===t.version&&(t.state="ready",t.error=null)}function gx({trackId:t,name:e,cwd:n,width:r}){if(!t)throw new Error("trackId required");if(typeof e!="string"||!e.trim())throw new Error("name (string) required");if(!n)throw new Error("cwd required");let o=r==null?sx:Number(r);if(!Number.isInteger(o)||o<320||o>4e3)throw new Error("invalid width (320-4000)");return o}async function Rf({trackId:t,group:e}={}){let n=await le(),r=t?n.mocks.filter(o=>o.trackId===t):n.mocks;return e&&(r=r.filter(o=>o.group===e)),r.map(o=>({...o}))}async function Ae(t){let n=(await le()).mocks.find(r=>r.id===t);return n?{...n}:null}async function $f({trackId:t,name:e,html:n,image:r,cwd:o,width:s,group:a,box:i,agent:c,prompt:l}){let u=gx({trackId:t,name:e,cwd:o,width:s}),d=typeof n=="string"&&n.trim();if(!d&&!r)throw new Error("html (string) or image (png) required");if(r&&!Af(r))throw new Error("image must be a PNG");let f=await le(),w={id:Zk().slice(0,8),trackId:t,box:i||null,agent:c||null,name:e.trim(),prompt:l?String(l).slice(0,500):null,group:a?String(a).trim():null,cwd:o,width:u,height:null,dpr:null,state:"queued",source:d?"html":"image",version:1,readyVersion:null,versions:[],error:null,renderMs:null,createdAt:Date.now(),updatedAt:Date.now()};return await ht.mkdir(Ko(o),{recursive:!0}),f.mocks.push(w),d?(await ht.writeFile(Bt(w,1).html,n),await Xt(),await Ft(w,"created"),_f(()=>Ef(w.id,1))):(await Cf(w,1,r),await Xt(),await Ft(w,"created"),await Ft(w,"ready",{version:1,width:w.width,height:w.height,dpr:w.dpr})),{...w}}async function Of(t,{html:e,image:n,width:r}={}){let s=(await le()).mocks.find(c=>c.id===t);if(!s)throw new Error("mock not found");let a=typeof e=="string"&&e.trim();if(!a&&!n)throw new Error("html (string) or image (png) required");if(n&&!Af(n))throw new Error("image must be a PNG");if(r!=null){let c=Number(r);if(!Number.isInteger(c)||c<320||c>4e3)throw new Error("invalid width (320-4000)");s.width=c}let i=s.version+1;return s.version=i,s.updatedAt=Date.now(),s.source=a?"html":"image",await ht.mkdir(Ko(s.cwd),{recursive:!0}),a?(s.state="queued",await ht.writeFile(Bt(s,i).html,e),await Xt(),await Ft(s,"updated",{version:i}),_f(()=>Ef(s.id,i))):(await Cf(s,i,n),await Xt(),await Ft(s,"updated",{version:i}),await Ft(s,"ready",{version:i,width:s.width,height:s.height,dpr:s.dpr})),{...s}}async function Cf(t,e,n){let r=xi(n);if(!r)throw new Error("image must be a PNG");let o=Bt(t,e);await ht.writeFile(o.png,n),If(t,{v:e,source:"image",htmlRel:null,pngRel:At.relative(t.cwd,o.png),width:r.width,height:r.height,dpr:1,renderMs:null,createdAt:Date.now()}),t.width=r.width}async function Mf(t){let e=await le(),n=e.mocks.find(o=>o.id===t);if(!n)throw new Error("mock not found");if(n.readyVersion==null)throw new Error(`mock is ${n.state} \u2014 nothing to pick yet`);let r=n.group?e.mocks.filter(o=>o.trackId===n.trackId&&o.group===n.group&&o.id!==t&&o.readyVersion!=null).map(o=>({mockId:o.id,name:o.name})):[];return await Ft(n,"picked",{version:n.readyVersion,siblings:r}),{mock:{...n},siblings:r}}async function Nf(t){let e=await le(),n=e.mocks.findIndex(o=>o.id===t);if(n===-1)throw new Error("mock not found");let r=e.mocks[n];e.mocks.splice(n,1);for(let o=1;o<=r.version;o++){let s=Bt(r,o);await ht.rm(s.html,{force:!0}).catch(()=>{}),await ht.rm(s.png,{force:!0}).catch(()=>{})}return await Xt(),await Ft(r,"deleted"),{ok:!0,id:t,name:r.name}}async function Pf(t){let n=(await le()).mocks.filter(i=>i.trackId===t.id);if(!n.length)return 0;let r=At.join(Sf,"mock-archive",t.id),o=Ko(t.cwd),s=At.join(r,".bostrat","mocks");await ht.mkdir(s,{recursive:!0});let a=await ht.readdir(o).catch(()=>[]);for(let i of a)await ht.copyFile(At.join(o,i),At.join(s,i));for(let i of n){for(let c of i.versions||[])for(let l of[c.htmlRel,c.pngRel]){if(!l)continue;if(!(await ht.stat(At.join(r,l)).catch(()=>null))?.isFile())throw new Error(`mock evacuation incomplete: ${l} missing from the archive`)}i.cwd=r,i.trackDeletedAt=Date.now(),i.trackName=t.name||t.id}return await Xt(),n.length}async function Bf(t){let e=await le(),n=e.mocks.filter(r=>r.trackId===t);return n.length&&(e.mocks=e.mocks.filter(r=>r.trackId!==t),await Xt()),n.length}import tn from"node:fs/promises";import _e from"node:path";import yx from"node:os";import{randomUUID as bx}from"node:crypto";var Ln=process.env.BOSTRAT_STATE_DIR||_e.join(process.env.HOME||yx.tmpdir(),".bostrat"),jf=_e.join(Ln,"runbooks.json"),vi=_e.join(Ln,"runbooks"),Df=256*1024,kx=[{label:"Anthropic API key",re:/\bsk-ant-[A-Za-z0-9_-]{20,}/},{label:"OpenAI-style API key",re:/\bsk-(?:proj-|svcacct-)?[A-Za-z0-9_-]{32,}\b/},{label:"Stripe secret key",re:/\bsk_(?:live|test)_[A-Za-z0-9]{16,}/},{label:"GitHub token",re:/\b(?:ghp|gho|ghu|ghs|ghr)_[A-Za-z0-9]{36,}|\bgithub_pat_[A-Za-z0-9_]{22,}/},{label:"AWS access key id",re:/\bAKIA[0-9A-Z]{16}\b/},{label:"Slack token",re:/\bxox[baprs]-[A-Za-z0-9-]{10,}/},{label:"private key block",re:/-----BEGIN [A-Z ]*PRIVATE KEY-----/},{label:"JWT",re:/\beyJ[A-Za-z0-9_-]{10,}\.eyJ[A-Za-z0-9_-]{10,}\.[A-Za-z0-9_-]{10,}\b/}];function xx(t){return kx.filter(({re:e})=>e.test(t)).map(({label:e})=>e)}var Qe=null,Si=null;async function Ee(){return Qe||(Si||=(async()=>{try{Qe=JSON.parse(await tn.readFile(jf,"utf8"))}catch{Qe={runbooks:[]}}finally{Si=null}return Qe.runbooks||=[],Qe})(),Si)}async function Un(){await qt(jf,Qe)}function Fn(t,e,n={}){return R({id:t.trackId,cwd:t.cwd},{type:"runbook.state.changed",actor:"orchestrator",source:"orchestrator",boxId:t.box||null,provider:t.agent||null,payload:{ts:Date.now(),change:e,runbookId:t.id,name:t.name,status:t.status,...n}}).catch(()=>{})}function jn(t,e){return _e.join(vi,`${t.id}.v${e}.md`)}function Lf(t,e=null){let n=t.versions||[];return e!=null?n.find(r=>r.v===Number(e))||null:n.length?n[n.length-1]:null}function Uf(t){if(typeof t!="string"||!t.trim())throw new Error("md (markdown string) required");let e=Buffer.byteLength(t,"utf8");if(e>Df)throw new Error(`runbook is ${Math.round(e/1024)} KB \u2014 over the ${Df/1024} KB cap; a doc that size belongs in the repo, not a runbook`);let n=xx(t);if(n.length)throw new Error(`runbook appears to contain a secret VALUE (${n.join(", ")}) \u2014 runbooks ship off-box; name the secret and where it goes, never its value. Replace it with a placeholder like <your key> and retry.`);return e}function Ff(t,e){if(t.trackDeletedAt)throw new Error(`runbook is orphaned (its Track was deleted) \u2014 cannot ${e} it`)}async function Hf({trackId:t,status:e}={}){let n=await Ee(),r=t?n.runbooks.filter(o=>o.trackId===t):n.runbooks;return e&&(r=r.filter(o=>o.status===e)),r.map(o=>({...o}))}async function Ti(t){let n=(await Ee()).runbooks.find(r=>r.id===t);return n?{...n}:null}async function Wf({trackId:t,trackName:e,name:n,md:r,cwd:o,box:s,agent:a}){if(!t)throw new Error("trackId required");if(typeof n!="string"||!n.trim())throw new Error("name (string) required");let i=Uf(r),c=await Ee(),l={id:bx().slice(0,8),trackId:t,trackName:e||t,box:s||null,agent:a||null,name:n.trim(),status:"open",version:1,versions:[],trackDeletedAt:null,cwd:o||null,createdAt:Date.now(),updatedAt:Date.now()};return await tn.mkdir(vi,{recursive:!0}),await tn.writeFile(jn(l,1),r),l.versions.push({v:1,mdRel:_e.relative(Ln,jn(l,1)),bytes:i,note:null,createdAt:Date.now()}),c.runbooks.push(l),await Un(),await Fn(l,"created",{version:1}),{...l}}async function qf(t,{md:e,note:n}={}){let o=(await Ee()).runbooks.find(i=>i.id===t);if(!o)throw new Error("runbook not found");Ff(o,"update");let s=Uf(e),a=o.version+1;return o.version=a,o.updatedAt=Date.now(),await tn.mkdir(vi,{recursive:!0}),await tn.writeFile(jn(o,a),e),o.versions.push({v:a,mdRel:_e.relative(Ln,jn(o,a)),bytes:s,note:n?String(n).slice(0,200):null,createdAt:Date.now()}),await Un(),await Fn(o,"updated",{version:a,...n?{note:String(n).slice(0,200)}:{}}),{...o}}async function Gf(t,e){if(e!=="open"&&e!=="done")throw new Error('status must be "open" or "done"');let r=(await Ee()).runbooks.find(o=>o.id===t);if(!r)throw new Error("runbook not found");return Ff(r,e==="done"?"complete":"reopen"),r.status===e?{...r}:(r.status=e,r.updatedAt=Date.now(),await Un(),await Fn(r,e==="done"?"done":"reopened"),{...r})}async function Kf(t){let e=await Ee(),n=e.runbooks.findIndex(o=>o.id===t);if(n===-1)throw new Error("runbook not found");let r=e.runbooks[n];e.runbooks.splice(n,1);for(let o of r.versions||[])await tn.rm(_e.join(Ln,o.mdRel),{force:!0}).catch(()=>{});return await Un(),r.trackDeletedAt||await Fn(r,"deleted"),{ok:!0,id:t,name:r.name}}async function Vf(t){let n=(await Ee()).runbooks.filter(r=>r.trackId===t&&!r.trackDeletedAt);for(let r of n)r.trackDeletedAt=Date.now(),await Fn(r,"orphaned"),r.cwd=null;return n.length&&await Un(),n.length}async function Jf(t){let e=await Ee(),n=e.runbooks.filter(r=>r.trackId===t);for(let r of n){await Fn(r,"deleted");for(let o of r.versions||[])await tn.rm(_e.join(Ln,o.mdRel),{force:!0}).catch(()=>{})}return n.length&&(e.runbooks=e.runbooks.filter(r=>r.trackId!==t),await Un()),n.length}import zf from"node:fs/promises";import Vo from"node:path";import{randomUUID as Sx}from"node:crypto";var Yf=".bostrat/uploads",Ai=t=>Vo.join(t,Yf),Xf=Number(process.env.BOSTRAT_MAX_UPLOAD_BYTES)||24*1024*1024,vx=new Set(["png","jpg","jpeg","gif","webp","svg","bmp","tif","tiff","heic","heif","avif","ico"]),Tx=new Set(["pdf","doc","docx","xls","xlsx","ppt","pptx","odt","ods","odp","rtf","txt","md","markdown","rst","csv","tsv","json","jsonl","yaml","yml","xml","html","htm","log","ini","toml","conf","env","js","jsx","ts","tsx","py","rb","go","rs","java","kt","c","h","cpp","cc","cs","php","swift","sh","bash","zsh","sql","css","scss"]);function _i(t){let e=String(t||"").toLowerCase().replace(/^\./,"");return vx.has(e)?"image":Tx.has(e)?"doc":null}function Ax(t){let r=Vo.basename(String(t||"")).replace(/[\x00-\x1f]/g,"").replace(/[^A-Za-z0-9._-]+/g,"_").replace(/^_+|_+$/g,"").slice(-80)||"file";return r.startsWith(".")?`f${r}`:r}async function Zf(t,{filename:e,buffer:n}){if(!t)throw new Error("cwd required");if(!Buffer.isBuffer(n)||n.length===0)throw new Error("empty upload");if(n.length>Xf)throw new Error(`file too large \u2014 max ${Math.floor(Xf/(1024*1024))} MB`);let r=Ax(e),o=Vo.extname(r).slice(1),s=_i(o);if(!s)throw new Error(`unsupported file type "${o||"unknown"}" \u2014 attach an image or a document`);let a=Sx().slice(0,8),i=Ai(t);await zf.mkdir(i,{recursive:!0});let c=`${a}-${r}`;return await zf.writeFile(Vo.join(i,c),n),{id:a,name:r,rel:`${Yf}/${c}`,kind:s,size:n.length}}function Qf(t){let e=t.map(r=>`\`${r}\``).join(", ");return`[Attached \u2014 please read ${t.length===1?"this file":`these ${t.length} files`}: ${e}]`}var _x=/\[Attached — please read (?:this file|these \d+ files): [^\]]*\]$/;function Ei(t){return t.role==="user"&&_x.test((t.text||"").trim())}function Hn(t,e,n=Date.now()){return e?t.idleSince==null?(t.idleSince=n,!1):n-t.idleSince>=6500:(t.idleSince=null,!1)}var Rx=Ix(Ex),Ie=process.env.BOSTRAT_STATE_DIR||G.join(process.env.HOME||"/tmp",".bostrat"),ap=G.join(Ie,"tracks.json"),$x=G.join(Ie,"events.jsonl"),Ni=G.join(Ie,"workspaces"),Ox=process.env.BOSTRAT_DEFAULT_REPO||null,Yo={claude:{envVar:"BOSTRAT_AGENT_CMD",base:"claude",skipFlag:"--dangerously-skip-permissions"},cursor:{envVar:"BOSTRAT_CURSOR_CMD",base:"cursor-agent",skipFlag:"--force"},codex:{envVar:"BOSTRAT_CODEX_CMD",base:"codex",skipFlag:"--dangerously-bypass-approvals-and-sandbox"}},Bi=Object.keys(Yo);function Cx(t,{skipPermissions:e=!0}={}){let n=Yo[t];if(!n)throw new Error(`unknown agent "${t}"`);let r=process.env[n.envVar];return r||(e&&n.skipFlag?`${n.base} ${n.skipFlag}`:n.base)}function ip(t,e,{skipPermissions:n=!0}={}){let r=Yo[t];if(!r)throw new Error(`unknown agent "${t}"`);let o=process.env[r.envVar];if(o)return o;let s=n&&r.skipFlag?` ${r.skipFlag}`:"";switch(t){case"claude":return`${r.base} --resume ${e}${s}`;case"cursor":return`${r.base} --continue${s}`;case"codex":return`${r.base} resume --last${s}`;default:return`${r.base}${s}`}}function Zo(t){let e=Yo[t];if(e)return process.env[e.envVar]||e.base}async function Qo(t,e=!0){let n=Cx(t,{skipPermissions:e});if(!await Bs(n)){let r=n.split(/\s+/)[0];throw new Error(`agent "${t}" isn't available on this box (command not found: ${r}). Its CLI must be installed and on PATH before you can hand off / delegate to it.`)}return n}var ts=".bostrat/handoff/latest.md";function Mx(t,e=ts){return`${t.trim()}
|
|
74
|
+
`)}`:e),this.name="MockRenderError"}},nn=null,_i=null;async function ce(){return nn||(_i||=(async()=>{try{nn=JSON.parse(await ht.readFile(Of,"utf8"))}catch{nn={mocks:[]}}finally{_i=null}return nn.mocks||=[],nn})(),_i)}async function Xt(){await qt(Of,nn)}function Ht(t,e,n={}){return R({id:t.trackId,cwd:t.cwd},{type:"mock.state.changed",actor:"orchestrator",source:"orchestrator",boxId:t.box||null,provider:t.agent||null,payload:{ts:Date.now(),change:e,mockId:t.id,name:t.name,...t.group?{group:t.group}:{},...n}}).catch(()=>{})}var Xo=t=>_t.join(t,".bostrat","mocks");function Dt(t,e){let n=Xo(t.cwd);return{html:_t.join(n,`${t.id}.v${e}.html`),png:_t.join(n,`${t.id}.v${e}.png`)}}function rn(t,e=null){let n=t.versions||[];return e!=null?n.find(r=>r.v===Number(e))||null:n.length?n[n.length-1]:null}function Ii(t){return t.length<24||t.readUInt32BE(12)!==1229472850?null:{width:t.readUInt32BE(16),height:t.readUInt32BE(20)}}var kx=Buffer.from([137,80,78,71,13,10,26,10]);function Mf(t){return Buffer.isBuffer(t)&&t.length>24&&t.subarray(0,8).equals(kx)}async function xx(t){let e=Buffer.alloc(24),n=await ht.open(t,"r");try{await n.read(e,0,24,0)}finally{await n.close()}return e}function Sx({imgW:t,imgH:e,bbox:n,pad:r,align:o=1}){if(!n||!(t>0)||!(e>0))return null;let s=n.right-n.left+1,a=n.bottom-n.top+1;if(s<=0||a<=0||s>=t*.9&&a>=e*.9)return null;let i=s+2*r,c=a+2*r;return i+=(o-i%o)%o,c+=(o-c%o)%o,{sx:n.left,sy:n.top,sw:s,sh:a,outW:i,outH:c,dx:Math.floor((i-s)/2),dy:Math.floor((c-a)/2)}}async function vx(t,e){return t.evaluate(async n=>{let r=new Image;await new Promise((m,g)=>{r.onload=m,r.onerror=()=>g(new Error("decode failed")),r.src=`data:image/png;base64,${n}`});let o=r.naturalWidth,s=r.naturalHeight;if(!o||!s||o*s>24e6)return null;let a=document.createElement("canvas");a.width=o,a.height=s;let i=a.getContext("2d",{willReadFrequently:!0});i.drawImage(r,0,0);let c=i.getImageData(0,0,o,s).data,l=[c[0],c[1],c[2]],u=o,d=s,f=-1,w=-1;for(let m=0;m<s;m++)for(let g=m*o*4,x=0;x<o;x++,g+=4)Math.abs(c[g]-l[0])+Math.abs(c[g+1]-l[1])+Math.abs(c[g+2]-l[2])>24&&(x<u&&(u=x),x>f&&(f=x),m<d&&(d=m),w=m);return f<0?null:{imgW:o,imgH:s,bg:l,bbox:{left:u,top:d,right:f,bottom:w}}},e.toString("base64"))}async function Tx(t,e,{bg:n,crop:r}){let o=await t.evaluate(async({b64:s,bg:a,crop:i})=>{let c=new Image;await new Promise((d,f)=>{c.onload=d,c.onerror=()=>f(new Error("decode failed")),c.src=`data:image/png;base64,${s}`});let l=document.createElement("canvas");l.width=i.outW,l.height=i.outH;let u=l.getContext("2d");return u.fillStyle=`rgb(${a[0]},${a[1]},${a[2]})`,u.fillRect(0,0,i.outW,i.outH),u.drawImage(c,i.sx,i.sy,i.sw,i.sh,i.dx,i.dy,i.sw,i.sh),l.toDataURL("image/png").split(",")[1]},{b64:e.toString("base64"),bg:n,crop:r});return Buffer.from(o,"base64")}async function Ax({htmlPath:t,outPath:e,width:n}){let r=Date.now(),o;try{o=await fx.launch({executablePath:mx,args:["--disable-gpu"],timeout:3e4})}catch(s){throw new Ft("chromium failed to launch",s?.message||"")}try{let a=await(await o.newContext({viewport:{width:n,height:800},deviceScaleFactor:jn})).newPage();a.setDefaultTimeout(Ln),await a.goto(`file://${t}`,{waitUntil:"load",timeout:Ln}),await a.waitForTimeout(250);let i;for(let l=1;;l++)try{i=await a.screenshot({fullPage:!0,timeout:Ln});break}catch(u){if(l>=3||!/captureScreenshot|Unable to capture/i.test(u?.message||""))throw u;await a.waitForTimeout(500)}try{let l=await vx(a,i),u=l&&Sx({...l,pad:gx*jn,align:jn});u&&(i=await Tx(a,i,{bg:l.bg,crop:u}))}catch{}if(i.length<Cf)throw new Ft(`capture produced a ${i.length}-byte image`);let c=Ii(i);if(!c)throw new Ft("output is not a PNG");return await ht.writeFile(e,i),{width:Math.round(c.width/jn),height:Math.round(c.height/jn),dpr:jn,renderMs:Date.now()-r}}catch(s){throw s instanceof Ft?s:new Ft(/timeout/i.test(s?.message||"")?`render timed out after ${Ln}ms`:"chromium render failed",s?.message||"")}finally{await o.close().catch(()=>{})}}async function If({htmlPath:t,outPath:e,width:n}){let r=await ht.mkdtemp(_t.join(Rr.tmpdir(),"bostrat-mock-profile-")),o=Date.now(),s="";try{try{s=(await Rf(hx,["--headless","--no-remote","--profile",r,`--window-size=${n}`,"--screenshot",e,`file://${t}`],{env:{...process.env,MOZ_HEADLESS:"1"},timeout:Ln,killSignal:"SIGKILL",maxBuffer:4194304})).stderr||""}catch(c){throw s=c?.stderr||"",new Ft(c?.killed?`render timed out after ${Ln}ms`:"firefox exited non-zero",s)}let a=await ht.stat(e).catch(()=>null);if(!a||a.size<Cf)throw new Ft(`render produced ${a?`a ${a.size}-byte file`:"no file"} (silent no-op \u2014 another firefox holding the profile?)`,s);let i=Ii(await xx(e));if(!i)throw new Ft("output is not a PNG",s);return{width:i.width,height:i.height,dpr:1,renderMs:Date.now()-o}}finally{await ht.rm(r,{recursive:!0,force:!0}).catch(()=>{})}}async function _x(t){if(px==="firefox")return If(t);try{return await Ax(t)}catch(e){if(/failed to launch/i.test(e?.message||""))return If(t);throw e}}var Ex=_x;var Ei=Promise.resolve();function Pf(t){return Ei=Ei.then(t,t),Ei}async function Nf(t,e){let n=await ce(),r=n.mocks.find(o=>o.id===t);if(r){r.state="rendering",await Xt(),await Ht(r,"rendering",{version:e});try{let o=await bx();if(o<yx)throw new Ft(`box is low on memory (${Math.round(o/1e6)} MB available) \u2014 not spawning a browser`);let s=Dt(r,e),a=await Ex({htmlPath:s.html,outPath:s.png,width:r.width});if(!n.mocks.some(i=>i.id===t)){await ht.rm(s.png,{force:!0}).catch(()=>{});return}Bf(r,{v:e,source:"html",htmlRel:_t.relative(r.cwd,s.html),pngRel:_t.relative(r.cwd,s.png),width:a.width,height:a.height,dpr:a.dpr,renderMs:a.renderMs,createdAt:Date.now()}),await Xt(),await Ht(r,"ready",{version:e,width:a.width,height:a.height,dpr:a.dpr})}catch(o){if(!n.mocks.some(s=>s.id===t))return;r.state="error",r.error=o?.message||String(o),await Xt(),await Ht(r,"error",{version:e,error:r.error})}}}function Bf(t,e){t.versions=(t.versions||[]).filter(r=>r.v!==e.v).concat(e),t.versions.sort((r,o)=>r.v-o.v);let n=t.versions[t.versions.length-1];t.readyVersion=n.v,t.height=n.height,t.dpr=n.dpr,t.renderMs=n.renderMs??null,n.v===t.version&&(t.state="ready",t.error=null)}function Ix({trackId:t,name:e,cwd:n,width:r}){if(!t)throw new Error("trackId required");if(typeof e!="string"||!e.trim())throw new Error("name (string) required");if(!n)throw new Error("cwd required");let o=r==null?wx:Number(r);if(!Number.isInteger(o)||o<320||o>4e3)throw new Error("invalid width (320-4000)");return o}async function Df({trackId:t,group:e}={}){let n=await ce(),r=t?n.mocks.filter(o=>o.trackId===t):n.mocks;return e&&(r=r.filter(o=>o.group===e)),r.map(o=>({...o}))}async function Ae(t){let n=(await ce()).mocks.find(r=>r.id===t);return n?{...n}:null}async function jf({trackId:t,name:e,html:n,image:r,cwd:o,width:s,group:a,box:i,agent:c,prompt:l}){let u=Ix({trackId:t,name:e,cwd:o,width:s}),d=typeof n=="string"&&n.trim();if(!d&&!r)throw new Error("html (string) or image (png) required");if(r&&!Mf(r))throw new Error("image must be a PNG");let f=await ce(),w={id:lx().slice(0,8),trackId:t,box:i||null,agent:c||null,name:e.trim(),prompt:l?String(l).slice(0,500):null,group:a?String(a).trim():null,cwd:o,width:u,height:null,dpr:null,state:"queued",source:d?"html":"image",version:1,readyVersion:null,versions:[],error:null,renderMs:null,createdAt:Date.now(),updatedAt:Date.now()};return await ht.mkdir(Xo(o),{recursive:!0}),f.mocks.push(w),d?(await ht.writeFile(Dt(w,1).html,n),await Xt(),await Ht(w,"created"),Pf(()=>Nf(w.id,1))):(await Uf(w,1,r),await Xt(),await Ht(w,"created"),await Ht(w,"ready",{version:1,width:w.width,height:w.height,dpr:w.dpr})),{...w}}async function Lf(t,{html:e,image:n,width:r}={}){let s=(await ce()).mocks.find(c=>c.id===t);if(!s)throw new Error("mock not found");let a=typeof e=="string"&&e.trim();if(!a&&!n)throw new Error("html (string) or image (png) required");if(n&&!Mf(n))throw new Error("image must be a PNG");if(r!=null){let c=Number(r);if(!Number.isInteger(c)||c<320||c>4e3)throw new Error("invalid width (320-4000)");s.width=c}let i=s.version+1;return s.version=i,s.updatedAt=Date.now(),s.source=a?"html":"image",await ht.mkdir(Xo(s.cwd),{recursive:!0}),a?(s.state="queued",await ht.writeFile(Dt(s,i).html,e),await Xt(),await Ht(s,"updated",{version:i}),Pf(()=>Nf(s.id,i))):(await Uf(s,i,n),await Xt(),await Ht(s,"updated",{version:i}),await Ht(s,"ready",{version:i,width:s.width,height:s.height,dpr:s.dpr})),{...s}}async function Uf(t,e,n){let r=Ii(n);if(!r)throw new Error("image must be a PNG");let o=Dt(t,e);await ht.writeFile(o.png,n),Bf(t,{v:e,source:"image",htmlRel:null,pngRel:_t.relative(t.cwd,o.png),width:r.width,height:r.height,dpr:1,renderMs:null,createdAt:Date.now()}),t.width=r.width}async function Ff(t){let e=await ce(),n=e.mocks.find(o=>o.id===t);if(!n)throw new Error("mock not found");if(n.readyVersion==null)throw new Error(`mock is ${n.state} \u2014 nothing to pick yet`);let r=n.group?e.mocks.filter(o=>o.trackId===n.trackId&&o.group===n.group&&o.id!==t&&o.readyVersion!=null).map(o=>({mockId:o.id,name:o.name})):[];return await Ht(n,"picked",{version:n.readyVersion,siblings:r}),{mock:{...n},siblings:r}}async function Hf(t){let e=await ce(),n=e.mocks.findIndex(o=>o.id===t);if(n===-1)throw new Error("mock not found");let r=e.mocks[n];e.mocks.splice(n,1);for(let o=1;o<=r.version;o++){let s=Dt(r,o);await ht.rm(s.html,{force:!0}).catch(()=>{}),await ht.rm(s.png,{force:!0}).catch(()=>{})}return await Xt(),await Ht(r,"deleted"),{ok:!0,id:t,name:r.name}}async function Wf(t){let n=(await ce()).mocks.filter(i=>i.trackId===t.id);if(!n.length)return 0;let r=_t.join($f,"mock-archive",t.id),o=Xo(t.cwd),s=_t.join(r,".bostrat","mocks");await ht.mkdir(s,{recursive:!0});let a=await ht.readdir(o).catch(()=>[]);for(let i of a)await ht.copyFile(_t.join(o,i),_t.join(s,i));for(let i of n){for(let c of i.versions||[])for(let l of[c.htmlRel,c.pngRel]){if(!l)continue;if(!(await ht.stat(_t.join(r,l)).catch(()=>null))?.isFile())throw new Error(`mock evacuation incomplete: ${l} missing from the archive`)}i.cwd=r,i.trackDeletedAt=Date.now(),i.trackName=t.name||t.id}return await Xt(),n.length}async function qf(t){let e=await ce(),n=e.mocks.filter(r=>r.trackId===t);return n.length&&(e.mocks=e.mocks.filter(r=>r.trackId!==t),await Xt()),n.length}import sn from"node:fs/promises";import _e from"node:path";import Rx from"node:os";import{randomUUID as $x}from"node:crypto";var Fn=process.env.BOSTRAT_STATE_DIR||_e.join(process.env.HOME||Rx.tmpdir(),".bostrat"),Kf=_e.join(Fn,"runbooks.json"),$i=_e.join(Fn,"runbooks"),Gf=256*1024,Ox=[{label:"Anthropic API key",re:/\bsk-ant-[A-Za-z0-9_-]{20,}/},{label:"OpenAI-style API key",re:/\bsk-(?:proj-|svcacct-)?[A-Za-z0-9_-]{32,}\b/},{label:"Stripe secret key",re:/\bsk_(?:live|test)_[A-Za-z0-9]{16,}/},{label:"GitHub token",re:/\b(?:ghp|gho|ghu|ghs|ghr)_[A-Za-z0-9]{36,}|\bgithub_pat_[A-Za-z0-9_]{22,}/},{label:"AWS access key id",re:/\bAKIA[0-9A-Z]{16}\b/},{label:"Slack token",re:/\bxox[baprs]-[A-Za-z0-9-]{10,}/},{label:"private key block",re:/-----BEGIN [A-Z ]*PRIVATE KEY-----/},{label:"JWT",re:/\beyJ[A-Za-z0-9_-]{10,}\.eyJ[A-Za-z0-9_-]{10,}\.[A-Za-z0-9_-]{10,}\b/}];function Cx(t){return Ox.filter(({re:e})=>e.test(t)).map(({label:e})=>e)}var on=null,Ri=null;async function Ee(){return on||(Ri||=(async()=>{try{on=JSON.parse(await sn.readFile(Kf,"utf8"))}catch{on={runbooks:[]}}finally{Ri=null}return on.runbooks||=[],on})(),Ri)}async function Hn(){await qt(Kf,on)}function Wn(t,e,n={}){return R({id:t.trackId,cwd:t.cwd},{type:"runbook.state.changed",actor:"orchestrator",source:"orchestrator",boxId:t.box||null,provider:t.agent||null,payload:{ts:Date.now(),change:e,runbookId:t.id,name:t.name,status:t.status,...n}}).catch(()=>{})}function Un(t,e){return _e.join($i,`${t.id}.v${e}.md`)}function Vf(t,e=null){let n=t.versions||[];return e!=null?n.find(r=>r.v===Number(e))||null:n.length?n[n.length-1]:null}function Jf(t){if(typeof t!="string"||!t.trim())throw new Error("md (markdown string) required");let e=Buffer.byteLength(t,"utf8");if(e>Gf)throw new Error(`runbook is ${Math.round(e/1024)} KB \u2014 over the ${Gf/1024} KB cap; a doc that size belongs in the repo, not a runbook`);let n=Cx(t);if(n.length)throw new Error(`runbook appears to contain a secret VALUE (${n.join(", ")}) \u2014 runbooks ship off-box; name the secret and where it goes, never its value. Replace it with a placeholder like <your key> and retry.`);return e}function zf(t,e){if(t.trackDeletedAt)throw new Error(`runbook is orphaned (its Track was deleted) \u2014 cannot ${e} it`)}async function Xf({trackId:t,status:e}={}){let n=await Ee(),r=t?n.runbooks.filter(o=>o.trackId===t):n.runbooks;return e&&(r=r.filter(o=>o.status===e)),r.map(o=>({...o}))}async function Oi(t){let n=(await Ee()).runbooks.find(r=>r.id===t);return n?{...n}:null}async function Yf({trackId:t,trackName:e,name:n,md:r,cwd:o,box:s,agent:a}){if(!t)throw new Error("trackId required");if(typeof n!="string"||!n.trim())throw new Error("name (string) required");let i=Jf(r),c=await Ee(),l={id:$x().slice(0,8),trackId:t,trackName:e||t,box:s||null,agent:a||null,name:n.trim(),status:"open",version:1,versions:[],trackDeletedAt:null,cwd:o||null,createdAt:Date.now(),updatedAt:Date.now()};return await sn.mkdir($i,{recursive:!0}),await sn.writeFile(Un(l,1),r),l.versions.push({v:1,mdRel:_e.relative(Fn,Un(l,1)),bytes:i,note:null,createdAt:Date.now()}),c.runbooks.push(l),await Hn(),await Wn(l,"created",{version:1}),{...l}}async function Zf(t,{md:e,note:n}={}){let o=(await Ee()).runbooks.find(i=>i.id===t);if(!o)throw new Error("runbook not found");zf(o,"update");let s=Jf(e),a=o.version+1;return o.version=a,o.updatedAt=Date.now(),await sn.mkdir($i,{recursive:!0}),await sn.writeFile(Un(o,a),e),o.versions.push({v:a,mdRel:_e.relative(Fn,Un(o,a)),bytes:s,note:n?String(n).slice(0,200):null,createdAt:Date.now()}),await Hn(),await Wn(o,"updated",{version:a,...n?{note:String(n).slice(0,200)}:{}}),{...o}}async function Qf(t,e){if(e!=="open"&&e!=="done")throw new Error('status must be "open" or "done"');let r=(await Ee()).runbooks.find(o=>o.id===t);if(!r)throw new Error("runbook not found");return zf(r,e==="done"?"complete":"reopen"),r.status===e?{...r}:(r.status=e,r.updatedAt=Date.now(),await Hn(),await Wn(r,e==="done"?"done":"reopened"),{...r})}async function tp(t){let e=await Ee(),n=e.runbooks.findIndex(o=>o.id===t);if(n===-1)throw new Error("runbook not found");let r=e.runbooks[n];e.runbooks.splice(n,1);for(let o of r.versions||[])await sn.rm(_e.join(Fn,o.mdRel),{force:!0}).catch(()=>{});return await Hn(),r.trackDeletedAt||await Wn(r,"deleted"),{ok:!0,id:t,name:r.name}}async function ep(t){let n=(await Ee()).runbooks.filter(r=>r.trackId===t&&!r.trackDeletedAt);for(let r of n)r.trackDeletedAt=Date.now(),await Wn(r,"orphaned"),r.cwd=null;return n.length&&await Hn(),n.length}async function np(t){let e=await Ee(),n=e.runbooks.filter(r=>r.trackId===t);for(let r of n){await Wn(r,"deleted");for(let o of r.versions||[])await sn.rm(_e.join(Fn,o.mdRel),{force:!0}).catch(()=>{})}return n.length&&(e.runbooks=e.runbooks.filter(r=>r.trackId!==t),await Hn()),n.length}import rp from"node:fs/promises";import Yo from"node:path";import{randomUUID as Mx}from"node:crypto";var sp=".bostrat/uploads",Ci=t=>Yo.join(t,sp),op=Number(process.env.BOSTRAT_MAX_UPLOAD_BYTES)||24*1024*1024,Px=new Set(["png","jpg","jpeg","gif","webp","svg","bmp","tif","tiff","heic","heif","avif","ico"]),Nx=new Set(["pdf","doc","docx","xls","xlsx","ppt","pptx","odt","ods","odp","rtf","txt","md","markdown","rst","csv","tsv","json","jsonl","yaml","yml","xml","html","htm","log","ini","toml","conf","env","js","jsx","ts","tsx","py","rb","go","rs","java","kt","c","h","cpp","cc","cs","php","swift","sh","bash","zsh","sql","css","scss"]);function Mi(t){let e=String(t||"").toLowerCase().replace(/^\./,"");return Px.has(e)?"image":Nx.has(e)?"doc":null}function Bx(t){let r=Yo.basename(String(t||"")).replace(/[\x00-\x1f]/g,"").replace(/[^A-Za-z0-9._-]+/g,"_").replace(/^_+|_+$/g,"").slice(-80)||"file";return r.startsWith(".")?`f${r}`:r}async function ap(t,{filename:e,buffer:n}){if(!t)throw new Error("cwd required");if(!Buffer.isBuffer(n)||n.length===0)throw new Error("empty upload");if(n.length>op)throw new Error(`file too large \u2014 max ${Math.floor(op/(1024*1024))} MB`);let r=Bx(e),o=Yo.extname(r).slice(1),s=Mi(o);if(!s)throw new Error(`unsupported file type "${o||"unknown"}" \u2014 attach an image or a document`);let a=Mx().slice(0,8),i=Ci(t);await rp.mkdir(i,{recursive:!0});let c=`${a}-${r}`;return await rp.writeFile(Yo.join(i,c),n),{id:a,name:r,rel:`${sp}/${c}`,kind:s,size:n.length}}function ip(t){let e=t.map(r=>`\`${r}\``).join(", ");return`[Attached \u2014 please read ${t.length===1?"this file":`these ${t.length} files`}: ${e}]`}var Dx=/\[Attached — please read (?:this file|these \d+ files): [^\]]*\]$/;function Pi(t){return t.role==="user"&&Dx.test((t.text||"").trim())}function qn(t,e,n=Date.now()){return e?t.idleSince==null?(t.idleSince=n,!1):n-t.idleSince>=6500:(t.idleSince=null,!1)}var Ux=Lx(jx),Ie=process.env.BOSTRAT_STATE_DIR||U.join(process.env.HOME||"/tmp",".bostrat"),mp=U.join(Ie,"tracks.json"),Fx=U.join(Ie,"events.jsonl"),Fi=U.join(Ie,"workspaces"),Hx=process.env.BOSTRAT_DEFAULT_REPO||null,es={claude:{envVar:"BOSTRAT_AGENT_CMD",base:"claude",skipFlag:"--dangerously-skip-permissions"},cursor:{envVar:"BOSTRAT_CURSOR_CMD",base:"cursor-agent",skipFlag:"--force"},codex:{envVar:"BOSTRAT_CODEX_CMD",base:"codex",skipFlag:"--dangerously-bypass-approvals-and-sandbox"}},Wi=Object.keys(es);function Wx(t,{skipPermissions:e=!0}={}){let n=es[t];if(!n)throw new Error(`unknown agent "${t}"`);let r=process.env[n.envVar];return r||(e&&n.skipFlag?`${n.base} ${n.skipFlag}`:n.base)}function hp(t,e,{skipPermissions:n=!0}={}){let r=es[t];if(!r)throw new Error(`unknown agent "${t}"`);let o=process.env[r.envVar];if(o)return o;let s=n&&r.skipFlag?` ${r.skipFlag}`:"";switch(t){case"claude":return`${r.base} --resume ${e}${s}`;case"cursor":return`${r.base} --continue${s}`;case"codex":return`${r.base} resume --last${s}`;default:return`${r.base}${s}`}}function ns(t){let e=es[t];if(e)return process.env[e.envVar]||e.base}async function rs(t,e=!0){let n=Wx(t,{skipPermissions:e});if(!await Us(n)){let r=n.split(/\s+/)[0];throw new Error(`agent "${t}" isn't available on this box (command not found: ${r}). Its CLI must be installed and on PATH before you can hand off / delegate to it.`)}return n}var os=".bostrat/handoff/latest.md";function qx(t,e=os){return`${t.trim()}
|
|
73
75
|
|
|
74
|
-
(Read ${e} for handoff context from the prior lead \u2014 you are now the lead; none of the prior chat is in your context. Then respond to the user above.)`}var
|
|
76
|
+
(Read ${e} for handoff context from the prior lead \u2014 you are now the lead; none of the prior chat is in your context. Then respond to the user above.)`}var Gx=/\(Read \S{1,300} for handoff context from the prior lead — you are now the lead; none of the prior chat is in your context\. Then respond to the user above\.\)$/;function cp(t){return t.role==="user"&&Gx.test((t.text||"").trim())}var qi=".bostrat/handoff/subtask-result.md",Zo=".bostrat/handoff/lead-context.md",Kx=t=>`You are a SUBTASK agent working in an isolated git worktree, delegated by a lead agent on a shared workstream. The lead's full working tree is already checked out here, and the conversation so far is summarized in ${Zo} \u2014 read that first for context. Your scoped task:
|
|
75
77
|
|
|
76
78
|
${t}
|
|
77
79
|
|
|
78
|
-
Work ONLY inside this directory. When you are finished: (1) make your code changes, (2) commit them with git and a clear message, (3) LAST, write a short markdown digest to ${
|
|
79
|
-
`;return Ri=Ri.then(()=>D.mkdir(Ie,{recursive:!0})).then(()=>D.appendFile($x,n)).catch(()=>{}),Ri}var it=(t,e,n={})=>{let r={at:Date.now(),type:e,...n};return t.events.push(r),Dx(t.id,r),ed(t,r),r};fl(async({sessionName:t,from:e,to:n,version:r,status:o,error:s})=>{let i=(await F()).tracks.find(c=>c.leadBox===t||(c.boxes||[]).some(l=>l.name===t));i&&(it(i,"codex-update",{box:t,from:e,to:r||n,status:o,...s?{error:s}:{}}),await K().catch(()=>{}))});var $i=(t,e,n)=>e!=null&&n.some(r=>t===r.box&&e>=r.from&&e<=r.to),jx=new Set(["stuck","wrong-approach","too-slow","want-different-model","context-refresh","cost","parallelize","other"]);function cp(t){if(!t)return null;let e=typeof t=="string"?[t]:Array.isArray(t?.codes)&&t.codes.length?t.codes:t?.code?[t.code]:[],n=[...new Set(e.filter(s=>jx.has(s)))];if(!n.length)return null;let r=typeof t=="object"?t?.note:null,o=r&&String(r).slice(0,500).trim()||null;return{code:n[0],...n.length>1?{codes:n}:{},note:o}}async function lp(t,e,n,{kind:r}={}){let o=await q(e);if(!o)throw new Error(`box "${e}" is not running`);let s=Zo(o.agent);await we({cwd:o.cwd,sessionId:o.sessionId,sessionName:e,agent:o.agent,agentCmd:s}),await yt(e,n,{...re(o.agent),kind:r||"orchestration"}),await j({flow:"orchestration",phase:"sent",trackId:t,sessionName:e,agent:o.agent,kind:r||"orchestration",promptHead:n.slice(0,160)});let a=await F(),i=Y(a,t);if(!i)return;let c=i.boxes.find(l=>l.name===e);it(i,"orchestration-prompt",{box:e,agent:c?.agent||o.agent,boxRole:c?.role,prompt:n,kind:r||"orchestration"}),await K()}var en=new Set(["merged","conflict","canceled","failed","handed-off"]),ep=new Map;async function Lx(t){let e=G.join(yo(t),`${t.id}.ndjson`),n;try{n=await D.stat(e)}catch{return null}let r=ep.get(t.id);if(r&&r.mtimeMs===n.mtimeMs)return r.at;let o=null;for(let s of await P(t)){if(s.type!=="turn.agent")continue;let a=s.payload?.ts??s.ts;a&&(!o||a>o)&&(o=a)}return ep.set(t.id,{mtimeMs:n.mtimeMs,at:o}),o}async function Ux(t,e){let n=e.find(r=>r.name===t.leadBox);return n?.state!=="live"?{working:!1,prompted:!1}:qa(n.name,n.agent)}async function Re(t){let e=new Map((await yn()).map(a=>[a.name,a])),n=t.boxes.map(a=>{let i=e.get(a.name),c=en.has(a.state)?a.state:i?a.role==="lead"?"live":a.state:"stopped";return{...a,state:c,cwd:i?.cwd||a.cwd,agent:i?.agent||a.agent,sessionId:i?.sessionId||a.sessionId}}),[r,{working:o,prompted:s}]=await Promise.all([Lx(t),Ux(t,n)]);return{...t,boxes:n,lastResponseAt:r,working:o,prompted:s}}var Fx=5e3;function Hx(t,e,n){let r=[...t].sort((l,u)=>l.createdAt-u.createdAt),o=[...e].sort((l,u)=>l.birthMs-u.birthMs),s=new Set,a=[];for(let l of r)l.sessionId&&n(l.sessionId)&&!s.has(l.sessionId)?s.add(l.sessionId):a.push(l);let i=new Map(r.map((l,u)=>[l,u])),c=[];for(let l of a){if(l.sessionId&&n(l.sessionId)){c.push({box:l.name,sessionId:null});continue}if(!l.sessionId||l.pinned)continue;let u=i.get(l),d=l.createdAt-Fx,f=r[u+1]?.createdAt??1/0,w=o.find(h=>!s.has(h.id)&&h.birthMs>=d&&h.birthMs<f);w&&(s.add(w.id),c.push({box:l.name,sessionId:w.id}))}return c}async function up(t){let e=new Map;for(let r of t.tracks)for(let o of r.boxes)o.agent==="claude"&&o.cwd&&(e.has(o.cwd)||e.set(o.cwd,[]),e.get(o.cwd).push({track:r,box:o}));let n=!1;for(let[r,o]of e){let s=o.map(f=>f.box.sessionId),a=al(r),i=new Set(a.map(f=>f.id)),c=s.every(f=>f&&i.has(f)),l=new Set(s).size===s.length;if(c&&l)continue;let u=o.map(f=>f.box),d=Hx(u,a,f=>i.has(f));for(let f of d){let w=u.find(h=>h.name===f.box);w&&(w.sessionId=f.sessionId),await Yc(f.box,f.sessionId),n=!0}}return n}async function dt(){let t=await F();return await up(t)&&await K(),Promise.all(t.tracks.map(Re))}async function dp(t,e){let n=await F();if(t)for(let r of n.tracks||[]){let o=r.boxes.find(s=>s.sessionId&&s.sessionId===t);if(o)return{track:r,box:o}}if(e)for(let r of n.tracks||[]){let o=r.boxes.find(s=>s.cwd===e);if(o)return{track:r,box:o}}return null}async function rt(t){let e=await F(),n=Y(e,t);return n?(await up(e)&&await K(),Re(n)):null}var Wx={claude:[/^\[Request interrupted by user(?: for tool use)?\]$/i],codex:[/^\[?Request interrupted/i,/^Conversation interrupted/i],cursor:[/^\[?Request interrupted/i]};function np(t){if(t.role!=="user")return!1;let e=(t.text||"").trim();return(Wx[t.agent]||[]).some(n=>n.test(e))}var qx=[/^\/model(?:\s|$)/i,/<command-(?:name|message|args)>[^<]*\bmodel\b/i,/^Model changed to\b/i];function rp(t){let e=(t.text||"").trim();return e?qx.some(n=>n.test(e)):!1}var Gx=10*6e4;async function ji(t,e={}){let n=await rt(t);if(!n)return null;let r=new Set([...n.events.filter(y=>y.type==="orchestration-prompt"&&y.prompt).map(y=>String(y.prompt).trim()),...n.events.filter(y=>y.type==="said"&&y.text).map(y=>String(y.text).trim())]),o=Mr(n.id),s=!!(o&&!o.finishedAt&&o.step!=="done"&&!o.error),a=s&&o?.startedAt?[...o.fromBox?[{box:o.fromBox,from:o.startedAt,to:Date.now()+1e12}]:[],...o.toBox?[{box:o.toBox,from:o.startedAt,to:Date.now()+1e12}]:[]]:[],i=ii(n.id),c=!!(i&&!i.finishedAt),l=c&&i.startedAt&&i.box?[{box:i.box,from:i.startedAt,to:Date.now()+1e12}]:[],u=[...Yd(n),...l],f={windows:[...vl(n),...Tl(n,co),...a,...u]},h=(await Promise.all(n.boxes.map(y=>du(y,e)))).flat(),g=h.map(y=>{let B={kind:"message",...y};if(B.role==="assistant"&&(B.text||"").trim()&&B.tools?.length){let dn=mr(B.agent).cliTools,zr=B.tools.filter(je=>!dn.has(je));return{...B,tools:zr.length?zr:void 0}}return B}).map(y=>{if(y.role!=="user")return y;let B=Co(y.text);return B?B.kind==="suppress"?null:{...y,note:B,text:Od(B)}:y}).filter(Boolean).filter(y=>!(y.role==="user"&&r.has((y.text||"").trim()))).filter(y=>!np(y)).filter(y=>!rp(y)).filter(y=>!tp(y)).filter(y=>!Ei(y)).filter(y=>y.role!=="assistant"||!!(y.text||"").trim()||!!y.images?.length).filter(y=>!Al(y,f)),x=g.filter(y=>y.failed&&y.text===ba);if(x.length){let y=new Map;for(let B of x){if(!y.has(B.box)){let je=[...(await pt(B.box,{lines:It}).catch(()=>"")).matchAll(/^\s*■\s+(.+?)\s*$/gm)].map(Is=>Is[1]);y.set(B.box,je.length?je[je.length-1]:null)}let dn=y.get(B.box);dn&&(B.text=`\u25A0 ${dn}`)}}let A=await Promise.all(n.boxes.map(y=>pu(y,e)));await $d(n,A.flat()),await Id(n,g),await nd(n),await of(n,g).catch(()=>{});for(let y of n.boxes)y.agent==="codex"&&y.state==="live"&&await Pd(n,y),y.agent==="cursor"&&y.state==="live"&&await Ud(n,y);let S=await Promise.all(n.boxes.map(y=>fu(y,e)));await Rd(n,S.flat().filter(y=>!$i(y.box,y.ts,u)));let v=await P(n),E=c&&i.viewId?i.viewId:null,J=dd(v),I=Mo(v).filter(y=>!(y.kind==="message"&&(np(y)||rp(y)||tp(y)||Ei(y)))&&!(y.kind==="view-card"&&E&&y.viewId===E)),M=fd(I,J),m=I.filter(y=>!(y.kind==="message"&&y.role==="assistant"&&!M.has(y)&&$i(y.box,y.ts,J))),p=Xa(v,m).map(y=>({kind:"command-batch",id:y.id,ts:y.pending?y.ts:(y.anchorTs??y.ts)-1,anchorTs:y.anchorTs,anchorBox:y.anchorBox,agent:y.agent,boxRole:y.boxRole,commands:y.commands,pending:y.pending})).filter(y=>!$i(y.anchorBox,y.anchorTs??y.ts,u)),b=[...m,...p].filter(y=>y.ts!=null).sort((y,B)=>y.ts-B.ts),T=Kx(n),ot=T.size?b.filter(y=>y.kind==="event"||!T.has(y.box??y.anchorBox)):b,$=n.boxes.find(y=>y.name===n.leadBox)||null,z=$?await sr($.cwd,$.sessionId,{fallbackNewest:!1}):null;if(!z&&$){let y=[...v].reverse().find(B=>B.type==="context.usage"&&B.boxId===$.name);y?.payload?.window&&(z={pct:y.payload.pct??(y.payload.occupancy!=null?y.payload.occupancy/y.payload.window:null),occupancy:y.payload.occupancy??null,window:y.payload.window,windowKnown:!0,model:y.payload.model||null})}let N=[...h].reverse().find(y=>y.role==="assistant"&&y.model),H=N?.model||null,_=[...n.events||[]].reverse().find(y=>y.type==="model-change"),W=z?.model||H;_&&(!N?.ts||_.at>=N.ts)&&(W=_.to);let X=$?await pt($.name,{lines:It}).catch(()=>""):"",lt=$&&$n($.agent,X)?await me($.name).catch(()=>X):X,tt=$?Ve($,lt):null;tt&&rd(n,$,tt);let wt=!!$&&!tt&&ir($.agent,X);$&&!$.readyAt&&(wt||tt||X&&so($.agent,X))&&($.readyAt=Date.now(),await K());let Ct=!!$&&!$.readyAt&&Date.now()-(n.createdAt||0)<Gx;if(n.kind==="setup"&&n.setup?.provider&&!n.setup.connectedAt&&$&&so($.agent,X)&&!tt){let y=await F(),B=Y(y,n.id);B?.setup&&!B.setup.connectedAt&&(B.setup.connectedAt=Date.now(),n.setup.connectedAt=B.setup.connectedAt,await R(B,{type:"provider.connected",actor:"system",source:"pane",boxId:$.name,provider:$.agent,payload:{provider:$.agent,ts:B.setup.connectedAt}}).catch(()=>{}),await K())}let De=s?{running:!0,step:o.step,toAgent:o.toAgent||null}:null,Dt=null;if(c){let y=Date.now()-i.startedAt;if(i.viewId&&!wt){let B=Lo(n.id);B&&(it(n,"view-spinup",{box:i.box,startedAt:i.startedAt,endedAt:B.finishedAt}),await K())}else if(tt||y>zd&&!wt&&!i.viewId||y>ai){let B=tt?"needs-input":y>ai?"timeout":"no-view";Lo(n.id,{error:B})&&await R(n,{type:"view.spinup.failed",actor:"orchestrator",source:"orchestrator",boxId:i.box,provider:$?.agent||null,payload:{ts:i.startedAt,name:i.name,reason:B}}).catch(()=>{})}else Dt={running:!0,step:i.step,name:i.name}}return{trackId:n.id,leadBox:n.leadBox,items:ot,context:z?{pct:z.pct,occupancy:z.occupancy,window:z.window,windowKnown:z.windowKnown,model:z.model}:null,models:{current:W,options:Ju($?.agent)},pendingPrompt:tt,working:wt,leadStarting:Ct,handoff:De,view:Dt,queued:Jx(n.id)}}function Kx(t){let e=new Set;for(let n of t.boxes||[])n.role==="subtask"&&n.name&&e.add(n.name);for(let n of Object.values(t.subtasks||{}))n?.box&&e.add(n.box);return e}async function fp(t,e){let n=await rt(t);if(!n)return null;let r=Gn(t).find(d=>d.id===e)||null;if(!r)return null;let o=r.box||null;if(!o)return{items:[],working:!1,box:null,agent:r.agent,task:r.task};let s=await P(n),a=Mo(s).filter(d=>d.box===o),i=Xa(s.filter(d=>d.boxId===o),a.filter(d=>d.kind==="message")).map(d=>({kind:"command-batch",id:d.id,ts:d.pending?d.ts:(d.anchorTs??d.ts)-1,anchorTs:d.anchorTs,anchorBox:d.anchorBox,agent:d.agent,boxRole:d.boxRole,commands:d.commands,pending:d.pending})),c=[...a,...i].filter(d=>d.ts!=null).sort((d,f)=>d.ts-f.ts),l=n.boxes.find(d=>d.name===o)||null,u=!1;if(l&&l.state==="live"){let d=await pt(o,{lines:It}).catch(()=>"");u=ir(l.agent||r.agent,d)}return{items:c,working:u,box:o,agent:r.agent,task:r.task}}async function Li(t,{promptId:e,answers:n,text:r}={}){let o=Y(await F(),t);if(!o)throw new Error(`track "${t}" not found`);let s=o.boxes.find(f=>f.name===o.leadBox);if(!s)throw new Error(`track "${t}" has no lead box`);if(!await q(o.leadBox))throw new Error(`lead box "${o.leadBox}" is not running`);let i=await Oa(s);if(!i)throw new Error("no pending prompt to answer");if(e&&e!==i.promptId)throw new Error("prompt changed since it was shown \u2014 reload and answer the current prompt");let c;if(i.answerMode==="text"){let f=typeof r=="string"?r.trim():"";if(!f)throw new Error("this prompt needs a code \u2014 paste it into the card");if(/\s/.test(f)||f.length>2048)throw new Error("that doesn't look like a sign-in code \u2014 paste the code exactly as shown");await yt(o.leadBox,f,{enter:!0,kind:"answer-prompt"}),c={mode:"text-input",value:"\u2039code\u203A",count:1,redacted:!0}}else if(i.answerMode==="esc")await gt(o.leadBox,["Escape"],{kind:"answer-prompt"}),c={mode:"keys",value:"Escape",count:1};else if(i.provider==="claude"||i.answerMode==="keys"){let f=Iu(i,n||[]);await gt(o.leadBox,f,{kind:"answer-prompt"}),c={mode:"keys",value:f.join(" "),count:f.length}}else{let f=yu(i,n||[]);await yt(o.leadBox,f,{enter:!0,kind:"answer-prompt"}),c={mode:"text",value:f,count:1}}let l=i.questions?.[0]||{},u=(Array.isArray(n?.[0])?n[0]:[]).map(f=>l.options?.[f]).filter(Boolean).map(f=>({label:f.label,description:f.description||""})),d=i.review?"review":/^claude:model-confirm:/.test(i.promptId||"")?"model-confirm":/^claude:resume-confirm:/.test(i.promptId||"")?"resume-confirm":void 0;return c={...c,groupId:i.groupId||i.promptId,questionIndex:i.questionIndex??0,questionTotal:i.questionTotal??1,header:l.header||"",question:l.question||"",multiSelect:!!l.multiSelect,chosen:u,...d?{control:d}:{}},await j({flow:"prompt",phase:"answered",sessionName:o.leadBox,agent:s.agent,provider:i.provider,promptId:i.promptId,...c}).catch(()=>{}),od(o,s,i,c),{ok:!0,promptId:i.promptId,sent:c.count}}async function pp(t,{promptId:e,questionIndex:n}={}){let r=Y(await F(),t);if(!r)throw new Error(`track "${t}" not found`);let o=r.boxes.find(l=>l.name===r.leadBox);if(!o)throw new Error(`track "${t}" has no lead box`);if(!await q(r.leadBox))throw new Error(`lead box "${r.leadBox}" is not running`);let a=await Oa(o);if(!a)throw new Error("no pending prompt to navigate");if(a.provider!=="claude")throw new Error("prompt navigation not supported for this provider");if(a.review)throw new Error("cannot navigate on the review screen \u2014 answer or cancel");let i=a.questionTotal??1;if(i<2)throw new Error("not a multi-question prompt");if(!Number.isInteger(n)||n<0||n>=i)throw new Error("questionIndex out of range");if(e&&e!==a.promptId)throw new Error("prompt changed since it was shown \u2014 reload and try again");let c=Ru(a,n);return c.length?(await gt(r.leadBox,c,{kind:"navigate-prompt"}),await j({flow:"prompt",phase:"navigated",sessionName:r.leadBox,agent:o.agent,provider:a.provider,promptId:a.promptId,fromIndex:a.questionIndex??0,toIndex:n,mode:"keys",value:c.join(" "),count:c.length}).catch(()=>{}),{ok:!0,promptId:a.promptId,sent:c.length}):{ok:!0,promptId:a.promptId,sent:0}}var Vx=1500,Ir=new Map;function mp(t){let e=Ir.get(t);return e||(e={items:[],timer:null,flushing:!1,idleSince:null},Ir.set(t,e)),e}function Jx(t){return(Ir.get(t)?.items??[]).map(e=>({...e}))}function es(t){let e=Ir.get(t);if(!e)return[];let n=e.items.splice(0);return e.idleSince=null,e.timer&&(clearInterval(e.timer),e.timer=null),n}function zx(t){let e=mp(t);e.timer||(e.timer=setInterval(()=>Xx(t).catch(()=>{}),Vx))}async function Xx(t){let e=Ir.get(t);if(!(!e||e.flushing)){if(!e.items.length){e.timer&&(clearInterval(e.timer),e.timer=null);return}e.flushing=!0;try{let n=Y(await F(),t);if(!n||n.archivedAt){es(t);return}let r=Mr(t);if(r&&!r.finishedAt&&r.step!=="done"&&!r.error){Hn(e,!1);return}let o=await q(n.leadBox);if(!o){Hn(e,!1);return}let s=n.boxes.find(c=>c.name===n.leadBox),a=await pt(n.leadBox,{lines:It}).catch(()=>"");if(oo(s?.agent,a)){Hn(e,!1);return}if($n(s?.agent,a)){let c=await me(n.leadBox).catch(()=>a);if(Ve(s,c)){Hn(e,!1);return}}if(!Hn(e,!0))return;let i=e.items.shift();e.idleSince=null,await hp(n,s,o,i.text,i.kind??null,i.attachments??null),await K()}finally{e.flushing=!1}}}async function hp(t,e,n,r,o=null,s=null){let a=r,i=!1;t.pendingHandoffHydrate&&(a=Mx(r,t.pendingHandoffHydrate.artifact||ts),delete t.pendingHandoffHydrate,i=!0);let c=Array.isArray(s)?s.filter(l=>l&&l.rel):[];if(c.length){let l=Qf(c.map(u=>u.rel));a=a.trim()?`${a.trim()}
|
|
80
|
+
Work ONLY inside this directory. When you are finished: (1) make your code changes, (2) commit them with git and a clear message, (3) LAST, write a short markdown digest to ${qi} with sections \u2014 Result (what you did), Files (paths you changed), Notes (anything the lead must know). Writing that file is your DONE signal: do it last, then stop and take no further action.`,Vx=(t,e,n,r)=>`A subtask box you delegated has finished: "${t}". Its changes are now merged into your working tree (${e.files} file${e.files===1?"":"s"}, +${e.insertions} \u2212${e.deletions}). Read ${r} for exactly what it did. `+(n.length?`IMPORTANT: ${n.length} merge conflict${n.length===1?"":"s"} need resolving \u2014 fix the conflict markers in: ${n.join(", ")}, then continue.`:"Review the merged changes and continue the work."),Yt=null,Ni=null;async function H(){return Yt||(Ni||=(async()=>{try{Yt=JSON.parse(await P.readFile(mp,"utf8"))}catch{Yt={tracks:[]}}finally{Ni=null}return Yt})(),Ni)}async function K(){await qt(mp,Yt)}var Q=(t,e)=>t.tracks.find(n=>n.id===e)||null,Bi=Promise.resolve();function Jx(t,e){let n=JSON.stringify({trackId:t,...e})+`
|
|
81
|
+
`;return Bi=Bi.then(()=>P.mkdir(Ie,{recursive:!0})).then(()=>P.appendFile(Fx,n)).catch(()=>{}),Bi}var it=(t,e,n={})=>{let r={at:Date.now(),type:e,...n};return t.events.push(r),Jx(t.id,r),cd(t,r),r};kl(async({sessionName:t,from:e,to:n,version:r,status:o,error:s})=>{let i=(await H()).tracks.find(c=>c.leadBox===t||(c.boxes||[]).some(l=>l.name===t));i&&(it(i,"codex-update",{box:t,from:e,to:r||n,status:o,...s?{error:s}:{}}),await K().catch(()=>{}))});var Di=(t,e,n)=>e!=null&&n.some(r=>t===r.box&&e>=r.from&&e<=r.to),zx=new Set(["stuck","wrong-approach","too-slow","want-different-model","context-refresh","cost","parallelize","other"]);function wp(t){if(!t)return null;let e=typeof t=="string"?[t]:Array.isArray(t?.codes)&&t.codes.length?t.codes:t?.code?[t.code]:[],n=[...new Set(e.filter(s=>zx.has(s)))];if(!n.length)return null;let r=typeof t=="object"?t?.note:null,o=r&&String(r).slice(0,500).trim()||null;return{code:n[0],...n.length>1?{codes:n}:{},note:o}}async function gp(t,e,n,{kind:r}={}){let o=await G(e);if(!o)throw new Error(`box "${e}" is not running`);let s=ns(o.agent);await we({cwd:o.cwd,sessionId:o.sessionId,sessionName:e,agent:o.agent,agentCmd:s}),await yt(e,n,{...ne(o.agent),kind:r||"orchestration"}),await L({flow:"orchestration",phase:"sent",trackId:t,sessionName:e,agent:o.agent,kind:r||"orchestration",promptHead:n.slice(0,160)});let a=await H(),i=Q(a,t);if(!i)return;let c=i.boxes.find(l=>l.name===e);it(i,"orchestration-prompt",{box:e,agent:c?.agent||o.agent,boxRole:c?.role,prompt:n,kind:r||"orchestration"}),await K()}var an=new Set(["merged","conflict","canceled","failed","handed-off"]),lp=new Map;async function Xx(t){let e=U.join(So(t),`${t.id}.ndjson`),n;try{n=await P.stat(e)}catch{return null}let r=lp.get(t.id);if(r&&r.mtimeMs===n.mtimeMs)return r.at;let o=null;for(let s of await B(t)){if(s.type!=="turn.agent")continue;let a=s.payload?.ts??s.ts;a&&(!o||a>o)&&(o=a)}return lp.set(t.id,{mtimeMs:n.mtimeMs,at:o}),o}async function Yx(t,e){let n=e.find(r=>r.name===t.leadBox);return n?.state!=="live"?{working:!1,prompted:!1}:Xa(n.name,n.agent)}async function Re(t){let e=new Map((await vn()).map(a=>[a.name,a])),n=t.boxes.map(a=>{let i=e.get(a.name),c=an.has(a.state)?a.state:i?a.role==="lead"?"live":a.state:"stopped";return{...a,state:c,cwd:i?.cwd||a.cwd,agent:i?.agent||a.agent,sessionId:i?.sessionId||a.sessionId}}),[r,{working:o,prompted:s}]=await Promise.all([Xx(t),Yx(t,n)]);return{...t,boxes:n,lastResponseAt:r,working:o,prompted:s}}var Zx=5e3;function Qx(t,e,n){let r=[...t].sort((l,u)=>l.createdAt-u.createdAt),o=[...e].sort((l,u)=>l.birthMs-u.birthMs),s=new Set,a=[];for(let l of r)l.sessionId&&n(l.sessionId)&&!s.has(l.sessionId)?s.add(l.sessionId):a.push(l);let i=new Map(r.map((l,u)=>[l,u])),c=[];for(let l of a){if(l.sessionId&&n(l.sessionId)){c.push({box:l.name,sessionId:null});continue}if(!l.sessionId||l.pinned)continue;let u=i.get(l),d=l.createdAt-Zx,f=r[u+1]?.createdAt??1/0,w=o.find(m=>!s.has(m.id)&&m.birthMs>=d&&m.birthMs<f);w&&(s.add(w.id),c.push({box:l.name,sessionId:w.id}))}return c}async function yp(t){let e=new Map;for(let r of t.tracks)for(let o of r.boxes)o.agent==="claude"&&o.cwd&&(e.has(o.cwd)||e.set(o.cwd,[]),e.get(o.cwd).push({track:r,box:o}));let n=!1;for(let[r,o]of e){let s=o.map(f=>f.box.sessionId),a=ml(r),i=new Set(a.map(f=>f.id)),c=s.every(f=>f&&i.has(f)),l=new Set(s).size===s.length;if(c&&l)continue;let u=o.map(f=>f.box),d=Qx(u,a,f=>i.has(f));for(let f of d){let w=u.find(m=>m.name===f.box);w&&(w.sessionId=f.sessionId),await sl(f.box,f.sessionId),n=!0}}return n}async function dt(){let t=await H();return await yp(t)&&await K(),Promise.all(t.tracks.map(Re))}async function bp(t,e){let n=await H();if(t)for(let r of n.tracks||[]){let o=r.boxes.find(s=>s.sessionId&&s.sessionId===t);if(o)return{track:r,box:o}}if(e)for(let r of n.tracks||[]){let o=r.boxes.find(s=>s.cwd===e);if(o)return{track:r,box:o}}return null}async function rt(t){let e=await H(),n=Q(e,t);return n?(await yp(e)&&await K(),Re(n)):null}var t0={claude:[/^\[Request interrupted by user(?: for tool use)?\]$/i],codex:[/^\[?Request interrupted/i,/^Conversation interrupted/i],cursor:[/^\[?Request interrupted/i]};function up(t){if(t.role!=="user")return!1;let e=(t.text||"").trim();return(t0[t.agent]||[]).some(n=>n.test(e))}var e0=[/^\/model(?:\s|$)/i,/<command-(?:name|message|args)>[^<]*\bmodel\b/i,/^Model changed to\b/i];function dp(t){let e=(t.text||"").trim();return e?e0.some(n=>n.test(e)):!1}var n0=10*6e4;async function Gi(t,e={}){let n=await rt(t);if(!n)return null;let r=new Set([...n.events.filter(y=>y.type==="orchestration-prompt"&&y.prompt).map(y=>String(y.prompt).trim()),...n.events.filter(y=>y.type==="said"&&y.text).map(y=>String(y.text).trim())]),o=Br(n.id),s=!!(o&&!o.finishedAt&&o.step!=="done"&&!o.error),a=s&&o?.startedAt?[...o.fromBox?[{box:o.fromBox,from:o.startedAt,to:Date.now()+1e12}]:[],...o.toBox?[{box:o.toBox,from:o.startedAt,to:Date.now()+1e12}]:[]]:[],i=mi(n.id),c=!!(i&&!i.finishedAt),l=c&&i.startedAt&&i.box?[{box:i.box,from:i.startedAt,to:Date.now()+1e12}]:[],u=[...sf(n),...l],f={windows:[...Ol(n),...Cl(n,fo),...a,...u]},m=(await Promise.all(n.boxes.map(y=>yu(y,e)))).flat(),g=m.map(y=>{let j={kind:"message",...y};if(j.role==="assistant"&&(j.text||"").trim()&&j.tools?.length){let wn=yr(j.agent).cliTools,Zr=j.tools.filter(je=>!wn.has(je));return{...j,tools:Zr.length?Zr:void 0}}return j}).map(y=>{if(y.role!=="user")return y;let j=Bo(y.text);return j?j.kind==="suppress"?null:{...y,note:j,text:Ld(j)}:y}).filter(Boolean).filter(y=>!(y.role==="user"&&r.has((y.text||"").trim()))).filter(y=>!up(y)).filter(y=>!dp(y)).filter(y=>!cp(y)).filter(y=>!Pi(y)).filter(y=>y.role!=="assistant"||!!(y.text||"").trim()||!!y.images?.length).filter(y=>!Ml(y,f)),x=g.filter(y=>y.failed&&y.text===Ta);if(x.length){let y=new Map;for(let j of x){if(!y.has(j.box)){let je=[...(await pt(j.box,{lines:$t}).catch(()=>"")).matchAll(/^\s*■\s+(.+?)\s*$/gm)].map(Os=>Os[1]);y.set(j.box,je.length?je[je.length-1]:null)}let wn=y.get(j.box);wn&&(j.text=`\u25A0 ${wn}`)}}let A=await Promise.all(n.boxes.map(y=>ku(y,e)));await Dd(n,A.flat()),await Nd(n,g),await ld(n),await pf(n,g).catch(()=>{});for(let y of n.boxes)y.agent==="codex"&&y.state==="live"&&await Wd(n,y),y.agent==="cursor"&&y.state==="live"&&await Jd(n,y);let S=await Promise.all(n.boxes.map(y=>bu(y,e)));await Bd(n,S.flat().filter(y=>!Di(y.box,y.ts,u)));let T=await B(n),E=c&&i.viewId?i.viewId:null,J=yd(T),M=Do(T).filter(y=>!(y.kind==="message"&&(up(y)||dp(y)||cp(y)||Pi(y)))&&!(y.kind==="view-card"&&E&&y.viewId===E)),I=bd(M,J),h=M.filter(y=>!(y.kind==="message"&&y.role==="assistant"&&!I.has(y)&&Di(y.box,y.ts,J))),p=ri(T,h).map(y=>({kind:"command-batch",id:y.id,ts:y.pending?y.ts:(y.anchorTs??y.ts)-1,anchorTs:y.anchorTs,anchorBox:y.anchorBox,agent:y.agent,boxRole:y.boxRole,commands:y.commands,pending:y.pending})).filter(y=>!Di(y.anchorBox,y.anchorTs??y.ts,u)),b=[...h,...p].filter(y=>y.ts!=null).sort((y,j)=>y.ts-j.ts),v=r0(n),ot=v.size?b.filter(y=>y.kind==="event"||!v.has(y.box??y.anchorBox)):b,$=n.boxes.find(y=>y.name===n.leadBox)||null,z=$?await lr($.cwd,$.sessionId,{fallbackNewest:!1}):null;if(!z&&$){let y=[...T].reverse().find(j=>j.type==="context.usage"&&j.boxId===$.name);y?.payload?.window&&(z={pct:y.payload.pct??(y.payload.occupancy!=null?y.payload.occupancy/y.payload.window:null),occupancy:y.payload.occupancy??null,window:y.payload.window,windowKnown:!0,model:y.payload.model||null})}let N=[...m].reverse().find(y=>y.role==="assistant"&&y.model),W=N?.model||null,_=[...n.events||[]].reverse().find(y=>y.type==="model-change"),q=z?.model||W;_&&(!N?.ts||_.at>=N.ts)&&(q=_.to);let X=$?await pt($.name,{lines:$t}).catch(()=>""):"",lt=$&&Cn($.agent,X)?await me($.name).catch(()=>X):X,nt=$?Xe($,lt):null;nt&&ud(n,$,nt);let wt=!!$&&!nt&&dr($.agent,X);$&&!$.readyAt&&(wt||nt||X&&co($.agent,X))&&($.readyAt=Date.now(),await K());let Pt=!!$&&!$.readyAt&&Date.now()-(n.createdAt||0)<n0;if(n.kind==="setup"&&n.setup?.provider&&!n.setup.connectedAt&&$&&co($.agent,X)&&!nt){let y=await H(),j=Q(y,n.id);j?.setup&&!j.setup.connectedAt&&(j.setup.connectedAt=Date.now(),n.setup.connectedAt=j.setup.connectedAt,await R(j,{type:"provider.connected",actor:"system",source:"pane",boxId:$.name,provider:$.agent,payload:{provider:$.agent,ts:j.setup.connectedAt}}).catch(()=>{}),await K())}let De=s?{running:!0,step:o.step,toAgent:o.toAgent||null}:null,jt=null;if(c){let y=Date.now()-i.startedAt;if(i.viewId&&!wt){let j=Wo(n.id);j&&(it(n,"view-spinup",{box:i.box,startedAt:i.startedAt,endedAt:j.finishedAt}),await K())}else if(nt||y>rf&&!wt&&!i.viewId||y>pi){let j=nt?"needs-input":y>pi?"timeout":"no-view";Wo(n.id,{error:j})&&await R(n,{type:"view.spinup.failed",actor:"orchestrator",source:"orchestrator",boxId:i.box,provider:$?.agent||null,payload:{ts:i.startedAt,name:i.name,reason:j}}).catch(()=>{})}else jt={running:!0,step:i.step,name:i.name}}return{trackId:n.id,leadBox:n.leadBox,items:ot,context:z?{pct:z.pct,occupancy:z.occupancy,window:z.window,windowKnown:z.windowKnown,model:z.model}:null,models:{current:q,options:ed($?.agent)},pendingPrompt:nt,working:wt,leadStarting:Pt,handoff:De,view:jt,queued:s0(n.id)}}function r0(t){let e=new Set;for(let n of t.boxes||[])n.role==="subtask"&&n.name&&e.add(n.name);for(let n of Object.values(t.subtasks||{}))n?.box&&e.add(n.box);return e}async function kp(t,e){let n=await rt(t);if(!n)return null;let r=Jn(t).find(d=>d.id===e)||null;if(!r)return null;let o=r.box||null;if(!o)return{items:[],working:!1,box:null,agent:r.agent,task:r.task};let s=await B(n),a=Do(s).filter(d=>d.box===o),i=ri(s.filter(d=>d.boxId===o),a.filter(d=>d.kind==="message")).map(d=>({kind:"command-batch",id:d.id,ts:d.pending?d.ts:(d.anchorTs??d.ts)-1,anchorTs:d.anchorTs,anchorBox:d.anchorBox,agent:d.agent,boxRole:d.boxRole,commands:d.commands,pending:d.pending})),c=[...a,...i].filter(d=>d.ts!=null).sort((d,f)=>d.ts-f.ts),l=n.boxes.find(d=>d.name===o)||null,u=!1;if(l&&l.state==="live"){let d=await pt(o,{lines:$t}).catch(()=>"");u=dr(l.agent||r.agent,d)}return{items:c,working:u,box:o,agent:r.agent,task:r.task}}async function Ki(t,{promptId:e,answers:n,text:r}={}){let o=Q(await H(),t);if(!o)throw new Error(`track "${t}" not found`);let s=o.boxes.find(f=>f.name===o.leadBox);if(!s)throw new Error(`track "${t}" has no lead box`);if(!await G(o.leadBox))throw new Error(`lead box "${o.leadBox}" is not running`);let i=await Ba(s);if(!i)throw new Error("no pending prompt to answer");if(e&&e!==i.promptId)throw new Error("prompt changed since it was shown \u2014 reload and answer the current prompt");let c;if(i.answerMode==="text"){let f=typeof r=="string"?r.trim():"";if(!f)throw new Error("this prompt needs a code \u2014 paste it into the card");if(/\s/.test(f)||f.length>2048)throw new Error("that doesn't look like a sign-in code \u2014 paste the code exactly as shown");await yt(o.leadBox,f,{enter:!0,kind:"answer-prompt"}),c={mode:"text-input",value:"\u2039code\u203A",count:1,redacted:!0}}else if(i.answerMode==="esc")await gt(o.leadBox,["Escape"],{kind:"answer-prompt"}),c={mode:"keys",value:"Escape",count:1};else if(i.provider==="claude"||i.answerMode==="keys"){let f=Bu(i,n||[]);await gt(o.leadBox,f,{kind:"answer-prompt"}),c={mode:"keys",value:f.join(" "),count:f.length}}else{let f=Au(i,n||[]);await yt(o.leadBox,f,{enter:!0,kind:"answer-prompt"}),c={mode:"text",value:f,count:1}}let l=i.questions?.[0]||{},u=(Array.isArray(n?.[0])?n[0]:[]).map(f=>l.options?.[f]).filter(Boolean).map(f=>({label:f.label,description:f.description||""})),d=i.review?"review":/^claude:model-confirm:/.test(i.promptId||"")?"model-confirm":/^claude:resume-confirm:/.test(i.promptId||"")?"resume-confirm":void 0;return c={...c,groupId:i.groupId||i.promptId,questionIndex:i.questionIndex??0,questionTotal:i.questionTotal??1,header:l.header||"",question:l.question||"",multiSelect:!!l.multiSelect,chosen:u,...d?{control:d}:{}},await L({flow:"prompt",phase:"answered",sessionName:o.leadBox,agent:s.agent,provider:i.provider,promptId:i.promptId,...c}).catch(()=>{}),dd(o,s,i,c),{ok:!0,promptId:i.promptId,sent:c.count}}async function xp(t,{promptId:e,questionIndex:n}={}){let r=Q(await H(),t);if(!r)throw new Error(`track "${t}" not found`);let o=r.boxes.find(l=>l.name===r.leadBox);if(!o)throw new Error(`track "${t}" has no lead box`);if(!await G(r.leadBox))throw new Error(`lead box "${r.leadBox}" is not running`);let a=await Ba(o);if(!a)throw new Error("no pending prompt to navigate");if(a.provider!=="claude")throw new Error("prompt navigation not supported for this provider");if(a.review)throw new Error("cannot navigate on the review screen \u2014 answer or cancel");let i=a.questionTotal??1;if(i<2)throw new Error("not a multi-question prompt");if(!Number.isInteger(n)||n<0||n>=i)throw new Error("questionIndex out of range");if(e&&e!==a.promptId)throw new Error("prompt changed since it was shown \u2014 reload and try again");let c=Du(a,n);return c.length?(await gt(r.leadBox,c,{kind:"navigate-prompt"}),await L({flow:"prompt",phase:"navigated",sessionName:r.leadBox,agent:o.agent,provider:a.provider,promptId:a.promptId,fromIndex:a.questionIndex??0,toIndex:n,mode:"keys",value:c.join(" "),count:c.length}).catch(()=>{}),{ok:!0,promptId:a.promptId,sent:c.length}):{ok:!0,promptId:a.promptId,sent:0}}var o0=1500,Or=new Map;function Sp(t){let e=Or.get(t);return e||(e={items:[],timer:null,flushing:!1,idleSince:null},Or.set(t,e)),e}function s0(t){return(Or.get(t)?.items??[]).map(e=>({...e}))}function ss(t){let e=Or.get(t);if(!e)return[];let n=e.items.splice(0);return e.idleSince=null,e.timer&&(clearInterval(e.timer),e.timer=null),n}function a0(t){let e=Sp(t);e.timer||(e.timer=setInterval(()=>i0(t).catch(()=>{}),o0))}async function i0(t){let e=Or.get(t);if(!(!e||e.flushing)){if(!e.items.length){e.timer&&(clearInterval(e.timer),e.timer=null);return}e.flushing=!0;try{let n=Q(await H(),t);if(!n||n.archivedAt){ss(t);return}let r=Br(t);if(r&&!r.finishedAt&&r.step!=="done"&&!r.error){qn(e,!1);return}let o=await G(n.leadBox);if(!o){qn(e,!1);return}let s=n.boxes.find(c=>c.name===n.leadBox),a=await pt(n.leadBox,{lines:$t}).catch(()=>"");if(io(s?.agent,a)){qn(e,!1);return}if(Cn(s?.agent,a)){let c=await me(n.leadBox).catch(()=>a);if(Xe(s,c)){qn(e,!1);return}}if(!qn(e,!0))return;let i=e.items.shift();e.idleSince=null,await vp(n,s,o,i.text,i.kind??null,i.attachments??null),await K()}finally{e.flushing=!1}}}async function vp(t,e,n,r,o=null,s=null){let a=r,i=!1;t.pendingHandoffHydrate&&(a=qx(r,t.pendingHandoffHydrate.artifact||os),delete t.pendingHandoffHydrate,i=!0);let c=Array.isArray(s)?s.filter(l=>l&&l.rel):[];if(c.length){let l=ip(c.map(u=>u.rel));a=a.trim()?`${a.trim()}
|
|
80
82
|
|
|
81
|
-
${l}`:l}await we({cwd:n.cwd,sessionId:n.sessionId,sessionName:t.leadBox,agent:n.agent,agentCmd:
|
|
83
|
+
${l}`:l}await we({cwd:n.cwd,sessionId:n.sessionId,sessionName:t.leadBox,agent:n.agent,agentCmd:ns(n.agent)}),await yt(t.leadBox,a,{...ne(e?.agent),kind:i?"lead-handoff-first-prompt":"say"}),i&&await L({flow:"lead-handoff",phase:"first-prompt",trackId:t.id,sessionName:t.leadBox,agent:e?.agent,userTextLen:r.length}),it(t,"said",{box:t.leadBox,agent:e?.agent,boxRole:e?.role,text:r,...o?{kind:o}:{},...c.length?{attachments:c}:{}}),delete t.draft}async function $e(t,e,{kind:n=null,attachments:r=null}={}){let o=Array.isArray(r)?r.filter(f=>f&&typeof f.rel=="string"):[];if((typeof e!="string"||!Mn(e))&&!o.length)throw new Error("text (string) or an attachment required");let s=Q(await H(),t);if(!s)throw new Error(`track "${t}" not found`);if(s.archivedAt)throw new Error(`track "${t}" is archived \u2014 restore it before driving it`);let a=await G(s.leadBox);if(!a&&await zi(s)&&(await K(),a=await G(s.leadBox)),!a)throw new Error(`lead box "${s.leadBox}" is not running`);let i=s.boxes.find(f=>f.name===s.leadBox),c=Mn(e),l=Sp(t),u=await pt(s.leadBox,{lines:$t}).catch(()=>""),d=l.items.length>0||io(i?.agent,u);if(!d&&Cn(i?.agent,u)){let f=await me(s.leadBox).catch(()=>u);d=!!Xe(i,f)}if(d){let f={id:ts(),text:c,ts:Date.now(),...n?{kind:n}:{},...o.length?{attachments:o}:{}};return l.items.push(f),a0(t),delete s.draft,await K(),{ok:!0,leadBox:s.leadBox,queued:!0,id:f.id,position:l.items.length}}return await vp(s,i,a,c,n,o),await K(),{ok:!0,leadBox:s.leadBox}}var fp="Spin up a live preview View of this app so I can see it on another device. From the repo root, run `bostrat-view up --name \"<short name>\" -- <serve command>` \u2014 pick the right dev/serve command for this repo yourself (e.g. `npm run dev`), pass $PORT through if the command needs an explicit port, and share the public URL it prints. If a suitable View is already live, just re-share its URL instead of starting another. Don't paste a localhost link \u2014 it's dead on my device.\n\nIMPORTANT: if you cannot confidently identify WHICH project/app to serve, or which serve command to use (e.g. several apps in this tree, or no obvious dev script), do NOT guess and do NOT run `bostrat-view up`. Instead, reply with a short question asking me which one you should preview, and stop.";async function Tp(t,{hint:e}={}){let n=Q(await H(),t);if(!n)throw new Error(`track "${t}" not found`);if(n.archivedAt)throw new Error(`track "${t}" is archived \u2014 restore it first`);let r=mi(n.id);if(r&&!r.finishedAt)throw new Error("a view is already being spun up for this Track");let o=n.boxes.find(i=>i.name===n.leadBox),s=String(e||"").trim()?String(e).trim().slice(0,40):"preview";of(n.id,{name:s,box:n.leadBox});let a=String(e||"").trim()?`${fp}
|
|
82
84
|
|
|
83
|
-
What to preview: ${String(e).trim()}`:
|
|
85
|
+
What to preview: ${String(e).trim()}`:fp;try{await $e(t,a,{kind:"spin-up-view"})}catch(i){throw Wo(n.id,{error:"drive-failed"}),i}return{ok:!0,running:!0}}async function Vi(t){let e=Q(await H(),t);if(!e)throw new Error(`track "${t}" not found`);if(e.archivedAt)throw new Error(`track "${t}" is archived`);let n=e.boxes.find(i=>i.name===e.leadBox),r=ss(t),o=await G(e.leadBox);o&&await gt(e.leadBox,["Escape"],{kind:"interrupt"});let s=o?await u0(e,n):null,a=o?await c0(e).catch(()=>null):null;return await L({flow:"interrupt",phase:"sent",trackId:t,sessionName:e.leadBox,agent:n?.agent,dropped:r.length,unsent:!!s,shipDismissed:!!a}).catch(()=>{}),{ok:!0,interrupted:!!o,dropped:r.length,restored:s}}async function c0(t){let e=Ai(await B(t));return e?.offerId?(await R(t,{type:"pr.offer.answered",actor:"user",source:"orchestrator",boxId:e.box,payload:{ts:Date.now(),offerId:e.offerId,action:"dismiss",actions:null,reason:"interrupt"}}),e.offerId):null}var l0=[250,400,500,600];async function u0(t,e){if(e?.agent!=="claude")return null;let n=[...t.events||[]].reverse().find(r=>r.type==="said"&&r.text);if(!n)return null;for(let r of l0){await ec(r);let o=await pt(t.leadBox,{lines:$t}).catch(()=>""),s=Da(e.agent,o);if(s&&Lu(n.text,s))return await d0(t.leadBox,e.agent,n.text.length+8),t.draft={text:n.text,updatedAt:Date.now()},it(t,"unsaid",{box:t.leadBox,agent:e.agent,boxRole:e.role,saidTs:n.at,text:n.text}),await K(),n.text}return null}async function d0(t,e,n){for(let r=0;r<3;r++){await ro(t,n,{kind:"unsend-wipe"}).catch(()=>{}),await ec(200);let o=await pt(t,{lines:$t}).catch(()=>""),s=Da(e,o);if(!s)return!0;n=s.length+8}return!1}async function Ap(t,e){if(typeof e!="string")throw new Error("text (string) required");let n=Q(await H(),t);if(!n)throw new Error(`track "${t}" not found`);let r=Mn(e);return r?n.draft={text:r,updatedAt:Date.now()}:delete n.draft,await K(),{ok:!0,draft:n.draft||null}}async function _p(t,e){if(typeof e!="string"||!e.trim())throw new Error("model (string) required");let n=e.trim(),r=Q(await H(),t);if(!r)throw new Error(`track "${t}" not found`);let o=await G(r.leadBox);if(!o)throw new Error(`lead box "${r.leadBox}" is not running`);let a=r.boxes.find(d=>d.name===r.leadBox)?.agent||o.agent||"claude",i=nd(a,n),l=[...r.events||[]].reverse().find(d=>d.type==="model-change")?.to??null;l||(l=(await lr(o.cwd,o.sessionId,{fallbackNewest:!1}).catch(()=>null))?.model??null),await we({cwd:o.cwd,sessionId:o.sessionId,sessionName:r.leadBox,agent:o.agent,agentCmd:ns(o.agent)});let u=await rd(a,r.leadBox,i);return it(r,"model-change",{to:n,from:l,box:r.leadBox,agent:a,verified:!!u.verified}),await K(),{ok:!0,requested:n,leadBox:r.leadBox,...u}}async function Ep(t){for(let e of t){if(!e||!await Ua(e))continue;let n=await Fu(e).catch(()=>null);if(!n)continue;if((await P.stat(U.join(n,".git")).catch(()=>null))?.isDirectory())return n}return null}function f0(t){let e=t;return e?e.kind==="multi"?Array.isArray(e.repos)?e.repos.filter(Boolean):[]:e.base?[{name:U.basename(e.base),dir:e.dir??null,base:e.base,baseRef:e.baseRef??null,repo:e.repo??null}]:[]:[]}async function p0(t){let e=t;typeof e=="string"&&!U.isAbsolute(e)&&/^[^/\s]+\/[^/\s]+$/.test(e)&&(e=await fa(e));let n=await Ep([e]);if(!n)throw new Error(`tagged repo "${t}" is not a usable base (need a primary checkout or a synced owner/name)`);return n}async function m0(t,e,n,r){let o=`track/${Zi(e)}-${t}`,s=[];try{await P.mkdir(n,{recursive:!0});let a=new Set([".bostrat"]);for(let i of r){let c=await p0(i);if(s.some(w=>w.base===c))throw new Error(`repo tagged twice: ${i}`);let l=await Y(c).catch(()=>null),u=U.basename(c);a.has(u)&&l&&(u=l.replace("/","-"));for(let w=2;a.has(u);w++)u=`${U.basename(c)}-${w}`;a.add(u);let d=U.join(n,u),f=await Fa(c);await bo(c,d,o,f),s.push({name:u,dir:d,base:c,baseRef:f,repo:l})}return await y0(n,s,o),{dir:n,kind:"multi",branch:o,repos:s}}catch(a){for(let i of s)await Ze(i.base,i.dir).catch(()=>{});throw await P.rm(n,{recursive:!0,force:!0}).catch(()=>{}),a}}async function h0(t,e,{repo:n,repos:r,cwd:o}={}){await P.mkdir(Fi,{recursive:!0});let s=U.join(Fi,t),a=Array.isArray(r)?r.map(c=>typeof c=="string"?c.trim():"").filter(Boolean):[];if(a.length>=2)return m0(t,e,s,a);a.length===1&&!n&&(n=a[0]),typeof n=="string"&&!U.isAbsolute(n)&&/^[^/\s]+\/[^/\s]+$/.test(n)&&(n=await fa(n));let i=await Ep([n,o,Hx]);if(i){let c=`track/${Zi(e)}-${t}`;try{let l=await Fa(i);await bo(i,s,c,l),await Hi(s);let u=await Y(i).catch(()=>null);return{dir:s,base:i,branch:c,kind:"worktree",baseRef:l,repo:u}}catch{await P.rm(s,{recursive:!0,force:!0}).catch(()=>{})}}return await P.mkdir(s,{recursive:!0}),await qu(s),await Hi(s),{dir:s,base:null,branch:null,kind:"repo"}}var Cr="# bostrat workspace",$r=`${Cr}
|
|
84
86
|
|
|
85
87
|
When asked to run, preview, or demo this app so the operator can review it: do NOT
|
|
86
88
|
start an ad-hoc dev server and do NOT share a localhost URL (the operator is on
|
|
@@ -186,23 +188,36 @@ which steps to run (and which to skip). An offer buried mid-message, phrased as
|
|
|
186
188
|
statement, or omitted entirely renders no controls \u2014 so close EVERY completed
|
|
187
189
|
implementation turn this way, every time. When the operator has already asked you
|
|
188
190
|
to commit / push / open the PR, just do it; do not re-offer.
|
|
189
|
-
`;async function
|
|
190
|
-
`)),
|
|
191
|
-
|
|
191
|
+
`;async function Hi(t){try{await P.writeFile(U.join(t,"AGENTS.md"),$r,{flag:"wx"})}catch{return}await b0(t,["/AGENTS.md"])}var ji=t=>typeof t=="string"&&(t===Cr||t.startsWith(`${Cr}
|
|
192
|
+
`)),w0="# bostrat multi-repo workspace";function g0(t,e){let n=t.map(r=>`- \`${r.name}/\`${r.repo?` \u2014 github.com/${r.repo}`:""}${r.baseRef?` (forked from ${r.baseRef})`:""}`).join(`
|
|
193
|
+
`);return`${w0}
|
|
194
|
+
|
|
195
|
+
This directory is NOT a git repo. It is a bostrat Track workspace holding one
|
|
196
|
+
git worktree per tagged repo, ALL on the same branch \`${e}\`:
|
|
197
|
+
|
|
198
|
+
${n}
|
|
199
|
+
|
|
200
|
+
cd into the repo a change belongs to before running git commands; commit (and
|
|
201
|
+
push / open PRs) in each repo separately, always on that shared branch. Work
|
|
202
|
+
only needs to land in the repos it actually touches \u2014 an untouched repo's
|
|
203
|
+
branch simply stays at its fork point and never gets pushed. When asked to run
|
|
204
|
+
or preview an app, run it from that app's own repo directory.
|
|
205
|
+
`}async function y0(t,e,n){let r=g0(e,n);for(let o of["CLAUDE.md","AGENTS.md"])await P.writeFile(U.join(t,o),r).catch(()=>{})}var Li=$r.replace(`${Cr}
|
|
206
|
+
`,`${Cr}
|
|
192
207
|
|
|
193
208
|
(Global copy managed by bostrat \u2014 rewritten on service start; edits will be lost.)
|
|
194
209
|
SCOPE: these instructions apply ONLY when your working directory is under
|
|
195
210
|
~/.bostrat/workspaces/ or ~/.bostrat/worktrees/ \u2014 a bostrat-orchestrated box.
|
|
196
211
|
When working anywhere else, ignore everything below.
|
|
197
|
-
`);async function
|
|
198
|
-
`).includes(s));o.length&&await
|
|
212
|
+
`);async function Ip(t){let{stdout:e}=await Ux("git",["-C",t,"rev-parse","--git-path","info/exclude"]);return U.resolve(t,e.trim())}async function b0(t,e){try{let n=await Ip(t);await P.mkdir(U.dirname(n),{recursive:!0});let r=await P.readFile(n,"utf8").catch(()=>""),o=e.filter(s=>!r.split(`
|
|
213
|
+
`).includes(s));o.length&&await P.appendFile(n,(r.endsWith(`
|
|
199
214
|
`)||!r?"":`
|
|
200
215
|
`)+o.join(`
|
|
201
216
|
`)+`
|
|
202
|
-
`)}catch{}}async function
|
|
217
|
+
`)}catch{}}async function k0(t,e){try{let n=await Ip(t),r=await P.readFile(n,"utf8"),o=r.split(`
|
|
203
218
|
`).filter(s=>s!==e);o.length!==r.split(`
|
|
204
|
-
`).length&&await
|
|
205
|
-
`))}catch{}}async function kp(){let t={refreshed:0,removed:0,codexGlobal:null};try{await D.mkdir(Ie,{recursive:!0});for(let r of["CLAUDE.md","AGENTS.md"])await D.writeFile(G.join(Ie,r),Er).catch(()=>{});let e=process.env.CODEX_HOME||G.join(process.env.HOME||"/tmp",".codex");if((await D.stat(e).catch(()=>null))?.isDirectory()){let r=G.join(e,"AGENTS.md"),o=await D.readFile(r,"utf8").catch(()=>null);o===null?(await D.writeFile(r,Ci,{flag:"wx"}).catch(()=>{}),t.codexGlobal="created"):Oi(o)&&o!==Ci&&(await D.writeFile(r,Ci).catch(()=>{}),t.codexGlobal="refreshed")}for(let r of[Ni,qn]){let o=await D.readdir(r,{withFileTypes:!0}).catch(()=>[]);for(let s of o){if(!s.isDirectory())continue;let a=G.join(r,s.name),i=G.join(a,"AGENTS.md"),c=await D.readFile(i,"utf8").catch(()=>null);c!==null&&Oi(c)&&c!==Er&&(await D.writeFile(i,Er).catch(()=>{}),t.refreshed++);let l=G.join(a,"CLAUDE.md"),u=await D.readFile(l,"utf8").catch(()=>null);u!==null&&Oi(u)&&(await D.rm(l,{force:!0}).catch(()=>{}),await o0(a,"/CLAUDE.md"),t.removed++)}}}catch{}return t}async function s0(t){let e=t.workspace;if(!(!e||!e.dir))try{e.kind==="worktree"&&e.base?await hr(e.base,e.dir):await D.rm(e.dir,{recursive:!0,force:!0})}catch{}}async function Fi({name:t,cwd:e,repo:n,agent:r="claude",skipPermissions:o=!0}){if(typeof t!="string"||!t.trim())throw new Error("name (string) required");let s=await Qo(r,o),a=await F(),i=Xo().slice(0,8),c=await n0(i,t,{repo:n,cwd:e}),l=new Set(a.tracks.flatMap(w=>(w.boxes||[]).map(h=>h.name))),u=`${t.trim()} lead`;l.has(Ds(u))&&(u=`${t.trim().slice(0,50).trim()} ${i} lead`);let d=await Ue(u,c.dir,{agentCmd:s,agent:r});ao({cwd:d.cwd,sessionName:d.name,agent:r,agentCmd:s});let f={id:i,name:t.trim(),cwd:d.cwd,workspace:c,leadBox:d.name,boxes:[Ep(d,"lead","live")],events:[],createdAt:Date.now()};return it(f,"opened",{agent:r,box:d.name,skipPermissions:o,name:f.name,cwd:f.cwd,workspace:{base:c.base,branch:c.branch,kind:c.kind,...c.baseRef?{baseRef:c.baseRef}:{},...c.repo?{repo:c.repo}:{}}}),a.tracks.push(f),await K(),Re(f)}var a0=new Set(["claude","codex","cursor"]);async function xp({provider:t="claude",fresh:e=!1}={}){if(!a0.has(t))throw new Error(`unknown setup provider "${t}" (claude | codex | cursor)`);let r=(await F()).tracks.find(u=>u.kind==="setup"&&u.setup?.provider===t&&!u.archivedAt);if(r){if(!e){let u=await Re(r);if(u.boxes.find(f=>f.name===u.leadBox)?.state==="live")return u}await ns(r.id).catch(()=>{})}let o=(await Qo(t,!1)).replace(/\s+--(?:dangerously-skip-permissions|dangerously-bypass-approvals-and-sandbox)\b/g,""),s=Xo().slice(0,8),a=G.join(Ie,"setup",s);await D.mkdir(a,{recursive:!0});let i=await Ue(`Connect ${t} ${s}`,a,{agentCmd:o,agent:t,firstRun:!0});t==="codex"&&ao({cwd:i.cwd,sessionName:i.name,agent:t,agentCmd:o});let c={id:s,name:`Connect ${t}`,kind:"setup",setup:{provider:t,startedAt:Date.now(),connectedAt:null},cwd:i.cwd,workspace:{dir:a,base:null,branch:null,kind:"scratch"},leadBox:i.name,boxes:[Ep(i,"lead","live")],events:[],createdAt:Date.now()};return it(c,"opened",{agent:t,box:i.name,skipPermissions:!1,trackKind:"setup",name:c.name,cwd:c.cwd,workspace:{base:null,branch:null,kind:"scratch"}}),(await F()).tracks.push(c),await K(),Re(c)}async function Sp(t,e,n){await rf(t).catch(()=>{});let r=$r.get(t);r&&!r.finishedAt&&(r.error=n,r.finishedAt=Date.now()),$r.delete(t),e.handoff&&!e.handoff.finishedAt&&(e.handoff.step="error",e.handoff.error=n,e.handoff.finishedAt=Date.now());for(let a of Gn(t))a.finishedAt||(a.canceled=!0,a.finishedAt=Date.now()),await Ki(a).catch(()=>{}),Zt.delete(a.id);for(let a of Object.values(e.subtasks||{}))a.finishedAt||(a.step="error",a.error=n,a.canceled=!0,a.finishedAt=Date.now());let o=await F(),s=new Set((o.tracks||[]).filter(a=>a.id!==t&&!a.archivedAt).flatMap(a=>(a.boxes||[]).map(i=>i.name)));for(let a of e.boxes)s.has(a.name)||await Wt(a.name).catch(()=>{})}async function vp(t,{cascadeMocks:e=!0,cascadeRunbooks:n=!1}={}){let r=await F(),o=r.tracks.findIndex(a=>a.id===t);if(o===-1)throw new Error(`track "${t}" not found`);let s=r.tracks[o];return e||await Pf(s),await Sp(t,s,"track deleted"),n?await Jf(t).catch(()=>{}):await Vf(t).catch(()=>{}),await td(),await s0(s),e&&await Bf(t).catch(()=>{}),it(s,"track-deleted",{name:s.name,boxes:s.boxes.length,ageMs:s.createdAt?Date.now()-s.createdAt:null}),r.tracks.splice(o,1),await K(),{ok:!0,id:t,name:s.name,boxes:s.boxes.length}}async function ns(t){let e=await F(),n=Y(e,t);if(!n)throw new Error(`track "${t}" not found`);if(n.archivedAt)throw new Error(`track "${t}" is already archived`);await Sp(t,n,"track archived");for(let r of n.boxes)en.has(r.state)||(r.state="stopped");return n.archivedAt=Date.now(),it(n,"archived",{boxes:n.boxes.length}),await K(),Re(n)}async function Hi(t){if(t.archivedAt)return!1;let e=t.boxes.find(r=>r.name===t.leadBox);if(!e||en.has(e.state)||await q(e.name))return!1;let n=ip(e.agent,e.sessionId);if(!await Bs(n))return it(t,"rehydrate-failed",{box:e.name,agent:e.agent,reason:`agent "${e.agent}" not available on this box`}),!1;try{return await Ue(e.name,e.cwd,{agentCmd:n,agent:e.agent,resumeSessionId:e.sessionId,pinned:e.pinned===!0}),ao({cwd:e.cwd,sessionName:e.name,agent:e.agent,agentCmd:n}),e.state="live",it(t,"rehydrated",{box:e.name,agent:e.agent}),!0}catch(r){return it(t,"rehydrate-failed",{box:e.name,agent:e.agent,reason:String(r?.message||r)}),!1}}async function Tp(t){let e=await F(),n=Y(e,t);if(!n)throw new Error(`track "${t}" not found`);if(!n.archivedAt)throw new Error(`track "${t}" is not archived`);return delete n.archivedAt,it(n,"unarchived",{}),await Hi(n),await K(),Re(n)}async function Ap(){let t=await F(),e=new Set((await yn()).map(s=>s.name)),n=new Set((t.tracks||[]).filter(s=>!s.archivedAt).flatMap(s=>(s.boxes||[]).map(a=>a.name))),r=0,o=!1;for(let s of t.tracks||[])if(s.archivedAt)for(let a of s.boxes)e.has(a.name)&&!n.has(a.name)&&(await Wt(a.name).catch(()=>{}),it(s,"orphan-reaped",{box:a.name,agent:a.agent}),r++,o=!0),!en.has(a.state)&&a.state!=="stopped"&&(a.state="stopped",o=!0);return o&&await K(),r}async function _p(t){let e=await F(),n=Y(e,t);if(!n)throw new Error(`track "${t}" not found`);if(n.archivedAt)throw new Error(`track "${t}" is archived \u2014 restore it before restarting the lead`);let r=n.boxes.find(a=>a.name===n.leadBox);if(!r)throw new Error(`track "${t}" has no lead box`);if(en.has(r.state))throw new Error("lead box is terminal \u2014 nothing to restart");if(await q(n.leadBox)){let a=await pt(n.leadBox,{lines:It}).catch(()=>"");if(oo(r.agent,a))throw new Error("lead is mid-turn \u2014 retry when it goes idle");await Wt(n.leadBox),r.state="stopped"}let s=await Hi(n);if(await K(),!s)throw new Error("rehydrate failed \u2014 see the track's rehydrate-failed event");return Re(n)}var Ep=(t,e,n)=>({name:t.name,agent:t.agent,role:e,state:n,cwd:t.cwd,sessionId:t.sessionId,pinned:t.pinned===!0,createdAt:Date.now()});function Ip(t){return(!Array.isArray(t.epochs)||t.epochs.length===0)&&(t.epochs=[{agent:t.agent??null,sessionId:t.sessionId??null,pinned:t.pinned===!0,startedAt:t.createdAt??null,endedAt:null}]),t.epochs}var $r=new Map,Mr=t=>$r.get(t)||(Yt?Y(Yt,t)?.handoff:null)||null;async function zo(t,e){let n=await F(),r=Y(n,t);r&&(r.handoff={...r.handoff||{},...e},await K())}function i0(t,e=Date.now()){let n=t?.handoff;if(!n||n.finishedAt)return null;let r=n.step,o=!!n.toBox&&n.toBox===n.fromBox,s=t.boxes?.find?.(i=>i.name===t.leadBox)||null;return(o?t.leadBox===n.fromBox&&!!n.startedAt&&Array.isArray(s?.epochs)&&s.epochs.some(i=>i.startedAt!=null&&i.startedAt>=n.startedAt):!!n.toBox&&t.leadBox===n.toBox)?{...n,step:"done",finishedAt:e,recovered:`committed before restart (was "${r}")`}:{...n,step:"error",finishedAt:e,error:`interrupted by an agent restart during "${r}"; lead "${t.leadBox}" retained \u2014 retry the handoff`,recovered:"rolled-back"}}async function Rp(){let t=await F(),e=!1;for(let n of t.tracks||[]){let r=n.handoff,o=i0(n);if(!o)continue;e=!0;let s=!!r.toBox&&r.toBox===r.fromBox;if(s&&o.step==="error"){let c=await q(r.fromBox).catch(()=>null),l=n.boxes.find(u=>u.name===r.fromBox);if(c&&l&&c.agent===r.toAgent&&r.toAgent!==l.agent){let u=Date.now(),d=Ip(l),f=d[d.length-1],w=f.agent,h=f.sessionId??null;f.endedAt=u,r.reason&&(f.endReason=r.reason);let g=c.agent==="claude"&&!!c.sessionId;d.push({agent:r.toAgent,sessionId:c.sessionId||null,pinned:g,startedAt:u,endedAt:null}),l.agent=r.toAgent,l.sessionId=c.sessionId||null,l.pinned=g,l.state="live",n.pendingHandoffHydrate={artifact:ts,at:u},it(n,"lead-handoff",{from:r.fromBox,to:r.fromBox,fromAgent:w,toAgent:r.toAgent,fromSessionId:h,toSessionId:c.sessionId||null,epoch:d.length-1,singleBox:!0,skipPermissions:r.skipPermissions,reason:r.reason??null,handoffStartedAt:r.startedAt,recovered:!0}),n.handoff={...r,step:"done",finishedAt:u,recovered:"swap adopted from live tmux tags after restart"},await j({flow:"lead-handoff",phase:"reconciled",trackId:n.id,outcome:"done",recovered:n.handoff.recovered,fromBox:r.fromBox,toBox:r.toBox}).catch(()=>{});continue}}let a=!s&&o.recovered==="rolled-back"&&r.toBox&&r.toBox!==n.leadBox?r.toBox:null,i=!s&&o.step==="done"?r.fromBox:null;n.handoff=o,await j({flow:"lead-handoff",phase:"reconciled",trackId:n.id,outcome:o.step,recovered:o.recovered,fromBox:o.fromBox,toBox:o.toBox}).catch(()=>{}),i&&await Wt(i).catch(()=>{}),a&&await Wt(a).catch(()=>{})}return e&&await K(),e}async function Wi(t,e,n=!0,r=null){let o=$r.get(t);if(o&&!o.finishedAt)throw new Error("a lead handoff is already running for this Track");let s=Y(await F(),t);if(!s)throw new Error(`track "${t}" not found`);let a=await Qo(e,n),i=await q(s.leadBox);if(!i)throw new Error(`lead box "${s.leadBox}" is not running`);let c={trackId:t,step:"checkpoint",fromBox:s.leadBox,toAgent:e,skipPermissions:n,reason:cp(r),toBox:s.leadBox,startedAt:Date.now(),finishedAt:null,error:null};return $r.set(t,c),await zo(t,{step:c.step,fromBox:c.fromBox,toAgent:e,toBox:c.toBox,skipPermissions:n,startedAt:c.startedAt,finishedAt:null,error:null}),c0(c,s,i,a,e).catch(async l=>{c.error=l?.message||String(l),c.finishedAt=Date.now(),console.error(`[lead-handoff ${t}] FAILED at step "${c.step}": ${c.error}`),await zo(t,{step:c.step,error:c.error,finishedAt:c.finishedAt}).catch(()=>{}),j({flow:"lead-handoff",phase:"failed",trackId:t,step:c.step,error:c.error,fromBox:c.fromBox,toAgent:c.toAgent,toBox:c.toBox}).catch(()=>{})}),j({flow:"lead-handoff",phase:"started",trackId:t,fromBox:c.fromBox,toAgent:e,cwd:i.cwd}).catch(()=>{}),c}async function c0(t,e,n,r,o){let s=async(v,E={})=>{t.step=v,console.log(`[lead-handoff ${t.trackId}] step \u2192 ${v}`),await zo(t.trackId,{step:v,toBox:t.toBox}),await j({flow:"lead-handoff",phase:v,trackId:t.trackId,fromBox:t.fromBox,toBox:t.toBox,toAgent:o,sessionName:E.sessionName,...E})};console.log(`[lead-handoff ${t.trackId}] ${t.fromBox}: ${n.agent} \u2192 ${o} IN PLACE`);let a=await F(),i=Y(a,t.trackId)?.boxes.find(v=>v.name===t.fromBox)||null,c=i?.agent||n.agent,l=i?.sessionId??n.sessionId??null,u=i?.pinned===!0;await s("checkpoint",{sessionName:t.fromBox}),await Il(t.fromBox),await s("swap",{cwd:n.cwd,agentCmd:r});let d=await ea(t.fromBox,{agentCmd:r,agent:o});await s("hydrate",{sessionName:t.fromBox}),await we({cwd:n.cwd,sessionId:d.sessionId,sessionName:t.fromBox,agent:o,agentCmd:Zo(o)});let f=await pt(t.fromBox,{lines:It}).catch(()=>"");if(!so(o,f)){await s("rollback",{sessionName:t.fromBox,fromAgent:c});let v=ip(c,l,{skipPermissions:t.skipPermissions});await ea(t.fromBox,{agentCmd:v,agent:c,resumeSessionId:l,pinned:u});let E=await F(),J=Y(E,t.trackId);if(J){let I=J.boxes.find(M=>M.name===t.fromBox);I&&(I.state="live"),it(J,"lead-handoff-failed",{box:t.fromBox,fromAgent:c,toAgent:o,reason:t.reason,error:`incoming ${o} CLI never drew its composer`,rolledBack:!0,handoffStartedAt:t.startedAt}),await K()}throw new Error(`incoming ${o} CLI never drew its composer \u2014 rolled back to ${c} (--resume, context intact)`)}await s("commit");let w=await F(),h=Y(w,t.trackId);if(!h)throw new Error(`track "${t.trackId}" not found during handoff commit \u2014 was it deleted or archived mid-flight?`);let g=h.boxes.find(v=>v.name===t.fromBox);if(!g)throw new Error(`lead box row "${t.fromBox}" missing during handoff commit`);let x=Date.now(),A=Ip(g),S=A[A.length-1];S.endedAt=x,t.reason&&(S.endReason=t.reason),A.push({agent:o,sessionId:d.sessionId,pinned:d.pinned,startedAt:x,endedAt:null}),g.agent=o,g.sessionId=d.sessionId,g.pinned=d.pinned,g.state="live",g.role="lead",h.pendingHandoffHydrate={artifact:ts,at:x},it(h,"lead-handoff",{from:t.fromBox,to:t.fromBox,fromAgent:c,toAgent:o,fromSessionId:l,toSessionId:d.sessionId,epoch:A.length-1,singleBox:!0,forced:d.forced||void 0,skipPermissions:t.skipPermissions,reason:t.reason,handoffStartedAt:t.startedAt}),await K(),await s("done",{sessionName:t.fromBox}),await zo(t.trackId,{finishedAt:Date.now(),error:null}),t.finishedAt=Date.now()}var l0=Number(process.env.BOSTRAT_SUBTASK_TIMEOUT_MS)||18e5,u0=Number(process.env.BOSTRAT_SUBTASK_POLL_MS)||3e3,qn=G.join(Ie,"worktrees"),Mi=[Jo,Di],Zt=new Map;var Gn=t=>{let e=new Map([...Zt.values()].filter(r=>r.trackId===t).map(r=>[r.id,r])),n=Yt?Y(Yt,t):null;if(n?.subtasks)for(let[r,o]of Object.entries(n.subtasks))e.has(r)||e.set(r,o);return[...e.values()].sort((r,o)=>r.startedAt-o.startedAt)},qi=t=>({id:t.id,trackId:t.trackId,trackName:t.trackName,agent:t.agent,task:t.task,skipPermissions:t.skipPermissions,reason:t.reason||null,step:t.step,failedStep:t.failedStep||null,box:t.box,branch:t.branch,base:t.base,dir:t.dir,stat:t.stat,files:t.files||null,conflicts:t.conflicts||[],applied:t.applied,error:t.error,canceled:t.canceled,startedAt:t.startedAt,finishedAt:t.finishedAt});async function d0(t,e,n){let r=await F(),o=Y(r,t);o&&(o.subtasks=o.subtasks||{},o.subtasks[e]={...o.subtasks[e]||{},...n},await K())}var Or=t=>d0(t.trackId,t.id,qi(t));async function Wn(t,e){t.step=e,await Or(t)}function f0(t,e=Date.now()){if(!t||t.finishedAt)return null;let n=t.step;return["snapshot","spawn","working"].includes(n)?{...t,step:"error",finishedAt:e,error:`interrupted by an agent restart during "${n}"; no changes were applied to the lead \u2014 retry the delegation`,recovered:"rolled-back"}:{...t,step:"error",finishedAt:e,error:`interrupted by an agent restart during "${n}"; the lead's working tree may hold a PARTIAL merge \u2014 review git status. The subtask's work is preserved on branch ${t.branch}`,recovered:"partial-merge"}}function p0(t){return!!t&&!t.finishedAt&&!t.canceled&&t.step==="working"&&!!t.box&&!!t.dir&&!!t.base&&!!t.branch}async function m0(t,e,n){if(!p0(n)||Zt.has(e)||!await q(n.box).catch(()=>null))return!1;try{await D.access(n.dir)}catch{return!1}let o=await q(t.leadBox).catch(()=>null);if(!o?.cwd)return!1;let s={...n,id:e,leadBox:t.leadBox,step:"working",canceled:!1,forceMerge:!1};return Zt.set(e,s),Pp(s,o).catch(a=>Mp(s,a)),!0}async function $p(){let t=await F(),e=!1;for(let n of t.tracks||[]){for(let[r,o]of Object.entries(n.subtasks||{})){if(Zt.has(r))continue;if(await m0(n,r,o)){e=!0,it(n,"subtask-resumed",{box:o.box,agent:o.agent,task:o.task,subtaskId:r,resumedStep:o.step}),await j({flow:"subtask",phase:"resumed",trackId:n.id,subtaskId:r,box:o.box,branch:o.branch}).catch(()=>{});continue}let s=o.step,a=f0(o);if(!a)continue;e=!0,n.subtasks[r]=a;let i=n.boxes.find(c=>c.subtaskId===r);i&&!en.has(i.state)&&(i.state="canceled"),it(n,"subtask-failed",{from:o.box,fromAgent:o.agent,task:o.task,subtaskId:r,failedStep:s,error:a.error,recovered:a.recovered,durationMs:o.startedAt?a.finishedAt-o.startedAt:null}),await j({flow:"subtask",phase:"reconciled",trackId:n.id,subtaskId:r,outcome:a.step,recovered:a.recovered,box:o.box,branch:o.branch}).catch(()=>{}),await sp(n,o.box,o.dir)}for(let r of n.boxes){if(r.role!=="subtask"||en.has(r.state)||Zt.has(r.subtaskId)||n.subtasks?.[r.subtaskId])continue;e=!0,r.state="canceled";let o=r.subtaskId?G.join(qn,`${n.id}-${r.subtaskId}`):null;await j({flow:"subtask",phase:"reaped-legacy-orphan",trackId:n.id,subtaskId:r.subtaskId,box:r.name}).catch(()=>{}),await sp(n,r.name,o)}}return e=await h0(t)||e,e&&await K(),e}async function sp(t,e,n){if(e&&await Wt(e).catch(()=>{}),n){let r=await q(t.leadBox).catch(()=>null);r?.cwd&&await hr(r.cwd,n).catch(()=>{})}}async function h0(t){let e;try{e=await D.readdir(qn)}catch{return!1}let n=new Set;for(let o of t.tracks||[]){for(let s of Object.keys(o.subtasks||{}))n.add(`${o.id}-${s}`);for(let s of o.boxes||[])s.subtaskId&&n.add(`${o.id}-${s.subtaskId}`)}for(let o of Zt.values())o.dir&&n.add(G.basename(o.dir));let r=!1;for(let o of e){if(n.has(o))continue;let s=G.join(qn,o),a=(t.tracks||[]).find(c=>o.startsWith(`${c.id}-`)),i=a?await q(a.leadBox).catch(()=>null):null;i?.cwd?await hr(i.cwd,s).catch(()=>{}):await D.rm(s,{recursive:!0,force:!0}).catch(()=>{}),await j({flow:"subtask",phase:"swept-orphan-worktree",dir:s}).catch(()=>{}),r=!0}return r}var Op=t=>t.trim().replace(/\s+/g,"-").replace(/[^A-Za-z0-9._-]/g,"").toLowerCase()||"track",Cr=class extends Error{};async function Cp(t,{agent:e,task:n,skipPermissions:r=!0,reason:o=null}={}){if(typeof n!="string"||!On(n))throw new Error("task (string) required");let s=await Qo(e,r),a=Y(await F(),t);if(!a)throw new Error(`track "${t}" not found`);let i=await q(a.leadBox);if(!i)throw new Error(`lead box "${a.leadBox}" is not running`);if(!i.cwd)throw new Error("can't resolve the lead's working directory");if(!await Pa(i.cwd))throw new Error(`subtask delegation needs the lead's workspace to be a git repo (got ${i.cwd})`);let c=Xo().slice(0,8),l={id:c,trackId:t,trackName:a.name,agent:e,task:On(n),skipPermissions:r,reason:cp(o),step:"snapshot",leadBox:a.leadBox,box:null,branch:`bostrat/${Op(a.name)}-${c}`,base:null,dir:G.join(qn,`${t}-${c}`),stat:null,conflicts:[],applied:null,startedAt:Date.now(),finishedAt:null,error:null,canceled:!1,forceMerge:!1};return Zt.set(c,l),await Or(l),w0(l,i,s).catch(u=>Mp(l,u)),l}async function Mp(t,e){t.failedStep=t.step,t.error=e instanceof Cr?"canceled":e?.message||String(e),t.step=e instanceof Cr?"canceled":"error",t.finishedAt=Date.now(),await Ki(t).catch(()=>{}),await Or(t).catch(()=>{}),await k0(t).catch(()=>{})}function Gi(t){let e=Zt.get(t);if(!e)throw new Error(`subtask "${t}" not found`);if(e.finishedAt)throw new Error("subtask already finished");return e.canceled=!0,e}function Np(t){let e=Zt.get(t);if(!e)throw new Error(`subtask "${t}" not found`);if(e.finishedAt)throw new Error("subtask already finished");if(e.step!=="working")throw new Error(`subtask is not waiting to merge (step: ${e.step})`);return e.forceMerge=!0,e}async function w0(t,e,n){await Wn(t,"snapshot"),t.base=await Nu(e.cwd,`bostrat: snapshot for subtask ${t.id}`),await Wn(t,"spawn"),await D.mkdir(qn,{recursive:!0}),await Ba(e.cwd,t.dir,t.branch,t.base),await Pi(t.dir),await g0(t,e);let r=t.trackName.slice(0,38).trim(),o=await Ue(`${r} ${t.agent} sub ${t.id}`,t.dir,{agentCmd:n,agent:t.agent});t.box=o.name,await y0(t,o),await lp(t.trackId,o.name,Px(t.task),{kind:"subtask"}),await Pp(t,e)}async function Pp(t,e){await Wn(t,"working");let n=G.join(t.dir,Di);if(await S0(n,t),t.canceled)throw new Cr;await Wn(t,"merge"),await Pu(t.dir,`bostrat: subtask ${t.id} result (auto-committed)`),t.stat=await ju(e.cwd,t.base,t.branch,Mi),t.files=await Lu(e.cwd,t.base,t.branch,Mi);let r=await Uu(e.cwd,t.base,t.branch,Mi);t.applied=r.applied,t.conflicts=r.conflicts,await Or(t);let o=await x0(t,e,n);await Wn(t,"resume"),await lp(t.trackId,t.leadBox,Bx(t.task,t.stat,t.conflicts,o),{kind:"subtask-resume"}),await Wn(t,"reap"),await Ki(t),t.step="done",t.finishedAt=Date.now(),await b0(t),await Or(t)}async function g0(t,e){let n=G.join(t.dir,G.dirname(Jo));await D.mkdir(n,{recursive:!0});let r="";try{r=await cl(e.cwd,e.sessionId)}catch{}if(!r)try{r=await D.readFile(G.join(e.cwd,".bostrat/handoff/latest.md"),"utf8")}catch{r="_(no prior conversation digest available \u2014 rely on the working tree.)_"}let o=`# Lead context for subtask ${t.id}
|
|
219
|
+
`).length&&await P.writeFile(n,o.join(`
|
|
220
|
+
`))}catch{}}async function Rp(){let t={refreshed:0,removed:0,codexGlobal:null};try{await P.mkdir(Ie,{recursive:!0});for(let r of["CLAUDE.md","AGENTS.md"])await P.writeFile(U.join(Ie,r),$r).catch(()=>{});let e=process.env.CODEX_HOME||U.join(process.env.HOME||"/tmp",".codex");if((await P.stat(e).catch(()=>null))?.isDirectory()){let r=U.join(e,"AGENTS.md"),o=await P.readFile(r,"utf8").catch(()=>null);o===null?(await P.writeFile(r,Li,{flag:"wx"}).catch(()=>{}),t.codexGlobal="created"):ji(o)&&o!==Li&&(await P.writeFile(r,Li).catch(()=>{}),t.codexGlobal="refreshed")}for(let r of[Fi,Kn]){let o=await P.readdir(r,{withFileTypes:!0}).catch(()=>[]);for(let s of o){if(!s.isDirectory())continue;let a=U.join(r,s.name),i=U.join(a,"AGENTS.md"),c=await P.readFile(i,"utf8").catch(()=>null);c!==null&&ji(c)&&c!==$r&&(await P.writeFile(i,$r).catch(()=>{}),t.refreshed++);let l=U.join(a,"CLAUDE.md"),u=await P.readFile(l,"utf8").catch(()=>null);u!==null&&ji(u)&&(await P.rm(l,{force:!0}).catch(()=>{}),await k0(a,"/CLAUDE.md"),t.removed++)}}}catch{}return t}async function x0(t){let e=t.workspace;if(!(!e||!e.dir))try{if(e.kind==="multi"){for(let n of f0(e))n.base&&n.dir&&await Ze(n.base,n.dir).catch(()=>{});await P.rm(e.dir,{recursive:!0,force:!0})}else e.kind==="worktree"&&e.base?await Ze(e.base,e.dir):await P.rm(e.dir,{recursive:!0,force:!0})}catch{}}async function Ji({name:t,cwd:e,repo:n,repos:r,agent:o="claude",skipPermissions:s=!0}){if(typeof t!="string"||!t.trim())throw new Error("name (string) required");let a=await rs(o,s),i=await H(),c=ts().slice(0,8),l=await h0(c,t,{repo:n,repos:r,cwd:e}),u=new Set(i.tracks.flatMap(m=>(m.boxes||[]).map(g=>g.name))),d=`${t.trim()} lead`;u.has(Fs(d))&&(d=`${t.trim().slice(0,50).trim()} ${c} lead`);let f=await Ue(d,l.dir,{agentCmd:a,agent:o});lo({cwd:f.cwd,sessionName:f.name,agent:o,agentCmd:a});let w={id:c,name:t.trim(),cwd:f.cwd,workspace:l,leadBox:f.name,boxes:[Bp(f,"lead","live")],events:[],createdAt:Date.now()};return it(w,"opened",{agent:o,box:f.name,skipPermissions:s,name:w.name,cwd:w.cwd,workspace:{base:l.base??null,branch:l.branch,kind:l.kind,...l.baseRef?{baseRef:l.baseRef}:{},...l.repo?{repo:l.repo}:{},...l.repos?{repos:l.repos.map(({name:m,base:g,baseRef:x,repo:A})=>({name:m,base:g,baseRef:x,repo:A}))}:{}}}),i.tracks.push(w),await K(),Re(w)}var S0=new Set(["claude","codex","cursor"]);async function $p({provider:t="claude",fresh:e=!1}={}){if(!S0.has(t))throw new Error(`unknown setup provider "${t}" (claude | codex | cursor)`);let r=(await H()).tracks.find(u=>u.kind==="setup"&&u.setup?.provider===t&&!u.archivedAt);if(r){if(!e){let u=await Re(r);if(u.boxes.find(f=>f.name===u.leadBox)?.state==="live")return u}await Vn(r.id).catch(()=>{})}let o=(await rs(t,!1)).replace(/\s+--(?:dangerously-skip-permissions|dangerously-bypass-approvals-and-sandbox)\b/g,""),s=ts().slice(0,8),a=U.join(Ie,"setup",s);await P.mkdir(a,{recursive:!0});let i=await Ue(`Connect ${t} ${s}`,a,{agentCmd:o,agent:t,firstRun:!0});t==="codex"&&lo({cwd:i.cwd,sessionName:i.name,agent:t,agentCmd:o});let c={id:s,name:`Connect ${t}`,kind:"setup",setup:{provider:t,startedAt:Date.now(),connectedAt:null},cwd:i.cwd,workspace:{dir:a,base:null,branch:null,kind:"scratch"},leadBox:i.name,boxes:[Bp(i,"lead","live")],events:[],createdAt:Date.now()};return it(c,"opened",{agent:t,box:i.name,skipPermissions:!1,trackKind:"setup",name:c.name,cwd:c.cwd,workspace:{base:null,branch:null,kind:"scratch"}}),(await H()).tracks.push(c),await K(),Re(c)}async function Op(t,e,n){await ff(t).catch(()=>{});let r=Mr.get(t);r&&!r.finishedAt&&(r.error=n,r.finishedAt=Date.now()),Mr.delete(t),e.handoff&&!e.handoff.finishedAt&&(e.handoff.step="error",e.handoff.error=n,e.handoff.finishedAt=Date.now());for(let a of Jn(t))a.finishedAt||(a.canceled=!0,a.finishedAt=Date.now()),await tc(a).catch(()=>{}),Zt.delete(a.id);for(let a of Object.values(e.subtasks||{}))a.finishedAt||(a.step="error",a.error=n,a.canceled=!0,a.finishedAt=Date.now());let o=await H(),s=new Set((o.tracks||[]).filter(a=>a.id!==t&&!a.archivedAt).flatMap(a=>(a.boxes||[]).map(i=>i.name)));for(let a of e.boxes)s.has(a.name)||await Wt(a.name).catch(()=>{})}async function Cp(t,{cascadeMocks:e=!0,cascadeRunbooks:n=!1}={}){let r=await H(),o=r.tracks.findIndex(a=>a.id===t);if(o===-1)throw new Error(`track "${t}" not found`);let s=r.tracks[o];return e||await Wf(s),await Op(t,s,"track deleted"),n?await np(t).catch(()=>{}):await ep(t).catch(()=>{}),await id(),await x0(s),e&&await qf(t).catch(()=>{}),it(s,"track-deleted",{name:s.name,boxes:s.boxes.length,ageMs:s.createdAt?Date.now()-s.createdAt:null}),r.tracks.splice(o,1),await K(),{ok:!0,id:t,name:s.name,boxes:s.boxes.length}}async function Vn(t){let e=await H(),n=Q(e,t);if(!n)throw new Error(`track "${t}" not found`);if(n.archivedAt)throw new Error(`track "${t}" is already archived`);await Op(t,n,"track archived");for(let r of n.boxes)an.has(r.state)||(r.state="stopped");return n.archivedAt=Date.now(),it(n,"archived",{boxes:n.boxes.length}),await K(),Re(n)}async function tn(t,e){let n=await Tt(t,e).catch(()=>null);if(!n?.archiveOnMerge||!n.trackId)return!1;try{return await Vn(n.trackId),!0}catch(r){if(/already archived/i.test(String(r?.message||r)))return!1;throw r}}async function zi(t){if(t.archivedAt)return!1;let e=t.boxes.find(r=>r.name===t.leadBox);if(!e||an.has(e.state)||await G(e.name))return!1;let n=hp(e.agent,e.sessionId);if(!await Us(n))return it(t,"rehydrate-failed",{box:e.name,agent:e.agent,reason:`agent "${e.agent}" not available on this box`}),!1;try{return await Ue(e.name,e.cwd,{agentCmd:n,agent:e.agent,resumeSessionId:e.sessionId,pinned:e.pinned===!0}),lo({cwd:e.cwd,sessionName:e.name,agent:e.agent,agentCmd:n}),e.state="live",it(t,"rehydrated",{box:e.name,agent:e.agent}),!0}catch(r){return it(t,"rehydrate-failed",{box:e.name,agent:e.agent,reason:String(r?.message||r)}),!1}}async function Mp(t){let e=await H(),n=Q(e,t);if(!n)throw new Error(`track "${t}" not found`);if(!n.archivedAt)throw new Error(`track "${t}" is not archived`);return delete n.archivedAt,it(n,"unarchived",{}),await zi(n),await K(),Re(n)}async function Pp(){let t=await H(),e=new Set((await vn()).map(s=>s.name)),n=new Set((t.tracks||[]).filter(s=>!s.archivedAt).flatMap(s=>(s.boxes||[]).map(a=>a.name))),r=0,o=!1;for(let s of t.tracks||[])if(s.archivedAt)for(let a of s.boxes)e.has(a.name)&&!n.has(a.name)&&(await Wt(a.name).catch(()=>{}),it(s,"orphan-reaped",{box:a.name,agent:a.agent}),r++,o=!0),!an.has(a.state)&&a.state!=="stopped"&&(a.state="stopped",o=!0);return o&&await K(),r}async function Np(t){let e=await H(),n=Q(e,t);if(!n)throw new Error(`track "${t}" not found`);if(n.archivedAt)throw new Error(`track "${t}" is archived \u2014 restore it before restarting the lead`);let r=n.boxes.find(a=>a.name===n.leadBox);if(!r)throw new Error(`track "${t}" has no lead box`);if(an.has(r.state))throw new Error("lead box is terminal \u2014 nothing to restart");if(await G(n.leadBox)){let a=await pt(n.leadBox,{lines:$t}).catch(()=>"");if(io(r.agent,a))throw new Error("lead is mid-turn \u2014 retry when it goes idle");await Wt(n.leadBox),r.state="stopped"}let s=await zi(n);if(await K(),!s)throw new Error("rehydrate failed \u2014 see the track's rehydrate-failed event");return Re(n)}var Bp=(t,e,n)=>({name:t.name,agent:t.agent,role:e,state:n,cwd:t.cwd,sessionId:t.sessionId,pinned:t.pinned===!0,createdAt:Date.now()});function Dp(t){return(!Array.isArray(t.epochs)||t.epochs.length===0)&&(t.epochs=[{agent:t.agent??null,sessionId:t.sessionId??null,pinned:t.pinned===!0,startedAt:t.createdAt??null,endedAt:null}]),t.epochs}var Mr=new Map,Br=t=>Mr.get(t)||(Yt?Q(Yt,t)?.handoff:null)||null;async function Qo(t,e){let n=await H(),r=Q(n,t);r&&(r.handoff={...r.handoff||{},...e},await K())}function v0(t,e=Date.now()){let n=t?.handoff;if(!n||n.finishedAt)return null;let r=n.step,o=!!n.toBox&&n.toBox===n.fromBox,s=t.boxes?.find?.(i=>i.name===t.leadBox)||null;return(o?t.leadBox===n.fromBox&&!!n.startedAt&&Array.isArray(s?.epochs)&&s.epochs.some(i=>i.startedAt!=null&&i.startedAt>=n.startedAt):!!n.toBox&&t.leadBox===n.toBox)?{...n,step:"done",finishedAt:e,recovered:`committed before restart (was "${r}")`}:{...n,step:"error",finishedAt:e,error:`interrupted by an agent restart during "${r}"; lead "${t.leadBox}" retained \u2014 retry the handoff`,recovered:"rolled-back"}}async function jp(){let t=await H(),e=!1;for(let n of t.tracks||[]){let r=n.handoff,o=v0(n);if(!o)continue;e=!0;let s=!!r.toBox&&r.toBox===r.fromBox;if(s&&o.step==="error"){let c=await G(r.fromBox).catch(()=>null),l=n.boxes.find(u=>u.name===r.fromBox);if(c&&l&&c.agent===r.toAgent&&r.toAgent!==l.agent){let u=Date.now(),d=Dp(l),f=d[d.length-1],w=f.agent,m=f.sessionId??null;f.endedAt=u,r.reason&&(f.endReason=r.reason);let g=c.agent==="claude"&&!!c.sessionId;d.push({agent:r.toAgent,sessionId:c.sessionId||null,pinned:g,startedAt:u,endedAt:null}),l.agent=r.toAgent,l.sessionId=c.sessionId||null,l.pinned=g,l.state="live",n.pendingHandoffHydrate={artifact:os,at:u},it(n,"lead-handoff",{from:r.fromBox,to:r.fromBox,fromAgent:w,toAgent:r.toAgent,fromSessionId:m,toSessionId:c.sessionId||null,epoch:d.length-1,singleBox:!0,skipPermissions:r.skipPermissions,reason:r.reason??null,handoffStartedAt:r.startedAt,recovered:!0}),n.handoff={...r,step:"done",finishedAt:u,recovered:"swap adopted from live tmux tags after restart"},await L({flow:"lead-handoff",phase:"reconciled",trackId:n.id,outcome:"done",recovered:n.handoff.recovered,fromBox:r.fromBox,toBox:r.toBox}).catch(()=>{});continue}}let a=!s&&o.recovered==="rolled-back"&&r.toBox&&r.toBox!==n.leadBox?r.toBox:null,i=!s&&o.step==="done"?r.fromBox:null;n.handoff=o,await L({flow:"lead-handoff",phase:"reconciled",trackId:n.id,outcome:o.step,recovered:o.recovered,fromBox:o.fromBox,toBox:o.toBox}).catch(()=>{}),i&&await Wt(i).catch(()=>{}),a&&await Wt(a).catch(()=>{})}return e&&await K(),e}async function Xi(t,e,n=!0,r=null){let o=Mr.get(t);if(o&&!o.finishedAt)throw new Error("a lead handoff is already running for this Track");let s=Q(await H(),t);if(!s)throw new Error(`track "${t}" not found`);let a=await rs(e,n),i=await G(s.leadBox);if(!i)throw new Error(`lead box "${s.leadBox}" is not running`);let c={trackId:t,step:"checkpoint",fromBox:s.leadBox,toAgent:e,skipPermissions:n,reason:wp(r),toBox:s.leadBox,startedAt:Date.now(),finishedAt:null,error:null};return Mr.set(t,c),await Qo(t,{step:c.step,fromBox:c.fromBox,toAgent:e,toBox:c.toBox,skipPermissions:n,startedAt:c.startedAt,finishedAt:null,error:null}),T0(c,s,i,a,e).catch(async l=>{c.error=l?.message||String(l),c.finishedAt=Date.now(),console.error(`[lead-handoff ${t}] FAILED at step "${c.step}": ${c.error}`),await Qo(t,{step:c.step,error:c.error,finishedAt:c.finishedAt}).catch(()=>{}),L({flow:"lead-handoff",phase:"failed",trackId:t,step:c.step,error:c.error,fromBox:c.fromBox,toAgent:c.toAgent,toBox:c.toBox}).catch(()=>{})}),L({flow:"lead-handoff",phase:"started",trackId:t,fromBox:c.fromBox,toAgent:e,cwd:i.cwd}).catch(()=>{}),c}async function T0(t,e,n,r,o){let s=async(T,E={})=>{t.step=T,console.log(`[lead-handoff ${t.trackId}] step \u2192 ${T}`),await Qo(t.trackId,{step:T,toBox:t.toBox}),await L({flow:"lead-handoff",phase:T,trackId:t.trackId,fromBox:t.fromBox,toBox:t.toBox,toAgent:o,sessionName:E.sessionName,...E})};console.log(`[lead-handoff ${t.trackId}] ${t.fromBox}: ${n.agent} \u2192 ${o} IN PLACE`);let a=await H(),i=Q(a,t.trackId)?.boxes.find(T=>T.name===t.fromBox)||null,c=i?.agent||n.agent,l=i?.sessionId??n.sessionId??null,u=i?.pinned===!0;await s("checkpoint",{sessionName:t.fromBox}),await Bl(t.fromBox),await s("swap",{cwd:n.cwd,agentCmd:r});let d=await sa(t.fromBox,{agentCmd:r,agent:o});await s("hydrate",{sessionName:t.fromBox}),await we({cwd:n.cwd,sessionId:d.sessionId,sessionName:t.fromBox,agent:o,agentCmd:ns(o)});let f=await pt(t.fromBox,{lines:$t}).catch(()=>"");if(!co(o,f)){await s("rollback",{sessionName:t.fromBox,fromAgent:c});let T=hp(c,l,{skipPermissions:t.skipPermissions});await sa(t.fromBox,{agentCmd:T,agent:c,resumeSessionId:l,pinned:u});let E=await H(),J=Q(E,t.trackId);if(J){let M=J.boxes.find(I=>I.name===t.fromBox);M&&(M.state="live"),it(J,"lead-handoff-failed",{box:t.fromBox,fromAgent:c,toAgent:o,reason:t.reason,error:`incoming ${o} CLI never drew its composer`,rolledBack:!0,handoffStartedAt:t.startedAt}),await K()}throw new Error(`incoming ${o} CLI never drew its composer \u2014 rolled back to ${c} (--resume, context intact)`)}await s("commit");let w=await H(),m=Q(w,t.trackId);if(!m)throw new Error(`track "${t.trackId}" not found during handoff commit \u2014 was it deleted or archived mid-flight?`);let g=m.boxes.find(T=>T.name===t.fromBox);if(!g)throw new Error(`lead box row "${t.fromBox}" missing during handoff commit`);let x=Date.now(),A=Dp(g),S=A[A.length-1];S.endedAt=x,t.reason&&(S.endReason=t.reason),A.push({agent:o,sessionId:d.sessionId,pinned:d.pinned,startedAt:x,endedAt:null}),g.agent=o,g.sessionId=d.sessionId,g.pinned=d.pinned,g.state="live",g.role="lead",m.pendingHandoffHydrate={artifact:os,at:x},it(m,"lead-handoff",{from:t.fromBox,to:t.fromBox,fromAgent:c,toAgent:o,fromSessionId:l,toSessionId:d.sessionId,epoch:A.length-1,singleBox:!0,forced:d.forced||void 0,skipPermissions:t.skipPermissions,reason:t.reason,handoffStartedAt:t.startedAt}),await K(),await s("done",{sessionName:t.fromBox}),await Qo(t.trackId,{finishedAt:Date.now(),error:null}),t.finishedAt=Date.now()}var A0=Number(process.env.BOSTRAT_SUBTASK_TIMEOUT_MS)||18e5,_0=Number(process.env.BOSTRAT_SUBTASK_POLL_MS)||3e3,Kn=U.join(Ie,"worktrees"),Ui=[Zo,qi],Zt=new Map;var Jn=t=>{let e=new Map([...Zt.values()].filter(r=>r.trackId===t).map(r=>[r.id,r])),n=Yt?Q(Yt,t):null;if(n?.subtasks)for(let[r,o]of Object.entries(n.subtasks))e.has(r)||e.set(r,o);return[...e.values()].sort((r,o)=>r.startedAt-o.startedAt)},Yi=t=>({id:t.id,trackId:t.trackId,trackName:t.trackName,agent:t.agent,task:t.task,skipPermissions:t.skipPermissions,reason:t.reason||null,step:t.step,failedStep:t.failedStep||null,box:t.box,branch:t.branch,base:t.base,dir:t.dir,stat:t.stat,files:t.files||null,conflicts:t.conflicts||[],applied:t.applied,error:t.error,canceled:t.canceled,startedAt:t.startedAt,finishedAt:t.finishedAt});async function E0(t,e,n){let r=await H(),o=Q(r,t);o&&(o.subtasks=o.subtasks||{},o.subtasks[e]={...o.subtasks[e]||{},...n},await K())}var Pr=t=>E0(t.trackId,t.id,Yi(t));async function Gn(t,e){t.step=e,await Pr(t)}function I0(t,e=Date.now()){if(!t||t.finishedAt)return null;let n=t.step;return["snapshot","spawn","working"].includes(n)?{...t,step:"error",finishedAt:e,error:`interrupted by an agent restart during "${n}"; no changes were applied to the lead \u2014 retry the delegation`,recovered:"rolled-back"}:{...t,step:"error",finishedAt:e,error:`interrupted by an agent restart during "${n}"; the lead's working tree may hold a PARTIAL merge \u2014 review git status. The subtask's work is preserved on branch ${t.branch}`,recovered:"partial-merge"}}function R0(t){return!!t&&!t.finishedAt&&!t.canceled&&t.step==="working"&&!!t.box&&!!t.dir&&!!t.base&&!!t.branch}async function $0(t,e,n){if(!R0(n)||Zt.has(e)||!await G(n.box).catch(()=>null))return!1;try{await P.access(n.dir)}catch{return!1}let o=await G(t.leadBox).catch(()=>null);if(!o?.cwd)return!1;let s={...n,id:e,leadBox:t.leadBox,step:"working",canceled:!1,forceMerge:!1};return Zt.set(e,s),Wp(s,o).catch(a=>Fp(s,a)),!0}async function Lp(){let t=await H(),e=!1;for(let n of t.tracks||[]){for(let[r,o]of Object.entries(n.subtasks||{})){if(Zt.has(r))continue;if(await $0(n,r,o)){e=!0,it(n,"subtask-resumed",{box:o.box,agent:o.agent,task:o.task,subtaskId:r,resumedStep:o.step}),await L({flow:"subtask",phase:"resumed",trackId:n.id,subtaskId:r,box:o.box,branch:o.branch}).catch(()=>{});continue}let s=o.step,a=I0(o);if(!a)continue;e=!0,n.subtasks[r]=a;let i=n.boxes.find(c=>c.subtaskId===r);i&&!an.has(i.state)&&(i.state="canceled"),it(n,"subtask-failed",{from:o.box,fromAgent:o.agent,task:o.task,subtaskId:r,failedStep:s,error:a.error,recovered:a.recovered,durationMs:o.startedAt?a.finishedAt-o.startedAt:null}),await L({flow:"subtask",phase:"reconciled",trackId:n.id,subtaskId:r,outcome:a.step,recovered:a.recovered,box:o.box,branch:o.branch}).catch(()=>{}),await pp(n,o.box,o.dir)}for(let r of n.boxes){if(r.role!=="subtask"||an.has(r.state)||Zt.has(r.subtaskId)||n.subtasks?.[r.subtaskId])continue;e=!0,r.state="canceled";let o=r.subtaskId?U.join(Kn,`${n.id}-${r.subtaskId}`):null;await L({flow:"subtask",phase:"reaped-legacy-orphan",trackId:n.id,subtaskId:r.subtaskId,box:r.name}).catch(()=>{}),await pp(n,r.name,o)}}return e=await O0(t)||e,e&&await K(),e}async function pp(t,e,n){if(e&&await Wt(e).catch(()=>{}),n){let r=await G(t.leadBox).catch(()=>null);r?.cwd&&await Ze(r.cwd,n).catch(()=>{})}}async function O0(t){let e;try{e=await P.readdir(Kn)}catch{return!1}let n=new Set;for(let o of t.tracks||[]){for(let s of Object.keys(o.subtasks||{}))n.add(`${o.id}-${s}`);for(let s of o.boxes||[])s.subtaskId&&n.add(`${o.id}-${s.subtaskId}`)}for(let o of Zt.values())o.dir&&n.add(U.basename(o.dir));let r=!1;for(let o of e){if(n.has(o))continue;let s=U.join(Kn,o),a=(t.tracks||[]).find(c=>o.startsWith(`${c.id}-`)),i=a?await G(a.leadBox).catch(()=>null):null;i?.cwd?await Ze(i.cwd,s).catch(()=>{}):await P.rm(s,{recursive:!0,force:!0}).catch(()=>{}),await L({flow:"subtask",phase:"swept-orphan-worktree",dir:s}).catch(()=>{}),r=!0}return r}var Zi=t=>t.trim().replace(/\s+/g,"-").replace(/[^A-Za-z0-9._-]/g,"").toLowerCase()||"track",Nr=class extends Error{};async function Up(t,{agent:e,task:n,skipPermissions:r=!0,reason:o=null}={}){if(typeof n!="string"||!Mn(n))throw new Error("task (string) required");let s=await rs(e,r),a=Q(await H(),t);if(!a)throw new Error(`track "${t}" not found`);if(a.workspace?.kind==="multi")throw new Error("subtask delegation isn't available on a multi-repo Track yet \u2014 ask the lead directly, or use a single-repo Track");let i=await G(a.leadBox);if(!i)throw new Error(`lead box "${a.leadBox}" is not running`);if(!i.cwd)throw new Error("can't resolve the lead's working directory");if(!await Ua(i.cwd))throw new Error(`subtask delegation needs the lead's workspace to be a git repo (got ${i.cwd})`);let c=ts().slice(0,8),l={id:c,trackId:t,trackName:a.name,agent:e,task:Mn(n),skipPermissions:r,reason:wp(o),step:"snapshot",leadBox:a.leadBox,box:null,branch:`bostrat/${Zi(a.name)}-${c}`,base:null,dir:U.join(Kn,`${t}-${c}`),stat:null,conflicts:[],applied:null,startedAt:Date.now(),finishedAt:null,error:null,canceled:!1,forceMerge:!1};return Zt.set(c,l),await Pr(l),C0(l,i,s).catch(u=>Fp(l,u)),l}async function Fp(t,e){t.failedStep=t.step,t.error=e instanceof Nr?"canceled":e?.message||String(e),t.step=e instanceof Nr?"canceled":"error",t.finishedAt=Date.now(),await tc(t).catch(()=>{}),await Pr(t).catch(()=>{}),await B0(t).catch(()=>{})}function Qi(t){let e=Zt.get(t);if(!e)throw new Error(`subtask "${t}" not found`);if(e.finishedAt)throw new Error("subtask already finished");return e.canceled=!0,e}function Hp(t){let e=Zt.get(t);if(!e)throw new Error(`subtask "${t}" not found`);if(e.finishedAt)throw new Error("subtask already finished");if(e.step!=="working")throw new Error(`subtask is not waiting to merge (step: ${e.step})`);return e.forceMerge=!0,e}async function C0(t,e,n){await Gn(t,"snapshot"),t.base=await Hu(e.cwd,`bostrat: snapshot for subtask ${t.id}`),await Gn(t,"spawn"),await P.mkdir(Kn,{recursive:!0}),await bo(e.cwd,t.dir,t.branch,t.base),await Hi(t.dir),await M0(t,e);let r=t.trackName.slice(0,38).trim(),o=await Ue(`${r} ${t.agent} sub ${t.id}`,t.dir,{agentCmd:n,agent:t.agent});t.box=o.name,await P0(t,o),await gp(t.trackId,o.name,Kx(t.task),{kind:"subtask"}),await Wp(t,e)}async function Wp(t,e){await Gn(t,"working");let n=U.join(t.dir,qi);if(await j0(n,t),t.canceled)throw new Nr;await Gn(t,"merge"),await Wu(t.dir,`bostrat: subtask ${t.id} result (auto-committed)`),t.stat=await Gu(e.cwd,t.base,t.branch,Ui),t.files=await Ku(e.cwd,t.base,t.branch,Ui);let r=await Vu(e.cwd,t.base,t.branch,Ui);t.applied=r.applied,t.conflicts=r.conflicts,await Pr(t);let o=await D0(t,e,n);await Gn(t,"resume"),await gp(t.trackId,t.leadBox,Vx(t.task,t.stat,t.conflicts,o),{kind:"subtask-resume"}),await Gn(t,"reap"),await tc(t),t.step="done",t.finishedAt=Date.now(),await N0(t),await Pr(t)}async function M0(t,e){let n=U.join(t.dir,U.dirname(Zo));await P.mkdir(n,{recursive:!0});let r="";try{r=await wl(e.cwd,e.sessionId)}catch{}if(!r)try{r=await P.readFile(U.join(e.cwd,".bostrat/handoff/latest.md"),"utf8")}catch{r="_(no prior conversation digest available \u2014 rely on the working tree.)_"}let o=`# Lead context for subtask ${t.id}
|
|
206
221
|
|
|
207
222
|
**Delegated task:** ${t.task}
|
|
208
223
|
|
|
@@ -213,53 +228,53 @@ When working anywhere else, ignore everything below.
|
|
|
213
228
|
## Conversation so far (lead \u2192 you)
|
|
214
229
|
|
|
215
230
|
${r}
|
|
216
|
-
`;await
|
|
217
|
-
`).forEach((o,s)=>{s>0&&r.push({type:"hardBreak"}),r.push(...
|
|
231
|
+
`;await P.writeFile(U.join(t.dir,Zo),o)}async function P0(t,e){let n=await H(),r=Q(n,t.trackId);r&&(r.boxes.push({name:e.name,agent:e.agent,role:"subtask",state:"live",cwd:e.cwd,sessionId:e.sessionId,createdAt:Date.now(),subtaskId:t.id,branch:t.branch,task:t.task}),it(r,"subtask-delegated",{to:e.name,toAgent:t.agent,task:t.task,subtaskId:t.id,skipPermissions:t.skipPermissions,reason:t.reason}),r.subtasks=r.subtasks||{},r.subtasks[t.id]={...r.subtasks[t.id]||{},...Yi(t)},await K())}async function N0(t){let e=await H(),n=Q(e,t.trackId);if(!n)return;let r=n.boxes.find(o=>o.subtaskId===t.id);r&&(r.state=t.conflicts.length?"conflict":"merged",r.stat=t.stat),it(n,"subtask-merged",{from:t.box,fromAgent:t.agent,task:t.task,subtaskId:t.id,stat:t.stat,conflicts:t.conflicts.length,files:t.files,durationMs:t.finishedAt?t.finishedAt-t.startedAt:null}),await K()}async function B0(t){let e=await H(),n=Q(e,t.trackId);if(!n)return;let r=t.canceled||t.error==="canceled",o=n.boxes.find(s=>s.subtaskId===t.id);o&&(o.state=r?"canceled":"failed"),it(n,r?"subtask-canceled":"subtask-failed",{from:t.box,fromAgent:t.agent,task:t.task,subtaskId:t.id,failedStep:t.failedStep||t.step,error:t.error,durationMs:t.finishedAt?t.finishedAt-t.startedAt:null}),n.subtasks=n.subtasks||{},n.subtasks[t.id]={...n.subtasks[t.id]||{},...Yi(t)},await K()}async function D0(t,e,n){let r=`.bostrat/handoff/subtask-${t.id}.md`;try{let o=U.join(e.cwd,r);await P.mkdir(U.dirname(o),{recursive:!0}),await P.copyFile(n,o)}catch{}return r}async function tc(t){if(t.box&&await Wt(t.box).catch(()=>{}),t.dir){let n=(await G(t.leadBox).catch(()=>null))?.cwd;n&&await Ze(n,t.dir).catch(()=>{})}}async function j0(t,e){let n=(e.startedAt||Date.now())+A0;for(;;){if(e.canceled||e.forceMerge)return;try{await P.access(t);return}catch{}if(Date.now()>n)throw new Error("subtask timed out without signaling completion (no result digest written)");await ec(_0)}}function ec(t){return new Promise(e=>setTimeout(e,t))}import Hr from"node:fs/promises";import gs from"node:path";import ES from"node:os";import L0 from"node:fs/promises";import qp from"node:path";import U0 from"node:os";var Gp=()=>qp.join(process.env.BOSTRAT_STATE_DIR||qp.join(process.env.HOME||U0.tmpdir(),".bostrat"),"integrations.json");async function cn(){try{return JSON.parse(await L0.readFile(Gp(),"utf8"))||{}}catch{return{}}}var as=["linked","prOpened","prMerged","create"],nc=["prompt","auto","off"],F0={create:["prompt","off"]},Kp=t=>F0[t]??nc;function is(t){let e=t.linear||{},n=e.transitions===!0?"auto":"prompt",r={};for(let o of as){let s=Kp(o),a=s.includes(n)?n:"prompt",i=t.ticketFlow?.[o]??e.ticketFlow?.[o];r[o]=s.includes(i)?i:a}return r}async function ln(){let t=await cn(),e=process.env.BOSTRAT_LINEAR_API_KEY||t.linear?.apiKey,n=process.env.BOSTRAT_LINEAR_DEFAULT_TEAM||t.linear?.defaultTeam||null;return e?{apiKey:e,ticketFlow:is(t),defaultTeam:n}:null}async function cs(){let t=await cn(),e=t.jira||{},n=process.env.BOSTRAT_JIRA_BASE_URL||e.baseUrl,r=process.env.BOSTRAT_JIRA_EMAIL||e.email,o=process.env.BOSTRAT_JIRA_API_TOKEN||e.apiToken;return!n||!r||!o?null:{baseUrl:String(n).replace(/\/$/,""),email:r,apiToken:o,tokenExpiresAt:e.tokenExpiresAt??null,ticketFlow:is(t)}}async function rc(){return is(await cn())}async function Vp(t){let e=Object.entries(t||{});if(!e.length)throw new Error(`nothing to set \u2014 phases: ${as.join(", ")}`);for(let[r,o]of e){if(!as.includes(r))throw new Error(`unknown phase "${r}" \u2014 configurable phases: ${as.join(", ")}`);let s=Kp(r);if(!s.includes(o))throw new Error(s.length<nc.length?`mode "${o}" not allowed for "${r}" \u2014 ${r} is always confirmed (modes: ${s.join(", ")})`:`unknown mode "${o}" \u2014 modes: ${nc.join(", ")}`)}let n=await cn();return n.ticketFlow={...is(n),...Object.fromEntries(e)},n.linear&&(delete n.linear.ticketFlow,delete n.linear.transitions),await qt(Gp(),n,{mode:384}),n.ticketFlow}async function Dr(){let t=(await cn()).deepgram||{},e=process.env.DEEPGRAM_API_KEY||t.apiKey;return e?{apiKey:e}:null}async function Jp(){let t=(await cn()).cedar||{},e=process.env.BOSTRAT_CEDAR_INGEST_KEY||t.ingestKey,n=process.env.BOSTRAT_CEDAR_URL||t.url||"https://cedar-api.fly.dev";return e?{url:n,ingestKey:e}:null}async function un(){let t=(await cn()).slack||{},e=process.env.BOSTRAT_SLACK_BOT_TOKEN||t.botToken,n=process.env.BOSTRAT_SLACK_APP_TOKEN||t.appToken;return!e||!n?null:{botToken:e,appToken:n,channel:process.env.BOSTRAT_SLACK_CHANNEL||t.channel||null,maxConcurrent:Number.isInteger(t.maxConcurrent)&&t.maxConcurrent>0?t.maxConcurrent:2}}import Ur from"node:fs/promises";import sm from"node:path";import pS from"node:os";import{randomUUID as am}from"node:crypto";var zp=()=>({input:0,output:0,cacheRead:0,cacheCreation:0});function H0(t){return t?{input:t.input||0,output:t.output||0,cacheRead:t.cacheRead||0,cacheWrite:t.cacheWrite??t.cacheCreation??0}:null}function Xp(t,e){e&&(t.input+=e.input,t.output+=e.output,t.cacheRead+=e.cacheRead,t.cacheCreation+=e.cacheWrite)}function us(t,{boxLabels:e={}}={}){let n=(t||[]).filter(d=>d.type==="cost.recorded"),r={},o={},s={},a=0,i=0,c=0,l=new Set,u=zp();for(let d of n){let f=d.payload?.model||"unknown",w=d.provider||"unknown",m=d.boxId||"unattributed",g=H0(d.payload?.tokens),x=Number(d.payload?.usd)||0;!x&&g&&(x=Kt(f,g),x>0&&i++);let A=g?g.input+g.output+g.cacheRead+g.cacheWrite:0;if(!x&&A>0&&!jo(f)&&(c++,l.add(f)),a+=x,Xp(u,g),r[f]||={usd:0,tokens:zp(),events:0},r[f].usd+=x,r[f].events+=1,Xp(r[f].tokens,g),o[w]||={usd:0,events:0},o[w].usd+=x,o[w].events+=1,s[m]||={usd:0,events:0,provider:w,models:new Set,label:e[m]||m},s[m].usd+=x,s[m].events+=1,f!=="unknown"&&s[m].models.add(f),d.epoch!=null){let S=(s[m].epochs||={})[d.epoch]||={usd:0,events:0,provider:w,models:new Set};S.usd+=x,S.events+=1,S.provider=w,f!=="unknown"&&S.models.add(f)}}return{totalUsd:jr(a),totalTokens:u,turns:n.length,repricedTurns:i,unpricedTurns:c,unpricedModels:[...l],byModel:ls(r,d=>({...d,usd:jr(d.usd)})),byProvider:ls(o,d=>({...d,usd:jr(d.usd)})),byBox:ls(s,d=>({usd:jr(d.usd),events:d.events,provider:d.provider,label:d.label,models:[...d.models],...d.epochs?{epochs:ls(d.epochs,f=>({usd:jr(f.usd),events:f.events,provider:f.provider,models:[...f.models]}))}:{}}))}}var jr=t=>Math.round(t*1e4)/1e4,ls=(t,e)=>Object.fromEntries(Object.entries(t).map(([n,r])=>[n,e(r)]));var sc={};Yc(sc,{createComment:()=>Y0,findIssueUrl:()=>K0,issueStates:()=>tS,mdToAdf:()=>Qp,moveIssue:()=>eS,parseIssueRef:()=>G0,resolveIssue:()=>V0,uploadImage:()=>Z0,upsertCard:()=>J0,whoami:()=>oc});var zn=t=>String(t.baseUrl||"").replace(/\/$/,""),Zp=t=>`Basic ${Buffer.from(`${t.email}:${t.apiToken}`).toString("base64")}`;async function dn(t,e,n,r){let o=await fetch(`${zn(t)}${n}`,{method:e,headers:{authorization:Zp(t),accept:"application/json",...r!==void 0?{"content-type":"application/json"}:{}},body:r!==void 0?JSON.stringify(r):void 0}),s=await o.text(),a=null;try{a=s?JSON.parse(s):null}catch{a=null}if(!o.ok){let i=a?.errorMessages?.[0]??Object.values(a?.errors??{})[0]??`HTTP ${o.status}`,c=new Error(`jira: ${i}`);throw c.status=o.status,c}return a}var W0=/\/browse\/([A-Za-z][A-Za-z0-9]{0,9}-\d+)/,q0=/^([A-Z][A-Z0-9]{0,9}-\d+)$/;function G0(t){let e=String(t||"").trim();if(/^https?:\/\//i.test(e)){let r=e.match(W0);return r?r[1].toUpperCase():null}let n=e.match(q0);return n?n[1]:null}function K0(t,e=null){let n=null;try{n=e?.baseUrl?new URL(e.baseUrl).host:null}catch{n=null}let r=/https?:\/\/[^\s)]+\/browse\/[A-Za-z][A-Za-z0-9]{0,9}-\d+[^\s)]*/g;for(let o of String(t||"").matchAll(r))try{let s=new URL(o[0]).host;if(s===n||/\.atlassian\.net$/i.test(s))return o[0]}catch{}return null}async function V0(t,e){let n;try{n=await dn(t,"GET",`/rest/api/3/issue/${encodeURIComponent(e)}?fields=summary,status`)}catch(r){throw r.status===404?new Error(`jira: issue "${e}" not found`):r}return{issueId:n.id,key:n.key,title:n.fields?.summary??null,url:`${zn(t)}/browse/${n.key}`,state:n.fields?.status?.name??null}}async function J0(t,e,{url:n,title:r,subtitle:o}){await dn(t,"POST",`/rest/api/3/issue/${encodeURIComponent(e.key)}/remotelink`,{globalId:n,application:{type:"com.bostrat.tracks",name:"Bostrat"},object:{url:n,title:r,summary:o,icon:{title:"Bostrat"}}})}var z0=/(!\[[^\]]*\]\([^)\s]+\)|\*\*[^*\n]+\*\*|`[^`\n]+`|_[^_\n]+_|https?:\/\/[^\s)]+)/g;function X0(t){let e=[],n=0;for(let r of t.matchAll(z0)){r.index>n&&e.push({type:"text",text:t.slice(n,r.index)});let o=r[0];if(o.startsWith("![")){let[,s,a]=o.match(/^!\[([^\]]*)\]\(([^)\s]+)\)$/);e.push({type:"text",text:s||a,marks:[{type:"link",attrs:{href:a}}]})}else o.startsWith("**")?e.push({type:"text",text:o.slice(2,-2),marks:[{type:"strong"}]}):o.startsWith("`")?e.push({type:"text",text:o.slice(1,-1),marks:[{type:"code"}]}):o.startsWith("_")?e.push({type:"text",text:o.slice(1,-1),marks:[{type:"em"}]}):e.push({type:"text",text:o,marks:[{type:"link",attrs:{href:o}}]});n=r.index+o.length}return n<t.length&&e.push({type:"text",text:t.slice(n)}),e}function Qp(t){let e=String(t??"").split(/\n{2,}/).map(n=>n.trim()).filter(Boolean).map(n=>{let r=[];return n.split(`
|
|
232
|
+
`).forEach((o,s)=>{s>0&&r.push({type:"hardBreak"}),r.push(...X0(o))}),{type:"paragraph",content:r.length?r:[{type:"text",text:" "}]}});return{type:"doc",version:1,content:e.length?e:[{type:"paragraph",content:[{type:"text",text:" "}]}]}}async function Y0(t,e,n){let r=await dn(t,"POST",`/rest/api/3/issue/${encodeURIComponent(e.key)}/comment`,{body:Qp(n)});return r?.id?`${zn(t)}/browse/${e.key}?focusedCommentId=${r.id}`:null}async function oc(t){let e=await dn(t,"GET","/rest/api/3/myself"),n=null;try{n=new URL(zn(t)).host}catch{n=t.baseUrl??null}return{user:e?.displayName??e?.emailAddress??null,org:n}}async function Z0(t,{issue:e,filename:n,contentType:r,buffer:o}){let s=new FormData;s.append("file",new Blob([o],{type:r}),n);let a=await fetch(`${zn(t)}/rest/api/3/issue/${encodeURIComponent(e)}/attachments`,{method:"POST",headers:{authorization:Zp(t),"x-atlassian-token":"no-check",accept:"application/json"},body:s}),i=await a.json().catch(()=>null),c=Array.isArray(i)?i[0]:null;if(!a.ok||!c)throw new Error(`jira: attachment upload failed (HTTP ${a.status})`);return c.content??`${zn(t)}/rest/api/3/attachment/content/${c.id}`}var Q0={new:"unstarted",indeterminate:"started",done:"completed"},Yp=t=>Q0[t?.key]??"started";async function tS(t,e){let n=encodeURIComponent(e.key),[r,o]=await Promise.all([dn(t,"GET",`/rest/api/3/issue/${n}?fields=status`),dn(t,"GET",`/rest/api/3/issue/${n}/transitions`)]),s=r?.fields?.status??null,a=s?{id:`status:${s.id}`,name:s.name,type:Yp(s.statusCategory)}:null,i=(o?.transitions??[]).map((c,l)=>({id:c.id,name:c.to?.name??c.name,type:Yp(c.to?.statusCategory),position:l}));return{current:a,states:i}}async function eS(t,e,n){await dn(t,"POST",`/rest/api/3/issue/${encodeURIComponent(e.key)}/transitions`,{transition:{id:String(n)}})}var lc={};Yc(lc,{createComment:()=>lS,createIssue:()=>cc,createProject:()=>ic,findIssueUrl:()=>aS,issueStates:()=>dS,moveIssue:()=>fS,parseIssueRef:()=>sS,resolveIssue:()=>iS,teams:()=>Xn,uploadImage:()=>uS,upsertCard:()=>cS,whoami:()=>ac});var nS=()=>process.env.BOSTRAT_LINEAR_API_URL||"https://api.linear.app/graphql";async function Qt(t,e,n={}){let r=await fetch(nS(),{method:"POST",headers:{"content-type":"application/json",authorization:t.apiKey},body:JSON.stringify({query:e,variables:n})}),o=await r.json().catch(()=>null);if(!r.ok)throw new Error(`linear: HTTP ${r.status}${o?.errors?.[0]?.message?` \u2014 ${o.errors[0].message}`:""}`);if(o?.errors?.length)throw new Error(`linear: ${o.errors[0].message}`);return o?.data??{}}var rS=/linear\.app\/[^/\s]+\/issue\/([A-Za-z][A-Za-z0-9]{0,9}-\d+)/,oS=/^([A-Z][A-Z0-9]{0,9}-\d+)$/;function sS(t){let e=String(t||"").trim(),n=e.match(rS);if(n)return n[1].toUpperCase();let r=e.match(oS);return r?r[1]:null}function aS(t){let e=String(t||"").match(/https?:\/\/linear\.app\/[^/\s]+\/issue\/[A-Za-z][A-Za-z0-9]{0,9}-\d+[^\s)]*/);return e?e[0]:null}async function iS(t,e){let r=(await Qt(t,"query($id: String!) { issue(id: $id) { id identifier title url state { name } team { key } } }",{id:e})).issue;if(!r)throw new Error(`linear: issue "${e}" not found`);return{issueId:r.id,key:r.identifier,title:r.title,url:r.url,state:r.state?.name??null,team:r.team?.key??null}}async function cS(t,e,{url:n,title:r,subtitle:o}){let s=await Qt(t,"mutation($input: AttachmentCreateInput!) { attachmentCreate(input: $input) { success attachment { id } } }",{input:{issueId:e.issueId,url:n,title:r,subtitle:o}});if(!s.attachmentCreate?.success)throw new Error("linear: attachmentCreate did not succeed");return s.attachmentCreate.attachment?.id??null}async function lS(t,e,n){let r=await Qt(t,"mutation($input: CommentCreateInput!) { commentCreate(input: $input) { success comment { url } } }",{input:{issueId:e.issueId,body:n}});if(!r.commentCreate?.success)throw new Error("linear: commentCreate did not succeed");return r.commentCreate.comment?.url??null}async function ac(t){let e=await Qt(t,"query { viewer { name email } organization { name urlKey } }");return{user:e.viewer?.name??e.viewer?.email??null,org:e.organization?.name??e.organization?.urlKey??null}}async function uS(t,{filename:e,contentType:n,buffer:r}){let o=await Qt(t,`mutation($contentType: String!, $filename: String!, $size: Int!) {
|
|
218
233
|
fileUpload(contentType: $contentType, filename: $filename, size: $size) {
|
|
219
234
|
success
|
|
220
235
|
uploadFile { uploadUrl assetUrl headers { key value } }
|
|
221
236
|
}
|
|
222
|
-
}`,{contentType:n,filename:e,size:r.length}),s=o.fileUpload?.uploadFile;if(!o.fileUpload?.success||!s?.uploadUrl)throw new Error("linear: fileUpload did not succeed");let a={"content-type":n};for(let c of s.headers||[])a[c.key]=c.value;let i=await fetch(s.uploadUrl,{method:"PUT",headers:a,body:r});if(!i.ok)throw new Error(`linear: asset PUT failed (HTTP ${i.status})`);return s.assetUrl}async function z0(t,e){let n=await Qt(t,"query($id: String!) { issue(id: $id) { state { id name type } team { states { nodes { id name type position } } } } }",{id:e.key});if(!n.issue)throw new Error(`linear: issue "${e.key}" not found`);return{current:n.issue.state??null,states:n.issue.team?.states?.nodes??[]}}async function X0(t,e,n){if(!(await Qt(t,"mutation($id: String!, $input: IssueUpdateInput!) { issueUpdate(id: $id, input: $input) { success } }",{id:e.issueId,input:{stateId:n}})).issueUpdate?.success)throw new Error("linear: issueUpdate did not succeed")}async function Vn(t){return((await Qt(t,"query { teams(first: 100) { nodes { id key name } } }")).teams?.nodes??[]).map(n=>({id:n.id,key:n.key,name:n.name}))}async function Qi(t,{teamId:e,name:n,description:r}){let o=await Qt(t,"mutation($input: ProjectCreateInput!) { projectCreate(input: $input) { success project { id url name } } }",{input:{teamIds:[e],name:n,...r?{description:r}:{}}}),s=o.projectCreate?.project;if(!o.projectCreate?.success||!s)throw new Error("linear: projectCreate did not succeed");return{id:s.id,url:s.url,name:s.name}}async function tc(t,{teamId:e,title:n,description:r,projectId:o,priority:s}){let a=await Qt(t,"mutation($input: IssueCreateInput!) { issueCreate(input: $input) { success issue { id identifier url title } } }",{input:{teamId:e,title:n,...r?{description:r}:{},...o?{projectId:o}:{},...Number.isInteger(s)?{priority:s}:{}}}),i=a.issueCreate?.issue;if(!a.issueCreate?.success||!i)throw new Error("linear: issueCreate did not succeed");return{id:i.id,key:i.identifier,url:i.url,title:i.title}}var Qp={linear:{api:ec,config:rn},jira:{api:Yi,config:ss}};async function ic(){let t=[];for(let[e,{api:n,config:r}]of Object.entries(Qp)){let o=await r();o&&t.push({name:e,api:n,cfg:o})}return t}async function an(t){let e=Qp[t?.provider];if(!e)return null;let n=await e.config();return n?{name:t.provider,api:e.api,cfg:n}:null}function cc(t,e){if(!t||t.name)return t;let n=e.find(r=>r.id===t.id);return n?{...t,name:n.name}:t}var tm=()=>process.env.BOSTRAT_STATE_DIR||Yp.join(process.env.HOME||Y0.tmpdir(),".bostrat"),sc=()=>Yp.join(tm(),"tickets.json"),em=()=>(process.env.BOSTRAT_PUBLIC_URL||"http://bostrat.globopi").replace(/\/$/,""),ls=t=>`${em()}/track/${t}`,cs=null,nc=null,rc=Promise.resolve();async function us(){return cs||(nc??=Dr.readFile(sc(),"utf8").then(t=>JSON.parse(t)?.tickets??{}).catch(()=>({})).then(t=>cs=t).finally(()=>nc=null),nc)}async function Kp(){let t=JSON.stringify({tickets:cs},null,2),e=`${sc()}.${process.pid}.tmp`;await Dr.mkdir(tm(),{recursive:!0}),await Dr.writeFile(e,t),await Dr.rename(e,sc())}async function Oe(){rc=rc.then(Kp,Kp),await rc.catch(()=>{})}async function te(t){return(await us())[t]??null}async function nm(){return{...await us()}}async function rm(){return(await ic()).length>0}async function Jn(t,e,{source:n="api"}={}){let r=await ic();if(!r.length)throw new Error("no ticketing integration configured \u2014 add a linear or jira entry to ~/.bostrat/integrations.json");let o=null,s=null,a=null;for(let u of r){let d=u.api.parseIssueRef(e);if(d)try{s=await u.api.resolveIssue(u.cfg,d),o=u;break}catch(f){a=f}}if(!o)throw a||new Error(`"${e}" is not a Linear or Jira issue URL or key (ABC-123)`);let i=(t.boxes||[]).find(u=>u.name===t.leadBox),c={provider:o.name,key:s.key,issueId:s.issueId,url:s.url,title:s.title,state:s.state,source:n,linkedAt:Date.now(),track:{name:t.name,branch:t.workspace?.branch??null,leadAgent:i?.agent??null,cwd:t.cwd??null},pr:null};(await us())[t.id]=c,await Oe(),await R({id:t.id,cwd:t.cwd},{type:"ticket.linked",actor:"user",source:"orchestrator",payload:{ts:Date.now(),provider:c.provider,key:c.key,url:c.url,title:c.title,linkSource:n}}).catch(()=>{}),await jr(t.id,c);let l=[];return c.track.branch&&l.push(`\`${c.track.branch}\``),c.track.leadAgent&&l.push(`lead ${c.track.leadAgent}`),await Br(c,`**Bostrat** \u2014 Track **${c.track.name}** linked${l.length?` (${l.join(", ")})`:""}.`),await am(t.id,c,"linked"),c}async function lc(t){let e=await us();return e[t]?(delete e[t],await Oe(),!0):!1}var ac=t=>`$${t>=100?Math.round(t):t.toFixed(2)}`;async function om(t,e){if(!t.track?.cwd)return null;let n=is(await P({id:e,cwd:t.track.cwd}));return!n.totalUsd||n.totalUsd<.01?null:n}function Z0(t){let e=Object.entries(t.byProvider).sort((r,o)=>o[1].usd-r[1].usd).map(([r,o])=>`${r} ${ac(o.usd)}`).join(" \xB7 "),n=Object.keys(t.byBox).length;return`${ac(t.totalUsd)}${e?` (${e})`:""}${n>1?` across ${n} boxes`:""}`}function Q0(t,e){let n=[];return t.track.leadAgent&&n.push(`lead ${t.track.leadAgent}`),t.track.branch&&n.push(t.track.branch),t.pr?.number&&n.push(`PR #${t.pr.number} ${t.pr.change==="merged"?"merged":"open"}`),e&&n.push(ac(e.totalUsd)),n.join(" \xB7 ")||"linked"}async function jr(t,e){let n=await an(e);if(!n)return;let r=await om(e,t).catch(()=>null);await n.api.upsertCard(n.cfg,e,{url:ls(t),title:`Bostrat Track: ${e.track.name}`,subtitle:Q0(e,r)})}function ds(t){let e=new URL(em());return`${e.protocol}//${t}.${e.host}/`}var tS={linked:"linked",opened:"prOpened",merged:"prMerged"};function eS(t,e,n){let r=o=>e.filter(s=>s.type===o).sort((s,a)=>s.position-a.position);if(n==="linked"){if(["triage","backlog","unstarted"].includes(t?.type))return r("started")[0]??null}else if(n==="opened"){if(t?.type!=="completed")return r("started").find(o=>/review/i.test(o.name))??null}else if(n==="merged"&&t?.type!=="completed")return r("completed")[0]??null;return null}async function sm(t,e,{from:n,to:r,mode:o,phase:s=null,actor:a="user"}){e.state=r,await Oe(),await R({id:t,cwd:e.track?.cwd},{type:"ticket.state.changed",actor:a,source:"orchestrator",payload:{ts:Date.now(),key:e.key,url:e.url,from:n??null,to:r,mode:o,...s?{phase:s}:{}}}).catch(()=>{})}async function am(t,e,n){let r=await an(e);if(!r)return;let o=r.cfg.ticketFlow?.[tS[n]]??"prompt";if(o==="off")return;let{current:s,states:a}=await r.api.issueStates(r.cfg,e),i=cc(s,a),c=eS(i,a,n);if(!c||c.id===i?.id)return;let l=i?.name??e.state??null;if(o==="auto"){await r.api.moveIssue(r.cfg,e,c.id),await sm(t,e,{from:l,to:c.name,mode:"auto",phase:n,actor:"system"});return}await R({id:t,cwd:e.track?.cwd},{type:"ticket.move.offered",actor:"system",source:"orchestrator",payload:{ts:Date.now(),offerId:Zp(),provider:e.provider,key:e.key,url:e.url,phase:n,from:l,to:{id:c.id,name:c.name,type:c.type},...e.pr?.number?{prNumber:e.pr.number}:{}}}).catch(()=>{})}async function im(t){let e=await te(t);if(!e)throw new Error("no ticket linked to this Track");let n=await an(e);if(!n)throw new Error("no ticketing integration configured");let{current:r,states:o}=await n.api.issueStates(n.cfg,e),s=cc(r,o);return s?.name&&e.state!==s.name&&(e.state=s.name,await Oe()),{key:e.key,url:e.url,current:s?{id:s.id,name:s.name??null,type:s.type??null}:null,states:[...o].sort((a,i)=>a.position-i.position).map(({id:a,name:i,type:c,position:l})=>({id:a,name:i,type:c,position:l}))}}async function uc(t,e,{mode:n="manual"}={}){if(!e)throw new Error("stateId required");let r=await te(t);if(!r)throw new Error("no ticket linked to this Track");let o=await an(r);if(!o)throw new Error("no ticketing integration configured");let{current:s,states:a}=await o.api.issueStates(o.cfg,r),i=cc(s,a),c=a.find(u=>u.id===e);if(!c)throw new Error("unknown state for this ticket's team");let l=i?.name??r.state??null;return i?.id===c.id?(r.state!==c.name&&(r.state=c.name,await Oe()),{key:r.key,from:l,to:c.name,moved:!1}):(await o.api.moveIssue(o.cfg,r,c.id),await sm(t,r,{from:l,to:c.name,mode:n}),{key:r.key,from:l,to:c.name,moved:!0})}async function cm(t,e){let n=await te(t);if(!n)throw new Error("no ticket linked to this Track");let r=await an(n);if(!r)throw new Error("no ticketing integration configured");let o=String(e||"").trim();if(!o)throw new Error("comment body required");let s=await r.api.createComment(r.cfg,n,o);return await R({id:t,cwd:n.track?.cwd},{type:"ticket.commented",actor:"user",source:"orchestrator",payload:{ts:Date.now(),key:n.key,url:s??n.url,excerpt:o.length>280?`${o.slice(0,279)}\u2026`:o}}).catch(()=>{}),{key:n.key,url:s??n.url}}async function lm(t,e,n){if(!e)throw new Error("offerId required");let r=await P({id:t.id,cwd:t.cwd}),o=r.find(a=>a.type==="ticket.move.offered"&&a.payload?.offerId===e);if(!o)throw new Error("unknown offer");if(r.some(a=>a.type==="ticket.move.answered"&&a.payload?.offerId===e))throw new Error("offer already answered");let s=null;return n&&(s=await uc(t.id,o.payload.to?.id,{mode:"offer"})),await R({id:t.id,cwd:t.cwd},{type:"ticket.move.answered",actor:"user",source:"orchestrator",payload:{ts:Date.now(),offerId:e,action:n?"moved":"dismiss",...s?{from:s.from,to:s.to}:{}}}),{offerId:e,action:n?"moved":"dismiss",...s??{}}}var Vp=50;async function um(){let t=await rn();if(!t)throw new Error("no ticketing integration configured");return Vn(t)}function nS(t){let e=t&&typeof t=="object"?t:{},n=(Array.isArray(e.issues)?e.issues:[]).map(o=>o&&typeof o=="object"?o:{title:String(o||"")}).map(o=>({title:String(o.title||"").trim(),description:o.description?String(o.description):null,priority:Number.isInteger(o.priority)&&o.priority>=0&&o.priority<=4?o.priority:null})).filter(o=>o.title),r=e.epic&&String(e.epic.name||"").trim()?{name:String(e.epic.name).trim(),description:e.epic.description?String(e.epic.description):null}:null;if(!n.length&&!r)throw new Error("a plan needs an epic or at least one issue");if(n.length>Vp)throw new Error(`too many issues (${n.length}); cap is ${Vp} per proposal`);return{epic:r,issues:n,team:e.team?String(e.team).trim():null,linkCurrentTrack:e.linkCurrentTrack===!0}}async function rS(t,e,n,r){let o=r??await Vn(t),s=i=>o.find(c=>c.id===i||c.key.toLowerCase()===String(i).toLowerCase()||c.name.toLowerCase()===String(i).toLowerCase())??null;if(e.team){let i=s(e.team);if(i)return i}let a=await te(n.id);if(a?.key){let i=a.key.split("-")[0],c=o.find(l=>l.key===i);if(c)return c}if(t.defaultTeam){let i=s(t.defaultTeam);if(i)return i}return o.length===1?o[0]:null}async function dm(t,e){let n=await rn();if(!n)throw new Error("no ticketing integration configured \u2014 add a linear apiKey to ~/.bostrat/integrations.json");if(n.ticketFlow?.create==="off")throw new Error("ticket creation offers are off (bostrat-ticket flow set create=prompt to re-enable)");let r=nS(e),o=await Vn(n),s=await rS(n,r,t,o),a=s?{id:s.id,key:s.key,name:s.name}:null,i=Zp();return await R({id:t.id,cwd:t.cwd},{type:"ticket.create.offered",actor:"system",source:"orchestrator",payload:{ts:Date.now(),proposalId:i,provider:"linear",epic:r.epic,issues:r.issues,team:a,needsTeam:!s,linkCurrentTrack:r.linkCurrentTrack}}),{proposalId:i,epic:r.epic,issues:r.issues,team:a,needsTeam:!s}}async function fm(t,e,n,{teamId:r=null}={}){if(!e)throw new Error("proposalId required");let o=await P({id:t.id,cwd:t.cwd}),s=o.find(h=>h.type==="ticket.create.offered"&&h.payload?.proposalId===e);if(!s)throw new Error("unknown proposal");if(o.some(h=>h.type==="ticket.create.answered"&&h.payload?.proposalId===e))throw new Error("proposal already answered");if(!n)return await R({id:t.id,cwd:t.cwd},{type:"ticket.create.answered",actor:"user",source:"orchestrator",payload:{ts:Date.now(),proposalId:e,action:"dismiss"}}),{proposalId:e,action:"dismiss"};let a=await rn();if(!a)throw new Error("no ticketing integration configured");let i=s.payload,c=i.team??null;if(!c&&r){let h=(await Vn(a)).find(g=>g.id===r);if(!h)throw new Error("unknown team");c={id:h.id,key:h.key,name:h.name}}if(!c)throw new Error("a team is required to create \u2014 pick one");let l=[],u=[],d=null;if(i.epic){let h=await Qi(a,{teamId:c.id,name:i.epic.name,description:i.epic.description});d=h.id,l.push({kind:"epic",title:h.name,url:h.url})}for(let h of i.issues||[])try{let g=await tc(a,{teamId:c.id,title:h.title,description:h.description,projectId:d,priority:h.priority});l.push({kind:"issue",key:g.key,title:g.title,url:g.url})}catch(g){u.push({title:h.title,message:g?.message||String(g)})}await R({id:t.id,cwd:t.cwd},{type:"ticket.created",actor:"user",source:"orchestrator",payload:{ts:Date.now(),proposalId:e,team:{key:c.key,name:c.name},items:l,failed:u}}),await R({id:t.id,cwd:t.cwd},{type:"ticket.create.answered",actor:"user",source:"orchestrator",payload:{ts:Date.now(),proposalId:e,action:"created",createdCount:l.length,failedCount:u.length}});let f=l.filter(h=>h.kind==="issue"),w=null;return i.linkCurrentTrack&&f.length===1&&!await te(t.id)&&(w=await Jn(t,f[0].key,{source:"created"}).catch(()=>null)),{proposalId:e,action:"created",created:l,failed:u,linked:w?w.key:null}}async function Br(t,e){let n=await an(t);n&&await n.api.createComment(n.cfg,t,e)}async function oS(t,e,n){let r=await an(t);if(!r)return null;let o=await Ae(e);if(!o)return null;let s=Ze(o,n);if(!s)return null;let a=await Dr.readFile(Bt(o,s.v).png).catch(()=>null);return a?r.api.uploadImage(r.cfg,{issue:t.key,filename:`${o.name.replace(/[^\w.-]+/g,"-")}.v${s.v}.png`,contentType:"image/png",buffer:a}):null}var Jp=()=>Number(process.env.BOSTRAT_TICKET_COST_SYNC_MS??12e4),oc=new Map;function sS(t,e){if(Jp()<=0)return jr(t,e);if(oc.has(t))return;let n=setTimeout(()=>{oc.delete(t),te(t).then(r=>r?jr(t,r):null).catch(()=>{})},Jp());n.unref?.(),oc.set(t,n)}var zp=new Map;async function Xp(t,e,n){let r=zp.get(t)??0;Date.now()-r<600*1e3||(zp.set(t,Date.now()),await R({id:t,cwd:e},{type:"ticket.sync.failed",actor:"system",source:"orchestrator",payload:{ts:Date.now(),message:n?.message||String(n)}}).catch(()=>{}))}async function aS(t,{resolveTrack:e=null}={}){let n=await te(t.trackId),r=t.payload||{};if(!n){if(t.type!=="turn.user"||t.actor!=="user")return;let o=null;for(let a of await ic())if(o=a.api.findIssueUrl(r.text,a.cfg),o)break;if(!o)return;let s=e?await e(t.trackId):null;if(!s)return;try{await Jn(s,o,{source:"chat"})}catch(a){await Xp(t.trackId,s.cwd,a)}return}if(!n.track.cwd&&e){let o=await e(t.trackId).catch(()=>null);o?.cwd&&(n.track.cwd=o.cwd,await Oe())}try{switch(t.type){case"handoff.done":n.track.leadAgent=r.toAgent??n.track.leadAgent,await Oe(),await jr(t.trackId,n);break;case"pr.state.changed":{if(!r.change)break;if(r.number&&(n.pr={number:r.number,url:r.url??n.pr?.url??null,change:r.change}),await Oe(),await jr(t.trackId,n),r.change==="opened"||r.change==="merged"){let o=r.change==="merged"?await om(n,t.trackId).catch(()=>null):null;await Br(n,`**Bostrat** \u2014 PR ${r.change} for **${n.track.name}**${r.url?`: ${r.url}`:r.number?` (#${r.number})`:""}`+(o?`
|
|
237
|
+
}`,{contentType:n,filename:e,size:r.length}),s=o.fileUpload?.uploadFile;if(!o.fileUpload?.success||!s?.uploadUrl)throw new Error("linear: fileUpload did not succeed");let a={"content-type":n};for(let c of s.headers||[])a[c.key]=c.value;let i=await fetch(s.uploadUrl,{method:"PUT",headers:a,body:r});if(!i.ok)throw new Error(`linear: asset PUT failed (HTTP ${i.status})`);return s.assetUrl}async function dS(t,e){let n=await Qt(t,"query($id: String!) { issue(id: $id) { state { id name type } team { states { nodes { id name type position } } } } }",{id:e.key});if(!n.issue)throw new Error(`linear: issue "${e.key}" not found`);return{current:n.issue.state??null,states:n.issue.team?.states?.nodes??[]}}async function fS(t,e,n){if(!(await Qt(t,"mutation($id: String!, $input: IssueUpdateInput!) { issueUpdate(id: $id, input: $input) { success } }",{id:e.issueId,input:{stateId:n}})).issueUpdate?.success)throw new Error("linear: issueUpdate did not succeed")}async function Xn(t){return((await Qt(t,"query { teams(first: 100) { nodes { id key name } } }")).teams?.nodes??[]).map(n=>({id:n.id,key:n.key,name:n.name}))}async function ic(t,{teamId:e,name:n,description:r}){let o=await Qt(t,"mutation($input: ProjectCreateInput!) { projectCreate(input: $input) { success project { id url name } } }",{input:{teamIds:[e],name:n,...r?{description:r}:{}}}),s=o.projectCreate?.project;if(!o.projectCreate?.success||!s)throw new Error("linear: projectCreate did not succeed");return{id:s.id,url:s.url,name:s.name}}async function cc(t,{teamId:e,title:n,description:r,projectId:o,priority:s}){let a=await Qt(t,"mutation($input: IssueCreateInput!) { issueCreate(input: $input) { success issue { id identifier url title } } }",{input:{teamId:e,title:n,...r?{description:r}:{},...o?{projectId:o}:{},...Number.isInteger(s)?{priority:s}:{}}}),i=a.issueCreate?.issue;if(!a.issueCreate?.success||!i)throw new Error("linear: issueCreate did not succeed");return{id:i.id,key:i.identifier,url:i.url,title:i.title}}var im={linear:{api:lc,config:ln},jira:{api:sc,config:cs}};async function hc(){let t=[];for(let[e,{api:n,config:r}]of Object.entries(im)){let o=await r();o&&t.push({name:e,api:n,cfg:o})}return t}async function fn(t){let e=im[t?.provider];if(!e)return null;let n=await e.config();return n?{name:t.provider,api:e.api,cfg:n}:null}function wc(t,e){if(!t||t.name)return t;let n=e.find(r=>r.id===t.id);return n?{...t,name:n.name}:t}var cm=()=>process.env.BOSTRAT_STATE_DIR||sm.join(process.env.HOME||pS.tmpdir(),".bostrat"),pc=()=>sm.join(cm(),"tickets.json"),lm=()=>(process.env.BOSTRAT_PUBLIC_URL||"http://bostrat.globopi").replace(/\/$/,""),fs=t=>`${lm()}/track/${t}`,ds=null,uc=null,dc=Promise.resolve();async function ps(){return ds||(uc??=Ur.readFile(pc(),"utf8").then(t=>JSON.parse(t)?.tickets??{}).catch(()=>({})).then(t=>ds=t).finally(()=>uc=null),uc)}async function tm(){let t=JSON.stringify({tickets:ds},null,2),e=`${pc()}.${process.pid}.tmp`;await Ur.mkdir(cm(),{recursive:!0}),await Ur.writeFile(e,t),await Ur.rename(e,pc())}async function Oe(){dc=dc.then(tm,tm),await dc.catch(()=>{})}async function te(t){return(await ps())[t]??null}async function um(){return{...await ps()}}async function dm(){return(await hc()).length>0}async function Yn(t,e,{source:n="api"}={}){let r=await hc();if(!r.length)throw new Error("no ticketing integration configured \u2014 add a linear or jira entry to ~/.bostrat/integrations.json");let o=null,s=null,a=null;for(let u of r){let d=u.api.parseIssueRef(e);if(d)try{s=await u.api.resolveIssue(u.cfg,d),o=u;break}catch(f){a=f}}if(!o)throw a||new Error(`"${e}" is not a Linear or Jira issue URL or key (ABC-123)`);let i=(t.boxes||[]).find(u=>u.name===t.leadBox),c={provider:o.name,key:s.key,issueId:s.issueId,url:s.url,title:s.title,state:s.state,source:n,linkedAt:Date.now(),track:{name:t.name,branch:t.workspace?.branch??null,leadAgent:i?.agent??null,cwd:t.cwd??null},pr:null};(await ps())[t.id]=c,await Oe(),await R({id:t.id,cwd:t.cwd},{type:"ticket.linked",actor:"user",source:"orchestrator",payload:{ts:Date.now(),provider:c.provider,key:c.key,url:c.url,title:c.title,linkSource:n}}).catch(()=>{}),await Fr(t.id,c);let l=[];return c.track.branch&&l.push(`\`${c.track.branch}\``),c.track.leadAgent&&l.push(`lead ${c.track.leadAgent}`),await Lr(c,`**Bostrat** \u2014 Track **${c.track.name}** linked${l.length?` (${l.join(", ")})`:""}.`),await mm(t.id,c,"linked"),c}async function gc(t){let e=await ps();return e[t]?(delete e[t],await Oe(),!0):!1}var mc=t=>`$${t>=100?Math.round(t):t.toFixed(2)}`;async function fm(t,e){if(!t.track?.cwd)return null;let n=us(await B({id:e,cwd:t.track.cwd}));return!n.totalUsd||n.totalUsd<.01?null:n}function mS(t){let e=Object.entries(t.byProvider).sort((r,o)=>o[1].usd-r[1].usd).map(([r,o])=>`${r} ${mc(o.usd)}`).join(" \xB7 "),n=Object.keys(t.byBox).length;return`${mc(t.totalUsd)}${e?` (${e})`:""}${n>1?` across ${n} boxes`:""}`}function hS(t,e){let n=[];return t.track.leadAgent&&n.push(`lead ${t.track.leadAgent}`),t.track.branch&&n.push(t.track.branch),t.pr?.number&&n.push(`PR #${t.pr.number} ${t.pr.change==="merged"?"merged":"open"}`),e&&n.push(mc(e.totalUsd)),n.join(" \xB7 ")||"linked"}async function Fr(t,e){let n=await fn(e);if(!n)return;let r=await fm(e,t).catch(()=>null);await n.api.upsertCard(n.cfg,e,{url:fs(t),title:`Bostrat Track: ${e.track.name}`,subtitle:hS(e,r)})}function ms(t){let e=new URL(lm());return`${e.protocol}//${t}.${e.host}/`}var wS={linked:"linked",opened:"prOpened",merged:"prMerged"};function gS(t,e,n){let r=o=>e.filter(s=>s.type===o).sort((s,a)=>s.position-a.position);if(n==="linked"){if(["triage","backlog","unstarted"].includes(t?.type))return r("started")[0]??null}else if(n==="opened"){if(t?.type!=="completed")return r("started").find(o=>/review/i.test(o.name))??null}else if(n==="merged"&&t?.type!=="completed")return r("completed")[0]??null;return null}async function pm(t,e,{from:n,to:r,mode:o,phase:s=null,actor:a="user"}){e.state=r,await Oe(),await R({id:t,cwd:e.track?.cwd},{type:"ticket.state.changed",actor:a,source:"orchestrator",payload:{ts:Date.now(),key:e.key,url:e.url,from:n??null,to:r,mode:o,...s?{phase:s}:{}}}).catch(()=>{})}async function mm(t,e,n){let r=await fn(e);if(!r)return;let o=r.cfg.ticketFlow?.[wS[n]]??"prompt";if(o==="off")return;let{current:s,states:a}=await r.api.issueStates(r.cfg,e),i=wc(s,a),c=gS(i,a,n);if(!c||c.id===i?.id)return;let l=i?.name??e.state??null;if(o==="auto"){await r.api.moveIssue(r.cfg,e,c.id),await pm(t,e,{from:l,to:c.name,mode:"auto",phase:n,actor:"system"});return}await R({id:t,cwd:e.track?.cwd},{type:"ticket.move.offered",actor:"system",source:"orchestrator",payload:{ts:Date.now(),offerId:am(),provider:e.provider,key:e.key,url:e.url,phase:n,from:l,to:{id:c.id,name:c.name,type:c.type},...e.pr?.number?{prNumber:e.pr.number}:{}}}).catch(()=>{})}async function hm(t){let e=await te(t);if(!e)throw new Error("no ticket linked to this Track");let n=await fn(e);if(!n)throw new Error("no ticketing integration configured");let{current:r,states:o}=await n.api.issueStates(n.cfg,e),s=wc(r,o);return s?.name&&e.state!==s.name&&(e.state=s.name,await Oe()),{key:e.key,url:e.url,current:s?{id:s.id,name:s.name??null,type:s.type??null}:null,states:[...o].sort((a,i)=>a.position-i.position).map(({id:a,name:i,type:c,position:l})=>({id:a,name:i,type:c,position:l}))}}async function yc(t,e,{mode:n="manual"}={}){if(!e)throw new Error("stateId required");let r=await te(t);if(!r)throw new Error("no ticket linked to this Track");let o=await fn(r);if(!o)throw new Error("no ticketing integration configured");let{current:s,states:a}=await o.api.issueStates(o.cfg,r),i=wc(s,a),c=a.find(u=>u.id===e);if(!c)throw new Error("unknown state for this ticket's team");let l=i?.name??r.state??null;return i?.id===c.id?(r.state!==c.name&&(r.state=c.name,await Oe()),{key:r.key,from:l,to:c.name,moved:!1}):(await o.api.moveIssue(o.cfg,r,c.id),await pm(t,r,{from:l,to:c.name,mode:n}),{key:r.key,from:l,to:c.name,moved:!0})}async function wm(t,e){let n=await te(t);if(!n)throw new Error("no ticket linked to this Track");let r=await fn(n);if(!r)throw new Error("no ticketing integration configured");let o=String(e||"").trim();if(!o)throw new Error("comment body required");let s=await r.api.createComment(r.cfg,n,o);return await R({id:t,cwd:n.track?.cwd},{type:"ticket.commented",actor:"user",source:"orchestrator",payload:{ts:Date.now(),key:n.key,url:s??n.url,excerpt:o.length>280?`${o.slice(0,279)}\u2026`:o}}).catch(()=>{}),{key:n.key,url:s??n.url}}async function gm(t,e,n){if(!e)throw new Error("offerId required");let r=await B({id:t.id,cwd:t.cwd}),o=r.find(a=>a.type==="ticket.move.offered"&&a.payload?.offerId===e);if(!o)throw new Error("unknown offer");if(r.some(a=>a.type==="ticket.move.answered"&&a.payload?.offerId===e))throw new Error("offer already answered");let s=null;return n&&(s=await yc(t.id,o.payload.to?.id,{mode:"offer"})),await R({id:t.id,cwd:t.cwd},{type:"ticket.move.answered",actor:"user",source:"orchestrator",payload:{ts:Date.now(),offerId:e,action:n?"moved":"dismiss",...s?{from:s.from,to:s.to}:{}}}),{offerId:e,action:n?"moved":"dismiss",...s??{}}}var em=50;async function ym(){let t=await ln();if(!t)throw new Error("no ticketing integration configured");return Xn(t)}function yS(t){let e=t&&typeof t=="object"?t:{},n=(Array.isArray(e.issues)?e.issues:[]).map(o=>o&&typeof o=="object"?o:{title:String(o||"")}).map(o=>({title:String(o.title||"").trim(),description:o.description?String(o.description):null,priority:Number.isInteger(o.priority)&&o.priority>=0&&o.priority<=4?o.priority:null})).filter(o=>o.title),r=e.epic&&String(e.epic.name||"").trim()?{name:String(e.epic.name).trim(),description:e.epic.description?String(e.epic.description):null}:null;if(!n.length&&!r)throw new Error("a plan needs an epic or at least one issue");if(n.length>em)throw new Error(`too many issues (${n.length}); cap is ${em} per proposal`);return{epic:r,issues:n,team:e.team?String(e.team).trim():null,linkCurrentTrack:e.linkCurrentTrack===!0}}async function bS(t,e,n,r){let o=r??await Xn(t),s=i=>o.find(c=>c.id===i||c.key.toLowerCase()===String(i).toLowerCase()||c.name.toLowerCase()===String(i).toLowerCase())??null;if(e.team){let i=s(e.team);if(i)return i}let a=await te(n.id);if(a?.key){let i=a.key.split("-")[0],c=o.find(l=>l.key===i);if(c)return c}if(t.defaultTeam){let i=s(t.defaultTeam);if(i)return i}return o.length===1?o[0]:null}async function bm(t,e){let n=await ln();if(!n)throw new Error("no ticketing integration configured \u2014 add a linear apiKey to ~/.bostrat/integrations.json");if(n.ticketFlow?.create==="off")throw new Error("ticket creation offers are off (bostrat-ticket flow set create=prompt to re-enable)");let r=yS(e),o=await Xn(n),s=await bS(n,r,t,o),a=s?{id:s.id,key:s.key,name:s.name}:null,i=am();return await R({id:t.id,cwd:t.cwd},{type:"ticket.create.offered",actor:"system",source:"orchestrator",payload:{ts:Date.now(),proposalId:i,provider:"linear",epic:r.epic,issues:r.issues,team:a,needsTeam:!s,linkCurrentTrack:r.linkCurrentTrack}}),{proposalId:i,epic:r.epic,issues:r.issues,team:a,needsTeam:!s}}async function km(t,e,n,{teamId:r=null}={}){if(!e)throw new Error("proposalId required");let o=await B({id:t.id,cwd:t.cwd}),s=o.find(m=>m.type==="ticket.create.offered"&&m.payload?.proposalId===e);if(!s)throw new Error("unknown proposal");if(o.some(m=>m.type==="ticket.create.answered"&&m.payload?.proposalId===e))throw new Error("proposal already answered");if(!n)return await R({id:t.id,cwd:t.cwd},{type:"ticket.create.answered",actor:"user",source:"orchestrator",payload:{ts:Date.now(),proposalId:e,action:"dismiss"}}),{proposalId:e,action:"dismiss"};let a=await ln();if(!a)throw new Error("no ticketing integration configured");let i=s.payload,c=i.team??null;if(!c&&r){let m=(await Xn(a)).find(g=>g.id===r);if(!m)throw new Error("unknown team");c={id:m.id,key:m.key,name:m.name}}if(!c)throw new Error("a team is required to create \u2014 pick one");let l=[],u=[],d=null;if(i.epic){let m=await ic(a,{teamId:c.id,name:i.epic.name,description:i.epic.description});d=m.id,l.push({kind:"epic",title:m.name,url:m.url})}for(let m of i.issues||[])try{let g=await cc(a,{teamId:c.id,title:m.title,description:m.description,projectId:d,priority:m.priority});l.push({kind:"issue",key:g.key,title:g.title,url:g.url})}catch(g){u.push({title:m.title,message:g?.message||String(g)})}await R({id:t.id,cwd:t.cwd},{type:"ticket.created",actor:"user",source:"orchestrator",payload:{ts:Date.now(),proposalId:e,team:{key:c.key,name:c.name},items:l,failed:u}}),await R({id:t.id,cwd:t.cwd},{type:"ticket.create.answered",actor:"user",source:"orchestrator",payload:{ts:Date.now(),proposalId:e,action:"created",createdCount:l.length,failedCount:u.length}});let f=l.filter(m=>m.kind==="issue"),w=null;return i.linkCurrentTrack&&f.length===1&&!await te(t.id)&&(w=await Yn(t,f[0].key,{source:"created"}).catch(()=>null)),{proposalId:e,action:"created",created:l,failed:u,linked:w?w.key:null}}async function Lr(t,e){let n=await fn(t);n&&await n.api.createComment(n.cfg,t,e)}async function kS(t,e,n){let r=await fn(t);if(!r)return null;let o=await Ae(e);if(!o)return null;let s=rn(o,n);if(!s)return null;let a=await Ur.readFile(Dt(o,s.v).png).catch(()=>null);return a?r.api.uploadImage(r.cfg,{issue:t.key,filename:`${o.name.replace(/[^\w.-]+/g,"-")}.v${s.v}.png`,contentType:"image/png",buffer:a}):null}var nm=()=>Number(process.env.BOSTRAT_TICKET_COST_SYNC_MS??12e4),fc=new Map;function xS(t,e){if(nm()<=0)return Fr(t,e);if(fc.has(t))return;let n=setTimeout(()=>{fc.delete(t),te(t).then(r=>r?Fr(t,r):null).catch(()=>{})},nm());n.unref?.(),fc.set(t,n)}var rm=new Map;async function om(t,e,n){let r=rm.get(t)??0;Date.now()-r<600*1e3||(rm.set(t,Date.now()),await R({id:t,cwd:e},{type:"ticket.sync.failed",actor:"system",source:"orchestrator",payload:{ts:Date.now(),message:n?.message||String(n)}}).catch(()=>{}))}async function SS(t,{resolveTrack:e=null}={}){let n=await te(t.trackId),r=t.payload||{};if(!n){if(t.type!=="turn.user"||t.actor!=="user")return;let o=null;for(let a of await hc())if(o=a.api.findIssueUrl(r.text,a.cfg),o)break;if(!o)return;let s=e?await e(t.trackId):null;if(!s)return;try{await Yn(s,o,{source:"chat"})}catch(a){await om(t.trackId,s.cwd,a)}return}if(!n.track.cwd&&e){let o=await e(t.trackId).catch(()=>null);o?.cwd&&(n.track.cwd=o.cwd,await Oe())}try{switch(t.type){case"handoff.done":n.track.leadAgent=r.toAgent??n.track.leadAgent,await Oe(),await Fr(t.trackId,n);break;case"pr.state.changed":{if(!r.change)break;if(r.number&&(n.pr={number:r.number,url:r.url??n.pr?.url??null,change:r.change}),await Oe(),await Fr(t.trackId,n),r.change==="opened"||r.change==="merged"){let o=r.change==="merged"?await fm(n,t.trackId).catch(()=>null):null;await Lr(n,`**Bostrat** \u2014 PR ${r.change} for **${n.track.name}**${r.url?`: ${r.url}`:r.number?` (#${r.number})`:""}`+(o?`
|
|
223
238
|
|
|
224
|
-
Agent cost for this Track: ${
|
|
239
|
+
Agent cost for this Track: ${mS(o)}.`:"")),await mm(t.trackId,n,r.change)}break}case"mock.state.changed":{if(r.change==="ready"&&r.mockId){let o=await kS(n,r.mockId,r.version??null);if(!o)break;let s=(r.version??1)>1?` revised (v${r.version})`:"";await Lr(n,`**Bostrat** \u2014 mock **${r.name}**${s}${r.group?` (group \`${r.group}\`)`:""}:
|
|
225
240
|
|
|
226
|
-
`)}else if(r.change==="picked"){let o=(r.siblings??[]).map(s=>`**${s.name}**`).join(", ");await
|
|
227
|
-
_(runs on the Track's branch; reachable on the operator's private network only)_`);break}case"cost.recorded":await
|
|
228
|
-
${n}`}},{type:"section",text:{type:"mrkdwn",text:r}},{type:"context",elements:[{type:"mrkdwn",text:`<${
|
|
241
|
+
`)}else if(r.change==="picked"){let o=(r.siblings??[]).map(s=>`**${s.name}**`).join(", ");await Lr(n,`**Bostrat** \u2014 design decision: **${r.name}** picked${o?` over ${o}`:""}.`)}break}case"view.state.changed":{if(r.change!=="created"||r.restarted||!r.slug)break;await Lr(n,`**Bostrat** \u2014 live preview **${r.name}**: ${ms(r.slug)}
|
|
242
|
+
_(runs on the Track's branch; reachable on the operator's private network only)_`);break}case"cost.recorded":await xS(t.trackId,n);break;default:break}}catch(o){let s=e?await e(t.trackId).catch(()=>null):null;await om(t.trackId,s?.cwd??null,o)}}function xm({resolveTrack:t}){Nt.on("event",e=>{SS(e,{resolveTrack:t}).catch(()=>{})})}import vS from"ws";var Sm=()=>process.env.BOSTRAT_SLACK_API_BASE||"https://slack.com/api";async function hs(t,e,n={}){let r=await fetch(`${Sm()}/${t}`,{method:"POST",headers:{authorization:`Bearer ${e}`,"content-type":"application/json; charset=utf-8"},body:JSON.stringify(n)}),o=await r.json().catch(()=>null);if(!o?.ok)throw new Error(`slack ${t}: ${o?.error||`http ${r.status}`}`);return o}var bc=(t,e)=>hs("chat.postMessage",t,e),Zn=(t,e)=>hs("chat.update",t,e);async function Et(t,e){await fetch(t,{method:"POST",headers:{"content-type":"application/json; charset=utf-8"},body:JSON.stringify(e)}).catch(()=>{})}async function TS(t,e,n={}){let r=await fetch(`${Sm()}/${t}`,{method:"POST",headers:{authorization:`Bearer ${e}`,"content-type":"application/x-www-form-urlencoded; charset=utf-8"},body:new URLSearchParams(n).toString()}),o=await r.json().catch(()=>null);if(!o?.ok)throw new Error(`slack ${t}: ${o?.error||`http ${r.status}`}`);return o}async function vm(t,{filename:e,buffer:n,title:r,channelId:o,threadTs:s,initialComment:a}){let{upload_url:i,file_id:c}=await TS("files.getUploadURLExternal",t,{filename:e,length:String(n.length)}),l=await fetch(i,{method:"POST",body:n});if(!l.ok)throw new Error(`slack upload: http ${l.status}`);return hs("files.completeUploadExternal",t,{files:[{id:c,title:r}],channel_id:o,thread_ts:s,...a?{initial_comment:a}:{}})}var AS=2200;function Tm({getToken:t,onEnvelope:e,log:n=()=>{}}){let r=!1,o=null,s=null,a=1e3;async function i(){if(r)return;let l;try{let u=await t();if(!u)throw new Error("app token missing");({url:l}=await hs("apps.connections.open",u))}catch(u){return n(`connections.open failed: ${u?.message||u}`),c()}o=new vS(l),o.on("message",u=>{let d;try{d=JSON.parse(u)}catch{return}if(d.type==="hello"){a=1e3,n("socket connected");return}if(d.type==="disconnect"){try{o.close()}catch{}return}if(!d.envelope_id)return;let f=g=>{try{o.send(JSON.stringify(g?{envelope_id:d.envelope_id,payload:g}:{envelope_id:d.envelope_id}))}catch{}},w=!1,m=setTimeout(()=>{w=!0,f()},AS);Promise.resolve().then(()=>e(d)).then(g=>{clearTimeout(m),!w&&(w=!0,f(d.accepts_response_payload?g:void 0))},g=>{clearTimeout(m),w||(w=!0,f()),n(`envelope handler failed: ${g?.message||g}`)})}),o.on("close",c),o.on("error",u=>{n(`socket error: ${u?.message||u}`);try{o.close()}catch{}})}function c(){if(r||s)return;let l=a;a=Math.min(a*2,6e4),s=setTimeout(()=>{s=null,i()},l)}return i(),()=>{r=!0,s&&clearTimeout(s);try{o?.close()}catch{}}}var Em=()=>process.env.BOSTRAT_STATE_DIR||gs.join(process.env.HOME||ES.tmpdir(),".bostrat"),Tc=()=>gs.join(Em(),"slack.json"),ws=null,kc=null,xc=Promise.resolve();async function _c(){return ws||(kc??=Hr.readFile(Tc(),"utf8").then(t=>JSON.parse(t)?.threads??{}).catch(()=>({})).then(t=>ws=t).finally(()=>kc=null),kc)}async function Am(){let t=JSON.stringify({threads:ws},null,2),e=`${Tc()}.${process.pid}.tmp`;await Hr.mkdir(Em(),{recursive:!0}),await Hr.writeFile(e,t),await Hr.rename(e,Tc())}async function le(){xc=xc.then(Am,Am),await xc.catch(()=>{})}async function Ec(t){return(await _c())[t]??null}async function IS(t,e){let n=await _c();for(let[r,o]of Object.entries(n))if(o.channel===t&&o.ts===e)return{trackId:r,rec:o};return null}var F=t=>String(t??"").replace(/&/g,"&").replace(/</g,"<").replace(/>/g,">");function Wr(t,e){let n=String(t??"").trim();return n.length<=e?n:`${n.slice(0,e-1).trimEnd()}\u2026`}function RS(t,e=500){let n=String(t??"").trim().split(/\n\s*\n/)[0]??"";return Wr(n.replace(/\n+/g," "),e)}function Im(t){let e=`*${F(t.name)}*${t.archivedAt?" \xB7 _archived_":""}`,n=`${F(t.repoLabel)} \xB7 \`${F(t.branch||"?")}\` \xB7 lead *${F(t.lead)}*`,r=t.kickoff?`> ${F(Wr(t.kickoff,300))}`:"_no kickoff yet \u2014 reply in this thread to steer the lead_";return[{type:"section",text:{type:"mrkdwn",text:`${e}
|
|
243
|
+
${n}`}},{type:"section",text:{type:"mrkdwn",text:r}},{type:"context",elements:[{type:"mrkdwn",text:`<${fs(t.trackId)}|open the Track> \xB7 reachable on the operator's private network only`}]}]}async function Ac(t,e){await Zn(t.botToken,{channel:e.channel,ts:e.ts,text:`Track: ${e.name}`,blocks:Im(e)})}async function mn(t,e,n,r=void 0){return bc(t.botToken,{channel:e.channel,thread_ts:e.ts,text:n,...r?{blocks:r}:{},unfurl_links:!1})}var _m=new Set;function pn(t){_m.add(t),t.catch(()=>{}).finally(()=>_m.delete(t))}var et=t=>({response_type:"ephemeral",text:t}),$S="*/bostrat* \u2014 `start <name> [repo]` \xB7 `archive <track>` \xB7 `handoff <track> <agent>` \xB7 `stop <track>` \xB7 `list`\n_Reply in a Track's thread to steer its lead; question prompts and ship offers appear there as buttons._",OS=["claude","cursor","codex"];function Sc(t,e){let n=(e||"").toLowerCase(),r=t.filter(s=>!s.archivedAt),o=r.filter(s=>s.name.toLowerCase().startsWith(n));return o.sort((s,a)=>(a.lastResponseAt??a.createdAt??0)-(s.lastResponseAt??s.createdAt??0)),{match:o[0]??null,live:r}}var vc=(t,e)=>et(`No live Track matches \u201C${F(e)}\u201D. Live: ${t.map(n=>`\`${F(n.name)}\``).join(" \xB7 ")||"none"}`);async function Rm(t,e,{name:n,repo:r,responseUrl:o}){let s=t.deps;try{let a=(await s.listTracks()).filter(d=>!d.archivedAt);if(a.length>=e.maxConcurrent){await Et(o,{...et(`At capacity \u2014 ${a.length} live Tracks (limit ${e.maxConcurrent}). Archive or stop something first.`),replace_original:!0});return}if(!e.channel){await Et(o,{...et("No designated channel configured \u2014 set `slack.channel` in integrations.json."),replace_original:!0});return}let i=await s.createTrack({name:n,...r?{repo:r}:{}}),c=i.workspace||{},l={trackId:i.id,channel:e.channel,name:i.name,branch:c.branch||null,repoLabel:c.repo||(c.base?gs.basename(c.base):"fresh workspace"),lead:"claude"},u=await bc(e.botToken,{channel:e.channel,text:`Track started: ${l.name}`,blocks:Im(l),unfurl_links:!1});l.ts=u.ts,(await _c())[i.id]=l,await le(),await Et(o,{...et(`\u2713 *${F(l.name)}* started \u2014 root message posted.`),replace_original:!0})}catch(a){await Et(o,{...et(`Start failed: ${a?.message||a}`),replace_original:!0})}}async function CS(t,e,{track:n,responseUrl:r}){let o=t.deps;try{await o.archiveTrack(n.id);let s=await Ec(n.id);s&&(s.archivedAt=Date.now(),await le(),await Ac(e,s).catch(()=>{})),await Et(r,{...et(`\u2713 *${F(n.name)}* archived.`),replace_original:!0})}catch(s){await Et(r,{...et(`Archive failed: ${s?.message||s}`),replace_original:!0})}}async function MS(t,e,{track:n,agent:r,responseUrl:o}){try{await t.deps.startLeadHandoff(n.id,r),await Et(o,{...et(`\u2713 Handing *${F(n.name)}* to ${F(r)} \u2014 the root message updates when the baton lands.`),replace_original:!0})}catch(s){await Et(o,{...et(`Handoff failed: ${s?.message||s}`),replace_original:!0})}}async function PS(t,e,{track:n,responseUrl:r}){let o=t.deps;try{await o.interruptLead(n.id);let s=(o.listSubtaskJobs?.(n.id)??[]).filter(a=>!a.finishedAt);for(let a of s)try{o.cancelSubtask(a.id)}catch{}await Et(r,{...et(`\u2713 Stopped *${F(n.name)}*${s.length?` (+ ${s.length} subtask${s.length>1?"s":""} canceled)`:""} \u2014 Track parked, resume from the Track page.`),replace_original:!0})}catch(s){await Et(r,{...et(`Stop failed: ${s?.message||s}`),replace_original:!0})}}function NS(t,e){let n=[...e.slice(0,99).map(r=>({text:{type:"plain_text",text:Wr(`${r.name} (${r.branch})`,75)},value:r.path})),{text:{type:"plain_text",text:"fresh workspace"},value:"__fresh__"}];return[{type:"section",block_id:JSON.stringify({name:t}),text:{type:"mrkdwn",text:`Repo for *${F(t)}*:`},accessory:{type:"static_select",action_id:"bostrat-start-repo",placeholder:{type:"plain_text",text:"Choose a repo"},options:n}}]}async function BS(t){let e=await t.listTracks(),n=e.filter(s=>!s.archivedAt);if(n.sort((s,a)=>(a.lastResponseAt??a.createdAt??0)-(s.lastResponseAt??s.createdAt??0)),!n.length)return"No live Tracks.";let r=n.map(s=>{let a=s.workspace||{},i=a.repo||(a.base?gs.basename(a.base):"fresh"),c=[...s.boxes||[]].reverse().find(u=>u.role==="lead")?.agent||"?",l=s.prompted?"\u26A0 needs an answer":s.working?"working":"idle";return`\u2022 *${F(s.name)}* \u2014 ${F(i)}${a.branch?` \`${F(a.branch)}\``:""} \xB7 lead ${F(c)} \xB7 ${l}`}),o=e.length-n.length;return r.join(`
|
|
229
244
|
`)+(o?`
|
|
230
|
-
_${o} archived_`:"")}async function
|
|
231
|
-
\u2713 *${
|
|
232
|
-
\u2713 _answered_`}}]})}async function
|
|
233
|
-
_runs on the Track's branch; reachable on the operator's private network only_`}}]);return}case"mock.state.changed":return
|
|
234
|
-
`)){let r=n.trim();if(!r||r[0]!=="{")continue;let o;try{o=JSON.parse(r)}catch{continue}let s=o?.message?.usage;if(!s)continue;let a={input:s.input_tokens||0,output:s.output_tokens||0,cacheWrite:s.cache_creation_input_tokens||0,cacheRead:s.cache_read_input_tokens||0};if(a.input+a.output+a.cacheWrite+a.cacheRead===0)continue;let i=o.message?.id||o.uuid||null;i&&e.set(i,{messageId:i,tokens:a,model:o.message?.model||o.model||null})}return[...e.values()]}function jS(t){let e=new Set;for(let n of t)if(n.type==="cost.recorded"){n.payload?.messageId&&e.add(n.payload.messageId);for(let r of n.payload?.messageIds||[])e.add(r)}return e}function LS(t,e){return t?.transcript_path?t.transcript_path:e?.cwd&&e?.sessionId?ne(e.cwd,e.sessionId):e?.cwd?Fs(e.cwd):null}async function vm(t,e){let n=await e(t?.session_id,t?.cwd);if(!n?.track)return[];let{track:r,box:o}=n,s={boxId:o?.name||null,provider:"claude",source:"hook"},a=[],i=async c=>{a.push(await R(r,{...s,...c}))};switch(t?.hook_event_name){case"SessionStart":await i({type:"session.ready",actor:"system",payload:{sessionId:t.session_id}});break;case"UserPromptSubmit":await i({type:"turn.user",actor:"user",payload:{promptHead:String(t.prompt??t.user_prompt??"").slice(0,160)||null}});break;case"PostToolUse":await i({type:"tool.called",actor:"agent",payload:{tool:t.tool_name??null}});break;case"Stop":await US(r,o,t,i);break;default:break}return a}async function US(t,e,n,r){let o=[],s=LS(n,e);if(s)try{o=DS(await BS.readFile(s,"utf8"))}catch{}let a=o.length?o[o.length-1]:null;if(await r({type:"turn.agent",actor:"agent",payload:{model:a?.model??e?.model??null,messageId:a?.messageId??null}}),!o.length)return;let i=jS(await P(t)),c=o.filter(u=>!i.has(u.messageId));if(c.length){let u=new Map;for(let d of c){let f=d.model||"unknown",w=u.get(f)||{tokens:{input:0,output:0,cacheWrite:0,cacheRead:0},messageIds:[]};for(let h of Object.keys(w.tokens))w.tokens[h]+=d.tokens[h];w.messageIds.push(d.messageId),u.set(f,w)}for(let[d,f]of u)await r({type:"cost.recorded",actor:"system",source:"transcript",payload:{model:d,usd:Kt(d,f.tokens),tokens:f.tokens,messageId:f.messageIds[f.messageIds.length-1],messageIds:f.messageIds,apiCalls:f.messageIds.length}})}let l=a.tokens.input+a.tokens.cacheWrite+a.tokens.cacheRead;await r({type:"context.usage",actor:"system",source:"transcript",payload:{model:a.model,occupancy:l,tokens:a.tokens}})}import FS from"node:fs";import kc from"node:fs/promises";import HS from"node:http";import Xn from"node:path";import ws from"node:os";import{createRequire as WS}from"node:module";import xc from"ws";var hs=(()=>{try{return WS(import.meta.url)("../package.json").version||null}catch{return null}})(),qS=process.env.BOSTRAT_STATE_DIR||Xn.join(process.env.HOME||ws.tmpdir(),".bostrat");function GS(t,e){let n=String(t).split(".").map(Number),r=String(e).split(".").map(Number);if(n.some(Number.isNaN)||r.some(Number.isNaN))return!1;for(let o=0;o<Math.max(n.length,r.length);o++){let s=(n[o]||0)-(r[o]||0);if(s)return s<0}return!1}function KS({home:t=process.env.HOME||ws.homedir(),env:e=process.env}={}){let n=r=>{try{return FS.statSync(r),!0}catch{return!1}};return{claude:n(Xn.join(t,".claude",".credentials.json")),codex:n(Xn.join(e.CODEX_HOME||Xn.join(t,".codex"),"auth.json"))}}var xt=1,Tm=1e3,VS=3e4,Am=100,JS=2e3,zS=25e3;function _m({url:t,token:e,nodeId:n=ws.hostname(),name:r=ws.hostname(),listTracks:o,selfUrl:s=`http://127.0.0.1:${process.env.PORT||3362}`,log:a=console}){if(!t||!e)throw new Error("plane-link: url and token are required");if(typeof o!="function")throw new Error("plane-link: listTracks is required");let i=s.replace(/^http/,"ws"),c=new Map,l=new Map,u=Xn.join(qS,"plane-link","cursor.json"),d={},f=new Map,w=null,h=!1,g=!1,x=Tm,A=null,S=null,v=null,E=!1;async function J(){try{d=JSON.parse(await kc.readFile(u,"utf8"))}catch{d={}}}function I(){E=!0,!S&&(S=setTimeout(async()=>{if(S=null,!!E){E=!1;try{await kc.mkdir(Xn.dirname(u),{recursive:!0}),await kc.writeFile(u,JSON.stringify(d))}catch(N){a.error("[plane-link] cursor flush failed:",N?.message||N)}}},JS),S.unref?.())}function M(N){return!w||w.readyState!==xc.OPEN?!1:(w.send(JSON.stringify(N)),!0)}function m(N){for(let{trackId:H,event:_}of N)f.set(_.id,{trackId:H,seq:_.seq});return M({v:xt,ch:"events",type:"append",events:N})}async function p(){let N;try{N=await o()}catch(H){a.error("[plane-link] backfill listTracks failed:",H?.message||H);return}M({v:xt,ch:"ctl",type:"tracks",ids:N.map(H=>H.id).filter(H=>typeof H=="string")});for(let H of N){if(!h)return;let _;try{_=await P(H)}catch{continue}let W=d[H.id]??0,X=_.filter(lt=>lt.seq>W&&typeof lt.id=="string");for(let lt=0;lt<X.length;lt+=Am){let tt=X.slice(lt,lt+Am).map(wt=>({trackId:H.id,event:wt}));if(!m(tt))return}}}function b(N){!h||!N?.trackId||typeof N.id!="string"||m([{trackId:N.trackId,event:N}])}let T=new Map;async function ot(N){let{reqId:H,method:_,path:W,headers:X={},body:lt}=N,tt=Object.fromEntries(Object.entries(X).filter(([,Ct])=>Ct!=null)),wt=new AbortController;T.set(H,wt);try{if(N.host){await $(N,tt,wt);return}let Ct=await fetch(s+W,{method:_,headers:tt,body:lt?Buffer.from(lt,"base64"):void 0,signal:wt.signal});if(M({v:xt,ch:"control",type:"http_head",reqId:H,status:Ct.status,headers:{"content-type":Ct.headers.get("content-type")||"application/octet-stream"}}),Ct.body){for await(let De of Ct.body)if(!M({v:xt,ch:"control",type:"http_data",reqId:H,data:Buffer.from(De).toString("base64")}))break}M({v:xt,ch:"control",type:"http_end",reqId:H,error:null})}finally{T.delete(H)}}function $(N,H,_){let{reqId:W,method:X,path:lt,body:tt}=N,wt=new URL(s);return new Promise(Ct=>{let De=HS.request({host:wt.hostname,port:wt.port,method:X,path:lt,headers:{...H,host:N.host},signal:_.signal},Dt=>{let{connection:y,"keep-alive":B,"transfer-encoding":dn,upgrade:zr,...je}=Dt.headers;M({v:xt,ch:"control",type:"http_head",reqId:W,status:Dt.statusCode||502,headers:je}),Dt.on("data",Is=>{M({v:xt,ch:"control",type:"http_data",reqId:W,data:Buffer.from(Is).toString("base64")})||Dt.destroy()}),Dt.on("end",()=>{M({v:xt,ch:"control",type:"http_end",reqId:W,error:null}),Ct()}),Dt.on("error",()=>{M({v:xt,ch:"control",type:"http_end",reqId:W,error:"view upstream error"}),Ct()})});De.on("error",Dt=>{M({v:xt,ch:"control",type:"http_end",reqId:W,error:Dt?.message||"view request failed"}),Ct()}),tt&&De.write(Buffer.from(tt,"base64")),De.end()})}function z(){if(g)return;w=new xc(t,{headers:{authorization:`Bearer ${e}`}}),w.on("open",()=>{M({v:xt,ch:"ctl",type:"hello",nodeId:n,name:r,platform:process.platform,version:hs,providers:KS()})}),w.on("message",H=>{let _;try{_=JSON.parse(H)}catch{return}if(_.ch==="ctl"&&_.type==="hello_ok"){h=!0,x=Tm,a.log(`[plane-link] connected to plane as ${n}`),_.minNodeVersion&&hs&&GS(hs,_.minNodeVersion)&&a.error(`[plane-link] this node is bostrat-node ${hs}; the plane asks for \u2265${_.minNodeVersion} \u2014 update with: npm install -g bostrat-node`),v&&clearInterval(v),v=setInterval(()=>M({v:xt,ch:"ctl",type:"ping"}),zS),v.unref?.(),p().catch(W=>a.error("[plane-link] backfill failed:",W?.message||W));return}if(_.ch==="events"&&_.type==="ack"&&Array.isArray(_.ids)){for(let W of _.ids){let X=f.get(W);X&&(f.delete(W),(d[X.trackId]??0)<X.seq&&(d[X.trackId]=X.seq))}I();return}if(_.ch==="control"&&_.type==="http_open"){ot(_).catch(W=>M({v:xt,ch:"control",type:"http_end",reqId:_.reqId,error:W?.message||"tunnel error"}));return}if(_.ch==="control"&&_.type==="http_cancel"){T.get(_.reqId)?.abort();return}if(_.ch==="attach"&&_.type==="open"){let W=new xc(i+_.path,Array.isArray(_.protocols)?_.protocols:[],_.host?{headers:{host:_.host}}:void 0);c.set(_.streamId,W),l.set(_.streamId,[]),W.on("open",()=>{let lt=l.get(_.streamId)||[];l.delete(_.streamId);for(let{buf:tt,bin:wt}of lt)W.send(tt,{binary:wt})}),W.on("message",(lt,tt)=>{let wt=tt?lt:Buffer.from(String(lt));M({v:xt,ch:"attach",type:"data",streamId:_.streamId,data:wt.toString("base64"),...tt?{bin:!0}:{}})});let X=()=>{l.delete(_.streamId),c.delete(_.streamId)&&M({v:xt,ch:"attach",type:"close",streamId:_.streamId})};W.on("close",X),W.on("error",X);return}if(_.ch==="attach"&&_.type==="data"){let W=c.get(_.streamId);if(!W)return;let X=Buffer.from(_.data,"base64"),lt=_.bin===!0,tt=l.get(_.streamId);tt?tt.push({buf:X,bin:lt}):W.send(X,{binary:lt});return}if(_.ch==="attach"&&_.type==="close"){let W=c.get(_.streamId);c.delete(_.streamId),l.delete(_.streamId),W?.close();return}});let N=()=>{h&&a.log("[plane-link] disconnected from plane"),h=!1,v&&(clearInterval(v),v=null),f.clear();for(let H of c.values())H.close();c.clear(),l.clear();for(let H of T.values())H.abort();T.clear(),!(g||A)&&(A=setTimeout(()=>{A=null,z()},x),A.unref?.(),x=Math.min(x*2,VS))};w.on("close",N),w.on("error",N)}return Mt.on("event",b),J().then(z),{stop:()=>{g=!0,Mt.off("event",b),A&&clearTimeout(A),S&&clearTimeout(S),v&&clearInterval(v),w?.close()},status:()=>({connected:h,nodeId:n,cursor:{...d},inflight:f.size})}}function XS(t){let e={};for(let n of t.boxes||[]){let r=n.role&&n.role!=="lead"?` \xB7 ${n.role}`:"";e[n.name]=`${n.name} (${n.agent||"?"}${r})`}return e}var Em=()=>({input:0,output:0,cacheRead:0,cacheCreation:0}),Im=(t,e)=>{for(let n of Object.keys(t))t[n]+=e?.[n]||0},Sc=t=>Math.round(t*1e4)/1e4;async function Rm(t){let e=[],n={},r={},o=0,s=0,a=0,i=0,c=new Set,l=Em(),u=0,d=[];for(let f of t||[]){let w=await P(f),h=is(w,{boxLabels:XS(f)});if(h.turns===0){f.archivedAt?u++:d.push(f.name||f.id);continue}o+=h.totalUsd,s+=h.turns,a+=h.repricedTurns||0,i+=h.unpricedTurns||0;for(let g of h.unpricedModels||[])c.add(g);Im(l,h.totalTokens);for(let[g,x]of Object.entries(h.byModel))n[g]||={usd:0,events:0,tokens:Em()},n[g].usd=Sc(n[g].usd+x.usd),n[g].events+=x.events,Im(n[g].tokens,x.tokens);for(let[g,x]of Object.entries(h.byProvider))r[g]||={usd:0,events:0},r[g].usd=Sc(r[g].usd+x.usd),r[g].events+=x.events;e.push({id:f.id,name:f.name,usd:h.totalUsd,rollup:h})}return e.sort((f,w)=>w.usd-f.usd),{generatedAt:new Date().toISOString(),pricingBasis:"api-equivalent (subscription usage priced at public API rates)",fleet:{totalUsd:Sc(o),turns:s,repricedTurns:a,unpricedTurns:i,unpricedModels:[...c],totalTokens:l,byModel:n,byProvider:r,tracksWithCost:e.length,tracksSilent:u+d.length,tracksSilentArchived:u,tracksSilentLive:d},tracks:e}}var Fr=t=>`$${t.toFixed(4)}`,Yn=(t,e)=>String(t).padEnd(e);function $m(t){let{fleet:e,tracks:n}=t,r=[];r.push(`BOSTRAT COST READ-OUT \xB7 ${t.generatedAt}`),t.pricingBasis&&r.push(`basis: ${t.pricingBasis}`);let o=e.tracksSilentLive||[];r.push(`fleet total ${Fr(e.totalUsd)} over ${e.turns} turn(s) \xB7 ${e.tracksWithCost} track(s) with cost, ${e.tracksSilentArchived??e.tracksSilent} silent (archived)`+(o.length?`, ${o.length} LIVE with no cost data: ${o.join(", ")}`:"")),e.repricedTurns&&r.push(`${e.repricedTurns} turn(s) imputed at current rates (recorded before their model was priced)`),e.unpricedTurns&&r.push(`\u26A0 ${e.unpricedTurns} turn(s) UNPRICED \u2014 total under-reports. Add PER_MTOK rate(s) for: ${(e.unpricedModels||[]).join(", ")}`),r.push(""),r.push("BY MODEL");let s=Object.entries(e.byModel).sort((a,i)=>i[1].usd-a[1].usd);s.length||r.push(" (no cost events yet \u2014 hooks emit on each turn of a newly started box)");for(let[a,i]of s){let c=i.tokens;r.push(` ${Yn(a,26)} ${Yn(Fr(i.usd),10)} ${i.events} turn(s) \xB7 in ${c.input} out ${c.output} cacheR ${c.cacheRead} cacheW ${c.cacheCreation}`)}r.push(""),r.push("BY TRACK / TASK");for(let a of n){r.push(` ${Yn(a.name||a.id,30)} ${Fr(a.usd)}`);let i=Object.values(a.rollup.byBox).sort((c,l)=>l.usd-c.usd);for(let c of i)if(r.push(` ${Yn(c.label,40)} ${Yn(Fr(c.usd),10)} ${c.models.join(",")||"?"}`),c.epochs)for(let[l,u]of Object.entries(c.epochs).sort((d,f)=>Number(d[0])-Number(f[0])))r.push(` \u21B3 tenure ${l} (${u.provider||"?"})${" ".repeat(Math.max(1,24-String(u.provider||"?").length))}${Yn(Fr(u.usd),10)} ${u.models.join(",")||"?"}`)}return r.join(`
|
|
235
|
-
`)}import sv from"node:fs/promises";import av from"node:path";var YS=["bug-fix","orchestration","design","ops-infra","docs-writing","research","feature-build"],ZS={research:[/\bresearch\b/,/\binvestigat/,/\bverif/,/\bcompar(e|ing|ison)/,/\bcompetit/,/\bdeep[ -]?dive\b/,/\banaly[sz]/,/\bassess/,/\bdiligence\b/,/\bmarket\b/,/\bstrateg/,/\broadmap\b/,/\bnorth star/,/\bwhat('| i)?s the (state|status)\b/,/\bstatus of\b/,/\bexplore\b/,/\bfeasib/,/\bevaluat/,/\bscope out\b/,/\btalk through\b/],"bug-fix":[/\bfix(e[sd]|ing)?\b/,/\bbugs?\b/,/\bbroken\b/,/\bregression\b/,/\bcrash/,/\bincident\b/,/\bdebug/,/\bdoesn'?t (work|render|load|start)/,/\bnot working\b/,/\btroubleshoot/,/\bflaky?\b/,/\brepro(duce|duction)?\b/,/\brace condition\b/,/\bleak\b/],"feature-build":[/\bimplement/,/\bbuild(ing)?\b/,/\badd(ing)?\b/,/\bcreate\b/,/\bwire (it |them )?(up|in)\b/,/\bship(ping)?\b/,/\bintegrat/,/\bsupport for\b/,/\bfeature\b/,/\bendpoint\b/,/\badapter\b/,/\bprototype\b/],design:[/\bdesign/,/\bui\b/,/\bux\b/,/\bstyl(e|es|ing)\b/,/\bcss\b/,/\blayout\b/,/\bmock-?up/,/\brender(ing|s)?\b/,/\bpolish/,/\btheme\b/,/\bfonts?\b/,/\bresponsive\b/,/\banimation/,/\bvisual/,/\bpopover\b/],orchestration:[/\bhand[ -]?(off|over)/,/\bhand (it|the lead|this) (over|off)\b/,/\bdelegat/,/\bsubtask/,/\bmerge[ -]?back\b/,/\borchestrat/,/\b(switch|change) (the )?model\b/,/\bmodel switch/],"docs-writing":[/\bdocs?\b/,/\bdocument/,/\bmemo\b/,/\breadme\b/,/\bwrite-?up\b/,/\bchangelog\b/,/\bclaude\.md\b/,/\bblog/,/\bproofread/,/\bcopy(edit|writ)/],"ops-infra":[/\bdeploy/,/\bsystemd\b/,/\bdaemon\b/,/\btmux\b/,/\bdns\b/,/\bwireguard\b/,/\brestart/,/\bnginx\b/,/\bprovision/,/\binstall/,/\bupgrade/,/\bcron\b/,/\bbackups?\b/,/\bcertificat/,/\bssh\b/,/\bsecrets?\b/]};function gs(t){let e=String(t||"").toLowerCase();if(!e.trim())return null;let n=null;for(let r of YS){let o=0;for(let s of ZS[r])s.test(e)&&o++;o>0&&(!n||o>n.score)&&(n={category:r,score:o})}return n}var QS=new Set(["Read","Grep","Glob","LS","WebSearch","WebFetch","NotebookRead"]),tv=new Set(["Edit","Write","MultiEdit","NotebookEdit"]);function Om(t){let e=0,n=0;for(let[r,o]of Object.entries(t||{}))QS.has(r)&&(e+=o||0),tv.has(r)&&(n+=o||0);return e>=5&&n===0?{category:"research",score:1}:n>=3?{category:"feature-build",score:1}:null}function vc(t){return String(t||"").replace(/([a-z0-9])([A-Z])/g,"$1 $2").replace(/([A-Z]+)([A-Z][a-z])/g,"$1 $2").replace(/[-_/]+/g," ")}var ys=t=>t.payload?.ts??t.ts,ev=12e4,Ce=t=>Math.round(t*1e4)/1e4,nv=()=>({usd:0,activeMs:0,turns:0,toolCalls:0,models:{},providers:{},byTrack:new Map});function rv(t,{since:e}){let{track:n,events:r,brief:o}=t,s=S=>ys(S)>=e,a=gs([vc(n?.name),vc(n?.workspace?.branch),o].filter(Boolean).join(" ")),i=new Map;for(let S of r)if(S.type==="subtask.delegated"&&S.boxId&&S.payload?.task){let v=gs(S.payload.task);v&&i.set(S.boxId,v)}let c=new Map,l=S=>{let v=S||"unattributed";return c.has(v)||c.set(v,{segments:[u(null)],lastTs:null,provider:null}),c.get(v)},u=S=>({textCat:S,tools:{},usd:0,tokens:{input:0,output:0,cacheRead:0,cacheWrite:0},models:{},provider:null,activeMs:0,turns:0,toolCalls:0}),d=[...r].sort((S,v)=>ys(S)-ys(v)),f=0,w=0,h=0,g={turns:0};for(let S of d){let v=l(S.boxId),E=v.segments[v.segments.length-1],J=ys(S);switch(v.lastTs!=null&&J>v.lastTs&&s(S)&&(E.activeMs+=Math.min(J-v.lastTs,ev)),v.lastTs=J,S.provider&&(v.provider=S.provider),S.type){case"turn.user":{let I=S.payload?.text;if(S.source==="hook"||!String(I||"").trim())break;let M=u(gs(I));v.segments.push(M),s(S)&&(M.turns+=1);break}case"tool.called":{if(S.source==="transcript")break;let I=S.payload?.tool;I&&(E.tools[I]=(E.tools[I]||0)+1),s(S)&&(E.toolCalls+=1);break}case"cost.recorded":{if(!s(S))break;let I=S.payload||{},M={input:I.tokens?.input||0,output:I.tokens?.output||0,cacheRead:I.tokens?.cacheRead||0,cacheWrite:I.tokens?.cacheWrite??I.tokens?.cacheCreation??0},m=Number(I.usd)||0;!m&&I.tokens&&(m=Kt(I.model,M)),!m&&I.tokens&&!No(I.model)&&(g.turns+=1),E.usd+=m,E.tokens.input+=M.input,E.tokens.output+=M.output,E.tokens.cacheRead+=M.cacheRead,E.tokens.cacheWrite+=M.cacheWrite;let p=I.model||"unknown";E.models[p]=(E.models[p]||0)+m,S.provider&&(E.provider=S.provider);break}case"handoff.done":s(S)&&(f+=1);break;case"subtask.merged":s(S)&&(w+=1);break;case"error":s(S)&&S.payload?.kind==="subtask-failed"&&(h+=1);break;default:break}}let x=new Map,A=!1;for(let[S,v]of c)for(let E of v.segments){if(!(E.usd>0||E.activeMs>0||E.turns>0||E.toolCalls>0))continue;let I=E.textCat?.category??i.get(S)?.category??Om(E.tools)?.category??a?.category??"uncategorized";x.has(I)||x.set(I,{usd:0,activeMs:0,turns:0,toolCalls:0,models:{},providers:{}});let M=x.get(I);M.usd+=E.usd,M.activeMs+=E.activeMs,M.turns+=E.turns,M.toolCalls+=E.toolCalls;for(let[p,b]of Object.entries(E.models))M.models[p]=(M.models[p]||0)+b;let m=E.provider||v.provider;m&&(M.providers[m]=(M.providers[m]||0)+E.usd),m==="cursor"&&E.usd===0&&(A=!0)}return{perCategory:x,handoffs:f,merged:w,failed:h,sawCursorActivity:A,unpricedTurns:g.turns}}function Mm(t,{windowMs:e=null,now:n=Date.now()}={}){let r=e==null?0:n-e,o=new Map,s=[],a=0,i=!1,c=0;for(let g of t||[]){let{track:x,events:A}=g;if(!A?.length){a+=1;continue}let{perCategory:S,handoffs:v,merged:E,failed:J,sawCursorActivity:I,unpricedTurns:M}=rv(g,{since:r});I&&(i=!0),c+=M;let m={trackId:x?.id,name:x?.name||x?.id,archived:!!x?.archivedAt,usd:0,activeMs:0,turns:0,categories:{},models:{},providers:{},handoffs:v,subtasksMerged:E,subtasksFailed:J};for(let[p,b]of S){m.usd+=b.usd,m.activeMs+=b.activeMs,m.turns+=b.turns,m.categories[p]={usd:Ce(b.usd),activeMs:b.activeMs};for(let[$,z]of Object.entries(b.models))m.models[$]=Ce((m.models[$]||0)+z);for(let[$,z]of Object.entries(b.providers))m.providers[$]=Ce((m.providers[$]||0)+z);o.has(p)||o.set(p,nv());let T=o.get(p);T.usd+=b.usd,T.activeMs+=b.activeMs,T.turns+=b.turns,T.toolCalls+=b.toolCalls;for(let[$,z]of Object.entries(b.models))T.models[$]=(T.models[$]||0)+z;for(let[$,z]of Object.entries(b.providers))T.providers[$]=(T.providers[$]||0)+z;let ot=T.byTrack.get(x?.id)||{usd:0,activeMs:0};ot.usd+=b.usd,ot.activeMs+=b.activeMs,T.byTrack.set(x?.id,ot)}(m.usd>0||m.activeMs>0||m.turns>0)&&(m.usd=Ce(m.usd),m.dominantCategory=ov(m.categories),s.push(m))}let l=[...o.values()].reduce((g,x)=>g+x.usd,0),u=[...o.values()].reduce((g,x)=>g+x.activeMs,0),d=l>0?"usd":"time",f=d==="usd"?l:u,w=new Map((t||[]).map(g=>[g.track?.id,g.track?.name||g.track?.id])),h=[...o.entries()].map(([g,x])=>({category:g,sharePct:f>0?Math.round((d==="usd"?x.usd:x.activeMs)/f*100):0,shareBasis:d,usd:Ce(x.usd),activeMs:x.activeMs,turns:x.turns,toolCalls:x.toolCalls,models:Cm(x.models),providers:Cm(x.providers),topWorkstreams:[...x.byTrack.entries()].sort((A,S)=>S[1].usd-A[1].usd||S[1].activeMs-A[1].activeMs).slice(0,2).map(([A,S])=>({trackId:A,name:w.get(A),usd:Ce(S.usd),activeMs:S.activeMs}))})).sort((g,x)=>x.usd-g.usd||x.activeMs-g.activeMs);return s.sort((g,x)=>x.usd-g.usd||x.activeMs-g.activeMs),{since:r,capturedAt:n,totalUsd:Ce(l),activeMs:u,turns:[...o.values()].reduce((g,x)=>g+x.turns,0),bullets:h,workstreams:s,gaps:{tracksWithoutEvents:a,cursorCostUnknown:i,unpricedTurns:c}}}var ov=t=>{let e=null;for(let[n,r]of Object.entries(t))(!e||r.usd>e.v.usd||r.usd===e.v.usd&&r.activeMs>e.v.activeMs)&&(e={cat:n,v:r});return e?.cat??null},Cm=t=>Object.fromEntries(Object.entries(t).map(([e,n])=>[e,Ce(n)]));var Tc={"24h":24*36e5,"7d":168*36e5,"30d":720*36e5,all:null};function iv(t){let e=String(t||"7d");return e in Tc?{key:e,ms:Tc[e]}:{key:"7d",ms:Tc["7d"]}}function cv(t,e){let n=new Set((e||[]).filter(o=>o.source==="orchestrator").map(o=>`${o.type}|${o.payload?.ts??"?"}`)),r=[...e||[]];for(let o of t?.events||[]){let s=ko(o);!s||n.has(`${s.type}|${s.payload?.ts??"?"}`)||r.push({trackId:t.id,boxId:s.boxId??null,ts:o.at,type:s.type,actor:s.actor,provider:s.provider??null,source:s.source,payload:s.payload})}return r}async function lv(t){if(!t?.cwd)return null;try{let n=(await sv.readFile(av.join(t.cwd,".bostrat","handoff","latest.md"),"utf8")).match(/^##\s*Objective\s*\n([\s\S]*?)(?:\n##\s|\n*$)/m);return n?n[1].trim().slice(0,1e3):null}catch{return null}}async function Nm(t,{window:e="7d",now:n=Date.now()}={}){let{key:r,ms:o}=iv(e),s=[];for(let a of t||[]){let i=await P(a);s.push({track:a,events:cv(a,i),brief:await lv(a)})}return{window:r,...Mm(s,{windowMs:o,now:n})}}import Zn from"node:fs/promises";import uv from"node:fs";import ft from"node:path";import dv from"node:os";import{execFile as fv}from"node:child_process";import{promisify as pv}from"node:util";var mv=pv(fv),Pm=process.env.BOSTRAT_STATE_DIR||ft.join(process.env.HOME||dv.tmpdir(),".bostrat"),Ac=!1;async function Hr(t,e){let n=await Zn.stat(t).catch(()=>null);if(!n?.isFile())return!1;let r=await Zn.stat(e).catch(()=>null);return r&&r.size===n.size&&r.mtimeMs>=n.mtimeMs?!1:(await Zn.mkdir(ft.dirname(e),{recursive:!0}),await Zn.copyFile(t,e),!0)}var hv=new Set([".claude",".codex",".credentials.json","auth.json",".claude.json","integrations.json"]);async function Wr(t,e){let n=0,r=await Zn.readdir(t,{withFileTypes:!0}).catch(()=>[]);for(let o of r){if(hv.has(o.name))continue;let s=ft.join(t,o.name);o.isDirectory()?n+=await Wr(s,ft.join(e,o.name)):o.isFile()&&(n+=await Hr(s,ft.join(e,o.name))?1:0)}return n}async function Bm({stateDir:t=Pm,dest:e=process.env.BOSTRAT_SYNC_DIR||`${Pm}-sync`,remote:n=process.env.BOSTRAT_STATE_REMOTE||null,tracks:r=null}={}){try{await Zn.mkdir(e,{recursive:!0});let o=0;o+=await Hr(ft.join(t,"tracks.json"),ft.join(e,"tracks.json"))?1:0,o+=await Hr(ft.join(t,"mocks.json"),ft.join(e,"mocks.json"))?1:0,o+=await Hr(ft.join(t,"runbooks.json"),ft.join(e,"runbooks.json"))?1:0,o+=await Wr(ft.join(t,"runbooks"),ft.join(e,"runbooks")),o+=await Wr(ft.join(t,"mock-archive"),ft.join(e,"mock-archive")),o+=await Hr(ft.join(t,"events.jsonl"),ft.join(e,"events.jsonl"))?1:0,o+=await Wr(ft.join(t,"events"),ft.join(e,"events"));for(let l of r||[])!l?.cwd||!l?.id||(o+=await Wr(ft.join(l.cwd,".bostrat"),ft.join(e,"workspaces",l.id,".bostrat")));let s=(...l)=>mv("git",["-C",e,...l]);uv.existsSync(ft.join(e,".git"))||(await s("init","-q"),await s("config","user.email","agent@bostrat.local"),await s("config","user.name","bostrat state sync")),n&&/^https:\/\/github\.com\//i.test(n)&&(await s("config","--get","credential.useHttpPath").then(()=>!0).catch(()=>!1)||await ia(e).catch(()=>{}));let a=!1,{stdout:i}=await s("status","--porcelain");i.trim()&&(await s("add","-A"),await s("commit","-q","-m",`state sync ${new Date().toISOString()}`),a=!0);let c=!1;if(n&&(a||Ac))try{await s("push","-q",n,"HEAD:refs/heads/main"),c=!0,Ac=!1}catch{Ac=!0}return{staged:o,committed:a,pushed:c}}catch(o){return{staged:0,committed:!1,pushed:!1,error:o?.message||String(o)}}}import{execFile as wv}from"node:child_process";import{promisify as gv}from"node:util";import{randomUUID as yv}from"node:crypto";import bv from"node:fs";import kv from"node:os";import xv from"node:path";var Ne=gv(wv),bs=t=>new Promise(e=>setTimeout(e,t)),Pe=Ms,_c=process.env.BOSTRAT_USAGE_PROBE_CWD||xv.join(kv.homedir(),".bostrat","usage-probe"),Sv=Number(process.env.BOSTRAT_USAGE_IDLE_REAP_MS)||5*6e4,vv=Number(process.env.BOSTRAT_USAGE_PANEL_TIMEOUT_MS)||15e3,Tv=Number(process.env.BOSTRAT_USAGE_READY_TIMEOUT_MS)||45e3,Dm=Number(process.env.BOSTRAT_USAGE_CACHE_MS)||2e4,Me=null,qr=null,Gr=null;async function Av(){try{return await Ne(O,C("has-session","-t",Pe)),!0}catch{return!1}}function _v(){Me&&clearTimeout(Me),Me=setTimeout(()=>Ev().catch(()=>{}),Sv),Me.unref&&Me.unref()}async function Ev(){Me&&(clearTimeout(Me),Me=null);try{await Ne(O,C("kill-session","-t",Pe))}catch{}}async function Iv(){let t=Date.now()+Tv;for(;;){let e=await Ec().catch(()=>"");if(/for agents|for shortcuts|bypass permissions on/i.test(e)){await bs(500);return}if(Date.now()>t)throw new Error("usage probe didn't reach the prompt (trust/onboarding may be blocking)");await bs(500)}}async function Rv(){if(await Av())return;if(!await ee())throw new Error("host tmux server not running \u2014 start globo-tmux.service");bv.mkdirSync(_c,{recursive:!0}),await pe(_c);let t=yv();await Ne(O,C("new-session","-d","-s",Pe,"-c",_c,"-x","200","-y","60"));let e=`${Cs} --session-id ${t}`;await Ne(O,C("send-keys","-t",Pe,e,"Enter")),await Iv()}async function Ec(){let{stdout:t}=await Ne(O,C("capture-pane","-t",Pe,"-p"));return t}async function $v(){await Rv(),await Ne(O,C("send-keys","-t",Pe,"-l","/usage")),await Ne(O,C("send-keys","-t",Pe,"Enter"));let t=Date.now()+vv,e="";for(;await bs(400),e=await Ec(),!(/Current\s+(session|week)/i.test(e)||/\d+%\s*used/i.test(e)||Date.now()>t););return await bs(900),e=await Ec(),await Ne(O,C("send-keys","-t",Pe,"Escape")).catch(()=>{}),_v(),e}function Ov(t){let e=String(t||"").split(`
|
|
236
|
-
`).map(s=>s.replace(/\s+$/,"").trim()),n=s=>{let a=null,i=null;for(let c=s+1;c<Math.min(s+5,e.length)&&!/^Current\s/i.test(e[c]);c++){let l=e[c].match(/(\d+)%\s*used/i);l&&a===null&&(a=Number(l[1]));let u=e[c].match(/^Resets\s+(.+)$/i);u&&i===null&&(i=u[1].trim())}return a===null?null:{pct:a,resets:i||null}},r=s=>{let a=e.findIndex(i=>s.test(i));return a<0?null:n(a)},o=[];return e.forEach((s,a)=>{let i=s.match(/^Current week \(([^)]+)\)$/i);if(!i||/all models/i.test(i[1]))return;let c=n(a);c&&o.push({model:i[1].trim(),...c})}),{session:r(/^Current session\b/i),week:r(/^Current week \(all models\)/i)||r(/^Current week$/i),weekModels:o.length?o:null}}function
|
|
237
|
-
`).map(s=>s.replace(/\s+$/,"")),n=e.findIndex(s=>/What's contributing/i.test(s));if(n<0)return null;let r=/(Usage credits|Esc to cancel|d to day · w to week|^\s*d to day)/i,o=[];for(let s=n+1;s<e.length&&!r.test(e[s]);s++)e[s].trim()&&o.push(e[s].trim());return o.length?o:null}async function
|
|
238
|
-
`),n=!1;for(let r=e.length-1;r>=0;r--){let o=e[r].trim();if(!o||o[0]!=="{"||!o.includes("token_count"))continue;let s;try{s=JSON.parse(o)}catch{continue}let a=s?.payload;if(a?.type!=="token_count")continue;n=!0;let i=a.rate_limits;if(!i)return{session:null,week:null,sawTokenCount:n};if(i.primary||i.secondary)return{session:
|
|
245
|
+
_${o} archived_`:"")}async function DS(t,e){let n=await un();if(!n)return et("Slack integration not configured.");let r=t.deps,o=(e.text||"").trim().split(/\s+/).filter(Boolean),s=(o[0]||"help").toLowerCase(),a=o.slice(1);switch(s){case"start":{if(!a.length)return et("Usage: `/bostrat start <name> [repo]`");let i=await r.listRepos(),c=a[a.length-1]?.toLowerCase(),l=a.length>1?i.find(d=>d.name.toLowerCase()===c):null,u=(l?a.slice(0,-1):a).join(" ");return l?(pn(Rm(t,n,{name:u,repo:l.path,responseUrl:e.response_url})),et(`Starting *${F(u)}* on ${F(l.name)}\u2026`)):{response_type:"ephemeral",text:`Pick a repo for \u201C${u}\u201D`,blocks:NS(u,i)}}case"archive":{if(!a.length)return et("Usage: `/bostrat archive <track>`");let{match:i,live:c}=Sc(await r.listTracks(),a.join(" "));return i?(pn(CS(t,n,{track:i,responseUrl:e.response_url})),et(`Archiving *${F(i.name)}*\u2026`)):vc(c,a.join(" "))}case"handoff":{let i=r.knownAgents??OS,c=a.at(-1)?.toLowerCase();if(a.length<2||!i.includes(c))return et(`Usage: \`/bostrat handoff <track> <${i.join("|")}>\``);let l=a.slice(0,-1).join(" "),{match:u,live:d}=Sc(await r.listTracks(),l);return u?(pn(MS(t,n,{track:u,agent:c,responseUrl:e.response_url})),et(`Handing *${F(u.name)}* to ${F(c)}\u2026`)):vc(d,l)}case"stop":{if(!a.length)return et("Usage: `/bostrat stop <track>`");let{match:i,live:c}=Sc(await r.listTracks(),a.join(" "));return i?(pn(PS(t,n,{track:i,responseUrl:e.response_url})),et(`Stopping *${F(i.name)}*\u2026`)):vc(c,a.join(" "))}case"list":return et(await BS(r));default:return et($S)}}async function jS(t,e,n,r){let{trackId:o,promptId:s,label:a}=r,i=await Ec(o);try{if(await t.deps.answerPrompt(o,{promptId:s,answers:[a]}),i?.prompt?.promptId===s){let{ts:c,question:l}=i.prompt;delete i.prompt,await le(),await Zn(e.botToken,{channel:i.channel,ts:c,text:`\u2713 ${a}`,blocks:[{type:"section",text:{type:"mrkdwn",text:`~${F(l||"question")}~
|
|
246
|
+
\u2713 *${F(a)}*`}}]}).catch(()=>{})}}catch(c){await Et(n.response_url,et(`Couldn't answer: ${c?.message||c}`))}}async function LS(t,e,n,r){let{trackId:o,offerId:s,action:a}=r,i=t.deps;try{let c=await i.resolveTrack(o);if(!c)throw new Error("track not found");let l=await i.readEvents(c),u=l.find(f=>f.type==="pr.offered"&&f.payload?.offerId===s);if(!u)throw new Error("unknown offer");if(l.some(f=>f.type==="pr.offer.answered"&&f.payload?.offerId===s))throw new Error("offer already answered");let d=a==="pr"?["commit","push","pr"]:a==="push"?["commit","push"]:null;d&&await i.sayToLead(c.id,Ao(d,{branch:u.payload.branch,base:u.payload.base}),{kind:d.includes("pr")?"pr-go":"push-go"}),await i.appendEvent({id:c.id,cwd:c.cwd},{type:"pr.offer.answered",actor:"user",source:"orchestrator",boxId:u.boxId,payload:{ts:Date.now(),offerId:s,action:d?a:"dismiss",actions:d}})}catch(c){await Et(n.response_url,et(`Couldn't act on the offer: ${c?.message||c}`))}}async function US(t,e){let n=e.actions?.[0];if(!n?.action_id)return;let r=await un();if(!r)return;if(n.action_id==="bostrat-start-repo"){let s;try{({name:s}=JSON.parse(n.block_id))}catch{return}let a=n.selected_option?.value;await Et(e.response_url,{...et(`Starting *${F(s)}*\u2026`),replace_original:!0}),pn(Rm(t,r,{name:s,repo:a==="__fresh__"?null:a,responseUrl:e.response_url}));return}let o;try{o=JSON.parse(n.value)}catch{return}n.action_id==="bostrat-prompt"?pn(jS(t,r,e,o)):n.action_id==="bostrat-ship"&&pn(LS(t,r,e,o))}async function FS(t,e){if(!e||e.type!=="message"||e.bot_id||e.subtype||!e.thread_ts||e.thread_ts===e.ts||!e.text?.trim())return;let n=await IS(e.channel,e.thread_ts);if(!n)return;let r=await un();if(r)try{await t.deps.sayToLead(n.trackId,e.text.trim())}catch(o){await mn(r,n.rec,`Couldn't deliver that to the lead: ${o?.message||o}`).catch(()=>{})}}function HS(t){return async e=>{if(e.type==="slash_commands")return DS(t,e.payload);if(e.type==="interactive"){await US(t,e.payload);return}if(e.type==="events_api"){await FS(t,e.payload?.event);return}}}var WS=new Set(["turn.user","turn.agent","prompt.raised","prompt.answered","pr.offered","pr.offer.answered","pr.state.changed","handoff.done","view.state.changed","mock.state.changed"]);function qS(t,e){let n=[{type:"section",text:{type:"mrkdwn",text:`\u2753 ${F(e.question||"The lead needs an answer")}`}}],r=(e.options||[]).slice(0,12);return r.length?n.push({type:"actions",elements:r.map(o=>({type:"button",action_id:"bostrat-prompt",text:{type:"plain_text",text:Wr(String(o),75)},value:JSON.stringify({trackId:t,promptId:e.promptId,label:String(o)})}))}):n.push({type:"context",elements:[{type:"mrkdwn",text:"_reply in this thread to answer_"}]}),n}async function GS(t,e,n){let r=n.payload||{};if(!r.promptId||e.prompt?.promptId===r.promptId)return;e.prompt&&await Zn(t.botToken,{channel:e.channel,ts:e.prompt.ts,text:"superseded",blocks:[{type:"section",text:{type:"mrkdwn",text:`~${F(e.prompt.question||"question")}~ _superseded_`}}]}).catch(()=>{});let o=await mn(t,e,r.question||"The lead needs an answer",qS(n.trackId,r));e.prompt={promptId:r.promptId,ts:o.ts,question:r.question||null},await le()}async function KS(t,e,n){let r=n.payload||{};if(!e.prompt||r.promptId&&e.prompt.promptId!==r.promptId)return;let{ts:o,question:s}=e.prompt;delete e.prompt,await le(),await Zn(t.botToken,{channel:e.channel,ts:o,text:"answered",blocks:[{type:"section",text:{type:"mrkdwn",text:`~${F(s||"question")}~
|
|
247
|
+
\u2713 _answered_`}}]})}async function VS(t,e,n){let r=n.payload||{};if(!r.offerId||e.offers?.[r.offerId])return;let o=(i,c,l)=>({type:"button",action_id:"bostrat-ship",text:{type:"plain_text",text:c},value:JSON.stringify({trackId:n.trackId,offerId:r.offerId,action:i}),...l?{style:l}:{}}),s=r.toPr?[o("push",`Commit & push${r.toPr.number?` to #${r.toPr.number}`:""}`,"primary"),o("dismiss","Not now")]:[o("pr","Open PR","primary"),o("push","Just push"),o("dismiss","Not now")],a=await mn(t,e,r.excerpt||"Ship it?",[{type:"section",text:{type:"mrkdwn",text:`\u{1F6A2} ${F(Wr(r.excerpt||"The lead offered to ship.",300))}`}},{type:"actions",elements:s}]);e.offers=e.offers||{},e.offers[r.offerId]=a.ts,await le()}async function JS(t,e,n){let r=n.payload||{},o=e.offers?.[r.offerId];if(!o)return;delete e.offers[r.offerId],await le();let s=r.action==="dismiss"?"dismissed":r.action==="pr"?"\u2713 opening a PR":"\u2713 pushing";await Zn(t.botToken,{channel:e.channel,ts:o,text:s,blocks:[{type:"section",text:{type:"mrkdwn",text:`\u{1F6A2} ${s}`}}]})}async function zS(t,e,n,r){let o=n.payload||{};if(o.change==="ready"&&o.mockId){let s=await(r.readMockPng??XS)(o.mockId,o.version??null);if(!s)return;let a=(o.version??1)>1?` (v${o.version})`:"";await vm(t.botToken,{filename:s.filename,buffer:s.buffer,title:`${o.name}${a}`,channelId:e.channel,threadTs:e.ts,initialComment:`\u{1F3A8} mock *${F(o.name)}*${a}${o.group?` (group \`${F(o.group)}\`)`:""}`})}else if(o.change==="picked"){let s=(o.siblings??[]).map(a=>`*${F(a.name)}*`).join(", ");await mn(t,e,`Design decision: ${o.name} picked`,[{type:"section",text:{type:"mrkdwn",text:`\u{1F3A8} design decision: *${F(o.name)}* picked${s?` over ${s}`:""}`}}])}}async function XS(t,e){let n=await Ae(t);if(!n)return null;let r=rn(n,e);if(!r)return null;let o=await Hr.readFile(Dt(n,r.v).png).catch(()=>null);return o?{filename:`${n.name.replace(/[^\w.-]+/g,"-")}.v${r.v}.png`,buffer:o}:null}async function YS(t,e){if(!WS.has(e.type))return;let n=await Ec(e.trackId);if(!n)return;let r=await un();if(!r)return;let o=e.payload||{};switch(e.type){case"turn.user":if(n.kickoff||e.actor!=="user"||!o.text)return;n.kickoff=o.text,await le(),await Ac(r,n);return;case"turn.agent":{if(!o.text)return;let s=RS(o.text);if(!s)return;await mn(r,n,s,[{type:"section",text:{type:"mrkdwn",text:F(s)}},{type:"context",elements:[{type:"mrkdwn",text:`<${fs(e.trackId)}|full turn \u2192>`}]}]);return}case"prompt.raised":return GS(r,n,e);case"prompt.answered":return KS(r,n,e);case"pr.offered":return VS(r,n,e);case"pr.offer.answered":return JS(r,n,e);case"pr.state.changed":{if(o.change!=="opened"&&o.change!=="merged")return;let s=o.change==="opened"?"PR opened":"PR merged";await mn(r,n,`${s}${o.url?`: ${o.url}`:o.number?` (#${o.number})`:""}`);return}case"handoff.done":if(!o.toAgent||o.toAgent===n.lead)return;n.lead=o.toAgent,await le(),await Ac(r,n);return;case"view.state.changed":{if(o.change!=="created"||o.restarted||!o.slug)return;await mn(r,n,`Live preview ${o.name}: ${ms(o.slug)}`,[{type:"section",text:{type:"mrkdwn",text:`\u{1F50D} live preview *${F(o.name)}*: ${ms(o.slug)}
|
|
248
|
+
_runs on the Track's branch; reachable on the operator's private network only_`}}]);return}case"mock.state.changed":return zS(r,n,e,t.deps)}}function $m(t){let e={deps:t};Nt.on("event",s=>{YS(e,s).catch(()=>{})});let n=null,r=null,o=async()=>{n||!await un()||(r&&clearInterval(r),r=null,n=Tm({getToken:async()=>(await un())?.appToken,onEnvelope:HS(e),log:s=>console.log(`[slack] ${s}`)}))};return r=setInterval(()=>o().catch(()=>{}),3e4),r.unref?.(),o().catch(()=>{}),()=>{r&&clearInterval(r),n?.()}}import ZS from"node:fs/promises";function QS(t){let e=new Map;for(let n of String(t||"").split(`
|
|
249
|
+
`)){let r=n.trim();if(!r||r[0]!=="{")continue;let o;try{o=JSON.parse(r)}catch{continue}let s=o?.message?.usage;if(!s)continue;let a={input:s.input_tokens||0,output:s.output_tokens||0,cacheWrite:s.cache_creation_input_tokens||0,cacheRead:s.cache_read_input_tokens||0};if(a.input+a.output+a.cacheWrite+a.cacheRead===0)continue;let i=o.message?.id||o.uuid||null;i&&e.set(i,{messageId:i,tokens:a,model:o.message?.model||o.model||null})}return[...e.values()]}function tv(t){let e=new Set;for(let n of t)if(n.type==="cost.recorded"){n.payload?.messageId&&e.add(n.payload.messageId);for(let r of n.payload?.messageIds||[])e.add(r)}return e}function ev(t,e){return t?.transcript_path?t.transcript_path:e?.cwd&&e?.sessionId?ee(e.cwd,e.sessionId):e?.cwd?Gs(e.cwd):null}async function Om(t,e){let n=await e(t?.session_id,t?.cwd);if(!n?.track)return[];let{track:r,box:o}=n,s={boxId:o?.name||null,provider:"claude",source:"hook"},a=[],i=async c=>{a.push(await R(r,{...s,...c}))};switch(t?.hook_event_name){case"SessionStart":await i({type:"session.ready",actor:"system",payload:{sessionId:t.session_id}});break;case"UserPromptSubmit":await i({type:"turn.user",actor:"user",payload:{promptHead:String(t.prompt??t.user_prompt??"").slice(0,160)||null}});break;case"PostToolUse":await i({type:"tool.called",actor:"agent",payload:{tool:t.tool_name??null}});break;case"Stop":await nv(r,o,t,i);break;default:break}return a}async function nv(t,e,n,r){let o=[],s=ev(n,e);if(s)try{o=QS(await ZS.readFile(s,"utf8"))}catch{}let a=o.length?o[o.length-1]:null;if(await r({type:"turn.agent",actor:"agent",payload:{model:a?.model??e?.model??null,messageId:a?.messageId??null}}),!o.length)return;let i=tv(await B(t)),c=o.filter(u=>!i.has(u.messageId));if(c.length){let u=new Map;for(let d of c){let f=d.model||"unknown",w=u.get(f)||{tokens:{input:0,output:0,cacheWrite:0,cacheRead:0},messageIds:[]};for(let m of Object.keys(w.tokens))w.tokens[m]+=d.tokens[m];w.messageIds.push(d.messageId),u.set(f,w)}for(let[d,f]of u)await r({type:"cost.recorded",actor:"system",source:"transcript",payload:{model:d,usd:Kt(d,f.tokens),tokens:f.tokens,messageId:f.messageIds[f.messageIds.length-1],messageIds:f.messageIds,apiCalls:f.messageIds.length}})}let l=a.tokens.input+a.tokens.cacheWrite+a.tokens.cacheRead;await r({type:"context.usage",actor:"system",source:"transcript",payload:{model:a.model,occupancy:l,tokens:a.tokens}})}import sv from"node:fs";import Ic from"node:fs/promises";import av from"node:http";import Qn from"node:path";import bs from"node:os";import{createRequire as iv}from"node:module";import Rc from"ws";import rv from"node:path";import ov from"node:os";var Z$=process.env.BOSTRAT_STATE_DIR||rv.join(process.env.HOME||ov.tmpdir(),".bostrat");function Cm(t=process.env){let e=(t.BOSTRAT_HOST||"").trim();if(e!=="all")return e||(t.BOSTRAT_PLANE_URL?"127.0.0.1":void 0)}function Mm(t){let e=String(t||"").trim();if(!e)return"";let n=/^[a-z][a-z0-9+.-]*:\/\//i.test(e)?e:`wss://${e}`,r;try{r=new URL(n)}catch{return e}let o={"https:":"wss:","http:":"ws:","wss:":"wss:","ws:":"ws:"}[r.protocol];return o?(r.protocol=o,(r.pathname===""||r.pathname==="/")&&(r.pathname="/v1/node"),r.toString()):e}var ys=(()=>{try{return iv(import.meta.url)("../package.json").version||null}catch{return null}})(),cv=process.env.BOSTRAT_STATE_DIR||Qn.join(process.env.HOME||bs.tmpdir(),".bostrat");function lv(t,e){let n=String(t).split(".").map(Number),r=String(e).split(".").map(Number);if(n.some(Number.isNaN)||r.some(Number.isNaN))return!1;for(let o=0;o<Math.max(n.length,r.length);o++){let s=(n[o]||0)-(r[o]||0);if(s)return s<0}return!1}function uv({home:t=process.env.HOME||bs.homedir(),env:e=process.env}={}){let n=r=>{try{return sv.statSync(r),!0}catch{return!1}};return{claude:n(Qn.join(t,".claude",".credentials.json")),codex:n(Qn.join(e.CODEX_HOME||Qn.join(t,".codex"),"auth.json"))}}var St=1,Pm=1e3,dv=3e4,Nm=100,fv=2e3,pv=25e3;function Bm({url:t,token:e,nodeId:n=bs.hostname(),name:r=bs.hostname(),listTracks:o,selfUrl:s=`http://127.0.0.1:${process.env.PORT||3362}`,log:a=console}){if(!t||!e)throw new Error("plane-link: url and token are required");if(typeof o!="function")throw new Error("plane-link: listTracks is required");t=Mm(t);let i=s.replace(/^http/,"ws"),c=new Map,l=new Map,u=Qn.join(cv,"plane-link","cursor.json"),d={},f=new Map,w=null,m=!1,g=!1,x=Pm,A=null,S=null,T=null,E=!1;async function J(){try{d=JSON.parse(await Ic.readFile(u,"utf8"))}catch{d={}}}function M(){E=!0,!S&&(S=setTimeout(async()=>{if(S=null,!!E){E=!1;try{await Ic.mkdir(Qn.dirname(u),{recursive:!0}),await Ic.writeFile(u,JSON.stringify(d))}catch(N){a.error("[plane-link] cursor flush failed:",N?.message||N)}}},fv),S.unref?.())}function I(N){return!w||w.readyState!==Rc.OPEN?!1:(w.send(JSON.stringify(N)),!0)}function h(N){for(let{trackId:W,event:_}of N)f.set(_.id,{trackId:W,seq:_.seq});return I({v:St,ch:"events",type:"append",events:N})}async function p(){let N;try{N=await o()}catch(W){a.error("[plane-link] backfill listTracks failed:",W?.message||W);return}I({v:St,ch:"ctl",type:"tracks",ids:N.map(W=>W.id).filter(W=>typeof W=="string")});for(let W of N){if(!m)return;let _;try{_=await B(W)}catch{continue}let q=d[W.id]??0,X=_.filter(lt=>lt.seq>q&&typeof lt.id=="string");for(let lt=0;lt<X.length;lt+=Nm){let nt=X.slice(lt,lt+Nm).map(wt=>({trackId:W.id,event:wt}));if(!h(nt))return}}}function b(N){!m||!N?.trackId||typeof N.id!="string"||h([{trackId:N.trackId,event:N}])}let v=new Map;async function ot(N){let{reqId:W,method:_,path:q,headers:X={},body:lt}=N,nt=Object.fromEntries(Object.entries(X).filter(([,Pt])=>Pt!=null)),wt=new AbortController;v.set(W,wt);try{if(N.host){await $(N,nt,wt);return}let Pt=await fetch(s+q,{method:_,headers:nt,body:lt?Buffer.from(lt,"base64"):void 0,signal:wt.signal});if(I({v:St,ch:"control",type:"http_head",reqId:W,status:Pt.status,headers:{"content-type":Pt.headers.get("content-type")||"application/octet-stream"}}),Pt.body){for await(let De of Pt.body)if(!I({v:St,ch:"control",type:"http_data",reqId:W,data:Buffer.from(De).toString("base64")}))break}I({v:St,ch:"control",type:"http_end",reqId:W,error:null})}finally{v.delete(W)}}function $(N,W,_){let{reqId:q,method:X,path:lt,body:nt}=N,wt=new URL(s);return new Promise(Pt=>{let De=av.request({host:wt.hostname,port:wt.port,method:X,path:lt,headers:{...W,host:N.host},signal:_.signal},jt=>{let{connection:y,"keep-alive":j,"transfer-encoding":wn,upgrade:Zr,...je}=jt.headers;I({v:St,ch:"control",type:"http_head",reqId:q,status:jt.statusCode||502,headers:je}),jt.on("data",Os=>{I({v:St,ch:"control",type:"http_data",reqId:q,data:Buffer.from(Os).toString("base64")})||jt.destroy()}),jt.on("end",()=>{I({v:St,ch:"control",type:"http_end",reqId:q,error:null}),Pt()}),jt.on("error",()=>{I({v:St,ch:"control",type:"http_end",reqId:q,error:"view upstream error"}),Pt()})});De.on("error",jt=>{I({v:St,ch:"control",type:"http_end",reqId:q,error:jt?.message||"view request failed"}),Pt()}),nt&&De.write(Buffer.from(nt,"base64")),De.end()})}function z(){if(g)return;w=new Rc(t,{headers:{authorization:`Bearer ${e}`}}),w.on("open",()=>{I({v:St,ch:"ctl",type:"hello",nodeId:n,name:r,platform:process.platform,version:ys,providers:uv()})}),w.on("message",W=>{let _;try{_=JSON.parse(W)}catch{return}if(_.ch==="ctl"&&_.type==="hello_ok"){m=!0,x=Pm,a.log(`[plane-link] connected to plane as ${n}`),_.minNodeVersion&&ys&&lv(ys,_.minNodeVersion)&&a.error(`[plane-link] this node is bostrat-node ${ys}; the plane asks for \u2265${_.minNodeVersion} \u2014 update with: npm install -g bostrat-node`),T&&clearInterval(T),T=setInterval(()=>I({v:St,ch:"ctl",type:"ping"}),pv),T.unref?.(),p().catch(q=>a.error("[plane-link] backfill failed:",q?.message||q));return}if(_.ch==="events"&&_.type==="ack"&&Array.isArray(_.ids)){for(let q of _.ids){let X=f.get(q);X&&(f.delete(q),(d[X.trackId]??0)<X.seq&&(d[X.trackId]=X.seq))}M();return}if(_.ch==="control"&&_.type==="http_open"){ot(_).catch(q=>I({v:St,ch:"control",type:"http_end",reqId:_.reqId,error:q?.message||"tunnel error"}));return}if(_.ch==="control"&&_.type==="http_cancel"){v.get(_.reqId)?.abort();return}if(_.ch==="attach"&&_.type==="open"){let q=new Rc(i+_.path,Array.isArray(_.protocols)?_.protocols:[],_.host?{headers:{host:_.host}}:void 0);c.set(_.streamId,q),l.set(_.streamId,[]),q.on("open",()=>{let lt=l.get(_.streamId)||[];l.delete(_.streamId);for(let{buf:nt,bin:wt}of lt)q.send(nt,{binary:wt})}),q.on("message",(lt,nt)=>{let wt=nt?lt:Buffer.from(String(lt));I({v:St,ch:"attach",type:"data",streamId:_.streamId,data:wt.toString("base64"),...nt?{bin:!0}:{}})});let X=()=>{l.delete(_.streamId),c.delete(_.streamId)&&I({v:St,ch:"attach",type:"close",streamId:_.streamId})};q.on("close",X),q.on("error",X);return}if(_.ch==="attach"&&_.type==="data"){let q=c.get(_.streamId);if(!q)return;let X=Buffer.from(_.data,"base64"),lt=_.bin===!0,nt=l.get(_.streamId);nt?nt.push({buf:X,bin:lt}):q.send(X,{binary:lt});return}if(_.ch==="attach"&&_.type==="close"){let q=c.get(_.streamId);c.delete(_.streamId),l.delete(_.streamId),q?.close();return}});let N=()=>{m&&a.log("[plane-link] disconnected from plane"),m=!1,T&&(clearInterval(T),T=null),f.clear();for(let W of c.values())W.close();c.clear(),l.clear();for(let W of v.values())W.abort();v.clear(),!(g||A)&&(A=setTimeout(()=>{A=null,z()},x),A.unref?.(),x=Math.min(x*2,dv))};w.on("close",N),w.on("error",N)}return Nt.on("event",b),J().then(z),{stop:()=>{g=!0,Nt.off("event",b),A&&clearTimeout(A),S&&clearTimeout(S),T&&clearInterval(T),w?.close()},status:()=>({connected:m,nodeId:n,cursor:{...d},inflight:f.size})}}function mv(t){let e={};for(let n of t.boxes||[]){let r=n.role&&n.role!=="lead"?` \xB7 ${n.role}`:"";e[n.name]=`${n.name} (${n.agent||"?"}${r})`}return e}var Dm=()=>({input:0,output:0,cacheRead:0,cacheCreation:0}),jm=(t,e)=>{for(let n of Object.keys(t))t[n]+=e?.[n]||0},$c=t=>Math.round(t*1e4)/1e4;async function Lm(t){let e=[],n={},r={},o=0,s=0,a=0,i=0,c=new Set,l=Dm(),u=0,d=[];for(let f of t||[]){let w=await B(f),m=us(w,{boxLabels:mv(f)});if(m.turns===0){f.archivedAt?u++:d.push(f.name||f.id);continue}o+=m.totalUsd,s+=m.turns,a+=m.repricedTurns||0,i+=m.unpricedTurns||0;for(let g of m.unpricedModels||[])c.add(g);jm(l,m.totalTokens);for(let[g,x]of Object.entries(m.byModel))n[g]||={usd:0,events:0,tokens:Dm()},n[g].usd=$c(n[g].usd+x.usd),n[g].events+=x.events,jm(n[g].tokens,x.tokens);for(let[g,x]of Object.entries(m.byProvider))r[g]||={usd:0,events:0},r[g].usd=$c(r[g].usd+x.usd),r[g].events+=x.events;e.push({id:f.id,name:f.name,usd:m.totalUsd,rollup:m})}return e.sort((f,w)=>w.usd-f.usd),{generatedAt:new Date().toISOString(),pricingBasis:"api-equivalent (subscription usage priced at public API rates)",fleet:{totalUsd:$c(o),turns:s,repricedTurns:a,unpricedTurns:i,unpricedModels:[...c],totalTokens:l,byModel:n,byProvider:r,tracksWithCost:e.length,tracksSilent:u+d.length,tracksSilentArchived:u,tracksSilentLive:d},tracks:e}}var qr=t=>`$${t.toFixed(4)}`,tr=(t,e)=>String(t).padEnd(e);function Um(t){let{fleet:e,tracks:n}=t,r=[];r.push(`BOSTRAT COST READ-OUT \xB7 ${t.generatedAt}`),t.pricingBasis&&r.push(`basis: ${t.pricingBasis}`);let o=e.tracksSilentLive||[];r.push(`fleet total ${qr(e.totalUsd)} over ${e.turns} turn(s) \xB7 ${e.tracksWithCost} track(s) with cost, ${e.tracksSilentArchived??e.tracksSilent} silent (archived)`+(o.length?`, ${o.length} LIVE with no cost data: ${o.join(", ")}`:"")),e.repricedTurns&&r.push(`${e.repricedTurns} turn(s) imputed at current rates (recorded before their model was priced)`),e.unpricedTurns&&r.push(`\u26A0 ${e.unpricedTurns} turn(s) UNPRICED \u2014 total under-reports. Add PER_MTOK rate(s) for: ${(e.unpricedModels||[]).join(", ")}`),r.push(""),r.push("BY MODEL");let s=Object.entries(e.byModel).sort((a,i)=>i[1].usd-a[1].usd);s.length||r.push(" (no cost events yet \u2014 hooks emit on each turn of a newly started box)");for(let[a,i]of s){let c=i.tokens;r.push(` ${tr(a,26)} ${tr(qr(i.usd),10)} ${i.events} turn(s) \xB7 in ${c.input} out ${c.output} cacheR ${c.cacheRead} cacheW ${c.cacheCreation}`)}r.push(""),r.push("BY TRACK / TASK");for(let a of n){r.push(` ${tr(a.name||a.id,30)} ${qr(a.usd)}`);let i=Object.values(a.rollup.byBox).sort((c,l)=>l.usd-c.usd);for(let c of i)if(r.push(` ${tr(c.label,40)} ${tr(qr(c.usd),10)} ${c.models.join(",")||"?"}`),c.epochs)for(let[l,u]of Object.entries(c.epochs).sort((d,f)=>Number(d[0])-Number(f[0])))r.push(` \u21B3 tenure ${l} (${u.provider||"?"})${" ".repeat(Math.max(1,24-String(u.provider||"?").length))}${tr(qr(u.usd),10)} ${u.models.join(",")||"?"}`)}return r.join(`
|
|
250
|
+
`)}import vv from"node:fs/promises";import Tv from"node:path";var hv=["bug-fix","orchestration","design","ops-infra","docs-writing","research","feature-build"],wv={research:[/\bresearch\b/,/\binvestigat/,/\bverif/,/\bcompar(e|ing|ison)/,/\bcompetit/,/\bdeep[ -]?dive\b/,/\banaly[sz]/,/\bassess/,/\bdiligence\b/,/\bmarket\b/,/\bstrateg/,/\broadmap\b/,/\bnorth star/,/\bwhat('| i)?s the (state|status)\b/,/\bstatus of\b/,/\bexplore\b/,/\bfeasib/,/\bevaluat/,/\bscope out\b/,/\btalk through\b/],"bug-fix":[/\bfix(e[sd]|ing)?\b/,/\bbugs?\b/,/\bbroken\b/,/\bregression\b/,/\bcrash/,/\bincident\b/,/\bdebug/,/\bdoesn'?t (work|render|load|start)/,/\bnot working\b/,/\btroubleshoot/,/\bflaky?\b/,/\brepro(duce|duction)?\b/,/\brace condition\b/,/\bleak\b/],"feature-build":[/\bimplement/,/\bbuild(ing)?\b/,/\badd(ing)?\b/,/\bcreate\b/,/\bwire (it |them )?(up|in)\b/,/\bship(ping)?\b/,/\bintegrat/,/\bsupport for\b/,/\bfeature\b/,/\bendpoint\b/,/\badapter\b/,/\bprototype\b/],design:[/\bdesign/,/\bui\b/,/\bux\b/,/\bstyl(e|es|ing)\b/,/\bcss\b/,/\blayout\b/,/\bmock-?up/,/\brender(ing|s)?\b/,/\bpolish/,/\btheme\b/,/\bfonts?\b/,/\bresponsive\b/,/\banimation/,/\bvisual/,/\bpopover\b/],orchestration:[/\bhand[ -]?(off|over)/,/\bhand (it|the lead|this) (over|off)\b/,/\bdelegat/,/\bsubtask/,/\bmerge[ -]?back\b/,/\borchestrat/,/\b(switch|change) (the )?model\b/,/\bmodel switch/],"docs-writing":[/\bdocs?\b/,/\bdocument/,/\bmemo\b/,/\breadme\b/,/\bwrite-?up\b/,/\bchangelog\b/,/\bclaude\.md\b/,/\bblog/,/\bproofread/,/\bcopy(edit|writ)/],"ops-infra":[/\bdeploy/,/\bsystemd\b/,/\bdaemon\b/,/\btmux\b/,/\bdns\b/,/\bwireguard\b/,/\brestart/,/\bnginx\b/,/\bprovision/,/\binstall/,/\bupgrade/,/\bcron\b/,/\bbackups?\b/,/\bcertificat/,/\bssh\b/,/\bsecrets?\b/]};function ks(t){let e=String(t||"").toLowerCase();if(!e.trim())return null;let n=null;for(let r of hv){let o=0;for(let s of wv[r])s.test(e)&&o++;o>0&&(!n||o>n.score)&&(n={category:r,score:o})}return n}var gv=new Set(["Read","Grep","Glob","LS","WebSearch","WebFetch","NotebookRead"]),yv=new Set(["Edit","Write","MultiEdit","NotebookEdit"]);function Fm(t){let e=0,n=0;for(let[r,o]of Object.entries(t||{}))gv.has(r)&&(e+=o||0),yv.has(r)&&(n+=o||0);return e>=5&&n===0?{category:"research",score:1}:n>=3?{category:"feature-build",score:1}:null}function Oc(t){return String(t||"").replace(/([a-z0-9])([A-Z])/g,"$1 $2").replace(/([A-Z]+)([A-Z][a-z])/g,"$1 $2").replace(/[-_/]+/g," ")}var xs=t=>t.payload?.ts??t.ts,bv=12e4,Ce=t=>Math.round(t*1e4)/1e4,kv=()=>({usd:0,activeMs:0,turns:0,toolCalls:0,models:{},providers:{},byTrack:new Map});function xv(t,{since:e}){let{track:n,events:r,brief:o}=t,s=S=>xs(S)>=e,a=ks([Oc(n?.name),Oc(n?.workspace?.branch),o].filter(Boolean).join(" ")),i=new Map;for(let S of r)if(S.type==="subtask.delegated"&&S.boxId&&S.payload?.task){let T=ks(S.payload.task);T&&i.set(S.boxId,T)}let c=new Map,l=S=>{let T=S||"unattributed";return c.has(T)||c.set(T,{segments:[u(null)],lastTs:null,provider:null}),c.get(T)},u=S=>({textCat:S,tools:{},usd:0,tokens:{input:0,output:0,cacheRead:0,cacheWrite:0},models:{},provider:null,activeMs:0,turns:0,toolCalls:0}),d=[...r].sort((S,T)=>xs(S)-xs(T)),f=0,w=0,m=0,g={turns:0};for(let S of d){let T=l(S.boxId),E=T.segments[T.segments.length-1],J=xs(S);switch(T.lastTs!=null&&J>T.lastTs&&s(S)&&(E.activeMs+=Math.min(J-T.lastTs,bv)),T.lastTs=J,S.provider&&(T.provider=S.provider),S.type){case"turn.user":{let M=S.payload?.text;if(S.source==="hook"||!String(M||"").trim())break;let I=u(ks(M));T.segments.push(I),s(S)&&(I.turns+=1);break}case"tool.called":{if(S.source==="transcript")break;let M=S.payload?.tool;M&&(E.tools[M]=(E.tools[M]||0)+1),s(S)&&(E.toolCalls+=1);break}case"cost.recorded":{if(!s(S))break;let M=S.payload||{},I={input:M.tokens?.input||0,output:M.tokens?.output||0,cacheRead:M.tokens?.cacheRead||0,cacheWrite:M.tokens?.cacheWrite??M.tokens?.cacheCreation??0},h=Number(M.usd)||0;!h&&M.tokens&&(h=Kt(M.model,I)),!h&&M.tokens&&!jo(M.model)&&(g.turns+=1),E.usd+=h,E.tokens.input+=I.input,E.tokens.output+=I.output,E.tokens.cacheRead+=I.cacheRead,E.tokens.cacheWrite+=I.cacheWrite;let p=M.model||"unknown";E.models[p]=(E.models[p]||0)+h,S.provider&&(E.provider=S.provider);break}case"handoff.done":s(S)&&(f+=1);break;case"subtask.merged":s(S)&&(w+=1);break;case"error":s(S)&&S.payload?.kind==="subtask-failed"&&(m+=1);break;default:break}}let x=new Map,A=!1;for(let[S,T]of c)for(let E of T.segments){if(!(E.usd>0||E.activeMs>0||E.turns>0||E.toolCalls>0))continue;let M=E.textCat?.category??i.get(S)?.category??Fm(E.tools)?.category??a?.category??"uncategorized";x.has(M)||x.set(M,{usd:0,activeMs:0,turns:0,toolCalls:0,models:{},providers:{}});let I=x.get(M);I.usd+=E.usd,I.activeMs+=E.activeMs,I.turns+=E.turns,I.toolCalls+=E.toolCalls;for(let[p,b]of Object.entries(E.models))I.models[p]=(I.models[p]||0)+b;let h=E.provider||T.provider;h&&(I.providers[h]=(I.providers[h]||0)+E.usd),h==="cursor"&&E.usd===0&&(A=!0)}return{perCategory:x,handoffs:f,merged:w,failed:m,sawCursorActivity:A,unpricedTurns:g.turns}}function Wm(t,{windowMs:e=null,now:n=Date.now()}={}){let r=e==null?0:n-e,o=new Map,s=[],a=0,i=!1,c=0;for(let g of t||[]){let{track:x,events:A}=g;if(!A?.length){a+=1;continue}let{perCategory:S,handoffs:T,merged:E,failed:J,sawCursorActivity:M,unpricedTurns:I}=xv(g,{since:r});M&&(i=!0),c+=I;let h={trackId:x?.id,name:x?.name||x?.id,archived:!!x?.archivedAt,usd:0,activeMs:0,turns:0,categories:{},models:{},providers:{},handoffs:T,subtasksMerged:E,subtasksFailed:J};for(let[p,b]of S){h.usd+=b.usd,h.activeMs+=b.activeMs,h.turns+=b.turns,h.categories[p]={usd:Ce(b.usd),activeMs:b.activeMs};for(let[$,z]of Object.entries(b.models))h.models[$]=Ce((h.models[$]||0)+z);for(let[$,z]of Object.entries(b.providers))h.providers[$]=Ce((h.providers[$]||0)+z);o.has(p)||o.set(p,kv());let v=o.get(p);v.usd+=b.usd,v.activeMs+=b.activeMs,v.turns+=b.turns,v.toolCalls+=b.toolCalls;for(let[$,z]of Object.entries(b.models))v.models[$]=(v.models[$]||0)+z;for(let[$,z]of Object.entries(b.providers))v.providers[$]=(v.providers[$]||0)+z;let ot=v.byTrack.get(x?.id)||{usd:0,activeMs:0};ot.usd+=b.usd,ot.activeMs+=b.activeMs,v.byTrack.set(x?.id,ot)}(h.usd>0||h.activeMs>0||h.turns>0)&&(h.usd=Ce(h.usd),h.dominantCategory=Sv(h.categories),s.push(h))}let l=[...o.values()].reduce((g,x)=>g+x.usd,0),u=[...o.values()].reduce((g,x)=>g+x.activeMs,0),d=l>0?"usd":"time",f=d==="usd"?l:u,w=new Map((t||[]).map(g=>[g.track?.id,g.track?.name||g.track?.id])),m=[...o.entries()].map(([g,x])=>({category:g,sharePct:f>0?Math.round((d==="usd"?x.usd:x.activeMs)/f*100):0,shareBasis:d,usd:Ce(x.usd),activeMs:x.activeMs,turns:x.turns,toolCalls:x.toolCalls,models:Hm(x.models),providers:Hm(x.providers),topWorkstreams:[...x.byTrack.entries()].sort((A,S)=>S[1].usd-A[1].usd||S[1].activeMs-A[1].activeMs).slice(0,2).map(([A,S])=>({trackId:A,name:w.get(A),usd:Ce(S.usd),activeMs:S.activeMs}))})).sort((g,x)=>x.usd-g.usd||x.activeMs-g.activeMs);return s.sort((g,x)=>x.usd-g.usd||x.activeMs-g.activeMs),{since:r,capturedAt:n,totalUsd:Ce(l),activeMs:u,turns:[...o.values()].reduce((g,x)=>g+x.turns,0),bullets:m,workstreams:s,gaps:{tracksWithoutEvents:a,cursorCostUnknown:i,unpricedTurns:c}}}var Sv=t=>{let e=null;for(let[n,r]of Object.entries(t))(!e||r.usd>e.v.usd||r.usd===e.v.usd&&r.activeMs>e.v.activeMs)&&(e={cat:n,v:r});return e?.cat??null},Hm=t=>Object.fromEntries(Object.entries(t).map(([e,n])=>[e,Ce(n)]));var Cc={"24h":24*36e5,"7d":168*36e5,"30d":720*36e5,all:null};function Av(t){let e=String(t||"7d");return e in Cc?{key:e,ms:Cc[e]}:{key:"7d",ms:Cc["7d"]}}function _v(t,e){let n=new Set((e||[]).filter(o=>o.source==="orchestrator").map(o=>`${o.type}|${o.payload?.ts??"?"}`)),r=[...e||[]];for(let o of t?.events||[]){let s=To(o);!s||n.has(`${s.type}|${s.payload?.ts??"?"}`)||r.push({trackId:t.id,boxId:s.boxId??null,ts:o.at,type:s.type,actor:s.actor,provider:s.provider??null,source:s.source,payload:s.payload})}return r}async function Ev(t){if(!t?.cwd)return null;try{let n=(await vv.readFile(Tv.join(t.cwd,".bostrat","handoff","latest.md"),"utf8")).match(/^##\s*Objective\s*\n([\s\S]*?)(?:\n##\s|\n*$)/m);return n?n[1].trim().slice(0,1e3):null}catch{return null}}async function qm(t,{window:e="7d",now:n=Date.now()}={}){let{key:r,ms:o}=Av(e),s=[];for(let a of t||[]){let i=await B(a);s.push({track:a,events:_v(a,i),brief:await Ev(a)})}return{window:r,...Wm(s,{windowMs:o,now:n})}}import er from"node:fs/promises";import Iv from"node:fs";import ft from"node:path";import Rv from"node:os";import{execFile as $v}from"node:child_process";import{promisify as Ov}from"node:util";var Cv=Ov($v),Gm=process.env.BOSTRAT_STATE_DIR||ft.join(process.env.HOME||Rv.tmpdir(),".bostrat"),Mc=!1;async function Gr(t,e){let n=await er.stat(t).catch(()=>null);if(!n?.isFile())return!1;let r=await er.stat(e).catch(()=>null);return r&&r.size===n.size&&r.mtimeMs>=n.mtimeMs?!1:(await er.mkdir(ft.dirname(e),{recursive:!0}),await er.copyFile(t,e),!0)}var Mv=new Set([".claude",".codex",".credentials.json","auth.json",".claude.json","integrations.json"]);async function Kr(t,e){let n=0,r=await er.readdir(t,{withFileTypes:!0}).catch(()=>[]);for(let o of r){if(Mv.has(o.name))continue;let s=ft.join(t,o.name);o.isDirectory()?n+=await Kr(s,ft.join(e,o.name)):o.isFile()&&(n+=await Gr(s,ft.join(e,o.name))?1:0)}return n}async function Km({stateDir:t=Gm,dest:e=process.env.BOSTRAT_SYNC_DIR||`${Gm}-sync`,remote:n=process.env.BOSTRAT_STATE_REMOTE||null,tracks:r=null}={}){try{await er.mkdir(e,{recursive:!0});let o=0;o+=await Gr(ft.join(t,"tracks.json"),ft.join(e,"tracks.json"))?1:0,o+=await Gr(ft.join(t,"mocks.json"),ft.join(e,"mocks.json"))?1:0,o+=await Gr(ft.join(t,"runbooks.json"),ft.join(e,"runbooks.json"))?1:0,o+=await Kr(ft.join(t,"runbooks"),ft.join(e,"runbooks")),o+=await Kr(ft.join(t,"mock-archive"),ft.join(e,"mock-archive")),o+=await Gr(ft.join(t,"events.jsonl"),ft.join(e,"events.jsonl"))?1:0,o+=await Kr(ft.join(t,"events"),ft.join(e,"events"));for(let l of r||[])!l?.cwd||!l?.id||(o+=await Kr(ft.join(l.cwd,".bostrat"),ft.join(e,"workspaces",l.id,".bostrat")));let s=(...l)=>Cv("git",["-C",e,...l]);Iv.existsSync(ft.join(e,".git"))||(await s("init","-q"),await s("config","user.email","agent@bostrat.local"),await s("config","user.name","bostrat state sync")),n&&/^https:\/\/github\.com\//i.test(n)&&(await s("config","--get","credential.useHttpPath").then(()=>!0).catch(()=>!1)||await da(e).catch(()=>{}));let a=!1,{stdout:i}=await s("status","--porcelain");i.trim()&&(await s("add","-A"),await s("commit","-q","-m",`state sync ${new Date().toISOString()}`),a=!0);let c=!1;if(n&&(a||Mc))try{await s("push","-q",n,"HEAD:refs/heads/main"),c=!0,Mc=!1}catch{Mc=!0}return{staged:o,committed:a,pushed:c}}catch(o){return{staged:0,committed:!1,pushed:!1,error:o?.message||String(o)}}}import{execFile as Pv}from"node:child_process";import{promisify as Nv}from"node:util";import{randomUUID as Bv}from"node:crypto";import Dv from"node:fs";import jv from"node:os";import Lv from"node:path";var Pe=Nv(Pv),Ss=t=>new Promise(e=>setTimeout(e,t)),Ne=Ds,Pc=process.env.BOSTRAT_USAGE_PROBE_CWD||Lv.join(jv.homedir(),".bostrat","usage-probe"),Uv=Number(process.env.BOSTRAT_USAGE_IDLE_REAP_MS)||5*6e4,Fv=Number(process.env.BOSTRAT_USAGE_PANEL_TIMEOUT_MS)||15e3,Hv=Number(process.env.BOSTRAT_USAGE_READY_TIMEOUT_MS)||45e3,Vm=Number(process.env.BOSTRAT_USAGE_CACHE_MS)||2e4,Me=null,Vr=null,Jr=null;async function Wv(){try{return await Pe(O,C("has-session","-t",Ne)),!0}catch{return!1}}function qv(){Me&&clearTimeout(Me),Me=setTimeout(()=>Gv().catch(()=>{}),Uv),Me.unref&&Me.unref()}async function Gv(){Me&&(clearTimeout(Me),Me=null);try{await Pe(O,C("kill-session","-t",Ne))}catch{}}async function Kv(){let t=Date.now()+Hv;for(;;){let e=await Nc().catch(()=>"");if(/for agents|for shortcuts|bypass permissions on/i.test(e)){await Ss(500);return}if(Date.now()>t)throw new Error("usage probe didn't reach the prompt (trust/onboarding may be blocking)");await Ss(500)}}async function Vv(){if(await Wv())return;if(!await pe())throw new Error("host tmux server not running \u2014 start globo-tmux.service");Dv.mkdirSync(Pc,{recursive:!0}),await fe(Pc);let t=Bv();await Pe(O,C("new-session","-d","-s",Ne,"-c",Pc,"-x","200","-y","60"));let e=`${Bs} --session-id ${t}`;await Pe(O,C("send-keys","-t",Ne,e,"Enter")),await Kv()}async function Nc(){let{stdout:t}=await Pe(O,C("capture-pane","-t",Ne,"-p"));return t}async function Jv(){await Vv(),await Pe(O,C("send-keys","-t",Ne,"-l","/usage")),await Pe(O,C("send-keys","-t",Ne,"Enter"));let t=Date.now()+Fv,e="";for(;await Ss(400),e=await Nc(),!(/Current\s+(session|week)/i.test(e)||/\d+%\s*used/i.test(e)||Date.now()>t););return await Ss(900),e=await Nc(),await Pe(O,C("send-keys","-t",Ne,"Escape")).catch(()=>{}),qv(),e}function zv(t){let e=String(t||"").split(`
|
|
251
|
+
`).map(s=>s.replace(/\s+$/,"").trim()),n=s=>{let a=null,i=null;for(let c=s+1;c<Math.min(s+5,e.length)&&!/^Current\s/i.test(e[c]);c++){let l=e[c].match(/(\d+)%\s*used/i);l&&a===null&&(a=Number(l[1]));let u=e[c].match(/^Resets\s+(.+)$/i);u&&i===null&&(i=u[1].trim())}return a===null?null:{pct:a,resets:i||null}},r=s=>{let a=e.findIndex(i=>s.test(i));return a<0?null:n(a)},o=[];return e.forEach((s,a)=>{let i=s.match(/^Current week \(([^)]+)\)$/i);if(!i||/all models/i.test(i[1]))return;let c=n(a);c&&o.push({model:i[1].trim(),...c})}),{session:r(/^Current session\b/i),week:r(/^Current week \(all models\)/i)||r(/^Current week$/i),weekModels:o.length?o:null}}function Xv(t){let e=String(t||"").split(`
|
|
252
|
+
`).map(s=>s.replace(/\s+$/,"")),n=e.findIndex(s=>/What's contributing/i.test(s));if(n<0)return null;let r=/(Usage credits|Esc to cancel|d to day · w to week|^\s*d to day)/i,o=[];for(let s=n+1;s<e.length&&!r.test(e[s]);s++)e[s].trim()&&o.push(e[s].trim());return o.length?o:null}async function Jm({fresh:t=!1}={}){return!t&&Vr&&Date.now()-Vr.at<Vm?Vr.data:Jr||(Jr=(async()=>{try{let e=await Jv(),n=zv(e),r={...n,contributors:Xv(e),subscription:!!(n.session||n.week||n.weekModels),capturedAt:Date.now()};return Vr={at:Date.now(),data:r},r}catch(e){let n={session:null,week:null,contributors:null,subscription:!1,error:e?.message||String(e),capturedAt:Date.now()};return Vr={at:Date.now()-(Vm-3e3),data:n},n}finally{Jr=null}})(),Jr)}var Yv=262144;function Zv(t){let e=Number(t);if(!Number.isFinite(e)||e<0)return null;let n=Math.floor(e/3600),r=Math.round(e%3600/60);return n>=48?`in ${Math.round(n/24)}d`:n>0?`in ${n}h ${r}m`:`in ${Math.max(1,r)}m`}function zm(t){if(!t||typeof t!="object")return null;let e=Number(t.used_percent);return Number.isFinite(e)?{pct:Math.round(e),resets:Zv(t.resets_in_seconds)}:null}function Qv(t){let e=String(t||"").split(`
|
|
253
|
+
`),n=!1;for(let r=e.length-1;r>=0;r--){let o=e[r].trim();if(!o||o[0]!=="{"||!o.includes("token_count"))continue;let s;try{s=JSON.parse(o)}catch{continue}let a=s?.payload;if(a?.type!=="token_count")continue;n=!0;let i=a.rate_limits;if(!i)return{session:null,week:null,sawTokenCount:n};if(i.primary||i.secondary)return{session:zm(i.primary),week:zm(i.secondary),sawTokenCount:n};if(i.primary_used_percent!=null||i.secondary_used_percent!=null){let c=l=>Number.isFinite(Number(l))?{pct:Math.round(Number(l)),resets:null}:null;return{session:c(i.primary_used_percent),week:c(i.secondary_used_percent),sawTokenCount:n}}return{session:null,week:null,sawTokenCount:n}}return{session:null,week:null,sawTokenCount:n}}async function tT(t){let e={provider:"codex",supported:!0,contributors:null};try{let n=await Ge(t?.cwd,t?.sessionId);if(!n)return{...e,session:null,week:null,subscription:!1,error:"no codex rollout found for this box yet",capturedAt:Date.now()};let r=await vt(n.file,Yv),o=Qv(r);return{...e,session:o.session,week:o.week,subscription:!!(o.session||o.week),capturedAt:n.mtimeMs||Date.now()}}catch(n){return{...e,session:null,week:null,subscription:!1,error:n?.message||String(n),capturedAt:Date.now()}}}async function Xm(t,{fresh:e=!1}={}){let n=t?.agent||"claude";return n==="codex"?tT(t):n==="cursor"?{provider:"cursor",supported:!1,subscription:!1,session:null,week:null,contributors:null,capturedAt:Date.now()}:{provider:"claude",supported:!0,...await Jm({fresh:e})}}function Ym(t){let e=t.split(".");if(e.length!==4)return null;let n=0;for(let r of e){let o=Number(r);if(!Number.isInteger(o)||o<0||o>255)return null;n=n*256+o}return n>>>0}function eT(t){let[e,n]=t.split("/"),r=Ym(e);if(r==null)return null;let o=n==null?32:Number(n);if(!Number.isInteger(o)||o<0||o>32)return null;let s=o===0?0:4294967295<<32-o>>>0;return a=>(a&s)===(r&s)}function nT(t){return(t||"").split(",").map(e=>e.trim()).filter(Boolean).map(eT).filter(Boolean)}function rT(t){let e=t.headers["fly-client-ip"];if(typeof e=="string"&&e.trim())return e.trim();let n=t.headers["x-forwarded-for"];return typeof n=="string"&&n.trim()?n.split(",")[0].trim():(t.socket&&t.socket.remoteAddress||"").replace(/^::ffff:/,"")}function Zm(t){let e=nT(t),n=e.length===0;return{open:n,clientIp:rT,allowed(r){if(n)return!0;let o=Ym((r||"").replace(/^::ffff:/,""));return o==null?!1:e.some(s=>s(o))}}}import oT from"node:http";import sT from"node:net";function Qm(t){if(!t)return null;let e=String(t).replace(/:\d+$/,"").toLowerCase();if(/^[\d.]+$/.test(e)||e.includes(":"))return null;let n=e.split(".");if(n.length<3)return null;let r=n[0];return/^[a-z0-9-]+$/.test(r)?r:null}function th(t){let e=Qm(t.headers.host);return e?{slug:e,view:gi(e)}:null}var aT=t=>`<!doctype html><meta charset="utf-8">
|
|
239
254
|
<meta http-equiv="refresh" content="3">
|
|
240
255
|
<title>${t.name} \u2014 starting</title>
|
|
241
256
|
<body style="font-family:system-ui;background:#0b0e14;color:#dce3f0;display:grid;place-items:center;height:100vh;margin:0">
|
|
242
257
|
<div style="text-align:center"><p style="font-size:2rem;margin:0">\u23F3</p>
|
|
243
|
-
<p><b>${t.name}</b> is waking up\u2026</p><p style="opacity:.6">auto-refreshing</p></div>`;function
|
|
258
|
+
<p><b>${t.name}</b> is waking up\u2026</p><p style="opacity:.6">auto-refreshing</p></div>`;function eh(t,e,n){let r=th(t);if(!r)return n();let{view:o,slug:s}=r;if(!o)return e.statusCode=404,e.setHeader("content-type","text/plain"),e.end(`bostrat: no view named "${s}"`);if(o.state==="starting")return e.statusCode=503,e.setHeader("content-type","text/html"),e.end(aT(o));if(o.state!=="live"&&o.state!=="unhealthy")return e.statusCode=404,e.setHeader("content-type","text/plain"),e.end(`bostrat: view "${s}" is ${o.state} \u2014 restart it from the Views tab`);let a={...t.headers,host:`127.0.0.1:${o.port}`,"x-forwarded-host":t.headers.host,"x-forwarded-proto":t.headers["x-forwarded-proto"]||"http"},i=oT.request({host:"127.0.0.1",port:o.port,method:t.method,path:t.url,headers:a},c=>{e.writeHead(c.statusCode||502,c.headers),c.pipe(e)});i.on("error",()=>{e.headersSent?e.destroy():(e.statusCode=502,e.setHeader("content-type","text/plain"),e.end(`bostrat: view "${s}" did not answer on :${o.port}`))}),t.pipe(i),t.on("aborted",()=>i.destroy())}function nh(t,e,n){let r=th(t);if(!r)return!1;let{view:o,slug:s}=r;if(!o||o.state!=="live"&&o.state!=="unhealthy")return e.write(`HTTP/1.1 404 Not Found\r
|
|
244
259
|
\r
|
|
245
260
|
bostrat: no live view "${s}"\r
|
|
246
|
-
`),e.destroy(),!0;let a=
|
|
261
|
+
`),e.destroy(),!0;let a=sT.connect(o.port,"127.0.0.1",()=>{let c=[`${t.method} ${t.url} HTTP/1.1`];for(let[l,u]of Object.entries(t.headers))l!=="host"&&c.push(`${l}: ${Array.isArray(u)?u.join(", "):u}`);c.push(`host: 127.0.0.1:${o.port}`),a.write(c.join(`\r
|
|
247
262
|
`)+`\r
|
|
248
263
|
\r
|
|
249
|
-
`),n?.length&&a.write(n),e.pipe(a),a.pipe(e)}),i=()=>{a.destroy(),e.destroy()};return a.on("error",i),e.on("error",i),!0}function Vm(t,e){let n=typeof e=="string"?{host:e}:e||{},r=n["x-bostrat-view-base"];return typeof r=="string"&&r?`${n["x-forwarded-proto"]==="https"?"https":"http"}://${t.slug}.${r.replace(/:\d+$/,"")}/`:`http://${t.slug}.${Wv(n.host)}/`}function Wv(t){if(process.env.BOSTRAT_PUBLIC_HOST)return String(process.env.BOSTRAT_PUBLIC_HOST).replace(/:\d+$/,"");let e=String(t||"").replace(/:\d+$/,"").toLowerCase();if(!e||!e.includes(".")||/^[\d.]+$/.test(e))return"bostrat.globopi";let n=e.split(".");return Wm(e)&&ui(n[0])?n.slice(1).join("."):e}import{execFile as qv}from"node:child_process";import{promisify as Gv}from"node:util";var Cc=Gv(qv),Xm=3e4,Kv=30*6e4,Jm=Xm,Vv=5*6e4,Jv=10080*6e4,zv=60*6e4,Xv=3,Ym="number,url,title,headRefName,headRefOid,state",Oc=60*6e4,zm=new Map;function Yv(t,e){return[t.ciFix?.delegatedAt,t.reconcile?.delegatedAt,t.ciFixAwait?.since,t.reconcileAwait?.since].some(n=>n&&e-n<Oc)}function Zv(t,e){return t.armedAt&&e-t.armedAt<Kv||Yv(t,e)?Jm:e-(t.firstSeen||0)>Jv?zv:Vv}async function Qv(t,e){let{stdout:n}=await Cc("gh",["pr","view",String(e),"--repo",t,"--json",Ym],{timeout:3e4,maxBuffer:4194304,env:{...process.env,...await Gt({slug:t})}});return JSON.parse(n||"null")}async function tT(t){let{stdout:e}=await Cc("gh",["pr","list","--repo",t,"--state","all","--limit","100","--json",Ym],{timeout:3e4,maxBuffer:8388608,env:{...process.env,...await Gt({slug:t})}}),n=new Map;for(let r of JSON.parse(e||"[]"))n.set(Number(r.number),r);return n}async function eT(){let t=new Map;for(let e of await dt().catch(()=>[]))t.set(e.id,e);return t}async function nT(t,e){await R({id:t.id,cwd:t.cwd},{type:"pr.state.changed",actor:"user",source:"orchestrator",boxId:null,payload:{ts:Date.now(),change:"merged",...e}})}async function rT(t,e){let n=await gi(t);return n?Tr(n,e):null}async function oT(t,e,n){let r=await gi(t);if(!r)throw new Error(`no local checkout for ${t}`);return Ar(r,e,{method:n})}async function sT(t,e){await R({id:t.id,cwd:t.cwd},{type:"pr.state.changed",actor:"user",source:"orchestrator",boxId:null,payload:{ts:Date.now(),change:"auto-canceled",...e}})}async function ks(t,e,n,r,o){if(!await r.disarm(t.repo,t.number).catch(()=>null))return;let a=n.get(t.trackId);a&&await r.emitDisarmed(a,{number:Number(t.number),url:e?.url||t.url,title:e?.title||t.title,branch:e?.headRefName||t.headRef,repo:t.repo,reason:o}).catch(()=>{})}var aT=10*6e4;async function iT(t,e,n){let r=await n.detail(t.repo,t.number).catch(()=>null);if(!r)return;if(r.checks.failed>0){let s=r.checks.failing.map(a=>a.name).join(", ");await ks(t,r,e,n,`check${r.checks.failed===1?"":"s"} failed: ${s}`);return}if(r.mergeable==="CONFLICTING"){await ks(t,r,e,n,`conflicts with ${r.baseRefName}`);return}if(!r.checks.total){let s=Math.max(t.autoMerge?.armedAt||0,Date.parse(r.updatedAt||"")||0);if((n.now??Date.now())-s<aT)return;await ks(t,r,e,n,r.checks.unavailable?"this token can't read the checks":"no CI checks appeared");return}if(r.checks.pending>0||r.mergeable==="UNKNOWN"||r.mergeStateStatus==="UNKNOWN"||r.isDraft)return;let o=await n.lookup(t.repo,t.number).catch(()=>null);if(o?.autoMerge){try{await n.merge(t.repo,t.number,o.autoMerge.method||"merge")}catch(s){if(/already merged|not open|is closed/i.test(String(s?.message||s)))return;await ks(t,r,e,n,String(s?.message||s).trim()||"merge failed");return}await Zm(t,{...r,state:"MERGED"},e,n)}}async function Ic(t,e,n){await R({id:t.id,cwd:t.cwd},{type:"pr.state.changed",actor:"user",source:"orchestrator",boxId:null,payload:{ts:Date.now(),change:e,...n}})}async function cT(t,e){try{return await Cc("git",["-C",t.cwd,"cat-file","-e",`${e}^{commit}`],{timeout:1e4}),!0}catch{return!1}}async function Rc(t,e,n,r=()=>!0){return(await P(t).catch(()=>[])).some(s=>s.type==="pr.state.changed"&&s.payload?.change===e&&s.payload?.number===Number(n)&&r(s))}var $c=(t,e)=>({number:Number(t.number),url:e?.url||t.url,title:e?.title||t.title,branch:e?.headRefName||t.headRef,repo:t.repo});async function lT(t,e,n,r){let o=r.now??Date.now(),s=e.headRefOid||null,a=n.get(t.trackId)||null;if(s&&(t.ciFix?.headSha&&s!==t.ciFix.headSha&&await To(t.repo,t.number,{headSha:s}).catch(()=>{}),t.reconcile?.headSha&&s!==t.reconcile.headSha&&await _o(t.repo,t.number,{headSha:s}).catch(()=>{})),s&&s!==t.lastHeadSha){let i=!t.lastHeadSha,c=!!(t.ciFix||t.reconcile||t.ciFixAwait||t.reconcileAwait);await gd(t.repo,t.number,s).catch(()=>{}),!i&&!c&&a&&!await r.localHasCommit(a,s).catch(()=>!1)&&!await Rc(a,"pushed",t.number,u=>u.payload?.sha===s)&&await Ic(a,"pushed",{...$c(t,e),sha:s}).catch(()=>{})}if(t.ciFixAwait)if(o-(t.ciFixAwait.since||0)>Oc)await Ao(t.repo,t.number).catch(()=>{});else{let i=await r.detail(t.repo,t.number).catch(()=>null);if(i?.checks){if(i.checks.failed>0)await Ao(t.repo,t.number).catch(()=>{});else if(i.checks.total>0&&i.checks.pending===0){let c=t.ciFixAwait.since||0;a&&!await Rc(a,"ci-fixed",t.number,l=>(l.payload?.ts||0)>=c)&&await Ic(a,"ci-fixed",{...$c(t,e),passed:i.checks.passed}).catch(()=>{}),await Ao(t.repo,t.number).catch(()=>{})}}}if(t.reconcileAwait)if(o-(t.reconcileAwait.since||0)>Oc)await Eo(t.repo,t.number).catch(()=>{});else{let i=await r.detail(t.repo,t.number).catch(()=>null);if(i){if(i.mergeable==="CONFLICTING")await Eo(t.repo,t.number).catch(()=>{});else if(i.mergeable==="MERGEABLE"){let c=t.reconcileAwait.since||0;a&&!await Rc(a,"reconciled",t.number,l=>(l.payload?.ts||0)>=c)&&await Ic(a,"reconciled",{...$c(t,e),base:i.baseRefName||null}).catch(()=>{}),await Eo(t.repo,t.number).catch(()=>{})}}}}async function Zm(t,e,n,r){let o=n.get(t.trackId);o&&((await P(o).catch(()=>[])).some(i=>i.type==="pr.state.changed"&&i.payload?.change==="merged"&&i.payload?.number===Number(t.number))||await r.emitMerged(o,{number:Number(t.number),url:e.url||t.url,title:e.title||t.title,branch:e.headRefName||t.headRef,repo:t.repo}).catch(()=>{})),await r.mark(t.repo,t.number,"merged").catch(()=>{})}async function uT(t={}){let{now:e=Date.now(),auth:n=mi,view:r=Qv,list:o=tT,links:s=yd,trackMap:a=eT,emitMerged:i=nT,mark:c=ze,batchThreshold:l=Xv,detail:u=rT,merge:d=oT,lookup:f=Pt,disarm:w=Ro,emitDisarmed:h=sT,localHasCommit:g=cT}=t;if(!(await n().catch(()=>null))?.authenticated)return 0;let S=(await s().catch(()=>[])).filter(I=>e-(zm.get(kt(I.repo,I.number))||0)>=Zv(I,e));if(!S.length)return 0;let v=new Map;for(let I of S)zm.set(kt(I.repo,I.number),e),v.has(I.repo)||v.set(I.repo,[]),v.get(I.repo).push(I);let E=await a().catch(()=>new Map),J={now:e,emitMerged:i,mark:c,detail:u,merge:d,lookup:f,disarm:w,emitDisarmed:h,localHasCommit:g};for(let[I,M]of v){let m;if(M.length>l)m=await o(I).catch(()=>null);else{m=new Map;for(let p of M){let b=await r(I,p.number).catch(()=>null);b?.number!=null&&m.set(Number(b.number),b)}}if(m)for(let p of M){let b=m.get(Number(p.number));b&&(b.state==="MERGED"?await Zm(p,b,E,J):b.state==="CLOSED"?await c(p.repo,p.number,"closed").catch(()=>{}):(await lT(p,b,E,J).catch(()=>{}),p.autoMerge&&await iT(p,E,J).catch(()=>{})))}}return S.length}function Qm(){let t=!1;setInterval(async()=>{if(!t){t=!0;try{await uT()}catch{}finally{t=!1}}},Xm)}import de from"node:fs/promises";import Qn from"node:path";import ah from"node:os";import th from"node:fs/promises";import xs from"node:path";import{execFile as dT}from"node:child_process";import{promisify as fT}from"node:util";import{fileURLToPath as pT}from"node:url";var mT=fT(dT),nh=xs.dirname(pT(import.meta.url)),hT=xs.resolve(nh,"..","..","client","dist"),wT="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/",gT=new Map([...wT].map((t,e)=>[t,e]));function yT(t){let e=[],n=0,r=0;for(let o of t){let s=gT.get(o);if(s===void 0)return e;n+=(s&31)<<r,s&32?r+=5:(e.push(n&1?-(n>>>1):n>>>1),n=0,r=0)}return e}function bT(t){let e=[],n=0,r=0,o=0;for(let s of t.split(";")){let a=[],i=0;if(s)for(let c of s.split(",")){let l=yT(c);l.length!==0&&(i+=l[0],l.length>=4&&(n+=l[1],r+=l[2],o+=l[3],a.push([i,n,r,o])))}e.push(a)}return e}function kT(t,e){if(!t||t.length===0||t[0][0]>e)return null;let n=0,r=t.length-1;for(;n<r;){let o=n+r+1>>1;t[o][0]<=e?n=o:r=o-1}return t[n]}var eh=new Map;async function xT(t){let e;try{e=await th.stat(t)}catch{return null}let n=eh.get(t);if(n&&n.mtimeMs===e.mtimeMs)return n;try{let r=JSON.parse(await th.readFile(t,"utf8")),o={mtimeMs:e.mtimeMs,sources:r.sources||[],lines:bT(r.mappings||"")};return eh.set(t,o),o}catch{return null}}var Mc=null;function ST(){return Mc||(Mc=process.env.BOSTRAT_RELEASE?Promise.resolve(process.env.BOSTRAT_RELEASE):mT("git",["rev-parse","--short","HEAD"],{cwd:xs.resolve(nh,"..","..")}).then(t=>t.stdout.trim()).catch(()=>null)),Mc}var vT=/(?:https?:\/\/[^\s()]*)?\/assets\/([^\s()/]+\.js):(\d+):(\d+)/g;async function TT(t,{distDir:e=hT}={}){let n=[],r=0,o=!1;for(let s of t.matchAll(vT)){let[a,i,c,l]=s,u=await xT(xs.join(e,"assets",`${i}.map`)),d=u?kT(u.lines[Number(c)-1],Number(l)-1):null;if(n.push(t.slice(r,s.index)),d&&u.sources[d[1]]){let f=u.sources[d[1]].replace(/^(\.\.\/)+/,"");n.push(`${f}:${d[2]+1}:${d[3]+1}`),o=!0}else n.push(a);r=s.index+a.length}return n.push(t.slice(r)),{stack:n.join(""),symbolicated:o}}async function rh(t,e={}){try{if(!t?.error?.stack||!t.release||t.release==="unknown")return t;let n=e.release??await ST();if(!n||t.release!==n)return t;let{stack:r,symbolicated:o}=await TT(t.error.stack,e);o&&(t.error.stack=r,t.error.symbolicated=!0)}catch{}return t}var AT=()=>process.env.BOSTRAT_STATE_DIR||Qn.join(process.env.HOME||ah.tmpdir(),".bostrat"),Be=()=>Qn.join(AT(),"telemetry"),ih=()=>Qn.join(Be(),".relay-offsets.json"),_T=50,ET=14,IT=3e4,oh=500,Ot=(t,e)=>typeof t=="string"?t.slice(0,e):"";function RT(t,e){if(!t||typeof t!="object")return null;let n=Ot(t.name,64);if(!n)return null;let r=Number.isFinite(t.ts)?t.ts:e,o={name:n,ts:Math.abs(r-e)<168*3600*1e3?r:e,receivedAt:e,anonId:Ot(t.anonId,64),sessionId:Ot(t.sessionId,64),userId:Ot(t.userId,64),release:Ot(t.release,64),appVersion:Ot(t.appVersion,32),route:Ot(t.route,128),deviceClass:t.deviceClass==="phone"||t.deviceClass==="desktop"?t.deviceClass:""};return t.props&&typeof t.props=="object"&&!Array.isArray(t.props)&&(JSON.stringify(t.props).length<=8192?o.props=t.props:o.props={clamped:!0}),t.error&&typeof t.error=="object"&&(o.error={kind:Ot(t.error.kind,256),message:Ot(t.error.message,8192),stack:Ot(t.error.stack,32768),fingerprint:Ot(t.error.fingerprint,64)}),Array.isArray(t.breadcrumbs)&&(o.breadcrumbs=t.breadcrumbs.slice(-50).map(s=>({t:Number.isFinite(s?.t)?s.t:0,n:Ot(s?.n,64),r:Ot(s?.r,128)}))),o}var ch=t=>new Date(t).toISOString().slice(0,10),sh=0;async function lh(t,e=Date.now()){let r=(Array.isArray(t?.events)?t.events.slice(0,_T):[]).map(s=>RT(s,e)).filter(Boolean);if(!r.length)return 0;for(let s of r)s.error?.stack&&await rh(s);await de.mkdir(Be(),{recursive:!0});let o=Qn.join(Be(),`${ch(e)}.ndjson`);return await de.appendFile(o,r.map(s=>JSON.stringify(s)+`
|
|
250
|
-
`).join(""),"utf8"),e-
|
|
251
|
-
`).filter(Boolean),d=[];for(let f of u)try{d.push(JSON.parse(f))}catch{}for(let f=0;f<d.length;f+=oh){let w=d.slice(f,f+oh).map(g=>OT(g)),h=await t(`${e.url.replace(/\/$/,"")}/v1/logs`,{method:"POST",headers:{"content-type":"application/json",authorization:`Bearer ${e.ingestKey}`},body:JSON.stringify({events:w})}).catch(()=>null);if(!h||!h.ok)return{sent:o,disabled:!1};o+=w.length}r[s]=i+l+1,await fh(r)}return{sent:o,disabled:!1}}var Nc=null,Pc=!1;function ph(){Nc||(Nc=setInterval(async()=>{if(!Pc){Pc=!0;try{await CT()}catch(t){console.error("[telemetry] relay failed:",t?.message||t)}finally{Pc=!1}}},IT),Nc.unref?.())}import Ts from"node:fs/promises";import yh from"node:path";import MT from"node:os";var bh=()=>process.env.BOSTRAT_STATE_DIR||yh.join(process.env.HOME||MT.tmpdir(),".bostrat"),kh=()=>yh.join(bh(),"dictation-glossary.json"),NT=100,PT=500,BT=["bostrat","globopi","codex","tmux","worktree","Deepgram","Linear","Firecracker","Tigris","WireGuard","misous","postyx","cedar","gerbil","Anthropic","Opus","Haiku","Sonnet","keyterm","monorepo","dogfood"],As=null,Bc=null,Dc=Promise.resolve();function mh(){let t={},e=Date.now();for(let n of BT)t[n.toLowerCase()]={term:n,count:1,lastUsed:e,source:"seed"};return t}async function xh(){return As||(Bc??=Ts.readFile(kh(),"utf8").then(t=>JSON.parse(t)?.terms??mh()).catch(()=>mh()).then(t=>As=t).finally(()=>Bc=null),Bc)}async function hh(){let t=JSON.stringify({terms:As},null,2),e=kh(),n=`${e}.${process.pid}.tmp`;await Ts.mkdir(bh(),{recursive:!0}),await Ts.writeFile(n,t),await Ts.rename(n,e)}function DT(){return Dc=Dc.then(hh,hh),Dc.catch(()=>{})}async function jc(){return Object.values(await xh()).sort((e,n)=>n.count-e.count||n.lastUsed-e.lastUsed)}var jT=t=>Math.max(1,Math.ceil(t.length/4));async function LT(){let t=[],e=0;for(let{term:n}of await jc()){let r=jT(n);if(!(e+r>PT)&&(t.push(n),e+=r,t.length>=NT))break}return t}async function UT(t,e){let n=await xh(),r=t.toLowerCase(),o=n[r];n[r]={term:t,count:(o?.count??0)+1,lastUsed:Date.now(),source:o?.source??e}}var wh=t=>(t||"").split(/\s+/).map(e=>e.replace(/^[^\p{L}\p{N}]+|[^\p{L}\p{N}]+$/gu,"")).filter(Boolean),vs=t=>t.toLowerCase();function gh(t){return!new RegExp("\\p{L}","u").test(t)||t.length<3?!1:/^[a-z]+$/.test(t)?t.length>=4:!0}async function Sh(t,e){let n=wh(t),r=wh(e);if(!n.length||!r.length)return[];let o=n.length,s=r.length,a=Array.from({length:o+1},()=>new Array(s+1).fill(0));for(let u=o-1;u>=0;u--)for(let d=s-1;d>=0;d--)a[u][d]=vs(n[u])===vs(r[d])?a[u+1][d+1]+1:Math.max(a[u+1][d],a[u][d+1]);let i=new Set,c=0,l=0;for(;c<o&&l<s;)vs(n[c])===vs(r[l])?(c++,l++):a[c+1][l]>=a[c][l+1]?c++:(gh(r[l])&&i.add(r[l]),l++);for(;l<s;l++)gh(r[l])&&i.add(r[l]);if(!i.size)return[];for(let u of i)await UT(u,"learned");return await DT(),[...i]}var Kr=null;function FT(t,e){let n=null;try{n=JSON.parse(e)}catch{}let r=n?.err_code||n?.error_code||n?.category||"";return t===402||/PAYMENT_REQUIRED/i.test(r)?{status:402,code:"credits_exhausted",persistent:!0,message:"Deepgram credits are exhausted \u2014 dictation is down until you top up at console.deepgram.com or drop a new key into ~/.bostrat/integrations.json."}:t===401||t===403?{status:502,code:"key_invalid",persistent:!0,message:"Deepgram rejected the API key (revoked, or missing ASR scope) \u2014 replace it in ~/.bostrat/integrations.json."}:{status:502,code:"transcription_failed",persistent:!1,message:`Deepgram error ${t}: ${e.slice(0,300)}`}}async function vh(){if(!Kr)return null;let t=await Nr();if(!t||t.apiKey.slice(-6)!==Kr.keyTail)return null;let{keyTail:e,...n}=Kr;return n}async function Th(t,e){let n=await Nr();if(!n){let i=new Error("Deepgram is not configured \u2014 add deepgram.apiKey to ~/.bostrat/integrations.json");throw i.status=503,i.code="not_configured",i}let r=new URL("https://api.deepgram.com/v1/listen");r.searchParams.set("model","nova-3"),r.searchParams.set("smart_format","true");for(let i of await LT())r.searchParams.append("keyterm",i);let o=await fetch(r,{method:"POST",headers:{Authorization:`Token ${n.apiKey}`,"Content-Type":e||"application/octet-stream"},body:t});if(!o.ok){let i=await o.text().catch(()=>""),c=FT(o.status,i);c.persistent&&(Kr={code:c.code,message:c.message,at:Date.now(),keyTail:n.apiKey.slice(-6)});let l=new Error(c.message);throw l.status=c.status,l.code=c.code,l}return Kr=null,{transcript:(await o.json())?.results?.channels?.[0]?.alternatives?.[0]?.transcript??""}}import HT from"node:path";import WT from"node:os";var QO=process.env.BOSTRAT_STATE_DIR||HT.join(process.env.HOME||WT.tmpdir(),".bostrat");function Ah(t=process.env){let e=(t.BOSTRAT_HOST||"").trim();if(e!=="all")return e||(t.BOSTRAT_PLANE_URL?"127.0.0.1":void 0)}var $h=ct.dirname(GT(import.meta.url)),_h=Number(process.env.PORT)||3362,_s=Ah(process.env),Oh=Number(process.env.BOSTRAT_AUTO_TRIGGER_PCT)||80,tr=Hm(process.env.BOSTRAT_ALLOW_IPS),k=fe();k.disable("x-powered-by");k.use((t,e,n)=>{if(tr.allowed(tr.clientIp(t)))return n();e.status(403).type("text/plain").send("bostrat: access restricted to the allowlist")});k.use(Gm);k.use("/api/mocks",fe.json({limit:"1mb"}));k.use("/api/mocks",fe.raw({type:"image/png",limit:"24mb"}));k.use("/api/runbooks",fe.json({limit:"1mb"}));k.use("/api/telemetry",fe.json({limit:"256kb"}));k.use("/api/dictation",fe.raw({type:["audio/*","video/mp4","application/octet-stream"],limit:"32mb"}));k.use("/api/tracks/:id/upload",fe.raw({type:()=>!0,limit:"25mb"}));k.use(fe.json({limit:"64kb"}));k.get("/api/health",async(t,e)=>{e.json({ok:!0,tmuxSocket:Ht,hostTmuxAlive:await ee(),autoTriggerPct:Oh})});k.get("/api/sessions",async(t,e)=>{e.json({sessions:await yn()})});k.post("/api/sessions",async(t,e)=>{let{displayName:n,cwd:r}=t.body||{};if(typeof n!="string")return e.status(400).json({error:"displayName (string) required"});try{let o=await Ue(n,r||void 0);e.json({ok:!0,...o})}catch(o){let s=o?.message||String(o),a=/already exists/i.test(s)?409:/must be|invalid/i.test(s)?400:500;e.status(a).json({error:s})}});k.delete("/api/sessions/:name",async(t,e)=>{let n=decodeURIComponent(t.params.name);if(!ut(n))return e.status(400).json({error:"invalid session name"});if(!await q(n))return e.status(404).json({error:"session not found"});try{await Wt(n),e.json({ok:!0})}catch(r){e.status(500).json({error:`failed to kill: ${r?.message||r}`})}});k.get("/api/context/:name",async(t,e)=>{let n=decodeURIComponent(t.params.name);if(!ut(n))return e.status(400).json({error:"invalid session name"});let r=await q(n);if(!r)return e.status(404).json({error:"session not found"});let o=await sr(r.cwd,r.sessionId);if(!o)return e.json({available:!1,cwd:r.cwd,sessionId:r.sessionId||null});e.json({available:!0,...o})});k.post("/api/handoff/:name",async(t,e)=>{let n=decodeURIComponent(t.params.name);if(!ut(n))return e.status(400).json({error:"invalid session name"});try{let r=await _l(n);e.json({ok:!0,step:r.step,file:r.file,startedAt:r.startedAt})}catch(r){e.status(409).json({error:r?.message||String(r)})}});k.get("/api/handoff/:name",(t,e)=>{let n=decodeURIComponent(t.params.name),r=co(n);if(!r)return e.json({running:!1});e.json({running:!r.finishedAt,step:r.step,error:r.error,file:r.file,startedAt:r.startedAt,finishedAt:r.finishedAt})});k.get("/api/tracks",async(t,e)=>{e.json({tracks:await dt(),agents:Bi,tickets:await nm()})});k.get("/api/cost",async(t,e)=>{try{let n=await Rm(await dt());if(n.cursorUsage=Ld(),t.query.format==="text")return e.type("text/plain").send($m(n));e.json(n)}catch(n){e.status(500).json({error:n?.message||String(n)})}});k.get("/api/usage/roundup",async(t,e)=>{try{e.json(await Nm(await dt(),{window:t.query.window}))}catch(n){e.status(500).json({error:n?.message||String(n)})}});k.post("/api/hook",async(t,e)=>{try{let n=await vm(t.body||{},dp);e.json({ok:!0,events:n.length})}catch(n){e.json({ok:!1,error:n?.message||String(n)})}});k.post("/api/telemetry",async(t,e)=>{try{if(!uh(tr.clientIp(t)))return e.json({ok:!0,dropped:!0});let n=await lh(t.body||{});e.json({ok:!0,events:n})}catch(n){e.json({ok:!1,error:n?.message||String(n)})}});k.get("/api/repos",async(t,e)=>{try{let n=await oe(),r=new Set((await Promise.all(n.map(a=>Z(a.path)))).filter(Boolean).map(a=>a.toLowerCase())),s=((await lr()).repos||[]).filter(a=>!r.has(String(a.slug).toLowerCase())).map(a=>({slug:a.slug,name:a.slug.split("/")[1],branch:a.defaultBranch||null,private:!!a.private,remote:!0}));e.json({repos:[...n,...s]})}catch(n){e.status(500).json({error:n?.message||String(n)})}});k.get("/api/github/token",async(t,e)=>{try{e.json(await fo(String(t.query.repo||"")))}catch(n){e.status(n?.status||502).json({error:n?.message||String(n)})}});k.get("/api/github/status",(t,e)=>{e.json({configured:uo()})});k.post("/api/github/refresh",(t,e)=>{Ul(),e.json({ok:!0})});async function Ch({repo:t=null,number:e=null,branch:n=null}){let r=t?await Z(t):null;if(r&&e!=null){let s=await Pt(r,e),a=s&&await rt(s.trackId);if(a)return a}return n?(await dt()).find(s=>s.workspace?.branch===n&&(!s.workspace?.base||!t||s.workspace.base===t||r&&s.workspace.repo===r))??null:null}async function un(t,{number:e,url:n=null,title:r=null,branch:o=null,repo:s=null,track:a=null,extra:i={}}){let c=a||await Ch({repo:s,number:e,branch:o}).catch(()=>null);c&&await R({id:c.id,cwd:c.cwd},{type:"pr.state.changed",actor:"user",source:"orchestrator",payload:{ts:Date.now(),change:t,number:e,url:n,title:r,branch:o??c.workspace?.branch??null,...i}}).catch(()=>{})}async function zT(t){let e=(await dt()).filter(n=>!n.archivedAt);for(let n of t??[]){if(!n.prs?.length)continue;let r=await Z(n.path);if(r)for(let o of n.prs){if(await Pt(r,o.number))continue;let s=e.find(a=>a.workspace?.branch===o.headRefName&&(!a.workspace?.base||a.workspace.base===n.path||a.workspace.repo===r));s&&await Nt({repo:r,number:o.number,trackId:s.id,url:o.url,headRef:o.headRefName,title:o.title,source:"matched"})}}}k.get("/api/git/prs",async(t,e)=>{try{let n=await ff();await zT(n.repos).catch(()=>{}),e.json(n)}catch(n){e.status(500).json({error:n?.message||String(n)})}});k.post("/api/git/prs/merge",async(t,e)=>{let{repo:n,number:r,method:o,auto:s,headRefName:a,title:i,url:c}=t.body||{};try{if(s){let d=o||"merge";if(!qo.has(d))throw new Error(`invalid merge method: ${d}`);let f=Number(r);if(!Number.isInteger(f)||f<=0)throw new Error("invalid PR number");let w=await Z((await Te(n)).path);if(!w)throw new Error("repo has no GitHub remote");let h=await Ch({repo:n,number:f,branch:a}).catch(()=>null);if(!h)throw new Error("auto-merge needs a Track to watch this PR \u2014 none owns it");if(await Nt({repo:w,number:f,trackId:h.id,url:c,headRef:a,title:i,source:"matched"}).catch(()=>{}),!await Io(w,f,{merge:{method:d}}))throw new Error("couldn't record the auto-merge arm");return Xe(),await un("auto-armed",{number:f,url:c,title:i,branch:a,repo:n,track:h}),e.json({ok:!0,number:f,auto:!0})}let l=await Ar(n,r,{method:o||"squash"});await un("merged",{number:l.number,url:c,title:i,branch:a,repo:n});let u=await Z(n).catch(()=>null);u&&await ze(u,l.number,"merged").catch(()=>{}),e.json(l)}catch(l){let u=l?.message||String(l),d=/invalid|unknown repo|required|needs a Track|no GitHub remote/i.test(u)?400:502;e.status(d).json({error:u})}});k.post("/api/git/prs/disarm-auto",async(t,e)=>{let{repo:n,number:r,headRefName:o,title:s,url:a}=t.body||{};try{let i=Number(r);if(!Number.isInteger(i)||i<=0)throw new Error("invalid PR number");let c=await Z((await Te(n)).path).catch(()=>null),l=c?await Ro(c,i).catch(()=>null):null;try{await pf(n,i)}catch(u){if(!l)throw u}Xe(),await un("auto-canceled",{number:i,url:a,title:s,branch:o,repo:n}),e.json({ok:!0,number:i})}catch(i){let c=i?.message||String(i),l=/invalid|unknown repo|required/i.test(c)?400:502;e.status(l).json({error:c})}});k.post("/api/git/prs/merge-all",async(t,e)=>{let{repo:n,method:r}=t.body||{};try{let o=await Z(n).catch(()=>null),s=await wf(n,{method:r||"merge",onMerged:a=>{un("merged",{number:a.number,url:a.url,title:a.title,branch:a.headRefName,repo:n}),o&&ze(o,a.number,"merged").catch(()=>{})}});e.json(s)}catch(o){let s=o?.message||String(o),a=/already running/i.test(s)?409:/invalid|unknown repo|no PRs ready/i.test(s)?400:502;e.status(a).json({error:s})}});k.get("/api/git/prs/merge-all",(t,e)=>{e.json({jobs:hf()})});k.post("/api/git/prs/open",async(t,e)=>{let{trackId:n,title:r,body:o,base:s}=t.body||{};try{let a=n?await rt(n):null;if(!a)return e.status(400).json({error:"trackId required"});let i=await gf(a.cwd,{title:r,body:o,base:s}),c=await Z(a.cwd).catch(()=>null);c&&i.number&&await Nt({repo:c,number:i.number,trackId:a.id,url:i.url,headRef:a.workspace?.branch??null,title:r,source:"api"}),await un("opened",{number:i.number,url:i.url,title:r,track:a,branch:a.workspace?.branch}),e.json(i)}catch(a){let i=a?.message||String(a),c=/required|invalid/i.test(i)?400:502;e.status(c).json({error:i})}});k.post("/api/git/prs/fix",async(t,e)=>{let{repo:n,number:r}=t.body||{};try{let o=await Tr(n,r),a=(await dt()).find(w=>!w.archivedAt&&w.workspace?.branch===o.headRefName);if(!a)return e.status(404).json({error:`no active Track owns branch "${o.headRefName}"`});let i=await yf(n,o.number);if(!i.length)return e.status(409).json({error:"no failing checks on this PR"});let c=ct.join(a.cwd,".bostrat","ci"),l=ct.join(".bostrat","ci",`pr-${o.number}-fail.log`);await Et.promises.mkdir(c,{recursive:!0}),await Et.promises.writeFile(ct.join(a.cwd,l),i.map(w=>`\u2550\u2550\u2550\u2550 ${w.name}${w.link?` \xB7 ${w.link}`:""} \u2550\u2550\u2550\u2550
|
|
264
|
+
`),n?.length&&a.write(n),e.pipe(a),a.pipe(e)}),i=()=>{a.destroy(),e.destroy()};return a.on("error",i),e.on("error",i),!0}function rh(t,e){let n=typeof e=="string"?{host:e}:e||{},r=n["x-bostrat-view-base"];return typeof r=="string"&&r?`${n["x-forwarded-proto"]==="https"?"https":"http"}://${t.slug}.${r.replace(/:\d+$/,"")}/`:`http://${t.slug}.${iT(n.host)}/`}function iT(t){if(process.env.BOSTRAT_PUBLIC_HOST)return String(process.env.BOSTRAT_PUBLIC_HOST).replace(/:\d+$/,"");let e=String(t||"").replace(/:\d+$/,"").toLowerCase();if(!e||!e.includes(".")||/^[\d.]+$/.test(e))return"bostrat.globopi";let n=e.split(".");return Qm(e)&&gi(n[0])?n.slice(1).join("."):e}import{execFile as cT}from"node:child_process";import{promisify as lT}from"node:util";var Uc=lT(cT),ah=3e4,uT=30*6e4,oh=ah,dT=5*6e4,fT=10080*6e4,pT=60*6e4,mT=3,ih="number,url,title,headRefName,headRefOid,state",Lc=60*6e4,sh=new Map;function hT(t,e){return[t.ciFix?.delegatedAt,t.reconcile?.delegatedAt,t.ciFixAwait?.since,t.reconcileAwait?.since].some(n=>n&&e-n<Lc)}function wT(t,e){return t.armedAt&&e-t.armedAt<uT||hT(t,e)?oh:e-(t.firstSeen||0)>fT?pT:dT}async function gT(t,e){let{stdout:n}=await Uc("gh",["pr","view",String(e),"--repo",t,"--json",ih],{timeout:3e4,maxBuffer:4194304,env:{...process.env,...await Gt({slug:t})}});return JSON.parse(n||"null")}async function yT(t){let{stdout:e}=await Uc("gh",["pr","list","--repo",t,"--state","all","--limit","100","--json",ih],{timeout:3e4,maxBuffer:8388608,env:{...process.env,...await Gt({slug:t})}}),n=new Map;for(let r of JSON.parse(e||"[]"))n.set(Number(r.number),r);return n}async function bT(){let t=new Map;for(let e of await dt().catch(()=>[]))t.set(e.id,e);return t}async function kT(t,e){await R({id:t.id,cwd:t.cwd},{type:"pr.state.changed",actor:"user",source:"orchestrator",boxId:null,payload:{ts:Date.now(),change:"merged",...e}})}async function xT(t,e){let n=await Ti(t);return n?Er(n,e):null}async function ST(t,e,n){let r=await Ti(t);if(!r)throw new Error(`no local checkout for ${t}`);return Ir(r,e,{method:n})}async function vT(t,e){await R({id:t.id,cwd:t.cwd},{type:"pr.state.changed",actor:"user",source:"orchestrator",boxId:null,payload:{ts:Date.now(),change:"auto-canceled",...e}})}async function vs(t,e,n,r,o){if(!await r.disarm(t.repo,t.number).catch(()=>null))return;let a=n.get(t.trackId);a&&await r.emitDisarmed(a,{number:Number(t.number),url:e?.url||t.url,title:e?.title||t.title,branch:e?.headRefName||t.headRef,repo:t.repo,reason:o}).catch(()=>{})}var TT=10*6e4;async function AT(t,e,n){let r=await n.detail(t.repo,t.number).catch(()=>null);if(!r)return;if(r.checks.failed>0){let s=r.checks.failing.map(a=>a.name).join(", ");await vs(t,r,e,n,`check${r.checks.failed===1?"":"s"} failed: ${s}`);return}if(r.mergeable==="CONFLICTING"){await vs(t,r,e,n,`conflicts with ${r.baseRefName}`);return}if(!r.checks.total){let s=Math.max(t.autoMerge?.armedAt||0,Date.parse(r.updatedAt||"")||0);if((n.now??Date.now())-s<TT)return;await vs(t,r,e,n,r.checks.unavailable?"this token can't read the checks":"no CI checks appeared");return}if(r.checks.pending>0||r.mergeable==="UNKNOWN"||r.mergeStateStatus==="UNKNOWN"||r.isDraft)return;let o=await n.lookup(t.repo,t.number).catch(()=>null);if(o?.autoMerge){try{await n.merge(t.repo,t.number,o.autoMerge.method||"merge")}catch(s){if(/already merged|not open|is closed/i.test(String(s?.message||s)))return;await vs(t,r,e,n,String(s?.message||s).trim()||"merge failed");return}await ch(t,{...r,state:"MERGED"},e,n)}}async function Bc(t,e,n){await R({id:t.id,cwd:t.cwd},{type:"pr.state.changed",actor:"user",source:"orchestrator",boxId:null,payload:{ts:Date.now(),change:e,...n}})}async function _T(t,e){try{return await Uc("git",["-C",t.cwd,"cat-file","-e",`${e}^{commit}`],{timeout:1e4}),!0}catch{return!1}}async function Dc(t,e,n,r=()=>!0){return(await B(t).catch(()=>[])).some(s=>s.type==="pr.state.changed"&&s.payload?.change===e&&s.payload?.number===Number(n)&&r(s))}var jc=(t,e)=>({number:Number(t.number),url:e?.url||t.url,title:e?.title||t.title,branch:e?.headRefName||t.headRef,repo:t.repo});async function ET(t,e,n,r){let o=r.now??Date.now(),s=e.headRefOid||null,a=n.get(t.trackId)||null;if(s&&(t.ciFix?.headSha&&s!==t.ciFix.headSha&&await Io(t.repo,t.number,{headSha:s}).catch(()=>{}),t.reconcile?.headSha&&s!==t.reconcile.headSha&&await $o(t.repo,t.number,{headSha:s}).catch(()=>{})),s&&s!==t.lastHeadSha){let i=!t.lastHeadSha,c=!!(t.ciFix||t.reconcile||t.ciFixAwait||t.reconcileAwait);await Td(t.repo,t.number,s).catch(()=>{}),!i&&!c&&a&&!await r.localHasCommit(a,s).catch(()=>!1)&&!await Dc(a,"pushed",t.number,u=>u.payload?.sha===s)&&await Bc(a,"pushed",{...jc(t,e),sha:s}).catch(()=>{})}if(t.ciFixAwait)if(o-(t.ciFixAwait.since||0)>Lc)await Ro(t.repo,t.number).catch(()=>{});else{let i=await r.detail(t.repo,t.number).catch(()=>null);if(i?.checks){if(i.checks.failed>0)await Ro(t.repo,t.number).catch(()=>{});else if(i.checks.total>0&&i.checks.pending===0){let c=t.ciFixAwait.since||0;a&&!await Dc(a,"ci-fixed",t.number,l=>(l.payload?.ts||0)>=c)&&await Bc(a,"ci-fixed",{...jc(t,e),passed:i.checks.passed}).catch(()=>{}),await Ro(t.repo,t.number).catch(()=>{})}}}if(t.reconcileAwait)if(o-(t.reconcileAwait.since||0)>Lc)await Oo(t.repo,t.number).catch(()=>{});else{let i=await r.detail(t.repo,t.number).catch(()=>null);if(i){if(i.mergeable==="CONFLICTING")await Oo(t.repo,t.number).catch(()=>{});else if(i.mergeable==="MERGEABLE"){let c=t.reconcileAwait.since||0;a&&!await Dc(a,"reconciled",t.number,l=>(l.payload?.ts||0)>=c)&&await Bc(a,"reconciled",{...jc(t,e),base:i.baseRefName||null}).catch(()=>{}),await Oo(t.repo,t.number).catch(()=>{})}}}}async function ch(t,e,n,r){let o=n.get(t.trackId);o&&((await B(o).catch(()=>[])).some(i=>i.type==="pr.state.changed"&&i.payload?.change==="merged"&&i.payload?.number===Number(t.number))||await r.emitMerged(o,{number:Number(t.number),url:e.url||t.url,title:e.title||t.title,branch:e.headRefName||t.headRef,repo:t.repo}).catch(()=>{})),await r.archiveIfArmed(t.repo,t.number).catch(()=>{}),await r.mark(t.repo,t.number,"merged").catch(()=>{})}async function IT(t={}){let{now:e=Date.now(),auth:n=xi,view:r=gT,list:o=yT,links:s=Ad,trackMap:a=bT,emitMerged:i=kT,mark:c=Qe,archiveIfArmed:l=tn,batchThreshold:u=mT,detail:d=xT,merge:f=ST,lookup:w=Tt,disarm:m=Mo,emitDisarmed:g=vT,localHasCommit:x=_T}=t;if(!(await n().catch(()=>null))?.authenticated)return 0;let T=(await s().catch(()=>[])).filter(I=>e-(sh.get(bt(I.repo,I.number))||0)>=wT(I,e));if(!T.length)return 0;let E=new Map;for(let I of T)sh.set(bt(I.repo,I.number),e),E.has(I.repo)||E.set(I.repo,[]),E.get(I.repo).push(I);let J=await a().catch(()=>new Map),M={now:e,emitMerged:i,mark:c,archiveIfArmed:l,detail:d,merge:f,lookup:w,disarm:m,emitDisarmed:g,localHasCommit:x};for(let[I,h]of E){let p;if(h.length>u)p=await o(I).catch(()=>null);else{p=new Map;for(let b of h){let v=await r(I,b.number).catch(()=>null);v?.number!=null&&p.set(Number(v.number),v)}}if(p)for(let b of h){let v=p.get(Number(b.number));v&&(v.state==="MERGED"?await ch(b,v,J,M):v.state==="CLOSED"?await c(b.repo,b.number,"closed").catch(()=>{}):(await ET(b,v,J,M).catch(()=>{}),b.autoMerge&&await AT(b,J,M).catch(()=>{})))}}return T.length}function lh(){let t=!1;setInterval(async()=>{if(!t){t=!0;try{await IT()}catch{}finally{t=!1}}},ah)}import ue from"node:fs/promises";import nr from"node:path";import wh from"node:os";import uh from"node:fs/promises";import Ts from"node:path";import{execFile as RT}from"node:child_process";import{promisify as $T}from"node:util";import{fileURLToPath as OT}from"node:url";var CT=$T(RT),fh=Ts.dirname(OT(import.meta.url)),MT=Ts.resolve(fh,"..","..","client","dist"),PT="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/",NT=new Map([...PT].map((t,e)=>[t,e]));function BT(t){let e=[],n=0,r=0;for(let o of t){let s=NT.get(o);if(s===void 0)return e;n+=(s&31)<<r,s&32?r+=5:(e.push(n&1?-(n>>>1):n>>>1),n=0,r=0)}return e}function DT(t){let e=[],n=0,r=0,o=0;for(let s of t.split(";")){let a=[],i=0;if(s)for(let c of s.split(",")){let l=BT(c);l.length!==0&&(i+=l[0],l.length>=4&&(n+=l[1],r+=l[2],o+=l[3],a.push([i,n,r,o])))}e.push(a)}return e}function jT(t,e){if(!t||t.length===0||t[0][0]>e)return null;let n=0,r=t.length-1;for(;n<r;){let o=n+r+1>>1;t[o][0]<=e?n=o:r=o-1}return t[n]}var dh=new Map;async function LT(t){let e;try{e=await uh.stat(t)}catch{return null}let n=dh.get(t);if(n&&n.mtimeMs===e.mtimeMs)return n;try{let r=JSON.parse(await uh.readFile(t,"utf8")),o={mtimeMs:e.mtimeMs,sources:r.sources||[],lines:DT(r.mappings||"")};return dh.set(t,o),o}catch{return null}}var Fc=null;function UT(){return Fc||(Fc=process.env.BOSTRAT_RELEASE?Promise.resolve(process.env.BOSTRAT_RELEASE):CT("git",["rev-parse","--short","HEAD"],{cwd:Ts.resolve(fh,"..","..")}).then(t=>t.stdout.trim()).catch(()=>null)),Fc}var FT=/(?:https?:\/\/[^\s()]*)?\/assets\/([^\s()/]+\.js):(\d+):(\d+)/g;async function HT(t,{distDir:e=MT}={}){let n=[],r=0,o=!1;for(let s of t.matchAll(FT)){let[a,i,c,l]=s,u=await LT(Ts.join(e,"assets",`${i}.map`)),d=u?jT(u.lines[Number(c)-1],Number(l)-1):null;if(n.push(t.slice(r,s.index)),d&&u.sources[d[1]]){let f=u.sources[d[1]].replace(/^(\.\.\/)+/,"");n.push(`${f}:${d[2]+1}:${d[3]+1}`),o=!0}else n.push(a);r=s.index+a.length}return n.push(t.slice(r)),{stack:n.join(""),symbolicated:o}}async function ph(t,e={}){try{if(!t?.error?.stack||!t.release||t.release==="unknown")return t;let n=e.release??await UT();if(!n||t.release!==n)return t;let{stack:r,symbolicated:o}=await HT(t.error.stack,e);o&&(t.error.stack=r,t.error.symbolicated=!0)}catch{}return t}var WT=()=>process.env.BOSTRAT_STATE_DIR||nr.join(process.env.HOME||wh.tmpdir(),".bostrat"),Be=()=>nr.join(WT(),"telemetry"),gh=()=>nr.join(Be(),".relay-offsets.json"),qT=50,GT=14,KT=3e4,mh=500,Mt=(t,e)=>typeof t=="string"?t.slice(0,e):"";function VT(t,e){if(!t||typeof t!="object")return null;let n=Mt(t.name,64);if(!n)return null;let r=Number.isFinite(t.ts)?t.ts:e,o={name:n,ts:Math.abs(r-e)<168*3600*1e3?r:e,receivedAt:e,anonId:Mt(t.anonId,64),sessionId:Mt(t.sessionId,64),userId:Mt(t.userId,64),release:Mt(t.release,64),appVersion:Mt(t.appVersion,32),route:Mt(t.route,128),deviceClass:t.deviceClass==="phone"||t.deviceClass==="desktop"?t.deviceClass:""};return t.props&&typeof t.props=="object"&&!Array.isArray(t.props)&&(JSON.stringify(t.props).length<=8192?o.props=t.props:o.props={clamped:!0}),t.error&&typeof t.error=="object"&&(o.error={kind:Mt(t.error.kind,256),message:Mt(t.error.message,8192),stack:Mt(t.error.stack,32768),fingerprint:Mt(t.error.fingerprint,64)}),Array.isArray(t.breadcrumbs)&&(o.breadcrumbs=t.breadcrumbs.slice(-50).map(s=>({t:Number.isFinite(s?.t)?s.t:0,n:Mt(s?.n,64),r:Mt(s?.r,128)}))),o}var yh=t=>new Date(t).toISOString().slice(0,10),hh=0;async function bh(t,e=Date.now()){let r=(Array.isArray(t?.events)?t.events.slice(0,qT):[]).map(s=>VT(s,e)).filter(Boolean);if(!r.length)return 0;for(let s of r)s.error?.stack&&await ph(s);await ue.mkdir(Be(),{recursive:!0});let o=nr.join(Be(),`${yh(e)}.ndjson`);return await ue.appendFile(o,r.map(s=>JSON.stringify(s)+`
|
|
265
|
+
`).join(""),"utf8"),e-hh>6*3600*1e3&&(hh=e,JT(e).catch(()=>{})),r.length}async function JT(t=Date.now()){let e=yh(t-GT*24*3600*1e3),n=[];try{n=await ue.readdir(Be())}catch{return}let r=n.filter(s=>s.endsWith(".ndjson")&&s.slice(0,10)<e);if(!r.length)return;for(let s of r)await ue.rm(nr.join(Be(),s),{force:!0});let o=await xh();for(let s of r)delete o[s];await Sh(o)}var As=new Map;function kh(t,e=Date.now()){let n=t||"unknown",r=As.get(n);return r||(As.size>512&&As.clear(),r={tokens:40,at:e},As.set(n,r)),r.tokens=Math.min(40,r.tokens+(e-r.at)/6e4*20),r.at=e,r.tokens<1?!1:(r.tokens-=1,!0)}async function xh(){try{return JSON.parse(await ue.readFile(gh(),"utf8"))||{}}catch{return{}}}async function Sh(t){await ue.mkdir(Be(),{recursive:!0}),await ue.writeFile(gh(),JSON.stringify(t),"utf8")}function zT(t,{env:e,host:n}={}){let r=t.name==="app.error"&&t.error,o={};for(let[a,i]of[["event",t.name],["route",t.route],["session",t.sessionId],["anon",t.anonId],["user",t.userId],["release",t.release],["appVersion",t.appVersion],["device",t.deviceClass]])i&&(o[a]=i);let s={timestamp:new Date(t.ts).toISOString(),service:"bostrat-client",source:"treetop-js",env:e||process.env.BOSTRAT_ENV||"pi",host:n||wh.hostname(),level:r?"error":"info",message:r?`${t.error.kind||"Error"}: ${t.error.message||"(no message)"}`.slice(0,8192):t.name,tags:o,attributes:{...t.props?{props:t.props}:{},receivedAt:t.receivedAt}};return r&&(s.error={kind:t.error.kind,message:t.error.message,stack:t.error.stack},t.error.fingerprint&&(s.fingerprint=t.error.fingerprint),t.error.symbolicated&&(s.attributes.symbolicated=!0),t.breadcrumbs&&(s.attributes.breadcrumbs=t.breadcrumbs)),s}async function XT({fetchImpl:t=fetch}={}){let e=await Jp();if(!e)return{sent:0,disabled:!0};let n;try{n=(await ue.readdir(Be())).filter(s=>s.endsWith(".ndjson")).sort()}catch{return{sent:0,disabled:!1}}let r=await xh(),o=0;for(let s of n){let a=nr.join(Be(),s),i=r[s]||0,c;try{let f=await ue.open(a,"r");try{let w=(await f.stat()).size;if(w<=i)continue;c=Buffer.alloc(w-i),await f.read(c,0,c.length,i)}finally{await f.close()}}catch{continue}let l=c.lastIndexOf(10);if(l<0)continue;let u=c.toString("utf8",0,l+1).split(`
|
|
266
|
+
`).filter(Boolean),d=[];for(let f of u)try{d.push(JSON.parse(f))}catch{}for(let f=0;f<d.length;f+=mh){let w=d.slice(f,f+mh).map(g=>zT(g)),m=await t(`${e.url.replace(/\/$/,"")}/v1/logs`,{method:"POST",headers:{"content-type":"application/json",authorization:`Bearer ${e.ingestKey}`},body:JSON.stringify({events:w})}).catch(()=>null);if(!m||!m.ok)return{sent:o,disabled:!1};o+=w.length}r[s]=i+l+1,await Sh(r)}return{sent:o,disabled:!1}}var Hc=null,Wc=!1;function vh(){Hc||(Hc=setInterval(async()=>{if(!Wc){Wc=!0;try{await XT()}catch(t){console.error("[telemetry] relay failed:",t?.message||t)}finally{Wc=!1}}},KT),Hc.unref?.())}import Es from"node:fs/promises";import Ih from"node:path";import YT from"node:os";var Rh=()=>process.env.BOSTRAT_STATE_DIR||Ih.join(process.env.HOME||YT.tmpdir(),".bostrat"),$h=()=>Ih.join(Rh(),"dictation-glossary.json"),ZT=100,QT=500,tA=["bostrat","globopi","codex","tmux","worktree","Deepgram","Linear","Firecracker","Tigris","WireGuard","misous","postyx","cedar","gerbil","Anthropic","Opus","Haiku","Sonnet","keyterm","monorepo","dogfood"],Is=null,qc=null,Gc=Promise.resolve();function Th(){let t={},e=Date.now();for(let n of tA)t[n.toLowerCase()]={term:n,count:1,lastUsed:e,source:"seed"};return t}async function Oh(){return Is||(qc??=Es.readFile($h(),"utf8").then(t=>JSON.parse(t)?.terms??Th()).catch(()=>Th()).then(t=>Is=t).finally(()=>qc=null),qc)}async function Ah(){let t=JSON.stringify({terms:Is},null,2),e=$h(),n=`${e}.${process.pid}.tmp`;await Es.mkdir(Rh(),{recursive:!0}),await Es.writeFile(n,t),await Es.rename(n,e)}function eA(){return Gc=Gc.then(Ah,Ah),Gc.catch(()=>{})}async function Kc(){return Object.values(await Oh()).sort((e,n)=>n.count-e.count||n.lastUsed-e.lastUsed)}var nA=t=>Math.max(1,Math.ceil(t.length/4));async function rA(){let t=[],e=0;for(let{term:n}of await Kc()){let r=nA(n);if(!(e+r>QT)&&(t.push(n),e+=r,t.length>=ZT))break}return t}async function oA(t,e){let n=await Oh(),r=t.toLowerCase(),o=n[r];n[r]={term:t,count:(o?.count??0)+1,lastUsed:Date.now(),source:o?.source??e}}var _h=t=>(t||"").split(/\s+/).map(e=>e.replace(/^[^\p{L}\p{N}]+|[^\p{L}\p{N}]+$/gu,"")).filter(Boolean),_s=t=>t.toLowerCase();function Eh(t){return!new RegExp("\\p{L}","u").test(t)||t.length<3?!1:/^[a-z]+$/.test(t)?t.length>=4:!0}async function Ch(t,e){let n=_h(t),r=_h(e);if(!n.length||!r.length)return[];let o=n.length,s=r.length,a=Array.from({length:o+1},()=>new Array(s+1).fill(0));for(let u=o-1;u>=0;u--)for(let d=s-1;d>=0;d--)a[u][d]=_s(n[u])===_s(r[d])?a[u+1][d+1]+1:Math.max(a[u+1][d],a[u][d+1]);let i=new Set,c=0,l=0;for(;c<o&&l<s;)_s(n[c])===_s(r[l])?(c++,l++):a[c+1][l]>=a[c][l+1]?c++:(Eh(r[l])&&i.add(r[l]),l++);for(;l<s;l++)Eh(r[l])&&i.add(r[l]);if(!i.size)return[];for(let u of i)await oA(u,"learned");return await eA(),[...i]}var zr=null;function sA(t,e){let n=null;try{n=JSON.parse(e)}catch{}let r=n?.err_code||n?.error_code||n?.category||"";return t===402||/PAYMENT_REQUIRED/i.test(r)?{status:402,code:"credits_exhausted",persistent:!0,message:"Deepgram credits are exhausted \u2014 dictation is down until you top up at console.deepgram.com or drop a new key into ~/.bostrat/integrations.json."}:t===401||t===403?{status:502,code:"key_invalid",persistent:!0,message:"Deepgram rejected the API key (revoked, or missing ASR scope) \u2014 replace it in ~/.bostrat/integrations.json."}:{status:502,code:"transcription_failed",persistent:!1,message:`Deepgram error ${t}: ${e.slice(0,300)}`}}async function Mh(){if(!zr)return null;let t=await Dr();if(!t||t.apiKey.slice(-6)!==zr.keyTail)return null;let{keyTail:e,...n}=zr;return n}async function Ph(t,e){let n=await Dr();if(!n){let i=new Error("Deepgram is not configured \u2014 add deepgram.apiKey to ~/.bostrat/integrations.json");throw i.status=503,i.code="not_configured",i}let r=new URL("https://api.deepgram.com/v1/listen");r.searchParams.set("model","nova-3"),r.searchParams.set("smart_format","true");for(let i of await rA())r.searchParams.append("keyterm",i);let o=await fetch(r,{method:"POST",headers:{Authorization:`Token ${n.apiKey}`,"Content-Type":e||"application/octet-stream"},body:t});if(!o.ok){let i=await o.text().catch(()=>""),c=sA(o.status,i);c.persistent&&(zr={code:c.code,message:c.message,at:Date.now(),keyTail:n.apiKey.slice(-6)});let l=new Error(c.message);throw l.status=c.status,l.code=c.code,l}return zr=null,{transcript:(await o.json())?.results?.channels?.[0]?.alternatives?.[0]?.transcript??""}}var Lh=ct.dirname(iA(import.meta.url)),Nh=Number(process.env.PORT)||3362,Rs=Cm(process.env),Uh=Number(process.env.BOSTRAT_AUTO_TRIGGER_PCT)||80,rr=Zm(process.env.BOSTRAT_ALLOW_IPS),k=de();k.disable("x-powered-by");k.use((t,e,n)=>{if(rr.allowed(rr.clientIp(t)))return n();e.status(403).type("text/plain").send("bostrat: access restricted to the allowlist")});k.use(eh);k.use("/api/mocks",de.json({limit:"1mb"}));k.use("/api/mocks",de.raw({type:"image/png",limit:"24mb"}));k.use("/api/runbooks",de.json({limit:"1mb"}));k.use("/api/telemetry",de.json({limit:"256kb"}));k.use("/api/dictation",de.raw({type:["audio/*","video/mp4","application/octet-stream"],limit:"32mb"}));k.use("/api/tracks/:id/upload",de.raw({type:()=>!0,limit:"25mb"}));k.use(de.json({limit:"64kb"}));k.get("/api/health",async(t,e)=>{e.json({ok:!0,tmuxSocket:Rt,hostTmuxAlive:await pe(),autoTriggerPct:Uh})});k.get("/api/sessions",async(t,e)=>{e.json({sessions:await vn()})});k.post("/api/sessions",async(t,e)=>{let{displayName:n,cwd:r}=t.body||{};if(typeof n!="string")return e.status(400).json({error:"displayName (string) required"});try{let o=await Ue(n,r||void 0);e.json({ok:!0,...o})}catch(o){let s=o?.message||String(o),a=/already exists/i.test(s)?409:/must be|invalid/i.test(s)?400:500;e.status(a).json({error:s})}});k.delete("/api/sessions/:name",async(t,e)=>{let n=decodeURIComponent(t.params.name);if(!ut(n))return e.status(400).json({error:"invalid session name"});if(!await G(n))return e.status(404).json({error:"session not found"});try{await Wt(n),e.json({ok:!0})}catch(r){e.status(500).json({error:`failed to kill: ${r?.message||r}`})}});k.get("/api/context/:name",async(t,e)=>{let n=decodeURIComponent(t.params.name);if(!ut(n))return e.status(400).json({error:"invalid session name"});let r=await G(n);if(!r)return e.status(404).json({error:"session not found"});let o=await lr(r.cwd,r.sessionId);if(!o)return e.json({available:!1,cwd:r.cwd,sessionId:r.sessionId||null});e.json({available:!0,...o})});k.post("/api/handoff/:name",async(t,e)=>{let n=decodeURIComponent(t.params.name);if(!ut(n))return e.status(400).json({error:"invalid session name"});try{let r=await Pl(n);e.json({ok:!0,step:r.step,file:r.file,startedAt:r.startedAt})}catch(r){e.status(409).json({error:r?.message||String(r)})}});k.get("/api/handoff/:name",(t,e)=>{let n=decodeURIComponent(t.params.name),r=fo(n);if(!r)return e.json({running:!1});e.json({running:!r.finishedAt,step:r.step,error:r.error,file:r.file,startedAt:r.startedAt,finishedAt:r.finishedAt})});k.get("/api/tracks",async(t,e)=>{e.json({tracks:await dt(),agents:Wi,tickets:await um()})});k.get("/api/cost",async(t,e)=>{try{let n=await Lm(await dt());if(n.cursorUsage=Vd(),t.query.format==="text")return e.type("text/plain").send(Um(n));e.json(n)}catch(n){e.status(500).json({error:n?.message||String(n)})}});k.get("/api/usage/roundup",async(t,e)=>{try{e.json(await qm(await dt(),{window:t.query.window}))}catch(n){e.status(500).json({error:n?.message||String(n)})}});k.post("/api/hook",async(t,e)=>{try{let n=await Om(t.body||{},bp);e.json({ok:!0,events:n.length})}catch(n){e.json({ok:!1,error:n?.message||String(n)})}});k.post("/api/telemetry",async(t,e)=>{try{if(!kh(rr.clientIp(t)))return e.json({ok:!0,dropped:!0});let n=await bh(t.body||{});e.json({ok:!0,events:n})}catch(n){e.json({ok:!1,error:n?.message||String(n)})}});k.get("/api/repos",async(t,e)=>{try{let n=await re(),r=new Set((await Promise.all(n.map(a=>Y(a.path)))).filter(Boolean).map(a=>a.toLowerCase())),s=((await pr()).repos||[]).filter(a=>!r.has(String(a.slug).toLowerCase())).map(a=>({slug:a.slug,name:a.slug.split("/")[1],branch:a.defaultBranch||null,private:!!a.private,remote:!0}));e.json({repos:[...n,...s]})}catch(n){e.status(500).json({error:n?.message||String(n)})}});k.get("/api/github/token",async(t,e)=>{try{e.json(await ho(String(t.query.repo||"")))}catch(n){e.status(n?.status||502).json({error:n?.message||String(n)})}});k.get("/api/github/status",(t,e)=>{e.json({configured:mo()})});k.post("/api/github/refresh",(t,e)=>{Jl(),e.json({ok:!0})});function Fh(t,e,n){let r=t.workspace;return r?.kind==="multi"?(r.repos??[]).some(o=>e&&o?.base===e||n&&o?.repo===n):!r?.base||!e||r.base===e||n&&r.repo===n}async function Xc({repo:t=null,number:e=null,branch:n=null}){let r=t?await Y(t):null;if(r&&e!=null){let s=await Tt(r,e),a=s&&await rt(s.trackId);if(a)return a}return n?(await dt()).find(s=>s.workspace?.branch===n&&Fh(s,t,r))??null:null}async function hn(t,{number:e,url:n=null,title:r=null,branch:o=null,repo:s=null,track:a=null,extra:i={}}){let c=a||await Xc({repo:s,number:e,branch:o}).catch(()=>null);c&&await R({id:c.id,cwd:c.cwd},{type:"pr.state.changed",actor:"user",source:"orchestrator",payload:{ts:Date.now(),change:t,number:e,url:n,title:r,branch:o??c.workspace?.branch??null,...i}}).catch(()=>{})}async function dA(t){let e=(await dt()).filter(n=>!n.archivedAt);for(let n of t??[]){if(!n.prs?.length)continue;let r=await Y(n.path);if(r)for(let o of n.prs){if(await Tt(r,o.number))continue;let s=e.find(a=>a.workspace?.branch===o.headRefName&&Fh(a,n.path,r));s&&await Bt({repo:r,number:o.number,trackId:s.id,url:o.url,headRef:o.headRefName,title:o.title,source:"matched"})}}}k.get("/api/git/prs",async(t,e)=>{try{let n=await kf();await dA(n.repos).catch(()=>{}),e.json(n)}catch(n){e.status(500).json({error:n?.message||String(n)})}});k.post("/api/git/prs/merge",async(t,e)=>{let{repo:n,number:r,method:o,auto:s,archive:a,headRefName:i,title:c,url:l}=t.body||{};try{if(s){let w=o||"merge";if(!Jo.has(w))throw new Error(`invalid merge method: ${w}`);let m=Number(r);if(!Number.isInteger(m)||m<=0)throw new Error("invalid PR number");let g=await Y((await Te(n)).path);if(!g)throw new Error("repo has no GitHub remote");let x=await Xc({repo:n,number:m,branch:i}).catch(()=>null);if(!x)throw new Error("auto-merge needs a Track to watch this PR \u2014 none owns it");if(await Bt({repo:g,number:m,trackId:x.id,url:l,headRef:i,title:c,source:"matched"}).catch(()=>{}),!await Co(g,m,{merge:{method:w}}))throw new Error("couldn't record the auto-merge arm");return await za(g,m,a===!0),en(),await hn("auto-armed",{number:m,url:l,title:c,branch:i,repo:n,track:x}),e.json({ok:!0,number:m,auto:!0})}let u=await Ir(n,r,{method:o||"squash"});await hn("merged",{number:u.number,url:l,title:c,branch:i,repo:n});let d=await Y(n).catch(()=>null),f=!1;if(d&&(a===!0&&await za(d,u.number,!0).catch(()=>{}),f=await tn(d,u.number).catch(()=>!1),await Qe(d,u.number,"merged").catch(()=>{})),a===!0&&!f){let w=await Xc({repo:n,number:u.number,branch:i}).catch(()=>null);w&&!w.archivedAt&&await Vn(w.id).catch(()=>{})}e.json(u)}catch(u){let d=u?.message||String(u),f=/invalid|unknown repo|required|needs a Track|no GitHub remote/i.test(d)?400:502;e.status(f).json({error:d})}});k.post("/api/git/prs/disarm-auto",async(t,e)=>{let{repo:n,number:r,headRefName:o,title:s,url:a}=t.body||{};try{let i=Number(r);if(!Number.isInteger(i)||i<=0)throw new Error("invalid PR number");let c=await Y((await Te(n)).path).catch(()=>null),l=c?await Mo(c,i).catch(()=>null):null;try{await xf(n,i)}catch(u){if(!l)throw u}en(),await hn("auto-canceled",{number:i,url:a,title:s,branch:o,repo:n}),e.json({ok:!0,number:i})}catch(i){let c=i?.message||String(i),l=/invalid|unknown repo|required/i.test(c)?400:502;e.status(l).json({error:c})}});k.post("/api/git/prs/merge-all",async(t,e)=>{let{repo:n,method:r}=t.body||{};try{let o=await Y(n).catch(()=>null),s=await Tf(n,{method:r||"merge",onMerged:a=>{hn("merged",{number:a.number,url:a.url,title:a.title,branch:a.headRefName,repo:n}),o&&tn(o,a.number).catch(()=>{}).then(()=>Qe(o,a.number,"merged")).catch(()=>{})}});e.json(s)}catch(o){let s=o?.message||String(o),a=/already running/i.test(s)?409:/invalid|unknown repo|no PRs ready/i.test(s)?400:502;e.status(a).json({error:s})}});k.get("/api/git/prs/merge-all",(t,e)=>{e.json({jobs:vf()})});k.post("/api/git/prs/open",async(t,e)=>{let{trackId:n,title:r,body:o,base:s}=t.body||{};try{let a=n?await rt(n):null;if(!a)return e.status(400).json({error:"trackId required"});if(a.workspace?.kind==="multi")return e.status(400).json({error:"this Track tags multiple repos \u2014 ask the lead in chat to push and open the PR(s); the Git-tab door becomes per-repo in the next phase"});let i=await Af(a.cwd,{title:r,body:o,base:s}),c=await Y(a.cwd).catch(()=>null);c&&i.number&&await Bt({repo:c,number:i.number,trackId:a.id,url:i.url,headRef:a.workspace?.branch??null,title:r,source:"api"}),await hn("opened",{number:i.number,url:i.url,title:r,track:a,branch:a.workspace?.branch}),e.json(i)}catch(a){let i=a?.message||String(a),c=/required|invalid/i.test(i)?400:502;e.status(c).json({error:i})}});k.post("/api/git/prs/fix",async(t,e)=>{let{repo:n,number:r}=t.body||{};try{let o=await Er(n,r),a=(await dt()).find(w=>!w.archivedAt&&w.workspace?.branch===o.headRefName);if(!a)return e.status(404).json({error:`no active Track owns branch "${o.headRefName}"`});let i=await _f(n,o.number);if(!i.length)return e.status(409).json({error:"no failing checks on this PR"});let c=ct.join(a.cwd,".bostrat","ci"),l=ct.join(".bostrat","ci",`pr-${o.number}-fail.log`);await It.promises.mkdir(c,{recursive:!0}),await It.promises.writeFile(ct.join(a.cwd,l),i.map(w=>`\u2550\u2550\u2550\u2550 ${w.name}${w.link?` \xB7 ${w.link}`:""} \u2550\u2550\u2550\u2550
|
|
252
267
|
${w.log??"(no fetchable log \u2014 see link)"}
|
|
253
268
|
`).join(`
|
|
254
|
-
`));let u=i.map(w=>w.name).join(", "),d=await $e(a.id,`CI is red on PR #${o.number} "${o.title}" (${o.url}) \u2014 branch ${o.headRefName}. Failing check${i.length===1?"":"s"}: ${u}. The failure log is at ${l} in this worktree \u2014 read it, fix the root cause, run the relevant tests locally, then commit and push to update the PR.`,{kind:"ci-fix"}),f=await Z(n).catch(()=>null);f&&(await Nt({repo:f,number:o.number,trackId:a.id,url:o.url,headRef:o.headRefName,title:o.title,source:"matched"}),await hd(f,o.number,{headSha:o.headSha,trackId:a.id}).catch(()=>{})),await un("fix-delegated",{number:o.number,url:o.url,title:o.title,branch:o.headRefName,repo:n,track:a,extra:{checks:i.map(w=>w.name)}}),e.json({ok:!0,leadBox:d.leadBox,queued:!!d.queued,checks:i.map(w=>w.name),log:l})}catch(o){let s=o?.message||String(o),a=/unknown repo|invalid|required/i.test(s)?400:/not found|no active Track/i.test(s)?404:502;e.status(a).json({error:s})}});k.post("/api/git/prs/reconcile",async(t,e)=>{let{repo:n,number:r}=t.body||{};try{let o=await Tr(n,r);if(o.mergeable!=="CONFLICTING")return e.status(409).json({error:"this PR does not conflict with its base branch"});let a=(await dt()).find(l=>!l.archivedAt&&l.workspace?.branch===o.headRefName);if(!a)return e.status(404).json({error:`no active Track owns branch "${o.headRefName}"`});let i=await $e(a.id,`PR #${o.number} "${o.title}" (${o.url}) \u2014 branch ${o.headRefName} \u2014 has merge conflicts with ${o.baseRefName}. Bring ${o.baseRefName} into this branch (fetch origin, then merge origin/${o.baseRefName} \u2014 don't rebase this pushed PR branch), and resolve every conflict so that BOTH sides' intent is preserved. Run the relevant tests, then commit and push to update the PR. If any conflict is a genuine ambiguity \u2014 where you can't confidently tell which side's behavior should win, or reconciling would drop work from either side \u2014 STOP and ask me here rather than guessing, laying out the specific choice and your recommendation.`,{kind:"reconcile"}),c=await Z(n).catch(()=>null);c&&(await Nt({repo:c,number:o.number,trackId:a.id,url:o.url,headRef:o.headRefName,title:o.title,source:"matched"}),await wd(c,o.number,{headSha:o.headSha,trackId:a.id}).catch(()=>{})),await un("reconcile-delegated",{number:o.number,url:o.url,title:o.title,branch:o.headRefName,repo:n,track:a,extra:{base:o.baseRefName}}),e.json({ok:!0,leadBox:i.leadBox,queued:!!i.queued,base:o.baseRefName})}catch(o){let s=o?.message||String(o),a=/unknown repo|invalid|required/i.test(s)?400:/not found|no active Track/i.test(s)?404:502;e.status(a).json({error:s})}});k.post("/api/tracks/:id/pr-offer",async(t,e)=>{let{offerId:n,action:r}=t.body||{};try{let o=null;if(Array.isArray(t.body?.actions)){if(o=ld.filter(u=>t.body.actions.includes(u)),!o.length)return e.status(400).json({error:"actions must include commit, push and/or pr"})}else if(r==="pr")o=["commit","push","pr"];else if(r==="push")o=["commit","push"];else if(r!=="dismiss")return e.status(400).json({error:'offerId and actions ["commit"|"push"|"pr"] (or action "dismiss") required'});if(!n)return e.status(400).json({error:"offerId required"});let s=await rt(t.params.id);if(!s)return e.status(404).json({error:"track not found"});let a=await P(s),i=a.find(u=>u.type==="pr.offered"&&u.payload?.offerId===n);if(!i)return e.status(404).json({error:"unknown offer"});if(a.some(u=>u.type==="pr.offer.answered"&&u.payload?.offerId===n))return e.status(409).json({error:"offer already answered"});let c=null;o&&(c=await $e(s.id,xo(o,{branch:i.payload.branch,base:i.payload.base,toPr:i.payload.toPr||null}),{kind:o.includes("pr")?"pr-go":"push-go"}));let l=o?o.includes("pr")?"pr":o.includes("push")?"push":"commit":"dismiss";await R({id:s.id,cwd:s.cwd},{type:"pr.offer.answered",actor:"user",source:"orchestrator",boxId:i.boxId,payload:{ts:Date.now(),offerId:n,action:l,actions:o}}),e.json({ok:!0,offerId:n,action:l,actions:o,queued:!!c?.queued})}catch(o){let s=o?.message||String(o),a=/required|invalid/i.test(s)?400:/not found|not running|archived/i.test(s)?409:502;e.status(a).json({error:s})}});var Vr=t=>e=>({...e,url:Vm(e,t.headers)});k.get("/api/views",async(t,e)=>{try{let n=await Zd({trackId:t.query.track||void 0});e.json({views:n.map(Vr(t)),maxLive:ve})}catch(n){e.status(500).json({error:n?.message||String(n)})}});k.get("/api/views/:id",async(t,e)=>{let n=await Qd(t.params.id);if(!n)return e.status(404).json({error:"view not found"});e.json({view:Vr(t)(n),session:Mn(n.slug)})});k.post("/api/views",async(t,e)=>{let{trackId:n,name:r,cmd:o,cwd:s,port:a}=t.body||{};try{let i=n?await rt(n):null;if(!i&&s&&(i=(await dt()).find(u=>u.cwd&&(s===u.cwd||s.startsWith(u.cwd+ct.sep)))||null),!i)return e.status(400).json({error:"trackId (or a cwd inside a Track workspace) required"});let c=await di({trackId:i.id,name:r,cmd:o,cwd:i.cwd,serveCwd:s,port:a,box:i.leadBox,branch:i.workspace?.branch??null});e.json({ok:!0,view:Vr(t)(c)})}catch(i){let c=i?.message||String(i),l=i?.status??(/cap reached/i.test(c)?409:/required|invalid|already owned|no free/i.test(c)?400:500);e.status(l).json({error:c})}});k.post("/api/views/:id/stop",async(t,e)=>{try{e.json({ok:!0,view:Vr(t)(await tf(t.params.id))})}catch(n){let r=n?.message||String(n);e.status(/not found/i.test(r)?404:500).json({error:r})}});k.post("/api/views/:id/restart",async(t,e)=>{try{e.json({ok:!0,view:Vr(t)(await ef(t.params.id))})}catch(n){let r=n?.message||String(n),o=/not found/i.test(r)?404:/cap reached|no known command/i.test(r)?409:500;e.status(o).json({error:r})}});k.delete("/api/views/:id",async(t,e)=>{try{e.json(await nf(t.params.id))}catch(n){let r=n?.message||String(n);e.status(/not found/i.test(r)?404:500).json({error:r})}});var XT=[["linear",rn,Zi],["jira",ss,Xi]],Lc=new Map;k.get("/api/integrations/ticketing",async(t,e)=>{try{let n=await zi(),r={};for(let[o,s,a]of XT){let i=await s();if(!i){Lc.delete(o),r[o]={configured:!1};continue}let c=Lc.get(o);if(!c||Date.now()-c.at>300*1e3){let l;try{l={configured:!0,ok:!0,...await a(i)}}catch(u){l={configured:!0,ok:!1,error:u?.message||String(u)}}c={at:Date.now(),body:l},Lc.set(o,c)}r[o]={...c.body,flow:n},o==="jira"&&i.tokenExpiresAt&&(r[o].tokenExpiresAt=i.tokenExpiresAt)}e.json(r)}catch(n){e.status(500).json({error:n?.message||String(n)})}});k.get("/api/tickets/flow",async(t,e)=>{try{e.json({flow:await zi()})}catch(n){e.status(500).json({error:n?.message||String(n)})}});k.post("/api/tickets/flow",async(t,e)=>{let{trackId:n,cwd:r,set:o,actor:s}=t.body||{};try{let a=await Lp(o),i=n?await rt(n):null;!i&&r&&(i=(await dt()).find(l=>l.cwd&&(r===l.cwd||r.startsWith(l.cwd+ct.sep)))||null),i&&await R({id:i.id,cwd:i.cwd},{type:"ticket.flow.changed",actor:s==="user"?"user":"agent",source:"orchestrator",payload:{ts:Date.now(),set:{...o},flow:a}}).catch(()=>{}),e.json({ok:!0,flow:a})}catch(a){let i=a?.message||String(a);e.status(/unknown|nothing to set/i.test(i)?400:500).json({error:i})}});k.post("/api/tickets/link",async(t,e)=>{let{trackId:n,cwd:r,ref:o}=t.body||{};try{let s=n?await rt(n):null;if(!s&&r&&(s=(await dt()).find(c=>c.cwd&&(r===c.cwd||r.startsWith(c.cwd+ct.sep)))||null),!s)return e.status(400).json({error:"trackId (or a cwd inside a Track workspace) required"});if(!o)return e.status(400).json({error:"ref (issue URL or key) required"});let a=await Jn(s,o,{source:n?"api":"cli"});e.json({ok:!0,trackId:s.id,ticket:a})}catch(s){let a=s?.message||String(s);e.status(/not a Linear|not found|no ticketing|required/i.test(a)?400:502).json({error:a})}});k.get("/api/tickets/status",async(t,e)=>{let{cwd:n,track:r}=t.query;try{let o=r?await rt(r):null;if(!o&&n&&(o=(await dt()).find(a=>a.cwd&&(n===a.cwd||String(n).startsWith(a.cwd+ct.sep)))||null),!o)return e.status(400).json({error:"track (or a cwd inside a Track workspace) required"});e.json({trackId:o.id,configured:await rm(),ticket:await te(o.id)})}catch(o){e.status(500).json({error:o?.message||String(o)})}});k.get("/api/tracks/:id/ticket",async(t,e)=>{e.json({ticket:await te(t.params.id)})});k.post("/api/tracks/:id/ticket",async(t,e)=>{try{let n=await rt(t.params.id);if(!n)return e.status(404).json({error:"track not found"});let r=await Jn(n,(t.body||{}).ref,{source:"api"});e.json({ok:!0,ticket:r})}catch(n){let r=n?.message||String(n);e.status(/not a Linear|not found|no ticketing|required/i.test(r)?400:502).json({error:r})}});k.delete("/api/tracks/:id/ticket",async(t,e)=>{e.json({ok:!0,removed:await lc(t.params.id)})});var Es=t=>/no ticket linked|unknown offer|unknown state/i.test(t)?404:/already answered/i.test(t)?409:/required|no ticketing/i.test(t)?400:502;k.get("/api/tracks/:id/ticket/states",async(t,e)=>{try{e.json(await im(t.params.id))}catch(n){let r=n?.message||String(n);e.status(Es(r)).json({error:r})}});k.post("/api/tracks/:id/ticket/state",async(t,e)=>{try{e.json({ok:!0,...await uc(t.params.id,(t.body||{}).stateId)})}catch(n){let r=n?.message||String(n);e.status(Es(r)).json({error:r})}});k.post("/api/tracks/:id/ticket/comment",async(t,e)=>{try{e.json({ok:!0,...await cm(t.params.id,(t.body||{}).body)})}catch(n){let r=n?.message||String(n);e.status(Es(r)).json({error:r})}});k.post("/api/tracks/:id/ticket-offer",async(t,e)=>{let{offerId:n,accept:r}=t.body||{};try{let o=await rt(t.params.id);if(!o)return e.status(404).json({error:"track not found"});e.json({ok:!0,...await lm(o,n,r===!0)})}catch(o){let s=o?.message||String(o);e.status(Es(s)).json({error:s})}});k.post("/api/tickets/propose",async(t,e)=>{let{trackId:n,cwd:r,plan:o}=t.body||{};try{let s=n?await rt(n):null;if(!s&&r&&(s=(await dt()).find(i=>i.cwd&&(r===i.cwd||r.startsWith(i.cwd+ct.sep)))||null),!s)return e.status(400).json({error:"trackId (or a cwd inside a Track workspace) required"});e.json({ok:!0,trackId:s.id,proposal:await dm(s,o)})}catch(s){let a=s?.message||String(s);e.status(/no ticketing|are off|needs an epic|too many|required/i.test(a)?400:502).json({error:a})}});k.get("/api/tickets/teams",async(t,e)=>{try{e.json({teams:await um()})}catch(n){let r=n?.message||String(n);e.status(/no ticketing/i.test(r)?400:502).json({error:r})}});k.post("/api/tracks/:id/ticket-create-offer",async(t,e)=>{let{proposalId:n,accept:r,teamId:o}=t.body||{};try{let s=await rt(t.params.id);if(!s)return e.status(404).json({error:"track not found"});e.json({ok:!0,...await fm(s,n,r===!0,{teamId:o||null})})}catch(s){let a=s?.message||String(s);e.status(/unknown proposal|unknown team/i.test(a)?404:/already answered/i.test(a)?409:/required|no ticketing|pick one/i.test(a)?400:502).json({error:a})}});k.get("/api/mocks",async(t,e)=>{try{e.json({mocks:await Rf({trackId:t.query.track||void 0,group:t.query.group||void 0})})}catch(n){e.status(500).json({error:n?.message||String(n)})}});k.get("/api/mocks/:id",async(t,e)=>{let n=await Ae(t.params.id);if(!n)return e.status(404).json({error:"mock not found"});e.json({mock:n})});k.get("/api/mocks/:id/png",async(t,e)=>{let n=await Ae(t.params.id);if(!n)return e.status(404).json({error:"mock not found"});let r=Ze(n,t.query.v??null);if(!r)return e.status(409).json({error:`mock is ${n.state} \u2014 no captured version yet`});e.set("cache-control","private, max-age=31536000, immutable"),e.sendFile(Bt(n,r.v).png,o=>{o&&!e.headersSent&&e.status(404).json({error:"rendered artifact missing"})})});k.get("/api/mocks/:id/html",async(t,e)=>{let n=await Ae(t.params.id);if(!n)return e.status(404).json({error:"mock not found"});let r=Ze(n,t.query.v??null);if(!r?.htmlRel)return e.status(404).json({error:"this version has no HTML source (image-sourced)"});e.set("content-security-policy","sandbox"),e.sendFile(Bt(n,r.v).html,o=>{o&&!e.headersSent&&e.status(404).json({error:"source artifact missing"})})});k.post("/api/mocks",async(t,e)=>{let n=Buffer.isBuffer(t.body)?t.body:null,r=n?t.query:t.body||{},{trackId:o,name:s,cwd:a,width:i,group:c}=r;try{let l=o?await rt(o):null;if(!l&&a&&(l=(await dt()).find(w=>w.cwd&&(a===w.cwd||a.startsWith(w.cwd+ct.sep)))||null),!l)return e.status(400).json({error:"trackId (or a cwd inside a Track workspace) required"});let u=(l.boxes||[]).find(f=>f.name===l.leadBox),d=await $f({trackId:l.id,name:s,html:n?null:r.html,image:n,cwd:l.cwd,width:i,group:c,box:l.leadBox,agent:u?.agent||null});e.json({ok:!0,mock:d})}catch(l){let u=l?.message||String(l);e.status(/required|invalid|must be/i.test(u)?400:500).json({error:u})}});k.post("/api/mocks/:id/update",async(t,e)=>{let n=Buffer.isBuffer(t.body)?t.body:null;try{let r=await Of(t.params.id,{html:n?null:(t.body||{}).html,image:n,width:n?t.query.width:(t.body||{}).width});e.json({ok:!0,mock:r})}catch(r){let o=r?.message||String(r),s=/not found/i.test(o)?404:/required|invalid|must be/i.test(o)?400:500;e.status(s).json({error:o})}});k.post("/api/mocks/:id/pick",async(t,e)=>{try{let{mock:n,siblings:r}=await Mf(t.params.id),o=r.length?` over ${r.map(s=>`"${s.name}"`).join(", ")}`:"";await $e(n.trackId,`Design decision: go with "${n.name}" (mock:${n.id})${o} \u2014 proceed with this design.`,{kind:"mock-picked"}).catch(()=>{}),e.json({ok:!0,mock:n,siblings:r})}catch(n){let r=n?.message||String(n),o=/not found/i.test(r)?404:/nothing to pick/i.test(r)?409:500;e.status(o).json({error:r})}});k.delete("/api/mocks/:id",async(t,e)=>{try{e.json(await Nf(t.params.id))}catch(n){let r=n?.message||String(n);e.status(/not found/i.test(r)?404:500).json({error:r})}});k.get("/api/runbooks",async(t,e)=>{try{let n=t.query.track||void 0;if(!n&&t.query.cwd){let r=await dt(),o=String(t.query.cwd);if(n=r.find(s=>s.cwd&&(o===s.cwd||o.startsWith(s.cwd+ct.sep)))?.id,!n)return e.json({runbooks:[]})}e.json({runbooks:await Hf({trackId:n,status:t.query.status||void 0})})}catch(n){e.status(500).json({error:n?.message||String(n)})}});k.get("/api/runbooks/:id",async(t,e)=>{let n=await Ti(t.params.id);if(!n)return e.status(404).json({error:"runbook not found"});e.json({runbook:n})});k.get("/api/runbooks/:id/md",async(t,e)=>{let n=await Ti(t.params.id);if(!n)return e.status(404).json({error:"runbook not found"});let r=Lf(n,t.query.v??null);if(!r)return e.status(404).json({error:"no such version"});e.set("content-security-policy","sandbox"),e.set("cache-control","private, max-age=31536000, immutable"),e.type("text/markdown; charset=utf-8"),e.sendFile(jn(n,r.v),o=>{o&&!e.headersSent&&e.status(404).json({error:"runbook artifact missing"})})});k.post("/api/runbooks",async(t,e)=>{let{trackId:n,name:r,md:o,cwd:s}=t.body||{};try{let a=n?await rt(n):null;if(!a&&s&&(a=(await dt()).find(u=>u.cwd&&(s===u.cwd||s.startsWith(u.cwd+ct.sep)))||null),!a)return e.status(400).json({error:"trackId (or a cwd inside a Track workspace) required"});let i=(a.boxes||[]).find(l=>l.name===a.leadBox),c=await Wf({trackId:a.id,trackName:a.name,name:r,md:o,cwd:a.cwd,box:a.leadBox,agent:i?.agent||null});e.json({ok:!0,runbook:c})}catch(a){let i=a?.message||String(a);e.status(/required|invalid|over the|secret VALUE/i.test(i)?400:500).json({error:i})}});k.post("/api/runbooks/:id/update",async(t,e)=>{try{let{md:n,note:r}=t.body||{};e.json({ok:!0,runbook:await qf(t.params.id,{md:n,note:r})})}catch(n){let r=n?.message||String(n),o=/not found/i.test(r)?404:/orphaned/i.test(r)?410:/required|invalid|over the|secret VALUE/i.test(r)?400:500;e.status(o).json({error:r})}});k.post("/api/runbooks/:id/status",async(t,e)=>{try{e.json({ok:!0,runbook:await Gf(t.params.id,(t.body||{}).status)})}catch(n){let r=n?.message||String(n),o=/not found/i.test(r)?404:/orphaned/i.test(r)?410:/must be/i.test(r)?400:500;e.status(o).json({error:r})}});k.delete("/api/runbooks/:id",async(t,e)=>{try{e.json(await Kf(t.params.id))}catch(n){let r=n?.message||String(n);e.status(/not found/i.test(r)?404:500).json({error:r})}});k.post("/api/dictation",async(t,e)=>{if(!Buffer.isBuffer(t.body)||t.body.length===0)return e.status(400).json({error:"expected a raw audio body"});try{e.json(await Th(t.body,t.headers["content-type"]))}catch(n){e.status(n?.status||500).json({error:n?.message||String(n),code:n?.code})}});k.get("/api/dictation/glossary",async(t,e)=>{e.json({configured:!!await Nr(),terms:await jc(),problem:await vh()})});k.post("/api/dictation/learn",async(t,e)=>{let{transcript:n,finalText:r}=t.body||{};if(typeof n!="string"||typeof r!="string")return e.status(400).json({error:"transcript and finalText are required"});e.json({learned:await Sh(n,r)})});k.post("/api/tracks",async(t,e)=>{let{name:n,cwd:r,repo:o,agent:s,skipPermissions:a,ticket:i}=t.body||{};try{let c=await Fi({name:n,cwd:r,repo:o,agent:s,skipPermissions:a!==!1}),l=null;if(i)try{await Jn(c,i,{source:"create"})}catch(u){l=u?.message||String(u)}e.json({ok:!0,track:c,...l?{ticketWarning:l}:{}})}catch(c){let l=c?.message||String(c);e.status(/required|unknown agent|invalid|must be/i.test(l)?400:500).json({error:l})}});k.get("/api/tracks/:id",async(t,e)=>{let n=await rt(t.params.id);if(!n)return e.status(404).json({error:"track not found"});e.json({track:n,handoff:Mr(t.params.id),subtasks:Gn(t.params.id).map(Jr)})});k.delete("/api/tracks/:id",async(t,e)=>{try{let n=await vp(t.params.id,{cascadeMocks:t.query.mocks!=="0",cascadeRunbooks:t.query.runbooks==="1"});await lc(t.params.id).catch(()=>{}),e.json(n)}catch(n){let r=n?.message||String(n);e.status(/not found/i.test(r)?404:500).json({error:r})}});k.post("/api/tracks/:id/archive",async(t,e)=>{try{e.json({ok:!0,track:await ns(t.params.id)})}catch(n){let r=n?.message||String(n);e.status(/not found/i.test(r)?404:/already archived/i.test(r)?409:500).json({error:r})}});k.post("/api/tracks/:id/unarchive",async(t,e)=>{try{e.json({ok:!0,track:await Tp(t.params.id)})}catch(n){let r=n?.message||String(n);e.status(/not found/i.test(r)?404:/not archived/i.test(r)?409:500).json({error:r})}});k.get("/api/tracks/:id/messages",async(t,e)=>{let n=await ji(t.params.id);if(!n)return e.status(404).json({error:"track not found"});e.json(n)});k.get("/api/tracks/:id/events",async(t,e)=>{let n=await rt(t.params.id);if(!n)return e.status(404).json({error:"track not found"});e.set({"content-type":"text/event-stream","cache-control":"no-cache",connection:"keep-alive","x-accel-buffering":"no"}),e.flushHeaders();let r=i=>e.write(`id: ${i.seq}
|
|
269
|
+
`));let u=i.map(w=>w.name).join(", "),d=await $e(a.id,`CI is red on PR #${o.number} "${o.title}" (${o.url}) \u2014 branch ${o.headRefName}. Failing check${i.length===1?"":"s"}: ${u}. The failure log is at ${l} in this worktree \u2014 read it, fix the root cause, run the relevant tests locally, then commit and push to update the PR.`,{kind:"ci-fix"}),f=await Y(n).catch(()=>null);f&&(await Bt({repo:f,number:o.number,trackId:a.id,url:o.url,headRef:o.headRefName,title:o.title,source:"matched"}),await Sd(f,o.number,{headSha:o.headSha,trackId:a.id}).catch(()=>{})),await hn("fix-delegated",{number:o.number,url:o.url,title:o.title,branch:o.headRefName,repo:n,track:a,extra:{checks:i.map(w=>w.name)}}),e.json({ok:!0,leadBox:d.leadBox,queued:!!d.queued,checks:i.map(w=>w.name),log:l})}catch(o){let s=o?.message||String(o),a=/unknown repo|invalid|required/i.test(s)?400:/not found|no active Track/i.test(s)?404:502;e.status(a).json({error:s})}});k.post("/api/git/prs/reconcile",async(t,e)=>{let{repo:n,number:r}=t.body||{};try{let o=await Er(n,r);if(o.mergeable!=="CONFLICTING")return e.status(409).json({error:"this PR does not conflict with its base branch"});let a=(await dt()).find(l=>!l.archivedAt&&l.workspace?.branch===o.headRefName);if(!a)return e.status(404).json({error:`no active Track owns branch "${o.headRefName}"`});let i=await $e(a.id,`PR #${o.number} "${o.title}" (${o.url}) \u2014 branch ${o.headRefName} \u2014 has merge conflicts with ${o.baseRefName}. Bring ${o.baseRefName} into this branch (fetch origin, then merge origin/${o.baseRefName} \u2014 don't rebase this pushed PR branch), and resolve every conflict so that BOTH sides' intent is preserved. Run the relevant tests, then commit and push to update the PR. If any conflict is a genuine ambiguity \u2014 where you can't confidently tell which side's behavior should win, or reconciling would drop work from either side \u2014 STOP and ask me here rather than guessing, laying out the specific choice and your recommendation.`,{kind:"reconcile"}),c=await Y(n).catch(()=>null);c&&(await Bt({repo:c,number:o.number,trackId:a.id,url:o.url,headRef:o.headRefName,title:o.title,source:"matched"}),await vd(c,o.number,{headSha:o.headSha,trackId:a.id}).catch(()=>{})),await hn("reconcile-delegated",{number:o.number,url:o.url,title:o.title,branch:o.headRefName,repo:n,track:a,extra:{base:o.baseRefName}}),e.json({ok:!0,leadBox:i.leadBox,queued:!!i.queued,base:o.baseRefName})}catch(o){let s=o?.message||String(o),a=/unknown repo|invalid|required/i.test(s)?400:/not found|no active Track/i.test(s)?404:502;e.status(a).json({error:s})}});k.post("/api/tracks/:id/pr-offer",async(t,e)=>{let{offerId:n,action:r}=t.body||{};try{let o=null;if(Array.isArray(t.body?.actions)){if(o=wd.filter(u=>t.body.actions.includes(u)),!o.length)return e.status(400).json({error:"actions must include commit, push and/or pr"})}else if(r==="pr")o=["commit","push","pr"];else if(r==="push")o=["commit","push"];else if(r!=="dismiss")return e.status(400).json({error:'offerId and actions ["commit"|"push"|"pr"] (or action "dismiss") required'});if(!n)return e.status(400).json({error:"offerId required"});let s=await rt(t.params.id);if(!s)return e.status(404).json({error:"track not found"});let a=await B(s),i=a.find(u=>u.type==="pr.offered"&&u.payload?.offerId===n);if(!i)return e.status(404).json({error:"unknown offer"});if(a.some(u=>u.type==="pr.offer.answered"&&u.payload?.offerId===n))return e.status(409).json({error:"offer already answered"});let c=null;o&&(c=await $e(s.id,Ao(o,{branch:i.payload.branch,base:i.payload.base,toPr:i.payload.toPr||null}),{kind:o.includes("pr")?"pr-go":"push-go"}));let l=o?o.includes("pr")?"pr":o.includes("push")?"push":"commit":"dismiss";await R({id:s.id,cwd:s.cwd},{type:"pr.offer.answered",actor:"user",source:"orchestrator",boxId:i.boxId,payload:{ts:Date.now(),offerId:n,action:l,actions:o}}),e.json({ok:!0,offerId:n,action:l,actions:o,queued:!!c?.queued})}catch(o){let s=o?.message||String(o),a=/required|invalid/i.test(s)?400:/not found|not running|archived/i.test(s)?409:502;e.status(a).json({error:s})}});var Xr=t=>e=>({...e,url:rh(e,t.headers)});k.get("/api/views",async(t,e)=>{try{let n=await af({trackId:t.query.track||void 0});e.json({views:n.map(Xr(t)),maxLive:ve})}catch(n){e.status(500).json({error:n?.message||String(n)})}});k.get("/api/views/:id",async(t,e)=>{let n=await cf(t.params.id);if(!n)return e.status(404).json({error:"view not found"});e.json({view:Xr(t)(n),session:Nn(n.slug)})});k.post("/api/views",async(t,e)=>{let{trackId:n,name:r,cmd:o,cwd:s,port:a}=t.body||{};try{let i=n?await rt(n):null;if(!i&&s&&(i=(await dt()).find(u=>u.cwd&&(s===u.cwd||s.startsWith(u.cwd+ct.sep)))||null),!i)return e.status(400).json({error:"trackId (or a cwd inside a Track workspace) required"});let c=await yi({trackId:i.id,name:r,cmd:o,cwd:i.cwd,serveCwd:s,port:a,box:i.leadBox,branch:i.workspace?.branch??null});e.json({ok:!0,view:Xr(t)(c)})}catch(i){let c=i?.message||String(i),l=i?.status??(/cap reached/i.test(c)?409:/required|invalid|already owned|no free/i.test(c)?400:500);e.status(l).json({error:c})}});k.post("/api/views/:id/stop",async(t,e)=>{try{e.json({ok:!0,view:Xr(t)(await lf(t.params.id))})}catch(n){let r=n?.message||String(n);e.status(/not found/i.test(r)?404:500).json({error:r})}});k.post("/api/views/:id/restart",async(t,e)=>{try{e.json({ok:!0,view:Xr(t)(await uf(t.params.id))})}catch(n){let r=n?.message||String(n),o=/not found/i.test(r)?404:/cap reached|no known command/i.test(r)?409:500;e.status(o).json({error:r})}});k.delete("/api/views/:id",async(t,e)=>{try{e.json(await df(t.params.id))}catch(n){let r=n?.message||String(n);e.status(/not found/i.test(r)?404:500).json({error:r})}});var fA=[["linear",ln,ac],["jira",cs,oc]],Vc=new Map;k.get("/api/integrations/ticketing",async(t,e)=>{try{let n=await rc(),r={};for(let[o,s,a]of fA){let i=await s();if(!i){Vc.delete(o),r[o]={configured:!1};continue}let c=Vc.get(o);if(!c||Date.now()-c.at>300*1e3){let l;try{l={configured:!0,ok:!0,...await a(i)}}catch(u){l={configured:!0,ok:!1,error:u?.message||String(u)}}c={at:Date.now(),body:l},Vc.set(o,c)}r[o]={...c.body,flow:n},o==="jira"&&i.tokenExpiresAt&&(r[o].tokenExpiresAt=i.tokenExpiresAt)}e.json(r)}catch(n){e.status(500).json({error:n?.message||String(n)})}});k.get("/api/tickets/flow",async(t,e)=>{try{e.json({flow:await rc()})}catch(n){e.status(500).json({error:n?.message||String(n)})}});k.post("/api/tickets/flow",async(t,e)=>{let{trackId:n,cwd:r,set:o,actor:s}=t.body||{};try{let a=await Vp(o),i=n?await rt(n):null;!i&&r&&(i=(await dt()).find(l=>l.cwd&&(r===l.cwd||r.startsWith(l.cwd+ct.sep)))||null),i&&await R({id:i.id,cwd:i.cwd},{type:"ticket.flow.changed",actor:s==="user"?"user":"agent",source:"orchestrator",payload:{ts:Date.now(),set:{...o},flow:a}}).catch(()=>{}),e.json({ok:!0,flow:a})}catch(a){let i=a?.message||String(a);e.status(/unknown|nothing to set/i.test(i)?400:500).json({error:i})}});k.post("/api/tickets/link",async(t,e)=>{let{trackId:n,cwd:r,ref:o}=t.body||{};try{let s=n?await rt(n):null;if(!s&&r&&(s=(await dt()).find(c=>c.cwd&&(r===c.cwd||r.startsWith(c.cwd+ct.sep)))||null),!s)return e.status(400).json({error:"trackId (or a cwd inside a Track workspace) required"});if(!o)return e.status(400).json({error:"ref (issue URL or key) required"});let a=await Yn(s,o,{source:n?"api":"cli"});e.json({ok:!0,trackId:s.id,ticket:a})}catch(s){let a=s?.message||String(s);e.status(/not a Linear|not found|no ticketing|required/i.test(a)?400:502).json({error:a})}});k.get("/api/tickets/status",async(t,e)=>{let{cwd:n,track:r}=t.query;try{let o=r?await rt(r):null;if(!o&&n&&(o=(await dt()).find(a=>a.cwd&&(n===a.cwd||String(n).startsWith(a.cwd+ct.sep)))||null),!o)return e.status(400).json({error:"track (or a cwd inside a Track workspace) required"});e.json({trackId:o.id,configured:await dm(),ticket:await te(o.id)})}catch(o){e.status(500).json({error:o?.message||String(o)})}});k.get("/api/tracks/:id/ticket",async(t,e)=>{e.json({ticket:await te(t.params.id)})});k.post("/api/tracks/:id/ticket",async(t,e)=>{try{let n=await rt(t.params.id);if(!n)return e.status(404).json({error:"track not found"});let r=await Yn(n,(t.body||{}).ref,{source:"api"});e.json({ok:!0,ticket:r})}catch(n){let r=n?.message||String(n);e.status(/not a Linear|not found|no ticketing|required/i.test(r)?400:502).json({error:r})}});k.delete("/api/tracks/:id/ticket",async(t,e)=>{e.json({ok:!0,removed:await gc(t.params.id)})});var $s=t=>/no ticket linked|unknown offer|unknown state/i.test(t)?404:/already answered/i.test(t)?409:/required|no ticketing/i.test(t)?400:502;k.get("/api/tracks/:id/ticket/states",async(t,e)=>{try{e.json(await hm(t.params.id))}catch(n){let r=n?.message||String(n);e.status($s(r)).json({error:r})}});k.post("/api/tracks/:id/ticket/state",async(t,e)=>{try{e.json({ok:!0,...await yc(t.params.id,(t.body||{}).stateId)})}catch(n){let r=n?.message||String(n);e.status($s(r)).json({error:r})}});k.post("/api/tracks/:id/ticket/comment",async(t,e)=>{try{e.json({ok:!0,...await wm(t.params.id,(t.body||{}).body)})}catch(n){let r=n?.message||String(n);e.status($s(r)).json({error:r})}});k.post("/api/tracks/:id/ticket-offer",async(t,e)=>{let{offerId:n,accept:r}=t.body||{};try{let o=await rt(t.params.id);if(!o)return e.status(404).json({error:"track not found"});e.json({ok:!0,...await gm(o,n,r===!0)})}catch(o){let s=o?.message||String(o);e.status($s(s)).json({error:s})}});k.post("/api/tickets/propose",async(t,e)=>{let{trackId:n,cwd:r,plan:o}=t.body||{};try{let s=n?await rt(n):null;if(!s&&r&&(s=(await dt()).find(i=>i.cwd&&(r===i.cwd||r.startsWith(i.cwd+ct.sep)))||null),!s)return e.status(400).json({error:"trackId (or a cwd inside a Track workspace) required"});e.json({ok:!0,trackId:s.id,proposal:await bm(s,o)})}catch(s){let a=s?.message||String(s);e.status(/no ticketing|are off|needs an epic|too many|required/i.test(a)?400:502).json({error:a})}});k.get("/api/tickets/teams",async(t,e)=>{try{e.json({teams:await ym()})}catch(n){let r=n?.message||String(n);e.status(/no ticketing/i.test(r)?400:502).json({error:r})}});k.post("/api/tracks/:id/ticket-create-offer",async(t,e)=>{let{proposalId:n,accept:r,teamId:o}=t.body||{};try{let s=await rt(t.params.id);if(!s)return e.status(404).json({error:"track not found"});e.json({ok:!0,...await km(s,n,r===!0,{teamId:o||null})})}catch(s){let a=s?.message||String(s);e.status(/unknown proposal|unknown team/i.test(a)?404:/already answered/i.test(a)?409:/required|no ticketing|pick one/i.test(a)?400:502).json({error:a})}});k.get("/api/mocks",async(t,e)=>{try{e.json({mocks:await Df({trackId:t.query.track||void 0,group:t.query.group||void 0})})}catch(n){e.status(500).json({error:n?.message||String(n)})}});k.get("/api/mocks/:id",async(t,e)=>{let n=await Ae(t.params.id);if(!n)return e.status(404).json({error:"mock not found"});e.json({mock:n})});k.get("/api/mocks/:id/png",async(t,e)=>{let n=await Ae(t.params.id);if(!n)return e.status(404).json({error:"mock not found"});let r=rn(n,t.query.v??null);if(!r)return e.status(409).json({error:`mock is ${n.state} \u2014 no captured version yet`});e.set("cache-control","private, max-age=31536000, immutable"),e.sendFile(Dt(n,r.v).png,o=>{o&&!e.headersSent&&e.status(404).json({error:"rendered artifact missing"})})});k.get("/api/mocks/:id/html",async(t,e)=>{let n=await Ae(t.params.id);if(!n)return e.status(404).json({error:"mock not found"});let r=rn(n,t.query.v??null);if(!r?.htmlRel)return e.status(404).json({error:"this version has no HTML source (image-sourced)"});e.set("content-security-policy","sandbox"),e.sendFile(Dt(n,r.v).html,o=>{o&&!e.headersSent&&e.status(404).json({error:"source artifact missing"})})});k.post("/api/mocks",async(t,e)=>{let n=Buffer.isBuffer(t.body)?t.body:null,r=n?t.query:t.body||{},{trackId:o,name:s,cwd:a,width:i,group:c}=r;try{let l=o?await rt(o):null;if(!l&&a&&(l=(await dt()).find(w=>w.cwd&&(a===w.cwd||a.startsWith(w.cwd+ct.sep)))||null),!l)return e.status(400).json({error:"trackId (or a cwd inside a Track workspace) required"});let u=(l.boxes||[]).find(f=>f.name===l.leadBox),d=await jf({trackId:l.id,name:s,html:n?null:r.html,image:n,cwd:l.cwd,width:i,group:c,box:l.leadBox,agent:u?.agent||null});e.json({ok:!0,mock:d})}catch(l){let u=l?.message||String(l);e.status(/required|invalid|must be/i.test(u)?400:500).json({error:u})}});k.post("/api/mocks/:id/update",async(t,e)=>{let n=Buffer.isBuffer(t.body)?t.body:null;try{let r=await Lf(t.params.id,{html:n?null:(t.body||{}).html,image:n,width:n?t.query.width:(t.body||{}).width});e.json({ok:!0,mock:r})}catch(r){let o=r?.message||String(r),s=/not found/i.test(o)?404:/required|invalid|must be/i.test(o)?400:500;e.status(s).json({error:o})}});k.post("/api/mocks/:id/pick",async(t,e)=>{try{let{mock:n,siblings:r}=await Ff(t.params.id),o=r.length?` over ${r.map(s=>`"${s.name}"`).join(", ")}`:"";await $e(n.trackId,`Design decision: go with "${n.name}" (mock:${n.id})${o} \u2014 proceed with this design.`,{kind:"mock-picked"}).catch(()=>{}),e.json({ok:!0,mock:n,siblings:r})}catch(n){let r=n?.message||String(n),o=/not found/i.test(r)?404:/nothing to pick/i.test(r)?409:500;e.status(o).json({error:r})}});k.delete("/api/mocks/:id",async(t,e)=>{try{e.json(await Hf(t.params.id))}catch(n){let r=n?.message||String(n);e.status(/not found/i.test(r)?404:500).json({error:r})}});k.get("/api/runbooks",async(t,e)=>{try{let n=t.query.track||void 0;if(!n&&t.query.cwd){let r=await dt(),o=String(t.query.cwd);if(n=r.find(s=>s.cwd&&(o===s.cwd||o.startsWith(s.cwd+ct.sep)))?.id,!n)return e.json({runbooks:[]})}e.json({runbooks:await Xf({trackId:n,status:t.query.status||void 0})})}catch(n){e.status(500).json({error:n?.message||String(n)})}});k.get("/api/runbooks/:id",async(t,e)=>{let n=await Oi(t.params.id);if(!n)return e.status(404).json({error:"runbook not found"});e.json({runbook:n})});k.get("/api/runbooks/:id/md",async(t,e)=>{let n=await Oi(t.params.id);if(!n)return e.status(404).json({error:"runbook not found"});let r=Vf(n,t.query.v??null);if(!r)return e.status(404).json({error:"no such version"});e.set("content-security-policy","sandbox"),e.set("cache-control","private, max-age=31536000, immutable"),e.type("text/markdown; charset=utf-8"),e.sendFile(Un(n,r.v),o=>{o&&!e.headersSent&&e.status(404).json({error:"runbook artifact missing"})})});k.post("/api/runbooks",async(t,e)=>{let{trackId:n,name:r,md:o,cwd:s}=t.body||{};try{let a=n?await rt(n):null;if(!a&&s&&(a=(await dt()).find(u=>u.cwd&&(s===u.cwd||s.startsWith(u.cwd+ct.sep)))||null),!a)return e.status(400).json({error:"trackId (or a cwd inside a Track workspace) required"});let i=(a.boxes||[]).find(l=>l.name===a.leadBox),c=await Yf({trackId:a.id,trackName:a.name,name:r,md:o,cwd:a.cwd,box:a.leadBox,agent:i?.agent||null});e.json({ok:!0,runbook:c})}catch(a){let i=a?.message||String(a);e.status(/required|invalid|over the|secret VALUE/i.test(i)?400:500).json({error:i})}});k.post("/api/runbooks/:id/update",async(t,e)=>{try{let{md:n,note:r}=t.body||{};e.json({ok:!0,runbook:await Zf(t.params.id,{md:n,note:r})})}catch(n){let r=n?.message||String(n),o=/not found/i.test(r)?404:/orphaned/i.test(r)?410:/required|invalid|over the|secret VALUE/i.test(r)?400:500;e.status(o).json({error:r})}});k.post("/api/runbooks/:id/status",async(t,e)=>{try{e.json({ok:!0,runbook:await Qf(t.params.id,(t.body||{}).status)})}catch(n){let r=n?.message||String(n),o=/not found/i.test(r)?404:/orphaned/i.test(r)?410:/must be/i.test(r)?400:500;e.status(o).json({error:r})}});k.delete("/api/runbooks/:id",async(t,e)=>{try{e.json(await tp(t.params.id))}catch(n){let r=n?.message||String(n);e.status(/not found/i.test(r)?404:500).json({error:r})}});k.post("/api/dictation",async(t,e)=>{if(!Buffer.isBuffer(t.body)||t.body.length===0)return e.status(400).json({error:"expected a raw audio body"});try{e.json(await Ph(t.body,t.headers["content-type"]))}catch(n){e.status(n?.status||500).json({error:n?.message||String(n),code:n?.code})}});k.get("/api/dictation/glossary",async(t,e)=>{e.json({configured:!!await Dr(),terms:await Kc(),problem:await Mh()})});k.post("/api/dictation/learn",async(t,e)=>{let{transcript:n,finalText:r}=t.body||{};if(typeof n!="string"||typeof r!="string")return e.status(400).json({error:"transcript and finalText are required"});e.json({learned:await Ch(n,r)})});k.post("/api/tracks",async(t,e)=>{let{name:n,cwd:r,repo:o,repos:s,agent:a,skipPermissions:i,ticket:c}=t.body||{};try{let l=await Ji({name:n,cwd:r,repo:o,repos:s,agent:a,skipPermissions:i!==!1}),u=null;if(c)try{await Yn(l,c,{source:"create"})}catch(d){u=d?.message||String(d)}e.json({ok:!0,track:l,...u?{ticketWarning:u}:{}})}catch(l){let u=l?.message||String(l);e.status(/required|unknown agent|invalid|must be/i.test(u)?400:500).json({error:u})}});k.get("/api/tracks/:id",async(t,e)=>{let n=await rt(t.params.id);if(!n)return e.status(404).json({error:"track not found"});e.json({track:n,handoff:Br(t.params.id),subtasks:Jn(t.params.id).map(Yr)})});k.delete("/api/tracks/:id",async(t,e)=>{try{let n=await Cp(t.params.id,{cascadeMocks:t.query.mocks!=="0",cascadeRunbooks:t.query.runbooks==="1"});await gc(t.params.id).catch(()=>{}),e.json(n)}catch(n){let r=n?.message||String(n);e.status(/not found/i.test(r)?404:500).json({error:r})}});k.post("/api/tracks/:id/archive",async(t,e)=>{try{e.json({ok:!0,track:await Vn(t.params.id)})}catch(n){let r=n?.message||String(n);e.status(/not found/i.test(r)?404:/already archived/i.test(r)?409:500).json({error:r})}});k.post("/api/tracks/:id/unarchive",async(t,e)=>{try{e.json({ok:!0,track:await Mp(t.params.id)})}catch(n){let r=n?.message||String(n);e.status(/not found/i.test(r)?404:/not archived/i.test(r)?409:500).json({error:r})}});k.get("/api/tracks/:id/messages",async(t,e)=>{let n=await Gi(t.params.id);if(!n)return e.status(404).json({error:"track not found"});e.json(n)});k.get("/api/tracks/:id/events",async(t,e)=>{let n=await rt(t.params.id);if(!n)return e.status(404).json({error:"track not found"});e.set({"content-type":"text/event-stream","cache-control":"no-cache",connection:"keep-alive","x-accel-buffering":"no"}),e.flushHeaders();let r=i=>e.write(`id: ${i.seq}
|
|
255
270
|
data: ${JSON.stringify(i)}
|
|
256
271
|
|
|
257
|
-
`),o=Number(t.query.since??t.headers["last-event-id"])||0;for(let i of await
|
|
272
|
+
`),o=Number(t.query.since??t.headers["last-event-id"])||0;for(let i of await B(n))i.seq>o&&r(i);let s=sd(n.id,r),a=setInterval(()=>e.write(`: hb
|
|
258
273
|
|
|
259
|
-
`),25e3);t.on("close",()=>{clearInterval(a),s()})});k.post("/api/tracks/:id/lead/restart",async(t,e)=>{try{e.json({ok:!0,track:await
|
|
274
|
+
`),25e3);t.on("close",()=>{clearInterval(a),s()})});k.post("/api/tracks/:id/lead/restart",async(t,e)=>{try{e.json({ok:!0,track:await Np(t.params.id)})}catch(n){let r=n?.message||String(n),o=/not found/i.test(r)?404:/mid-turn|terminal|archived/i.test(r)?409:500;e.status(o).json({error:r})}});k.post("/api/tracks/:id/say",async(t,e)=>{let{text:n,attachments:r}=t.body||{};try{e.json(await $e(t.params.id,n,{attachments:r}))}catch(o){let s=o?.message||String(o);e.status(/not found/i.test(s)?404:/archived/i.test(s)?409:/required|not running/i.test(s)?400:500).json({error:s})}});k.post("/api/tracks/:id/upload",async(t,e)=>{try{let n=await rt(t.params.id);if(!n)return e.status(404).json({error:`track "${t.params.id}" not found`});if(n.archivedAt)return e.status(409).json({error:"track is archived"});if(!n.cwd)return e.status(409).json({error:"track has no workspace yet"});let r=Buffer.isBuffer(t.body)?t.body:null,o=await ap(n.cwd,{filename:t.query.filename,buffer:r});e.json({ok:!0,attachment:o})}catch(n){let r=n?.message||String(n);e.status(/too large|unsupported|empty|required/i.test(r)?400:500).json({error:r})}});k.get("/api/tracks/:id/uploads/:file",async(t,e)=>{try{let n=await rt(t.params.id);if(!n)return e.status(404).json({error:`track "${t.params.id}" not found`});if(!n.cwd)return e.status(404).json({error:"track has no workspace"});let r=ct.basename(String(t.params.file||""));if(!r||!Mi(ct.extname(r).slice(1)))return e.status(400).json({error:"not an upload path"});e.set("cache-control","private, max-age=31536000, immutable"),e.set("content-security-policy","sandbox"),e.sendFile(ct.join(Ci(n.cwd),r),o=>{o&&!e.headersSent&&e.status(404).json({error:"upload not found"})})}catch(n){e.status(500).json({error:n?.message||String(n)})}});var pA=/^[0-9a-fA-F-]{8,64}$/;k.get("/api/tracks/:id/chat-images/:box/:uuid/:idx",async(t,e)=>{try{let n=await rt(t.params.id);if(!n)return e.status(404).json({error:`track "${t.params.id}" not found`});let{box:r,uuid:o}=t.params,s=Number(t.params.idx);if(!pA.test(o)||!Number.isInteger(s)||s<0)return e.status(400).json({error:"bad image ref"});let a=(n.boxes||[]).find(f=>f.name===r);if(!a||a.agent!=="claude"||!a.cwd||!a.sessionId)return e.status(404).json({error:"image source not available"});let i=ee(a.cwd,a.sessionId);if(!i)return e.status(404).json({error:"transcript not found"});let c=`"${o}"`,l=null,u=aA.createInterface({input:It.createReadStream(i,{encoding:"utf8"}),crlfDelay:1/0});for await(let f of u){if(!f.includes(c))continue;let w;try{w=JSON.parse(f)}catch{continue}if(w.uuid===o){l=w,u.close();break}}let d=l?Ks(l)[s]:null;if(!d||!/^image\//.test(d.mediaType))return e.status(404).json({error:"image not found"});e.set("content-type",d.mediaType),e.set("cache-control","private, max-age=31536000, immutable"),e.set("content-security-policy","sandbox"),e.send(Buffer.from(d.data,"base64"))}catch(n){e.status(500).json({error:n?.message||String(n)})}});k.post("/api/tracks/:id/interrupt",async(t,e)=>{try{e.json(await Vi(t.params.id))}catch(n){let r=n?.message||String(n);e.status(/not found/i.test(r)?404:/archived/i.test(r)?409:500).json({error:r})}});k.delete("/api/tracks/:id/queue",(t,e)=>{e.json({ok:!0,removed:ss(t.params.id)})});k.post("/api/tracks/:id/answer-prompt",async(t,e)=>{let{promptId:n,answers:r,text:o}=t.body||{};try{e.json(await Ki(t.params.id,{promptId:n,answers:r,text:o}))}catch(s){let a=s?.message||String(s),i=/not found/i.test(a)?404:/no pending prompt|changed since|not running|not supported|needs a code|look like a sign-in code/i.test(a)?409:500;e.status(i).json({error:a})}});k.post("/api/setup/:provider/start",async(t,e)=>{try{let n=await $p({provider:t.params.provider,fresh:t.body?.fresh===!0});e.json({ok:!0,track:n})}catch(n){let r=n?.message||String(n);e.status(/unknown setup provider|isn't available/i.test(r)?400:500).json({error:r})}});k.post("/api/tracks/:id/navigate-prompt",async(t,e)=>{let{promptId:n,questionIndex:r}=t.body||{};try{e.json(await xp(t.params.id,{promptId:n,questionIndex:r}))}catch(o){let s=o?.message||String(o),a=/not found/i.test(s)?404:/no pending prompt|changed since|not running|not supported|out of range|review screen/i.test(s)?409:500;e.status(a).json({error:s})}});k.put("/api/tracks/:id/draft",async(t,e)=>{let{text:n}=t.body||{};try{e.json(await Ap(t.params.id,typeof n=="string"?n:""))}catch(r){let o=r?.message||String(r),s=/not found/i.test(o)?404:/required/i.test(o)?400:500;e.status(s).json({error:o})}});k.get("/api/tracks/:id/usage",async(t,e)=>{let n=await rt(t.params.id);if(!n)return e.status(404).json({error:"track not found"});let r=n.boxes?.find(s=>s.name===n.leadBox)||null,o=t.query.fresh==="1"||t.query.fresh==="true";e.json(await Xm(r,{fresh:o}))});k.post("/api/tracks/:id/model",async(t,e)=>{let{model:n}=t.body||{};try{e.json(await _p(t.params.id,n))}catch(r){let o=r?.message||String(r),s=/not found/i.test(o)?404:/required|unknown model|not running/i.test(o)?400:500;e.status(s).json({error:o})}});k.post("/api/tracks/:id/subtask",async(t,e)=>{let{agent:n,task:r,skipPermissions:o,reason:s}=t.body||{};try{let a=await Up(t.params.id,{agent:n,task:r,skipPermissions:o!==!1,reason:s});e.json({ok:!0,subtask:Yr(a)})}catch(a){let i=a?.message||String(a),c=/not found/i.test(i)?404:/required|unknown agent|git repo|not running|can't resolve/i.test(i)?400:500;e.status(c).json({error:i})}});k.get("/api/tracks/:id/subtasks",(t,e)=>{e.json({subtasks:Jn(t.params.id).map(Yr)})});k.get("/api/tracks/:id/subtask/:sid/messages",async(t,e)=>{let n=await kp(t.params.id,t.params.sid);if(!n)return e.status(404).json({error:"subtask not found"});e.json(n)});k.post("/api/tracks/:id/subtask/:sid/cancel",(t,e)=>{try{e.json({ok:!0,subtask:Yr(Qi(t.params.sid))})}catch(n){let r=n?.message||String(n);e.status(/not found/i.test(r)?404:409).json({error:r})}});k.post("/api/tracks/:id/subtask/:sid/merge-now",(t,e)=>{try{e.json({ok:!0,subtask:Yr(Hp(t.params.sid))})}catch(n){let r=n?.message||String(n);e.status(/not found/i.test(r)?404:409).json({error:r})}});function Yr(t){return t?{id:t.id,agent:t.agent,task:t.task,skipPermissions:t.skipPermissions,step:t.step,running:!t.finishedAt,box:t.box,branch:t.branch,stat:t.stat,files:t.files||null,conflicts:t.conflicts||[],applied:t.applied,error:t.error,canceled:t.canceled,startedAt:t.startedAt,finishedAt:t.finishedAt}:null}k.post("/api/tracks/:id/handoff-lead",async(t,e)=>{let{toAgent:n,skipPermissions:r,reason:o}=t.body||{};try{let s=await Xi(t.params.id,n,r!==!1,o);e.json({ok:!0,step:s.step,toAgent:s.toAgent,fromBox:s.fromBox,skipPermissions:s.skipPermissions})}catch(s){let a=s?.message||String(s);e.status(/already running/i.test(a)?409:/not found/i.test(a)?404:400).json({error:a})}});k.post("/api/tracks/:id/spin-up-view",async(t,e)=>{try{e.json(await Tp(t.params.id,t.body||{}))}catch(n){let r=n?.message||String(n);e.status(/already being spun up/i.test(r)?409:/not found/i.test(r)?404:400).json({error:r})}});k.get("/api/tracks/:id/handoff-lead",(t,e)=>{let n=Br(t.params.id);if(!n)return e.json({running:!1});e.json({running:!n.finishedAt,step:n.step,error:n.error,fromBox:n.fromBox,toBox:n.toBox,toAgent:n.toAgent,skipPermissions:n.skipPermissions,startedAt:n.startedAt,finishedAt:n.finishedAt})});k.get("/api/debug/handoff",async(t,e)=>{let n=Math.min(Number(t.query.limit)||80,500);e.json({verbose:eo(),paths:ol,trace:await rl({limit:n})})});var Jc=ct.resolve(Lh,"..","..","client","dist");It.existsSync(Jc)&&(k.use(de.static(Jc)),k.get(/^(?!\/api\/).*/,(t,e)=>e.sendFile(ct.join(Jc,"index.html"))));var Hh=cA.createServer(k),mA=new uA({noServer:!0}),Wh=(t,e,n)=>{if(!rr.allowed(rr.clientIp(t))){e.write(`HTTP/1.1 403 Forbidden\r
|
|
260
275
|
\r
|
|
261
|
-
`),e.destroy();return}if(
|
|
276
|
+
`),e.destroy();return}if(nh(t,e,n))return;let r=fl(t.url);if(!r.ok){e.write(`HTTP/1.1 ${r.status} ${r.status===400?"Bad Request":"Not Found"}\r
|
|
262
277
|
\r
|
|
263
278
|
`),e.destroy();return}let o=r.sessionName;if(!ut(o)){e.write(`HTTP/1.1 400 Bad Request\r
|
|
264
279
|
\r
|
|
265
|
-
`),e.destroy();return}let{cols:s,rows:a}=r;
|
|
280
|
+
`),e.destroy();return}let{cols:s,rows:a}=r;mA.handleUpgrade(t,e,n,i=>ll(i,o,{cols:s,rows:a}))};Hh.on("upgrade",Wh);var qh=process.env.BOSTRAT_CERT_DIR||ct.join(process.env.HOME||zc.tmpdir(),".bostrat","certs"),Bh=Number(process.env.BOSTRAT_TLS_PORT)||3443,Dh=ct.join(qh,"bostrat.globopi.key"),jh=ct.join(qh,"bostrat.globopi.crt");if(It.existsSync(Dh)&&It.existsSync(jh)){let t=lA.createServer({key:It.readFileSync(Dh),cert:It.readFileSync(jh)},k);t.on("upgrade",Wh),t.on("error",e=>{console.error(`[bostrat-agent] https listener failed (${e?.code||e?.message}) \u2014 continuing HTTP-only`)}),t.listen(Bh,Rs,()=>{console.log(`[bostrat-agent] https listening on ${Rs||"0.0.0.0"}:${Bh}`)})}Hh.listen(Nh,Rs,()=>{console.log(`[bostrat-agent] listening on ${Rs||"0.0.0.0"}:${Nh}`),console.log(`[bostrat-agent] tmux socket: ${Rt}`),console.log(`[bostrat-agent] auto-trigger default: ${Uh}%`),console.log(`[bostrat-agent] ip allowlist: ${rr.open?"OPEN (BOSTRAT_ALLOW_IPS unset)":process.env.BOSTRAT_ALLOW_IPS}`),jp().then(t=>t&&console.log("[bostrat-agent] reconciled interrupted handoff(s)")).catch(t=>console.error("[bostrat-agent] handoff reconcile failed:",t?.message||t)),Lp().then(t=>t&&console.log("[bostrat-agent] reconciled interrupted subtask(s)")).catch(t=>console.error("[bostrat-agent] subtask reconcile failed:",t?.message||t)),Pp().then(t=>t&&console.log(`[bostrat-agent] reaped ${t} orphaned box(es) on archived track(s)`)).catch(t=>console.error("[bostrat-agent] archived-orphan sweep failed:",t?.message||t)),Rp().then(t=>console.log(`[bostrat-agent] standing instructions refreshed (${t.refreshed} updated, ${t.removed} legacy CLAUDE.md removed${t.codexGlobal?`, codex global ${t.codexGlobal}`:""})`)).catch(t=>console.error("[bostrat-agent] standing-instructions refresh failed:",t?.message||t)),yA(),gA(),lh(),xm({resolveTrack:rt}),$m({listTracks:dt,createTrack:Ji,archiveTrack:Vn,listRepos:re,resolveTrack:rt,sayToLead:$e,answerPrompt:Ki,startLeadHandoff:Xi,interruptLead:Vi,listSubtaskJobs:Jn,cancelSubtask:Qi,readEvents:B,appendEvent:R,knownAgents:Wi}),hA(),ir().then(t=>{t||console.warn(`[boot] no host tmux server on ${Rt} and auto-start failed \u2014 boxes cannot spawn until one runs`)}),vh(),process.env.BOSTRAT_PLANE_URL&&process.env.BOSTRAT_PLANE_TOKEN&&(Bm({url:process.env.BOSTRAT_PLANE_URL,token:process.env.BOSTRAT_PLANE_TOKEN,nodeId:process.env.BOSTRAT_NODE_ID||zc.hostname(),name:process.env.BOSTRAT_NODE_NAME||zc.hostname(),listTracks:dt}),console.log(`[bostrat-agent] plane link \u2192 ${process.env.BOSTRAT_PLANE_URL}`))});function hA(){wA(),qo().catch(()=>{}),setInterval(()=>qo().catch(()=>{}),1e4)}function wA(){for(let t of["bostrat-view","bostrat-mock","bostrat-ticket","bostrat-runbook","gh"])try{let e=ct.resolve(Lh,"..","bin",t),n=ct.join(process.env.HOME||"",".local","bin");if(!process.env.HOME||!It.existsSync(e))continue;It.mkdirSync(n,{recursive:!0});let r=ct.join(n,t);(It.existsSync(r)?It.readlinkSync(r):null)!==e&&(It.rmSync(r,{force:!0}),It.symlinkSync(e,r)),console.log(`[bostrat-agent] ${t} CLI linked at ${r}`)}catch(e){console.error(`[bostrat-agent] ${t} CLI link failed:`,e?.message||e)}}function gA(){let t=!1,e=async()=>{if(!t){t=!0;try{let n=await dt(),r=await Km({tracks:n});r.committed&&console.log(`[state-sync] committed (${r.staged} file(s) staged${r.pushed?", pushed":""})`)}catch{}finally{t=!1}}};setInterval(e,6e4),e()}function yA(){let t=!1;setInterval(async()=>{if(!t){t=!0;try{let e=await dt();for(let n of e)n.archivedAt||!n.boxes?.some(r=>r.state==="live")||(await Gi(n.id).catch(()=>{}),await Ef(n).catch(()=>{}))}catch{}finally{t=!1}}},4e3)}
|