bostrat-node 0.2.3 → 0.2.5
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/bin/bostrat-git-credential +6 -45
- package/bin/bostrat-mock +2 -117
- package/bin/bostrat-node.js +1 -11
- package/bin/bostrat-notify +2 -0
- package/bin/bostrat-runbook +1 -125
- package/bin/bostrat-ticket +2 -136
- package/bin/bostrat-view +2 -95
- package/dist/cli.js +36 -56
- package/dist/index.js +159 -112
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -1,88 +1,109 @@
|
|
|
1
|
-
var
|
|
1
|
+
var $g=Object.defineProperty;var Jl=(t,e)=>{for(var n in e)$g(t,n,{get:e[n],enumerable:!0})};import lt from"node:path";import Ct from"node:fs";import _E from"node:readline";import{fileURLToPath as EE}from"node:url";import ie from"express";import IE from"node:http";import RE from"node:https";import Gl from"node:os";import{WebSocketServer as $E}from"ws";import{execFile as Fg}from"node:child_process";import{promisify as Ug}from"node:util";import Aa from"node:fs";import{randomUUID as Hg}from"node:crypto";import Rn from"node:fs/promises";import zl from"node:os";import Ze from"node:path";function Co(){return/^(1|true|yes|on)$/i.test(process.env.BOSTRAT_CLAUDE_USE_API_KEY||"")&&process.env.ANTHROPIC_API_KEY?.trim()||null}function Og(){return process.env.CLAUDE_CONFIG_DIR?Ze.join(process.env.CLAUDE_CONFIG_DIR,".claude.json"):Ze.join(zl.homedir(),".claude.json")}function Cg(){return process.env.CLAUDE_CONFIG_DIR?Ze.join(process.env.CLAUDE_CONFIG_DIR,"settings.json"):Ze.join(zl.homedir(),".claude","settings.json")}async function xe(t){if(!t||typeof t!="string")return;let e=Ze.resolve(t),n=Og(),r=Cg(),o={};try{o=JSON.parse(await Rn.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 Rn.mkdir(Ze.dirname(n),{recursive:!0}),await Rn.writeFile(n,JSON.stringify(o,null,2));let i={};try{i=JSON.parse(await Rn.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 Rn.mkdir(Ze.dirname(r),{recursive:!0}),await Rn.writeFile(r,JSON.stringify(i,null,2))}import Sa from"node:fs/promises";import Mg from"node:os";import Mo from"node:path";function Pg(){let t=process.env.CODEX_HOME||Mo.join(Mg.homedir(),".codex");return Mo.join(t,"config.toml")}function Xl(t){return`[projects."${t}"]`}function Ng(t,e){let n=Xl(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 $n(t){if(!t||typeof t!="string")return;let e=Mo.resolve(t),n=Pg(),r="";try{r=await Sa.readFile(n,"utf8")}catch{}if(Ng(r,e))return;let o=Xl(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 Sa.mkdir(Mo.dirname(n),{recursive:!0}),await Sa.writeFile(n,r)}catch(s){console.warn(`[codex-config] could not seed folder trust for ${e}: ${s?.message||s}`)}}import Yl from"node:fs/promises";import Bg from"node:os";import Er from"node:path";function Dg(t){let n=Er.resolve(t).slice(1).replace(/\//g,"-").replace(/\./g,""),r=process.env.CURSOR_CONFIG_DIR||Er.join(Bg.homedir(),".cursor");return Er.join(r,"projects",n)}async function On(t){if(!t||typeof t!="string")return;let e=Er.resolve(t),n=Dg(e),r=Er.join(n,".workspace-trusted");await Yl.mkdir(n,{recursive:!0}),await Yl.writeFile(r,`${JSON.stringify({trustedAt:new Date().toISOString(),workspacePath:e},null,2)}
|
|
9
|
+
`,"utf8")}import Ir from"node:fs/promises";import jg from"node:os";import Ql from"node:path";var va=process.env.BOSTRAT_DEBUG_DIR||Ql.join(jg.homedir(),".bostrat","debug"),Cn=Ql.join(va,"handoff.jsonl"),Zl=Number(process.env.BOSTRAT_HANDOFF_DEBUG_MAX)||500;function Po(){let t=process.env.BOSTRAT_HANDOFF_DEBUG;return t==="1"||t==="true"}async function Lg(t){try{await Ir.mkdir(va,{recursive:!0}),await Ir.appendFile(Cn,`${JSON.stringify(t)}
|
|
10
|
+
`,"utf8");let n=(await Ir.readFile(Cn,"utf8")).split(`
|
|
11
|
+
`).filter(Boolean);n.length>Zl&&await Ir.writeFile(Cn,`${n.slice(-Zl).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("|"),
|
|
16
|
-
`).map(n=>n.trim()).includes(t)}catch{return!1}}async function
|
|
17
|
-
`).filter(Boolean).length}catch{return 0}}async function
|
|
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 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
|
|
21
|
-
|
|
22
|
-
`):""}import
|
|
23
|
-
`)
|
|
24
|
-
|
|
25
|
-
`)
|
|
26
|
-
|
|
27
|
-
`).
|
|
28
|
-
`)
|
|
29
|
-
`)
|
|
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
|
-
`)
|
|
32
|
-
`).trim()
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
`)
|
|
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
|
-
`)
|
|
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(`
|
|
13
|
+
`,"utf8")}catch(e){console.warn(`[handoff-debug] failed to write ${Cn}: ${e?.message||e}`)}}async function q(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&&Po())try{e.paneTail=(await wt(e.sessionName,{lines:35})).slice(-4e3)}catch{e.paneTail=null}(Po()||e.error||/^(checkpoint|spawn|hydrate|commit|done|summarize|clear|resume|ready|send|failed)/.test(String(e.phase||e.step||"")))&&await Lg(e)}async function tu({limit:t=80}={}){try{return(await Ir.readFile(Cn,"utf8")).split(`
|
|
14
|
+
`).filter(Boolean).slice(-t).map(r=>{try{return JSON.parse(r)}catch{return{raw:r}}})}catch{return[]}}var eu={DEBUG_DIR:va,LOG_FILE:Cn};var rt=Ug(Fg);function Rr(t){return new Promise(e=>setTimeout(e,t))}function Ta(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 C=process.env.BOSTRAT_TMUX_BIN||"/usr/bin/tmux",Mt=process.env.BOSTRAT_TMUX_SOCKET||"/tmp/tmux-1000/default",Wg=process.env.BOSTRAT_DEFAULT_CWD||process.env.HOME||"/",_a=process.env.BOSTRAT_AGENT_CMD||"claude --dangerously-skip-permissions",Ea="bostrat-usage-probe",qg=new Set([Ea]);function Ia(t,e){let n=Mn(t),r=/(^|\s)(--session-id|--resume|--continue|-r|-c)(\s|=|$)/.test(t);return!n||r?t:`${t} --session-id ${e}`}function Mn(t){return/(^|\/|\s)claude(\s|$)/.test(String(t||""))}function Pn(t){return/(^|\/|\s)claude(\s|$)/.test(t)?"claude":/cursor/.test(t)?"cursor":/codex/.test(t)?"codex":/gemini/.test(t)?"gemini":"agent"}async function Ra(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 rt(r,[o,'command -v "$1" >/dev/null 2>&1',"_",t],{timeout:5e3}),!0}catch(s){return s?.code===1?!1:n}}async function No(t){return Ra(String(t||"").trim().split(/\s+/)[0])}var $a={claude:"npm install -g @anthropic-ai/claude-code",codex:"npm install -g @openai/codex","cursor-agent":"curl https://cursor.com/install -fsS | bash"};function P(...t){return["-S",Mt,...t]}async function Se(){if(!Aa.existsSync(Mt))return!1;try{return await rt(C,P("list-sessions")),!0}catch(t){let e=t&&t.stderr?String(t.stderr):"";return!/no server running/i.test(e)}}async function $r(){if(await Se())return!0;try{Aa.mkdirSync(Mt.replace(/\/[^/]+$/,""),{recursive:!0}),await rt(C,P("new-session","-d","-s","keeper","sleep 2147483647"))}catch{}return await Se()}var Gg=/^[A-Za-z0-9_.-]{1,64}$/;function pt(t){return typeof t=="string"&&Gg.test(t)}async function Nn(){if(!Aa.existsSync(Mt))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 rt(C,P("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("|"),p=Number(i);return{name:o,title:u||null,windows:Number(s),attached:Number(a),lastActivityMs:Number.isFinite(p)?n-p*1e3:null,cwd:c||null,isBostrat:l==="1",sessionId:d||null,agent:f||(l==="1"?"claude":null)}}).filter(r=>!qg.has(r.name)&&!r.name.startsWith("bostrat-view-"))}catch{return[]}}async function z(t){return(await Nn()).find(n=>n.name===t)||null}var Kg=/^[A-Za-z0-9 ._↻-]+$/,Vg=64;function Jg(t){if(typeof t!="string")return!1;let e=t.trim();return e.length>=1&&e.length<=Vg&&Kg.test(e)}function Oa(t){return t.trim().replace(/\s+/g,"-").toLowerCase().replace(/[^A-Za-z0-9_.-]/g,"").replace(/^-+|-+$/g,"")}async function zg(t){try{let{stdout:e}=await rt(C,P("list-sessions","-F","#{session_name}"));return e.split(`
|
|
16
|
+
`).map(n=>n.trim()).includes(t)}catch{return!1}}async function Qe(t,e=Wg,n={}){if(!Jg(t))throw new Error("displayName must be 1\u201364 chars (letters, digits, space . _ -)");let r=t.trim(),o=Oa(r);if(!pt(o))throw new Error("resulting tmux session name is invalid");if(!await $r())throw new Error(`host tmux server not running on ${Mt} and auto-start failed \u2014 start one: ${C} -S ${Mt} new-session -d -s keeper "sleep 2147483647"`);if(await zg(o))throw new Error(`tmux session "${o}" already exists`);let s=n.agentCmd||_a,a=n.agent||Pn(s),i=!!n.resumeSessionId,c=n.resumeSessionId||Hg(),l=i?s:Ia(s,c),u=i?n.pinned===!0:/(^|\s)--session-id(\s|=)/.test(l),d=a==="claude"&&Mn(s),f=n.firstRun===!0;f||(d&&await xe(e),a==="codex"&&await $n(e),a==="cursor"&&await On(e));let p=d&&!f?Co():null,m=p?["-e",`ANTHROPIC_API_KEY=${p}`]:[];try{await rt(C,P("new-session","-d","-s",o,"-c",e,...m))}catch(h){throw p?Ta(h,p):h}return await tn(o),await rt(C,P("set-option","-t",o,"@bostrat-session","1")),await rt(C,P("set-option","-t",o,"@bostrat-title",r)),await rt(C,P("set-option","-t",o,"@bostrat-sid",c)),await rt(C,P("set-option","-t",o,"@bostrat-agent",a)),await rt(C,P("send-keys","-t",o,l,"Enter")),{name:o,title:r,cwd:e,sessionId:c,agent:a,pinned:u}}async function Jt(t){if(!pt(t))throw new Error("invalid session name");await rt(C,P("kill-session","-t",t))}async function nu(t,e){if(pt(t))try{let n=e?P("set-option","-t",t,"@bostrat-sid",e):P("set-option","-u","-t",t,"@bostrat-sid");await rt(C,n)}catch{}}async function wt(t,{lines:e=40}={}){if(!pt(t))return"";try{let n=P("capture-pane","-t",t,"-p");Number.isFinite(e)&&e>0&&n.push("-S",`-${Math.floor(e)}`);let{stdout:r}=await rt(C,n);return String(r||"")}catch{return""}}var Xg=Number(process.env.BOSTRAT_PROMPT_CAPTURE_COLS)||100,Yg=Number(process.env.BOSTRAT_PROMPT_CAPTURE_ROWS)||40,_t=Number(process.env.BOSTRAT_PROMPT_CAPTURE_LINES)||80,Zg=Number(process.env.BOSTRAT_BOX_COLS)||120,Qg=Number(process.env.BOSTRAT_BOX_ROWS)||40;async function ru(t){if(!pt(t))return 0;try{let{stdout:e}=await rt(C,P("list-clients","-t",t,"-F","1"));return String(e||"").trim().split(`
|
|
17
|
+
`).filter(Boolean).length}catch{return 0}}async function tn(t,{cols:e=Zg,rows:n=Qg}={}){if(pt(t)&&!await ru(t))try{let r=await su(t);if(r.cols===e&&r.rows===n)return;await rt(C,P("resize-window","-t",t,"-x",String(e),"-y",String(n))),await rt(C,P("set-window-option","-t",t,"window-size","latest")),await Rr(80)}catch{}}async function ou(t){if(!pt(t))return 0;try{let{stdout:e}=await rt(C,P("display-message","-t",t,"-p","#{pane_pid}"));return Number(String(e||"").trim())||0}catch{return 0}}async function su(t){if(!pt(t))return{cols:0,rows:0};try{let{stdout:e}=await rt(C,P("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 Bo(t,{cols:e=Xg,rows:n=Yg}={}){if(pt(t)&&!await ru(t))try{let r=await su(t),o=Math.max(r.cols,e),s=Math.max(r.rows,n);(o>r.cols||s>r.rows)&&(await rt(C,P("resize-window","-t",t,"-x",String(o),"-y",String(s))),await rt(C,P("set-window-option","-t",t,"window-size","latest")),await Rr(80))}catch{}}async function ve(t){return await Bo(t),wt(t,{lines:_t})}var ty=new Set(["Up","Down","Left","Right","Space","Enter","Tab","BTab","Escape","BSpace"]);async function bt(t,e,{settleMs:n=40,kind:r}={}){if(!pt(t))throw new Error("invalid session name");let o=(Array.isArray(e)?e:[e]).filter(s=>ty.has(s));for(let s=0;s<o.length;s++)await rt(C,P("send-keys","-t",t,o[s])),n>0&&s<o.length-1&&await Rr(n);return q({flow:"tmux",phase:"send-keys",sessionName:t,kind:r||"keys",keys:o.join(" ")}).catch(()=>{}),o.length}async function Do(t,e,{kind:n}={}){if(!pt(t))throw new Error("invalid session name");let r=Math.max(0,Math.floor(Number(e)||0));return r>0&&await rt(C,P("send-keys","-t",t,"-N",String(r),"BSpace")),q({flow:"tmux",phase:"send-keys",sessionName:t,kind:n||"wipe",keys:`BSpace x${r}`}).catch(()=>{}),r}async function jo(t,e,{perCharDelayMs:n=120,kind:r}={}){if(!pt(t))throw new Error("invalid session name");let o=[...String(e)];for(let s of o)await rt(C,P("send-keys","-t",t,"-l",s)),n>0&&await Rr(n);q({flow:"tmux",phase:"type",sessionName:t,kind:r||"type",textLen:o.length,textHead:String(e).slice(0,120)}).catch(()=>{})}async function kt(t,e,{enter:n=!0,submitDelayMs:r=0,kind:o}={}){if(!pt(t))throw new Error("invalid session name");if(typeof e=="string"&&e.length>0&&await rt(C,P("send-keys","-t",t,"-l",e)),n){let s=Number(r);Number.isFinite(s)&&s>0&&await Rr(s),await rt(C,P("send-keys","-t",t,"Enter"))}q({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 ey from"node-pty";async function iu(t,e,n={}){if(!pt(e)){Lo(t,`invalid session name: ${e}`),t.close(1008,"invalid session name");return}if(!await Se()){Lo(t,`host tmux server not running on ${Mt}`),t.close(1011,"tmux server unavailable");return}if(!await z(e)){Lo(t,`session "${e}" not found`),t.close(1008,"session not found");return}let r=au(n.cols,120,cu),o=au(n.rows,32,lu),s;try{s=ey.spawn(C,["-S",Mt,"attach-session","-d","-t",e],{name:"xterm-256color",cols:r,rows:o,cwd:process.env.HOME||"/",env:{...process.env,TERM:"xterm-256color"}})}catch(a){Lo(t,`failed to spawn tmux attach: ${a.message}`),t.close(1011,"spawn failed");return}s.onData(a=>{t.readyState===t.OPEN&&Ca(t,{type:"data",data:a})}),s.onExit(({exitCode:a,signal:i})=>{t.readyState===t.OPEN&&(Ca(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"&&ny(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{}tn(e).catch(()=>{})})}var cu=20,lu=6;function ny(t,e){return Number.isFinite(Number(t))&&Number.isFinite(Number(e))&&Number(t)>=cu&&Number(e)>=lu}function au(t,e,n){let r=Number(t);return!Number.isFinite(r)||r<n?e:Math.min(500,r|0)}function Ca(t,e){try{t.send(JSON.stringify(e))}catch{}}function Lo(t,e){Ca(t,{type:"error",message:e})}function uu(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 Or from"node:fs";import ry from"node:fs/promises";import Bn from"node:path";import oy from"node:os";var du={"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},sy=2e5,Ma=process.env.BOSTRAT_CLAUDE_PROJECTS||Bn.join(oy.homedir(),".claude","projects");function Pa(t){return t.replace(/[^A-Za-z0-9]/g,"-")}function Na(t){let e=Bn.join(Ma,Pa(t)),n;try{n=Or.readdirSync(e).filter(o=>o.endsWith(".jsonl"))}catch{return null}let r=null;for(let o of n){let s=Bn.join(e,o);try{let{mtimeMs:a}=Or.statSync(s);(!r||a>r.mtimeMs)&&(r={full:s,mtimeMs:a})}catch{}}return r?r.full:null}function ce(t,e){if(!e)return null;let n=Bn.join(Ma,Pa(t),`${e}.jsonl`);return Or.existsSync(n)?n:null}function fu(t){if(!t)return[];let e=Bn.join(Ma,Pa(t)),n;try{n=Or.readdirSync(e).filter(o=>o.endsWith(".jsonl"))}catch{return[]}let r=[];for(let o of n)try{let s=Or.statSync(Bn.join(e,o));r.push({id:o.slice(0,-6),birthMs:s.birthtimeMs||s.ctimeMs||s.mtimeMs})}catch{}return r}async function Et(t,e=131072){let n=await ry.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 ay(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 Cr(t,e=null,{fallbackNewest:n=!0}={}){if(!t)return null;let r,o;if(e){if(r=ce(t,e),o=!0,!r)return null}else if(n){if(r=Na(t),o=!1,!r)return null}else return null;let s;try{s=ay(await Et(r))}catch{return null}if(!s)return null;let a=Object.prototype.hasOwnProperty.call(du,s.model),i=du[s.model]||sy;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 iy(t,e){return!t||!e?null:ce(t,e)}function cy(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 ly(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 uy(t){return Array.isArray(t)&&t.length>0&&t.every(e=>e&&e.type==="tool_result")}function Ba(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 pu(t,e){if(!t.uuid)return null;let n=Ba(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 dy=/\.(png|jpe?g|gif|webp|bmp|svg|tiff?|avif|heic|heif|ico)$/i;async function Da(t,{maxBytes:e=524288,limit:n=80}={}){let r;try{r=await Et(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 h=m?.type==="tool_use"?m.input?.file_path||m.input?.path:null;typeof h=="string"&&dy.test(h)&&s.set(m.id,h.split("/").pop())}if(uy(l.content)){let m=pu(c,s);m&&o.push({role:"assistant",text:"",images:m,ts:c.timestamp?Date.parse(c.timestamp):null,model:null});continue}let d=cy(l.content),f=u==="assistant"?ly(l.content):null,p=u==="user"?pu(c,s):null;!d&&!f&&!p||o.push({role:u,text:d,tools:f||void 0,images:p||void 0,ts:c.timestamp?Date.parse(c.timestamp):null,model:l.model||null})}return o.slice(-n)}var fy=(t,e)=>t.length>e?`${t.slice(0,e).trimEnd()}\u2026`:t;async function mu(t,e,{turns:n=16}={}){let r=iy(t,e);if(!r)return"";let o=await Da(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}:** ${fy(s.text||c,700)} ${i}`.trim()}).join(`
|
|
21
|
+
|
|
22
|
+
`):""}import Wy from"node:fs";import Pr from"node:fs/promises";import Ln from"node:path";import py from"node:fs/promises";import{execFile as my}from"node:child_process";import{promisify as hy}from"node:util";var ja=hy(my),wy=Number(process.env.BOSTRAT_CODEX_UPDATE_TIMEOUT_MS)||5*6e4,hu=new Set,wu=Promise.resolve(),La=null;function yu(t){La=typeof t=="function"?t:null}function gy(t){let e=String(t||""),n=e.indexOf("/lib/node_modules/");return n>0?e.slice(0,n):null}function bu({sessionName:t,from:e,to:n}={}){let r=String(n||"").trim();!r||hu.has(r)||(hu.add(r),wu=wu.then(()=>yy({sessionName:t,from:e,to:r})).catch(()=>{}))}async function yy({sessionName:t,from:e,to:n}){await q({flow:"codex-update",phase:"start",sessionName:t,from:e,to:n}).catch(()=>{});try{let r=(await ja("/bin/sh",["-c","command -v codex"])).stdout.trim();if(!r)throw new Error("codex not on PATH");let o=await py.realpath(r),s=gy(o),a=s?{...process.env,PATH:`${s}/bin:${process.env.PATH||""}`}:process.env;await ja("npm",["install","-g","@openai/codex"],{env:a,timeout:wy});let i=(await ja(r,["--version"])).stdout.trim().replace(/^codex-cli\s*/,"")||n;await q({flow:"codex-update",phase:"done",sessionName:t,from:e,to:n,version:i}).catch(()=>{}),await gu({sessionName:t,from:e,to:n,version:i,status:"updated"})}catch(r){let o=String(r?.message||r).slice(0,300);await q({flow:"codex-update",phase:"failed",sessionName:t,from:e,to:n,error:o}).catch(()=>{}),await gu({sessionName:t,from:e,to:n,status:"failed",error:o})}}async function gu(t){if(La)try{await La(t)}catch{}}var by=/^\s*[›>*\-❯]?\s*(\d+)[.)]\s+(.+?)\s*$/,ku=/\b(allow|approve|apply (this )?patch|run (this )?command|do you want to run|grant)\b/i;function ky(t,{provider:e,box:n}){let r=String(t).replace(/\r/g,"").split(`
|
|
23
|
+
`);if(!ku.test(t))return null;let o=[],s=!1;for(let l=r.length-1;l>=0;l--){let u=r[l].match(by);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"&&ku.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 Fo=/(^|\n)\s*›/,vu=/^\s*Welcome to Codex\b/;function Dn(t){let e=String(t??"").replace(/\r/g,""),n=e.split(`
|
|
24
|
+
`);for(let r=n.length-1;r>=0;r--)if(vu.test(n[r]))return n.slice(r).join(`
|
|
25
|
+
`);return e}function xy(t){let e=String(t).trim();if(!e)return!1;let n=(e.match(/[A-Za-z]/g)||[]).length;return n>=3&&n/e.length>=.5}function Sy(t){return/device code/i.test(t)?"works from the device you're reading this on":/sign in with chatgpt/i.test(t)?"opens a browser link on the node itself \u2014 can't finish from another device":""}var xu=/^\s*(>)?\s*(\d+)\.\s+(.+?)\s*$/;function vy(t,e=""){let n=Dn(t);if(Fo.test(n)||!/Sign in with ChatGPT/.test(n)||!/Press enter to continue/.test(n))return null;let r=n.split(`
|
|
26
|
+
`),o=[],s=-1;for(let l=0;l<r.length;l++){let u=r[l].match(xu);if(!u)continue;s<0&&(s=l);let d="";for(let f=l+1;f<Math.min(l+3,r.length);f++){let p=r[f].trim();if(p){if(xu.test(r[f])||/^Press enter/.test(p))break;d=p;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&&!vu.test(r[l])&&xy(u)&&i.push(u)}let c=i.join(" ").slice(0,400)||"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=>{let u=Sy(l.label);return{label:l.label,description:[l.desc,u].filter(Boolean).join(" \xB7 ")}})}]}}var Su=/Enter this one-time code/,Ay=/^\s*([A-Z0-9]{2,10}-[A-Z0-9]{2,10})\s*$/;function Ty(t,e=""){let n=Dn(t);if(Fo.test(n)||!Su.test(n))return null;let r=n.split(`
|
|
27
|
+
`),o=r.findIndex(i=>Su.test(i)),s="";for(let i=o+1;i<r.length;i++){let c=r[i].match(Ay);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 _y(t,e=""){let n=Dn(t);return Fo.test(n)||!/open the following link to authenticate/.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 Ey(t,e=""){let n=Dn(t);return Fo.test(n)||!/Paste or type your API key/.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:""}]}]}}var Fa=/Run Everything|Add a follow-up|Plan, search, build anything/;function Iy(t,e=""){let n=String(t);if(!/Press any key to log in/.test(n)||Fa.test(n))return null;let r=n.replace(/\r/g,"").split(`
|
|
28
|
+
`).map(a=>a.trim()),o=r.findIndex(a=>/Press any key to log in/.test(a)),s=r.slice(0,o<0?r.length:o).filter(a=>a&&!/^Cursor Agent$/i.test(a)&&!/^v?\d{4}\.\d{2}\.\d{2}/.test(a)).join(" ");return{provider:"cursor",box:e,multiSelect:!1,cursorIndex:0,answerMode:"keys",promptId:"cursor:signin-start",questions:[{question:(s?`${s} `:"")+"Cursor isn't signed in on this box \u2014 start the sign-in to get a login link you can open from any device.",header:"Sign in",multiSelect:!1,options:[{label:"Start sign-in",description:"Shows a login link (and a QR option) \u2014 your login goes to cursor.com, never through bostrat"}]}]}}var Ry=/click this link to log in|Signing in with the browser/;function $y(t,e=""){let n=String(t);if(!Ry.test(n)||Fa.test(n))return null;let r=n.replace(/\r/g,"").split(`
|
|
29
|
+
`),o="",s=r.findIndex(a=>/https:\/\//.test(a));if(s>=0){o=r[s].slice(r[s].indexOf("https://")).trim();for(let a=s+1;a<r.length;a++){let i=r[a].trim();if(!i||!/^[A-Za-z0-9~%&=?/_.-]+$/.test(i))break;o+=i}}return{provider:"cursor",box:e,multiSelect:!1,cursorIndex:0,promptId:`cursor:signin-link:${(o.match(/[?&]challenge=([\w-]+)/)||[])[1]?.slice(0,16)||o.slice(-24)||"pending"}`,questions:[{question:o?`Cursor is waiting for you to finish signing in: open ${o} on any device and log in. Your login goes straight to cursor.com \u2014 bostrat never sees it. This card clears once sign-in completes.`:"Cursor is waiting for you to finish signing in \u2014 open the login link shown in the Terminal tab. This card clears once sign-in completes.",header:"Sign in",multiSelect:!1,options:[]}]}}function Oy(t,e=""){let n=String(t);return!/Scan this QR code to log in/.test(n)||Fa.test(n)?null:{provider:"cursor",box:e,multiSelect:!1,cursorIndex:0,promptId:"cursor:signin-qr",questions:[{question:"Cursor is showing a QR sign-in code in the Terminal tab \u2014 scan it from another device to log in. This card clears once sign-in completes.",header:"Sign in",multiSelect:!1,options:[]}]}}function Au(t,e,n=""){if(t!=="cursor"&&t!=="codex")return null;if(t==="codex"){let r=Ty(e,n)||_y(e,n)||Ey(e,n)||vy(e,n);if(r)return r}if(t==="cursor"){let r=$y(e,n)||Oy(e,n)||Iy(e,n);if(r)return r}return ky(e,{provider:t,box:n})}function Tu(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 Cy=()=>Number(process.env.BOSTRAT_AGENT_READY_TIMEOUT_MS)||9e4,Ua=Number(process.env.BOSTRAT_AGENT_READY_POLL_MS)||500,Mr=Number(process.env.BOSTRAT_AGENT_READY_SETTLE_MS)||800,xt={claude:{up:/shift\+tab to cycle|⏵⏵ bypass permissions on|⏸ manual mode on/,busy:/esc to interrupt/,busyAlt:/^[·✢✳✶✻✽✺✹✸✷]\s+[A-Z][\w '’-]{1,40}…/m,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/,block:/Press\s+Enter\s+to\s+continue\s*[.…]{0,3}\s*$/m},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/,auth:/Press any key to log in|click this link to log in|Scan this QR code to log in|Signing in with the browser/}},My=/Trust this workspace|\[a\]\s*Trust/,Py=/Update available!\s*([\d][\w.-]*)\s*->\s*([\d][\w.-]*)/;function Ny(t){let e=String(t||"");if(!xt.codex.block.test(e))return null;let n=Dn(e);if(!xt.codex.block.test(n))return null;let r=n.match(Py);return{from:r?.[1]||null,to:r?.[2]||null}}function Ae(t){return new Promise(e=>setTimeout(e,t))}var By=()=>Number(process.env.BOSTRAT_GATE_RESEND_MS)||3e3,Uo=new Map;function _u(t,e){let n=Date.now(),r=By();for(let[s,a]of Uo)n-a>=r&&Uo.delete(s);let o=`${t}::${e}`;return Uo.has(o)?!1:(Uo.set(o,n),!0)}var Dy=68,jy=/^\s*[─━]{10,}\s*$/;function Ly(t){let e=0;for(let n of String(t).split(`
|
|
30
|
+
`))jy.test(n)&&(e=Math.max(e,n.trimEnd().length));return e}function Ha(t,e){let n=xt[t];if(!n?.busy)return"idle";if(Wa(t,e))return"unknown";if(n.up&&!n.up.test(e))return"idle";if(n.busy.test(e)||n.busyAlt&&n.busyAlt.test(e))return"busy";let r=Ly(e);return t==="claude"&&r>0&&r<Dy?"unknown":"idle"}var Fy=/\bWaiting for \d+\b[^\n]*\bto finish\b|\/workflows to (?:stop|monitor)\b/;function Iu(t,e){return t==="claude"&&Fy.test(String(e||""))}function jn(t,e){return Ha(t,e)==="busy"}function Ho(t,e){return Ha(t,e)!=="idle"}function Wo(t,e){let n=xt[t];return n?.up?n.up.test(e):!0}function Uy(t,e){let n=xt[t];return n?Wa(t,e)?!1:n.up.test(e)&&Ha(t,e)==="idle":!0}function Wa(t,e){let n=xt[t];return n?n.auth?.test(e)&&!n.up.test(e)?!0:n.block?.test(e)?t==="codex"?!0:!n.up.test(e):!1:!1}async function Eu(t,e,n){let r=Date.now()+n,o=0;for(await tn(t).catch(()=>{});;){let s=await wt(t);if(Uy(e,s)){await Ae(Mr);return}if(e==="cursor"&&My.test(s)&&!xt.cursor.up.test(s)&&o<3){if(!_u(t,"cursor-trust")){await Ae(Ua);continue}o+=1,await q({flow:"agent-ready",phase:"gate-accept",sessionName:t,agent:e,attempt:o}).catch(()=>{}),await kt(t,"a",{enter:!1,kind:"cursor-trust-accept"}),await Ae(Mr);continue}if(e==="codex"&&o<3){let a=Ny(s);if(a){if(!_u(t,`codex-update:${a.from}->${a.to}`)){await Ae(Ua);continue}o+=1,await q({flow:"agent-ready",phase:"gate-accept",sessionName:t,agent:e,attempt:o,update:`${a.from} -> ${a.to}`}).catch(()=>{}),await kt(t,"3",{enter:!1,kind:"codex-update-skip"}),bu({sessionName:t,from:a.from,to:a.to}),await Ae(Mr);continue}}if(Date.now()>r){if(Wa(e,s)){let a=`${e} is blocked on an interactive prompt`;throw e==="codex"?a=xt.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==="cursor"&&xt.cursor.auth.test(s)?a="cursor isn't signed in on this box \u2014 answer the Sign in card in chat (the login link works from any device) or finish signing in from the Terminal tab, then send again":e==="claude"&&xt.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":e==="claude"&&xt.claude.block.test(s)&&(a='claude is waiting on its "Press Enter to continue" screen \u2014 answer the Continue card in chat (or press Enter in the Terminal tab), then send again'),q({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.`),q({flow:"agent-ready",phase:"timeout",sessionName:t,agent:e,timeoutMs:n,gateAccepts:o,paneBlocked:!!xt[e]?.block?.test(s),paneUp:!!xt[e]?.up?.test(s),paneBusy:!!xt[e]?.busy?.test(s)}).catch(()=>{}),await Ae(Mr);return}await Ae(Ua)}}function Ru({agent:t,agentCmd:e}={}){let n=e?Pn(e):Pn(t||"");return{label:n,realClaude:e?Mn(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 qo({cwd:t,sessionName:e,agent:n,agentCmd:r}={}){let{realCodex:o,realCursor:s}=Ru({agent:n,agentCmd:r});return!e||!o&&!s?null:Te({cwd:t,sessionName:e,agent:n,agentCmd:r}).catch(()=>{})}async function Te({cwd:t,sessionId:e,sessionName:n,agent:r,agentCmd:o,timeoutMs:s=Cy()}={}){let{label:a,realClaude:i,realCodex:c,realCursor:l}=Ru({agent:r,agentCmd:o});if(i&&t&&await xe(t),c&&t&&await $n(t),l&&t&&await On(t),(i||c||l)&&n){await Eu(n,a,s);return}let u=xt[r]?r:xt[a]?a:null;if(process.env.BOSTRAT_FAKE_POLL_READY==="1"&&n&&u){await Eu(n,u,s);return}await Ae(Mr)}var Hy=(()=>{let t=Number(process.env.BOSTRAT_CODEX_SUBMIT_SETTLE_MS??120);return Number.isFinite(t)&&t>=0?t:120})();function le(t){return t==="codex"?{enter:!0,submitDelayMs:Hy}:{enter:!0}}var Cu=".bostrat/handoff",Mu="latest.md",Pu="Checkpoint: write a work-in-progress handoff",Nu="HANDOFF WRITTEN",Bu="Resume from .bostrat/handoff/latest.md",qy="/clear",Gy=t=>`${Pu} 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 ${Nu} and take no further action.`,Ky=`${Bu}: read it, restate the Objective and the single next step in one line, then continue the work from 'Next steps'.`,Du="Beat: write a",ju="BEAT WRITTEN",Vy=".bostrat/arc",Jy="beat.txt",$u=200,Lu=4,Fu=5,zy=36,Xy=t=>`${Du} short summary of what this workstream accomplished, 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). Format the file as: FIRST LINE = one sentence of at most 18 words, plain past tense, saying the outcome \u2014 no heading, no bullet, no trailing list. THEN, only if this workstream covered several separable pieces of work, up to ${Lu} further lines, each starting with "- " and naming ONE piece in at most ${Fu} words \u2014 a label, not a sentence (e.g. "- stale working flag on poll"). Write nothing else in the file. When the file is written, reply only with ${ju} and take no further action.`,Ou=/^(?:[-–—*•·]|\d+[.)])\s+/,Yy=(t,e,n)=>{let r=t.split(/\s+/).filter(Boolean).slice(0,e).join(" ");return r.length>n?`${r.slice(0,n-1).trimEnd()}\u2026`:r};function Zy(t){let e=String(t||"").split(/\r?\n/).map(s=>s.trim()).filter(Boolean),n=[],r=[];for(let s of e)if(Ou.test(s)){if(r.length>=Lu)continue;let a=Yy(s.replace(Ou,"").replace(/[.;,:]+$/,""),Fu,zy);a&&r.push(a)}else r.length||n.push(s);let o=n.join(" ").replace(/\s+/g," ").replace(/[:\-–—]+$/,"").trim();if(!o)throw new Error("beat file has no summary line");return o.length>$u&&(o=`${o.slice(0,$u-1).trimEnd()}\u2026`),{text:o,items:r.length>1?r:[]}}async function Uu(t,e,{timeoutMs:n=9e4}={}){let r=Ln.join(e,Vy),o=Ln.join(r,Jy);await Pr.mkdir(r,{recursive:!0});let s=await Go(o),a=await z(t);if(!a)throw new Error(`session "${t}" not found`);return await Ko(t,a,Xy(o)),await Ju(o,s,n),Zy(await Pr.readFile(o,"utf8"))}function Qy(t,e){let n=(e||"").trim();return n?t==="assistant"?n===Nu||n===ju:n.startsWith(Pu)||n===qy||n.startsWith(Bu)||n.startsWith(Du):!1}var tb=6e5;function Hu(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-tb;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 Wu(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 qu(t,{windows:e=[]}={}){if(Qy(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 eb=3500,nb=3e5,qa=new Map;function Vo(t){return qa.get(t)||null}async function Gu(t){let e=qa.get(t);if(e&&!e.finishedAt)throw new Error("a handoff is already running for this session");let n=await z(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:Ln.join(n.cwd,Cu,Mu)};return qa.set(t,r),rb(r,n.cwd).catch(o=>{r.error=o?.message||String(o),r.finishedAt=Date.now()}),r}async function rb(t,e){t.step="summarize",await Ku(t.name,e),t.step="clear";let n=await z(t.name);await Ko(t.name,n,"/clear"),await zu(eb),t.step="resume",await Ko(t.name,n,Ky),t.step="done",t.finishedAt=Date.now()}async function Ku(t,e){let n=Ln.join(e,Cu),r=Ln.join(n,Mu);await Pr.mkdir(n,{recursive:!0});let o=await Go(r),s=await z(t);return await q({flow:"checkpoint",phase:"summarize-start",sessionName:t,cwd:e,file:r,beforeMtime:o}),await Ko(t,s,Gy(r)),await Ju(r,o,nb),await q({flow:"checkpoint",phase:"summarize-done",sessionName:t,file:r,afterMtime:await Go(r)}),await ob(n,r),r}async function Vu(t){let e=await z(t);if(!e)throw new Error(`session "${t}" not found`);if(!e.cwd)throw new Error(`can't resolve cwd for "${t}"`);return Ku(t,e.cwd)}async function Go(t){try{return(await Pr.stat(t)).mtimeMs}catch{return 0}}async function Ju(t,e,n){let r=Date.now()+n;for(;;){if(await Go(t)>e)return;if(Date.now()>r)throw new Error(`timed out waiting for the handoff file to be written (${t})`);await zu(2e3)}}async function ob(t,e){try{if(!Wy.existsSync(e))return;let n=new Date().toISOString().replace(/[:.]/g,"-");await Pr.copyFile(e,Ln.join(t,`${n}.md`))}catch{}}function zu(t){return new Promise(e=>setTimeout(e,t))}async function Ko(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 Te({cwd:e.cwd,sessionId:e.sessionId,sessionName:t,agent:e.agent,agentCmd:o})}let r=await z(t);if(!r)throw new Error(`session "${t}" disappeared before the orchestration prompt could be sent`);await kt(t,n,{kind:"orchestration-drive",...le(r.agent)})}import D from"node:fs/promises";import j from"node:path";import{randomUUID as mo}from"node:crypto";import{execFile as $v}from"node:child_process";import{promisify as Ov}from"node:util";import Cf from"node:path";import Ga from"node:fs/promises";import Fn from"node:path";import sb from"node:os";import{EventEmitter as ab}from"node:events";import{randomUUID as ib}from"node:crypto";var cb=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","notify.raised","provider.connected","track.summary.recorded","stall.detected","error"]),lb=new Set(["user","agent","orchestrator","system"]),ub=new Set(["hook","otel","statusline","transcript","pane","orchestrator","vendor"]),jt=new ab;jt.setMaxListeners(0);var Ka=process.env.BOSTRAT_STATE_DIR||Fn.join(process.env.HOME||sb.tmpdir(),".bostrat");function zo(t){return t?.cwd?Fn.join(t.cwd,".bostrat","events"):Fn.join(Ka,"events")}var Xu=(t,e)=>Fn.join(t,`${e}.ndjson`),Jo=new Map;async function db(t,e){if(!Jo.has(e)){let o=(await F(t,e)).reduce((s,a)=>a.seq>s?a.seq:s,0);Jo.set(e,o)}let n=Jo.get(e)+1;return Jo.set(e,n),n}function fb(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 O(t,e){let n=typeof t=="string"?t:t?.id;if(!n)throw new Error("appendEvent: a track (or trackId) is required");if(!e||!cb.has(e.type))throw new Error(`appendEvent: unknown event type "${e?.type}"`);if(e.actor&&!lb.has(e.actor))throw new Error(`appendEvent: bad actor "${e.actor}"`);if(e.source&&!ub.has(e.source))throw new Error(`appendEvent: bad source "${e.source}"`);let r=typeof t=="string"?Fn.join(Ka,"events"):zo(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=fb(c,Number(e.payload?.ts??e.ts)||Date.now())}let s=await db(r,n),a=e.userId??process.env.BOSTRAT_OWNER??null,i={id:e.id||ib(),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 Ga.mkdir(r,{recursive:!0}),await Ga.appendFile(Xu(r,n),JSON.stringify(i)+`
|
|
31
|
+
`),jt.emit("event",i),jt.emit(`track:${n}`,i),i}async function F(t,e){let n,r;typeof t=="string"&&e?(n=t,r=e):(r=typeof t=="string"?t:t?.id,n=typeof t=="string"?Fn.join(Ka,"events"):zo(t));let o;try{o=await Ga.readFile(Xu(n,r),"utf8")}catch{return[]}let s=[];for(let a of o.split(`
|
|
32
|
+
`))if(a.trim())try{s.push(JSON.parse(a))}catch{}return s}function Yu(t,e){let n=`track:${t}`;return jt.on(n,e),()=>jt.off(n,e)}var Va={"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}},Zu={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 Qu(t){if(!t)return null;let e=String(t).trim();return Va[e]?e:Zu[e]?Zu[e]:Object.keys(Va).find(r=>e.startsWith(r))||null}var Xo=t=>Qu(t)!==null;function zt(t,e={}){let n=Va[Qu(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}var td=()=>({input:0,output:0,cacheRead:0,cacheCreation:0});function pb(t){return t?{input:t.input||0,output:t.output||0,cacheRead:t.cacheRead||0,cacheWrite:t.cacheWrite??t.cacheCreation??0}:null}function ed(t,e){e&&(t.input+=e.input,t.output+=e.output,t.cacheRead+=e.cacheRead,t.cacheCreation+=e.cacheWrite)}function _e(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=td();for(let d of n){let f=d.payload?.model||"unknown",p=d.provider||"unknown",m=d.boxId||"unattributed",h=pb(d.payload?.tokens),b=Number(d.payload?.usd)||0;!b&&h&&(b=zt(f,h),b>0&&i++);let x=h?h.input+h.output+h.cacheRead+h.cacheWrite:0;if(!b&&x>0&&!Xo(f)&&(c++,l.add(f)),a+=b,ed(u,h),r[f]||={usd:0,tokens:td(),events:0},r[f].usd+=b,r[f].events+=1,ed(r[f].tokens,h),o[p]||={usd:0,events:0},o[p].usd+=b,o[p].events+=1,s[m]||={usd:0,events:0,provider:p,models:new Set,label:e[m]||m},s[m].usd+=b,s[m].events+=1,f!=="unknown"&&s[m].models.add(f),d.epoch!=null){let v=(s[m].epochs||={})[d.epoch]||={usd:0,events:0,provider:p,models:new Set};v.usd+=b,v.events+=1,v.provider=p,f!=="unknown"&&v.models.add(f)}}return{totalUsd:Nr(a),totalTokens:u,turns:n.length,repricedTurns:i,unpricedTurns:c,unpricedModels:[...l],byModel:Yo(r,d=>({...d,usd:Nr(d.usd)})),byProvider:Yo(o,d=>({...d,usd:Nr(d.usd)})),byBox:Yo(s,d=>({usd:Nr(d.usd),events:d.events,provider:d.provider,label:d.label,models:[...d.models],...d.epochs?{epochs:Yo(d.epochs,f=>({usd:Nr(f.usd),events:f.events,provider:f.provider,models:[...f.models]}))}:{}}))}}var Nr=t=>Math.round(t*1e4)/1e4,Yo=(t,e)=>Object.fromEntries(Object.entries(t).map(([n,r])=>[n,e(r)]));import yt from"node:fs/promises";import It from"node:path";import Br from"node:os";import{randomUUID as wb}from"node:crypto";import{execFile as gb}from"node:child_process";import{promisify as yb}from"node:util";import{chromium as bb}from"playwright-core";import Ja from"node:fs/promises";import mb from"node:path";var hb=0;async function Xt(t,e,{mode:n}={}){await Ja.mkdir(mb.dirname(t),{recursive:!0});let r=`${t}.${process.pid}.${++hb}.tmp`;await Ja.writeFile(r,JSON.stringify(e,null,2)+`
|
|
33
|
+
`,n!=null?{mode:n}:void 0),await Ja.rename(r,t)}var rd=yb(gb),od=process.env.BOSTRAT_STATE_DIR||It.join(process.env.HOME||Br.tmpdir(),".bostrat"),sd=It.join(od,"mocks.json"),kb=process.env.BOSTRAT_MOCK_ENGINE||"chromium",xb=process.env.BOSTRAT_CHROMIUM_BIN||"/usr/bin/chromium",Sb=process.env.BOSTRAT_FIREFOX_BIN||"firefox",vb=1300,Un=2,Ab=32,ad=1500,Hn=Number(process.env.BOSTRAT_MOCK_TIMEOUT_MS)||75e3,Tb=700*1024*1024;async function _b(){if(process.platform!=="darwin")return Br.freemem();try{let{stdout:t}=await rd("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:Br.freemem()}catch{return Br.freemem()}}var Wt=class extends Error{constructor(e,n=""){super(n?`${e}
|
|
34
|
+
${n.trim().split(`
|
|
35
|
+
`).slice(-6).join(`
|
|
36
|
+
`)}`:e),this.name="MockRenderError"}},en=null,za=null;async function ue(){return en||(za||=(async()=>{try{en=JSON.parse(await yt.readFile(sd,"utf8"))}catch{en={mocks:[]}}finally{za=null}return en.mocks||=[],en})(),za)}async function Yt(){await Xt(sd,en)}function qt(t,e,n={}){return O({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 Zo=t=>It.join(t,".bostrat","mocks");function Lt(t,e){let n=Zo(t.cwd);return{html:It.join(n,`${t.id}.v${e}.html`),png:It.join(n,`${t.id}.v${e}.png`)}}function nn(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 Ya(t){return t.length<24||t.readUInt32BE(12)!==1229472850?null:{width:t.readUInt32BE(16),height:t.readUInt32BE(20)}}var Eb=Buffer.from([137,80,78,71,13,10,26,10]);function id(t){return Buffer.isBuffer(t)&&t.length>24&&t.subarray(0,8).equals(Eb)}async function Ib(t){let e=Buffer.alloc(24),n=await yt.open(t,"r");try{await n.read(e,0,24,0)}finally{await n.close()}return e}function Rb({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 $b(t,e){return t.evaluate(async n=>{let r=new Image;await new Promise((m,h)=>{r.onload=m,r.onerror=()=>h(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,p=-1;for(let m=0;m<s;m++)for(let h=m*o*4,b=0;b<o;b++,h+=4)Math.abs(c[h]-l[0])+Math.abs(c[h+1]-l[1])+Math.abs(c[h+2]-l[2])>24&&(b<u&&(u=b),b>f&&(f=b),m<d&&(d=m),p=m);return f<0?null:{imgW:o,imgH:s,bg:l,bbox:{left:u,top:d,right:f,bottom:p}}},e.toString("base64"))}async function Ob(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 Cb({htmlPath:t,outPath:e,width:n}){let r=Date.now(),o;try{o=await bb.launch({executablePath:xb,args:["--disable-gpu"],timeout:3e4})}catch(s){throw new Wt("chromium failed to launch",s?.message||"")}try{let a=await(await o.newContext({viewport:{width:n,height:800},deviceScaleFactor:Un})).newPage();a.setDefaultTimeout(Hn),await a.goto(`file://${t}`,{waitUntil:"load",timeout:Hn}),await a.waitForTimeout(250);let i;for(let l=1;;l++)try{i=await a.screenshot({fullPage:!0,timeout:Hn});break}catch(u){if(l>=3||!/captureScreenshot|Unable to capture/i.test(u?.message||""))throw u;await a.waitForTimeout(500)}try{let l=await $b(a,i),u=l&&Rb({...l,pad:Ab*Un,align:Un});u&&(i=await Ob(a,i,{bg:l.bg,crop:u}))}catch{}if(i.length<ad)throw new Wt(`capture produced a ${i.length}-byte image`);let c=Ya(i);if(!c)throw new Wt("output is not a PNG");return await yt.writeFile(e,i),{width:Math.round(c.width/Un),height:Math.round(c.height/Un),dpr:Un,renderMs:Date.now()-r}}catch(s){throw s instanceof Wt?s:new Wt(/timeout/i.test(s?.message||"")?`render timed out after ${Hn}ms`:"chromium render failed",s?.message||"")}finally{await o.close().catch(()=>{})}}async function nd({htmlPath:t,outPath:e,width:n}){let r=await yt.mkdtemp(It.join(Br.tmpdir(),"bostrat-mock-profile-")),o=Date.now(),s="";try{try{s=(await rd(Sb,["--headless","--no-remote","--profile",r,`--window-size=${n}`,"--screenshot",e,`file://${t}`],{env:{...process.env,MOZ_HEADLESS:"1"},timeout:Hn,killSignal:"SIGKILL",maxBuffer:4194304})).stderr||""}catch(c){throw s=c?.stderr||"",new Wt(c?.killed?`render timed out after ${Hn}ms`:"firefox exited non-zero",s)}let a=await yt.stat(e).catch(()=>null);if(!a||a.size<ad)throw new Wt(`render produced ${a?`a ${a.size}-byte file`:"no file"} (silent no-op \u2014 another firefox holding the profile?)`,s);let i=Ya(await Ib(e));if(!i)throw new Wt("output is not a PNG",s);return{width:i.width,height:i.height,dpr:1,renderMs:Date.now()-o}}finally{await yt.rm(r,{recursive:!0,force:!0}).catch(()=>{})}}async function Mb(t){if(kb==="firefox")return nd(t);try{return await Cb(t)}catch(e){if(/failed to launch/i.test(e?.message||""))return nd(t);throw e}}var Pb=Mb;var Xa=Promise.resolve();function cd(t){return Xa=Xa.then(t,t),Xa}async function ld(t,e){let n=await ue(),r=n.mocks.find(o=>o.id===t);if(r){r.state="rendering",await Yt(),await qt(r,"rendering",{version:e});try{let o=await _b();if(o<Tb)throw new Wt(`box is low on memory (${Math.round(o/1e6)} MB available) \u2014 not spawning a browser`);let s=Lt(r,e),a=await Pb({htmlPath:s.html,outPath:s.png,width:r.width});if(!n.mocks.some(i=>i.id===t)){await yt.rm(s.png,{force:!0}).catch(()=>{});return}ud(r,{v:e,source:"html",htmlRel:It.relative(r.cwd,s.html),pngRel:It.relative(r.cwd,s.png),width:a.width,height:a.height,dpr:a.dpr,renderMs:a.renderMs,createdAt:Date.now()}),await Yt(),await qt(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 Yt(),await qt(r,"error",{version:e,error:r.error})}}}function ud(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 Nb({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?vb:Number(r);if(!Number.isInteger(o)||o<320||o>4e3)throw new Error("invalid width (320-4000)");return o}async function Qo({trackId:t,group:e}={}){let n=await ue(),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 Ee(t){let n=(await ue()).mocks.find(r=>r.id===t);return n?{...n}:null}async function dd({trackId:t,name:e,html:n,image:r,cwd:o,width:s,group:a,box:i,agent:c,prompt:l}){let u=Nb({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&&!id(r))throw new Error("image must be a PNG");let f=await ue(),p={id:wb().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 yt.mkdir(Zo(o),{recursive:!0}),f.mocks.push(p),d?(await yt.writeFile(Lt(p,1).html,n),await Yt(),await qt(p,"created"),cd(()=>ld(p.id,1))):(await pd(p,1,r),await Yt(),await qt(p,"created"),await qt(p,"ready",{version:1,width:p.width,height:p.height,dpr:p.dpr})),{...p}}async function fd(t,{html:e,image:n,width:r}={}){let s=(await ue()).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&&!id(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 yt.mkdir(Zo(s.cwd),{recursive:!0}),a?(s.state="queued",await yt.writeFile(Lt(s,i).html,e),await Yt(),await qt(s,"updated",{version:i}),cd(()=>ld(s.id,i))):(await pd(s,i,n),await Yt(),await qt(s,"updated",{version:i}),await qt(s,"ready",{version:i,width:s.width,height:s.height,dpr:s.dpr})),{...s}}async function pd(t,e,n){let r=Ya(n);if(!r)throw new Error("image must be a PNG");let o=Lt(t,e);await yt.writeFile(o.png,n),ud(t,{v:e,source:"image",htmlRel:null,pngRel:It.relative(t.cwd,o.png),width:r.width,height:r.height,dpr:1,renderMs:null,createdAt:Date.now()}),t.width=r.width}async function md(t){let e=await ue(),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 qt(n,"picked",{version:n.readyVersion,siblings:r}),{mock:{...n},siblings:r}}async function hd(t){let e=await ue(),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=Lt(r,o);await yt.rm(s.html,{force:!0}).catch(()=>{}),await yt.rm(s.png,{force:!0}).catch(()=>{})}return await Yt(),await qt(r,"deleted"),{ok:!0,id:t,name:r.name}}async function wd(t){let n=(await ue()).mocks.filter(i=>i.trackId===t.id);if(!n.length)return 0;let r=It.join(od,"mock-archive",t.id),o=Zo(t.cwd),s=It.join(r,".bostrat","mocks");await yt.mkdir(s,{recursive:!0});let a=await yt.readdir(o).catch(()=>[]);for(let i of a)await yt.copyFile(It.join(o,i),It.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 yt.stat(It.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 Yt(),n.length}async function gd(t){let e=await ue(),n=e.mocks.filter(r=>r.trackId===t);return n.length&&(e.mocks=e.mocks.filter(r=>r.trackId!==t),await Yt()),n.length}import jr from"node:fs/promises";import Rt from"node:path";import Bb from"node:os";import Db from"node:net";import{randomUUID as jb}from"node:crypto";import{execFile as Lb}from"node:child_process";import{promisify as Fb}from"node:util";var Qt=Fb(Lb),Ub=process.env.BOSTRAT_STATE_DIR||Rt.join(process.env.HOME||Bb.tmpdir(),".bostrat"),Sd=Rt.join(Ub,"views.json"),[yd,bd]=(process.env.BOSTRAT_VIEW_PORTS||"4300-4349").split("-").map(t=>Number(t)||0),Ie=Number(process.env.BOSTRAT_VIEW_MAX)||3,Hb="bostrat-view-",Wn=t=>`${Hb}${t}`,kd=new Set(["bostrat","www","api","views","app"]),ts=t=>Object.assign(new Error(t),{status:400}),Wb=new Set(["npm","pnpm","yarn","bun"]),ti="package.json",qb=/[|;&`]|\$\(/,Gb=new Set(["sh","bash","zsh","dash","env","exec","nohup","time"]),Kb=/(^|\s)(-C|--cwd|--manifest-path|--dir)(=|\s)/,vd=t=>String(t).trim().split(/\s+/).filter(Boolean),Vb=/^[A-Za-z_][A-Za-z0-9_]*=/;function Jb(t){for(let e of vd(t))if(!Vb.test(e))return e;return null}function Ad(t){let e=vd(t),n=e.indexOf("--");return n===-1?e:e.slice(0,n)}function zb(t,e){let n=Ad(e);for(let r=0;r<n.length;r++){let o=/^--prefix=(.+)$/.exec(n[r]);if(o)return Rt.resolve(t,o[1]);if(n[r]==="--prefix"&&n[r+1])return Rt.resolve(t,n[r+1])}return t}var Xb=t=>jr.stat(t).then(e=>e.isDirectory(),()=>!1),Td=t=>jr.stat(t).then(e=>e.isFile(),()=>!1),es=t=>jr.realpath(t).catch(()=>Rt.resolve(t));async function Yb(t,e){if(Rt.resolve(t)===Rt.resolve(e))return!0;let[n,r]=await Promise.all([es(t),es(e)]);return n===r}async function Zb(t){let n=(await jr.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 Td(Rt.join(t,o.name,ti))?`./${o.name}`:null))).filter(Boolean)}async function Qb(t,e){let[n,r]=await Promise.all([es(t),es(e)]),o=Rt.relative(n,r);if(o&&(o.startsWith("..")||Rt.isAbsolute(o)))throw ts(`serve directory escapes the Track workspace: ${e}`)}async function tk(t,e){if(!await Xb(t))throw ts(`serve directory does not exist: ${t}`);let n=Jb(e);if(!n)return;if(!n.includes("/")&&!await Ra(n,{loginShell:!0,failOpen:!0}))throw ts(`\`${n}\` is not on the box's PATH \u2014 install it in the box first`);if(qb.test(e)||Gb.has(n)||!Wb.has(n)||!Ad(e).includes("run")||Kb.test(e))return;let r=zb(t,e);if(await Td(Rt.join(r,ti)))return;let o=await Zb(r),s=o.length?` \u2014 found one in ${o.join(", ")}; run bostrat-view from there`:"";throw ts(`no ${ti} in ${r}${s}`)}var Zt=null,Za=null;async function pe(){return Zt||(Za||=(async()=>{try{Zt=JSON.parse(await jr.readFile(Sd,"utf8"))}catch{Zt={views:[],adoptWatermarks:{}}}finally{Za=null}return Zt.views||=[],Zt.adoptWatermarks||={},Zt})(),Za)}async function te(){await Xt(Sd,Zt)}function fe(t,e,n={}){return O({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 Dr=new Map,ri=Number(process.env.BOSTRAT_VIEW_JOB_TIMEOUT)||3e5,_d=12e3;function oi(t){return Dr.get(t)||null}function Ed(t,{name:e,box:n}={}){let r=Dr.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 Dr.set(t,o),o}function ei(t,e){let n=Dr.get(t);n&&!n.finishedAt&&!n.viewId&&(n.viewId=e,n.step="booting")}function ns(t,{error:e=null}={}){let n=Dr.get(t);return!n||n.finishedAt?null:(n.finishedAt=Date.now(),e&&(n.error=e),n)}function Id(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 ni(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=ni(e),r=new Set(t.views.map(o=>o.slug));if(!r.has(n)&&!kd.has(n))return n;for(let o=2;;o++){let s=`${n}-${o}`;if(!r.has(s)&&!kd.has(s))return s}}function Lr(t,{timeoutMs:e=1200}={}){return new Promise(n=>{let r=Db.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 si(t){if(process.platform!=="darwin"){await Qt("fuser",["-k",`${t}/tcp`]).catch(()=>{});return}let e="";try{({stdout:e}=await Qt("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 nk(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=yd;r<=bd;r++)if(!n.has(r)&&!await Lr(r,{timeoutMs:250}))return r;throw new Error(`no free view port in ${yd}-${bd}`)}async function rk(t){try{let{stdout:e}=await Qt(C,P("list-sessions","-F","#{session_name}"));return e.split(`
|
|
37
|
+
`).some(n=>n.trim()===Wn(t))}catch{return!1}}async function Fr(t){try{await Qt(C,P("kill-session","-t",Wn(t)))}catch{}}async function ai(t){if(!await $r())throw new Error("host tmux server not running and auto-start failed \u2014 cannot spawn a view server");let e=Wn(t.slug),n=t.serveCwd??t.cwd,r=Rt.dirname(process.execPath);await Qt(C,P("new-session","-d","-s",e,"-c",n)),await Qt(C,P("send-keys","-t",e,"-l",`export PORT=${t.port} PATH="${r}:$PATH"`)),await Qt(C,P("send-keys","-t",e,"Enter")),await Qt(C,P("send-keys","-t",e,"-l",t.cmd)),await Qt(C,P("send-keys","-t",e,"Enter"))}async function xd(t,e=12){try{let{stdout:n}=await Qt(C,P("capture-pane","-t",Wn(t),"-p"));return String(n||"").trimEnd().split(`
|
|
63
38
|
`).filter(Boolean).slice(-e).join(`
|
|
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}
|
|
65
|
-
|
|
66
|
-
|
|
39
|
+
`)}catch{return""}}var de=new Map,ok=12e4;async function sk(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 xd(t.slug)||"server exited before listening"),de.delete(t.id),await fe(t,t.state,t.error?{error:t.error}:{}),!0}if(await Lr(t.port))return de.delete(t.id),t.lastHealthyAt=Date.now(),t.state!=="live"?(t.state="live",t.error=null,await fe(t,"live"),!0):!1;if(t.state==="starting")return Date.now()-t.createdAt>ok?(t.state="error",t.error=await xd(t.slug)||"never started listening",await fe(t,"error",{error:t.error}),!0):!1;let o=(de.get(t.id)||0)+1;return de.set(t.id,o),o>=3&&t.state==="live"?(t.state="unhealthy",!0):!1}var Qa=!1;async function rs(){if(!Qa){Qa=!0;try{let t=await pe(),e=!1;for(let n of t.views)await sk(n)&&(e=!0);e&&await te()}finally{Qa=!1}}}async function os({trackId:t}={}){await rs();let e=await pe();return(t?e.views.filter(r=>r.trackId===t):e.views).map(r=>({...r}))}async function Rd(t){let n=(await pe()).views.find(r=>r.id===t);return n?{...n}:null}function ii(t){return Zt&&Zt.views.find(e=>e.slug===t)||null}async function ci({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=Rt.resolve(r,o??"."),d=i==="detected"?null:async()=>{await Qb(r,u),await tk(u,n)},f=await pe();if(i!=="detected"){let h=ni(e),b=String(n).trim(),x=f.views.find(v=>v.trackId===t&&(v.slug===h||ni(v.name)===h||v.cmd===b&&Rt.resolve(v.serveCwd??v.cwd)===u));if(x)return ak(f,x,{cmd:b,box:a,branch:c,serveCwd:l?u:void 0,validate:d})}if(d&&await d(),f.views.filter(h=>h.state==="live"||h.state==="starting").length>=Ie)throw new Error(`view cap reached (${Ie} live) \u2014 stop one first`);let m={id:jb().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 nk(f,s),origin:i,state:"starting",error:null,createdAt:Date.now(),lastHealthyAt:null};return i!=="detected"&&await ai(m),f.views.push(m),await te(),await fe(m,"created",{origin:i}),ei(t,m.id),i==="detected"&&await Lr(m.port)&&(m.state="live",m.lastHealthyAt=Date.now(),await te(),await fe(m,"live")),{...m}}async function ak(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 Yb(e.serveCwd??e.cwd,s);if((e.state==="live"||e.state==="starting")&&!i&&!c&&await Lr(e.port))return await te(),ei(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>=Ie)throw new Error(`view cap reached (${Ie} live) \u2014 stop one first`);return e.origin==="detected"?(await si(e.port),e.origin="agent"):await Fr(e.slug),e.cmd=n,s!==void 0&&(e.serveCwd=s),e.state="starting",e.error=null,e.createdAt=Date.now(),de.delete(e.id),await ai(e),await te(),await fe(e,"created",{restarted:!0,reused:!0}),ei(e.trackId,e.id),{...e,reused:!0}}async function $d(t){let n=(await pe()).views.find(r=>r.id===t);if(!n)throw new Error("view not found");return n.origin==="detected"?await si(n.port):await Fr(n.slug),n.state!=="stopped"&&(n.state="stopped",de.delete(n.id),await te(),await fe(n,"stopped")),{...n}}async function Od(t){let e=await pe(),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>=Ie)throw new Error(`view cap reached (${Ie} live) \u2014 stop one first`);return await Fr(n.slug),n.state="starting",n.error=null,n.createdAt=Date.now(),de.delete(n.id),await ai(n),await te(),await fe(n,"created",{restarted:!0}),{...n}}async function Cd(t){let e=await pe(),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 si(r.port):await Fr(r.slug),e.views.splice(n,1),de.delete(r.id),await te(),await fe(r,"deleted"),{ok:!0,id:t,slug:r.slug}}async function Md(t){let e=await pe(),n=e.views.filter(r=>r.trackId===t);for(let r of n)r.origin!=="detected"&&await Fr(r.slug),de.delete(r.id);return n.length&&(e.views=e.views.filter(r=>r.trackId!==t),await te()),n.length}var ik=/https?:\/\/(?:localhost|127\.0\.0\.1|0\.0\.0\.0)(?::(\d{2,5}))?(?:\/|\b)/g;async function Pd(t,e){if(!t?.id||!Array.isArray(e)||!e.length)return[];let n=await pe(),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 te()),!o.length)return[];let a=new Set;for(let c of o)for(let l of String(c.text||"").matchAll(ik)){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 Lr(c))try{i.push(await ci({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 on from"node:fs/promises";import Re from"node:path";import ck from"node:os";import{randomUUID as lk}from"node:crypto";var Gn=process.env.BOSTRAT_STATE_DIR||Re.join(process.env.HOME||ck.tmpdir(),".bostrat"),Bd=Re.join(Gn,"runbooks.json"),ui=Re.join(Gn,"runbooks"),Nd=256*1024,uk=[{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 di(t){return uk.filter(({re:e})=>e.test(t)).map(({label:e})=>e)}var rn=null,li=null;async function $e(){return rn||(li||=(async()=>{try{rn=JSON.parse(await on.readFile(Bd,"utf8"))}catch{rn={runbooks:[]}}finally{li=null}return rn.runbooks||=[],rn})(),li)}async function Kn(){await Xt(Bd,rn)}function Vn(t,e,n={}){return O({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 qn(t,e){return Re.join(ui,`${t.id}.v${e}.md`)}function Dd(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 jd(t){if(typeof t!="string"||!t.trim())throw new Error("md (markdown string) required");let e=Buffer.byteLength(t,"utf8");if(e>Nd)throw new Error(`runbook is ${Math.round(e/1024)} KB \u2014 over the ${Nd/1024} KB cap; a doc that size belongs in the repo, not a runbook`);let n=di(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 Ld(t,e){if(t.trackDeletedAt)throw new Error(`runbook is orphaned (its Track was deleted) \u2014 cannot ${e} it`)}async function ss({trackId:t,status:e}={}){let n=await $e(),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 fi(t){let n=(await $e()).runbooks.find(r=>r.id===t);return n?{...n}:null}async function Fd({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=jd(r),c=await $e(),l={id:lk().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 on.mkdir(ui,{recursive:!0}),await on.writeFile(qn(l,1),r),l.versions.push({v:1,mdRel:Re.relative(Gn,qn(l,1)),bytes:i,note:null,createdAt:Date.now()}),c.runbooks.push(l),await Kn(),await Vn(l,"created",{version:1}),{...l}}async function Ud(t,{md:e,note:n}={}){let o=(await $e()).runbooks.find(i=>i.id===t);if(!o)throw new Error("runbook not found");Ld(o,"update");let s=jd(e),a=o.version+1;return o.version=a,o.updatedAt=Date.now(),await on.mkdir(ui,{recursive:!0}),await on.writeFile(qn(o,a),e),o.versions.push({v:a,mdRel:Re.relative(Gn,qn(o,a)),bytes:s,note:n?String(n).slice(0,200):null,createdAt:Date.now()}),await Kn(),await Vn(o,"updated",{version:a,...n?{note:String(n).slice(0,200)}:{}}),{...o}}async function Hd(t,e){if(e!=="open"&&e!=="done")throw new Error('status must be "open" or "done"');let r=(await $e()).runbooks.find(o=>o.id===t);if(!r)throw new Error("runbook not found");return Ld(r,e==="done"?"complete":"reopen"),r.status===e?{...r}:(r.status=e,r.updatedAt=Date.now(),await Kn(),await Vn(r,e==="done"?"done":"reopened"),{...r})}async function Wd(t){let e=await $e(),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 on.rm(Re.join(Gn,o.mdRel),{force:!0}).catch(()=>{});return await Kn(),r.trackDeletedAt||await Vn(r,"deleted"),{ok:!0,id:t,name:r.name}}async function qd(t){let n=(await $e()).runbooks.filter(r=>r.trackId===t&&!r.trackDeletedAt);for(let r of n)r.trackDeletedAt=Date.now(),await Vn(r,"orphaned"),r.cwd=null;return n.length&&await Kn(),n.length}async function Gd(t){let e=await $e(),n=e.runbooks.filter(r=>r.trackId===t);for(let r of n){await Vn(r,"deleted");for(let o of r.versions||[])await on.rm(Re.join(Gn,o.mdRel),{force:!0}).catch(()=>{})}return n.length&&(e.runbooks=e.runbooks.filter(r=>r.trackId!==t),await Kn()),n.length}import Wr from"node:fs/promises";import af from"node:path";import jk from"node:os";import{randomUUID as cf}from"node:crypto";import dk from"node:fs/promises";import Kd from"node:path";import fk from"node:os";var Vd=()=>Kd.join(process.env.BOSTRAT_STATE_DIR||Kd.join(process.env.HOME||fk.tmpdir(),".bostrat"),"integrations.json");async function sn(){try{return JSON.parse(await dk.readFile(Vd(),"utf8"))||{}}catch{return{}}}var as=["linked","prOpened","prMerged","create"],pi=["prompt","auto","off"],pk={create:["prompt","off"]},Jd=t=>pk[t]??pi;function is(t){let e=t.linear||{},n=e.transitions===!0?"auto":"prompt",r={};for(let o of as){let s=Jd(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 an(){let t=await sn(),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 sn(),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 mi(){return is(await sn())}async function zd(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=Jd(r);if(!s.includes(o))throw new Error(s.length<pi.length?`mode "${o}" not allowed for "${r}" \u2014 ${r} is always confirmed (modes: ${s.join(", ")})`:`unknown mode "${o}" \u2014 modes: ${pi.join(", ")}`)}let n=await sn();return n.ticketFlow={...is(n),...Object.fromEntries(e)},n.linear&&(delete n.linear.ticketFlow,delete n.linear.transitions),await Xt(Vd(),n,{mode:384}),n.ticketFlow}async function Ur(){let t=(await sn()).deepgram||{},e=process.env.DEEPGRAM_API_KEY||t.apiKey;return e?{apiKey:e}:null}async function Xd(){let t=(await sn()).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 cn(){let t=(await sn()).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}}var wi={};Jl(wi,{createComment:()=>Sk,findIssueUrl:()=>gk,issueStates:()=>Tk,mdToAdf:()=>Qd,moveIssue:()=>_k,parseIssueRef:()=>wk,resolveIssue:()=>yk,uploadImage:()=>vk,upsertCard:()=>bk,whoami:()=>hi});var Jn=t=>String(t.baseUrl||"").replace(/\/$/,""),Zd=t=>`Basic ${Buffer.from(`${t.email}:${t.apiToken}`).toString("base64")}`;async function ln(t,e,n,r){let o=await fetch(`${Jn(t)}${n}`,{method:e,headers:{authorization:Zd(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 mk=/\/browse\/([A-Za-z][A-Za-z0-9]{0,9}-\d+)/,hk=/^([A-Z][A-Z0-9]{0,9}-\d+)$/;function wk(t){let e=String(t||"").trim();if(/^https?:\/\//i.test(e)){let r=e.match(mk);return r?r[1].toUpperCase():null}let n=e.match(hk);return n?n[1]:null}function gk(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 yk(t,e){let n;try{n=await ln(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:`${Jn(t)}/browse/${n.key}`,state:n.fields?.status?.name??null}}async function bk(t,e,{url:n,title:r,subtitle:o}){await ln(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 kk=/(!\[[^\]]*\]\([^)\s]+\)|\*\*[^*\n]+\*\*|`[^`\n]+`|_[^_\n]+_|https?:\/\/[^\s)]+)/g;function xk(t){let e=[],n=0;for(let r of t.matchAll(kk)){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 Qd(t){let e=String(t??"").split(/\n{2,}/).map(n=>n.trim()).filter(Boolean).map(n=>{let r=[];return n.split(`
|
|
40
|
+
`).forEach((o,s)=>{s>0&&r.push({type:"hardBreak"}),r.push(...xk(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 Sk(t,e,n){let r=await ln(t,"POST",`/rest/api/3/issue/${encodeURIComponent(e.key)}/comment`,{body:Qd(n)});return r?.id?`${Jn(t)}/browse/${e.key}?focusedCommentId=${r.id}`:null}async function hi(t){let e=await ln(t,"GET","/rest/api/3/myself"),n=null;try{n=new URL(Jn(t)).host}catch{n=t.baseUrl??null}return{user:e?.displayName??e?.emailAddress??null,org:n}}async function vk(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(`${Jn(t)}/rest/api/3/issue/${encodeURIComponent(e)}/attachments`,{method:"POST",headers:{authorization:Zd(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??`${Jn(t)}/rest/api/3/attachment/content/${c.id}`}var Ak={new:"unstarted",indeterminate:"started",done:"completed"},Yd=t=>Ak[t?.key]??"started";async function Tk(t,e){let n=encodeURIComponent(e.key),[r,o]=await Promise.all([ln(t,"GET",`/rest/api/3/issue/${n}?fields=status`),ln(t,"GET",`/rest/api/3/issue/${n}/transitions`)]),s=r?.fields?.status??null,a=s?{id:`status:${s.id}`,name:s.name,type:Yd(s.statusCategory)}:null,i=(o?.transitions??[]).map((c,l)=>({id:c.id,name:c.to?.name??c.name,type:Yd(c.to?.statusCategory),position:l}));return{current:a,states:i}}async function _k(t,e,n){await ln(t,"POST",`/rest/api/3/issue/${encodeURIComponent(e.key)}/transitions`,{transition:{id:String(n)}})}var ki={};Jl(ki,{createComment:()=>Pk,createIssue:()=>bi,createProject:()=>yi,findIssueUrl:()=>Ok,issueStates:()=>Bk,moveIssue:()=>Dk,parseIssueRef:()=>$k,resolveIssue:()=>Ck,teams:()=>zn,uploadImage:()=>Nk,upsertCard:()=>Mk,whoami:()=>gi});var Ek=()=>process.env.BOSTRAT_LINEAR_API_URL||"https://api.linear.app/graphql";async function ee(t,e,n={}){let r=await fetch(Ek(),{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){let s=o.errors[0],a=(s.extensions?.validationErrors??[]).flatMap(i=>Object.values(i?.constraints??{})).join("; ");throw new Error(`linear: ${s.message}${a?` \u2014 ${a}`:""}`)}return o?.data??{}}var Ik=/linear\.app\/[^/\s]+\/issue\/([A-Za-z][A-Za-z0-9]{0,9}-\d+)/,Rk=/^([A-Z][A-Z0-9]{0,9}-\d+)$/;function $k(t){let e=String(t||"").trim(),n=e.match(Ik);if(n)return n[1].toUpperCase();let r=e.match(Rk);return r?r[1]:null}function Ok(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 Ck(t,e){let r=(await ee(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 Mk(t,e,{url:n,title:r,subtitle:o}){let s=await ee(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 Pk(t,e,n){let r=await ee(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 gi(t){let e=await ee(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 Nk(t,{filename:e,contentType:n,buffer:r}){let o=await ee(t,`mutation($contentType: String!, $filename: String!, $size: Int!) {
|
|
41
|
+
fileUpload(contentType: $contentType, filename: $filename, size: $size) {
|
|
42
|
+
success
|
|
43
|
+
uploadFile { uploadUrl assetUrl headers { key value } }
|
|
44
|
+
}
|
|
45
|
+
}`,{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 Bk(t,e){let n=await ee(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 Dk(t,e,n){if(!(await ee(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 zn(t){return((await ee(t,"query { teams(first: 100) { nodes { id key name } } }")).teams?.nodes??[]).map(n=>({id:n.id,key:n.key,name:n.name}))}var tf=255;async function yi(t,{teamId:e,name:n,description:r}){let o=r||null,s=!!o&&o.length>tf,a=s?`${o.slice(0,tf-1).trimEnd()}\u2026`:o,i=await ee(t,"mutation($input: ProjectCreateInput!) { projectCreate(input: $input) { success project { id url name } } }",{input:{teamIds:[e],name:n,...a?{description:a}:{},...s?{content:o}:{}}}),c=i.projectCreate?.project;if(!i.projectCreate?.success||!c)throw new Error("linear: projectCreate did not succeed");return{id:c.id,url:c.url,name:c.name}}async function bi(t,{teamId:e,title:n,description:r,projectId:o,priority:s}){let a=await ee(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 lf={linear:{api:ki,config:an},jira:{api:wi,config:cs}};async function _i(){let t=[];for(let[e,{api:n,config:r}]of Object.entries(lf)){let o=await r();o&&t.push({name:e,api:n,cfg:o})}return t}async function un(t){let e=lf[t?.provider];if(!e)return null;let n=await e.config();return n?{name:t.provider,api:e.api,cfg:n}:null}function Ei(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 uf=()=>process.env.BOSTRAT_STATE_DIR||af.join(process.env.HOME||jk.tmpdir(),".bostrat"),Ai=()=>af.join(uf(),"tickets.json"),df=()=>(process.env.BOSTRAT_PUBLIC_URL||"http://bostrat.globopi").replace(/\/$/,""),us=t=>`${df()}/track/${t}`,ls=null,xi=null,Si=Promise.resolve();async function ds(){return ls||(xi??=Wr.readFile(Ai(),"utf8").then(t=>JSON.parse(t)?.tickets??{}).catch(()=>({})).then(t=>ls=t).finally(()=>xi=null),xi)}async function ef(){let t=JSON.stringify({tickets:ls},null,2),e=`${Ai()}.${process.pid}.tmp`;await Wr.mkdir(uf(),{recursive:!0}),await Wr.writeFile(e,t),await Wr.rename(e,Ai())}async function Oe(){Si=Si.then(ef,ef),await Si.catch(()=>{})}async function ne(t){return(await ds())[t]??null}async function fs(){return{...await ds()}}async function ff(){return(await _i()).length>0}async function Xn(t,e,{source:n="api"}={}){let r=await _i();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,...t.workspace?.kind==="multi"?{repos:(t.workspace.repos??[]).map(u=>u?.name).filter(Boolean)}:{}},pr:null};(await ds())[t.id]=c,await Oe(),await O({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 qr(t.id,c);let l=[];return c.track.branch&&l.push(`\`${c.track.branch}\``),c.track.repos?.length&&l.push(`repos ${c.track.repos.join(" + ")}`),c.track.leadAgent&&l.push(`lead ${c.track.leadAgent}`),await Hr(c,`**Bostrat** \u2014 Track **${c.track.name}** linked${l.length?` (${l.join(", ")})`:""}.`),await hf(t.id,c,"linked"),c}async function Ii(t){let e=await ds();return e[t]?(delete e[t],await Oe(),!0):!1}var Ti=t=>`$${t>=100?Math.round(t):t.toFixed(2)}`;async function pf(t,e){if(!t.track?.cwd)return null;let n=_e(await F({id:e,cwd:t.track.cwd}));return!n.totalUsd||n.totalUsd<.01?null:n}function Lk(t){let e=Object.entries(t.byProvider).sort((r,o)=>o[1].usd-r[1].usd).map(([r,o])=>`${r} ${Ti(o.usd)}`).join(" \xB7 "),n=Object.keys(t.byBox).length;return`${Ti(t.totalUsd)}${e?` (${e})`:""}${n>1?` across ${n} boxes`:""}`}function Fk(t,e){let n=[];return t.track.leadAgent&&n.push(`lead ${t.track.leadAgent}`),t.track.branch&&n.push(t.track.branch),t.track.repos?.length&&n.push(t.track.repos.join(" + ")),t.pr?.number&&n.push(`PR #${t.pr.number} ${t.pr.change==="merged"?"merged":"open"}`),e&&n.push(Ti(e.totalUsd)),n.join(" \xB7 ")||"linked"}async function qr(t,e){let n=await un(e);if(!n)return;let r=await pf(e,t).catch(()=>null);await n.api.upsertCard(n.cfg,e,{url:us(t),title:`Bostrat Track: ${e.track.name}`,subtitle:Fk(e,r)})}function ps(t){let e=new URL(df());return`${e.protocol}//${t}.${e.host}/`}var Uk={linked:"linked",opened:"prOpened",merged:"prMerged"};function Hk(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 mf(t,e,{from:n,to:r,mode:o,phase:s=null,actor:a="user"}){e.state=r,await Oe(),await O({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 hf(t,e,n){let r=await un(e);if(!r)return;let o=r.cfg.ticketFlow?.[Uk[n]]??"prompt";if(o==="off")return;let{current:s,states:a}=await r.api.issueStates(r.cfg,e),i=Ei(s,a),c=Hk(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 mf(t,e,{from:l,to:c.name,mode:"auto",phase:n,actor:"system"});return}await O({id:t,cwd:e.track?.cwd},{type:"ticket.move.offered",actor:"system",source:"orchestrator",payload:{ts:Date.now(),offerId:cf(),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 wf(t){let e=await ne(t);if(!e)throw new Error("no ticket linked to this Track");let n=await un(e);if(!n)throw new Error("no ticketing integration configured");let{current:r,states:o}=await n.api.issueStates(n.cfg,e),s=Ei(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 Ri(t,e,{mode:n="manual"}={}){if(!e)throw new Error("stateId required");let r=await ne(t);if(!r)throw new Error("no ticket linked to this Track");let o=await un(r);if(!o)throw new Error("no ticketing integration configured");let{current:s,states:a}=await o.api.issueStates(o.cfg,r),i=Ei(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 mf(t,r,{from:l,to:c.name,mode:n}),{key:r.key,from:l,to:c.name,moved:!0})}async function gf(t,e){let n=await ne(t);if(!n)throw new Error("no ticket linked to this Track");let r=await un(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 O({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 yf(t,e,n){if(!e)throw new Error("offerId required");let r=await F({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 Ri(t.id,o.payload.to?.id,{mode:"offer"})),await O({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 nf=50;async function bf(){let t=await an();if(!t)throw new Error("no ticketing integration configured");return zn(t)}function Wk(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>nf)throw new Error(`too many issues (${n.length}); cap is ${nf} per proposal`);return{epic:r,issues:n,team:e.team?String(e.team).trim():null,linkCurrentTrack:e.linkCurrentTrack===!0}}async function qk(t,e,n,r){let o=r??await zn(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 ne(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 kf(t,e){let n=await an();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=Wk(e),o=await zn(n),s=await qk(n,r,t,o),a=s?{id:s.id,key:s.key,name:s.name}:null,i=cf();return await O({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 xf(t,e,n,{teamId:r=null}={}){if(!e)throw new Error("proposalId required");let o=await F({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 O({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 an();if(!a)throw new Error("no ticketing integration configured");let i=s.payload,c=i.team??null;if(!c&&r){let m=(await zn(a)).find(h=>h.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 yi(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 h=await bi(a,{teamId:c.id,title:m.title,description:m.description,projectId:d,priority:m.priority});l.push({kind:"issue",key:h.key,title:h.title,url:h.url})}catch(h){u.push({title:m.title,message:h?.message||String(h)})}await O({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 O({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"),p=null;return i.linkCurrentTrack&&f.length===1&&!await ne(t.id)&&(p=await Xn(t,f[0].key,{source:"created"}).catch(()=>null)),{proposalId:e,action:"created",created:l,failed:u,linked:p?p.key:null}}async function Hr(t,e){let n=await un(t);n&&await n.api.createComment(n.cfg,t,e)}async function Gk(t,e,n){let r=await un(t);if(!r)return null;let o=await Ee(e);if(!o)return null;let s=nn(o,n);if(!s)return null;let a=await Wr.readFile(Lt(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 rf=()=>Number(process.env.BOSTRAT_TICKET_COST_SYNC_MS??12e4),vi=new Map;function Kk(t,e){if(rf()<=0)return qr(t,e);if(vi.has(t))return;let n=setTimeout(()=>{vi.delete(t),ne(t).then(r=>r?qr(t,r):null).catch(()=>{})},rf());n.unref?.(),vi.set(t,n)}var of=new Map;async function sf(t,e,n){let r=of.get(t)??0;Date.now()-r<600*1e3||(of.set(t,Date.now()),await O({id:t,cwd:e},{type:"ticket.sync.failed",actor:"system",source:"orchestrator",payload:{ts:Date.now(),message:n?.message||String(n)}}).catch(()=>{}))}async function Vk(t,{resolveTrack:e=null}={}){let n=await ne(t.trackId),r=t.payload||{};if(!n){if(t.type!=="turn.user"||t.actor!=="user")return;let o=null;for(let a of await _i())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 Xn(s,o,{source:"chat"})}catch(a){await sf(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 qr(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 qr(t.trackId,n),r.change==="opened"||r.change==="merged"){let o=r.change==="merged"?await pf(n,t.trackId).catch(()=>null):null;await Hr(n,`**Bostrat** \u2014 PR ${r.change} for **${n.track.name}**${r.url?`: ${r.url}`:r.number?` (#${r.number})`:""}`+(o?`
|
|
46
|
+
|
|
47
|
+
Agent cost for this Track: ${Lk(o)}.`:"")),await hf(t.trackId,n,r.change)}break}case"mock.state.changed":{if(r.change==="ready"&&r.mockId){let o=await Gk(n,r.mockId,r.version??null);if(!o)break;let s=(r.version??1)>1?` revised (v${r.version})`:"";await Hr(n,`**Bostrat** \u2014 mock **${r.name}**${s}${r.group?` (group \`${r.group}\`)`:""}:
|
|
48
|
+
|
|
49
|
+
`)}else if(r.change==="picked"){let o=(r.siblings??[]).map(s=>`**${s.name}**`).join(", ");await Hr(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 Hr(n,`**Bostrat** \u2014 live preview **${r.name}**: ${ps(r.slug)}
|
|
50
|
+
_(runs on the Track's branch; reachable on the operator's private network only)_`);break}case"cost.recorded":await Kk(t.trackId,n);break;default:break}}catch(o){let s=e?await e(t.trackId).catch(()=>null):null;await sf(t.trackId,s?.cwd??null,o)}}function Sf({resolveTrack:t}){jt.on("event",e=>{Vk(e,{resolveTrack:t}).catch(()=>{})})}import ms from"node:fs/promises";import vf from"node:path";import Jk from"node:os";var Af=process.env.BOSTRAT_STATE_DIR||vf.join(process.env.HOME||Jk.tmpdir(),".bostrat"),Oi=vf.join(Af,"pr-links.json"),it=(t,e)=>`${t}#${Number(e)}`,hs=null,$i=null,N=Promise.resolve();async function gt(){return hs||($i??=ms.readFile(Oi,"utf8").then(t=>JSON.parse(t)?.links??{}).catch(()=>({})).then(t=>hs=t).finally(()=>$i=null),$i)}async function X(){let t=JSON.stringify({links:hs},null,2),e=`${Oi}.${process.pid}.tmp`;await ms.mkdir(Af,{recursive:!0}),await ms.writeFile(e,t),await ms.rename(e,Oi)}var zk=new Set(["api","observed","reconciled"]);async function Pt({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 gt(),c=it(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:zk.has(a),firstSeen:Date.now()},N=N.then(X,X),await N.catch(()=>{}),{link:i[c],created:!0})}async function Gr(t,e){if(!t||e==null)return null;let n=(await gt())[it(t,e)];return!n||n.announced?null:(n.announced=!0,N=N.then(X,X),await N.catch(()=>{}),n)}async function vt(t,e){return!t||e==null?null:(await gt())[it(t,e)]??null}async function Ce(t,e,n){if(!t||e==null)return null;let r=(await gt())[it(t,e)];return r?(r.state=n,r.resolvedAt=Date.now(),delete r.armedAt,delete r.autoMerge,delete r.archiveOnMerge,delete r.notifyOnMerge,delete r.notifyPending,N=N.then(X,X),await N.catch(()=>{}),r):null}async function Tf(t,e,{headSha:n=null,trackId:r=null}={}){if(!t||e==null)return null;let o=(await gt())[it(t,e)];return o?(o.ciFix={delegatedAt:Date.now(),headSha:n,trackId:r},N=N.then(X,X),await N.catch(()=>{}),o):null}async function _f(t,e,{headSha:n=null,trackId:r=null}={}){if(!t||e==null)return null;let o=(await gt())[it(t,e)];return o?(o.reconcile={delegatedAt:Date.now(),headSha:n,trackId:r},N=N.then(X,X),await N.catch(()=>{}),o):null}async function ws(t,e,{headSha:n=null}={}){if(!t||e==null)return null;let r=(await gt())[it(t,e)];return!r||!r.ciFix?null:(r.ciFixAwait={since:Date.now(),trackId:r.ciFix.trackId??null,headSha:n},delete r.ciFix,N=N.then(X,X),await N.catch(()=>{}),r)}async function gs(t,e){if(!t||e==null)return null;let n=(await gt())[it(t,e)];return!n||!n.ciFixAwait?null:(delete n.ciFixAwait,N=N.then(X,X),await N.catch(()=>{}),n)}async function ys(t,e,{headSha:n=null}={}){if(!t||e==null)return null;let r=(await gt())[it(t,e)];return!r||!r.reconcile?null:(r.reconcileAwait={since:Date.now(),trackId:r.reconcile.trackId??null,headSha:n},delete r.reconcile,N=N.then(X,X),await N.catch(()=>{}),r)}async function bs(t,e){if(!t||e==null)return null;let n=(await gt())[it(t,e)];return!n||!n.reconcileAwait?null:(delete n.reconcileAwait,N=N.then(X,X),await N.catch(()=>{}),n)}async function Ef(t,e,{at:n=Date.now()}={}){if(!t||e==null)return null;let r=(await gt())[it(t,e)];return r?(r.baseFoldAt=n,N=N.then(X,X),await N.catch(()=>{}),r):null}async function If(t,e){if(!t||e==null)return null;let n=(await gt())[it(t,e)];return!n||!n.baseFoldAt?null:(delete n.baseFoldAt,N=N.then(X,X),await N.catch(()=>{}),n)}async function Rf(t,e,n){if(!t||e==null||!n)return null;let r=(await gt())[it(t,e)];return r?(r.lastHeadSha=n,N=N.then(X,X),await N.catch(()=>{}),r):null}async function Kr(t,e,{merge:n=null}={}){if(!t||e==null)return null;let r=(await gt())[it(t,e)];return r?(r.armedAt=Date.now(),n&&(r.autoMerge={method:n.method||"merge",armedAt:r.armedAt}),N=N.then(X,X),await N.catch(()=>{}),r):null}async function Yn(t,e,n=!0){if(!t||e==null)return null;let r=(await gt())[it(t,e)];return r?(n?r.archiveOnMerge=!0:delete r.archiveOnMerge,N=N.then(X,X),await N.catch(()=>{}),r):null}async function ks(t,e,n=!0){if(!t||e==null)return null;let r=(await gt())[it(t,e)];return r?(n?r.notifyOnMerge=!0:delete r.notifyOnMerge,N=N.then(X,X),await N.catch(()=>{}),r):null}async function $f(t,e,n){if(!t||e==null)return null;let r=(await gt())[it(t,e)];if(!r)return null;r.notifyOnMerge=!0;let o=new Map((r.notifyPending||[]).map(s=>[`${s.repo}#${s.number}`,s]));for(let s of n||[])s&&s.number!=null&&o.set(`${s.repo}#${s.number}`,s);return r.notifyPending=[...o.values()],N=N.then(X,X),await N.catch(()=>{}),r}async function Vr(t,e){if(!t||e==null)return null;let n=(await gt())[it(t,e)];return!n||!n.armedAt&&!n.autoMerge?null:(delete n.armedAt,delete n.autoMerge,delete n.archiveOnMerge,delete n.notifyOnMerge,delete n.notifyPending,N=N.then(X,X),await N.catch(()=>{}),n)}var Xk=new Set(["merged","closed","lost"]);async function Of(){return Object.values(await gt()).filter(t=>!Xk.has(t.state))}async function Me(){return Object.values(await gt()).sort((t,e)=>e.firstSeen-t.firstSeen)}var Mf=t=>Math.round(t*100)/100;function Yk(t){return!t||t.kind==="setup"?[]:$t(t.workspace).filter(e=>e&&(e.repo||e.base)).map(e=>({key:e.repo||e.base,slug:e.repo??null,base:e.base??null,name:e.repo?e.repo.split("/").pop():Cf.basename(e.base)}))}function Pf(t){let e=new Map;for(let n of t||[])for(let r of Yk(n)){let o=e.get(r.key);o||e.set(r.key,o={...r,tracks:[]}),o.tracks.push(n)}return e}function Nf(t){let e=t.events?.length?t.events[t.events.length-1].at:0;return Math.max(t.createdAt||0,t.archivedAt||0,t.lastResponseAt||0,e||0)||null}var Ci=(t,e)=>`${t} ${e}${t===1?"":"s"}`;function Zk(t){if(!Number.isFinite(t)||t<=0)return null;let e=t/864e5;if(e>=1)return`${Math.round(e)}d`;let n=t/36e5;return n>=1?`${Math.round(n)}h`:`${Math.max(1,Math.round(t/6e4))}m`}function Mi(t,e=[]){let n=[],r=t?.events||[],o=new Map,s=0;for(let u of e){if(u.type!=="pr.state.changed")continue;let d=u.payload||{};!d.repo||d.number==null||(d.change==="merged"?o.set(`${d.repo}#${d.number}`,d):d.change==="opened"&&s++)}if(o.size){let u=[...o.keys()].map(d=>{let[f,p]=d.split("#");return`${f.split("/").pop()}#${p}`});n.push(`Merged ${u.slice(0,4).join(", ")}${u.length>4?` +${u.length-4}`:""}`)}else s&&n.push(Ci(s,"PR")+" opened");let a=Object.keys(t?.subtasks||{}).length||r.filter(u=>u.type==="subtask-delegated").length;a&&n.push(Ci(a,"subtask"));let i=r.filter(u=>u.type==="lead-handoff"&&u.fromAgent&&u.toAgent);if(i.length){let u=i.slice(0,3).map(d=>`${d.fromAgent}\u2192${d.toAgent}`).join(", ");n.push(`${Ci(i.length,"handoff")} ${u}${i.length>3?"\u2026":""}`)}let c=_e(e);c.totalUsd>=.01&&n.push(`$${c.totalUsd.toFixed(2)}`);let l=Number.isFinite(t?.archivedAt)&&Number.isFinite(t?.createdAt)?Zk(t.archivedAt-t.createdAt):null;return n.length&&l&&n.push(l),n.length?n.join(" \xB7 "):null}function Qk(t){let e=[],n=[...t?.boxes||[]].sort((o,s)=>(o.createdAt||0)-(s.createdAt||0));for(let o of n){if(o.role!=="lead"&&o.role!=="handed-off")continue;let s=Array.isArray(o.epochs)&&o.epochs.length?o.epochs.map(a=>a.agent):[o.agent];for(let a of s)a&&e[e.length-1]!==a&&e.push(a)}let r=new Set(n.filter(o=>o.role==="subtask"&&o.agent&&!e.includes(o.agent)).map(o=>o.agent));return{lead:e,subtask:[...r]}}function Bf(t,e,n){let r=new Set(t.tracks.map(f=>f.id)),o=0,s=0,a=0,i=null,c=null,l=0;for(let f of t.tracks){f.archivedAt?s++:o++,f.beat?.text&&a++;let p=Nf(f);p&&(!i||p>i)&&(i=p),f.createdAt&&(!c||f.createdAt<c)&&(c=f.createdAt),l+=e.get(f.id)||0}let u=0,d=0;for(let f of n)r.has(f.trackId)&&(t.slug&&f.repo!==t.slug||t.slug&&(f.state==="merged"?d++:f.state!=="closed"&&u++));return{key:t.key,slug:t.slug,base:t.base,name:t.name,tracks:{total:t.tracks.length,live:o,archived:s,withBeat:a},prs:{open:u,merged:d},usd:Mf(l),firstActivityAt:c,lastActivityAt:i}}async function Df(t){let e=Pf(t),n=await Me().catch(()=>[]),r=new Map,o=new Set;for(let a of e.values())for(let i of a.tracks){if(o.has(i.id))continue;o.add(i.id);let c=await F(i).catch(()=>[]);r.set(i.id,_e(c).totalUsd)}let s=[...e.values()].map(a=>Bf(a,r,n)).sort((a,i)=>(i.lastActivityAt??0)-(a.lastActivityAt??0));return{generatedAt:Date.now(),arcs:s}}async function jf(t,e){let r=Pf(t).get(e);if(!r)return null;let[o,s,a,i,c]=await Promise.all([Me().catch(()=>[]),Qo().catch(()=>[]),os().catch(()=>[]),ss().catch(()=>[]),fs().catch(()=>({}))]),l=new Map,u=[];for(let d of[...r.tracks].sort((f,p)=>(p.createdAt||0)-(f.createdAt||0))){let f=await F(d).catch(()=>[]),p=_e(f);l.set(d.id,p.totalUsd);let m=c[d.id]||null;u.push({trackId:d.id,name:d.name,createdAt:d.createdAt??null,archivedAt:d.archivedAt??null,live:!d.archivedAt,beat:d.beat??null,agents:Qk(d),handoffs:(d.events||[]).filter(h=>h.type==="lead-handoff").length,subtasks:Object.keys(d.subtasks||{}).length,repos:$t(d.workspace).map(h=>h.repo||Cf.basename(h.base||"")),prs:o.filter(h=>h.trackId===d.id&&(!r.slug||h.repo===r.slug)).map(h=>({repo:h.repo,number:h.number,state:h.state??null,title:h.title??null,url:h.url??null})),mocks:s.filter(h=>h.trackId===d.id).length,views:a.filter(h=>h.trackId===d.id).length,runbooks:i.filter(h=>h.trackId===d.id).map(h=>({name:h.name,status:h.status})),ticket:m?{provider:m.provider,key:m.key,url:m.url??null}:null,usd:Mf(p.totalUsd),lastActivityAt:Nf(d)})}return{...Bf(r,l,o),generatedAt:Date.now(),segments:u}}import{execFile as tx}from"node:child_process";import{promisify as ex}from"node:util";import{randomUUID as nx}from"node:crypto";var dn=ex(tx),xs=t=>new Promise(e=>setTimeout(e,t)),rx=Number(process.env.BOSTRAT_SWAP_EXIT_TIMEOUT_MS)||15e3,Lf=Number(process.env.BOSTRAT_SWAP_SHELL_TIMEOUT_MS)||8e3,ox=250,sx=400,Ff={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 Hf(t){let e=await ou(t);if(!e)return!1;try{return await dn("pgrep",["-P",String(e)]),!1}catch(n){return n?.code===1}}async function Pi(t,e){let n=Date.now()+e;for(;;){if(await Hf(t))return!0;if(Date.now()>n)return!1;await xs(ox)}}async function ax(t,e){let n=Ff[e]||Ff.default;n.escape&&(await bt(t,["Escape"],{kind:"swap-exit"}),await xs(150)),n.wipe&&await Do(t,400,{kind:"swap-exit"}),await jo(t,n.text,{perCharDelayMs:90,kind:"swap-exit"});let r=le(e).submitDelayMs||0;await xs(Math.max(r,250)),await bt(t,["Enter"],{kind:"swap-exit"})}async function ix(t,e){try{let{stdout:n}=await dn(C,P("show-environment","-t",t,e));return!String(n||"").trimStart().startsWith("-")}catch{return!1}}async function Uf(t){await dn(C,P("respawn-pane","-k","-t",t))}async function Ni(t,e={}){if(!pt(t))throw new Error("invalid session name");let n=String(e.agentCmd||"").trim();if(!n)throw new Error("agentCmd is required");let r=await z(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||Pn(n),a=Number(e.exitTimeoutMs)||rx;await q({flow:"swap",phase:"start",sessionName:t,from:o,to:s,cwd:r.cwd});let i=!1;if(!await Hf(t)&&(await ax(t,o),!await Pi(t,a)&&(i=!0,await q({flow:"swap",phase:"respawn-forced",sessionName:t,from:o}),await Uf(t),!await Pi(t,Lf))))throw new Error(`pane shell did not come back after respawn-pane in "${t}"`);let c=s==="claude"&&Mn(n),l=c?Co():null,u=await ix(t,"ANTHROPIC_API_KEY"),d=!1;try{l?(await dn(C,P("set-environment","-t",t,"ANTHROPIC_API_KEY",l)),d=!0):u&&(await dn(C,P("set-environment","-u","-t",t,"ANTHROPIC_API_KEY")),d=!0)}catch(b){throw l?Ta(b,l):b}if(d&&!i&&(await Uf(t),!await Pi(t,Lf)))throw new Error(`pane shell did not come back after env respawn in "${t}"`);let f=!!e.resumeSessionId,p=e.resumeSessionId||nx(),m=f?n:Ia(n,p),h=f?e.pinned===!0:/(^|\s)--session-id(\s|=)/.test(m);return c&&await xe(r.cwd),s==="codex"&&await $n(r.cwd),s==="cursor"&&await On(r.cwd),await tn(t),await dn(C,P("set-option","-t",t,"@bostrat-sid",p)),await dn(C,P("set-option","-t",t,"@bostrat-agent",s)),await xs(sx),await kt(t,m,{kind:"swap-launch"}),await q({flow:"swap",phase:"done",sessionName:t,from:o,to:s,sid:p,forced:i,envRespawned:d}),{name:t,cwd:r.cwd,sessionId:p,agent:s,pinned:h,forced:i,envRespawned:d}}import yx from"node:fs";import qf from"node:fs/promises";import Jr from"node:path";import bx from"node:os";import{fileURLToPath as kx}from"node:url";import{execFile as xx}from"node:child_process";import{promisify as Sx}from"node:util";import Di from"node:fs/promises";import Pe from"node:path";import{execFile as cx}from"node:child_process";import{promisify as lx}from"node:util";var ux=lx(cx),Wf=process.env.HOME||"/home/globo",dx=Pe.join(process.env.BOSTRAT_STATE_DIR||Pe.join(Wf,".bostrat"),"repos"),fx=[...(process.env.BOSTRAT_REPOS_ROOT||Pe.join(Wf,"Documents")).split(","),dx].map(t=>t.trim()).filter(Boolean),px=Number(process.env.BOSTRAT_REPOS_DEPTH)||3,mx=new Set(["node_modules",".git",".cache","dist","build",".next","vendor","__pycache__","venv",".venv","target"]);async function hx(t){try{return(await Di.stat(Pe.join(t,".git"))).isDirectory()?"repo":"worktree"}catch{return null}}async function wx(t){let e=i=>Di.readFile(Pe.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 me(){let t=new Map;async function e(n,r){if(t.has(n))return;let o=await hx(n);if(o==="worktree")return;if(o==="repo"){t.set(n,{path:n,name:Pe.basename(n),branch:await wx(n)});return}if(r>=px)return;let s;try{s=await Di.readdir(n,{withFileTypes:!0})}catch{return}for(let a of s)!a.isDirectory()||a.name.startsWith(".")||mx.has(a.name)||await e(Pe.join(n,a.name),r+1)}for(let n of fx)await e(Pe.resolve(n),0);return[...t.values()].sort((n,r)=>n.name.localeCompare(r.name))}function gx(t){let e=String(t||"").trim().match(/github\.com[:/]+([^/\s]+)\/([^/\s]+?)(?:\.git)?\/?$/i);return e?`${e[1]}/${e[2]}`:null}var Bi=new Map;async function Y(t){if(!t)return null;if(Bi.has(t))return Bi.get(t);let e=null;try{let{stdout:n}=await ux("git",["-C",t,"config","--get","remote.origin.url"],{timeout:5e3});e=gx(n)}catch{e=null}return Bi.set(t,e),e}var Gf=Sx(xx),vx=process.env.BOSTRAT_STATE_DIR||Jr.join(process.env.HOME||bx.tmpdir(),".bostrat"),Ax=Jr.join(vx,"repos"),Kf=/^[^/\s]+\/[^/\s]+$/,Tx=5*6e4,_x=6e4,Ex=10*6e4,Vf=fetch,ji=new Map,fn=null;function Li(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 Ss(t=process.env){return!!(Li(t)&&t.BOSTRAT_PLANE_TOKEN)}var Jf=t=>({authorization:`Bearer ${t.BOSTRAT_PLANE_TOKEN||""}`}),Zn=(t,e)=>Object.assign(new Error(t),{status:e});async function vs(t,{env:e=process.env}={}){let n=String(t||"").toLowerCase();if(!Kf.test(n))throw Zn("repo must be owner/name",400);let r=ji.get(n);if(r&&r.expiresAt-Date.now()>Tx)return r;if(!Ss(e))throw Zn("node is not plane-linked \u2014 GitHub auth unavailable",503);let o;try{o=await Vf(`${Li(e)}/v1/github/token?repo=${encodeURIComponent(t)}`,{headers:Jf(e)})}catch(i){if(r&&r.expiresAt>Date.now())return r;throw Zn(`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 Zn(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 ji.set(n,a),a}async function zr({env:t=process.env,force:e=!1}={}){if(!Ss(t))return{configured:!1,repos:[],username:null};if(!e&&fn&&Date.now()-fn.at<_x)return fn.data;try{let n=await Vf(`${Li(t)}/v1/github/repos`,{headers:Jf(t)});if(!n.ok)throw new Error(`plane replied ${n.status}`);let r=await n.json();return fn={at:Date.now(),data:r},r}catch(n){return fn?fn.data:{configured:!0,repos:[],username:null,error:n?.message||String(n)}}}function zf(){ji.clear(),fn=null}async function re({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 vs(r,{env:n});return o?{GH_TOKEN:o}:{}}catch{return{}}}async function Fi({env:t=process.env}={}){return(await zr({env:t})).username??null}var Ix=()=>kx(new URL("../bin/bostrat-git-credential",import.meta.url)),Rx=(t=process.env)=>`http://127.0.0.1:${Number(t.PORT)||3362}`;function $x(t){let[e,n]=String(t).split("/");return Jr.join(Ax,e,n)}async function Ui(t,{env:e=process.env}={}){let n=(...r)=>Gf("git",["-C",t,...r],{timeout:1e4});await n("config","--replace-all","credential.helper",""),await n("config","--add","credential.helper",`!"${Ix()}" ${Rx(e)}`),await n("config","credential.useHttpPath","true")}async function Hi(t,{env:e=process.env,baseUrl:n="https://github.com"}={}){if(!Kf.test(String(t||"")))throw Zn(`bad repo slug "${t}"`,400);let r=$x(t);if(yx.existsSync(Jr.join(r,".git")))return r;let{token:o}=await vs(t,{env:e});await qf.mkdir(Jr.dirname(r),{recursive:!0});let s="!f() { echo username=x-access-token; echo password=$BOSTRAT_GIT_TOKEN; }; f";try{await Gf("git",["-c","credential.helper=","-c",`credential.helper=${s}`,"clone","--no-tags","-q",`${n}/${t}.git`,r],{timeout:Ex,env:{...e,BOSTRAT_GIT_TOKEN:o,GIT_TERMINAL_PROMPT:"0"}})}catch(a){throw await qf.rm(r,{recursive:!0,force:!0}).catch(()=>{}),Zn(`clone of ${t} failed: ${a?.stderr?.trim?.()||a?.message||a}`,502)}return await Ui(r,{env:e}),r}import l0 from"node:fs/promises";function Ox(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===`
|
|
51
|
+
`){e.push(n),n="";continue}n+=s}return e.push(n),e.map(o=>o.trim()).filter(Boolean)}function Xr(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 Yr(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 tr=(t,e)=>t.length>e?`${t.slice(0,e).trimEnd()}\u2026`:t,St=t=>t&&!/[$`]/.test(t)?t:null,dt=t=>t.replace(/\/+$/,"").split("/").pop()||t,Qn=t=>{let e=t.replace(/\/+$/,"").split("/").filter(Boolean);return e.length>3?e.slice(-2).join("/"):t},Zf=t=>t.startsWith("-"),Xf=new Set(["-g","--glob","-t","--type","-A","-B","-C","-m","--max-count","-e","--regexp","-f","--file"]);function tt(t,e=new Set){let n=[];for(let r=1;r<t.length;r++){let o=t[r];if(o==="<<")break;if(Zf(o)){e.has(o)&&r++;continue}n.push(o)}return n}var Cx={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 ${dt(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"},Mx={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 Qf(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 Px(t){let e=Qf(t),n=St(tt(t).find(r=>!/^\d+(,\d+)?p$/.test(r)));return e&&n?e.from===e.to?`Read line ${e.from} of ${dt(n)}`:`Read lines ${e.from}\u2013${e.to} of ${dt(n)}`:t.includes("-i")&&n?`Edit ${dt(n)}`:null}function Wi(t){if(t.includes("--files")){let s=St(tt(t,Xf)[0]);return s&&s!=="."?`List files in ${Qn(s)}`:"List files"}let e=tt(t,Xf),n=e[0];if(!n)return null;let r=St(e.slice(1).find(s=>s!==".")),o=tr(n,40);return r?`Search for \u201C${o}\u201D in ${Qn(r)}`:`Search for \u201C${o}\u201D`}function qi(t){let e=tt(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 Gi(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=tt(t)[0];return n?`Run ${dt(n)}`:null}function Nx(t){let e=tt(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 Yf(t){return t.find(e=>/^https?:\/\//.test(e))||null}var Bx={git:t=>{let e=tt(t,new Set(["-C","-c"])),n=Cx[e[0]];return n?n(e,t):null},gh:t=>{let e=tt(t),n=Mx[e[0]];return n?n(e):null},sed:Px,rg:Wi,grep:Wi,egrep:Wi,nl:t=>{let e=St(tt(t)[0]);return e?`Read ${dt(e)}`:null},cat:t=>{let e=St(tt(t)[0]);return e?`Read ${dt(e)}`:null},head:t=>{let e=St(tt(t,new Set(["-n","-c"])).find(n=>!/^\d+$/.test(n)));return e?`Read the start of ${dt(e)}`:null},tail:t=>{let e=St(tt(t,new Set(["-n","-c"])).find(n=>!/^\d+$/.test(n)));return e?`Read the end of ${dt(e)}`:null},wc:t=>{let e=St(tt(t)[0]);return e?`Count lines in ${dt(e)}`:"Count input lines"},ls:t=>{let e=St(tt(t)[0]);return e&&e!=="."?`List files in ${Qn(e)}`:"List files"},find:t=>St(t[1])&&!Zf(t[1])&&t[1]!=="."?`Find files in ${Qn(t[1])}`:"Find files",tree:()=>"List the directory tree",mkdir:t=>{let e=St(tt(t)[0]);return e?`Create directory ${Qn(e)}`:null},touch:t=>{let e=St(tt(t)[0]);return e?`Create ${dt(e)}`:null},rm:t=>{let e=St(tt(t)[0]);return e?`Delete ${Qn(e)}`:null},mv:t=>{let e=St(tt(t)[0]);return e?`Move ${dt(e)}`:null},cp:t=>{let e=St(tt(t)[0]);return e?`Copy ${dt(e)}`:null},chmod:t=>{let e=St(tt(t)[1]);return e?`Change permissions on ${dt(e)}`:null},diff:t=>{let e=tt(t);return e.length>=2?`Compare ${dt(e[0])} and ${dt(e[1])}`:null},curl:t=>{let e=Yf(t);return e?`Fetch ${tr(e,56)}`:"Fetch a URL"},wget:t=>{let e=Yf(t);return e?`Fetch ${tr(e,56)}`:"Fetch a URL"},npm:qi,pnpm:qi,yarn:qi,npx:t=>{let e=tt(t)[0];return e?`Run ${e}`:null},node:t=>Gi(t,"Node"),python:t=>Gi(t,"Python"),python3:t=>Gi(t,"Python"),make:t=>{let e=tt(t)[0];return e?`Run make ${e}`:"Run make"},systemctl:Nx,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=tt(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=tt(t)[0];return e?`Locate ${e}`:null},command:t=>t[1]==="-v"&&t[2]?`Locate ${t[2]}`:null,fly:t=>{let e=tt(t)[0];return e==="deploy"?"Deploy to Fly":e?`Run fly ${e}`:null}},Dx=new Set(["pwd","echo","cd","sleep","true","false","test","[","set","export","exit","read"]);function jx(t){let e=Yr(Xr(t));if(!e.length)return null;if(/^(ba|z)?sh$/.test(dt(e[0]))&&(e[1]==="-lc"||e[1]==="-c")&&e[2])return Ki(e[2]);let n=dt(e[0]);if(Dx.has(n))return null;if(e.includes("--version"))return`Check ${n} version`;if(n.endsWith(".sh"))return`Run ${n}`;let r=Bx[n];return r?r(e.map((o,s)=>s===0?n:o)):null}function Ki(t){let e=String(t||"").trim();if(!e)return null;let n=e.indexOf("<<");if(n!==-1){let i=e.indexOf(`
|
|
52
|
+
`,n);i!==-1&&(e=e.slice(0,i))}let r=Ox(e);for(let i=0;i<r.length-1;i++){let c=Yr(Xr(r[i])),l=Yr(Xr(r[i+1]));if(!(!c.length||!l.length)&&["nl","cat"].includes(dt(c[0]))&&dt(l[0])==="sed"){let u=Qf(l),d=tt(c)[0];if(u&&d)return tr(u.from===u.to?`Read line ${u.from} of ${dt(d)}`:`Read lines ${u.from}\u2013${u.to} of ${dt(d)}`,88)}}let o=[];for(let i of r){let c=jx(i);c&&o.push({d:c,head:dt(Yr(Xr(i))[0]||""),sub:tt(Yr(Xr(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":tr(s?s.d:o[0].d,88)}var Ji=new Set(["Bash"]);function er(t){let e=t.split(`
|
|
53
|
+
`).map(n=>n.trim()).find(Boolean)||t;return e.length>88?`${e.slice(0,88).trimEnd()}\u2026`:e}function Ne(t,e=""){let n=e.trim();return n||Ki(t)||er(t)}function Vi(t,e){if(t){if(Array.isArray(t)){for(let n of t)Vi(n,e);return}if(typeof t=="object"){if(t.type==="tool_use"&&Ji.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))Vi(n,e)}}}async function tp(t,{maxBytes:e=1048576,limit:n=400}={}){let r;try{r=await Et(t,e)}catch{return[]}let o=[];for(let s of r.split(`
|
|
54
|
+
`)){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(Vi(c.content,l),!l.length)continue;let u=i.timestamp?Date.parse(i.timestamp):null;for(let d of l)o.push({...d,summary:Ne(d.command,d.description),ts:u})}return o.slice(-n)}function ep(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,h)=>h>=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 p=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:p,ts:Math.max(...p.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 Lx=/^Async (?:agent|task) launched/i,Fx=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 np(t,{maxBytes:e=2097152,limit:n=100}={}){let r;try{r=await Et(t,e)}catch{return[]}let o=new Map,s=new Set;for(let i of r.split(`
|
|
55
|
+
`)){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 p=f.input||{};o.set(f.id,{id:f.id,kind:"agent",name:String(p.description||"").trim()||null,detail:String(p.subagent_type||"").trim()||null,ts:d,proven:!1})}else if(f.name==="Bash"&&f.input?.run_in_background===!0){let p=String(f.input.command||"").trim();o.set(f.id,{id:f.id,kind:"command",name:String(f.input.description||"").trim()||(p?er(p):null),detail:p?er(p):null,ts:d,proven:!0})}}else f.type==="tool_result"&&f.tool_use_id&&Lx.test(Fx(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 Zr from"node:fs";import Hx from"node:fs/promises";import pn from"node:path";import Wx from"node:os";import qx from"node:crypto";import Gx from"node:zlib";var As=new Set(["Shell","Read","Grep","Glob","StrReplace","Write","ReadLints","Delete","ApplyPatch","Await"]),Gt=(t,e=88)=>t.length>e?`${t.slice(0,e).trimEnd()}\u2026`:t,nr=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 zi=t=>`"${String(t).replace(/"/g,'\\"')}"`;function Ux(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 rp(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:Gt(`read ${Be(r,n)}${o?` (lines ${o}\u2013${s??"end"})`:""}`,140),description:o?`Read lines ${o}\u2013${s??"end"} of ${nr(r)}`:`Read ${nr(r)}`}}case"Grep":{let r=e?.pattern;if(!r)return null;let o=e?.path?Be(e.path,n):null,s=e?.glob?` --glob ${zi(e.glob)}`:"",a=Gt(String(r),40);return{command:Gt(`grep ${zi(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:Gt(`glob ${zi(r)}${o&&o!=="."?` ${o}`:""}`,140),description:`Find files matching \u201C${Gt(String(r),40)}\u201D`}}case"StrReplace":return e?.path?{command:Gt(`edit ${Be(e.path,n)}`,140),description:`Edit ${nr(e.path)}`}:null;case"Write":return e?.path?{command:Gt(`write ${Be(e.path,n)}`,140),description:`Write ${nr(e.path)}`}:null;case"Delete":return e?.path?{command:Gt(`delete ${Be(e.path,n)}`,140),description:`Delete ${nr(e.path)}`}:null;case"ReadLints":{let o=(Array.isArray(e?.paths)?e.paths:[]).map(s=>Be(s,n)).filter(Boolean);return{command:Gt(o.length?`lints ${o.join(" ")}`:"lints",140),description:"Check lints"}}case"ApplyPatch":{let r=Ux(e);if(!r.length)return null;let o=r.length>1?` (+${r.length-1} more)`:"";return{command:Gt(`apply_patch ${Be(r[0],n)}${o}`,140),description:r.length>1?`Patch ${r.length} files`:`Patch ${nr(r[0])}`}}case"Await":{let r=e?.shell_id??e?.task_id;return{command:Gt(`await ${r??"background output"}`,140),description:"Wait for a background command"}}default:return null}}var Kx=process.env.BOSTRAT_CURSOR_CHATS||pn.join(Wx.homedir(),".cursor","chats");function ap(t){let e=qx.createHash("md5").update(String(t)).digest("hex");return pn.join(Kx,e)}function ip(t){try{let e=JSON.parse(Zr.readFileSync(pn.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 Vx(t){let e=ap(t),n;try{n=Zr.readdirSync(e,{withFileTypes:!0}).filter(o=>o.isDirectory())}catch{return null}let r=null;for(let o of n){let s=pn.join(e,o.name),a=pn.join(s,"store.db");if(!Zr.existsSync(a))continue;let i=ip(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 Ts(t,e){if(!t)return null;if(e){let n=pn.join(ap(t),e),r=pn.join(n,"store.db");if(Zr.existsSync(r))return{id:e,dir:n,store:r,...ip(n)||{createdAtMs:null,updatedAtMs:null}}}return Vx(t)}function op(t,e){let n=t.prepare("SELECT data FROM blobs WHERE id = ?").get(e);return n?Buffer.from(n.data):null}function Jx(t){if(!t)return null;let e=t;if(e.length>1&&e[0]===31&&e[1]===139)try{e=Gx.gunzipSync(e)}catch{}try{return JSON.parse(e.toString("utf8"))}catch{return null}}function zx(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 Xx(t,e){if(!Array.isArray(t))return[];let n=[];for(let r of t){if(!r||r.type!=="tool-call"||!As.has(r.toolName))continue;let o=rp(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 Yx=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(`
|
|
56
|
+
`).trim():"",Zx=t=>Array.isArray(t)?t.filter(e=>e&&e.type==="tool-call"&&e.toolName).map(e=>e.toolName):[],sp=process.getBuiltinModule?.("node:sqlite"),rr={turns:[],trailingCommands:[],model:null,requestIds:[]};async function Xi(t,e){if(!sp)return rr;let{DatabaseSync:n}=sp,r;try{r=new n(t,{readOnly:!0})}catch{return rr}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 rr;let s;try{s=JSON.parse(Buffer.from(String(o.value),"hex").toString("utf8"))}catch{return rr}let a=s.lastUsedModel||null,i=s.latestRootBlobId?op(r,s.latestRootBlobId):null;if(!i)return{...rr,model:a};let c=[],l=[],u=[],d=null,f=[];for(let p of zx(i)){let m=Jx(op(r,p));if(!m)continue;let h=m.providerOptions?.cursor;if(h?.requestId&&f.push(h.requestId),h?.modelProviderMessageId&&f.push(h.modelProviderMessageId),m.role!=="assistant")continue;if(Array.isArray(m.content))for(let v of m.content){let E=v?.providerOptions?.cursor?.modelName;E&&(d=E)}l.push(...Xx(m.content,e).map((v,E)=>({...v,id:v.id||`${p}:${E}`}))),u.push(...Zx(m.content));let b=Yx(m.content);if(!b)continue;let x=[...new Set(u)];u=[],c.push({id:p,text:b,tools:x.length?x:void 0,commands:l,model:d}),l=[]}return{turns:c,trailingCommands:l,model:a,requestIds:f}}catch{return rr}finally{try{r.close()}catch{}}}function cp(t,e){let n=t.updatedAtMs;if(!n)try{n=Math.round(Zr.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 lp(t,e,{limit:n=80}={}){let r=Ts(t,e);if(!r)return[];let{turns:o,model:s}=await Xi(r.store,t);if(!o.length)return[];let a=o.slice(-n),{tsAt:i}=cp(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 up(t,e){let n=Ts(t,e);if(!n)return null;let{turns:r,requestIds:o,model:s}=await Xi(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 dp(t,e,{limit:n=80}={}){let r=Ts(t,e);if(!r)return[];let{turns:o,trailingCommands:s}=await Xi(r.store,t);if(!o.length&&!s.length)return[];let a=o.slice(-n),{end:i,tsAt:c}=cp(r,a.length||1),l=[];a.forEach((u,d)=>{for(let f of u.commands)l.push({...f,summary:Ne(f.command,f.description),ts:c(d)})});for(let u of s)l.push({...u,summary:Ne(u.command,u.description),ts:i+1});return l}async function fp(t,e){let n=Ts(t,e);if(!n)return null;if(n.updatedAtMs)return n.updatedAtMs;try{return Math.round((await Hx.stat(n.store)).mtimeMs)}catch{return null}}import pp from"node:fs";import Zi from"node:fs/promises";import Yi from"node:path";import Qx from"node:os";var t0=process.env.BOSTRAT_CODEX_SESSIONS||Yi.join(process.env.CODEX_HOME||Yi.join(Qx.homedir(),".codex"),"sessions");async function mp(t){try{return await Zi.realpath(t)}catch{return t}}function e0(t=t0){let e=[],n=r=>{let o;try{o=pp.readdirSync(r,{withFileTypes:!0})}catch{return}for(let s of o){let a=Yi.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:pp.statSync(a).mtimeMs})}catch{}}}};return n(t),e.sort((r,o)=>o.mtimeMs-r.mtimeMs)}async function n0(t,e=1048576){let n;try{n=await Zi.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(`
|
|
57
|
+
`);if(i!==-1)return o.slice(0,i);if(s+=a,o.length>=e)break}return o}finally{await n.close()}}async function r0(t){let e=await n0(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 mn(t,e){if(!t&&!e)return null;let n=e0();if(e){let o=n.find(s=>s.id===e);return o||null}let r=await mp(t);for(let o of n){let s=await r0(o.file);if(s&&(s===t||await mp(s)===r))return o}return null}var o0=new Set(["output_text","text"]),s0=t=>typeof t=="string"?t.trim():Array.isArray(t)?t.filter(e=>e&&typeof e.text=="string"&&o0.has(e.type)).map(e=>e.text).join(`
|
|
58
|
+
`).trim():"";async function hp(t,e,{limit:n=80,maxBytes:r=524288}={}){let o=await mn(t,e);if(!o)return[];let s;try{s=await Et(o.file,r)}catch{return[]}let a=[],i=null,c=[],l=!1,u=!1,d=null;for(let f of s.split(`
|
|
59
|
+
`)){let p=f.trim();if(!p||p[0]!=="{")continue;let m;try{m=JSON.parse(p)}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 v=m.payload?.type;v==="task_started"?(l=!0,u=!1,d=m.payload?.turn_id||null):v==="task_complete"&&l&&(l=!1,!u&&m.payload?.last_agent_message==null&&a.push({role:"assistant",failed:!0,text:tc,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 h=m.payload;if(!h)continue;if((h.type==="function_call"||h.type==="custom_tool_call")&&h.name){u=!0,Qi.has(h.name)||c.push(h.name);continue}if(h.type!=="message"||h.role!=="assistant")continue;u=!0;let b=s0(h.content),x=c.length?c.slice():null;c=[],!(!b&&!x)&&a.push({role:"assistant",text:b,tools:x||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 Qr=new Set(["exec_command","shell","exec"]),Qi=new Set(["wait","write_stdin"]),tc="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 a0(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 i0(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+=`
|
|
60
|
+
`;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 c0(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=i0(i.slice(c.index+c[0].length).trimStart());l&&l.trim()&&o.push(l.trim())}),o}async function wp(t,e,{limit:n=400,maxBytes:r=524288}={}){let o=await mn(t,e);if(!o)return[];let s;try{s=await Et(o.file,r)}catch{return[]}let a=[];for(let i of s.split(`
|
|
61
|
+
`)){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"&&Qr.has(u.name)){let p;try{p=JSON.parse(u.arguments||"{}")}catch{continue}let m=a0(p);m&&(d=[m])}else u.type==="custom_tool_call"&&u.name==="exec"&&(d=c0(u.input));if(!d.length)continue;let f=u.call_id||u.id||null;d.forEach((p,m)=>{a.push({id:f?m?`${f}:${m}`:f:null,tool:u.name,command:p,description:"",summary:Ne(p),ts:l.timestamp?Date.parse(l.timestamp):null})})}return a.slice(-n)}async function gp(t,e){let n=await mn(t,e);if(!n)return null;try{return Math.round((await Zi.stat(n.file)).mtimeMs)}catch{return n.mtimeMs||null}}var or=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}},ec=class extends or{transcript(e){return!e?.cwd||!e?.sessionId?null:ce(e.cwd,e.sessionId)}async readMessages(e,n){let r=this.transcript(e);return r?Da(r,n):[]}async readCommands(e,n){let r=this.transcript(e);return r?tp(r,n):[]}async readJobs(e,n){let r=this.transcript(e);return r?np(r,n):[]}async updatedAt(e){let n=this.transcript(e);if(!n)return null;try{return Math.round((await l0.stat(n)).mtimeMs)}catch{return null}}},nc=class extends or{async readMessages(e,n){return lp(e.cwd,e.sessionId,n)}async readCommands(e,n){return dp(e.cwd,e.sessionId,n)}async updatedAt(e){return fp(e.cwd,e.sessionId)}},rc=class extends or{sessionId(e){return e?.pinned?e.sessionId:null}async readMessages(e,n){return hp(e.cwd,this.sessionId(e),n)}async readCommands(e,n){return wp(e.cwd,this.sessionId(e),n)}async updatedAt(e){return gp(e.cwd,this.sessionId(e))}},u0=new or("unknown"),yp=new Map;function oc(t){return yp.set(t.agent,t),t}function to(t){return yp.get(t)||u0}oc(new ec("claude",Ji));oc(new nc("cursor",As));oc(new rc("codex",Qr));var sc=t=>e=>({...e,box:t.name,agent:t.agent,boxRole:t.role});async function bp(t,e={}){return(await to(t.agent).readMessages(t,e)).map(sc(t))}async function kp(t,e={}){return(await to(t.agent).readCommands(t,e)).map(sc(t))}async function xp(t,e={}){return(await to(t.agent).readJobs(t,e)).map(sc(t))}var Ap=/to navigate\b.*\bEsc to cancel|Enter to select\b/,cc=/Chat about this/i,hn=/^\s*(❯)?\s*(\d+)\.\s+(?:\[([ xX✔])\]\s+)?(.+?)\s*$/,Sp=/^(type something|chat about this|submit)\b/i,wn=/^[─—-]{6,}\s*$/,lc=/[\u2500-\u257F\u2550-\u256F┌┐└┘├┤│┬┴┼─✂]/;function gn(t){let e=t.search(lc);return(e>=0?t.slice(0,e):t).trim()}function d0(t){if(!t)return"";let e=t.replace(/[│┌└├─┘┤┬┴┼─✂].*$/,"").trim();return!e||/hidden|\d+\s+lines?/i.test(e)?"":e}function f0(t){let e=String(t).trim();return!!(!e||wn.test(t)||lc.test(e)||/hidden|\d+\s+lines?/i.test(e)||/notespress|press n to add/i.test(e)||/^\s*notes?\s*$/i.test(e))}function p0(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=m0(n,e);return n.forEach((a,i)=>{a.current=i===s}),{tabs:n,questionIndex:s,questionTotal:n.length,currentTabLabel:n[s].label}}function m0(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 vp(t){return t.replace(/[←→✔☐☒●▶]/g," ").replace(/\bSubmit\b/g," ").replace(/\s+/g," ").trim()}var Tp=/^\s*Switch model\?\s*$/m;function h0(t,e=""){let n=String(t);if(!Tp.test(n))return null;let r=n.replace(/\r/g,"").split(`
|
|
62
|
+
`),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(hn);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&&!wn.test(r[u])&&a.push(f)}}if(s.length<2)return null;let i=s.map(u=>({label:gn(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 _p=/^\s*Enter to confirm\s*·\s*Esc to cancel\s*$/,w0=/^\s*[─—▔-]{6,}\s*$/;function g0(t,e=""){let n=String(t).replace(/\r/g,"").split(`
|
|
63
|
+
`),r=n.length-1;for(;r>=0&&n[r].trim()==="";)r--;if(r<0||!_p.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(hn);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&&!(w0.test(n[c])||wn.test(n[c]));c--){let l=n[c].trim();l&&a.unshift(l)}let i=s.map(c=>({label:gn(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 y0(t,e=""){let n=String(t);if(!/ready to submit your answers/i.test(n))return null;let r=n.replace(/\r/g,"").split(`
|
|
64
|
+
`),o=[];for(let c=0;c<r.length;c++){let l=r[c].match(hn);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 ac=/Resume from summary/i,b0=/^\s*This session is\b/;function k0(t,e=""){let n=String(t);if(!ac.test(n))return null;let r=n.replace(/\r/g,"").split(`
|
|
65
|
+
`),o=-1;for(let u=r.length-1;u>=0;u--)if(b0.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(hn);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&&!wn.test(r[u])&&a.push(f)}}if(s.length<2||!s.some(u=>ac.test(u.label)))return null;let i=s.map(u=>({label:gn(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 yn=/shift\+tab to cycle|⏵⏵ bypass permissions on|⏸ manual mode on/;function uc(t){let e=[];for(let r=0;r<t.length;r++){let o=t[r].match(hn);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 Ep=/Choose the text style that looks best with your terminal/,Ip=/Let's get started\./;function x0(t,e=""){let n=String(t);if(!Ep.test(n)||!Ip.test(n)||yn.test(n))return null;let r=n.replace(/\r/g,"").split(`
|
|
66
|
+
`),o=uc(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:gn(s.label),description:""}))}]}:null}var Rp=/Select login method:/;function S0(t,e=""){let n=String(t);if(!Rp.test(n)||yn.test(n))return null;let r=n.replace(/\r/g,"").split(`
|
|
67
|
+
`),o=uc(r);if(!o)return null;let s=o.map(a=>{let[i,...c]=a.label.split("\xB7");return{label:gn(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 dc=/Paste code here if prompted/,v0=/^[A-Za-z0-9%&=+._~#?/:\-]+$/;function A0(t,e){let n=t[e].trim();for(let r=e+1;r<t.length;r++){let o=t[r].trim();if(!o||!v0.test(o)||dc.test(t[r]))break;n+=o}return n}function T0(t,e=""){let n=String(t);if(!dc.test(n)||yn.test(n))return null;let r=n.replace(/\r/g,"").split(`
|
|
68
|
+
`),o="";for(let s=0;s<r.length;s++)if(/^\s*https:\/\//.test(r[s])){o=A0(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 ic=/OAuth error:/,_0=/Press Enter to retry\./;function E0(t,e=""){let n=String(t);if(!ic.test(n)||!_0.test(n)||yn.test(n))return null;let s=([...n.replace(/\r/g,"").split(`
|
|
69
|
+
`)].reverse().find(a=>ic.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 $p=/Quick safety check: Is this a project you created or one you trust/;function I0(t,e=""){let n=String(t);if(!$p.test(n)||yn.test(n))return null;let r=n.replace(/\r/g,"").split(`
|
|
70
|
+
`),o=uc(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:gn(s.label),description:""}))}]}:null}var Op=/^(.{0,60}?)Press\s+Enter\s+to\s+continue\s*[.…]{0,3}\s*$/;function R0(t,e=""){let n=String(t);if(yn.test(n))return null;let r=n.replace(/\r/g,"").split(`
|
|
71
|
+
`),o=-1;for(let l=r.length-1;l>=0;l--)if(r[l].trim()!==""){o=l;break}if(o<0)return null;let s=r[o].match(Op);if(!s)return null;let a=[];for(let l=o-1;l>=0&&a.length<3;l--){let u=r[l].trim();if(!u){if(a.length)break;continue}if(wn.test(r[l])||lc.test(u))break;a.unshift(u)}let i=s[1].trim();i&&a.push(i);let c=a.join(" ")||"Claude Code is waiting for you to continue.";return{provider:"claude",box:e,multiSelect:!1,cursorIndex:0,setup:"continue",promptId:`claude:setup:continue:${c}`.slice(0,200),questions:[{question:c,header:"Continue",multiSelect:!1,options:[{label:"Continue",description:""}]}]}}function $0(t){let e=-1;for(let r=t.length-1;r>=0;r--)if(cc.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 O0(t,e=""){let n=String(t);if(!Ap.test(n)||!cc.test(n))return null;let r=n.replace(/\r/g,"").split(`
|
|
72
|
+
`),o=$0(r);if(!o)return null;let{headerLine:s,chatLine:a,optStart:i,optEnd:c}=o,l=[],u=-1;for(let T=i;T<c;T++){let U=r[T].match(hn);U&&(u<0&&(u=T),l.push({line:T,cursor:!!U[1],checked:U[3]!=null&&U[3]!==" ",hasCheckbox:U[3]!=null,label:U[4].trim(),desc:""}))}if(!l.length)return null;for(let T of l)for(let U=T.line+1;U<Math.min(r.length,T.line+4)&&!(hn.test(r[U])||wn.test(r[U]));U++){let J=r[U].trim();if(!(!J||f0(r[U])||Sp.test(J))){T.desc=d0(J);break}}let d=l.filter(T=>!Sp.test(T.label));if(!d.length)return null;let f=d.some(T=>T.hasCheckbox),p=Math.max(0,d.findIndex(T=>T.cursor)),m=s>=0?s+1:0,h=[];for(let T=m;T<u;T++){let U=r[T].trim();U&&!wn.test(r[T])&&!/[☐☒]/.test(r[T])&&h.push(U)}let b=h.join(" ")||(s>=0?vp(r[s]):"")||"The agent is asking a question",x=s>=0?p0(r[s],b):null,v=x?.currentTabLabel||(s>=0?vp(r[s]):""),E=d.map(T=>({label:gn(T.label),description:T.desc})),R=E.map(T=>T.label).join("|"),I=`claude:${x?`q${x.questionIndex+1}/${x.questionTotal}`:"q1/1"}:${v}:${b}:${R}`.slice(0,240),L=x?`claude:ask:${x.tabs.map(T=>T.label).join("|")}`.slice(0,240):I;return{provider:"claude",box:e,multiSelect:f,cursorIndex:p,promptId:I,groupId:L,...x?{questionIndex:x.questionIndex,questionTotal:x.questionTotal,currentTabLabel:x.currentTabLabel,tabs:x.tabs}:{},questions:[{question:b,header:v,multiSelect:f,options:E}]}}function sr(t,e){let n=String(e||"");return n?t==="claude"?Ap.test(n)&&cc.test(n)||Tp.test(n)||ac.test(n)||_p.test(n.trimEnd().split(`
|
|
73
|
+
`).pop()||"")?!0:yn.test(n)?!1:Ep.test(n)&&Ip.test(n)||Rp.test(n)||dc.test(n)||ic.test(n)||$p.test(n)||Op.test(n.trimEnd().split(`
|
|
74
|
+
`).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 bn(t,e){return!t||!e?null:t.agent==="claude"?y0(e,t.name)||O0(e,t.name)||h0(e,t.name)||k0(e,t.name)||E0(e,t.name)||T0(e,t.name)||S0(e,t.name)||x0(e,t.name)||I0(e,t.name)||R0(e,t.name)||g0(e,t.name):t.agent==="cursor"||t.agent==="codex"?Au(t.agent,e,t.name):null}async function fc(t){if(!t)return null;let e=await ve(t.name).catch(()=>"");return bn(t,e)}function Cp(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 Mp(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 ar(t){return typeof t=="string"?t.trim():""}var Pp=/^\s*─{8,}\s*$/,C0=/^❯[ ]?(.*)$/;function pc(t,e){if(t!=="claude")return null;let n=String(e).replace(/\r/g,"").split(`
|
|
75
|
+
`).map(c=>c.replace(/\s+$/,"")),r=-1;for(let c=n.length-1;c>=0;c--)if(Pp.test(n[c])){r=c;break}if(r<=0)return null;let o=-1;for(let c=r-1;c>=0;c--)if(Pp.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(C0);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 kn(t){return String(t||"").replace(/\s+/g," ").trim()}function Np(t,e){let n=kn(t),r=kn(e).replace(/^…\s*/,"").replace(/\s*…$/,"");return!n||!r||r.length<Math.min(n.length,12)?!1:n===r||n.includes(r)}import{execFile as M0}from"node:child_process";import{promisify as P0}from"node:util";var hc=P0(M0),Bp=64*1024*1024;async function ft(t,e,{input:n}={}){let r=hc("git",["-C",t,...e],{maxBuffer:Bp});return n!==void 0&&r.child.stdin&&(r.child.stdin.on("error",()=>{}),r.child.stdin.end(n)),r}async function _s(t){if(!t)return!1;try{let{stdout:e}=await ft(t,["rev-parse","--is-inside-work-tree"]);return e.trim()==="true"}catch{return!1}}async function Dp(t){let{stdout:e}=await ft(t,["rev-parse","--show-toplevel"]);return e.trim()}async function N0(t){try{return await ft(t,["rev-parse","--verify","HEAD"]),!0}catch{return!1}}async function wc(t,e="bostrat: subtask snapshot"){let{stdout:n}=await ft(t,["rev-parse","--absolute-git-dir"]),r=`${n.trim()}/bostrat-snapshot-index`,o={...process.env,GIT_INDEX_FILE:r},s=(a,i)=>hc("git",["-C",t,...a],{maxBuffer:Bp,env:o,...i});try{let a=await N0(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 hc("rm",["-f",r])}catch{}}}async function gc(t,e){let{stdout:n}=await ft(t,["status","--porcelain"]);return n.trim()?(await ft(t,["add","-A"]),await ft(t,["-c","user.email=agent@bostrat.local","-c","user.name=bostrat","commit","-m",e]),!0):!1}async function yc(t,{ignorePlumbing:e=!1}={}){let{stdout:n}=await ft(t,["status","--porcelain"]),r=n.split(`
|
|
76
|
+
`).filter(Boolean);return e&&(r=r.filter(o=>{let s=o.slice(3);return!(s===".bostrat"||s===".bostrat/"||s.startsWith(".bostrat/")||o.startsWith("??")&&(s==="AGENTS.md"||s==="CLAUDE.md"))})),r.length>0}async function bc(t,e){if(!e)return 0;try{let{stdout:n}=await ft(t,["rev-list","--count",`${e}..HEAD`]);return Number(n.trim())||0}catch{return 0}}async function ir(t,e,n,r){return await ft(t,["worktree","add","--no-track","-b",n,e,r]),e}async function jp(t,e){try{return await ft(t,["rev-parse","--verify","--quiet",`refs/heads/${e}`]),!0}catch{return!1}}async function Lp(t,e,n){return await ft(t,["worktree","add",e,n]),e}async function Es(t){if(process.env.BOSTRAT_TRACK_BASE_REF)return process.env.BOSTRAT_TRACK_BASE_REF;try{let{stdout:e}=await ft(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 ft(t,["rev-parse","--verify","--quiet",e]),n}catch{}return"HEAD"}async function Fp(t){await ft(t,["init","-q"]),await ft(t,["-c","user.email=agent@bostrat.local","-c","user.name=bostrat","commit","--allow-empty","-qm","bostrat: track workspace"])}async function Ft(t,e){try{await ft(t,["worktree","remove","--force",e])}catch{try{await ft(t,["worktree","prune"])}catch{}}}function B0(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 kc(t,e){return!e||!e.length?t:[...t,"--",".",...e.map(n=>`:(exclude)${n}`)]}async function xc(t,e,n,r){let{stdout:o}=await ft(t,kc(["diff","--shortstat",e,n],r));return B0(o)}async function Sc(t,e,n,r){let{stdout:o}=await ft(t,kc(["diff","--name-only",e,n],r));return o.split(`
|
|
77
|
+
`).map(s=>s.trim()).filter(Boolean)}async function vc(t,e,n,r){let{stdout:o}=await ft(t,kc(["diff","--binary",e,n],r));if(!o.trim())return{applied:!0,empty:!0,conflicts:[]};if((await mc(t,o,["--whitespace=nowarn"])).ok)return{applied:!0,empty:!1,conflicts:[]};let s=await mc(t,o,["--3way","--whitespace=nowarn"]),a=await D0(t);return a.length>0?{applied:!1,empty:!1,conflicts:a}:s.ok?{applied:!0,empty:!1,conflicts:[]}:(await mc(t,o,["--reject","--whitespace=nowarn"]),{applied:!1,empty:!1,conflicts:[`patch did not apply cleanly: ${s.err}`]})}async function mc(t,e,n){try{return await ft(t,["apply",...n,"-"],{input:e}),{ok:!0}}catch(r){return{ok:!1,err:r?.stderr?.trim()||r?.message||String(r)}}}async function D0(t){try{let{stdout:e}=await ft(t,["diff","--name-only","--diff-filter=U"]);return e.split(`
|
|
78
|
+
`).map(n=>n.trim()).filter(Boolean)}catch{return[]}}import j0 from"node:fs/promises";import L0 from"node:os";import Up from"node:path";var Is=t=>new Promise(e=>setTimeout(e,t)),F0=Number(process.env.BOSTRAT_MODEL_PICKER_TIMEOUT_MS)||8e3,U0=300,Gp=[{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"}],H0=new Set([...Gp.map(t=>t.alias),"best","opus[1m]","sonnet[1m]","claude-opus-4-8","claude-sonnet-4-6","claude-haiku-4-5","claude-fable-5"]),W0=[{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"}],Kp=new Map([["low","Low"],["medium","Medium"],["high","High"],["xhigh","Extra high"]]),Vp=[{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 Jp(t){return t==="codex"?W0:t==="cursor"?Vp:Gp}function Hp(t){let e=String(t||"").split(`
|
|
79
|
+
`),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 Wp(t){let e=String(t||"").split(`
|
|
80
|
+
`),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 q0(t){let e=String(t||"").split(`
|
|
81
|
+
`);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 qp(t){let e=String(t||"").split(`
|
|
82
|
+
`),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 zp(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&&!Kp.has(o))throw new Error(`unknown codex effort "${o}" (low | medium | high | xhigh)`);return{model:r,effort:o||null}}if(t==="cursor"){let r=Vp.find(o=>o.alias===n);return{model:n,filter:r?r.label:n}}if(!H0.has(n))throw new Error(`unknown model "${n}"`);return{model:n}}async function eo(t,e,{timeoutMs:n=F0}={}){let r=Date.now()+n;for(;;){let o=await wt(t,{lines:80}).catch(()=>""),s=e(o);if(s)return s;if(Date.now()>r)return null;await Is(U0)}}async function G0(t,e){return await kt(t,`/model ${e.model}`,le("claude")),{verified:!1,note:"confirmed when the next turn's transcript model flips"}}async function K0(t,e){await Bo(t),await kt(t,"/model",{enter:!1}),await Is(250),await bt(t,["Enter"],{kind:"model-switch"});let n=await eo(t,Hp);if(!n)throw new Error("codex model picker didn't open");let r=n.find(i=>i.id===e.model);if(!r)throw await bt(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=q0(i);return!c||c.model!==e.model||e.effort&&c.effort&&c.effort.toLowerCase()!==e.effort.toLowerCase()?null:c};await kt(t,String(r.n),{enter:!1,kind:"model-switch"});let s=await eo(t,i=>Wp(i)||!Hp(i)&&o(i)&&{done:o(i)});if(!s)throw await bt(t,["Escape"],{kind:"model-switch"}).catch(()=>{}),new Error("codex reasoning-level picker didn't open");if(!s.done){let i=e.effort?Kp.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 bt(t,["Escape","Escape"],{kind:"model-switch"}).catch(()=>{}),new Error(`codex doesn't offer effort "${e.effort}" for ${e.model}`);await kt(t,String(c.n),{enter:!1,kind:"model-switch"})}let a=s.done||await eo(t,i=>!Wp(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 V0(){let t=Up.join(process.env.CURSOR_CONFIG_DIR||Up.join(L0.homedir(),".cursor"),"cli-config.json");try{return JSON.parse(await j0.readFile(t,"utf8"))?.model||null}catch{return null}}async function J0(t,e){await Bo(t),await jo(t,`/model ${e.filter}`,{kind:"model-switch"});let n=await eo(t,qp),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 eo(t,qp,{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 bt(t,["Down"],{kind:"model-switch"}),await Is(250)}if(!n||!s||!o(s)){await bt(t,["Escape"],{kind:"model-switch"}).catch(()=>{});let c=Array(`/model ${e.filter}`.length).fill("BSpace");throw await bt(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 bt(t,["Enter"],{kind:"model-switch"}),await Is(800);let a=await V0();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 Xp(t,e,n){return t==="codex"?K0(e,n):t==="cursor"?J0(e,n):G0(e,n)}var Nt=t=>t||null;function $s(t){let e=t||{},n=z0(e);return n&&(n.payload={ts:e.at,...n.payload}),n}function z0(t){switch(t.type){case"said":return{type:"turn.user",actor:"user",source:"orchestrator",boxId:t.box,provider:Nt(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:Nt(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:Nt(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:Nt(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:Nt(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:Nt(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:Nt(t.agent),payload:{...cr(t)}};case"subtask-failed":return{type:"error",actor:"system",source:"orchestrator",boxId:t.box||t.to,provider:Nt(t.agent||t.toAgent),payload:{kind:"subtask-failed",...cr(t)}};case"subtask-resumed":return{type:"box.state.changed",actor:"orchestrator",source:"orchestrator",boxId:t.box,provider:Nt(t.agent),payload:{change:"subtask-resumed",...cr(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",...cr(t)}};case"archived":case"unarchived":case"track-deleted":return{type:"box.state.changed",actor:"user",source:"orchestrator",payload:{change:t.type,...cr(t)}};case"rehydrated":return{type:"box.state.changed",actor:"orchestrator",source:"orchestrator",boxId:t.box,provider:Nt(t.agent),payload:{change:"rehydrated"}};case"retagged":return{type:"box.state.changed",actor:"user",source:"orchestrator",payload:{change:"retagged",...cr(t)}};case"rehydrate-failed":return{type:"error",actor:"system",source:"orchestrator",boxId:t.box,provider:Nt(t.agent),payload:{kind:"rehydrate-failed",reason:t.reason}};default:return null}}var Rs=Promise.resolve(),Tc=t=>(Rs=Rs.then(t).catch(()=>{}),Rs),Zp=()=>Rs;function Qp(t,e){let n=$s(e);if(!n)return;let r=e?.type==="track-deleted"?{id:t.id}:t;Tc(()=>O(r,n))}var Yp=new Set;async function tm(t){if(!(!t?.id||Yp.has(t.id))){Yp.add(t.id);try{let e=await F(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=$s(r);!o||n.has(`${o.type}|${o.payload?.ts??"?"}`)||await O(t,o)}}catch{}}}function cr(t){let{type:e,at:n,...r}=t;return r}var Ac=new Map;function em(t,e,n){!n?.promptId||Ac.get(t.id)===n.promptId||(Ac.set(t.id,n.promptId),Tc(()=>O(t,{type:"prompt.raised",actor:"agent",source:"pane",boxId:e?.name,provider:Nt(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 nm(t,e,n,r={}){Ac.delete(t.id),Tc(()=>O(t,{type:"prompt.answered",actor:"user",source:"orchestrator",boxId:e?.name,provider:Nt(n?.provider||e?.agent),payload:{promptId:n?.promptId,...r}}))}import{randomUUID as ES}from"node:crypto";var om=/(?<!\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,sm=/(?<!\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,am=/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,im=/(?<!\b(?:a|an|the|this|that|another|one|each|any|no|clean|interactive)\s)rebase(?!s\b)(?:\s+(?:it|this|that|them|(?:this|that|the)\s+branch))?(?:\s+(?:on(?:to)?|against|atop|over)\s+\S+)?/.source,X0=new RegExp(`\\b(?:${om}|${sm}|${am}|${im})\\b`,"i"),Y0=/\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,Z0=/^(?:commit|push|open|create|raise|rebase|ready|ok(?:ay)?\s+to|good\s+to)\b/i,Q0=90;function tS(t){return String(t).split(/(?<=[.?!])\s+|\n+/).map(e=>e.trim()).filter(Boolean)}function cm(t){let e=String(t||"").trim();if(!e)return null;let n=e.slice(-600);for(let r of tS(n).reverse()){if(!X0.test(r))continue;let o=r.endsWith("?")&&r.length<=Q0&&Z0.test(r);if(Y0.test(r)||o)return r.length>200?r.slice(0,197)+"\u2026":r}return null}var lm=["commit","rebase","push","pr"],eS=new RegExp(`\\b(?:${om})\\b`,"i"),nS=new RegExp(`\\b(?:${im})\\b`,"i"),rS=new RegExp(`\\b(?:${sm})\\b`,"i"),oS=new RegExp(`\\b(?:${am})\\b`,"i"),sS=/\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 um(t){let e=String(t||""),n=[];eS.test(e)&&n.push("commit"),nS.test(e)&&n.push("rebase"),rS.test(e)&&n.push("push");let r=sS.test(e);!r&&oS.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 Os(t,{branch:e=null,base:n=null,toPr:r=null,repos:o=null,offeredRebase:s=!1}={}){Array.isArray(t)||(t=t==="push"?["commit","push"]:["commit","push","pr"]);let a=new Set(t),i=Array.isArray(o)&&o.length>0,c=i?o.map(p=>`${p.name}/`).join(", "):null,l=e?` on this branch (${e})`:"",u=[];if(a.has("commit")&&u.push(i?`commit the work in each changed checkout (${c})${l} with a clear commit message per repo`:`commit the work${l} with a clear commit message`),a.has("rebase")){let p=a.has("commit")?"the branch":`this branch${e?` (${e})`:""}`;u.push(i?`rebase ${p} in each changed checkout (${c}) onto its own base branch (fetch origin first), resolving any conflicts so both sides' intent is preserved`:`rebase ${p} onto ${n?`the latest ${n}`:"its base branch"} (fetch origin first), resolving any conflicts so both sides' intent is preserved`)}if(a.has("push")||a.has("pr")){let p=r&&!a.has("pr")?` (the open PR${r.number?` #${r.number}`:""} picks it up)`:"",m=a.has("rebase")?" with `--force-with-lease` (the rebase rewrote history)":"";i?u.push(a.has("commit")||a.has("rebase")?`push each one to its remote${m}${p}`:`push this branch${e?` (${e})`:""} from each changed checkout (${c}) to its remote${p}`):u.push(a.has("commit")||a.has("rebase")?`push it to the remote${m}${p}`:`push this branch${l} to the remote${p}`)}a.has("pr")&&u.push(i?"open a pull request in each changed repo with a concise title and a description of what changed and why (run `gh pr create` inside each checkout)":`open a pull request${n?` against ${n}`:""} with a concise title and a description of what changed and why (use \`gh pr create\`)`);let d=[];a.has("commit")||d.push("don't create any new commits \u2014 ship what's already committed"),s&&!a.has("rebase")&&d.push("don't rebase \u2014 leave the branch's history as it is"),!a.has("push")&&!a.has("pr")&&d.push("don't push"),a.has("pr")||d.push("don't open a pull request");let f=`Yes \u2014 go ahead: ${u.join(", then ")}.`;return d.length&&(f+=` ${d.join(", and ").replace(/^./,p=>p.toUpperCase())}.`),a.has("pr")&&(f+=i?" If a repo's PR for this branch is already open, just push that repo's update instead.":" If a PR for this branch is already open, just push the update instead."),f}var aS=3e4;function dm(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<=aS&&n.push({box:r.box,from:r.from,to:e+1e12}),n}function fm(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}var rm={commit:"commit",rebase:"rebase",push:"push",pr:"open a PR"};function pm(t){let e=(Array.isArray(t)?t:[]).filter(r=>rm[r]).map(r=>rm[r]);return`The ship you were given the go-ahead for${e.length?` (${e.join(", then ")})`:""} should now be complete. This is an automated ship notification the operator armed on the offer: carry straight on with this Track's follow-up work (dependent tasks, cleanup, next steps) \u2014 don't wait for review or merge. If a ship step failed or is unfinished, finish the ship first. If nothing needs doing, say so briefly.`}import{execFile as dS}from"node:child_process";import{promisify as fS}from"node:util";import iS from"node:fs";import _c from"node:fs/promises";import gm from"node:path";import cS from"node:os";var ym=process.env.BOSTRAT_STATE_DIR||gm.join(process.env.HOME||cS.tmpdir(),".bostrat"),Cs=gm.join(ym,"github-metrics.json"),bm=60*6e4,mm=5*6e4,lS=6e4,lr=Date.now(),he=new Map,De=null,xn=0,hm=0,Ms=t=>[...t.values()].reduce((e,n)=>e+n,0);try{let t=JSON.parse(iS.readFileSync(Cs,"utf8"));t?.windowStart&&(Date.now()-t.windowStart<bm?(lr=t.windowStart,he=new Map(Object.entries(t.byCaller||{})),De=t.lastHour||null):Ms(new Map(Object.entries(t.byCaller||{})))&&(De={start:t.windowStart,total:Ms(new Map(Object.entries(t.byCaller||{}))),byCaller:t.byCaller||{}}))}catch{}var wm=Promise.resolve();function km(t,e=!1){if(!e&&t-hm<lS)return;hm=t;let n=JSON.stringify({windowStart:lr,byCaller:Object.fromEntries(he),lastHour:De});wm=wm.then(()=>_c.mkdir(ym,{recursive:!0})).then(()=>_c.writeFile(`${Cs}.tmp`,n)).then(()=>_c.rename(`${Cs}.tmp`,Cs)).catch(()=>{})}function xm(t){t-lr<bm||(De={start:lr,total:Ms(he),byCaller:Object.fromEntries(he)},De.total&&console.log(`[github] ${De.total} calls last hour`,De.byCaller),lr=t,he=new Map,km(t,!0))}function we(t="other",e=Date.now()){xm(e),he.set(t,(he.get(t)||0)+1),km(e)}function Sm(t=Date.now()){return xm(t),{windowStart:lr,total:Ms(he),byCaller:Object.fromEntries(he),lastHour:De,backoffUntil:xn>t?xn:null}}var uS=/rate limit|too quickly|abuse detection|HTTP 429/i;function je(t){return uS.test(String(t?.message||t||""))}function Le(t,e=Date.now()){return xn<=e&&console.warn(`[github] rate limited \u2014 backing off ${mm/6e4} min:`,String(t?.message||t||"").slice(0,200)),xn=Math.max(xn,e+mm),xn}function Ps(t=Date.now()){return Math.max(0,xn-t)}async function Ec(t,e){let n={working:!1,prompted:!1};if(!t)return n;let r=await wt(t,{lines:_t}).catch(()=>"");if(!jn(e,r))return Iu(e,r)?{working:!0,prompted:!1}:n;if(sr(e,r)){let o=await ve(t).catch(()=>r);if(bn({name:t,agent:e},o))return{working:!1,prompted:!0}}return{working:!0,prompted:!1}}async function Ns(t,e){return(await Ec(t,e)).working}var vm=fS(dS),Am=[4e3,15e3,45e3],pS=2500,mS=5*6e4;async function Ic(t,e,{midTurn:n=Ns,pollMs:r=pS,capMs:o=mS}={}){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 hS=6e4,wS=9e4,gS=2e3,yS=/(?:^|[;&|]\s*|\)\s*)gh\s+(?:[^\s;&|"'`$]+\s+)*?pr\s+create\b/,bS=/(?:^|[;&|]\s*|\)\s*)gh\s+(?:[^\s;&|"'`$]+\s+)*?pr\s+merge\b/,kS=/\s--web\b/,Tm=/(?:--repo|-R)[\s=]+([\w.-]+\/[\w.-]+)/;function Rc(t,e){return t.slice(e).replace(/^[\s;&|)]+/,"").replace(/"[^"]*"|'[^']*'/g,'""').split(/[;&|]/)[0]}function _m(t){let e=String(t||""),n=e.match(yS);if(!n)return null;let r=Rc(e,n.index);return kS.test(r)?null:{repo:r.match(Tm)?.[1]??null}}var xS=new Set(["--subject","-t","--body","-b","--body-file","-F","--match-head-commit","--author-email","-A","--repo","-R"]);function Em(t){let e=String(t||""),n=e.match(bS);if(!n)return null;let r=Rc(e,n.index),o=Rc(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("-")){xS.has(c)&&i++;continue}s=c.replace(/^["']+|["']+$/g,"")||null;break}return{repo:r.match(Tm)?.[1]??null,selector:s,auto:/\s--auto\b/.test(r)}}var SS="number,url,title,headRefName,createdAt";async function vS(t,e){let n=async s=>{we("observer");let{stdout:a}=await vm("gh",["pr","list","--repo",t,"--author","@me","--state","all","--limit","10","--json",SS],{timeout:3e4,maxBuffer:4*1024*1024,env:{...process.env,...s}}).catch(i=>{throw je(i)&&Le(i),i});return JSON.parse(a||"[]")},r=await re({slug:t}),o=await n(r);return!o.length&&r.GH_TOKEN&&(o=await n({}).catch(()=>[])),o.filter(s=>Date.parse(s.createdAt)>=e-hS).sort((s,a)=>Date.parse(a.createdAt)-Date.parse(s.createdAt))}async function AS(t,e){if(e)return[e];let n=[];for(let r of Im(t)){let o=await Y(r).catch(()=>null);o&&!n.includes(o)&&n.push(o)}for(let r of await me().catch(()=>[])){let o=await Y(r.path).catch(()=>null);o&&!n.includes(o)&&n.push(o)}return n}function Im(t){let e=t.workspace;return e?.kind==="multi"?(e.repos??[]).map(n=>n?.base).filter(Boolean):[e?.base||t.cwd].filter(Boolean)}async function TS(t,e,n){let r=t.workspace?.branch,o=[];for(let s of await AS(t,e)){let a;try{a=await vS(s,n)}catch{continue}for(let i of a){let c=await vt(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 Fe=new Set;function Rm(t,e,{delays:n=Am,turnEnd:r}={}){let o=_m(e.command);if(!o)return null;let s=!!Em(e.command),a=`${t.id}|${e.id??e.ts??e.command}`;if(Fe.has(a))return null;Fe.add(a);let i=e.ts??Date.now(),c=(async()=>{try{for(let l of n){await new Promise(h=>setTimeout(h,l));let u=await TS(t,o.repo,i).catch(()=>null);if(!u)continue;let{slug:d,pr:f}=u,{link:p,created:m}=await Pt({repo:d,number:f.number,trackId:t.id,url:f.url,headRef:f.headRefName,title:f.title,source:"observed"});return!m&&!(p?.trackId===t.id&&await Gr(d,f.number))?null:(await Ic(e.box,e.agent,r),await O({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{Fe.delete(a)}})();return c.catch(()=>Fe.delete(a)),c}var _S="number,url,title,headRefName,state";function $m(t,e,{delays:n=Am,turnEnd:r,openedWait:o}={}){let s=Em(e.command);if(!s)return null;let a=`merge|${t.id}|${e.id??e.ts??e.command}`;if(Fe.has(a))return null;Fe.add(a);let i=(async()=>{try{let c=s.repo,l=s.selector,u=l?.match(/github\.com\/([\w.-]+\/[\w.-]+)\/pull\/(\d+)/);u&&([,c,l]=u);let d=[];if(c)d=[c];else{let m=Im(t);if(m.length>1&&/^\d+$/.test(String(s.selector??"")))return null;for(let h of m){let b=await Y(h).catch(()=>null);b&&!d.includes(b)&&d.push(b)}}if(l||=t.workspace?.branch,!d.length||!l)return null;c=d[0];let f=async(m,h)=>{let b=await vt(c,m.number);return b&&b.trackId!==t.id?null:(b||await Pt({repo:c,number:m.number,trackId:t.id,url:m.url,headRef:m.headRefName,title:m.title,source:"merge-observed"}),await Kr(c,m.number),h&&((await F(t).catch(()=>[])).some(E=>E.type==="pr.state.changed"&&(E.payload?.change==="auto-armed"||E.payload?.change==="merged")&&E.payload?.number===m.number)||(await Ic(e.box,e.agent,r),await O({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:m.number,url:m.url,title:m.title,branch:m.headRefName,repo:c}}))),m)},p=null;for(let m of n){await new Promise(I=>setTimeout(I,m));let h=[];for(let I of d)try{we("observer");let{stdout:L}=await vm("gh",["pr","view",String(l),"--repo",I,"--json",_S],{timeout:3e4,maxBuffer:4*1024*1024,env:{...process.env,...await re({slug:I})}}),T=JSON.parse(L||"null");T?.number&&h.push({slug:I,pr:T})}catch(L){je(L)&&Le(L)}let b=h.find(I=>I.pr.state==="MERGED")??(h.length===1?h[0]:null);if(!b)continue;c=b.slug;let x=b.pr;if(!x?.number)continue;if(x.state==="CLOSED")return null;if(x.state!=="MERGED"){if(p=x,s.auto)return f(x,!0);continue}let v=await vt(c,x.number);if(v&&v.trackId!==t.id)return null;v||await Pt({repo:c,number:x.number,trackId:t.id,url:x.url,headRef:x.headRefName,title:x.title,source:"merge-observed"}),await Ic(e.box,e.agent,r);let E=await F(t).catch(()=>[]);if(E.some(I=>I.type==="pr.state.changed"&&I.payload?.change==="merged"&&I.payload?.number===x.number))return null;let R=I=>I.some(L=>L.type==="pr.state.changed"&&L.payload?.change==="opened"&&L.payload?.number===x.number),st=0;if(!R(E)){if(_m(e.command)&&await Gr(c,x.number)){let I=Date.now();st=I+1,await O({id:t.id,cwd:t.cwd},{type:"pr.state.changed",actor:"agent",source:"transcript",boxId:e.box??null,provider:e.agent??null,payload:{ts:I,change:"opened",number:x.number,url:x.url,title:x.title,branch:x.headRefName,repo:c,selfMerge:!0}})}else if((await vt(c,x.number))?.announced){let I=o?.capMs??wS,L=o?.pollMs??gS,T=Date.now()+I;for(;Date.now()<T&&(await new Promise(U=>setTimeout(U,L)),!R(await F(t).catch(()=>[]))););}}return await O({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(),st),change:"merged",number:x.number,url:x.url,title:x.title,branch:x.headRefName,repo:c}}),await He(c,x.number).catch(()=>{}),await Ue(c,x.number).catch(()=>{}),await Ce(c,x.number,"merged"),x}return p?f(p,!1):null}finally{Fe.delete(a)}})();return i.catch(()=>Fe.delete(a)),i}function Oc(t){return t.sourceId?`${t.role}|id:${t.sourceId}`:`${t.role}|${t.ts??"?"}|${String(t.text||"").slice(0,40)}`}function Om(t){return t.id?`cmd|${t.box}|id:${t.id}`:`cmd|${t.box}|${t.ts??"?"}|${String(t.command||"").slice(0,40)}`}function Cm(t){return`job|${t.box}|id:${t.id}`}var $c=new Map,Bs=new Map;async function Cc(t){return $c.has(t.id)?$c.get(t.id):(Bs.has(t.id)||Bs.set(t.id,F(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(Oc({role:o,sourceId:r.payload.sourceId})),n.keys.add(Oc({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(Om({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(Cm({id:r.payload.id,box:r.boxId}));return $c.set(t.id,n),n}).finally(()=>Bs.delete(t.id))),Bs.get(t.id))}async function Mm(t,e){try{let n=(await Cc(t)).keys;for(let r of e||[]){if(!r||r.ts==null||!String(r.text||"").trim()&&!r.images?.length)continue;let o=Oc(r);if(!n.has(o)&&(n.add(o),await O(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=cm(r.text);if(s){let{offered:a,toPr:i}=um(s),c;if(t.workspace?.kind==="multi"){let l=await no(t).catch(()=>[]);c=l.length?l.map(u=>({name:u.name,slug:u.repo??null})):void 0}await O(t,{type:"pr.offered",actor:"agent",source:"transcript",boxId:r.box,provider:r.agent||null,payload:{offerId:ES(),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,repos:c}})}}}}catch{}}async function Pm(t,e){try{let n=await Cc(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=Om(o);n.keys.has(s)||(n.keys.add(s),n.cmdN+=1,await O(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}}),Rm(t,o),$m(t,o))}}catch{}}async function Nm(t,e){try{let n=await Cc(t),r=[...e||[]].filter(o=>o&&o.id).sort((o,s)=>(o.ts||0)-(s.ts||0));for(let o of r){let s=Cm(o);n.keys.has(s)||(n.keys.add(s),await O(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 IS=/^Base directory for this skill:\s*(\S+)/,RS=/^Run the "([^"]+)" workflow\./,$S=/<command-name>\s*\/?([a-z0-9:_-]+)\s*<\/command-name>/i,OS=/^<local-command-(?:stdout|caveat)>/i,CS=/^\[Image: original \d+x\d+, displayed at \d+x\d+\.\s*Multiply coordinates by [\d.]+ to map to original image\.\]$/,MS=/^<task-notification>/i,PS=/<summary>([\s\S]*?)<\/summary>/i,NS=/<task-id>([\s\S]*?)<\/task-id>/i,BS=/<tool-use-id>([\s\S]*?)<\/tool-use-id>/i,DS=/<status>([\s\S]*?)<\/status>/i,jS=/<result>([\s\S]*?)(?:<\/result>|$)/i;var Mc=new Set(["model","clear","compact","login"]);function Bm(t){return Mc.has(String(t||"").replace(/^\//,"").toLowerCase())}var LS=/^\/([a-z0-9:_-]+)$/i;function Ds(t){let e=String(t||"").trim();if(!e)return null;let n=e.match(IS);if(n)return{kind:"skill",name:n[1].split("/").filter(Boolean).pop()||"skill"};let r=e.match(RS);if(r)return{kind:"skill",name:r[1]};let o=e.match($S);if(o){let a=o[1].toLowerCase();return Mc.has(a)?{kind:"suppress"}:{kind:"command",command:`/${a}`}}let s=e.match(LS);if(s&&Mc.has(s[1].toLowerCase()))return{kind:"suppress"};if(OS.test(e))return{kind:"suppress"};if(CS.test(e))return{kind:"suppress"};if(MS.test(e)){let a=c=>FS(e.match(c)?.[1].trim()||"")||null,i=a(jS);return{kind:"task",summary:a(PS)||"",taskId:a(NS),toolUseId:a(BS),status:a(DS)?.toLowerCase()||null,resultHead:i?i.length>600?`${i.slice(0,600).trimEnd()}\u2026`:i:null}}return null}function FS(t){return t.replace(/</g,"<").replace(/>/g,">").replace(/"/g,'"').replace(/'/g,"'").replace(/&/g,"&")}function Dm(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 jm(t){return t?.kind==="skill"?"skill-run":t?.kind==="task"?"task-done":"command-run"}var nt=t=>t.payload?.ts??t.ts,US=new Set(["opened","ci-fixed","reconciled","pushed"]),Lm=t=>`${t.repo??""}#${t.number}`;function js(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 g of t||[]){if(g.type!=="tool.called"||g.source!=="transcript")continue;let w=String(g.payload?.command||"");if(/\bbostrat-mock\s+(?:add|update)\b/.test(w))for(let y of w.matchAll(/[\w./~-]+\.(?:png|jpe?g|gif|webp|bmp|svg)\b/gi)){let A=y[0].split("/").pop().toLowerCase();d.has(A)||d.set(A,[]),d.get(A).push(g.payload?.ts??g.ts)}}let f=3*6e4,p=20*6e4,m=(g,w)=>!!(g?.name&&d.get(String(g.name).toLowerCase()))?.some(A=>A>=w-f&&A<=w+p);for(let g of t||[])if(g.type==="turn.retracted"){r.add(`${g.boxId}|${g.payload?.saidTs}`);let w=kn(g.payload?.text);w&&(o.has(g.boxId)||o.set(g.boxId,[]),o.get(g.boxId).push(w))}else if(g.type==="turn.user"&&g.source==="orchestrator"){let w=kn(g.payload?.text);w&&(s.has(g.boxId)||s.set(g.boxId,new Set),s.get(g.boxId).add(w))}else if(g.type==="job.started"&&g.payload?.id)a.add(g.payload.id),(g.payload.kind||"agent")==="agent"&&g.payload.name&&c.add(g.payload.name);else if(g.type==="turn.user"&&g.payload?.note?.kind==="task"){let w=g.payload.note,y={status:w.status||null,summary:w.summary||null,resultHead:w.resultHead??null,ts:nt(g)};w.toolUseId&&i.set(w.toolUseId,y);let A=String(w.summary||"").match(u);A&&l.set(A[1],y)}let h=new Map,b=new Map,x=null,v=new Map,E=new Map,R=new Map,st=new Map,I=new Map,L=new Map,T=new Map,U=new Map,J=(g,w)=>{let y=o.get(g);if(!y?.length)return!1;let A=kn(w),K=s.get(g);return y.some(B=>{if(!A.startsWith(B))return!1;let $=kn(A.slice(B.length));return!!$&&!!K?.has($)})};for(let g of t||[]){let w=g.payload||{};switch(g.type){case"turn.user":{if(g.source==="hook"||!String(w.text||"").trim()&&!w.images?.length)break;{let y=w.note||Ds(w.text);if(y){if(y.kind==="task"){if(y.toolUseId&&a.has(y.toolUseId))break;let A=String(y.summary||"").match(u);if(!y.toolUseId&&A&&c.has(A[1]))break}if(y.kind==="command"&&Bm(y.command))break;y.kind!=="suppress"&&e.push({id:g.id,kind:"event",type:jm(y),name:y.name??null,command:y.command??null,summary:y.summary??null,ts:nt(g),box:g.boxId,agent:g.provider});break}}if(r.has(`${g.boxId}|${nt(g)}`)||g.source==="transcript"&&J(g.boxId,w.text))break;if(w.kind==="merge-notify"||w.kind==="ship-notify"){e.push({id:g.id,kind:"event",type:w.kind==="ship-notify"?"ship-notified":"merge-notified",text:w.text,ts:nt(g),box:g.boxId,agent:g.provider});break}if(w.kind==="mock-picked"||w.kind==="ci-fix"||w.kind==="reconcile"||w.kind==="pr-go"||w.kind==="push-go"||w.kind==="spin-up-view")break;for(let y of v.values())y.status="stale";v.clear(),e.push({id:g.id,kind:"message",role:"user",text:w.text,ts:nt(g),box:g.boxId,agent:g.provider,boxRole:w.boxRole,...g.actor==="orchestrator"?{orchestration:w.kind||!0}:{},...w.sourceId?{sourceId:w.sourceId}:{},...w.attachments?.length?{attachments:w.attachments}:{},...w.images?.length?{images:w.images}:{}});break}case"turn.agent":{T.clear();let y=(w.images||[]).filter(K=>!m(K,nt(g)));if(!String(w.text||"").trim()&&!y.length)break;if(g.provider==="cursor"){let K=`${g.boxId}|${w.text}`;if(n.has(K))break;n.add(K)}let A=g.provider==="codex"?(w.tools||[]).filter(K=>!Qi.has(K)&&!Qr.has(K)):w.tools||[];e.push({id:g.id,kind:"message",role:"assistant",text:w.text,ts:nt(g),box:g.boxId,agent:g.provider,boxRole:w.boxRole,...A.length?{tools:A}:{},...w.model?{model:w.model}:{},...w.sourceId?{sourceId:w.sourceId}:{},...y.length?{images:y}:{}});break}case"job.started":{if(!w.id)break;let y=nt(g),A=i.get(w.id);if(!A&&(w.kind||"agent")==="agent"&&w.name){let _=l.get(w.name);_&&_.ts>=y&&(A=_)}let K={jobId:w.id,jobKind:w.kind||"agent",name:w.name||null,detail:w.detail||null,status:A?A.status&&A.status!=="completed"?"failed":"done":"running",startedTs:y,finishedTs:A?.ts??null,summary:A?.summary??null,resultHead:A?.resultHead??null},B=U.get(g.boxId);if(B&&B.jobs.some(_=>(_.finishedTs??1/0)>=y))B.jobs.push(K);else{let _={id:g.id,kind:"job-dock",dockId:w.id,ts:y,box:g.boxId,agent:g.provider,jobs:[K]};U.set(g.boxId,_),e.push(_)}break}case"handoff.done":e.push(We(g,"lead-handoff"));break;case"subtask.delegated":e.push({...We(g,"subtask-delegated"),to:g.boxId,toAgent:g.provider});break;case"subtask.merged":e.push(We(g,"subtask-merged"));break;case"pr.state.changed":{if(!w.change)break;if(w.change==="opened"){for(let A of v.values())A.status="stale";v.clear()}let y=We(g,`pr-${w.change}`);if(US.has(w.change)&&w.number!=null){let A=Lm(w);if(h.set(A,{number:w.number,url:w.url??null,title:w.title??null,repo:w.repo??null,branch:w.branch??null}),h.size>=2){for(let B of b.values())for(let $ of B)$.grouped=!0;y.groupPrs=[...h.values()];let K=[y];for(let B of h.keys())b.set(B,K)}else{let K=b.get(A)??[];K.push(y),b.set(A,K)}}else if(w.change==="merged"||w.change==="closed"||w.change==="lost"){let A=Lm(w),K=x&&!x.complete||h.size>=2;if(h.has(A))h.delete(A),b.delete(A);else if(w.number!=null)for(let B of[...h.keys()])B.endsWith(`#${w.number}`)&&(h.delete(B),b.delete(B));if(K&&w.number!=null){(!x||x.complete)&&(x={id:g.id,kind:"event",type:"pr-group-settled",prs:[],remaining:[],complete:!1,at:nt(g),ts:nt(g),box:g.boxId,agent:g.provider},e.push(x)),x.prs.push({number:w.number,url:w.url??null,title:w.title??null,repo:w.repo??null,branch:w.branch??null,change:w.change,ts:nt(g)}),x.remaining=[...h.values()],x.complete=h.size===0;break}}e.push(y);break}case"pr.offered":{if(!w.offerId)break;for(let A of v.values())A.status="stale";v.clear();let y={id:g.id,kind:"pr-offer",offerId:w.offerId,excerpt:w.excerpt||null,branch:w.branch||null,base:w.base||null,offered:Array.isArray(w.offered)&&w.offered.length?w.offered:null,toPr:w.toPr&&typeof w.toPr=="object"?{number:w.toPr.number??null}:null,repos:Array.isArray(w.repos)&&w.repos.length?w.repos:null,status:"open",ts:nt(g),box:g.boxId,agent:g.provider};v.set(w.offerId,y),E.set(w.offerId,y),e.push(y);break}case"pr.offer.answered":{let y=E.get(w.offerId);y&&(y.status=w.action||"dismiss",y.actions=Array.isArray(w.actions)?w.actions:null,y.notify=w.notify===!0,v.delete(w.offerId));break}case"box.state.changed":{let y=w.change==="model"?"model-change":w.change;if(!y)break;let A=We(g,y);y==="model-change"&&(A.to=w.model),e.push(A);break}case"view.state.changed":{if(w.change!=="created"||w.restarted)break;e.push({id:g.id,kind:"view-card",viewId:w.viewId,name:w.name,slug:w.slug,port:w.port,origin:w.origin,ts:nt(g),box:g.boxId,agent:g.provider});break}case"view.spinup.failed":{e.push({id:g.id,kind:"view-spinup-failed",name:w.name||null,reason:w.reason||"no-view",ts:nt(g),box:g.boxId,agent:g.provider});break}case"mock.state.changed":{if(w.change==="picked"){e.push({id:g.id,kind:"mock-picked",mockId:w.mockId,name:w.name,group:w.group??null,version:w.version??null,siblings:w.siblings??[],ts:nt(g),box:g.boxId,agent:g.provider});break}if(w.change!=="created")break;e.push({id:g.id,kind:"mock-card",mockId:w.mockId,name:w.name,group:w.group??null,ts:nt(g),box:g.boxId,agent:g.provider});break}case"runbook.state.changed":{if(w.change!=="created")break;e.push({id:g.id,kind:"runbook-card",runbookId:w.runbookId,name:w.name,ts:nt(g),box:g.boxId,agent:g.provider});break}case"ticket.linked":{e.push({id:g.id,kind:"ticket-card",provider:w.provider||"linear",key:w.key,url:w.url,title:w.title??null,linkSource:w.linkSource??null,ts:nt(g),box:g.boxId,agent:g.provider});break}case"ticket.move.offered":{if(!w.offerId)break;for(let A of R.values())A.key===w.key&&(A.status="stale",R.delete(A.offerId));let y={id:g.id,kind:"ticket-offer",offerId:w.offerId,provider:w.provider||"linear",key:w.key,url:w.url??null,phase:w.phase??null,from:w.from??null,to:w.to?.name??null,prNumber:w.prNumber??null,status:"open",ts:nt(g),box:g.boxId,agent:g.provider};R.set(w.offerId,y),st.set(w.offerId,y),e.push(y);break}case"ticket.move.answered":{let y=st.get(w.offerId);y&&(y.status=w.action==="moved"?"moved":"dismiss",R.delete(w.offerId));break}case"ticket.state.changed":{for(let y of R.values())y.key===w.key&&y.to===w.to&&(y.status="stale",R.delete(y.offerId));if(w.mode==="offer")break;e.push(We(g,"ticket-state"));break}case"ticket.commented":{e.push({id:g.id,kind:"ticket-comment",key:w.key,url:w.url??null,excerpt:w.excerpt??null,ts:nt(g),box:g.boxId,agent:g.provider});break}case"ticket.flow.changed":e.push(We(g,"ticket-flow"));break;case"ticket.create.offered":{if(!w.proposalId)break;for(let A of I.values())A.status="stale";I.clear();let y={id:g.id,kind:"ticket-create-offer",proposalId:w.proposalId,provider:w.provider||"linear",epic:w.epic??null,issues:Array.isArray(w.issues)?w.issues:[],team:w.team??null,needsTeam:!!w.needsTeam,linkCurrentTrack:!!w.linkCurrentTrack,status:"open",ts:nt(g),box:g.boxId,agent:g.provider};I.set(w.proposalId,y),L.set(w.proposalId,y),e.push(y);break}case"ticket.create.answered":{let y=L.get(w.proposalId);y&&(y.status=w.action==="created"?"created":"dismiss",y.createdCount=w.createdCount??null,y.failedCount=w.failedCount??null,I.delete(w.proposalId));break}case"ticket.created":{let y={items:Array.isArray(w.items)?w.items:[],failed:Array.isArray(w.failed)?w.failed:[],team:w.team??null},A=w.proposalId?L.get(w.proposalId):null;if(A){A.created=y;break}e.push({id:g.id,kind:"ticket-created",...y,ts:nt(g),box:g.boxId,agent:g.provider});break}case"error":{if(!w.kind)break;e.push(We(g,w.kind));break}case"provider.connected":{e.push({id:g.id,kind:"event",type:"provider-connected",provider:w.provider||g.provider,ts:nt(g),at:nt(g),box:g.boxId,agent:g.provider});break}case"prompt.answered":{if(w.control||!w.question)break;let y=w.groupId||w.promptId;if(!y)break;let A=T.get(y);A||(A={id:g.id,kind:"prompt",groupId:y,provider:g.provider,box:g.boxId,agent:g.provider,questionTotal:w.questionTotal??1,questions:[],ts:nt(g)},T.set(y,A));let K=w.questionIndex??0,B={index:K,header:w.header||"",question:w.question,multiSelect:!!w.multiSelect,chosen:Array.isArray(w.chosen)?w.chosen:[]},$=A.questions.findIndex(M=>M.index===K);$>=0?A.questions[$]=B:A.questions.push(B),A.questions.sort((M,at)=>M.index-at.index),A.questionTotal=Math.max(A.questionTotal,w.questionTotal??1),new Set(A.questions.map(M=>M.index)).size>=A.questionTotal&&(e.push(A),T.delete(y));break}default:break}}return WS(e.sort((g,w)=>(g.ts??0)-(w.ts??0)))}var HS=2*6e4;function WS(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<=HS){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 Pc(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:qS(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??js(t)).filter(o=>o.kind==="message");return ep(n,r)}function qS(t){let e=String(t.summary||"").trim();return e&&e!==t.command&&e!==er(t.command)?e:Ne(t.command,t.description||"")}function We(t,e){let{ts:n,change:r,...o}=t.payload||{};return{id:t.id,kind:"event",type:e,...o,at:nt(t),ts:nt(t)}}function GS(t){let e=[],n=null;for(let r of String(t||"").split(`
|
|
83
|
+
`)){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 Nc=new Map;async function KS(t,e){let n=`${t.id}:${e.name}`;if(!Nc.has(n)){let r=await F(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);Nc.set(n,o)}return Nc.get(n)}async function Fm(t,e){try{if(e?.agent!=="codex"||!e.cwd)return;let n=await mn(e.cwd,e.pinned?e.sessionId:null);if(!n)return;let r=await KS(t,e),o=GS(await Et(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 O(t,{type:"cost.recorded",actor:"agent",source:"transcript",boxId:e.name,provider:"codex",payload:{usd:zt(c,i),model:c,tokens:i,reasoningOutput:a.last.reasoningOutput||void 0,codexTs:a.ts,ts:a.ts}}),a===s&&a.contextWindow&&await O(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 Ls from"node:fs";import Dc from"node:path";import VS from"node:os";var JS=process.env.BOSTRAT_STATE_DIR||Dc.join(process.env.HOME||VS.tmpdir(),".bostrat"),Hm=(t=process.env)=>!!t.BOSTRAT_CURSOR_USAGE_COOKIE,zS="https://cursor.com/api/dashboard/get-filtered-usage-events",Um=100;async function XS({sinceMs:t,cookie:e,fetchImpl:n=fetch}){let r=[];for(let o=1;o<=20;o++){let s=await n(zS,{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:Um})});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<Um)break}return r}function YS(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 ZS=5*6e4,qe={at:0,rows:null,error:null},Fs=()=>Dc.join(JS,"cursor-usage.json");function Wm(){try{return JSON.parse(Ls.readFileSync(Fs(),"utf8"))}catch{return{sinceMs:0,seen:[]}}}function QS(t){try{Ls.mkdirSync(Dc.dirname(Fs()),{recursive:!0});let e=Fs()+".tmp";Ls.writeFileSync(e,JSON.stringify(t)),Ls.renameSync(e,Fs())}catch{}}var tv=5e3;async function ev({fetchImpl:t,now:e=Date.now()}={}){if(qe.rows&&e-qe.at<ZS)return qe.rows;let n=process.env.BOSTRAT_CURSOR_USAGE_COOKIE;if(!n)return null;let r=Wm(),o=Math.max(r.sinceMs-6e4,e-24*36e5);try{let s=await XS({sinceMs:o,cookie:n,fetchImpl:t});qe={at:e,rows:s.map(YS).filter(Boolean),error:null}}catch(s){qe={at:e,rows:null,error:String(s?.message||s)}}return qe.rows}var qm=()=>({enabled:Hm(),lastPollAt:qe.at||null,error:qe.error}),Bc=new Map;async function nv(t,e){let n=`${t.id}:${e.name}`;if(!Bc.has(n)){let r=await F(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);Bc.set(n,o)}return Bc.get(n)}async function Gm(t,e,{fetchImpl:n}={}){try{if(e?.agent!=="cursor"||!e.cwd||!Hm())return;let r=await ev({fetchImpl:n});if(!r||!r.length)return;let o=await up(e.cwd,e.pinned?e.sessionId:null);if(!o?.chatId)return;let s=await nv(t,e),a=Wm(),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=zt(u,d)),!f&&l.chargedCents!=null&&(f=l.chargedCents/100),await O(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}})}QS({sinceMs:c,seen:[...i].slice(-tv)})}catch{}}import{execFile as iv}from"node:child_process";import{promisify as cv}from"node:util";import Km from"node:fs/promises";import Vm from"node:path";import rv from"node:os";var Jm=process.env.BOSTRAT_STATE_DIR||Vm.join(process.env.HOME||rv.tmpdir(),".bostrat"),ov=Vm.join(Jm,"github-latency.ndjson"),sv=100,Us=[],jc=new Map;function Hs({change:t,repo:e=null,number:n=null,githubAt:r=null,observedAt:o=Date.now(),lane:s=null,intervalMs:a=null,source:i=null}){let c=r?o-r:null,l={change:t,repo:e,number:n,githubAt:r,observedAt:o,latencyMs:c,lane:s,intervalMs:a,source:i};Us.push(l),Us.length>sv&&Us.shift();let u=jc.get(t)||{count:0,measured:0,totalMs:0,maxMs:0};return u.count+=1,c!=null&&(u.measured+=1,u.totalMs+=c,u.maxMs=Math.max(u.maxMs,c)),jc.set(t,u),av(l),l}async function av(t){try{await Km.mkdir(Jm,{recursive:!0}),await Km.appendFile(ov,JSON.stringify(t)+`
|
|
84
|
+
`)}catch{}}function zm(){let t={};for(let[e,n]of jc)t[e]={count:n.count,measured:n.measured,avgMs:n.measured?Math.round(n.totalMs/n.measured):null,maxMs:n.measured?n.maxMs:null};return{byChange:t,recent:Us.slice(-20)}}var Gs=cv(iv),lv={timeout:3e4,maxBuffer:8*1024*1024};async function Ut(t,e,n={}){let{caller:r="prs",...o}=n,s=await re({cwd:t});we(r);try{let{stdout:a}=await Gs("gh",e,{cwd:t,...lv,...o,env:{...process.env,...s,...o.env||{}}});return String(a||"")}catch(a){throw je(a)&&Le(a),a}}var Ws=null;async function Uc(){if(Ws&&Date.now()-Ws.at<6e4)return Ws.data;let t;try{we("auth");let e=await Gs("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 zr();e.configured&&(t={installed:!0,authenticated:!0,login:e.username,via:"broker"})}catch{}return Ws={at:Date.now(),data:t},t}async function uv(t){try{let{stdout:e}=await Gs("git",["-C",t,"config","--get","remote.origin.url"],{timeout:5e3});return/github\.com/.test(e)}catch{return!1}}function Ym(t){let e=t.name||t.context||"check",n=t.detailsUrl||t.targetUrl||null,r=t.completedAt||t.createdAt||null,o;return t.__typename==="StatusContext"?o=t.state==="SUCCESS"?"pass":t.state==="PENDING"||t.state==="EXPECTED"?"pending":"fail":t.status&&t.status!=="COMPLETED"?o="pending":["SUCCESS","NEUTRAL","SKIPPED"].includes(t.conclusion)?o="pass":o="fail",{name:e,state:o,url:n,completedAt:r}}function Lc(t){let e=(t||[]).map(Ym),n=e.filter(s=>s.state==="pending").length,r=e.map(s=>Date.parse(s.completedAt||"")).filter(s=>s>0),o=e.length&&!n&&r.length?Math.max(...r):null;return{total:e.length,passed:e.filter(s=>s.state==="pass").length,failed:e.filter(s=>s.state==="fail").length,pending:n,failing:e.filter(s=>s.state==="fail").map(({name:s,url:a})=>({name:s,url:a})),settledAt:o}}var Hc="number,title,url,headRefName,headRefOid,baseRefName,isDraft,author,reviewDecision,mergeable,mergeStateStatus,statusCheckRollup,autoMergeRequest,additions,deletions,createdAt,updatedAt",Zm=Hc.replace("statusCheckRollup,","");function Wc(t){return/Resource not accessible by personal access token/.test(String(t?.stderr||t?.message||""))}async function Qm(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 th(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,completedAt:e.status==="completed"&&e.updated_at||null}))}async function eh(t,e){let n=await Qm(t,e);return n===null?{...Lc([]),unavailable:!0}:Lc(th(n))}function qs(t,e=Lc(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 dv=9e4,ur=new Map;function fv(t,e,n){let r=new Set;for(let o of e){let s=`${t}#${o.number}`;if(o.mergeable!=="UNKNOWN"&&o.mergeStateStatus!=="UNKNOWN"){ur.delete(s);continue}r.add(s),ur.has(s)||ur.set(s,n),o.mergeabilityPending=n-ur.get(s)<dv}for(let o of ur.keys())o.startsWith(`${t}#`)&&!r.has(o)&&ur.delete(o);return e}async function dr(t,e){let n=await ge(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",Hc]);return qs(JSON.parse(o))}catch(o){if(!Wc(o))throw o;let s=await Ut(n.path,["pr","view",String(r),"--json",Zm]),a=JSON.parse(s);return qs(a,await eh(n.path,a.headRefOid))}}var Xm=(t,e)=>t.number-e.number;async function oo(t,{caller:e="prs"}={}){try{let n=await Ut(t,["pr","list","--json",Hc,"--limit","50"],{caller:e});return JSON.parse(n||"[]").map(r=>qs(r)).sort(Xm)}catch(n){if(!Wc(n))throw n;let r=await Ut(t,["pr","list","--json",Zm,"--limit","50"],{caller:e});return(await Promise.all(JSON.parse(r||"[]").map(async o=>qs(o,await eh(t,o.headRefOid))))).sort(Xm)}}var pv=3e4,mv=1500,ro=new Map;function se(){ro.clear()}async function hv(t,e){if(!t)return e;for(let n of e){let r=await vt(t,n.number).catch(()=>null);if(!r)continue;r.trackId&&(n.trackId=r.trackId),r.autoMerge&&(n.autoMergeArmed=!0),r.archiveOnMerge&&(n.archiveOnMerge=!0),r.notifyOnMerge&&(n.notifyOnMerge=!0);let o=s=>n.headSha&&s.headSha&&n.headSha!==s.headSha;r.ciFix&&(o(r.ciFix)?await ws(t,n.number,{headSha:n.headSha}).catch(()=>{}):n.ciFix={delegatedAt:r.ciFix.delegatedAt,trackId:r.ciFix.trackId??null}),r.reconcile&&(o(r.reconcile)?await ys(t,n.number,{headSha:n.headSha}).catch(()=>{}):n.reconcile={delegatedAt:r.reconcile.delegatedAt,trackId:r.reconcile.trackId??null})}return e}async function nh(){let t=await Uc();if(!t.authenticated)return{auth:t,repos:[]};let e=[];for(let a of await me())await uv(a.path)&&e.push(a);let n=Date.now(),r=Ps(n)>0,o=await Promise.all(e.map(async a=>{let i=ro.get(a.path),c=i?.pending?mv:pv;if(i&&(r||n-i.at<c))return i.entry;try{let l=await Y(a.path).catch(()=>null),u=fv(a.path,await oo(a.path,{caller:"tab"}),n);if(await hv(l,u),i?.entry?.prs)for(let p of u)i.entry.prs.find(h=>h.number===p.number)?.checks?.pending>0&&p.checks.total>0&&p.checks.pending===0&&Hs({change:p.checks.failed>0?"checks-failed":"checks-green",repo:l||a.name,number:p.number,githubAt:p.checks.settledAt,source:"tab"});let d={name:a.name,path:a.path,prs:u},f=u.some(p=>p.mergeabilityPending||p.checks.pending>0||p.autoMergeArmed);return ro.set(a.path,{at:Date.now(),entry:d,pending:f}),d}catch(l){return i?i.entry:{name:a.name,path:a.path,prs:[],error:oe(l)}}})),s=new Set(e.map(a=>a.path));for(let a of ro.keys())s.has(a)||ro.delete(a);return{auth:t,repos:o}}async function ge(t){let e=(await me()).find(n=>n.path===t);if(!e)throw new Error(`unknown repo: ${t}`);return e}async function Sn(t){if(!t)return null;for(let e of await me())if(await Y(e.path).catch(()=>null)===t)return e.path;return null}var so=new Set(["squash","merge","rebase"]);async function fr(t,e,{method:n="squash"}={}){let r=await ge(t);if(!so.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 re({cwd:r.path})).GH_TOKEN){let a=await Fi().catch(()=>null);a&&s.push("--body",`Merged by bostrat on behalf of @${a}`)}try{await Ut(r.path,s)}catch(a){throw new Error(oe(a))}finally{se()}return{ok:!0,number:o}}async function ao(t,e){let n=await ge(t),r=Number(e);if(!Number.isInteger(r)||r<=0)throw new Error("invalid PR number");try{await Ut(n.path,["api","-X","PUT",`repos/{owner}/{repo}/pulls/${r}/update-branch`])}catch(s){throw new Error(oe(s))}finally{se()}let o=await Y(n.path).catch(()=>null);return o&&await Ef(o,r).catch(()=>{}),{ok:!0,number:r}}async function qc(t,e){let n=await ge(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(oe(o))}finally{se()}return{ok:!0,number:r}}var wv=2500,gv=9e4,yv=t=>new Promise(e=>setTimeout(e,t));function Gc(t){return t.green&&!t.isDraft}function bv(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})`}function rh(t,{update:e=!0}={}){return t.isDraft?{action:"skip",reason:"draft"}:Gc(t)?{action:"merge"}:e&&t.mergeStateStatus==="BEHIND"&&t.mergeable!=="CONFLICTING"&&t.checks.failed===0?{action:"update-arm"}:{action:"arm"}}function oh(t){return[...t.filter(e=>e.plan.action!=="merge"),...t.filter(e=>e.plan.action==="merge")]}var Fc=new Map;function sh(){let t={};for(let[e,n]of Fc)t[e]={...n};return t}async function ah(t,{method:e="merge",onMerged:n=null}={}){let r=await ge(t);if(!so.has(e))throw new Error(`invalid merge method: ${e}`);if(Fc.get(r.path)?.running)throw new Error("a merge-all is already running for this repo");let o=(await oo(r.path)).filter(Gc).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 Fc.set(r.path,s),xv(s,o,{listPrs:()=>oo(r.path),merge:a=>fr(r.path,a,{method:e}),sleep:yv,onMerged:n}).catch(()=>{}),{ok:!0,repo:r.path,total:s.total,queue:o.map(a=>a.number)}}async function kv(t,e){let n=Date.now()+(e.settleMaxMs??gv),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(wv)}}async function xv(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 kv(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: ${oe(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(!Gc(a)){t.skipped.push({number:a.number,title:a.title,reason:bv(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:oe(i)})}}}finally{t.current=null,t.running=!1,t.finishedAt=Date.now(),se()}}async function Kc(t,{title:e,body:n="",base:r=null}={}){if(typeof e!="string"||!e.trim())throw new Error("title required");try{await Gs("git",["-C",t,"push","-u","origin","HEAD"],{timeout:12e4,maxBuffer:4*1024*1024})}catch(l){throw new Error(`push failed: ${oe(l)}`)}let o=n||"";if((await re({cwd:t})).GH_TOKEN){let l=await Fi().catch(()=>null);l&&(o=`${o?`${o}
|
|
85
|
+
|
|
86
|
+
`:""}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(oe(l))}finally{se()}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 ih(t,e){let n=await ge(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(oe(c))}o=JSON.parse(i||"[]").filter(c=>c.bucket==="fail")}catch(i){if(!Wc(i))throw i;let c=await Ut(n.path,["pr","view",String(r),"--json","headRefOid"]),l=await Qm(n.path,JSON.parse(c).headRefOid);if(l===null)throw new Error(oe(i));o=th(l).map(Ym).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=Sv(i.link),l=null;c&&!s.has(c)&&(s.add(c),l=await Ut(n.path,["run","view",c,"--log-failed"]).then(u=>vv(u)).catch(()=>null)),a.push({name:i.name,link:i.link||null,log:l})}return a}function Sv(t){return String(t||"").match(/\/actions\/runs\/(\d+)/)?.[1]??null}function vv(t,{maxLines:e=400,maxBytes:n=64*1024}={}){let r=String(t||""),o=r.split(`
|
|
67
87
|
`);return o.length>e&&(r=`\u2026 (${o.length-e} earlier lines truncated)
|
|
68
88
|
`+o.slice(-e).join(`
|
|
69
89
|
`)),Buffer.byteLength(r)>n&&(r=`\u2026 (truncated)
|
|
70
|
-
`+r.slice(-n)),r}function
|
|
71
|
-
`).find(n=>n.trim())||"gh failed"}var
|
|
72
|
-
|
|
73
|
-
`).
|
|
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()}
|
|
90
|
+
`+r.slice(-n)),r}function oe(t){return String(t?.stderr||t?.message||t||"").trim().split(`
|
|
91
|
+
`).find(n=>n.trim())||"gh failed"}var Av=6e3,Ks=new Map;function Vc(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 ch(t){let e=t?.workspace?.branch;if(!e)return;let n;try{n=await F(t)}catch{return}if(Vc(n)==null)return;let r=(t.boxes||[]).find(c=>c.name===t.leadBox);if(await Ns(t.leadBox,r?.agent).catch(()=>!1))return;let o=Date.now();if(o-(Ks.get(t.id)||0)<Av)return;Ks.set(t.id,o);let s;try{s=await oo(t.cwd,{caller:"reconcile"})}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 Pt({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 Gr(i,a.number))){Ks.delete(t.id);return}}await O(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(()=>{}),Ks.delete(t.id)}import lh from"node:fs/promises";import Vs from"node:path";import{randomUUID as Tv}from"node:crypto";var dh=".bostrat/uploads",Jc=t=>Vs.join(t,dh),uh=Number(process.env.BOSTRAT_MAX_UPLOAD_BYTES)||24*1024*1024,_v=new Set(["png","jpg","jpeg","gif","webp","svg","bmp","tif","tiff","heic","heif","avif","ico"]),Ev=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 zc(t){let e=String(t||"").toLowerCase().replace(/^\./,"");return _v.has(e)?"image":Ev.has(e)?"doc":null}function Iv(t){let r=Vs.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 fh(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>uh)throw new Error(`file too large \u2014 max ${Math.floor(uh/(1024*1024))} MB`);let r=Iv(e),o=Vs.extname(r).slice(1),s=zc(o);if(!s)throw new Error(`unsupported file type "${o||"unknown"}" \u2014 attach an image or a document`);let a=Tv().slice(0,8),i=Jc(t);await lh.mkdir(i,{recursive:!0});let c=`${a}-${r}`;return await lh.writeFile(Vs.join(i,c),n),{id:a,name:r,rel:`${dh}/${c}`,kind:s,size:n.length}}function ph(t){let e=t.map(r=>`\`${r}\``).join(", ");return`[Attached \u2014 please read ${t.length===1?"this file":`these ${t.length} files`}: ${e}]`}var Rv=/\[Attached — please read (?:this file|these \d+ files): [^\]]*\]$/;function Xc(t){return t.role==="user"&&Rv.test((t.text||"").trim())}function pr(t,e,n=Date.now()){return e?t.idleSince==null?(t.idleSince=n,!1):n-t.idleSince>=6500:(t.idleSince=null,!1)}var Cv=Ov($v),Ge=process.env.BOSTRAT_STATE_DIR||j.join(process.env.HOME||"/tmp",".bostrat"),xh=j.join(Ge,"tracks.json"),Mv=j.join(Ge,"events.jsonl"),nl=j.join(Ge,"workspaces"),Pv=process.env.BOSTRAT_DEFAULT_REPO||null,Xs={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"}},Ys=Object.keys(Xs);function Nv(t,{skipPermissions:e=!0}={}){let n=Xs[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 Sh(t,e,{skipPermissions:n=!0}={}){let r=Xs[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 ho(t){let e=Xs[t];if(e)return process.env[e.envVar]||e.base}var vn={at:0,value:null};async function ol({maxAgeMs:t=6e4}={}){if(vn.value&&Date.now()-vn.at<t)return vn.value;let e=await Promise.all(Ys.map(async n=>[n,await No(ho(n))]));return vn.value=Object.fromEntries(e),vn.at=Date.now(),vn.value}async function Zs(t,e=!0){let n=Nv(t,{skipPermissions:e});if(!await No(n)){vn.at=0;let r=n.split(/\s+/)[0],o=process.env.FLY_APP_NAME?"This Fly node's image predates CLI parity \u2014 update the node image (Links page \u2192 Fly card \u2192 Update) or re-provision.":$a[r]?`Install it on this node: ${$a[r]}`:"Its CLI must be installed and on PATH first.";throw new Error(`agent "${t}" isn't available on this box (command not found: ${r}). ${o}`)}return n}var Qs=".bostrat/handoff/latest.md";function Bv(t,e=Qs){return`${t.trim()}
|
|
92
|
+
|
|
93
|
+
(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 Dv=/\(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 mh(t){return t.role==="user"&&Dv.test((t.text||"").trim())}var Js=".bostrat/handoff/subtask-result.md",co=".bostrat/handoff/lead-context.md",jv=(t,e=!1)=>e?`You are a SUBTASK agent working in an isolated multi-repo workspace, delegated by a lead agent on a shared workstream. This directory is NOT itself a git repo: each subdirectory is an isolated git worktree of one of the workstream's repos, already seeded with the lead's full working tree for that repo. The conversation so far is summarized in ${co} \u2014 read that first for context. Your scoped task:
|
|
94
|
+
|
|
95
|
+
${t}
|
|
75
96
|
|
|
76
|
-
|
|
97
|
+
Work ONLY inside this directory. cd into the repo a change belongs to before running git commands, and commit per repo with a clear message (repos you don't touch need nothing). When you are finished: (1) make your code changes, (2) commit them in each changed repo, (3) LAST, write a short markdown digest to ${Js} (at this directory's root) with sections \u2014 Result (what you did), Files (paths you changed, prefixed with the repo directory), Notes (anything the lead must know). Writing that file is your DONE signal: do it last, then stop and take no further action.`:`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 ${co} \u2014 read that first for context. Your scoped task:
|
|
77
98
|
|
|
78
99
|
${t}
|
|
79
100
|
|
|
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 ${
|
|
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()}
|
|
101
|
+
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 ${Js} 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.`,Lv=(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."),ae=null,Yc=null;async function H(){return ae||(Yc||=(async()=>{try{ae=JSON.parse(await D.readFile(xh,"utf8"))}catch{ae={tracks:[]}}finally{Yc=null}return ae})(),Yc)}async function V(){await Xt(xh,ae)}var Z=(t,e)=>t.tracks.find(n=>n.id===e)||null,Zc=Promise.resolve();function Fv(t,e){let n=JSON.stringify({trackId:t,...e})+`
|
|
102
|
+
`;return Zc=Zc.then(()=>D.mkdir(Ge,{recursive:!0})).then(()=>D.appendFile(Mv,n)).catch(()=>{}),Zc}var ot=(t,e,n={})=>{let r={at:Date.now(),type:e,...n};return t.events.push(r),Fv(t.id,r),Qp(t,r),r};yu(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&&(ot(i,"codex-update",{box:t,from:e,to:r||n,status:o,...s?{error:s}:{}}),await V().catch(()=>{}))});var Qc=(t,e,n)=>e!=null&&n.some(r=>t===r.box&&e>=r.from&&e<=r.to),Uv=new Set(["stuck","wrong-approach","too-slow","want-different-model","context-refresh","cost","parallelize","other"]);function vh(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=>Uv.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 Ah(t,e,n,{kind:r}={}){let o=await z(e);if(!o)throw new Error(`box "${e}" is not running`);let s=ho(o.agent);await Te({cwd:o.cwd,sessionId:o.sessionId,sessionName:e,agent:o.agent,agentCmd:s}),await kt(e,n,{...le(o.agent),kind:r||"orchestration"}),await q({flow:"orchestration",phase:"sent",trackId:t,sessionName:e,agent:o.agent,kind:r||"orchestration",promptHead:n.slice(0,160)});let a=await H(),i=Z(a,t);if(!i)return;let c=i.boxes.find(l=>l.name===e);ot(i,"orchestration-prompt",{box:e,agent:c?.agent||o.agent,boxRole:c?.role,prompt:n,kind:r||"orchestration"}),await V()}var An=new Set(["merged","conflict","canceled","failed","handed-off"]),hh=new Map;async function Hv(t){let e=j.join(zo(t),`${t.id}.ndjson`),n;try{n=await D.stat(e)}catch{return null}let r=hh.get(t.id);if(r&&r.mtimeMs===n.mtimeMs)return r.at;let o=null;for(let s of await F(t)){if(s.type!=="turn.agent")continue;let a=s.payload?.ts??s.ts;a&&(!o||a>o)&&(o=a)}return hh.set(t.id,{mtimeMs:n.mtimeMs,at:o}),o}async function Wv(t,e){let n=e.find(r=>r.name===t.leadBox);return n?.state!=="live"?{working:!1,prompted:!1}:Ec(n.name,n.agent)}async function Ke(t){let e=new Map((await Nn()).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([Hv(t),Wv(t,n)]);return{...t,boxes:n,lastResponseAt:r,working:o,prompted:s}}var qv=5e3;function Gv(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-qv,f=r[u+1]?.createdAt??1/0,p=o.find(m=>!s.has(m.id)&&m.birthMs>=d&&m.birthMs<f);p&&(s.add(p.id),c.push({box:l.name,sessionId:p.id}))}return c}async function Th(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=fu(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=Gv(u,a,f=>i.has(f));for(let f of d){let p=u.find(m=>m.name===f.box);p&&(p.sessionId=f.sessionId),await nu(f.box,f.sessionId),n=!0}}return n}async function ut(){let t=await H();return await Th(t)&&await V(),Promise.all(t.tracks.map(Ke))}async function _h(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 et(t){let e=await H(),n=Z(e,t);return n?(await Th(e)&&await V(),Ke(n)):null}var Kv={claude:[/^\[Request interrupted by user(?: for tool use)?\]$/i],codex:[/^\[?Request interrupted/i,/^Conversation interrupted/i],cursor:[/^\[?Request interrupted/i]};function wh(t){if(t.role!=="user")return!1;let e=(t.text||"").trim();return(Kv[t.agent]||[]).some(n=>n.test(e))}var Vv=[/^\/model(?:\s|$)/i,/<command-(?:name|message|args)>[^<]*\bmodel\b/i,/^Model changed to\b/i];function gh(t){let e=(t.text||"").trim();return e?Vv.some(n=>n.test(e)):!1}var Jv=10*6e4;async function sl(t,e={}){let n=await et(t);if(!n)return null;let r=new Set([...n.events.filter(k=>k.type==="orchestration-prompt"&&k.prompt).map(k=>String(k.prompt).trim()),...n.events.filter(k=>k.type==="said"&&k.text).map(k=>String(k.text).trim())]),o=wo(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=oi(n.id),c=!!(i&&!i.finishedAt),l=c&&i.startedAt&&i.box?[{box:i.box,from:i.startedAt,to:Date.now()+1e12}]:[],u=[...Id(n),...l],f={windows:[...Hu(n),...Wu(n,Vo),...a,...u]},m=(await Promise.all(n.boxes.map(k=>bp(k,e)))).flat(),h=m.map(k=>{let W={kind:"message",...k};if(W.role==="assistant"&&(W.text||"").trim()&&W.tools?.length){let En=to(W.agent).cliTools,Oo=W.tools.filter(In=>!En.has(In));return{...W,tools:Oo.length?Oo:void 0}}return W}).map(k=>{if(k.role!=="user")return k;let W=Ds(k.text);return W?W.kind==="suppress"?null:{...k,note:W,text:Dm(W)}:k}).filter(Boolean).filter(k=>!(k.role==="user"&&r.has((k.text||"").trim()))).filter(k=>!wh(k)).filter(k=>!gh(k)).filter(k=>!mh(k)).filter(k=>!Xc(k)).filter(k=>k.role!=="assistant"||!!(k.text||"").trim()||!!k.images?.length).filter(k=>!qu(k,f)),b=h.filter(k=>k.failed&&k.text===tc);if(b.length){let k=new Map;for(let W of b){if(!k.has(W.box)){let In=[...(await wt(W.box,{lines:_t}).catch(()=>"")).matchAll(/^\s*■\s+(.+?)\s*$/gm)].map(xa=>xa[1]);k.set(W.box,In.length?In[In.length-1]:null)}let En=k.get(W.box);En&&(W.text=`\u25A0 ${En}`)}}let x=await Promise.all(n.boxes.map(k=>xp(k,e)));await Nm(n,x.flat()),await Mm(n,h),await tm(n),await Pd(n,h).catch(()=>{});for(let k of n.boxes)k.agent==="codex"&&k.state==="live"&&await Fm(n,k),k.agent==="cursor"&&k.state==="live"&&await Gm(n,k);let v=await Promise.all(n.boxes.map(k=>kp(k,e)));await Pm(n,v.flat().filter(k=>!Qc(k.box,k.ts,u)));let E=await F(n),R=c&&i.viewId?i.viewId:null,st=dm(E),I=js(E).filter(k=>!(k.kind==="message"&&(wh(k)||gh(k)||mh(k)||Xc(k)))&&!(k.kind==="view-card"&&R&&k.viewId===R)),L=fm(I,st),T=I.filter(k=>!(k.kind==="message"&&k.role==="assistant"&&!L.has(k)&&Qc(k.box,k.ts,st))),U=Pc(E,T).map(k=>({kind:"command-batch",id:k.id,ts:k.pending?k.ts:(k.anchorTs??k.ts)-1,anchorTs:k.anchorTs,anchorBox:k.anchorBox,agent:k.agent,boxRole:k.boxRole,commands:k.commands,pending:k.pending})).filter(k=>!Qc(k.anchorBox,k.anchorTs??k.ts,u)),J=[...T,...U].filter(k=>k.ts!=null).sort((k,W)=>k.ts-W.ts),g=zv(n),w=g.size?J.filter(k=>k.kind==="event"||!g.has(k.box??k.anchorBox)):J,y=n.boxes.find(k=>k.name===n.leadBox)||null,A=y?await Cr(y.cwd,y.sessionId,{fallbackNewest:!1}):null;if(!A&&y){let k=[...E].reverse().find(W=>W.type==="context.usage"&&W.boxId===y.name);k?.payload?.window&&(A={pct:k.payload.pct??(k.payload.occupancy!=null?k.payload.occupancy/k.payload.window:null),occupancy:k.payload.occupancy??null,window:k.payload.window,windowKnown:!0,model:k.payload.model||null})}let K=[...m].reverse().find(k=>k.role==="assistant"&&k.model),B=K?.model||null,$=[...n.events||[]].reverse().find(k=>k.type==="model-change"),_=A?.model||B;$&&(!K?.ts||$.at>=K.ts)&&(_=$.to);let M=y?await wt(y.name,{lines:_t}).catch(()=>""):"",at=y&&sr(y.agent,M)?await ve(y.name).catch(()=>M):M,Q=y?bn(y,at):null;Q&&em(n,y,Q);let ht=!!y&&!Q&&jn(y.agent,M);y&&!y.readyAt&&(ht||Q||M&&Wo(y.agent,M))&&(y.readyAt=Date.now(),await V());let Tt=!!y&&!y.readyAt&&Date.now()-(n.createdAt||0)<Jv;if(n.kind==="setup"&&n.setup?.provider&&!n.setup.connectedAt&&y&&Wo(y.agent,M)&&!Q){let k=await H(),W=Z(k,n.id);W?.setup&&!W.setup.connectedAt&&(W.setup.connectedAt=Date.now(),n.setup.connectedAt=W.setup.connectedAt,await O(W,{type:"provider.connected",actor:"system",source:"pane",boxId:y.name,provider:y.agent,payload:{provider:y.agent,ts:W.setup.connectedAt}}).catch(()=>{}),await V())}let Dt=s?{running:!0,step:o.step,toAgent:o.toAgent||null}:null,Ht=null;if(c){let k=Date.now()-i.startedAt;if(i.viewId&&!ht){let W=ns(n.id);W&&(ot(n,"view-spinup",{box:i.box,startedAt:i.startedAt,endedAt:W.finishedAt}),await V())}else if(Q||k>_d&&!ht&&!i.viewId||k>ri){let W=Q?"needs-input":k>ri?"timeout":"no-view";ns(n.id,{error:W})&&await O(n,{type:"view.spinup.failed",actor:"orchestrator",source:"orchestrator",boxId:i.box,provider:y?.agent||null,payload:{ts:i.startedAt,name:i.name,reason:W}}).catch(()=>{})}else Ht={running:!0,step:i.step,name:i.name}}return{trackId:n.id,leadBox:n.leadBox,items:w,context:A?{pct:A.pct,occupancy:A.occupancy,window:A.window,windowKnown:A.windowKnown,model:A.model}:null,models:{current:_,options:Jp(y?.agent)},pendingPrompt:Q,working:ht,leadStarting:Tt,handoff:Dt,view:Ht,queued:Yv(n.id).filter(k=>k.kind!=="ship-notify")}}function zv(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 Eh(t,e){let n=await et(t);if(!n)return null;let r=br(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 F(n),a=js(s).filter(d=>d.box===o),i=Pc(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 wt(o,{lines:_t}).catch(()=>"");u=jn(l.agent||r.agent,d)}return{items:c,working:u,box:o,agent:r.agent,task:r.task}}async function al(t,{promptId:e,answers:n,text:r}={}){let o=Z(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 z(o.leadBox))throw new Error(`lead box "${o.leadBox}" is not running`);let i=await fc(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 kt(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 bt(o.leadBox,["Escape"],{kind:"answer-prompt"}),c={mode:"keys",value:"Escape",count:1};else if(i.provider==="claude"||i.answerMode==="keys"){let f=Cp(i,n||[]);await bt(o.leadBox,f,{kind:"answer-prompt"}),c={mode:"keys",value:f.join(" "),count:f.length}}else{let f=Tu(i,n||[]);await kt(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 q({flow:"prompt",phase:"answered",sessionName:o.leadBox,agent:s.agent,provider:i.provider,promptId:i.promptId,...c}).catch(()=>{}),nm(o,s,i,c),{ok:!0,promptId:i.promptId,sent:c.count}}async function Ih(t,{promptId:e,questionIndex:n}={}){let r=Z(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 z(r.leadBox))throw new Error(`lead box "${r.leadBox}" is not running`);let a=await fc(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=Mp(a,n);return c.length?(await bt(r.leadBox,c,{kind:"navigate-prompt"}),await q({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 Xv=1500,lo=new Map;function il(t){let e=lo.get(t);return e||(e={items:[],timer:null,flushing:!1,idleSince:null},lo.set(t,e)),e}function Yv(t){return(lo.get(t)?.items??[]).map(e=>({...e}))}function ta(t){let e=lo.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 Rh(t){let e=il(t);e.timer||(e.timer=setInterval(()=>Zv(t).catch(()=>{}),Xv))}async function Zv(t){let e=lo.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=Z(await H(),t);if(!n||n.archivedAt){ta(t);return}let r=wo(t);if(r&&!r.finishedAt&&r.step!=="done"&&!r.error){pr(e,!1);return}let o=await z(n.leadBox);if(!o){pr(e,!1);return}let s=n.boxes.find(c=>c.name===n.leadBox),a=await wt(n.leadBox,{lines:_t}).catch(()=>"");if(Ho(s?.agent,a)){pr(e,!1);return}if(sr(s?.agent,a)){let c=await ve(n.leadBox).catch(()=>a);if(bn(s,c)){pr(e,!1);return}}if(!pr(e,!0))return;let i=e.items.shift();e.idleSince=null,await $h(n,s,o,i.text,i.kind??null,i.attachments??null),await V()}finally{e.flushing=!1}}}async function $h(t,e,n,r,o=null,s=null){let a=r,i=!1;t.pendingHandoffHydrate&&(a=Bv(r,t.pendingHandoffHydrate.artifact||Qs),delete t.pendingHandoffHydrate,i=!0);let c=Array.isArray(s)?s.filter(u=>u&&u.rel):[];if(c.length){let u=ph(c.map(d=>d.rel));a=a.trim()?`${a.trim()}
|
|
82
103
|
|
|
83
|
-
${
|
|
104
|
+
${u}`:u}let l=ot(t,"said",{box:t.leadBox,agent:e?.agent,boxRole:e?.role,text:r,...o?{kind:o}:{},...c.length?{attachments:c}:{}});delete t.draft;try{await Te({cwd:n.cwd,sessionId:n.sessionId,sessionName:t.leadBox,agent:n.agent,agentCmd:ho(n.agent)}),await kt(t.leadBox,a,{...le(e?.agent),kind:i?"lead-handoff-first-prompt":"say"})}catch(u){throw ot(t,"unsaid",{box:t.leadBox,agent:e?.agent,boxRole:e?.role,saidTs:l.at,text:r}),u}i&&await q({flow:"lead-handoff",phase:"first-prompt",trackId:t.id,sessionName:t.leadBox,agent:e?.agent,userTextLen:r.length})}function Oh(t,{actions:e}={}){il(t).items.push({id:mo(),text:pm(e),ts:Date.now(),kind:"ship-notify"}),Rh(t)}async function ye(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"||!ar(e))&&!o.length)throw new Error("text (string) or an attachment required");let s=Z(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 z(s.leadBox);if(!a&&await dl(s)&&(await V(),a=await z(s.leadBox)),!a)throw new Error(`lead box "${s.leadBox}" is not running`);let i=s.boxes.find(f=>f.name===s.leadBox),c=ar(e),l=il(t),u=await wt(s.leadBox,{lines:_t}).catch(()=>""),d=l.items.length>0||Ho(i?.agent,u);if(!d&&sr(i?.agent,u)){let f=await ve(s.leadBox).catch(()=>u);d=!!bn(i,f)}if(d){let f={id:mo(),text:c,ts:Date.now(),...n?{kind:n}:{},...o.length?{attachments:o}:{}};return l.items.push(f),Rh(t),delete s.draft,await V(),{ok:!0,leadBox:s.leadBox,queued:!0,id:f.id,position:l.items.length}}return await $h(s,i,a,c,n,o),await V(),{ok:!0,leadBox:s.leadBox}}var yh="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 Ch(t,{hint:e}={}){let n=Z(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=oi(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";Ed(n.id,{name:s,box:n.leadBox});let a=String(e||"").trim()?`${yh}
|
|
84
105
|
|
|
85
|
-
What to preview: ${String(e).trim()}`:
|
|
106
|
+
What to preview: ${String(e).trim()}`:yh;try{await ye(t,a,{kind:"spin-up-view"})}catch(i){throw ns(n.id,{error:"drive-failed"}),i}return{ok:!0,running:!0}}async function cl(t){let e=Z(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(c=>c.name===e.leadBox),r=ta(t),o=await z(e.leadBox),s=o?await eA(e.leadBox,n?.agent):{stopped:!1,sends:0},a=o&&s.stopped?await oA(e,n):null,i=o?await nA(e).catch(()=>null):null;return await q({flow:"interrupt",phase:"sent",trackId:t,sessionName:e.leadBox,agent:n?.agent,dropped:r.length,escSends:s.sends,stopped:s.stopped,unsent:!!a,shipDismissed:!!i}).catch(()=>{}),{ok:!0,interrupted:!!o,stopped:s.stopped,dropped:r.length,restored:a}}var Qv=[300,350,400],tA=3;async function eA(t,e){let n=0;for(let r=0;r<tA;r++){await bt(t,["Escape"],{kind:"interrupt"}),n++;for(let o of Qv){await na(o);let s=await wt(t,{lines:_t}).catch(()=>"");if(!jn(e,s))return{stopped:!0,sends:n}}}return{stopped:!1,sends:n}}async function nA(t){let e=Vc(await F(t));return e?.offerId?(await O(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 rA=[250,400,500,600];async function oA(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 rA){await na(r);let o=await wt(t.leadBox,{lines:_t}).catch(()=>""),s=pc(e.agent,o);if(s&&Np(n.text,s))return await sA(t.leadBox,e.agent,n.text.length+8),t.draft={text:n.text,updatedAt:Date.now()},ot(t,"unsaid",{box:t.leadBox,agent:e.agent,boxRole:e.role,saidTs:n.at,text:n.text}),await V(),n.text}return null}async function sA(t,e,n){for(let r=0;r<3;r++){await Do(t,n,{kind:"unsend-wipe"}).catch(()=>{}),await na(200);let o=await wt(t,{lines:_t}).catch(()=>""),s=pc(e,o);if(!s)return!0;n=s.length+8}return!1}async function Mh(t,e){if(typeof e!="string")throw new Error("text (string) required");let n=Z(await H(),t);if(!n)throw new Error(`track "${t}" not found`);let r=ar(e);return r?n.draft={text:r,updatedAt:Date.now()}:delete n.draft,await V(),{ok:!0,draft:n.draft||null}}async function Ph(t,e){if(typeof e!="string"||!e.trim())throw new Error("model (string) required");let n=e.trim(),r=Z(await H(),t);if(!r)throw new Error(`track "${t}" not found`);let o=await z(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=zp(a,n),l=[...r.events||[]].reverse().find(d=>d.type==="model-change")?.to??null;l||(l=(await Cr(o.cwd,o.sessionId,{fallbackNewest:!1}).catch(()=>null))?.model??null),await Te({cwd:o.cwd,sessionId:o.sessionId,sessionName:r.leadBox,agent:o.agent,agentCmd:ho(o.agent)});let u=await Xp(a,r.leadBox,i);return ot(r,"model-change",{to:n,from:l,box:r.leadBox,agent:a,verified:!!u.verified}),await V(),{ok:!0,requested:n,leadBox:r.leadBox,...u}}async function Nh(t){for(let e of t){if(!e||!await _s(e))continue;let n=await Dp(e).catch(()=>null);if(!n)continue;if((await D.stat(j.join(n,".git")).catch(()=>null))?.isDirectory())return n}return null}function $t(t){let e=t;return e?e.kind==="multi"?Array.isArray(e.repos)?e.repos.filter(Boolean):[]:e.base?[{name:j.basename(e.base),dir:e.dir??null,base:e.base,baseRef:e.baseRef??null,repo:e.repo??null}]:[]:[]}async function no(t){let e=[];for(let n of $t(t?.workspace)){if(!n?.dir)continue;let r=await yc(n.dir,{ignorePlumbing:!0}).catch(()=>!1),o=await bc(n.dir,n.baseRef).catch(()=>0);(r||o>0)&&e.push({...n,dirty:r,ahead:o})}return e}async function Bh(t){let e=t;typeof e=="string"&&!j.isAbsolute(e)&&/^[^/\s]+\/[^/\s]+$/.test(e)&&(e=await Hi(e));let n=await Nh([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 aA(t,e,n,r){let o=`track/${ml(e)}-${t}`,s=[];try{await D.mkdir(n,{recursive:!0});let a=new Set([".bostrat"]);for(let i of r){let c=await Bh(i);if(s.some(p=>p.base===c))throw new Error(`repo tagged twice: ${i}`);let l=await Y(c).catch(()=>null),u=j.basename(c);a.has(u)&&l&&(u=l.replace("/","-"));for(let p=2;a.has(u);p++)u=`${j.basename(c)}-${p}`;a.add(u);let d=j.join(n,u),f=await Es(c);await ir(c,d,o,f),s.push({name:u,dir:d,base:c,baseRef:f,repo:l})}return await ea(n,s,o),{dir:n,kind:"multi",branch:o,repos:s}}catch(a){for(let i of s)await Ft(i.base,i.dir).catch(()=>{});throw await D.rm(n,{recursive:!0,force:!0}).catch(()=>{}),a}}var Dh=t=>({branch:t.branch,kind:t.kind,repos:(t.repos??[]).filter(Boolean).map(({name:e,base:n,baseRef:r,repo:o})=>({name:e,base:n,baseRef:r,repo:o}))});async function jh(t,e){let n=await H(),r=Z(n,t);if(!r)throw new Error(`track "${t}" not found`);if(r.archivedAt)throw new Error("track is archived \u2014 unarchive it first");let o=r.workspace;if(o?.kind!=="multi")throw new Error("live re-tagging needs a multi-repo Track \u2014 a single-repo Track's workspace IS its repo checkout; create the Track with multiple repos tagged instead");let s=await Bh(typeof e=="string"?e.trim():e);if((o.repos??[]).some(f=>f?.base===s))throw new Error(`repo already tagged: ${e}`);let a=await Y(s).catch(()=>null),i=new Set([".bostrat",...(o.repos??[]).map(f=>f?.name).filter(Boolean)]),c=j.basename(s);i.has(c)&&a&&(c=a.replace("/","-"));for(let f=2;i.has(c);f++)c=`${j.basename(s)}-${f}`;let l=j.join(o.dir,c),u=await Es(s);await jp(s,o.branch)?await Lp(s,l,o.branch):await ir(s,l,o.branch,u);let d={name:c,dir:l,base:s,baseRef:u,repo:a};return o.repos=[...(o.repos??[]).filter(Boolean),d],await ea(o.dir,o.repos,o.branch),ot(r,"retagged",{action:"add",repo:a??s,name:c,workspace:Dh(o)}),await V(),d}async function Lh(t,e){let n=await H(),r=Z(n,t);if(!r)throw new Error(`track "${t}" not found`);let o=r.workspace;if(o?.kind!=="multi")throw new Error("live re-tagging needs a multi-repo Track");let s=(o.repos??[]).find(c=>c&&(c.name===e||c.repo===e||c.base===e));if(!s)throw new Error(`repo "${e}" is not tagged on this Track`);if((o.repos??[]).filter(Boolean).length<=1)throw new Error("can't untag the last repo \u2014 archive or delete the Track instead");for(let c of Kt.values())if(c.trackId===t&&!c.finishedAt)throw new Error("a subtask is still running on this Track \u2014 wait for it to merge back before untagging");let a=await yc(s.dir,{ignorePlumbing:!0}).catch(()=>!1),i=await bc(s.dir,s.baseRef).catch(()=>0);if(a||i>0)throw new Error(`"${s.name}" has work (${a?"uncommitted changes":`${i} commit${i===1?"":"s"} ahead of ${s.baseRef??"base"}`}) \u2014 ship or discard it first`);return await Ft(s.base,s.dir).catch(()=>{}),await D.rm(s.dir,{recursive:!0,force:!0}).catch(()=>{}),o.repos=(o.repos??[]).filter(c=>c&&c!==s),await ea(o.dir,o.repos,o.branch),ot(r,"retagged",{action:"remove",repo:s.repo??s.base,name:s.name,workspace:Dh(o)}),await V(),{removed:s.name}}async function iA(t,e,{repo:n,repos:r,cwd:o}={}){await D.mkdir(nl,{recursive:!0});let s=j.join(nl,t),a=Array.isArray(r)?r.map(c=>typeof c=="string"?c.trim():"").filter(Boolean):[];if(a.length>=2)return aA(t,e,s,a);a.length===1&&!n&&(n=a[0]),typeof n=="string"&&!j.isAbsolute(n)&&/^[^/\s]+\/[^/\s]+$/.test(n)&&(n=await Hi(n));let i=await Nh([n,o,Pv]);if(i){let c=`track/${ml(e)}-${t}`;try{let l=await Es(i);await ir(i,s,c,l),await rl(s);let u=await Y(i).catch(()=>null);return{dir:s,base:i,branch:c,kind:"worktree",baseRef:l,repo:u}}catch{await D.rm(s,{recursive:!0,force:!0}).catch(()=>{})}}return await D.mkdir(s,{recursive:!0}),await Fp(s),await rl(s),{dir:s,base:null,branch:null,kind:"repo"}}var uo="# bostrat workspace",io=`${uo}
|
|
86
107
|
|
|
87
108
|
When asked to run, preview, or demo this app so the operator can review it: do NOT
|
|
88
109
|
start an ad-hoc dev server and do NOT share a localhost URL (the operator is on
|
|
@@ -154,6 +175,22 @@ configurable ticket automations \u2014 if asked to automate or suppress a ticket
|
|
|
154
175
|
behavior that isn't one of them, say it isn't configurable yet instead of
|
|
155
176
|
working around it. Never move the issue or edit its status by other means.
|
|
156
177
|
|
|
178
|
+
When the operator asks you to CREATE tracker work from scoping ("make tickets
|
|
179
|
+
for this", "we should have a project + issues for each item", "do the task
|
|
180
|
+
ceremony"), do NOT create issues by other means \u2014 write the plan as JSON and run:
|
|
181
|
+
|
|
182
|
+
bostrat-ticket propose <plan.json>
|
|
183
|
+
|
|
184
|
+
Plan shape: \`{ "epic": {"name","description"}, "issues":
|
|
185
|
+
[{"title","description","priority"}], "team": "KEY", "linkCurrentTrack": true }\`
|
|
186
|
+
(epic optional; the \`bostrat-ticket\` header documents the details). This posts a
|
|
187
|
+
one-tap review card in the operator's chat; NOTHING is created until they
|
|
188
|
+
confirm. Proposing is a BLOCKING prompt: once the CLI reports the card was
|
|
189
|
+
posted, STOP and end your turn \u2014 do not start (or resume) implementation, even
|
|
190
|
+
under an earlier go-ahead, until the operator's decision on the card comes back
|
|
191
|
+
to you as a chat message. A ticket ask mid-build means they want the tickets
|
|
192
|
+
agreed BEFORE the build continues.
|
|
193
|
+
|
|
157
194
|
When you FINISH a piece of work and steps remain that ONLY the operator can
|
|
158
195
|
perform (vendor/app registrations, secrets to set, interactive logins, device or
|
|
159
196
|
cert installs, DNS changes, subscriptions, physical steps), write those steps as
|
|
@@ -178,6 +215,23 @@ add a near-duplicate. When every step is confirmed, run
|
|
|
178
215
|
than creating another. Runbooks are read-only for the operator: they act and
|
|
179
216
|
report back in chat; you are the only writer.
|
|
180
217
|
|
|
218
|
+
When something happens that the operator would want to know about RIGHT NOW and
|
|
219
|
+
they are plausibly away from the screen, you can reach their phone:
|
|
220
|
+
|
|
221
|
+
bostrat-notify "<message>" [--title "<title>"] [--urgent]
|
|
222
|
+
|
|
223
|
+
Use this SPARINGLY and only for: long work finishing, a real blocker that needs
|
|
224
|
+
their decision, or a failure that wastes their time if it goes unnoticed. NEVER
|
|
225
|
+
for progress updates, never to announce that you are starting, and never more
|
|
226
|
+
than once per task unless the situation genuinely changed. If they are plausibly
|
|
227
|
+
watching the chat, just say it in chat \u2014 the chat is ALWAYS the default and a
|
|
228
|
+
notification is the exception. Write the message so it stands alone on a lock
|
|
229
|
+
screen (they cannot see the chat from there): say what happened and what it
|
|
230
|
+
means, not "done" or "see chat". Never put a secret value in one \u2014 notifications
|
|
231
|
+
travel off-box, and the server rejects token-shaped content. Anything worth
|
|
232
|
+
notifying about belongs in your chat message too; the notification is a nudge
|
|
233
|
+
toward the chat, never the only record.
|
|
234
|
+
|
|
181
235
|
When you FINISH a piece of implementation work (feature built, bug fixed, checks
|
|
182
236
|
green) and the operator has not already told you to ship it: do NOT commit, push,
|
|
183
237
|
or open a PR on your own. Instead END your final message with a short, explicit
|
|
@@ -188,9 +242,9 @@ which steps to run (and which to skip). An offer buried mid-message, phrased as
|
|
|
188
242
|
statement, or omitted entirely renders no controls \u2014 so close EVERY completed
|
|
189
243
|
implementation turn this way, every time. When the operator has already asked you
|
|
190
244
|
to commit / push / open the PR, just do it; do not re-offer.
|
|
191
|
-
`;async function
|
|
192
|
-
`)),
|
|
193
|
-
`);return`${
|
|
245
|
+
`;async function rl(t){try{await D.writeFile(j.join(t,"AGENTS.md"),io,{flag:"wx"})}catch{return}await uA(t,["/AGENTS.md"])}var tl=t=>typeof t=="string"&&(t===uo||t.startsWith(`${uo}
|
|
246
|
+
`)),cA="# bostrat multi-repo workspace";function lA(t,e){let n=t.map(r=>`- \`${r.name}/\`${r.repo?` \u2014 github.com/${r.repo}`:""}${r.baseRef?` (forked from ${r.baseRef})`:""}`).join(`
|
|
247
|
+
`);return`${cA}
|
|
194
248
|
|
|
195
249
|
This directory is NOT a git repo. It is a bostrat Track workspace holding one
|
|
196
250
|
git worktree per tagged repo, ALL on the same branch \`${e}\`:
|
|
@@ -202,22 +256,25 @@ push / open PRs) in each repo separately, always on that shared branch. Work
|
|
|
202
256
|
only needs to land in the repos it actually touches \u2014 an untouched repo's
|
|
203
257
|
branch simply stays at its fork point and never gets pushed. When asked to run
|
|
204
258
|
or preview an app, run it from that app's own repo directory.
|
|
205
|
-
`}async function
|
|
206
|
-
`,`${
|
|
259
|
+
`}async function ea(t,e,n){let r=lA(e,n);for(let o of["CLAUDE.md","AGENTS.md"])await D.writeFile(j.join(t,o),r).catch(()=>{})}var el=io.replace(`${uo}
|
|
260
|
+
`,`${uo}
|
|
207
261
|
|
|
208
262
|
(Global copy managed by bostrat \u2014 rewritten on service start; edits will be lost.)
|
|
209
263
|
SCOPE: these instructions apply ONLY when your working directory is under
|
|
210
264
|
~/.bostrat/workspaces/ or ~/.bostrat/worktrees/ \u2014 a bostrat-orchestrated box.
|
|
211
265
|
When working anywhere else, ignore everything below.
|
|
212
|
-
`);async function
|
|
213
|
-
`).includes(s));o.length&&await
|
|
266
|
+
`);async function Fh(t){let{stdout:e}=await Cv("git",["-C",t,"rev-parse","--git-path","info/exclude"]);return j.resolve(t,e.trim())}async function uA(t,e){try{let n=await Fh(t);await D.mkdir(j.dirname(n),{recursive:!0});let r=await D.readFile(n,"utf8").catch(()=>""),o=e.filter(s=>!r.split(`
|
|
267
|
+
`).includes(s));o.length&&await D.appendFile(n,(r.endsWith(`
|
|
214
268
|
`)||!r?"":`
|
|
215
269
|
`)+o.join(`
|
|
216
270
|
`)+`
|
|
217
|
-
`)}catch{}}async function
|
|
271
|
+
`)}catch{}}async function dA(t,e){try{let n=await Fh(t),r=await D.readFile(n,"utf8"),o=r.split(`
|
|
218
272
|
`).filter(s=>s!==e);o.length!==r.split(`
|
|
219
|
-
`).length&&await
|
|
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}
|
|
273
|
+
`).length&&await D.writeFile(n,o.join(`
|
|
274
|
+
`))}catch{}}async function Uh(){let t={refreshed:0,removed:0,codexGlobal:null};try{await D.mkdir(Ge,{recursive:!0});for(let r of["CLAUDE.md","AGENTS.md"])await D.writeFile(j.join(Ge,r),io).catch(()=>{});let e=process.env.CODEX_HOME||j.join(process.env.HOME||"/tmp",".codex");if((await D.stat(e).catch(()=>null))?.isDirectory()){let r=j.join(e,"AGENTS.md"),o=await D.readFile(r,"utf8").catch(()=>null);o===null?(await D.writeFile(r,el,{flag:"wx"}).catch(()=>{}),t.codexGlobal="created"):tl(o)&&o!==el&&(await D.writeFile(r,el).catch(()=>{}),t.codexGlobal="refreshed")}for(let r of[nl,wr]){let o=await D.readdir(r,{withFileTypes:!0}).catch(()=>[]);for(let s of o){if(!s.isDirectory())continue;let a=j.join(r,s.name),i=j.join(a,"AGENTS.md"),c=await D.readFile(i,"utf8").catch(()=>null);c!==null&&tl(c)&&c!==io&&(await D.writeFile(i,io).catch(()=>{}),t.refreshed++);let l=j.join(a,"CLAUDE.md"),u=await D.readFile(l,"utf8").catch(()=>null);u!==null&&tl(u)&&(await D.rm(l,{force:!0}).catch(()=>{}),await dA(a,"/CLAUDE.md"),t.removed++)}}}catch{}return t}async function fA(t){let e=t.workspace;if(!(!e||!e.dir))try{if(e.kind==="multi"){for(let n of $t(e))n.base&&n.dir&&await Ft(n.base,n.dir).catch(()=>{});await D.rm(e.dir,{recursive:!0,force:!0})}else e.kind==="worktree"&&e.base?await Ft(e.base,e.dir):await D.rm(e.dir,{recursive:!0,force:!0})}catch{}}async function ll({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 Zs(o,s),i=await H(),c=mo().slice(0,8),l=await iA(c,t,{repo:n,repos:r,cwd:e}),u=new Set(i.tracks.flatMap(m=>(m.boxes||[]).map(h=>h.name))),d=`${t.trim()} lead`;u.has(Oa(d))&&(d=`${t.trim().slice(0,50).trim()} ${c} lead`);let f=await Qe(d,l.dir,{agentCmd:a,agent:o});qo({cwd:f.cwd,sessionName:f.name,agent:o,agentCmd:a});let p={id:c,name:t.trim(),cwd:f.cwd,workspace:l,leadBox:f.name,boxes:[Yh(f,"lead","live")],events:[],createdAt:Date.now()};return ot(p,"opened",{agent:o,box:f.name,skipPermissions:s,name:p.name,cwd:p.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:h,baseRef:b,repo:x})=>({name:m,base:h,baseRef:b,repo:x}))}:{}}}),i.tracks.push(p),await V(),Ke(p)}var pA=new Set(["claude","codex","cursor"]);async function Hh({provider:t="claude",fresh:e=!1}={}){if(!pA.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 Ke(r);if(u.boxes.find(f=>f.name===u.leadBox)?.state==="live")return u}await yr(r.id).catch(()=>{})}let o=(await Zs(t,!1)).replace(/\s+--(?:dangerously-skip-permissions|dangerously-bypass-approvals-and-sandbox)\b/g,""),s=mo().slice(0,8),a=j.join(Ge,"setup",s);await D.mkdir(a,{recursive:!0});let i=await Qe(`Connect ${t} ${s}`,a,{agentCmd:o,agent:t,firstRun:!0});t==="codex"&&qo({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:[Yh(i,"lead","live")],events:[],createdAt:Date.now()};return ot(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 V(),Ke(c)}async function Wh(t,e,n){await Md(t).catch(()=>{});let r=fo.get(t);r&&!r.finishedAt&&(r.error=n,r.finishedAt=Date.now()),fo.delete(t),e.handoff&&!e.handoff.finishedAt&&(e.handoff.step="error",e.handoff.error=n,e.handoff.finishedAt=Date.now());for(let a of br(t))a.finishedAt||(a.canceled=!0,a.finishedAt=Date.now()),await wl(a).catch(()=>{}),Kt.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 Jt(a.name).catch(()=>{})}async function qh(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 wd(s),await Wh(t,s,"track deleted"),n?await Gd(t).catch(()=>{}):await qd(t).catch(()=>{}),await Zp(),await fA(s),e&&await gd(t).catch(()=>{}),ot(s,"track-deleted",{name:s.name,boxes:s.boxes.length,ageMs:s.createdAt?Date.now()-s.createdAt:null}),r.tracks.splice(o,1),await V(),{ok:!0,id:t,name:s.name,boxes:s.boxes.length}}async function yr(t){let e=await H(),n=Z(e,t);if(!n)throw new Error(`track "${t}" not found`);if(n.archivedAt)throw new Error(`track "${t}" is already archived`);return n.archivedAt=Date.now(),ot(n,"archived",{boxes:n.boxes.length}),await V(),n.kind==="setup"?await Gh(t):hA(t).catch(()=>{}),Ke(n)}var mA=9e4;async function hA(t){try{await wA(t)}catch{}await Gh(t).catch(()=>{})}async function Gh(t){let e=await H(),n=Z(e,t);if(!(!n||!n.archivedAt)){await Wh(t,n,"track archived");for(let r of n.boxes)An.has(r.state)||(r.state="stopped");await V()}}async function wA(t){let e=await H(),n=Z(e,t);if(!n||!n.archivedAt)return;let r=null,o=[],s="derived",a=(n.events||[]).some(l=>l.type==="said"),i=n.leadBox?await z(n.leadBox).catch(()=>null):null;if(i?.cwd&&a)try{({text:r,items:o}=await Uu(n.leadBox,i.cwd,{timeoutMs:mA})),s="lead-cli"}catch{}if(r||(r=Mi(n,await F(n).catch(()=>[]))),!r)return;let c={text:r,source:s,at:Date.now()};o.length&&(c.items=o),n.beat=c,await O(n,{type:"track.summary.recorded",actor:"orchestrator",source:"orchestrator",boxId:n.leadBox||null,provider:i?.agent||null,payload:o.length?{text:r,items:o,source:s}:{text:r,source:s}}).catch(()=>{}),await V()}async function Kh(){let t=await H(),e=0,n=0,r=0;for(let o of t.tracks){if(!o.archivedAt||o.kind==="setup"||o.beat?.text){n++;continue}let s=Mi(o,await F(o).catch(()=>[]));if(!s){r++;continue}o.beat={text:s,source:"derived",at:Date.now()},await O(o,{type:"track.summary.recorded",actor:"orchestrator",source:"orchestrator",payload:{text:s,source:"derived",backfill:!0}}).catch(()=>{}),e++}return await V(),{recorded:e,skipped:n,empty:r}}async function Ue(t,e){let n=await vt(t,e).catch(()=>null);if(!n?.archiveOnMerge||!n.trackId)return!1;let r=await Vh(n);if(r.length){for(let o of r)await Yn(o.repo,o.number,!0).catch(()=>{});return!1}try{return await yr(n.trackId),!0}catch(o){if(/already archived/i.test(String(o?.message||o)))return!1;throw o}}async function Vh(t){return(await Me().catch(()=>[])).filter(n=>n.trackId===t.trackId&&n.state!=="merged"&&n.state!=="closed"&&n.state!=="lost"&&!(n.repo===t.repo&&n.number===t.number))}async function He(t,e){return ul(t,e,"merged")}async function ul(t,e,n="merged"){let r=await vt(t,e).catch(()=>null);if(!r?.notifyOnMerge||r.archiveOnMerge||!r.trackId)return!1;let o={repo:r.repo??null,number:r.number,title:r.title??null,url:r.url??null,headRef:r.headRef??null,outcome:n},s=await Vh(r);if(s.length){let i=[...r.notifyPending||[],o];for(let c of s)await $f(c.repo,c.number,i).catch(()=>{});return!1}let a=[...r.notifyPending||[],o];try{return await ye(r.trackId,gA(a),{kind:"merge-notify"}),!0}catch{return!1}}var bh={merged:"merged",closed:"closed on GitHub WITHOUT merging",lost:"no longer found on GitHub (deleted, or its repo left the grant)"};function gA(t){let e=i=>`${i.repo?`${i.repo}`:""}#${i.number}${i.title?` "${i.title}"`:""}${i.url?` (${i.url})`:""}`;if(t.length===1){let i=t[0],c=`#${i.number}${i.title?` "${i.title}"`:""}`;return i.outcome!=="merged"?`PR ${c}${i.url?` (${i.url})`:""}${i.headRef?` \u2014 branch ${i.headRef} \u2014`:""} was ${bh[i.outcome]??i.outcome}. This is an automated notification: the work did NOT land \u2014 check whether it still needs to ship another way. If not, say so briefly.`:`PR ${c}${i.url?` (${i.url})`:""}${i.headRef?` \u2014 branch ${i.headRef} \u2014`:""} has merged. This is an automated merge notification: pick up whatever follow-up work this merge unblocks (dependent tasks, cleanup, next steps that were waiting on it). If nothing needs doing, say so briefly.`}let n=t.filter(i=>i.outcome==="merged"),r=t.filter(i=>i.outcome!=="merged"),o=r.length?`${n.length} of the ${t.length} PRs on this Track merged; ${r.length} did not land:`:`All ${t.length} PRs on this Track have merged:`,s=t.map(i=>`- ${e(i)} \u2014 ${bh[i.outcome]??i.outcome}`).join(`
|
|
275
|
+
`),a=n.length?r.length?`This is an automated merge notification: pick up whatever follow-up work the merged PR${n.length===1?"":"s"} unblock${n.length===1?"s":""} \u2014 and note the unmerged one${r.length===1?"":"s"}: that work did NOT land, so check whether it still needs to ship. If nothing needs doing, say so briefly.`:"This is an automated merge notification: pick up whatever follow-up work these merges unblock (dependent tasks, cleanup, next steps that were waiting on them). If nothing needs doing, say so briefly.":"This is an automated notification: NONE of the PRs landed \u2014 the work did not ship. Check whether it still needs to go out another way. If not, say so briefly.";return`${o}
|
|
276
|
+
${s}
|
|
277
|
+
${a}`}async function dl(t){if(t.archivedAt)return!1;let e=t.boxes.find(r=>r.name===t.leadBox);if(!e||An.has(e.state)||await z(e.name))return!1;let n=Sh(e.agent,e.sessionId);if(!await No(n))return ot(t,"rehydrate-failed",{box:e.name,agent:e.agent,reason:`agent "${e.agent}" not available on this box`}),!1;try{return await Qe(e.name,e.cwd,{agentCmd:n,agent:e.agent,resumeSessionId:e.sessionId,pinned:e.pinned===!0}),qo({cwd:e.cwd,sessionName:e.name,agent:e.agent,agentCmd:n}),e.state="live",ot(t,"rehydrated",{box:e.name,agent:e.agent}),!0}catch(r){return ot(t,"rehydrate-failed",{box:e.name,agent:e.agent,reason:String(r?.message||r)}),!1}}async function Jh(t){let e=await H(),n=Z(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,ot(n,"unarchived",{}),await dl(n),await V(),Ke(n)}async function zh(){let t=await H(),e=new Set((await Nn()).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 Jt(a.name).catch(()=>{}),ot(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 V(),r}async function Xh(t){let e=await H(),n=Z(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 z(n.leadBox)){let a=await wt(n.leadBox,{lines:_t}).catch(()=>"");if(Ho(r.agent,a))throw new Error("lead is mid-turn \u2014 retry when it goes idle");await Jt(n.leadBox),r.state="stopped"}let s=await dl(n);if(await V(),!s)throw new Error("rehydrate failed \u2014 see the track's rehydrate-failed event");return Ke(n)}var Yh=(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 Zh(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 fo=new Map,wo=t=>fo.get(t)||(ae?Z(ae,t)?.handoff:null)||null;async function zs(t,e){let n=await H(),r=Z(n,t);r&&(r.handoff={...r.handoff||{},...e},await V())}function yA(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 Qh(){let t=await H(),e=!1;for(let n of t.tracks||[]){let r=n.handoff,o=yA(n);if(!o)continue;e=!0;let s=!!r.toBox&&r.toBox===r.fromBox;if(s&&o.step==="error"){let c=await z(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=Zh(l),f=d[d.length-1],p=f.agent,m=f.sessionId??null;f.endedAt=u,r.reason&&(f.endReason=r.reason);let h=c.agent==="claude"&&!!c.sessionId;d.push({agent:r.toAgent,sessionId:c.sessionId||null,pinned:h,startedAt:u,endedAt:null}),l.agent=r.toAgent,l.sessionId=c.sessionId||null,l.pinned=h,l.state="live",n.pendingHandoffHydrate={artifact:Qs,at:u},ot(n,"lead-handoff",{from:r.fromBox,to:r.fromBox,fromAgent:p,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 q({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 q({flow:"lead-handoff",phase:"reconciled",trackId:n.id,outcome:o.step,recovered:o.recovered,fromBox:o.fromBox,toBox:o.toBox}).catch(()=>{}),i&&await Jt(i).catch(()=>{}),a&&await Jt(a).catch(()=>{})}return e&&await V(),e}async function fl(t,e,n=!0,r=null){let o=fo.get(t);if(o&&!o.finishedAt)throw new Error("a lead handoff is already running for this Track");let s=Z(await H(),t);if(!s)throw new Error(`track "${t}" not found`);let a=await Zs(e,n),i=await z(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:vh(r),toBox:s.leadBox,startedAt:Date.now(),finishedAt:null,error:null};return fo.set(t,c),await zs(t,{step:c.step,fromBox:c.fromBox,toAgent:e,toBox:c.toBox,skipPermissions:n,startedAt:c.startedAt,finishedAt:null,error:null}),bA(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 zs(t,{step:c.step,error:c.error,finishedAt:c.finishedAt}).catch(()=>{}),q({flow:"lead-handoff",phase:"failed",trackId:t,step:c.step,error:c.error,fromBox:c.fromBox,toAgent:c.toAgent,toBox:c.toBox}).catch(()=>{})}),q({flow:"lead-handoff",phase:"started",trackId:t,fromBox:c.fromBox,toAgent:e,cwd:i.cwd}).catch(()=>{}),c}async function bA(t,e,n,r,o){let s=async(E,R={})=>{t.step=E,console.log(`[lead-handoff ${t.trackId}] step \u2192 ${E}`),await zs(t.trackId,{step:E,toBox:t.toBox}),await q({flow:"lead-handoff",phase:E,trackId:t.trackId,fromBox:t.fromBox,toBox:t.toBox,toAgent:o,sessionName:R.sessionName,...R})};console.log(`[lead-handoff ${t.trackId}] ${t.fromBox}: ${n.agent} \u2192 ${o} IN PLACE`);let a=await H(),i=Z(a,t.trackId)?.boxes.find(E=>E.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 Vu(t.fromBox),await s("swap",{cwd:n.cwd,agentCmd:r});let d=await Ni(t.fromBox,{agentCmd:r,agent:o});await s("hydrate",{sessionName:t.fromBox}),await Te({cwd:n.cwd,sessionId:d.sessionId,sessionName:t.fromBox,agent:o,agentCmd:ho(o)});let f=await wt(t.fromBox,{lines:_t}).catch(()=>"");if(!Wo(o,f)){await s("rollback",{sessionName:t.fromBox,fromAgent:c});let E=Sh(c,l,{skipPermissions:t.skipPermissions});await Ni(t.fromBox,{agentCmd:E,agent:c,resumeSessionId:l,pinned:u});let R=await H(),st=Z(R,t.trackId);if(st){let I=st.boxes.find(L=>L.name===t.fromBox);I&&(I.state="live"),ot(st,"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 V()}throw new Error(`incoming ${o} CLI never drew its composer \u2014 rolled back to ${c} (--resume, context intact)`)}await s("commit");let p=await H(),m=Z(p,t.trackId);if(!m)throw new Error(`track "${t.trackId}" not found during handoff commit \u2014 was it deleted or archived mid-flight?`);let h=m.boxes.find(E=>E.name===t.fromBox);if(!h)throw new Error(`lead box row "${t.fromBox}" missing during handoff commit`);let b=Date.now(),x=Zh(h),v=x[x.length-1];v.endedAt=b,t.reason&&(v.endReason=t.reason),x.push({agent:o,sessionId:d.sessionId,pinned:d.pinned,startedAt:b,endedAt:null}),h.agent=o,h.sessionId=d.sessionId,h.pinned=d.pinned,h.state="live",h.role="lead",m.pendingHandoffHydrate={artifact:Qs,at:b},ot(m,"lead-handoff",{from:t.fromBox,to:t.fromBox,fromAgent:c,toAgent:o,fromSessionId:l,toSessionId:d.sessionId,epoch:x.length-1,singleBox:!0,forced:d.forced||void 0,skipPermissions:t.skipPermissions,reason:t.reason,handoffStartedAt:t.startedAt}),await V(),await s("done",{sessionName:t.fromBox}),await zs(t.trackId,{finishedAt:Date.now(),error:null}),t.finishedAt=Date.now()}var kA=Number(process.env.BOSTRAT_SUBTASK_TIMEOUT_MS)||18e5,xA=Number(process.env.BOSTRAT_SUBTASK_POLL_MS)||3e3,wr=j.join(Ge,"worktrees"),mr=[co,Js],Kt=new Map;var br=t=>{let e=new Map([...Kt.values()].filter(r=>r.trackId===t).map(r=>[r.id,r])),n=ae?Z(ae,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)},pl=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,repos:t.repos||null,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 SA(t,e,n){let r=await H(),o=Z(r,t);o&&(o.subtasks=o.subtasks||{},o.subtasks[e]={...o.subtasks[e]||{},...n},await V())}var gr=t=>SA(t.trackId,t.id,pl(t));async function hr(t,e){t.step=e,await gr(t)}function vA(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 AA(t){return!!t&&!t.finishedAt&&!t.canceled&&t.step==="working"&&!!t.box&&!!t.dir&&!!t.base&&!!t.branch}async function TA(t,e,n){if(!AA(n)||Kt.has(e)||!await z(n.box).catch(()=>null))return!1;try{await D.access(n.dir)}catch{return!1}let o=await z(t.leadBox).catch(()=>null);if(!o?.cwd)return!1;let s={...n,id:e,leadBox:t.leadBox,step:"working",canceled:!1,forceMerge:!1};return Kt.set(e,s),ow(s,o).catch(a=>nw(s,a)),!0}async function tw(){let t=await H(),e=!1;for(let n of t.tracks||[]){for(let[r,o]of Object.entries(n.subtasks||{})){if(Kt.has(r))continue;if(await TA(n,r,o)){e=!0,ot(n,"subtask-resumed",{box:o.box,agent:o.agent,task:o.task,subtaskId:r,resumedStep:o.step}),await q({flow:"subtask",phase:"resumed",trackId:n.id,subtaskId:r,box:o.box,branch:o.branch}).catch(()=>{});continue}let s=o.step,a=vA(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"),ot(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 q({flow:"subtask",phase:"reconciled",trackId:n.id,subtaskId:r,outcome:a.step,recovered:a.recovered,box:o.box,branch:o.branch}).catch(()=>{}),await kh(n,o.box,o.dir)}for(let r of n.boxes){if(r.role!=="subtask"||An.has(r.state)||Kt.has(r.subtaskId)||n.subtasks?.[r.subtaskId])continue;e=!0,r.state="canceled";let o=r.subtaskId?j.join(wr,`${n.id}-${r.subtaskId}`):null;await q({flow:"subtask",phase:"reaped-legacy-orphan",trackId:n.id,subtaskId:r.subtaskId,box:r.name}).catch(()=>{}),await kh(n,r.name,o)}}return e=await _A(t)||e,e&&await V(),e}async function kh(t,e,n){if(e&&await Jt(e).catch(()=>{}),!n)return;if(t.workspace?.kind==="multi"){for(let o of $t(t.workspace)){let s=j.join(n,o.name);await Ft(o.base,s).then(()=>!0,()=>!1)||await Ft(o.dir,s).catch(()=>{})}await D.rm(n,{recursive:!0,force:!0}).catch(()=>{});return}let r=await z(t.leadBox).catch(()=>null);r?.cwd&&await Ft(r.cwd,n).catch(()=>{})}async function _A(t){let e;try{e=await D.readdir(wr)}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 Kt.values())o.dir&&n.add(j.basename(o.dir));let r=!1;for(let o of e){if(n.has(o))continue;let s=j.join(wr,o),a=(t.tracks||[]).find(i=>o.startsWith(`${i.id}-`));if(a?.workspace?.kind==="multi"){for(let i of $t(a.workspace))await Ft(i.base,j.join(s,i.name)).catch(()=>{});await D.rm(s,{recursive:!0,force:!0}).catch(()=>{})}else{let i=a?await z(a.leadBox).catch(()=>null):null;i?.cwd?await Ft(i.cwd,s).catch(()=>{}):await D.rm(s,{recursive:!0,force:!0}).catch(()=>{})}await q({flow:"subtask",phase:"swept-orphan-worktree",dir:s}).catch(()=>{}),r=!0}return r}var ml=t=>t.trim().replace(/\s+/g,"-").replace(/[^A-Za-z0-9._-]/g,"").toLowerCase()||"track",po=class extends Error{};async function ew(t,{agent:e,task:n,skipPermissions:r=!0,reason:o=null}={}){if(typeof n!="string"||!ar(n))throw new Error("task (string) required");let s=await Zs(e,r),a=Z(await H(),t);if(!a)throw new Error(`track "${t}" not found`);let i=await z(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");let c=a.workspace?.kind==="multi",l=null;if(c){let f=$t(a.workspace);if(!f.length)throw new Error("this multi-repo Track has no tagged repos to delegate into");for(let p of f)if(!p?.dir||!await _s(p.dir))throw new Error(`tagged checkout "${p?.name??"?"}" is missing from the lead workspace (${p?.dir??"no dir"})`);l=f.map(p=>({name:p.name,leadDir:p.dir,base:null}))}else if(!await _s(i.cwd))throw new Error(`subtask delegation needs the lead's workspace to be a git repo (got ${i.cwd})`);let u=mo().slice(0,8),d={id:u,trackId:t,trackName:a.name,agent:e,task:ar(n),skipPermissions:r,reason:vh(o),step:"snapshot",leadBox:a.leadBox,box:null,branch:`bostrat/${ml(a.name)}-${u}`,base:null,repos:l,dir:j.join(wr,`${t}-${u}`),stat:null,conflicts:[],applied:null,startedAt:Date.now(),finishedAt:null,error:null,canceled:!1,forceMerge:!1};return Kt.set(u,d),await gr(d),EA(d,i,s).catch(f=>nw(d,f)),d}async function nw(t,e){t.failedStep=t.step,t.error=e instanceof po?"canceled":e?.message||String(e),t.step=e instanceof po?"canceled":"error",t.finishedAt=Date.now(),await wl(t).catch(()=>{}),await gr(t).catch(()=>{}),await OA(t).catch(()=>{})}function hl(t){let e=Kt.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 rw(t){let e=Kt.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 EA(t,e,n){if(await hr(t,"snapshot"),t.repos){for(let s of t.repos)s.base=await wc(s.leadDir,`bostrat: snapshot for subtask ${t.id}`);await gr(t)}else t.base=await wc(e.cwd,`bostrat: snapshot for subtask ${t.id}`);if(await hr(t,"spawn"),await D.mkdir(wr,{recursive:!0}),t.repos){await D.mkdir(t.dir,{recursive:!0});for(let a of t.repos)await ir(a.leadDir,j.join(t.dir,a.name),t.branch,a.base);let s=$t((Z(await H(),t.trackId)||{}).workspace);await ea(t.dir,t.repos.map(a=>{let i=s.find(c=>c?.name===a.name);return{name:a.name,repo:i?.repo??null,baseRef:null}}),t.branch)}else await ir(e.cwd,t.dir,t.branch,t.base),await rl(t.dir);await IA(t,e);let r=t.trackName.slice(0,38).trim(),o=await Qe(`${r} ${t.agent} sub ${t.id}`,t.dir,{agentCmd:n,agent:t.agent});t.box=o.name,await RA(t,o),await Ah(t.trackId,o.name,jv(t.task,!!t.repos),{kind:"subtask"}),await ow(t,e)}async function ow(t,e){await hr(t,"working");let n=j.join(t.dir,Js);if(await MA(n,t),t.canceled)throw new po;if(await hr(t,"merge"),t.repos){let o={files:0,insertions:0,deletions:0},s=[],a=[],i=!0;for(let c of t.repos){let l=j.join(t.dir,c.name);await gc(l,`bostrat: subtask ${t.id} result (auto-committed)`),c.stat=await xc(c.leadDir,c.base,t.branch,mr),c.files=await Sc(c.leadDir,c.base,t.branch,mr);let u=await vc(c.leadDir,c.base,t.branch,mr);c.applied=u.applied,c.conflicts=u.conflicts??[],o.files+=c.stat.files,o.insertions+=c.stat.insertions,o.deletions+=c.stat.deletions,s.push(...c.files.map(d=>j.join(c.name,d))),a.push(...c.conflicts.map(d=>d.startsWith("patch did not apply cleanly")?`${c.name}: ${d}`:j.join(c.name,d))),u.applied||(i=!1)}t.stat=o,t.files=s,t.conflicts=a,t.applied=i}else{await gc(t.dir,`bostrat: subtask ${t.id} result (auto-committed)`),t.stat=await xc(e.cwd,t.base,t.branch,mr),t.files=await Sc(e.cwd,t.base,t.branch,mr);let o=await vc(e.cwd,t.base,t.branch,mr);t.applied=o.applied,t.conflicts=o.conflicts}await gr(t);let r=await CA(t,e,n);await hr(t,"resume"),await Ah(t.trackId,t.leadBox,Lv(t.task,t.stat,t.conflicts,r),{kind:"subtask-resume"}),await hr(t,"reap"),await wl(t),t.step="done",t.finishedAt=Date.now(),await $A(t),await gr(t)}async function IA(t,e){let n=j.join(t.dir,j.dirname(co));await D.mkdir(n,{recursive:!0});let r="";try{r=await mu(e.cwd,e.sessionId)}catch{}if(!r)try{r=await D.readFile(j.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}
|
|
221
278
|
|
|
222
279
|
**Delegated task:** ${t.task}
|
|
223
280
|
|
|
@@ -228,53 +285,43 @@ When working anywhere else, ignore everything below.
|
|
|
228
285
|
## Conversation so far (lead \u2192 you)
|
|
229
286
|
|
|
230
287
|
${r}
|
|
231
|
-
`;await
|
|
232
|
-
|
|
233
|
-
fileUpload(contentType: $contentType, filename: $filename, size: $size) {
|
|
234
|
-
success
|
|
235
|
-
uploadFile { uploadUrl assetUrl headers { key value } }
|
|
236
|
-
}
|
|
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?`
|
|
238
|
-
|
|
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}\`)`:""}:
|
|
240
|
-
|
|
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(`
|
|
288
|
+
`;await D.writeFile(j.join(t.dir,co),o)}async function RA(t,e){let n=await H(),r=Z(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}),ot(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]||{},...pl(t)},await V())}async function $A(t){let e=await H(),n=Z(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),ot(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 V()}async function OA(t){let e=await H(),n=Z(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"),ot(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]||{},...pl(t)},await V()}async function CA(t,e,n){let r=`.bostrat/handoff/subtask-${t.id}.md`;try{let o=j.join(e.cwd,r);await D.mkdir(j.dirname(o),{recursive:!0}),await D.copyFile(n,o)}catch{}return r}async function wl(t){if(t.box&&await Jt(t.box).catch(()=>{}),t.repos&&t.dir){for(let e of t.repos)await Ft(e.leadDir,j.join(t.dir,e.name)).catch(()=>{});await D.rm(t.dir,{recursive:!0,force:!0}).catch(()=>{})}else if(t.dir){let n=(await z(t.leadBox).catch(()=>null))?.cwd;n&&await Ft(n,t.dir).catch(()=>{})}}async function MA(t,e){let n=(e.startedAt||Date.now())+kA;for(;;){if(e.canceled||e.forceMerge)return;try{await D.access(t);return}catch{}if(Date.now()>n)throw new Error("subtask timed out without signaling completion (no result digest written)");await na(xA)}}function na(t){return new Promise(e=>setTimeout(e,t))}import go from"node:fs/promises";import sa from"node:path";import jA from"node:os";import PA from"ws";var sw=()=>process.env.BOSTRAT_SLACK_API_BASE||"https://slack.com/api";async function ra(t,e,n={}){let r=await fetch(`${sw()}/${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 gl=(t,e)=>ra("chat.postMessage",t,e),kr=(t,e)=>ra("chat.update",t,e);async function Ot(t,e){await fetch(t,{method:"POST",headers:{"content-type":"application/json; charset=utf-8"},body:JSON.stringify(e)}).catch(()=>{})}async function NA(t,e,n={}){let r=await fetch(`${sw()}/${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 aw(t,{filename:e,buffer:n,title:r,channelId:o,threadTs:s,initialComment:a}){let{upload_url:i,file_id:c}=await NA("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 ra("files.completeUploadExternal",t,{files:[{id:c,title:r}],channel_id:o,thread_ts:s,...a?{initial_comment:a}:{}})}var BA=2200;function iw({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 ra("apps.connections.open",u))}catch(u){return n(`connections.open failed: ${u?.message||u}`),c()}o=new PA(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=h=>{try{o.send(JSON.stringify(h?{envelope_id:d.envelope_id,payload:h}:{envelope_id:d.envelope_id}))}catch{}},p=!1,m=setTimeout(()=>{p=!0,f()},BA);Promise.resolve().then(()=>e(d)).then(h=>{clearTimeout(m),!p&&(p=!0,f(d.accepts_response_payload?h:void 0))},h=>{clearTimeout(m),p||(p=!0,f()),n(`envelope handler failed: ${h?.message||h}`)})}),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 uw=()=>process.env.BOSTRAT_STATE_DIR||sa.join(process.env.HOME||jA.tmpdir(),".bostrat"),Sl=()=>sa.join(uw(),"slack.json"),oa=null,yl=null,bl=Promise.resolve();async function Al(){return oa||(yl??=go.readFile(Sl(),"utf8").then(t=>JSON.parse(t)?.threads??{}).catch(()=>({})).then(t=>oa=t).finally(()=>yl=null),yl)}async function cw(){let t=JSON.stringify({threads:oa},null,2),e=`${Sl()}.${process.pid}.tmp`;await go.mkdir(uw(),{recursive:!0}),await go.writeFile(e,t),await go.rename(e,Sl())}async function be(){bl=bl.then(cw,cw),await bl.catch(()=>{})}async function Tl(t){return(await Al())[t]??null}async function LA(t,e){let n=await Al();for(let[r,o]of Object.entries(n))if(o.channel===t&&o.ts===e)return{trackId:r,rec:o};return null}var G=t=>String(t??"").replace(/&/g,"&").replace(/</g,"<").replace(/>/g,">");function yo(t,e){let n=String(t??"").trim();return n.length<=e?n:`${n.slice(0,e-1).trimEnd()}\u2026`}function FA(t,e=500){let n=String(t??"").trim().split(/\n\s*\n/)[0]??"";return yo(n.replace(/\n+/g," "),e)}function dw(t){let e=`*${G(t.name)}*${t.archivedAt?" \xB7 _archived_":""}`,n=`${G(t.repoLabel)} \xB7 \`${G(t.branch||"?")}\` \xB7 lead *${G(t.lead)}*`,r=t.kickoff?`> ${G(yo(t.kickoff,300))}`:"_no kickoff yet \u2014 reply in this thread to steer the lead_";return[{type:"section",text:{type:"mrkdwn",text:`${e}
|
|
289
|
+
${n}`}},{type:"section",text:{type:"mrkdwn",text:r}},{type:"context",elements:[{type:"mrkdwn",text:`<${us(t.trackId)}|open the Track> \xB7 reachable on the operator's private network only`}]}]}async function vl(t,e){await kr(t.botToken,{channel:e.channel,ts:e.ts,text:`Track: ${e.name}`,blocks:dw(e)})}async function _n(t,e,n,r=void 0){return gl(t.botToken,{channel:e.channel,thread_ts:e.ts,text:n,...r?{blocks:r}:{},unfurl_links:!1})}var lw=new Set;function Tn(t){lw.add(t),t.catch(()=>{}).finally(()=>lw.delete(t))}var ct=t=>({response_type:"ephemeral",text:t}),UA="*/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._",HA=["claude","cursor","codex"];function kl(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 xl=(t,e)=>ct(`No live Track matches \u201C${G(e)}\u201D. Live: ${t.map(n=>`\`${G(n.name)}\``).join(" \xB7 ")||"none"}`);async function fw(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 Ot(o,{...ct(`At capacity \u2014 ${a.length} live Tracks (limit ${e.maxConcurrent}). Archive or stop something first.`),replace_original:!0});return}if(!e.channel){await Ot(o,{...ct("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?sa.basename(c.base):"fresh workspace"),lead:"claude"},u=await gl(e.botToken,{channel:e.channel,text:`Track started: ${l.name}`,blocks:dw(l),unfurl_links:!1});l.ts=u.ts,(await Al())[i.id]=l,await be(),await Ot(o,{...ct(`\u2713 *${G(l.name)}* started \u2014 root message posted.`),replace_original:!0})}catch(a){await Ot(o,{...ct(`Start failed: ${a?.message||a}`),replace_original:!0})}}async function WA(t,e,{track:n,responseUrl:r}){let o=t.deps;try{await o.archiveTrack(n.id);let s=await Tl(n.id);s&&(s.archivedAt=Date.now(),await be(),await vl(e,s).catch(()=>{})),await Ot(r,{...ct(`\u2713 *${G(n.name)}* archived.`),replace_original:!0})}catch(s){await Ot(r,{...ct(`Archive failed: ${s?.message||s}`),replace_original:!0})}}async function qA(t,e,{track:n,agent:r,responseUrl:o}){try{await t.deps.startLeadHandoff(n.id,r),await Ot(o,{...ct(`\u2713 Handing *${G(n.name)}* to ${G(r)} \u2014 the root message updates when the baton lands.`),replace_original:!0})}catch(s){await Ot(o,{...ct(`Handoff failed: ${s?.message||s}`),replace_original:!0})}}async function GA(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 Ot(r,{...ct(`\u2713 Stopped *${G(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 Ot(r,{...ct(`Stop failed: ${s?.message||s}`),replace_original:!0})}}function KA(t,e){let n=[...e.slice(0,99).map(r=>({text:{type:"plain_text",text:yo(`${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 *${G(t)}*:`},accessory:{type:"static_select",action_id:"bostrat-start-repo",placeholder:{type:"plain_text",text:"Choose a repo"},options:n}}]}async function VA(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?sa.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 *${G(s.name)}* \u2014 ${G(i)}${a.branch?` \`${G(a.branch)}\``:""} \xB7 lead ${G(c)} \xB7 ${l}`}),o=e.length-n.length;return r.join(`
|
|
244
290
|
`)+(o?`
|
|
245
|
-
_${o} archived_`:"")}async function
|
|
246
|
-
\u2713 *${
|
|
247
|
-
\u2713 _answered_`}}]})}async function
|
|
248
|
-
_runs on the Track's branch; reachable on the operator's private network only_`}}]);return}case"mock.state.changed":return
|
|
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
|
|
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
|
|
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:
|
|
291
|
+
_${o} archived_`:"")}async function JA(t,e){let n=await cn();if(!n)return ct("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 ct("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?(Tn(fw(t,n,{name:u,repo:l.path,responseUrl:e.response_url})),ct(`Starting *${G(u)}* on ${G(l.name)}\u2026`)):{response_type:"ephemeral",text:`Pick a repo for \u201C${u}\u201D`,blocks:KA(u,i)}}case"archive":{if(!a.length)return ct("Usage: `/bostrat archive <track>`");let{match:i,live:c}=kl(await r.listTracks(),a.join(" "));return i?(Tn(WA(t,n,{track:i,responseUrl:e.response_url})),ct(`Archiving *${G(i.name)}*\u2026`)):xl(c,a.join(" "))}case"handoff":{let i=r.knownAgents??HA,c=a.at(-1)?.toLowerCase();if(a.length<2||!i.includes(c))return ct(`Usage: \`/bostrat handoff <track> <${i.join("|")}>\``);let l=a.slice(0,-1).join(" "),{match:u,live:d}=kl(await r.listTracks(),l);return u?(Tn(qA(t,n,{track:u,agent:c,responseUrl:e.response_url})),ct(`Handing *${G(u.name)}* to ${G(c)}\u2026`)):xl(d,l)}case"stop":{if(!a.length)return ct("Usage: `/bostrat stop <track>`");let{match:i,live:c}=kl(await r.listTracks(),a.join(" "));return i?(Tn(GA(t,n,{track:i,responseUrl:e.response_url})),ct(`Stopping *${G(i.name)}*\u2026`)):xl(c,a.join(" "))}case"list":return ct(await VA(r));default:return ct(UA)}}async function zA(t,e,n,r){let{trackId:o,promptId:s,label:a}=r,i=await Tl(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 be(),await kr(e.botToken,{channel:i.channel,ts:c,text:`\u2713 ${a}`,blocks:[{type:"section",text:{type:"mrkdwn",text:`~${G(l||"question")}~
|
|
292
|
+
\u2713 *${G(a)}*`}}]}).catch(()=>{})}}catch(c){await Ot(n.response_url,ct(`Couldn't answer: ${c?.message||c}`))}}async function XA(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,Os(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 Ot(n.response_url,ct(`Couldn't act on the offer: ${c?.message||c}`))}}async function YA(t,e){let n=e.actions?.[0];if(!n?.action_id)return;let r=await cn();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 Ot(e.response_url,{...ct(`Starting *${G(s)}*\u2026`),replace_original:!0}),Tn(fw(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"?Tn(zA(t,r,e,o)):n.action_id==="bostrat-ship"&&Tn(XA(t,r,e,o))}async function ZA(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 LA(e.channel,e.thread_ts);if(!n)return;let r=await cn();if(r)try{await t.deps.sayToLead(n.trackId,e.text.trim())}catch(o){await _n(r,n.rec,`Couldn't deliver that to the lead: ${o?.message||o}`).catch(()=>{})}}function QA(t){return async e=>{if(e.type==="slash_commands")return JA(t,e.payload);if(e.type==="interactive"){await YA(t,e.payload);return}if(e.type==="events_api"){await ZA(t,e.payload?.event);return}}}var tT=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 eT(t,e){let n=[{type:"section",text:{type:"mrkdwn",text:`\u2753 ${G(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:yo(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 nT(t,e,n){let r=n.payload||{};if(!r.promptId||e.prompt?.promptId===r.promptId)return;e.prompt&&await kr(t.botToken,{channel:e.channel,ts:e.prompt.ts,text:"superseded",blocks:[{type:"section",text:{type:"mrkdwn",text:`~${G(e.prompt.question||"question")}~ _superseded_`}}]}).catch(()=>{});let o=await _n(t,e,r.question||"The lead needs an answer",eT(n.trackId,r));e.prompt={promptId:r.promptId,ts:o.ts,question:r.question||null},await be()}async function rT(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 be(),await kr(t.botToken,{channel:e.channel,ts:o,text:"answered",blocks:[{type:"section",text:{type:"mrkdwn",text:`~${G(s||"question")}~
|
|
293
|
+
\u2713 _answered_`}}]})}async function oT(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 _n(t,e,r.excerpt||"Ship it?",[{type:"section",text:{type:"mrkdwn",text:`\u{1F6A2} ${G(yo(r.excerpt||"The lead offered to ship.",300))}`}},{type:"actions",elements:s}]);e.offers=e.offers||{},e.offers[r.offerId]=a.ts,await be()}async function sT(t,e,n){let r=n.payload||{},o=e.offers?.[r.offerId];if(!o)return;delete e.offers[r.offerId],await be();let s=r.action==="dismiss"?"dismissed":r.action==="pr"?"\u2713 opening a PR":"\u2713 pushing";await kr(t.botToken,{channel:e.channel,ts:o,text:s,blocks:[{type:"section",text:{type:"mrkdwn",text:`\u{1F6A2} ${s}`}}]})}async function aT(t,e,n,r){let o=n.payload||{};if(o.change==="ready"&&o.mockId){let s=await(r.readMockPng??iT)(o.mockId,o.version??null);if(!s)return;let a=(o.version??1)>1?` (v${o.version})`:"";await aw(t.botToken,{filename:s.filename,buffer:s.buffer,title:`${o.name}${a}`,channelId:e.channel,threadTs:e.ts,initialComment:`\u{1F3A8} mock *${G(o.name)}*${a}${o.group?` (group \`${G(o.group)}\`)`:""}`})}else if(o.change==="picked"){let s=(o.siblings??[]).map(a=>`*${G(a.name)}*`).join(", ");await _n(t,e,`Design decision: ${o.name} picked`,[{type:"section",text:{type:"mrkdwn",text:`\u{1F3A8} design decision: *${G(o.name)}* picked${s?` over ${s}`:""}`}}])}}async function iT(t,e){let n=await Ee(t);if(!n)return null;let r=nn(n,e);if(!r)return null;let o=await go.readFile(Lt(n,r.v).png).catch(()=>null);return o?{filename:`${n.name.replace(/[^\w.-]+/g,"-")}.v${r.v}.png`,buffer:o}:null}async function cT(t,e){if(!tT.has(e.type))return;let n=await Tl(e.trackId);if(!n)return;let r=await cn();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 be(),await vl(r,n);return;case"turn.agent":{if(!o.text)return;let s=FA(o.text);if(!s)return;await _n(r,n,s,[{type:"section",text:{type:"mrkdwn",text:G(s)}},{type:"context",elements:[{type:"mrkdwn",text:`<${us(e.trackId)}|full turn \u2192>`}]}]);return}case"prompt.raised":return nT(r,n,e);case"prompt.answered":return rT(r,n,e);case"pr.offered":return oT(r,n,e);case"pr.offer.answered":return sT(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 _n(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 be(),await vl(r,n);return;case"view.state.changed":{if(o.change!=="created"||o.restarted||!o.slug)return;await _n(r,n,`Live preview ${o.name}: ${ps(o.slug)}`,[{type:"section",text:{type:"mrkdwn",text:`\u{1F50D} live preview *${G(o.name)}*: ${ps(o.slug)}
|
|
294
|
+
_runs on the Track's branch; reachable on the operator's private network only_`}}]);return}case"mock.state.changed":return aT(r,n,e,t.deps)}}function pw(t){let e={deps:t};jt.on("event",s=>{cT(e,s).catch(()=>{})});let n=null,r=null,o=async()=>{n||!await cn()||(r&&clearInterval(r),r=null,n=iw({getToken:async()=>(await cn())?.appToken,onEnvelope:QA(e),log:s=>console.log(`[slack] ${s}`)}))};return r=setInterval(()=>o().catch(()=>{}),3e4),r.unref?.(),o().catch(()=>{}),()=>{r&&clearInterval(r),n?.()}}import lT from"node:fs/promises";function uT(t){let e=new Map;for(let n of String(t||"").split(`
|
|
295
|
+
`)){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 dT(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 fT(t,e){return t?.transcript_path?t.transcript_path:e?.cwd&&e?.sessionId?ce(e.cwd,e.sessionId):e?.cwd?Na(e.cwd):null}async function mw(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 O(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 pT(r,o,t,i);break;default:break}return a}async function pT(t,e,n,r){let o=[],s=fT(n,e);if(s)try{o=uT(await lT.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=dT(await F(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",p=u.get(f)||{tokens:{input:0,output:0,cacheWrite:0,cacheRead:0},messageIds:[]};for(let m of Object.keys(p.tokens))p.tokens[m]+=d.tokens[m];p.messageIds.push(d.messageId),u.set(f,p)}for(let[d,f]of u)await r({type:"cost.recorded",actor:"system",source:"transcript",payload:{model:d,usd:zt(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 wT from"node:fs";import _l from"node:fs/promises";import gT from"node:http";import xr from"node:path";import ia from"node:os";import{createRequire as yT}from"node:module";import El from"ws";import mT from"node:path";import hT from"node:os";var qM=process.env.BOSTRAT_STATE_DIR||mT.join(process.env.HOME||hT.tmpdir(),".bostrat");function hw(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 ww(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 aa=(()=>{try{return yT(import.meta.url)("../package.json").version||null}catch{return null}})(),bT=process.env.BOSTRAT_STATE_DIR||xr.join(process.env.HOME||ia.tmpdir(),".bostrat");function kT(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 xT({home:t=process.env.HOME||ia.homedir(),env:e=process.env}={}){let n=r=>{try{return wT.statSync(r),!0}catch{return!1}};return{claude:n(xr.join(t,".claude",".credentials.json")),codex:n(xr.join(e.CODEX_HOME||xr.join(t,".codex"),"auth.json"))}}var At=1,gw=1e3,ST=3e4,yw=100,vT=2e3,AT=25e3;function bw({url:t,token:e,nodeId:n=ia.hostname(),name:r=ia.hostname(),listTracks:o,detectAgents:s=null,selfUrl:a=`http://127.0.0.1:${process.env.PORT||3362}`,log:i=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=ww(t);let c=a.replace(/^http/,"ws"),l=new Map,u=new Map,d=xr.join(bT,"plane-link","cursor.json"),f={},p=new Map,m=null,h=!1,b=!1,x=gw,v=null,E=null,R=null,st=!1;async function I(){try{f=JSON.parse(await _l.readFile(d,"utf8"))}catch{f={}}}function L(){st=!0,!E&&(E=setTimeout(async()=>{if(E=null,!!st){st=!1;try{await _l.mkdir(xr.dirname(d),{recursive:!0}),await _l.writeFile(d,JSON.stringify(f))}catch(B){i.error("[plane-link] cursor flush failed:",B?.message||B)}}},vT),E.unref?.())}function T(B){return!m||m.readyState!==El.OPEN?!1:(m.send(JSON.stringify(B)),!0)}function U(B){for(let{trackId:$,event:_}of B)p.set(_.id,{trackId:$,seq:_.seq});return T({v:At,ch:"events",type:"append",events:B})}async function J(){let B;try{B=await o()}catch($){i.error("[plane-link] backfill listTracks failed:",$?.message||$);return}T({v:At,ch:"ctl",type:"tracks",ids:B.map($=>$.id).filter($=>typeof $=="string")});for(let $ of B){if(!h)return;let _;try{_=await F($)}catch{continue}let M=f[$.id]??0,at=_.filter(Q=>Q.seq>M&&typeof Q.id=="string");for(let Q=0;Q<at.length;Q+=yw){let ht=at.slice(Q,Q+yw).map(Tt=>({trackId:$.id,event:Tt}));if(!U(ht))return}}}function g(B){!h||!B?.trackId||typeof B.id!="string"||U([{trackId:B.trackId,event:B}])}let w=new Map;async function y(B){let{reqId:$,method:_,path:M,headers:at={},body:Q}=B,ht=Object.fromEntries(Object.entries(at).filter(([,Dt])=>Dt!=null)),Tt=new AbortController;w.set($,Tt);try{if(B.host){await A(B,ht,Tt);return}let Dt=await fetch(a+M,{method:_,headers:ht,body:Q?Buffer.from(Q,"base64"):void 0,signal:Tt.signal}),Ht=Object.fromEntries(Dt.headers);for(let k of["connection","keep-alive","transfer-encoding","upgrade","content-encoding","content-length"])delete Ht[k];if(Ht["content-type"]||(Ht["content-type"]="application/octet-stream"),T({v:At,ch:"control",type:"http_head",reqId:$,status:Dt.status,headers:Ht}),Dt.body){for await(let k of Dt.body)if(!T({v:At,ch:"control",type:"http_data",reqId:$,data:Buffer.from(k).toString("base64")}))break}T({v:At,ch:"control",type:"http_end",reqId:$,error:null})}finally{w.delete($)}}function A(B,$,_){let{reqId:M,method:at,path:Q,body:ht}=B,Tt=new URL(a);return new Promise(Dt=>{let Ht=gT.request({host:Tt.hostname,port:Tt.port,method:at,path:Q,headers:{...$,host:B.host},signal:_.signal},k=>{let{connection:W,"keep-alive":En,"transfer-encoding":Oo,upgrade:In,...xa}=k.headers;T({v:At,ch:"control",type:"http_head",reqId:M,status:k.statusCode||502,headers:xa}),k.on("data",Rg=>{T({v:At,ch:"control",type:"http_data",reqId:M,data:Buffer.from(Rg).toString("base64")})||k.destroy()}),k.on("end",()=>{T({v:At,ch:"control",type:"http_end",reqId:M,error:null}),Dt()}),k.on("error",()=>{T({v:At,ch:"control",type:"http_end",reqId:M,error:"view upstream error"}),Dt()})});Ht.on("error",k=>{T({v:At,ch:"control",type:"http_end",reqId:M,error:k?.message||"view request failed"}),Dt()}),ht&&Ht.write(Buffer.from(ht,"base64")),Ht.end()})}function K(){if(b)return;m=new El(t,{headers:{authorization:`Bearer ${e}`}}),m.on("open",async()=>{let $=null;try{$=await s?.()||null}catch{$=null}T({v:At,ch:"ctl",type:"hello",nodeId:n,name:r,platform:process.platform,version:aa,providers:xT(),...$?{agents:$}:{}})}),m.on("message",$=>{let _;try{_=JSON.parse($)}catch{return}if(_.ch==="ctl"&&_.type==="hello_ok"){h=!0,x=gw,i.log(`[plane-link] connected to plane as ${n}`),_.minNodeVersion&&aa&&kT(aa,_.minNodeVersion)&&i.error(`[plane-link] this node is bostrat-node ${aa}; the plane asks for \u2265${_.minNodeVersion} \u2014 update with: npm install -g bostrat-node`),R&&clearInterval(R),R=setInterval(()=>T({v:At,ch:"ctl",type:"ping"}),AT),R.unref?.(),J().catch(M=>i.error("[plane-link] backfill failed:",M?.message||M));return}if(_.ch==="events"&&_.type==="ack"&&Array.isArray(_.ids)){for(let M of _.ids){let at=p.get(M);at&&(p.delete(M),(f[at.trackId]??0)<at.seq&&(f[at.trackId]=at.seq))}L();return}if(_.ch==="control"&&_.type==="http_open"){y(_).catch(M=>T({v:At,ch:"control",type:"http_end",reqId:_.reqId,error:M?.message||"tunnel error"}));return}if(_.ch==="control"&&_.type==="http_cancel"){w.get(_.reqId)?.abort();return}if(_.ch==="attach"&&_.type==="open"){let M=new El(c+_.path,Array.isArray(_.protocols)?_.protocols:[],_.host?{headers:{host:_.host}}:void 0);l.set(_.streamId,M),u.set(_.streamId,[]),M.on("open",()=>{let Q=u.get(_.streamId)||[];u.delete(_.streamId);for(let{buf:ht,bin:Tt}of Q)M.send(ht,{binary:Tt})}),M.on("message",(Q,ht)=>{let Tt=ht?Q:Buffer.from(String(Q));T({v:At,ch:"attach",type:"data",streamId:_.streamId,data:Tt.toString("base64"),...ht?{bin:!0}:{}})});let at=()=>{u.delete(_.streamId),l.delete(_.streamId)&&T({v:At,ch:"attach",type:"close",streamId:_.streamId})};M.on("close",at),M.on("error",at);return}if(_.ch==="attach"&&_.type==="data"){let M=l.get(_.streamId);if(!M)return;let at=Buffer.from(_.data,"base64"),Q=_.bin===!0,ht=u.get(_.streamId);ht?ht.push({buf:at,bin:Q}):M.send(at,{binary:Q});return}if(_.ch==="attach"&&_.type==="close"){let M=l.get(_.streamId);l.delete(_.streamId),u.delete(_.streamId),M?.close();return}});let B=()=>{h&&i.log("[plane-link] disconnected from plane"),h=!1,R&&(clearInterval(R),R=null),p.clear();for(let $ of l.values())$.close();l.clear(),u.clear();for(let $ of w.values())$.abort();w.clear(),!(b||v)&&(v=setTimeout(()=>{v=null,K()},x),v.unref?.(),x=Math.min(x*2,ST))};m.on("close",B),m.on("error",B)}return jt.on("event",g),I().then(K),{stop:()=>{b=!0,jt.off("event",g),v&&clearTimeout(v),E&&clearTimeout(E),R&&clearInterval(R),m?.close()},status:()=>({connected:h,nodeId:n,cursor:{...f},inflight:p.size})}}function TT(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 kw=()=>({input:0,output:0,cacheRead:0,cacheCreation:0}),xw=(t,e)=>{for(let n of Object.keys(t))t[n]+=e?.[n]||0},Il=t=>Math.round(t*1e4)/1e4;async function Sw(t){let e=[],n={},r={},o=0,s=0,a=0,i=0,c=new Set,l=kw(),u=0,d=[];for(let f of t||[]){let p=await F(f),m=_e(p,{boxLabels:TT(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 h of m.unpricedModels||[])c.add(h);xw(l,m.totalTokens);for(let[h,b]of Object.entries(m.byModel))n[h]||={usd:0,events:0,tokens:kw()},n[h].usd=Il(n[h].usd+b.usd),n[h].events+=b.events,xw(n[h].tokens,b.tokens);for(let[h,b]of Object.entries(m.byProvider))r[h]||={usd:0,events:0},r[h].usd=Il(r[h].usd+b.usd),r[h].events+=b.events;e.push({id:f.id,name:f.name,usd:m.totalUsd,rollup:m})}return e.sort((f,p)=>p.usd-f.usd),{generatedAt:new Date().toISOString(),pricingBasis:"api-equivalent (subscription usage priced at public API rates)",fleet:{totalUsd:Il(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 bo=t=>`$${t.toFixed(4)}`,Sr=(t,e)=>String(t).padEnd(e);function vw(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 ${bo(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(` ${Sr(a,26)} ${Sr(bo(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(` ${Sr(a.name||a.id,30)} ${bo(a.usd)}`);let i=Object.values(a.rollup.byBox).sort((c,l)=>l.usd-c.usd);for(let c of i)if(r.push(` ${Sr(c.label,40)} ${Sr(bo(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))}${Sr(bo(u.usd),10)} ${u.models.join(",")||"?"}`)}return r.join(`
|
|
296
|
+
`)}import PT from"node:fs/promises";import NT from"node:path";var _T=["bug-fix","orchestration","design","ops-infra","docs-writing","research","feature-build"],ET={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 ca(t){let e=String(t||"").toLowerCase();if(!e.trim())return null;let n=null;for(let r of _T){let o=0;for(let s of ET[r])s.test(e)&&o++;o>0&&(!n||o>n.score)&&(n={category:r,score:o})}return n}var IT=new Set(["Read","Grep","Glob","LS","WebSearch","WebFetch","NotebookRead"]),RT=new Set(["Edit","Write","MultiEdit","NotebookEdit"]);function Aw(t){let e=0,n=0;for(let[r,o]of Object.entries(t||{}))IT.has(r)&&(e+=o||0),RT.has(r)&&(n+=o||0);return e>=5&&n===0?{category:"research",score:1}:n>=3?{category:"feature-build",score:1}:null}function Rl(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 la=t=>t.payload?.ts??t.ts,$T=12e4,Ve=t=>Math.round(t*1e4)/1e4,OT=()=>({usd:0,activeMs:0,turns:0,toolCalls:0,models:{},providers:{},byTrack:new Map});function CT(t,{since:e}){let{track:n,events:r,brief:o}=t,s=v=>la(v)>=e,a=ca([Rl(n?.name),Rl(n?.workspace?.branch),o].filter(Boolean).join(" ")),i=new Map;for(let v of r)if(v.type==="subtask.delegated"&&v.boxId&&v.payload?.task){let E=ca(v.payload.task);E&&i.set(v.boxId,E)}let c=new Map,l=v=>{let E=v||"unattributed";return c.has(E)||c.set(E,{segments:[u(null)],lastTs:null,provider:null}),c.get(E)},u=v=>({textCat:v,tools:{},usd:0,tokens:{input:0,output:0,cacheRead:0,cacheWrite:0},models:{},provider:null,activeMs:0,turns:0,toolCalls:0}),d=[...r].sort((v,E)=>la(v)-la(E)),f=0,p=0,m=0,h={turns:0};for(let v of d){let E=l(v.boxId),R=E.segments[E.segments.length-1],st=la(v);switch(E.lastTs!=null&&st>E.lastTs&&s(v)&&(R.activeMs+=Math.min(st-E.lastTs,$T)),E.lastTs=st,v.provider&&(E.provider=v.provider),v.type){case"turn.user":{let I=v.payload?.text;if(v.source==="hook"||!String(I||"").trim())break;let L=u(ca(I));E.segments.push(L),s(v)&&(L.turns+=1);break}case"tool.called":{if(v.source==="transcript")break;let I=v.payload?.tool;I&&(R.tools[I]=(R.tools[I]||0)+1),s(v)&&(R.toolCalls+=1);break}case"cost.recorded":{if(!s(v))break;let I=v.payload||{},L={input:I.tokens?.input||0,output:I.tokens?.output||0,cacheRead:I.tokens?.cacheRead||0,cacheWrite:I.tokens?.cacheWrite??I.tokens?.cacheCreation??0},T=Number(I.usd)||0;!T&&I.tokens&&(T=zt(I.model,L)),!T&&I.tokens&&!Xo(I.model)&&(h.turns+=1),R.usd+=T,R.tokens.input+=L.input,R.tokens.output+=L.output,R.tokens.cacheRead+=L.cacheRead,R.tokens.cacheWrite+=L.cacheWrite;let U=I.model||"unknown";R.models[U]=(R.models[U]||0)+T,v.provider&&(R.provider=v.provider);break}case"handoff.done":s(v)&&(f+=1);break;case"subtask.merged":s(v)&&(p+=1);break;case"error":s(v)&&v.payload?.kind==="subtask-failed"&&(m+=1);break;default:break}}let b=new Map,x=!1;for(let[v,E]of c)for(let R of E.segments){if(!(R.usd>0||R.activeMs>0||R.turns>0||R.toolCalls>0))continue;let I=R.textCat?.category??i.get(v)?.category??Aw(R.tools)?.category??a?.category??"uncategorized";b.has(I)||b.set(I,{usd:0,activeMs:0,turns:0,toolCalls:0,models:{},providers:{}});let L=b.get(I);L.usd+=R.usd,L.activeMs+=R.activeMs,L.turns+=R.turns,L.toolCalls+=R.toolCalls;for(let[U,J]of Object.entries(R.models))L.models[U]=(L.models[U]||0)+J;let T=R.provider||E.provider;T&&(L.providers[T]=(L.providers[T]||0)+R.usd),T==="cursor"&&R.usd===0&&(x=!0)}return{perCategory:b,handoffs:f,merged:p,failed:m,sawCursorActivity:x,unpricedTurns:h.turns}}function _w(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 h of t||[]){let{track:b,events:x}=h;if(!x?.length){a+=1;continue}let{perCategory:v,handoffs:E,merged:R,failed:st,sawCursorActivity:I,unpricedTurns:L}=CT(h,{since:r});I&&(i=!0),c+=L;let T={trackId:b?.id,name:b?.name||b?.id,archived:!!b?.archivedAt,usd:0,activeMs:0,turns:0,categories:{},models:{},providers:{},handoffs:E,subtasksMerged:R,subtasksFailed:st};for(let[U,J]of v){T.usd+=J.usd,T.activeMs+=J.activeMs,T.turns+=J.turns,T.categories[U]={usd:Ve(J.usd),activeMs:J.activeMs};for(let[y,A]of Object.entries(J.models))T.models[y]=Ve((T.models[y]||0)+A);for(let[y,A]of Object.entries(J.providers))T.providers[y]=Ve((T.providers[y]||0)+A);o.has(U)||o.set(U,OT());let g=o.get(U);g.usd+=J.usd,g.activeMs+=J.activeMs,g.turns+=J.turns,g.toolCalls+=J.toolCalls;for(let[y,A]of Object.entries(J.models))g.models[y]=(g.models[y]||0)+A;for(let[y,A]of Object.entries(J.providers))g.providers[y]=(g.providers[y]||0)+A;let w=g.byTrack.get(b?.id)||{usd:0,activeMs:0};w.usd+=J.usd,w.activeMs+=J.activeMs,g.byTrack.set(b?.id,w)}(T.usd>0||T.activeMs>0||T.turns>0)&&(T.usd=Ve(T.usd),T.dominantCategory=MT(T.categories),s.push(T))}let l=[...o.values()].reduce((h,b)=>h+b.usd,0),u=[...o.values()].reduce((h,b)=>h+b.activeMs,0),d=l>0?"usd":"time",f=d==="usd"?l:u,p=new Map((t||[]).map(h=>[h.track?.id,h.track?.name||h.track?.id])),m=[...o.entries()].map(([h,b])=>({category:h,sharePct:f>0?Math.round((d==="usd"?b.usd:b.activeMs)/f*100):0,shareBasis:d,usd:Ve(b.usd),activeMs:b.activeMs,turns:b.turns,toolCalls:b.toolCalls,models:Tw(b.models),providers:Tw(b.providers),topWorkstreams:[...b.byTrack.entries()].sort((x,v)=>v[1].usd-x[1].usd||v[1].activeMs-x[1].activeMs).slice(0,2).map(([x,v])=>({trackId:x,name:p.get(x),usd:Ve(v.usd),activeMs:v.activeMs}))})).sort((h,b)=>b.usd-h.usd||b.activeMs-h.activeMs);return s.sort((h,b)=>b.usd-h.usd||b.activeMs-h.activeMs),{since:r,capturedAt:n,totalUsd:Ve(l),activeMs:u,turns:[...o.values()].reduce((h,b)=>h+b.turns,0),bullets:m,workstreams:s,gaps:{tracksWithoutEvents:a,cursorCostUnknown:i,unpricedTurns:c}}}var MT=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},Tw=t=>Object.fromEntries(Object.entries(t).map(([e,n])=>[e,Ve(n)]));var $l={"24h":24*36e5,"7d":168*36e5,"30d":720*36e5,all:null};function BT(t){let e=String(t||"7d");return e in $l?{key:e,ms:$l[e]}:{key:"7d",ms:$l["7d"]}}function DT(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=$s(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 jT(t){if(!t?.cwd)return null;try{let n=(await PT.readFile(NT.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 Ew(t,{window:e="7d",now:n=Date.now()}={}){let{key:r,ms:o}=BT(e),s=[];for(let a of t||[]){let i=await F(a);s.push({track:a,events:DT(a,i),brief:await jT(a)})}return{window:r,..._w(s,{windowMs:o,now:n})}}import vr from"node:fs/promises";import LT from"node:fs";import mt from"node:path";import FT from"node:os";import{execFile as UT}from"node:child_process";import{promisify as HT}from"node:util";var WT=HT(UT),Iw=process.env.BOSTRAT_STATE_DIR||mt.join(process.env.HOME||FT.tmpdir(),".bostrat"),Ol=!1;async function ko(t,e){let n=await vr.stat(t).catch(()=>null);if(!n?.isFile())return!1;let r=await vr.stat(e).catch(()=>null);return r&&r.size===n.size&&r.mtimeMs>=n.mtimeMs?!1:(await vr.mkdir(mt.dirname(e),{recursive:!0}),await vr.copyFile(t,e),!0)}var qT=new Set([".claude",".codex",".credentials.json","auth.json",".claude.json","integrations.json"]);async function xo(t,e){let n=0,r=await vr.readdir(t,{withFileTypes:!0}).catch(()=>[]);for(let o of r){if(qT.has(o.name))continue;let s=mt.join(t,o.name);o.isDirectory()?n+=await xo(s,mt.join(e,o.name)):o.isFile()&&(n+=await ko(s,mt.join(e,o.name))?1:0)}return n}async function Rw({stateDir:t=Iw,dest:e=process.env.BOSTRAT_SYNC_DIR||`${Iw}-sync`,remote:n=process.env.BOSTRAT_STATE_REMOTE||null,tracks:r=null}={}){try{await vr.mkdir(e,{recursive:!0});let o=0;o+=await ko(mt.join(t,"tracks.json"),mt.join(e,"tracks.json"))?1:0,o+=await ko(mt.join(t,"mocks.json"),mt.join(e,"mocks.json"))?1:0,o+=await ko(mt.join(t,"runbooks.json"),mt.join(e,"runbooks.json"))?1:0,o+=await xo(mt.join(t,"runbooks"),mt.join(e,"runbooks")),o+=await xo(mt.join(t,"mock-archive"),mt.join(e,"mock-archive")),o+=await ko(mt.join(t,"events.jsonl"),mt.join(e,"events.jsonl"))?1:0,o+=await xo(mt.join(t,"events"),mt.join(e,"events"));for(let l of r||[])!l?.cwd||!l?.id||(o+=await xo(mt.join(l.cwd,".bostrat"),mt.join(e,"workspaces",l.id,".bostrat")));let s=(...l)=>WT("git",["-C",e,...l]);LT.existsSync(mt.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 Ui(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||Ol))try{await s("push","-q",n,"HEAD:refs/heads/main"),c=!0,Ol=!1}catch{Ol=!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 GT}from"node:child_process";import{promisify as KT}from"node:util";import{randomUUID as VT}from"node:crypto";import JT from"node:fs";import zT from"node:os";import XT from"node:path";var ze=KT(GT),ua=t=>new Promise(e=>setTimeout(e,t)),Xe=Ea,Cl=process.env.BOSTRAT_USAGE_PROBE_CWD||XT.join(zT.homedir(),".bostrat","usage-probe"),YT=Number(process.env.BOSTRAT_USAGE_IDLE_REAP_MS)||5*6e4,ZT=Number(process.env.BOSTRAT_USAGE_PANEL_TIMEOUT_MS)||15e3,QT=Number(process.env.BOSTRAT_USAGE_READY_TIMEOUT_MS)||45e3,$w=Number(process.env.BOSTRAT_USAGE_CACHE_MS)||2e4,Je=null,So=null,vo=null;async function t_(){try{return await ze(C,P("has-session","-t",Xe)),!0}catch{return!1}}function e_(){Je&&clearTimeout(Je),Je=setTimeout(()=>n_().catch(()=>{}),YT),Je.unref&&Je.unref()}async function n_(){Je&&(clearTimeout(Je),Je=null);try{await ze(C,P("kill-session","-t",Xe))}catch{}}async function r_(){let t=Date.now()+QT;for(;;){let e=await Ml().catch(()=>"");if(/for agents|for shortcuts|bypass permissions on/i.test(e)){await ua(500);return}if(Date.now()>t)throw new Error("usage probe didn't reach the prompt (trust/onboarding may be blocking)");await ua(500)}}async function o_(){if(await t_())return;if(!await Se())throw new Error("host tmux server not running \u2014 start globo-tmux.service");JT.mkdirSync(Cl,{recursive:!0}),await xe(Cl);let t=VT();await ze(C,P("new-session","-d","-s",Xe,"-c",Cl,"-x","200","-y","60"));let e=`${_a} --session-id ${t}`;await ze(C,P("send-keys","-t",Xe,e,"Enter")),await r_()}async function Ml(){let{stdout:t}=await ze(C,P("capture-pane","-t",Xe,"-p"));return t}async function s_(){await o_(),await ze(C,P("send-keys","-t",Xe,"-l","/usage")),await ze(C,P("send-keys","-t",Xe,"Enter"));let t=Date.now()+ZT,e="";for(;await ua(400),e=await Ml(),!(/Current\s+(session|week)/i.test(e)||/\d+%\s*used/i.test(e)||Date.now()>t););return await ua(900),e=await Ml(),await ze(C,P("send-keys","-t",Xe,"Escape")).catch(()=>{}),e_(),e}function a_(t){let e=String(t||"").split(`
|
|
297
|
+
`).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 i_(t){let e=String(t||"").split(`
|
|
298
|
+
`).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 Ow({fresh:t=!1}={}){return!t&&So&&Date.now()-So.at<$w?So.data:vo||(vo=(async()=>{try{let e=await s_(),n=a_(e),r={...n,contributors:i_(e),subscription:!!(n.session||n.week||n.weekModels),capturedAt:Date.now()};return So={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 So={at:Date.now()-($w-3e3),data:n},n}finally{vo=null}})(),vo)}var c_=262144;function l_(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 Cw(t){if(!t||typeof t!="object")return null;let e=Number(t.used_percent);return Number.isFinite(e)?{pct:Math.round(e),resets:l_(t.resets_in_seconds)}:null}function u_(t){let e=String(t||"").split(`
|
|
299
|
+
`),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:Cw(i.primary),week:Cw(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 d_(t){let e={provider:"codex",supported:!0,contributors:null};try{let n=await mn(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 Et(n.file,c_),o=u_(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 Mw(t,{fresh:e=!1}={}){let n=t?.agent||"claude";return n==="codex"?d_(t):n==="cursor"?{provider:"cursor",supported:!1,subscription:!1,session:null,week:null,contributors:null,capturedAt:Date.now()}:{provider:"claude",supported:!0,...await Ow({fresh:e})}}function Pw(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 f_(t){let[e,n]=t.split("/"),r=Pw(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 p_(t){return(t||"").split(",").map(e=>e.trim()).filter(Boolean).map(f_).filter(Boolean)}function m_(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 Nw(t){let e=p_(t),n=e.length===0;return{open:n,clientIp:m_,allowed(r){if(n)return!0;let o=Pw((r||"").replace(/^::ffff:/,""));return o==null?!1:e.some(s=>s(o))}}}import h_ from"node:http";import w_ from"node:net";function Bw(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 Dw(t){let e=Bw(t.headers.host);return e?{slug:e,view:ii(e)}:null}var g_=t=>`<!doctype html><meta charset="utf-8">
|
|
254
300
|
<meta http-equiv="refresh" content="3">
|
|
255
301
|
<title>${t.name} \u2014 starting</title>
|
|
256
302
|
<body style="font-family:system-ui;background:#0b0e14;color:#dce3f0;display:grid;place-items:center;height:100vh;margin:0">
|
|
257
303
|
<div style="text-align:center"><p style="font-size:2rem;margin:0">\u23F3</p>
|
|
258
|
-
<p><b>${t.name}</b> is waking up\u2026</p><p style="opacity:.6">auto-refreshing</p></div>`;function
|
|
304
|
+
<p><b>${t.name}</b> is waking up\u2026</p><p style="opacity:.6">auto-refreshing</p></div>`;function jw(t,e,n){let r=Dw(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(g_(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=h_.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 Lw(t,e,n){let r=Dw(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
|
|
259
305
|
\r
|
|
260
306
|
bostrat: no live view "${s}"\r
|
|
261
|
-
`),e.destroy(),!0;let a=
|
|
307
|
+
`),e.destroy(),!0;let a=w_.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
|
|
262
308
|
`)+`\r
|
|
263
309
|
\r
|
|
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-
|
|
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
|
|
267
|
-
${
|
|
310
|
+
`),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 Fw(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}.${y_(n.host)}/`}function y_(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 Bw(e)&&ii(n[0])?n.slice(1).join("."):e}import{execFile as b_}from"node:child_process";import{promisify as k_}from"node:util";var Nl=k_(b_),Uw=3e4,x_=30*6e4,S_=Uw,v_=10*6e4,A_=10080*6e4,T_=60*6e4,__=8,E_=1440*6e4,I_=3,Hw="number,url,title,headRefName,headRefOid,state,mergedAt,closedAt",Pl=60*6e4,R_=15*6e4,da=new Map,fa=new Map,$_=/could not resolve|not found|no pull requests?/i,O_=t=>$_.test(String(t?.message||t||""));function C_(t,e){return[t.ciFix?.delegatedAt,t.reconcile?.delegatedAt,t.ciFixAwait?.since,t.reconcileAwait?.since].some(n=>n&&e-n<Pl)}var Ww={fast:S_,slow:v_,decay:T_};function qw(t,e){return t.armedAt&&e-t.armedAt<x_||C_(t,e)?"fast":e-(t.firstSeen||0)>A_?"decay":"slow"}function M_(t,e){return Ww[qw(t,e)]}function Eo(t,e,n,r){let o=t.lanes?.get(it(e.repo,e.number))||{};return Hs({change:n,repo:e.repo,number:Number(e.number),githubAt:r||null,lane:o.lane||null,intervalMs:o.intervalMs||null,source:"watch"})}async function P_(t,e){we("watch");try{let{stdout:n}=await Nl("gh",["pr","view",String(e),"--repo",t,"--json",Hw],{timeout:3e4,maxBuffer:4194304,env:{...process.env,...await re({slug:t})}});return JSON.parse(n||"null")}catch(n){throw je(n)&&Le(n),n}}async function N_(t){we("watch");try{let{stdout:e}=await Nl("gh",["pr","list","--repo",t,"--state","all","--limit","100","--json",Hw],{timeout:3e4,maxBuffer:8388608,env:{...process.env,...await re({slug:t})}}),n=new Map;for(let r of JSON.parse(e||"[]"))n.set(Number(r.number),r);return n}catch(e){throw je(e)&&Le(e),e}}async function B_(){let t=new Map;for(let e of await ut().catch(()=>[]))t.set(e.id,e);return t}async function D_(t,e){await O({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 j_(t,e){let n=await Sn(t);return n?dr(n,e):null}async function L_(t,e,n){let r=await Sn(t);if(!r)throw new Error(`no local checkout for ${t}`);return fr(r,e,{method:n})}async function F_(t,e){let n=await Sn(t);if(!n)throw new Error(`no local checkout for ${t}`);return ao(n,e)}async function U_(t,e){await O({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 Ar(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 H_=10*6e4;async function W_(t,e,n){let r=t.autoMerge?.armedAt||0;if(r&&(n.now??Date.now())-r>E_){await Ar(t,null,e,n,"auto-merge expired \u2014 checks never settled within 24h");return}let o=await n.detail(t.repo,t.number).catch(()=>null);if(!o)return;if(o.checks.failed>0){let a=o.checks.failing.map(i=>i.name).join(", ");await Ar(t,o,e,n,`check${o.checks.failed===1?"":"s"} failed: ${a}`);return}if(o.mergeable==="CONFLICTING"){await Ar(t,o,e,n,`conflicts with ${o.baseRefName}`);return}if(!o.checks.total){let a=Math.max(t.autoMerge?.armedAt||0,Date.parse(o.updatedAt||"")||0);if((n.now??Date.now())-a<H_)return;await Ar(t,o,e,n,o.checks.unavailable?"this token can't read the checks":"no CI checks appeared");return}if(o.checks.pending>0||o.mergeable==="UNKNOWN"||o.mergeStateStatus==="UNKNOWN"||o.isDraft)return;let s=await n.lookup(t.repo,t.number).catch(()=>null);if(s?.autoMerge){if(o.mergeStateStatus==="BEHIND"){try{await n.updateBranch(t.repo,t.number)}catch(a){let i=String(a?.message||a).trim();/no new commits|already up.to.date/i.test(i)||await Ar(t,o,e,n,i||`couldn't update with ${o.baseRefName}`)}return}try{await n.merge(t.repo,t.number,s.autoMerge.method||"merge")}catch(a){if(/already merged|not open|is closed/i.test(String(a?.message||a)))return;await Ar(t,o,e,n,String(a?.message||a).trim()||"merge failed");return}await Gw(t,{...o,state:"MERGED"},e,n,{change:"auto-merged",githubAt:o.checks.settledAt||null})}}async function Ao(t,e,n){await O({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 q_(t,e){try{return await Nl("git",["-C",t.cwd,"cat-file","-e",`${e}^{commit}`],{timeout:1e4}),!0}catch{return!1}}async function To(t,e,n,r=()=>!0){return(await F(t).catch(()=>[])).some(s=>s.type==="pr.state.changed"&&s.payload?.change===e&&s.payload?.number===Number(n)&&r(s))}var _o=(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 G_(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 ws(t.repo,t.number,{headSha:s}).catch(()=>{}),t.reconcile?.headSha&&s!==t.reconcile.headSha&&await ys(t.repo,t.number,{headSha:s}).catch(()=>{})),s&&s!==t.lastHeadSha){let i=!t.lastHeadSha,c=!!(t.ciFix||t.reconcile||t.ciFixAwait||t.reconcileAwait),l=!!t.baseFoldAt&&o-t.baseFoldAt<R_;await Rf(t.repo,t.number,s).catch(()=>{}),t.baseFoldAt&&await If(t.repo,t.number).catch(()=>{}),!i&&!c&&!l&&a&&!await r.localHasCommit(a,s).catch(()=>!1)&&!await To(a,"pushed",t.number,d=>d.payload?.sha===s)&&await Ao(a,"pushed",{..._o(t,e),sha:s}).catch(()=>{})}if(t.ciFixAwait)if(o-(t.ciFixAwait.since||0)>Pl)await gs(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 gs(t.repo,t.number).catch(()=>{});else if(i.checks.total>0&&i.checks.pending===0){let c=Eo(r,t,"ci-fixed",i.checks.settledAt||null),l=t.ciFixAwait.since||0;a&&!await To(a,"ci-fixed",t.number,u=>(u.payload?.ts||0)>=l)&&await Ao(a,"ci-fixed",{..._o(t,e),passed:i.checks.passed,githubAt:c.githubAt,latencyMs:c.latencyMs,lane:c.lane}).catch(()=>{}),await gs(t.repo,t.number).catch(()=>{})}}}if(t.reconcileAwait)if(o-(t.reconcileAwait.since||0)>Pl)await bs(t.repo,t.number).catch(()=>{});else{let i=await r.detail(t.repo,t.number).catch(()=>null);if(i){if(i.mergeable==="CONFLICTING")await bs(t.repo,t.number).catch(()=>{});else if(i.mergeable==="MERGEABLE"){Eo(r,t,"reconciled",null);let c=t.reconcileAwait.since||0;a&&!await To(a,"reconciled",t.number,l=>(l.payload?.ts||0)>=c)&&await Ao(a,"reconciled",{..._o(t,e),base:i.baseRefName||null}).catch(()=>{}),await bs(t.repo,t.number).catch(()=>{})}}}}async function K_(t,e,n,r){let o=Eo(r,t,"closed",Date.parse(e?.closedAt||"")||null),s=n.get(t.trackId);s&&!await To(s,"closed",t.number)&&await Ao(s,"closed",{..._o(t,e),githubAt:o.githubAt,latencyMs:o.latencyMs,lane:o.lane}).catch(()=>{}),await r.notifyIfResolved(t.repo,t.number,"closed").catch(()=>{}),await r.mark(t.repo,t.number,"closed").catch(()=>{})}async function V_(t,e,n){Eo(n,t,"lost",null);let r=e.get(t.trackId);r&&!await To(r,"lost",t.number)&&await Ao(r,"lost",_o(t,null)).catch(()=>{}),await n.notifyIfResolved(t.repo,t.number,"lost").catch(()=>{}),await n.mark(t.repo,t.number,"lost").catch(()=>{})}async function Gw(t,e,n,r,o={}){let s=Eo(r,t,o.change||"merged",o.githubAt??(Date.parse(e.mergedAt||"")||null)),a=n.get(t.trackId);a&&((await F(a).catch(()=>[])).some(l=>l.type==="pr.state.changed"&&l.payload?.change==="merged"&&l.payload?.number===Number(t.number))||await r.emitMerged(a,{number:Number(t.number),url:e.url||t.url,title:e.title||t.title,branch:e.headRefName||t.headRef,repo:t.repo,githubAt:s.githubAt,latencyMs:s.latencyMs,lane:s.lane}).catch(()=>{})),await r.notifyIfArmed(t.repo,t.number).catch(()=>{}),await r.archiveIfArmed(t.repo,t.number).catch(()=>{}),await r.mark(t.repo,t.number,"merged").catch(()=>{})}async function J_(t={}){let{now:e=Date.now(),auth:n=Uc,view:r=P_,list:o=N_,links:s=Of,trackMap:a=B_,emitMerged:i=D_,mark:c=Ce,archiveIfArmed:l=Ue,notifyIfArmed:u=He,notifyIfResolved:d=ul,batchThreshold:f=I_,detail:p=j_,merge:m=L_,updateBranch:h=F_,lookup:b=vt,disarm:x=Vr,emitDisarmed:v=U_,localHasCommit:E=q_}=t;if(!(await n().catch(()=>null))?.authenticated||Ps()>0)return 0;let I=(await s().catch(()=>[])).filter(y=>e-(da.get(it(y.repo,y.number))||0)>=M_(y,e));if(!I.length)return 0;let L=Date.now(),T=new Map,U=new Map;for(let y of I){let A=qw(y,e);T.set(it(y.repo,y.number),{lane:A,intervalMs:Ww[A]}),da.set(it(y.repo,y.number),e),U.has(y.repo)||U.set(y.repo,[]),U.get(y.repo).push(y)}let J=await a().catch(()=>new Map),g={now:e,lanes:T,emitMerged:i,mark:c,archiveIfArmed:l,notifyIfArmed:u,notifyIfResolved:d,detail:p,merge:m,updateBranch:h,lookup:b,disarm:x,emitDisarmed:v,localHasCommit:E};for(let[y,A]of U){let K,B=new Set;if(A.length>f)K=await o(y).catch(()=>null);else{K=new Map;for(let $ of A)try{let _=await r(y,$.number);_?.number!=null&&K.set(Number(_.number),_)}catch(_){O_(_)&&B.add(it($.repo,$.number))}}if(K)for(let $ of A){let _=it($.repo,$.number),M=K.get(Number($.number));if(!M){if(B.has(_)){let at=(fa.get(_)||0)+1;fa.set(_,at),at>=__&&(await V_($,J,g),fa.delete(_),da.delete(_))}continue}fa.delete(_),M.state==="MERGED"||M.state==="CLOSED"?(M.state==="MERGED"?await Gw($,M,J,g):await K_($,M,J,g),da.delete(_)):(await G_($,M,J,g).catch(()=>{}),$.autoMerge&&await W_($,J,g).catch(()=>{}))}}let w={};for(let{lane:y}of T.values())w[y]=(w[y]||0)+1;return console.log(`[pr-watch] probed ${I.length} link(s) (${Object.entries(w).map(([y,A])=>`${y} ${A}`).join(", ")}) in ${Date.now()-L}ms`),I.length}function Kw(){let t=!1;setInterval(async()=>{if(!t){t=!0;try{await J_()}catch{}finally{t=!1}}},Uw)}import ke from"node:fs/promises";import Tr from"node:path";import Qw from"node:os";import Vw from"node:fs/promises";import pa from"node:path";import{execFile as z_}from"node:child_process";import{promisify as X_}from"node:util";import{fileURLToPath as Y_}from"node:url";var Z_=X_(z_),zw=pa.dirname(Y_(import.meta.url)),Q_=pa.resolve(zw,"..","..","client","dist"),tE="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/",eE=new Map([...tE].map((t,e)=>[t,e]));function nE(t){let e=[],n=0,r=0;for(let o of t){let s=eE.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 rE(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=nE(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 oE(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 Jw=new Map;async function sE(t){let e;try{e=await Vw.stat(t)}catch{return null}let n=Jw.get(t);if(n&&n.mtimeMs===e.mtimeMs)return n;try{let r=JSON.parse(await Vw.readFile(t,"utf8")),o={mtimeMs:e.mtimeMs,sources:r.sources||[],lines:rE(r.mappings||"")};return Jw.set(t,o),o}catch{return null}}var Bl=null;function aE(){return Bl||(Bl=process.env.BOSTRAT_RELEASE?Promise.resolve(process.env.BOSTRAT_RELEASE):Z_("git",["rev-parse","--short","HEAD"],{cwd:pa.resolve(zw,"..","..")}).then(t=>t.stdout.trim()).catch(()=>null)),Bl}var iE=/(?:https?:\/\/[^\s()]*)?\/assets\/([^\s()/]+\.js):(\d+):(\d+)/g;async function cE(t,{distDir:e=Q_}={}){let n=[],r=0,o=!1;for(let s of t.matchAll(iE)){let[a,i,c,l]=s,u=await sE(pa.join(e,"assets",`${i}.map`)),d=u?oE(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 Xw(t,e={}){try{if(!t?.error?.stack||!t.release||t.release==="unknown")return t;let n=e.release??await aE();if(!n||t.release!==n)return t;let{stack:r,symbolicated:o}=await cE(t.error.stack,e);o&&(t.error.stack=r,t.error.symbolicated=!0)}catch{}return t}var lE=()=>process.env.BOSTRAT_STATE_DIR||Tr.join(process.env.HOME||Qw.tmpdir(),".bostrat"),Ye=()=>Tr.join(lE(),"telemetry"),tg=()=>Tr.join(Ye(),".relay-offsets.json"),uE=50,dE=14,fE=3e4,Yw=500,Bt=(t,e)=>typeof t=="string"?t.slice(0,e):"";function pE(t,e){if(!t||typeof t!="object")return null;let n=Bt(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:Bt(t.anonId,64),sessionId:Bt(t.sessionId,64),userId:Bt(t.userId,64),release:Bt(t.release,64),appVersion:Bt(t.appVersion,32),route:Bt(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:Bt(t.error.kind,256),message:Bt(t.error.message,8192),stack:Bt(t.error.stack,32768),fingerprint:Bt(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:Bt(s?.n,64),r:Bt(s?.r,128)}))),o}var eg=t=>new Date(t).toISOString().slice(0,10),Zw=0;async function ng(t,e=Date.now()){let r=(Array.isArray(t?.events)?t.events.slice(0,uE):[]).map(s=>pE(s,e)).filter(Boolean);if(!r.length)return 0;for(let s of r)s.error?.stack&&await Xw(s);await ke.mkdir(Ye(),{recursive:!0});let o=Tr.join(Ye(),`${eg(e)}.ndjson`);return await ke.appendFile(o,r.map(s=>JSON.stringify(s)+`
|
|
311
|
+
`).join(""),"utf8"),e-Zw>6*3600*1e3&&(Zw=e,mE(e).catch(()=>{})),r.length}async function mE(t=Date.now()){let e=eg(t-dE*24*3600*1e3),n=[];try{n=await ke.readdir(Ye())}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 ke.rm(Tr.join(Ye(),s),{force:!0});let o=await og();for(let s of r)delete o[s];await sg(o)}var ma=new Map;function rg(t,e=Date.now()){let n=t||"unknown",r=ma.get(n);return r||(ma.size>512&&ma.clear(),r={tokens:40,at:e},ma.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 og(){try{return JSON.parse(await ke.readFile(tg(),"utf8"))||{}}catch{return{}}}async function sg(t){await ke.mkdir(Ye(),{recursive:!0}),await ke.writeFile(tg(),JSON.stringify(t),"utf8")}function hE(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||Qw.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 wE({fetchImpl:t=fetch}={}){let e=await Xd();if(!e)return{sent:0,disabled:!0};let n;try{n=(await ke.readdir(Ye())).filter(s=>s.endsWith(".ndjson")).sort()}catch{return{sent:0,disabled:!1}}let r=await og(),o=0;for(let s of n){let a=Tr.join(Ye(),s),i=r[s]||0,c;try{let f=await ke.open(a,"r");try{let p=(await f.stat()).size;if(p<=i)continue;c=Buffer.alloc(p-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(`
|
|
312
|
+
`).filter(Boolean),d=[];for(let f of u)try{d.push(JSON.parse(f))}catch{}for(let f=0;f<d.length;f+=Yw){let p=d.slice(f,f+Yw).map(h=>hE(h)),m=await t(`${e.url.replace(/\/$/,"")}/v1/logs`,{method:"POST",headers:{"content-type":"application/json",authorization:`Bearer ${e.ingestKey}`},body:JSON.stringify({events:p})}).catch(()=>null);if(!m||!m.ok)return{sent:o,disabled:!1};o+=p.length}r[s]=i+l+1,await sg(r)}return{sent:o,disabled:!1}}var Dl=null,jl=!1;function ag(){Dl||(Dl=setInterval(async()=>{if(!jl){jl=!0;try{await wE()}catch(t){console.error("[telemetry] relay failed:",t?.message||t)}finally{jl=!1}}},fE),Dl.unref?.())}import wa from"node:fs/promises";import dg from"node:path";import gE from"node:os";var fg=()=>process.env.BOSTRAT_STATE_DIR||dg.join(process.env.HOME||gE.tmpdir(),".bostrat"),pg=()=>dg.join(fg(),"dictation-glossary.json"),yE=100,bE=500,kE=["bostrat","globopi","codex","tmux","worktree","Deepgram","Linear","Firecracker","Tigris","WireGuard","misous","postyx","cedar","gerbil","Anthropic","Opus","Haiku","Sonnet","keyterm","monorepo","dogfood"],ga=null,Ll=null,Fl=Promise.resolve();function ig(){let t={},e=Date.now();for(let n of kE)t[n.toLowerCase()]={term:n,count:1,lastUsed:e,source:"seed"};return t}async function mg(){return ga||(Ll??=wa.readFile(pg(),"utf8").then(t=>JSON.parse(t)?.terms??ig()).catch(()=>ig()).then(t=>ga=t).finally(()=>Ll=null),Ll)}async function cg(){let t=JSON.stringify({terms:ga},null,2),e=pg(),n=`${e}.${process.pid}.tmp`;await wa.mkdir(fg(),{recursive:!0}),await wa.writeFile(n,t),await wa.rename(n,e)}function xE(){return Fl=Fl.then(cg,cg),Fl.catch(()=>{})}async function Ul(){return Object.values(await mg()).sort((e,n)=>n.count-e.count||n.lastUsed-e.lastUsed)}var SE=t=>Math.max(1,Math.ceil(t.length/4));async function vE(){let t=[],e=0;for(let{term:n}of await Ul()){let r=SE(n);if(!(e+r>bE)&&(t.push(n),e+=r,t.length>=yE))break}return t}async function AE(t,e){let n=await mg(),r=t.toLowerCase(),o=n[r];n[r]={term:t,count:(o?.count??0)+1,lastUsed:Date.now(),source:o?.source??e}}var lg=t=>(t||"").split(/\s+/).map(e=>e.replace(/^[^\p{L}\p{N}]+|[^\p{L}\p{N}]+$/gu,"")).filter(Boolean),ha=t=>t.toLowerCase();function ug(t){return!new RegExp("\\p{L}","u").test(t)||t.length<3?!1:/^[a-z]+$/.test(t)?t.length>=4:!0}async function hg(t,e){let n=lg(t),r=lg(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]=ha(n[u])===ha(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;)ha(n[c])===ha(r[l])?(c++,l++):a[c+1][l]>=a[c][l+1]?c++:(ug(r[l])&&i.add(r[l]),l++);for(;l<s;l++)ug(r[l])&&i.add(r[l]);if(!i.size)return[];for(let u of i)await AE(u,"learned");return await xE(),[...i]}var Io=null;function TE(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 wg(){if(!Io)return null;let t=await Ur();if(!t||t.apiKey.slice(-6)!==Io.keyTail)return null;let{keyTail:e,...n}=Io;return n}async function gg(t,e){let n=await Ur();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 vE())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=TE(o.status,i);c.persistent&&(Io={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 Io=null,{transcript:(await o.json())?.results?.channels?.[0]?.alternatives?.[0]?.transcript??""}}var Sg=lt.dirname(EE(import.meta.url)),yg=Number(process.env.PORT)||3362,ya=hw(process.env),vg=Number(process.env.BOSTRAT_AUTO_TRIGGER_PCT)||80,_r=Nw(process.env.BOSTRAT_ALLOW_IPS),S=ie();S.disable("x-powered-by");S.use((t,e,n)=>{if(_r.allowed(_r.clientIp(t)))return n();e.status(403).type("text/plain").send("bostrat: access restricted to the allowlist")});S.use(jw);S.use("/api/mocks",ie.json({limit:"1mb"}));S.use("/api/mocks",ie.raw({type:"image/png",limit:"24mb"}));S.use("/api/runbooks",ie.json({limit:"1mb"}));S.use("/api/telemetry",ie.json({limit:"256kb"}));S.use("/api/hook",ie.json({limit:"2mb"}));S.use("/api/dictation",ie.raw({type:["audio/*","video/mp4","application/octet-stream"],limit:"32mb"}));S.use("/api/tracks/:id/upload",ie.raw({type:()=>!0,limit:"25mb"}));S.use(ie.json({limit:"64kb"}));S.get("/api/health",async(t,e)=>{e.json({ok:!0,tmuxSocket:Mt,hostTmuxAlive:await Se(),autoTriggerPct:vg})});S.get("/api/sessions",async(t,e)=>{e.json({sessions:await Nn()})});S.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 Qe(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})}});S.delete("/api/sessions/:name",async(t,e)=>{let n=decodeURIComponent(t.params.name);if(!pt(n))return e.status(400).json({error:"invalid session name"});if(!await z(n))return e.status(404).json({error:"session not found"});try{await Jt(n),e.json({ok:!0})}catch(r){e.status(500).json({error:`failed to kill: ${r?.message||r}`})}});S.get("/api/context/:name",async(t,e)=>{let n=decodeURIComponent(t.params.name);if(!pt(n))return e.status(400).json({error:"invalid session name"});let r=await z(n);if(!r)return e.status(404).json({error:"session not found"});let o=await Cr(r.cwd,r.sessionId);if(!o)return e.json({available:!1,cwd:r.cwd,sessionId:r.sessionId||null});e.json({available:!0,...o})});S.post("/api/handoff/:name",async(t,e)=>{let n=decodeURIComponent(t.params.name);if(!pt(n))return e.status(400).json({error:"invalid session name"});try{let r=await Gu(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)})}});S.get("/api/handoff/:name",(t,e)=>{let n=decodeURIComponent(t.params.name),r=Vo(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})});S.get("/api/tracks",async(t,e)=>{e.json({tracks:await ut(),agents:Ys,agentAvailability:await ol(),tickets:await fs()})});S.get("/api/cost",async(t,e)=>{try{let n=await Sw(await ut());if(n.cursorUsage=qm(),t.query.format==="text")return e.type("text/plain").send(vw(n));e.json(n)}catch(n){e.status(500).json({error:n?.message||String(n)})}});S.get("/api/usage/roundup",async(t,e)=>{try{e.json(await Ew(await ut(),{window:t.query.window}))}catch(n){e.status(500).json({error:n?.message||String(n)})}});S.get("/api/arcs",async(t,e)=>{try{e.json(await Df(await ut()))}catch(n){e.status(500).json({error:n?.message||String(n)})}});S.get("/api/arcs/:key",async(t,e)=>{try{let n=await jf(await ut(),t.params.key);if(!n)return e.status(404).json({error:"unknown arc"});e.json(n)}catch(n){e.status(500).json({error:n?.message||String(n)})}});S.post("/api/arcs/backfill-beats",async(t,e)=>{try{e.json({ok:!0,...await Kh()})}catch(n){e.status(500).json({error:n?.message||String(n)})}});S.post("/api/hook",async(t,e)=>{try{let n=await mw(t.body||{},_h);e.json({ok:!0,events:n.length})}catch(n){e.json({ok:!1,error:n?.message||String(n)})}});S.post("/api/telemetry",async(t,e)=>{try{if(!rg(_r.clientIp(t)))return e.json({ok:!0,dropped:!0});let n=await ng(t.body||{});e.json({ok:!0,events:n})}catch(n){e.json({ok:!1,error:n?.message||String(n)})}});S.get("/api/repos",async(t,e)=>{try{let n=await me(),r=new Set((await Promise.all(n.map(a=>Y(a.path)))).filter(Boolean).map(a=>a.toLowerCase())),s=((await zr()).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)})}});S.get("/api/github/token",async(t,e)=>{try{e.json(await vs(String(t.query.repo||"")))}catch(n){e.status(n?.status||502).json({error:n?.message||String(n)})}});S.get("/api/github/status",(t,e)=>{e.json({configured:Ss(),calls:Sm(),latency:zm()})});S.post("/api/github/refresh",(t,e)=>{zf(),e.json({ok:!0})});function Vl(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 ba({repo:t=null,number:e=null,branch:n=null}){let r=t?await Y(t):null;if(r&&e!=null){let s=await vt(r,e),a=s&&await et(s.trackId);if(a)return a}return n?(await ut()).find(s=>s.workspace?.branch===n&&Vl(s,t,r))??null:null}async function Ag({repo:t=null,number:e=null,branch:n=null}){let r=await ba({repo:t,number:e,branch:n}).catch(()=>null);if(r&&!r.archivedAt)return r;if(!n)return null;let o=t?await Y(t).catch(()=>null):null;return(await ut()).find(a=>!a.archivedAt&&a.workspace?.branch===n&&Vl(a,t,o))??null}async function Vt(t,{number:e,url:n=null,title:r=null,branch:o=null,repo:s=null,track:a=null,extra:i={}}){let c=a||await ba({repo:s,number:e,branch:o}).catch(()=>null);if(!c)return;let l=i.repo??(s?await Y(s).catch(()=>null):null);await O({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,...l?{repo:l}:{},...i}}).catch(()=>{})}async function OE(t){let e=(await ut()).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 vt(r,o.number))continue;let s=e.find(a=>a.workspace?.branch===o.headRefName&&Vl(a,n.path,r));s&&await Pt({repo:r,number:o.number,trackId:s.id,url:o.url,headRef:o.headRefName,title:o.title,source:"matched"})}}}S.get("/api/git/prs",async(t,e)=>{try{let n=await nh();await OE(n.repos).catch(()=>{}),e.json(n)}catch(n){e.status(500).json({error:n?.message||String(n)})}});S.post("/api/git/prs/merge",async(t,e)=>{let{repo:n,number:r,method:o,auto:s,update:a,archive:i,notify:c,headRefName:l,title:u,url:d}=t.body||{};try{if(s){let h=o||"merge";if(!so.has(h))throw new Error(`invalid merge method: ${h}`);let b=Number(r);if(!Number.isInteger(b)||b<=0)throw new Error("invalid PR number");let x=await Y((await ge(n)).path);if(!x)throw new Error("repo has no GitHub remote");let v=await ba({repo:n,number:b,branch:l}).catch(()=>null);if(!v)throw new Error("auto-merge needs a Track to watch this PR \u2014 none owns it");if(a&&await ao(n,b),await Pt({repo:x,number:b,trackId:v.id,url:d,headRef:l,title:u,source:"matched"}).catch(()=>{}),!await Kr(x,b,{merge:{method:h}}))throw new Error("couldn't record the auto-merge arm");return await Yn(x,b,i===!0),await ks(x,b,c===!0&&i!==!0),se(),await Vt("auto-armed",{number:b,url:d,title:u,branch:l,repo:n,track:v,...a?{extra:{updated:!0}}:{}}),e.json({ok:!0,number:b,auto:!0})}let f=await fr(n,r,{method:o||"squash"});await Vt("merged",{number:f.number,url:d,title:u,branch:l,repo:n});let p=await Y(n).catch(()=>null),m=!1;if(p&&(i===!0&&await Yn(p,f.number,!0).catch(()=>{}),c===!0&&i!==!0&&await ks(p,f.number,!0).catch(()=>{}),await He(p,f.number).catch(()=>{}),m=await Ue(p,f.number).catch(()=>!1),await Ce(p,f.number,"merged").catch(()=>{})),i===!0&&!m){let h=await ba({repo:n,number:f.number,branch:l}).catch(()=>null);h&&!h.archivedAt&&await yr(h.id).catch(()=>{})}e.json(f)}catch(f){let p=f?.message||String(f),m=/invalid|unknown repo|required|needs a Track|no GitHub remote/i.test(p)?400:502;e.status(m).json({error:p})}});S.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 ge(n)).path).catch(()=>null),l=c?await Vr(c,i).catch(()=>null):null;try{await qc(n,i)}catch(u){if(!l)throw u}se(),await Vt("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})}});S.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 ah(n,{method:r||"merge",onMerged:a=>{Vt("merged",{number:a.number,url:a.url,title:a.title,branch:a.headRefName,repo:n}),o&&He(o,a.number).catch(()=>{}).then(()=>Ue(o,a.number)).catch(()=>{}).then(()=>Ce(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})}});S.get("/api/git/prs/merge-all",(t,e)=>{e.json({jobs:sh()})});S.post("/api/git/prs/merge-track",async(t,e)=>{let{trackId:n,update:r,archive:o,notify:s,method:a}=t.body||{};try{let i=a||"merge";if(!so.has(i))throw new Error(`invalid merge method: ${i}`);let c=n?await et(n):null;if(!c)return e.status(400).json({error:"trackId required"});let l=(await Me()).filter(f=>f.trackId===c.id&&f.state!=="merged"&&f.state!=="closed");if(!l.length)return e.status(409).json({error:"no open PRs on this Track"});for(let f of l)await Yn(f.repo,f.number,o===!0).catch(()=>{}),await ks(f.repo,f.number,s===!0&&o!==!0).catch(()=>{});let u=[],d=[];for(let f of l){let p={repo:f.repo,number:f.number,ok:!0,action:"armed"};u.push(p);try{let m=await Sn(f.repo);if(!m)throw new Error(`no local checkout for ${f.repo}`);let h=await dr(m,f.number);d.push({r:p,l:f,repoPath:m,pr:h,plan:rh(h,{update:r!==!1})})}catch(m){p.ok=!1,p.action="failed",p.reason=m?.message||String(m)}}for(let{r:f,l:p,repoPath:m,pr:h,plan:b}of oh(d))try{if(b.action==="skip")f.action="skipped",f.reason=b.reason;else if(b.action==="merge")await fr(m,p.number,{method:i}),await Vt("merged",{number:p.number,url:h.url,title:h.title,branch:h.headRefName,repo:m,track:c}),await He(p.repo,p.number).catch(()=>{}),await Ue(p.repo,p.number).catch(()=>{}),await Ce(p.repo,p.number,"merged").catch(()=>{}),f.action="merged";else{if(b.action==="update-arm"&&(await ao(m,p.number).catch(()=>{}),f.action="updated-armed"),!await Kr(p.repo,p.number,{merge:{method:i}}))throw new Error("couldn't record the auto-merge arm");await Vt("auto-armed",{number:p.number,url:h.url,title:h.title,branch:h.headRefName,repo:m,track:c,...f.action==="updated-armed"?{extra:{updated:!0}}:{}})}}catch(x){f.ok=!1,f.action="failed",f.reason=x?.message||String(x)}if(se(),!u.some(f=>f.ok))return e.status(502).json({error:u.map(f=>`${f.repo}#${f.number}: ${f.reason}`).join("; "),results:u});e.json({ok:!0,trackId:c.id,results:u})}catch(i){let c=i?.message||String(i),l=/invalid|required/i.test(c)?400:502;e.status(l).json({error:c})}});S.post("/api/git/prs/disarm-track",async(t,e)=>{let{trackId:n}=t.body||{};try{let r=n?await et(n):null;if(!r)return e.status(400).json({error:"trackId required"});let o=(await Me()).filter(a=>a.trackId===r.id&&a.state!=="merged"&&a.state!=="closed"),s=[];for(let a of o){let i=await Vr(a.repo,a.number).catch(()=>null),c=await Sn(a.repo).catch(()=>null);c&&await qc(c,a.number).catch(()=>{}),i&&(await Vt("auto-canceled",{number:a.number,url:a.url,title:a.title,branch:a.headRef,repo:c,track:r}),s.push({repo:a.repo,number:a.number}))}se(),e.json({ok:!0,trackId:r.id,canceled:s})}catch(r){let o=r?.message||String(r),s=/invalid|required/i.test(o)?400:502;e.status(s).json({error:o})}});S.post("/api/git/prs/open",async(t,e)=>{let{trackId:n,title:r,body:o,base:s,repos:a}=t.body||{};try{let i=n?await et(n):null;if(!i)return e.status(400).json({error:"trackId required"});if(i.workspace?.kind==="multi"){let u=$t(i.workspace),d,f=Array.isArray(a)?a.filter(Boolean):[];if(f.length)d=f.map(h=>{let b=u.find(x=>x.name===h||x.repo===h||x.base===h||x.dir===h);if(!b)throw new Error(`invalid repo "${h}" \u2014 not tagged on this Track`);return b});else if(d=await no(i),!d.length)return e.status(409).json({error:"no tagged repo has work to ship"});let p=[];for(let h of d)try{let b=await Kc(h.dir,{title:r,body:o,base:s}),x=h.repo||await Y(h.dir).catch(()=>null);x&&b.number&&await Pt({repo:x,number:b.number,trackId:i.id,url:b.url,headRef:i.workspace?.branch??null,title:r,source:"api"}),await Vt("opened",{number:b.number,url:b.url,title:r,track:i,branch:i.workspace?.branch,extra:{repo:x??h.name}}),p.push({repo:h.name,slug:x??null,ok:!0,url:b.url,number:b.number})}catch(b){p.push({repo:h.name,slug:h.repo??null,ok:!1,error:b?.message||String(b)})}let m=p.filter(h=>h.ok);return m.length?e.json({ok:!0,multi:!0,results:p,url:m[0].url,number:m[0].number}):e.status(502).json({error:p.map(h=>`${h.repo}: ${h.error}`).join("; "),results:p})}let c=await Kc(i.cwd,{title:r,body:o,base:s}),l=await Y(i.cwd).catch(()=>null);l&&c.number&&await Pt({repo:l,number:c.number,trackId:i.id,url:c.url,headRef:i.workspace?.branch??null,title:r,source:"api"}),await Vt("opened",{number:c.number,url:c.url,title:r,track:i,branch:i.workspace?.branch}),e.json(c)}catch(i){let c=i?.message||String(i),l=/required|invalid/i.test(c)?400:502;e.status(l).json({error:c})}});S.get("/api/tracks/:id/repos",async(t,e)=>{try{let n=await et(t.params.id);if(!n)return e.status(404).json({error:"track not found"});let r=await no(n),o=new Map(r.map(a=>[a.base,a])),s=$t(n.workspace).map(a=>{let i=o.get(a.base);return{name:a.name,base:a.base,baseRef:a.baseRef??null,repo:a.repo??null,dirty:i?.dirty??!1,ahead:i?.ahead??0,changed:!!i}});e.json({repos:s,branch:n.workspace?.branch??null,kind:n.workspace?.kind??null})}catch(n){e.status(500).json({error:n?.message||String(n)})}});S.post("/api/tracks/:id/repos",async(t,e)=>{let{repo:n}=t.body||{};try{if(!n||typeof n!="string")return e.status(400).json({error:"repo (path or owner/name) required"});let r=await jh(t.params.id,n);e.json({ok:!0,added:{name:r.name,base:r.base,baseRef:r.baseRef??null,repo:r.repo??null}})}catch(r){let o=r?.message||String(r),s=/not found/i.test(o)?404:/already tagged|needs a multi|not a usable|archived|required/i.test(o)?400:502;e.status(s).json({error:o})}});S.delete("/api/tracks/:id/repos/:name",async(t,e)=>{try{let n=await Lh(t.params.id,t.params.name);e.json({ok:!0,...n})}catch(n){let r=n?.message||String(n),o=/track .* not found/i.test(r)?404:/has work|still running|last repo/i.test(r)?409:/needs a multi|not tagged/i.test(r)?400:502;e.status(o).json({error:r})}});S.post("/api/git/prs/fix",async(t,e)=>{let{repo:n,number:r}=t.body||{};try{let o=await dr(n,r),s=await Y(n).catch(()=>null),a=await Ag({repo:n,number:r,branch:o.headRefName});if(!a)return e.status(404).json({error:`no active Track owns PR #${o.number} (branch "${o.headRefName}")`});let i=$t(a.workspace).find(x=>x.base===n||s&&x.repo===s)??null,c=a.workspace?.kind==="multi",l=await ih(n,o.number);if(!l.length)return e.status(409).json({error:"no failing checks on this PR"});let u=c&&i?lt.join("ci",i.name):"ci",d=lt.join(a.cwd,".bostrat",u),f=lt.join(".bostrat",u,`pr-${o.number}-fail.log`);await Ct.promises.mkdir(d,{recursive:!0}),await Ct.promises.writeFile(lt.join(a.cwd,f),l.map(x=>`\u2550\u2550\u2550\u2550 ${x.name}${x.link?` \xB7 ${x.link}`:""} \u2550\u2550\u2550\u2550
|
|
313
|
+
${x.log??"(no fetchable log \u2014 see link)"}
|
|
268
314
|
`).join(`
|
|
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}
|
|
315
|
+
`));let p=l.map(x=>x.name).join(", "),m=c&&i?` in the ${i.name}/ checkout`:"",h=await ye(a.id,`CI is red on PR #${o.number} "${o.title}" (${o.url}) \u2014 branch ${o.headRefName}${m}. Failing check${l.length===1?"":"s"}: ${p}. The failure log is at ${f} in this ${c?"workspace":"worktree"} \u2014 read it, fix the root cause${c&&i?` in ${i.name}/`:""}, run the relevant tests locally, then commit and push to update the PR.`,{kind:"ci-fix"}),b=s;b&&(await Pt({repo:b,number:o.number,trackId:a.id,url:o.url,headRef:o.headRefName,title:o.title,source:"matched"}),await Tf(b,o.number,{headSha:o.headSha,trackId:a.id}).catch(()=>{})),await Vt("fix-delegated",{number:o.number,url:o.url,title:o.title,branch:o.headRefName,repo:n,track:a,extra:{checks:l.map(x=>x.name)}}),e.json({ok:!0,leadBox:h.leadBox,queued:!!h.queued,checks:l.map(x=>x.name),log:f})}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})}});S.post("/api/git/prs/reconcile",async(t,e)=>{let{repo:n,number:r}=t.body||{};try{let o=await dr(n,r);if(o.mergeable!=="CONFLICTING")return e.status(409).json({error:"this PR does not conflict with its base branch"});let s=await Y(n).catch(()=>null),a=await Ag({repo:n,number:r,branch:o.headRefName});if(!a)return e.status(404).json({error:`no active Track owns PR #${o.number} (branch "${o.headRefName}")`});let i=$t(a.workspace).find(d=>d.base===n||s&&d.repo===s)??null,c=a.workspace?.kind==="multi"&&i?` In the ${i.name}/ checkout:`:"",l=await ye(a.id,`PR #${o.number} "${o.title}" (${o.url}) \u2014 branch ${o.headRefName} \u2014 has merge conflicts with ${o.baseRefName}.${c} 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"}),u=s;u&&(await Pt({repo:u,number:o.number,trackId:a.id,url:o.url,headRef:o.headRefName,title:o.title,source:"matched"}),await _f(u,o.number,{headSha:o.headSha,trackId:a.id}).catch(()=>{})),await Vt("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:l.leadBox,queued:!!l.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})}});S.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=lm.filter(d=>t.body.actions.includes(d)),!o.length)return e.status(400).json({error:"actions must include commit, rebase, 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"|"rebase"|"push"|"pr"] (or action "dismiss") required'});if(!n)return e.status(400).json({error:"offerId required"});let s=o!=null&&t.body?.notify===!0,a=await et(t.params.id);if(!a)return e.status(404).json({error:"track not found"});let i=await F(a),c=i.find(d=>d.type==="pr.offered"&&d.payload?.offerId===n);if(!c)return e.status(404).json({error:"unknown offer"});if(i.some(d=>d.type==="pr.offer.answered"&&d.payload?.offerId===n))return e.status(409).json({error:"offer already answered"});let l=null;o&&(l=await ye(a.id,Os(o,{branch:c.payload.branch,base:c.payload.base,toPr:c.payload.toPr||null,repos:c.payload.repos||null,offeredRebase:Array.isArray(c.payload.offered)&&c.payload.offered.includes("rebase")}),{kind:o.includes("pr")?"pr-go":"push-go"}));let u=o?o.includes("pr")?"pr":o.includes("push")?"push":o.includes("commit")?"commit":"rebase":"dismiss";await O({id:a.id,cwd:a.cwd},{type:"pr.offer.answered",actor:"user",source:"orchestrator",boxId:c.boxId,payload:{ts:Date.now(),offerId:n,action:u,actions:o,...s?{notify:!0}:{}}}),s&&Oh(a.id,{actions:o}),e.json({ok:!0,offerId:n,action:u,actions:o,notify:s,queued:!!l?.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 Ro=t=>e=>({...e,url:Fw(e,t.headers)});S.get("/api/views",async(t,e)=>{try{let n=await os({trackId:t.query.track||void 0});e.json({views:n.map(Ro(t)),maxLive:Ie})}catch(n){e.status(500).json({error:n?.message||String(n)})}});S.get("/api/views/:id",async(t,e)=>{let n=await Rd(t.params.id);if(!n)return e.status(404).json({error:"view not found"});e.json({view:Ro(t)(n),session:Wn(n.slug)})});S.post("/api/views",async(t,e)=>{let{trackId:n,name:r,cmd:o,cwd:s,port:a}=t.body||{};try{let i=n?await et(n):null;if(!i&&s&&(i=(await ut()).find(u=>u.cwd&&(s===u.cwd||s.startsWith(u.cwd+lt.sep)))||null),!i)return e.status(400).json({error:"trackId (or a cwd inside a Track workspace) required"});let c=await ci({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:Ro(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})}});S.post("/api/views/:id/stop",async(t,e)=>{try{e.json({ok:!0,view:Ro(t)(await $d(t.params.id))})}catch(n){let r=n?.message||String(n);e.status(/not found/i.test(r)?404:500).json({error:r})}});S.post("/api/views/:id/restart",async(t,e)=>{try{e.json({ok:!0,view:Ro(t)(await Od(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})}});S.delete("/api/views/:id",async(t,e)=>{try{e.json(await Cd(t.params.id))}catch(n){let r=n?.message||String(n);e.status(/not found/i.test(r)?404:500).json({error:r})}});var CE=[["linear",an,gi],["jira",cs,hi]],Hl=new Map;S.get("/api/integrations/ticketing",async(t,e)=>{try{let n=await mi(),r={};for(let[o,s,a]of CE){let i=await s();if(!i){Hl.delete(o),r[o]={configured:!1};continue}let c=Hl.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},Hl.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)})}});S.get("/api/tickets/flow",async(t,e)=>{try{e.json({flow:await mi()})}catch(n){e.status(500).json({error:n?.message||String(n)})}});S.post("/api/tickets/flow",async(t,e)=>{let{trackId:n,cwd:r,set:o,actor:s}=t.body||{};try{let a=await zd(o),i=n?await et(n):null;!i&&r&&(i=(await ut()).find(l=>l.cwd&&(r===l.cwd||r.startsWith(l.cwd+lt.sep)))||null),i&&await O({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})}});S.post("/api/tickets/link",async(t,e)=>{let{trackId:n,cwd:r,ref:o}=t.body||{};try{let s=n?await et(n):null;if(!s&&r&&(s=(await ut()).find(c=>c.cwd&&(r===c.cwd||r.startsWith(c.cwd+lt.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 Xn(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})}});S.get("/api/tickets/status",async(t,e)=>{let{cwd:n,track:r}=t.query;try{let o=r?await et(r):null;if(!o&&n&&(o=(await ut()).find(a=>a.cwd&&(n===a.cwd||String(n).startsWith(a.cwd+lt.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 ff(),ticket:await ne(o.id)})}catch(o){e.status(500).json({error:o?.message||String(o)})}});S.get("/api/tracks/:id/ticket",async(t,e)=>{e.json({ticket:await ne(t.params.id)})});S.post("/api/tracks/:id/ticket",async(t,e)=>{try{let n=await et(t.params.id);if(!n)return e.status(404).json({error:"track not found"});let r=await Xn(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})}});S.delete("/api/tracks/:id/ticket",async(t,e)=>{e.json({ok:!0,removed:await Ii(t.params.id)})});var ka=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;S.get("/api/tracks/:id/ticket/states",async(t,e)=>{try{e.json(await wf(t.params.id))}catch(n){let r=n?.message||String(n);e.status(ka(r)).json({error:r})}});S.post("/api/tracks/:id/ticket/state",async(t,e)=>{try{e.json({ok:!0,...await Ri(t.params.id,(t.body||{}).stateId)})}catch(n){let r=n?.message||String(n);e.status(ka(r)).json({error:r})}});S.post("/api/tracks/:id/ticket/comment",async(t,e)=>{try{e.json({ok:!0,...await gf(t.params.id,(t.body||{}).body)})}catch(n){let r=n?.message||String(n);e.status(ka(r)).json({error:r})}});S.post("/api/tracks/:id/ticket-offer",async(t,e)=>{let{offerId:n,accept:r}=t.body||{};try{let o=await et(t.params.id);if(!o)return e.status(404).json({error:"track not found"});e.json({ok:!0,...await yf(o,n,r===!0)})}catch(o){let s=o?.message||String(o);e.status(ka(s)).json({error:s})}});S.post("/api/tickets/propose",async(t,e)=>{let{trackId:n,cwd:r,plan:o}=t.body||{};try{let s=n?await et(n):null;if(!s&&r&&(s=(await ut()).find(i=>i.cwd&&(r===i.cwd||r.startsWith(i.cwd+lt.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 kf(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})}});S.get("/api/tickets/teams",async(t,e)=>{try{e.json({teams:await bf()})}catch(n){let r=n?.message||String(n);e.status(/no ticketing/i.test(r)?400:502).json({error:r})}});S.post("/api/tracks/:id/ticket-create-offer",async(t,e)=>{let{proposalId:n,accept:r,teamId:o}=t.body||{};try{let s=await et(t.params.id);if(!s)return e.status(404).json({error:"track not found"});e.json({ok:!0,...await xf(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})}});S.get("/api/mocks",async(t,e)=>{try{e.json({mocks:await Qo({trackId:t.query.track||void 0,group:t.query.group||void 0})})}catch(n){e.status(500).json({error:n?.message||String(n)})}});S.get("/api/mocks/:id",async(t,e)=>{let n=await Ee(t.params.id);if(!n)return e.status(404).json({error:"mock not found"});e.json({mock:n})});S.get("/api/mocks/:id/png",async(t,e)=>{let n=await Ee(t.params.id);if(!n)return e.status(404).json({error:"mock not found"});let r=nn(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(Lt(n,r.v).png,o=>{o&&!e.headersSent&&e.status(404).json({error:"rendered artifact missing"})})});S.get("/api/mocks/:id/html",async(t,e)=>{let n=await Ee(t.params.id);if(!n)return e.status(404).json({error:"mock not found"});let r=nn(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(Lt(n,r.v).html,o=>{o&&!e.headersSent&&e.status(404).json({error:"source artifact missing"})})});S.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 et(o):null;if(!l&&a&&(l=(await ut()).find(p=>p.cwd&&(a===p.cwd||a.startsWith(p.cwd+lt.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 dd({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})}});S.post("/api/mocks/:id/update",async(t,e)=>{let n=Buffer.isBuffer(t.body)?t.body:null;try{let r=await fd(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})}});S.post("/api/mocks/:id/pick",async(t,e)=>{try{let{mock:n,siblings:r}=await md(t.params.id),o=r.length?` over ${r.map(s=>`"${s.name}"`).join(", ")}`:"";await ye(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})}});S.delete("/api/mocks/:id",async(t,e)=>{try{e.json(await hd(t.params.id))}catch(n){let r=n?.message||String(n);e.status(/not found/i.test(r)?404:500).json({error:r})}});S.get("/api/runbooks",async(t,e)=>{try{let n=t.query.track||void 0;if(!n&&t.query.cwd){let r=await ut(),o=String(t.query.cwd);if(n=r.find(s=>s.cwd&&(o===s.cwd||o.startsWith(s.cwd+lt.sep)))?.id,!n)return e.json({runbooks:[]})}e.json({runbooks:await ss({trackId:n,status:t.query.status||void 0})})}catch(n){e.status(500).json({error:n?.message||String(n)})}});S.get("/api/runbooks/:id",async(t,e)=>{let n=await fi(t.params.id);if(!n)return e.status(404).json({error:"runbook not found"});e.json({runbook:n})});S.get("/api/runbooks/:id/md",async(t,e)=>{let n=await fi(t.params.id);if(!n)return e.status(404).json({error:"runbook not found"});let r=Dd(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(qn(n,r.v),o=>{o&&!e.headersSent&&e.status(404).json({error:"runbook artifact missing"})})});S.post("/api/runbooks",async(t,e)=>{let{trackId:n,name:r,md:o,cwd:s}=t.body||{};try{let a=n?await et(n):null;if(!a&&s&&(a=(await ut()).find(u=>u.cwd&&(s===u.cwd||s.startsWith(u.cwd+lt.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 Fd({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})}});S.post("/api/runbooks/:id/update",async(t,e)=>{try{let{md:n,note:r}=t.body||{};e.json({ok:!0,runbook:await Ud(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})}});S.post("/api/runbooks/:id/status",async(t,e)=>{try{e.json({ok:!0,runbook:await Hd(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})}});S.delete("/api/runbooks/:id",async(t,e)=>{try{e.json(await Wd(t.params.id))}catch(n){let r=n?.message||String(n);e.status(/not found/i.test(r)?404:500).json({error:r})}});var ME=120,Wl=400,Kl=null;S.post("/api/notify",async(t,e)=>{let{title:n,body:r,urgency:o,cwd:s,trackId:a}=t.body||{};try{let i=typeof r=="string"?r.trim():"";if(!i)return e.status(400).json({error:"body (the message) required"});if(i.length>Wl)return e.status(400).json({error:`message is ${i.length} chars \u2014 over the ${Wl} cap; a notification is a nudge, put the detail in chat`});let c=di(`${n||""}
|
|
316
|
+
${i}`);if(c.length)return e.status(400).json({error:`notification appears to contain a secret VALUE (${c.join(", ")}) \u2014 notifications ship off-box; never put a credential in one`});let l=a?await et(a):null;if(!l&&s&&(l=(await ut()).find(m=>m.cwd&&(s===m.cwd||s.startsWith(m.cwd+lt.sep)))||null),!l)return e.status(400).json({error:"trackId (or a cwd inside a Track workspace) required"});let u=(l.boxes||[]).find(p=>p.name===l.leadBox),d=(p,m)=>String(p).replace(/\s+/g," ").trim().slice(0,m),f={title:n?d(n,ME):l.name,body:d(i,Wl),urgency:o==="high"?"high":"normal",trackName:l.name,ts:Date.now()};await O(l,{type:"notify.raised",actor:"agent",boxId:l.leadBox??null,provider:u?.agent||null,payload:f}),e.json({ok:!0,notification:f,delivered:Kl?Kl.status().connected:null})}catch(i){e.status(500).json({error:i?.message||String(i)})}});S.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 gg(t.body,t.headers["content-type"]))}catch(n){e.status(n?.status||500).json({error:n?.message||String(n),code:n?.code})}});S.get("/api/dictation/glossary",async(t,e)=>{e.json({configured:!!await Ur(),terms:await Ul(),problem:await wg()})});S.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 hg(n,r)})});S.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 ll({name:n,cwd:r,repo:o,repos:s,agent:a,skipPermissions:i!==!1}),u=null;if(c)try{await Xn(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})}});S.get("/api/tracks/:id",async(t,e)=>{let n=await et(t.params.id);if(!n)return e.status(404).json({error:"track not found"});e.json({track:n,handoff:Tg(wo(t.params.id)),subtasks:br(t.params.id).map($o)})});S.delete("/api/tracks/:id",async(t,e)=>{try{let n=await qh(t.params.id,{cascadeMocks:t.query.mocks!=="0",cascadeRunbooks:t.query.runbooks==="1"});await Ii(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})}});S.post("/api/tracks/:id/archive",async(t,e)=>{try{e.json({ok:!0,track:await yr(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})}});S.post("/api/tracks/:id/unarchive",async(t,e)=>{try{e.json({ok:!0,track:await Jh(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})}});S.get("/api/tracks/:id/messages",async(t,e)=>{let n=await sl(t.params.id);if(!n)return e.status(404).json({error:"track not found"});e.json(n)});S.get("/api/tracks/:id/events",async(t,e)=>{let n=await et(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}
|
|
270
317
|
data: ${JSON.stringify(i)}
|
|
271
318
|
|
|
272
|
-
`),o=Number(t.query.since??t.headers["last-event-id"])||0;for(let i of await
|
|
319
|
+
`),o=Number(t.query.since??t.headers["last-event-id"])||0;for(let i of await F(n))i.seq>o&&r(i);let s=Yu(n.id,r),a=setInterval(()=>e.write(`: hb
|
|
273
320
|
|
|
274
|
-
`),25e3);t.on("close",()=>{clearInterval(a),s()})});
|
|
321
|
+
`),25e3);t.on("close",()=>{clearInterval(a),s()})});S.post("/api/tracks/:id/lead/restart",async(t,e)=>{try{e.json({ok:!0,track:await Xh(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})}});S.post("/api/tracks/:id/say",async(t,e)=>{let{text:n,attachments:r}=t.body||{};try{e.json(await ye(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})}});S.post("/api/tracks/:id/upload",async(t,e)=>{try{let n=await et(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 fh(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})}});S.get("/api/tracks/:id/uploads/:file",async(t,e)=>{try{let n=await et(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=lt.basename(String(t.params.file||""));if(!r||!zc(lt.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(lt.join(Jc(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 PE=/^[0-9a-fA-F-]{8,64}$/;S.get("/api/tracks/:id/chat-images/:box/:uuid/:idx",async(t,e)=>{try{let n=await et(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(!PE.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=ce(a.cwd,a.sessionId);if(!i)return e.status(404).json({error:"transcript not found"});let c=`"${o}"`,l=null,u=_E.createInterface({input:Ct.createReadStream(i,{encoding:"utf8"}),crlfDelay:1/0});for await(let f of u){if(!f.includes(c))continue;let p;try{p=JSON.parse(f)}catch{continue}if(p.uuid===o){l=p,u.close();break}}let d=l?Ba(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)})}});S.post("/api/tracks/:id/interrupt",async(t,e)=>{try{e.json(await cl(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})}});S.delete("/api/tracks/:id/queue",(t,e)=>{e.json({ok:!0,removed:ta(t.params.id).filter(n=>n.kind!=="ship-notify")})});S.post("/api/tracks/:id/answer-prompt",async(t,e)=>{let{promptId:n,answers:r,text:o}=t.body||{};try{e.json(await al(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})}});S.post("/api/setup/:provider/start",async(t,e)=>{try{let n=await Hh({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})}});S.post("/api/tracks/:id/navigate-prompt",async(t,e)=>{let{promptId:n,questionIndex:r}=t.body||{};try{e.json(await Ih(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})}});S.put("/api/tracks/:id/draft",async(t,e)=>{let{text:n}=t.body||{};try{e.json(await Mh(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})}});S.get("/api/tracks/:id/usage",async(t,e)=>{let n=await et(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 Mw(r,{fresh:o}))});S.post("/api/tracks/:id/model",async(t,e)=>{let{model:n}=t.body||{};try{e.json(await Ph(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})}});S.post("/api/tracks/:id/subtask",async(t,e)=>{let{agent:n,task:r,skipPermissions:o,reason:s}=t.body||{};try{let a=await ew(t.params.id,{agent:n,task:r,skipPermissions:o!==!1,reason:s});e.json({ok:!0,subtask:$o(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})}});S.get("/api/tracks/:id/subtasks",(t,e)=>{e.json({subtasks:br(t.params.id).map($o)})});S.get("/api/tracks/:id/subtask/:sid/messages",async(t,e)=>{let n=await Eh(t.params.id,t.params.sid);if(!n)return e.status(404).json({error:"subtask not found"});e.json(n)});S.post("/api/tracks/:id/subtask/:sid/cancel",(t,e)=>{try{e.json({ok:!0,subtask:$o(hl(t.params.sid))})}catch(n){let r=n?.message||String(n);e.status(/not found/i.test(r)?404:409).json({error:r})}});S.post("/api/tracks/:id/subtask/:sid/merge-now",(t,e)=>{try{e.json({ok:!0,subtask:$o(rw(t.params.sid))})}catch(n){let r=n?.message||String(n);e.status(/not found/i.test(r)?404:409).json({error:r})}});function $o(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}S.post("/api/tracks/:id/handoff-lead",async(t,e)=>{let{toAgent:n,skipPermissions:r,reason:o}=t.body||{};try{let s=await fl(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})}});S.post("/api/tracks/:id/spin-up-view",async(t,e)=>{try{e.json(await Ch(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})}});S.get("/api/tracks/:id/handoff-lead",(t,e)=>{e.json(Tg(wo(t.params.id))??{running:!1})});function Tg(t){return t?{running:!t.finishedAt,step:t.step,error:t.error,fromBox:t.fromBox,toBox:t.toBox,toAgent:t.toAgent,skipPermissions:t.skipPermissions,startedAt:t.startedAt,finishedAt:t.finishedAt}:null}S.get("/api/debug/handoff",async(t,e)=>{let n=Math.min(Number(t.query.limit)||80,500);e.json({verbose:Po(),paths:eu,trace:await tu({limit:n})})});var ql=lt.resolve(Sg,"..","..","client","dist");Ct.existsSync(ql)&&(S.use(ie.static(ql)),S.get(/^(?!\/api\/).*/,(t,e)=>e.sendFile(lt.join(ql,"index.html"))));var _g=IE.createServer(S),NE=new $E({noServer:!0}),Eg=(t,e,n)=>{if(!_r.allowed(_r.clientIp(t))){e.write(`HTTP/1.1 403 Forbidden\r
|
|
275
322
|
\r
|
|
276
|
-
`),e.destroy();return}if(
|
|
323
|
+
`),e.destroy();return}if(Lw(t,e,n))return;let r=uu(t.url);if(!r.ok){e.write(`HTTP/1.1 ${r.status} ${r.status===400?"Bad Request":"Not Found"}\r
|
|
277
324
|
\r
|
|
278
|
-
`),e.destroy();return}let o=r.sessionName;if(!
|
|
325
|
+
`),e.destroy();return}let o=r.sessionName;if(!pt(o)){e.write(`HTTP/1.1 400 Bad Request\r
|
|
279
326
|
\r
|
|
280
|
-
`),e.destroy();return}let{cols:s,rows:a}=r;
|
|
327
|
+
`),e.destroy();return}let{cols:s,rows:a}=r;NE.handleUpgrade(t,e,n,i=>iu(i,o,{cols:s,rows:a}))};_g.on("upgrade",Eg);var Ig=process.env.BOSTRAT_CERT_DIR||lt.join(process.env.HOME||Gl.tmpdir(),".bostrat","certs"),bg=Number(process.env.BOSTRAT_TLS_PORT)||3443,kg=lt.join(Ig,"bostrat.globopi.key"),xg=lt.join(Ig,"bostrat.globopi.crt");if(Ct.existsSync(kg)&&Ct.existsSync(xg)){let t=RE.createServer({key:Ct.readFileSync(kg),cert:Ct.readFileSync(xg)},S);t.on("upgrade",Eg),t.on("error",e=>{console.error(`[bostrat-agent] https listener failed (${e?.code||e?.message}) \u2014 continuing HTTP-only`)}),t.listen(bg,ya,()=>{console.log(`[bostrat-agent] https listening on ${ya||"0.0.0.0"}:${bg}`)})}_g.listen(yg,ya,()=>{console.log(`[bostrat-agent] listening on ${ya||"0.0.0.0"}:${yg}`),console.log(`[bostrat-agent] tmux socket: ${Mt}`),console.log(`[bostrat-agent] auto-trigger default: ${vg}%`),console.log(`[bostrat-agent] ip allowlist: ${_r.open?"OPEN (BOSTRAT_ALLOW_IPS unset)":process.env.BOSTRAT_ALLOW_IPS}`),Qh().then(t=>t&&console.log("[bostrat-agent] reconciled interrupted handoff(s)")).catch(t=>console.error("[bostrat-agent] handoff reconcile failed:",t?.message||t)),tw().then(t=>t&&console.log("[bostrat-agent] reconciled interrupted subtask(s)")).catch(t=>console.error("[bostrat-agent] subtask reconcile failed:",t?.message||t)),zh().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)),Uh().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)),LE(),jE(),Kw(),Sf({resolveTrack:et}),pw({listTracks:ut,createTrack:ll,archiveTrack:yr,listRepos:me,resolveTrack:et,sayToLead:ye,answerPrompt:al,startLeadHandoff:fl,interruptLead:cl,listSubtaskJobs:br,cancelSubtask:hl,readEvents:F,appendEvent:O,knownAgents:Ys}),BE(),$r().then(t=>{t||console.warn(`[boot] no host tmux server on ${Mt} and auto-start failed \u2014 boxes cannot spawn until one runs`)}),ag(),process.env.BOSTRAT_PLANE_URL&&process.env.BOSTRAT_PLANE_TOKEN&&(Kl=bw({url:process.env.BOSTRAT_PLANE_URL,token:process.env.BOSTRAT_PLANE_TOKEN,nodeId:process.env.BOSTRAT_NODE_ID||Gl.hostname(),name:process.env.BOSTRAT_NODE_NAME||Gl.hostname(),listTracks:ut,detectAgents:ol}),console.log(`[bostrat-agent] plane link \u2192 ${process.env.BOSTRAT_PLANE_URL}`))});function BE(){DE(),rs().catch(()=>{}),setInterval(()=>rs().catch(()=>{}),1e4)}function DE(){for(let t of["bostrat-view","bostrat-mock","bostrat-ticket","bostrat-runbook","bostrat-notify","gh"])try{let e=lt.resolve(Sg,"..","bin",t),n=lt.join(process.env.HOME||"",".local","bin");if(!process.env.HOME||!Ct.existsSync(e))continue;Ct.mkdirSync(n,{recursive:!0});let r=lt.join(n,t);(Ct.existsSync(r)?Ct.readlinkSync(r):null)!==e&&(Ct.rmSync(r,{force:!0}),Ct.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 jE(){let t=!1,e=async()=>{if(!t){t=!0;try{let n=await ut(),r=await Rw({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 LE(){let t=!1;setInterval(async()=>{if(!t){t=!0;try{let e=await ut();for(let n of e)n.archivedAt||!n.boxes?.some(r=>r.state==="live")||(await sl(n.id).catch(()=>{}),await ch(n).catch(()=>{}))}catch{}finally{t=!1}}},4e3)}
|