borgmcp 1.1.9 → 1.1.12
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/assimilate-cmd.js +37 -36
- package/dist/claude.js +12 -12
- package/dist/ensure-mcp-config.d.ts +18 -0
- package/dist/ensure-mcp-config.js +1 -0
- package/dist/index.js +19 -19
- package/dist/list-roles-render.d.ts +2 -1
- package/dist/list-roles-render.js +1 -1
- package/dist/opencode-drone.d.ts +17 -4
- package/dist/opencode-drone.js +4 -2
- package/dist/regen-format.js +1 -1
- package/dist/regen.js +3 -3
- package/dist/remote-client.d.ts +8 -0
- package/dist/remote-client.js +1 -1
- package/dist/role-resolver.d.ts +4 -0
- package/dist/role-resolver.js +1 -1
- package/dist/roster-render.d.ts +7 -7
- package/dist/roster-render.js +3 -3
- package/dist/setup.js +17 -17
- package/dist/templates.d.ts +1 -0
- package/dist/templates.js +1 -1
- package/dist/tool-manifest.js +1 -1
- package/dist/working-repo.d.ts +35 -0
- package/dist/working-repo.js +1 -0
- package/package.json +1 -1
package/dist/assimilate-cmd.js
CHANGED
|
@@ -1,48 +1,49 @@
|
|
|
1
|
-
import{dirname as
|
|
2
|
-
`),1}if(
|
|
3
|
-
`),1}let a=await e.getCachedAuth();if(!a){if(!e.isTTY()&&!
|
|
4
|
-
`)}}let l=null;if(i&&i.includes("@")&&i.includes(":")){const t=i.lastIndexOf(":");l={ownerEmail:i.substring(0,t),cubeName:i.substring(t+1)},i=l.cubeName}const
|
|
5
|
-
`);let
|
|
6
|
-
`),a=await e.runSetup(),
|
|
7
|
-
`),1;let u,
|
|
1
|
+
import{dirname as V,basename as R}from"node:path";import{randomUUID as se}from"node:crypto";import{roleSlug as ce,matchRoleByName as ue,pickDefaultRole as me}from"./role-resolver.js";import{deriveCubeName as de,parseGitRemote as fe,sanitizeRemoteUrl as ge}from"./cube-name.js";import{validateName as X}from"./name-validator.js";import{renderAssimilationWelcome as he}from"./assimilate-welcome.js";import{shellEscape as we}from"./shell-escape.js";import{withCodexCwdArg as be}from"./codex-remote.js";import{buildAgentKickoffPrompt as ke,buildKickoffWakePathClause as pe,recordCodexWakeTarget as ye,socketPathFromRemoteArgs as ve}from"./codex-launch.js";import{perWorktreeBranchName as F,adoptWorktree as $e,computeWorktreePath as Q,localBranchExists as Z,isMerged as xe}from"./worktree-lifecycle.js";import{DroneEvictedError as Se}from"./drone-lifecycle.js";import{codexBorgSessionConfigArgs as Ce}from"./launch-gate.js";import{codexAgentKindConfigArgs as Re,codexRemoteWakeConfigArgs as Ee,withAgentRuntimeEnv as _e}from"./agent-runtime.js";import{inboxPathForDrone as Ae}from"./cubes.js";import{monitorStateRootForWorktree as Ie}from"./inbox-monitor.js";import{resolveLaunchEnv as Ne,resolveOllamaBaseUrl as Pe,parseModel as Le}from"./model-presets.js";import{unlinkSync as Te}from"node:fs";import{gcOrphanInboxesForCube as De,defaultListInboxLogs as Oe,defaultInboxLivenessDeps as Ue,isInboxLive as We,ORPHAN_INBOX_STALE_MS as je}from"./gc-orphan-inboxes.js";import{installBorgPlugin as Me}from"./opencode-plugin.js";import{computeOpenCodePort as Be,connectOpenCodeDrone as He,createOpenCodeLaunchKickoff as Fe,injectInitialKickoff as Ke}from"./opencode-drone.js";import{ensureCliMcpConfigured as Ye}from"./ensure-mcp-config.js";async function yt(n,e){if(n.role!==void 0){const t=X(n.role);if(!t.ok)return e.stderr(t.error+`
|
|
2
|
+
`),1}if(n.flags.worktree!==void 0){const t=X(n.flags.worktree);if(!t.ok)return e.stderr(t.error+`
|
|
3
|
+
`),1}let a=await e.getCachedAuth();if(!a){if(!e.isTTY()&&!n.flags.yes)return e.stderr("borg setup required and stdin is non-interactive. Run `borg setup` first in an interactive terminal, then `borg assimilate`.\n"),1;a=await e.runSetup()}const o=e.findProjectRoot(e.cwd());let i;if(n.flags.cubeName)i=n.flags.cubeName;else{const t=e.runSync("git",["remote","get-url","origin"],o),r=t.status===0?t.stdout:null;if(i=de(o,r),r){const c=ge(r),f=c?fe(c):null;c&&!f&&i&&e.stderr(`couldn't parse git remote '${c}' \u2014 using directory name '${i}' as cube name
|
|
4
|
+
`)}}let l=null;if(i&&i.includes("@")&&i.includes(":")){const t=i.lastIndexOf(":");l={ownerEmail:i.substring(0,t),cubeName:i.substring(t+1)},i=l.cubeName}const E=e.cwd();e.stderr(`Checking your cubes\u2026
|
|
5
|
+
`);let N;try{N=await e.listCubes(a.apiUrl,a.token)}catch(t){const r=t instanceof Error?t.message:String(t);if(r.includes("Authentication required")||r.includes("Authentication expired"))e.stderr(`Re-authenticating...
|
|
6
|
+
`),a=await e.runSetup(),N=await e.listCubes(a.apiUrl,a.token);else throw t}const P=N.find(t=>t.name===i);if(!P&&l)return e.stderr(`No cube named '${l.cubeName}' accessible to you owned by '${l.ownerEmail}'. Did you accept their invite? See borgmcp.ai/dashboard.
|
|
7
|
+
`),1;let u,L;if(P)u=await e.getCube(a.apiUrl,a.token,P.id),L=!1;else{let t;if(n.flags.template)t=n.flags.template;else if(n.flags.noTemplate)t=void 0;else if(e.isTTY())if(n.flags.yes)t="starter";else{const r=await e.listTemplates(a.apiUrl,a.token),c=["First drone joining a new cube. Apply a template?"];r.forEach((v,$)=>{const I=$===0?" (default)":"";c.push(` ${$+1}) ${v.name}${I} \u2014 ${v.description}`)}),c.push(` ${r.length+1}) skip \u2014 no template`);const f=(await e.prompt(c.join(`
|
|
8
8
|
`)+`
|
|
9
|
-
[1]: `)).trim(),
|
|
10
|
-
`),1;t=
|
|
9
|
+
[1]: `)).trim(),p=f===""?1:parseInt(f,10);if(Number.isNaN(p)||p<1||p>r.length+1)return e.stderr(`invalid choice "${f}"
|
|
10
|
+
`),1;t=p<=r.length?r[p-1].name:void 0}else{if(!n.flags.yes)return e.stderr(`cube creation needs a template choice but stdin is non-interactive.
|
|
11
11
|
Pass --template <name>, --no-template, or --yes (defaults to starter).
|
|
12
12
|
`),1;t="starter"}e.stderr(i?`Creating cube '${i}'\u2026
|
|
13
13
|
`:`Creating your cube\u2026
|
|
14
|
-
`),u=await e.createCube(a.apiUrl,a.token,t?{name:i??void 0,template:t}:{name:i??void 0}),
|
|
14
|
+
`),u=await e.createCube(a.apiUrl,a.token,t?{name:i??void 0,template:t}:{name:i??void 0}),L=!0}let h;if(n.role!==void 0){if(h=ue(u.roles,n.role),!h){const t=u.roles.map(f=>f.name).join(", "),r=Je(n.role,u.roles.map(f=>f.name)),c=r?` Did you mean "${r}"?`:"";return e.stderr(`no role matching "${n.role}" in cube "${u.name}". Available: ${t}.${c}
|
|
15
15
|
(Use --template <name> on first-drone setup or run \`borg_create-role\` from inside Claude.)
|
|
16
|
-
`),1}}else{const t=new Set((u.drones??[]).map(
|
|
17
|
-
`),1}const x=await e.getActiveCube();let S;if(x&&
|
|
18
|
-
`),1;const K=
|
|
19
|
-
`),1}const m=await e.resolveCli(
|
|
20
|
-
`)
|
|
21
|
-
`)
|
|
16
|
+
`),1}}else{const t=new Set((u.drones??[]).map(r=>r.role_id));if(h=me(u.roles,{isFirstDrone:L,occupiedRoleIds:t}),!h)return e.stderr(`cube "${u.name}" has no default or human-seat role; cannot infer a role. Either pass a role argument explicitly (e.g. \`borg assimilate builder\`) or run \`borg_create-role\` from inside Claude to set up roles.
|
|
17
|
+
`),1}const x=await e.getActiveCube();let S;if(x&&n.flags.here)if(x.cubeId===u.id)S=x.droneId;else return e.stderr(`this directory already hosts an active drone; remove --here or run from a fresh worktree
|
|
18
|
+
`),1;const K=n.flags.worktree!==void 0||x!==null&&!n.flags.here,te=S??x?.droneId??null,T=K?null:await e.getLaunchModel(u.id,o,te),b=n.flags.model??T?.model??h.default_model??null,D=Pe(process.env,b!=null&&b===T?.model?T?.ollamaBaseUrl:void 0);if(b){const t=await e.checkModelReachable(b,e.fetch,D);if(!t.ok)return e.stderr(`${t.message}
|
|
19
|
+
`),1}const m=await e.resolveCli(n.flags.cli);try{Ye(m)}catch(t){const r=t instanceof Error?t.message:String(t);return e.stderr(`${m} MCP configuration failed: ${r}
|
|
20
|
+
`),1}e.stderr(`Joining cube '${u.name}' as ${h.name}\u2026
|
|
21
|
+
`);let s;try{s=await e.assimilate(a.apiUrl,a.token,{cube_id:u.id,role_id:h.id,hostname:e.getHostname(),agent_kind:m,model:b,...S?{prior_drone_id:S}:{}})}catch(t){if(t instanceof Se&&S!=null)return e.stderr(`seat evicted \u2014 this worktree's saved seat was evicted from the cube. Re-assimilate fresh from a terminal, or remove this worktree.
|
|
22
|
+
`),1;const r=t instanceof Error?t.message:String(t);return e.stderr(`assimilate failed: ${r}
|
|
22
23
|
`),1}const k=u.roles.find(t=>t.id===s.role_id)??h;s.reattached?e.stderr(`re-attached to existing seat ${s.drone_label} (session token rotated, no new drone minted)
|
|
23
24
|
`):k.id!==h.id&&e.stderr(`Note: your invite didn't grant the "${h.name}" role \u2014 assimilated as "${k.name}" instead.
|
|
24
|
-
`);const
|
|
25
|
+
`);const re=K;let w=null;if(re){const t=e.runSync("git",["rev-parse","--verify","HEAD"],o);if(t.status!==0)return e.stderr(`sibling worktree spawn requires HEAD pointing at a commit.
|
|
25
26
|
Fix: create at least one commit (\`git commit --allow-empty -m "initial"\`)
|
|
26
27
|
OR: pass --here to skip the sibling spawn and use the current directory
|
|
27
|
-
`),1;e.runSync("git",["fetch","origin"],o);let
|
|
28
|
-
`);const v=R(o),$=
|
|
29
|
-
`),1;const
|
|
30
|
-
`),1;e.stderr(`spawned sibling worktree at ${g} on branch ${
|
|
31
|
-
`),e.chdir(g),e.stderr(
|
|
28
|
+
`),1;e.runSync("git",["fetch","origin"],o);let r="origin/main";e.runSync("git",["rev-parse","--verify","origin/main"],o).status!==0&&e.runSync("git",["rev-parse","--verify","origin/master"],o).status===0&&(r="origin/master");const f=t.stdout.trim(),p=e.runSync("git",["rev-parse",r],o).stdout.trim();f!==p&&e.stderr(`note: local HEAD (${f.slice(0,7)}) differs from ${r} (${p.slice(0,7)}); new worktree will start on ${r}
|
|
29
|
+
`);const v=R(o),$=n.flags.worktree??ce(k.name);if($.length===0)return e.stderr(`cannot derive a worktree name from role "${k.name}"; pass an explicit --worktree <name>
|
|
30
|
+
`),1;const I=e.homedir();let g=Q(I,v,$),y=F(R(g),v),z=2;for(;e.pathExists(g)||ze(e,o,g)||Z(e.runSync,o,y)&&!xe(e.runSync,o,y,r);)g=Q(I,v,$,z),y=F(R(g),v),z++;e.mkdirp(V(g));const J=Z(e.runSync,o,y)?e.runSync("git",["worktree","add",g,y],o):e.runSync("git",["worktree","add","-b",y,g,r],o);if(J.status!==0)return e.stderr(`git worktree add failed: ${ee(J.stderr)}
|
|
31
|
+
`),1;e.stderr(`spawned sibling worktree at ${g} on branch ${y} (${r}); original dir is registered as active (edit ~/.config/borgmcp/cubes.json if stale).
|
|
32
|
+
`),e.chdir(g),e.stderr(Ge(g,y,o)),w=e.cwd()}try{await e.setActiveCube({cubeId:s.cube_id,droneId:s.drone_id,name:u.name,sessionToken:s.session_token,droneLabel:s.drone_label,apiUrl:a.apiUrl,roleName:k.name,isHumanSeat:k.is_human_seat,...k.role_class?{roleClass:k.role_class}:{}})}catch(t){const r=t instanceof Error?t.message:String(t);if(e.stderr(`setActiveCube failed: ${r}
|
|
32
33
|
`),w){const c=e.runSync("git",["worktree","remove","--force",w],o);c.status===0?e.stderr(`rolled back spawned worktree at ${w}
|
|
33
|
-
`):e.stderr(`manual cleanup needed: \`git worktree remove --force ${w}\` (rollback attempt failed: ${
|
|
34
|
-
`)}return 1}const d=e.cwd(),
|
|
35
|
-
`)}if(!w){e.runSync("git",["fetch","origin","--prune"],d);const t=F(R(d),R(o)),
|
|
36
|
-
`),e.stderr(
|
|
34
|
+
`):e.stderr(`manual cleanup needed: \`git worktree remove --force ${w}\` (rollback attempt failed: ${ee(c.stderr).trim()||"unknown"})
|
|
35
|
+
`)}return 1}const d=e.cwd(),O=e.findProjectRoot(d),U=Ie(O);try{const t=Ue(),r=V(Ae(s.cube_id,s.drone_id));De({cubeInboxDir:r,selfDroneId:s.drone_id,deps:{listInboxLogs:Oe,isLive:c=>We(c,t,U),droneState:()=>"absent",unlink:c=>Te(c),now:t.now,staleMs:je},monitorStateRoot:U})}catch{}e.setTerminalTitle(s.drone_label,u.name);const ne=e.isTTY()&&!process.env.NO_COLOR&&!process.env.CI;e.stdout(he(k.name,u.name,ne));try{e.installProjectSessionHook(d)}catch{e.stderr(`warning: could not install the project-local SessionStart hook in ${d}; it will be re-attempted on the next borg launch
|
|
36
|
+
`)}if(!w){e.runSync("git",["fetch","origin","--prune"],d);const t=F(R(d),R(o)),r=$e(e.runSync,d,t,"origin/main");r.action==="adopted"?(e.stderr(`worktree: adopted branch ${t} at origin/main
|
|
37
|
+
`),e.stderr(qe(d,t))):r.message&&e.stderr(`worktree sync: ${r.message}
|
|
37
38
|
`)}await e.probeMcpReady()||e.stderr(`warning: borg-mcp readiness probe did not complete within the timeout; launching ${m} anyway \u2014 the kickoff prompt's ToolSearch fallback will recover if the MCP server takes longer to start.
|
|
38
|
-
`);const
|
|
39
|
-
`),
|
|
40
|
-
Agent exited. You were working in ${w}; your shell is back in ${
|
|
39
|
+
`);const oe=e.getInboxPath(s.cube_id,s.drone_id),W=m==="codex"?`borg-wake-${se()}`:null,ie=pe(m,m==="claude"?oe:null,m==="claude"?U:null);let j,Y=[],C,_=null,M=null;b?await e.setLaunchModel(s.cube_id,O,{model:b,ollamaBaseUrl:Le(b).kind==="ollama"?D:null}):await e.clearLaunchModel(s.cube_id,O);const G=Ne(b,D),B={..._e(process.env,m),...G.set,BORG_SESSION:"1"};for(const t of G.unset)delete B[t];if(m==="codex"){const t=await e.prepareCodexRemoteLaunch();t.warning?(e.stderr(`warning: ${t.warning}
|
|
40
|
+
`),j="\u26A0 Codex wake-path capability check failed: remote-control is unavailable for this session. Run borg_regen manually whenever you return, and expect only fallback wakeups until relaunch."):j="Codex wake-path capability check passed: remote-control socket established for this session.",Y=t.args,Object.keys(t.env).length>0&&Object.assign(B,t.env),_=ve(t.args),M=t.server?.cleanup??null}const q=ke({cli:m,codexWakeNonce:W,monitorClause:ie,codexWakePathClause:j});let A=null,H;if(C=[q],m==="codex")C=[...Ce(),...Re(),...Ee(_!==null),...Y,...be(C,d)];else if(m==="opencode"){H=Be(s.drone_id),Me();const t=d;A=Fe(q),C=[t,"--port",String(H),"--auto","--prompt",A.prompt]}const ae=e.exec(m,C,d,B);if(m==="codex"&&_&&W&&ye({deps:e,cubeId:s.cube_id,droneId:s.drone_id,socketPath:_,cwd:d,previewNeedle:W,launchedAtSeconds:Math.floor(Date.now()/1e3)}),m==="opencode"&&A){const t=A,r=`http://127.0.0.1:${H}`;He({serverUrl:r,directory:d,droneLabel:s.drone_label,cubeName:i??"borg"}).then(()=>Ke(t)).catch(()=>{})}const le=await ae;if(M)try{M()}catch{}return w&&E!==w&&e.stderr(`
|
|
41
|
+
Agent exited. You were working in ${w}; your shell is back in ${E}.
|
|
41
42
|
To return:
|
|
42
|
-
cd ${
|
|
43
|
-
`),
|
|
44
|
-
WORKTREE STEERING: You are in worktree ${
|
|
45
|
-
`}function
|
|
46
|
-
WORKTREE STEERING: This checkout is now on branch ${e}. Do ALL work HERE in ${
|
|
47
|
-
`}function
|
|
48
|
-
`).some(i=>i===`worktree ${a}`)}function
|
|
43
|
+
cd ${we(w)}
|
|
44
|
+
`),le}function Ge(n,e,a){return`
|
|
45
|
+
WORKTREE STEERING: You are in worktree ${n} on branch ${e}. Do ALL work HERE \u2014 cut your feature branch (fix/.../feat/...) off ${e} in THIS worktree, use relative paths / your cwd. NEVER \`git -C ${a}\` or operate on the primary checkout ${a}: the same branch can't be checked out in two worktrees, so work created in the primary won't reach your wt-branch without manual surgery (cherry-pick/merge).
|
|
46
|
+
`}function qe(n,e){return`
|
|
47
|
+
WORKTREE STEERING: This checkout is now on branch ${e}. Do ALL work HERE in ${n} \u2014 cut your feature branch (fix/.../feat/...) off ${e}, use relative paths / your cwd.
|
|
48
|
+
`}function ee(n){return n.replace(/[\x00-\x1F\x7F]/g,"")}function ze(n,e,a){const o=n.runSync("git",["worktree","list","--porcelain"],e);return o.status!==0?!1:o.stdout.split(`
|
|
49
|
+
`).some(i=>i===`worktree ${a}`)}function Je(n,e){if(e.length===0)return null;const a=n.toLowerCase();let o=null;for(const i of e){const l=Ve(a,i.toLowerCase());l<=2&&(o===null||l<o.distance)&&(o={name:i,distance:l})}return o?o.name:null}function Ve(n,e){if(n===e)return 0;if(n.length===0)return e.length;if(e.length===0)return n.length;const a=new Array(e.length+1),o=new Array(e.length+1);for(let i=0;i<=e.length;i++)a[i]=i;for(let i=1;i<=n.length;i++){o[0]=i;for(let l=1;l<=e.length;l++){const E=n[i-1]===e[l-1]?0:1;o[l]=Math.min(o[l-1]+1,a[l]+1,a[l-1]+E)}for(let l=0;l<=e.length;l++)a[l]=o[l]}return a[e.length]}export{yt as runAssimilate,ee as safeStderr,Je as suggestRoleName};
|
package/dist/claude.js
CHANGED
|
@@ -1,15 +1,15 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import{spawn as
|
|
3
|
-
`)})();if((process.argv[2]==="--help"||process.argv[2]==="-h")&&(process.stdout.write(
|
|
4
|
-
`)),process.stderr.write("Run `borg --help` for usage.\n"),process.exit(1));const
|
|
5
|
-
`)),process.stderr.write("Run `borg --help` for usage.\n"),process.exit(1));const
|
|
6
|
-
`)),process.stderr.write("Run `borg --help` for usage.\n"),process.exit(1));const
|
|
7
|
-
`)),process.stderr.write("Run `borg --help` for usage.\n"),process.exit(1));const
|
|
8
|
-
`)),process.stderr.write("Run `borg --help` for usage.\n"),process.exit(1)),
|
|
9
|
-
`)),process.stderr.write("Run `borg --help` for usage.\n"),process.exit(1));const
|
|
10
|
-
\u25FC Failed to launch ${
|
|
11
|
-
`)}`)}else console.error(`${t()}${
|
|
12
|
-
\u25FC Failed to launch ${
|
|
13
|
-
`)}`);process.exit(1)}),
|
|
2
|
+
import{spawn as K}from"child_process";import{randomUUID as G}from"node:crypto";import{basename as U}from"node:path";import{createInterface as Y}from"node:readline/promises";import n from"chalk";import{findProjectRoot as v,getActiveCube as V,getLaunchModel as j,inboxPathForDrone as q,setCodexWakeTarget as X,pruneDeadCodexWakeTargets as z}from"./cubes.js";import{monitorStateRootForWorktree as J}from"./inbox-monitor.js";import{applyOllamaLaunchEnv as Q,checkModelReachable as Z}from"./model-presets.js";import{handleVersionFlag as ee,getPackageVersion as C}from"./version.js";import{isHelpFlag as O,setupHelpText as re,topLevelHelpText as oe,assimilateHelpText as se}from"./cli-help.js";import{runSpawn as te}from"./spawn.js";import{buildClaudeLaunchArgs as ie}from"./claude-launch-args.js";import{parseSyncArgs as ne,runSync as ae}from"./sync.js";import{parseCleanupArgs as ce,runCleanup as le}from"./cleanup-cmd.js";import{parseAssimilateArgs as de}from"./parse-assimilate-args.js";import{runAssimilate as pe}from"./assimilate-cmd.js";import{buildDefaultAssimilateDeps as ue}from"./assimilate-deps.js";import{parseLaunchAllArgs as E}from"./parse-launch-all-args.js";import{unknownSubcommand as me}from"./unknown-subcommand.js";import{runLaunchAll as H}from"./launch-all-cmd.js";import{buildDefaultLaunchAllDeps as $}from"./launch-all-deps.js";import{discoverDroneCandidates as fe}from"./launch-all-discovery.js";import{explicitCliLaunchHint as ge,runBareLaunchMenu as he,shouldResolveExplicitCliLaunchHintTargets as we,shouldShowLaunchMenu as Ce}from"./bare-launch-menu.js";import{setTerminalTitle as xe}from"./terminal-title.js";import{initConsolePrefix as ke,consolePrefix as t}from"./console-prefix.js";import{initDebugFromArgv as be}from"./debug.js";import{fetchLatestBorgmcpVersion as ve,compareVersionsForStaleness as $e}from"./stale-version-check.js";import{defaultCliChoiceDeps as Ae,detectCliAvailability as A,installedCliNames as N,parseCliFlag as ye,resolveCliChoice as Se}from"./cli-platform.js";import{getRefreshToken as Te,getIdToken as Pe}from"./config.js";import{composeGetStarted as Ie,shouldShowGetStarted as Le}from"./get-started.js";import{prepareCodexRemoteLaunch as Re,withCodexCwdArg as De,defaultCodexRemoteDeps as Oe,checkCodexBridgeHealthy as Ee}from"./codex-remote.js";import{BORG_CODEX_REMOTE_WAKE_ENV as He,codexAgentKindConfigArgs as Ne,codexRemoteWakeConfigArgs as Fe,withAgentRuntimeEnv as y}from"./agent-runtime.js";import{findLoadedCodexThread as Be}from"./codex-app-server.js";import{buildAgentKickoffPrompt as Me,buildKickoffWakePathClause as We,recordCodexWakeTarget as _e,socketPathFromRemoteArgs as F}from"./codex-launch.js";import{codexBorgSessionConfigArgs as Ke}from"./launch-gate.js";import{addCodexSessionStartHook as Ge,addCodexUserPromptSubmitHook as Ue,addProjectSessionStartHook as Ye,addUserPromptSubmitHook as Ve,removeSessionStartHook as je}from"./config-utils.js";import{ensureCliMcpConfigured as S}from"./ensure-mcp-config.js";import{installBorgPlugin as qe}from"./opencode-plugin.js";import{connectOpenCodeDrone as Xe,computeOpenCodePort as B,createOpenCodeLaunchKickoff as ze,injectInitialKickoff as Je}from"./opencode-drone.js";async function Qe(){be(process.argv),ee(),await ke();const c=(async()=>{if(!process.stderr.isTTY)return;const e=C(),o=await ve();if(!o)return;const i=$e(e,o);i.stale&&i.message&&process.stderr.write(`${t()}${i.message}
|
|
3
|
+
`)})();if((process.argv[2]==="--help"||process.argv[2]==="-h")&&(process.stdout.write(oe(C())),process.exit(0)),process.argv[2]==="setup"){O(process.argv[3])&&(process.stdout.write(re(C())),process.exit(0)),await import("./setup.js");return}if(process.argv[2]==="assimilate"){process.argv.slice(3).some(O)&&(process.stdout.write(se(C())),process.exit(0));const e=de(process.argv.slice(3));e.ok||(process.stderr.write(n.red(`${t()}\u25FC borg assimilate: ${e.error}
|
|
4
|
+
`)),process.stderr.write("Run `borg --help` for usage.\n"),process.exit(1));const o=ue(),i=await pe({role:e.role,flags:e.flags},o);process.exit(i)}if(process.argv[2]==="spawn"){const e=await te();process.exit(e)}if(process.argv[2]==="sync"){const e=ne(process.argv.slice(3));e.ok||(process.stderr.write(n.red(`${t()}\u25FC borg sync: ${e.error}
|
|
5
|
+
`)),process.stderr.write("Run `borg --help` for usage.\n"),process.exit(1));const o=await ae({},e.options);process.exit(o)}if(process.argv[2]==="cleanup"){const e=ce(process.argv.slice(3));e.ok||(process.stderr.write(n.red(`${t()}\u25FC borg cleanup: ${e.error}
|
|
6
|
+
`)),process.stderr.write("Run `borg --help` for usage.\n"),process.exit(1));const o=await le({},e.options);process.exit(o)}if(process.argv[2]==="launch-all"){const e=E(process.argv.slice(3));e.ok||(process.stderr.write(n.red(`${t()}\u25FC borg launch-all: ${e.error}
|
|
7
|
+
`)),process.stderr.write("Run `borg --help` for usage.\n"),process.exit(1));const o=$(),i=await H(e.args,o);process.exit(i)}const a=me(process.argv[2]);if(a!==null&&(process.stderr.write(n.red(`${t()}\u25FC unknown command: ${a}
|
|
8
|
+
`)),process.stderr.write("Run `borg --help` for usage.\n"),process.exit(1)),Le(await Te()!==null,await Pe()!==null)){const e=N(A()).length>0;process.stdout.write(Ie(e)),process.exit(0)}const d=ye(process.argv.slice(2));d.error&&(process.stderr.write(n.red(`${t()}\u25FC ${d.error}
|
|
9
|
+
`)),process.stderr.write("Run `borg --help` for usage.\n"),process.exit(1));const T=async e=>{const o=Y({input:process.stdin,output:process.stdout});try{return await o.question(e)}finally{o.close()}};let r=await Se(d.cli,Ae(T,()=>process.stdin.isTTY===!0));Ze();const s=await V();let x;const P=async()=>s?(x===void 0&&(x=(await fe({targetCubeId:s.cubeId},$())).length>0),x):!1,k=process.stdin.isTTY===!0,b=process.stdout.isTTY===!0,I=ge({explicitCli:d.cli,stdinIsTTY:k,stdoutIsTTY:b,hasActiveCube:s!==null,hasLaunchAllTargets:we({explicitCli:d.cli,stdinIsTTY:k,stdoutIsTTY:b,hasActiveCube:s!==null})?await P():!1});if(I&&process.stderr.write(I),Ce({extraArgs:process.argv.slice(2),stdinIsTTY:k,stdoutIsTTY:b})){const e=N(A()).filter(i=>i!==r),o=await he({defaultCli:r,otherInstalledClis:e,hasLaunchAllTargets:await P()},T);if(o.kind==="launch-all"){const i=E([]),_=i.ok?await H(i.args,$()):1;process.exit(_)}r=o.cli}const l=d.rest;xe(s?{label:s.droneLabel,cubeName:s.name}:null,U(process.cwd()));const M=We(r,s&&r==="claude"?q(s.cubeId,s.droneId):null,s&&r==="claude"?J(v(process.cwd())):null);await Promise.race([c,new Promise(e=>setTimeout(e,2e3))]);const L=r==="codex"?`borg-wake-${G()}`:null;let f,R=[],u={...y(process.env,r),BORG_SESSION:"1"},p=null,m=null;if(r==="codex"&&!l.includes("--remote")){console.error(`${t()}${n.gray("\u25FC Starting Codex remote-wake app-server\u2026")}`);const e=await Re(Oe());e.warning?(console.error(`${t()}${n.yellow(`warning: ${e.warning}`)}`),f="\u26A0 Codex wake-path capability check failed: remote-control is unavailable for this session. Run borg_regen manually whenever you return, and expect only fallback wakeups until relaunch."):f="Codex wake-path capability check passed: remote-control socket established for this session.",R=e.args,u={...y(process.env,r),...e.env,BORG_SESSION:"1"},p=F(e.args),m=e.server?.cleanup??null}else r==="codex"&&l.includes("--remote")&&(f="Codex wake-path capability check: using caller-provided --remote socket; if no wake arrives, run borg_regen manually when returning to the session.",p=F(l),p&&(u={...y(process.env,r),[He]:"1",BORG_SESSION:"1"}));if(s){const e=await j(s.cubeId,v(),s.droneId),o=Q(u,e,process.env);if(u=o.env,o.probe){const i=await Z(o.probe.descriptor,fetch,o.probe.baseUrl);i.ok||console.error(`${t()}${n.yellow(`warning: ${i.message}`)}`)}}const g=Me({cli:r,codexWakeNonce:L,monitorClause:M,codexWakePathClause:f});let h=null,w;if(r==="codex")w=[...Ke(),...Ne(),...Fe(p!==null),...R,...De([...l,g],process.cwd())];else if(r==="opencode"){const e=s?B(s.droneId):14096;qe(),h=ze(g),w=[process.cwd(),"--port",String(e),"--auto","--prompt",h.prompt,...l]}else w=ie(l,g);const W=r==="claude"?"Claude Code":r==="codex"?"Codex":"OpenCode";console.error(`${t()}${n.blue(`\u25FC Launching ${W}\u2026`)}`);const D=K(r,w,{stdio:"inherit",shell:!1,env:u});if(r==="opencode"&&h){const e=h,i=`http://127.0.0.1:${s?B(s.droneId):14096}`;Xe({serverUrl:i,directory:process.cwd(),droneLabel:s?.droneLabel??"opencode",cubeName:s?.name??"borg"}).then(()=>Je(e)).catch(()=>{})}r==="codex"&&s&&p&&(_e({deps:{setCodexWakeTarget:X,findLoadedCodexThread:Be},cubeId:s.cubeId,droneId:s.droneId,socketPath:p,passthroughArgs:l,previewNeedle:L??g.slice(0,120),cwd:process.cwd(),launchedAtSeconds:Math.floor(Date.now()/1e3)}),z(e=>Ee(e))),D.on("error",e=>{if(m)try{m()}catch{}if(e.code==="ENOENT"){console.error(`${t()}${n.red(`
|
|
10
|
+
\u25FC Failed to launch ${r}`)}`);const o=r==="opencode"?"opencode":r;console.error(`${t()}${n.gray(`Make sure ${o} is installed.
|
|
11
|
+
`)}`)}else console.error(`${t()}${n.red(`
|
|
12
|
+
\u25FC Failed to launch ${r}: ${e.message}
|
|
13
|
+
`)}`);process.exit(1)}),D.on("exit",e=>{if(m)try{m()}catch{}process.exit(e??0)})}function Ze(){const c=A();if(c.claude)try{S("claude"),Ye(v(process.cwd())),je(),Ve()}catch(a){console.error(`${t()}${n.yellow(`warning: Claude Code integration check failed: ${a?.message??a}`)}`)}if(c.codex)try{S("codex"),Ge(),Ue()}catch(a){console.error(`${t()}${n.yellow(`warning: Codex integration check failed: ${a?.message??a}`)}`)}if(c.opencode)try{S("opencode")}catch(a){console.error(`${t()}${n.yellow(`warning: OpenCode integration check failed: ${a?.message??a}`)}`)}}Qe().catch(c=>{console.error(`${t()}${n.red(`
|
|
14
14
|
\u25FC Error: ${c.message}
|
|
15
15
|
`)}`),process.exit(1)});
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import type { BorgCli } from './cubes.js';
|
|
2
|
+
export interface EnsureMcpConfigDeps {
|
|
3
|
+
isClaudeConfigured: () => boolean;
|
|
4
|
+
addClaude: () => void;
|
|
5
|
+
isCodexConfigured: () => boolean;
|
|
6
|
+
addCodex: () => void;
|
|
7
|
+
isOpenCodeConfigured: () => boolean;
|
|
8
|
+
addOpenCode: () => void;
|
|
9
|
+
}
|
|
10
|
+
/**
|
|
11
|
+
* Ensure borg is registered as an MCP server for one selected agent CLI.
|
|
12
|
+
*
|
|
13
|
+
* Returns true when this call added the registration and false when it was
|
|
14
|
+
* already present. Errors from an adder intentionally propagate so callers
|
|
15
|
+
* can fail the launch with the CLI-specific remediation message.
|
|
16
|
+
*/
|
|
17
|
+
export declare function ensureCliMcpConfigured(cli: BorgCli, deps?: EnsureMcpConfigDeps): boolean;
|
|
18
|
+
//# sourceMappingURL=ensure-mcp-config.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{addCodexMcpServer as r,addMcpServer as o,addOpenCodeMcpServer as n,isCodexMcpServerConfigured as u,isMcpServerConfigured as C,isOpenCodeMcpServerConfigured as i}from"./config-utils.js";const a={isClaudeConfigured:C,addClaude:o,isCodexConfigured:u,addCodex:r,isOpenCodeConfigured:i,addOpenCode:n};function c(d,e=a){switch(d){case"claude":return e.isClaudeConfigured()?!1:(e.addClaude(),!0);case"codex":return e.isCodexConfigured()?!1:(e.addCodex(),!0);case"opencode":return e.isOpenCodeConfigured()?!1:(e.addOpenCode(),!0)}}export{c as ensureCliMcpConfigured};
|
package/dist/index.js
CHANGED
|
@@ -1,38 +1,38 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import{Server as
|
|
2
|
+
import{Server as ee}from"@modelcontextprotocol/sdk/server/index.js";import{StdioServerTransport as te}from"@modelcontextprotocol/sdk/server/stdio.js";import{CallToolRequestSchema as oe,ListToolsRequestSchema as re,ListPromptsRequestSchema as ne,GetPromptRequestSchema as se}from"@modelcontextprotocol/sdk/types.js";import{assertRoleMatches as ie}from"./role-match.js";import{getCubeInfo as ae,getRoleInfo as L,getRoleInfoByName as ce,getRoster as le,readLog as de,appendLog as ue,submitReport as pe,fetchReports as me,ackLogEntry as be,recordDecision as fe,listDecisions as ge,regen as O,listCubes as he,createCube as _e,updateCube as B,deleteCube as ye,createRole as we,updateRole as xe,patchRoleSection as C,patchTaxonomyClass as F,deleteRole as $e,reassignDrone as ve,evictDrone as ke,getCube as $,checkSubscriptionStatus as Re,createBillingPortalSession as Ee,createSubscription as Ce,syncRoles as Se,applyTemplate as Te,whoami as Ie,roleRationale as qe,getValidToken as De}from"./remote-client.js";import{startHealthBeatTick as Pe}from"./health-beat.js";import{getTemplate as S,listTemplateNames as T,resolveCubeDirectiveForCreate as Ue,resolveCubeDirectiveForApply as Me,resolveMessageTaxonomyForCreate as je}from"./templates.js";import{activeCubeWithFreshRegenIdentity as H,getActiveCube as h,setActiveCube as W,findProjectRoot as I,inboxPathForDrone as q}from"./cubes.js";import{monitorStateRootForWorktree as D}from"./inbox-monitor.js";import{addSessionStartHook as Ne,addUserPromptSubmitHook as Ae}from"./config-utils.js";import{humanAgo as V,formatLogEntryMarkdown as Le,formatRegenMarkdown as z,getDronePlaybook as Oe,getDronePlaybookChapter as Be,nullTaxonomyTip as Fe,regenWakePathDroneLabel as He}from"./regen-format.js";import{startLogStream as We,getStreamStatus as P}from"./log-stream.js";import{isMcpReadinessProbe as Ve}from"./readiness-probe.js";import{runMcpStartupServices as ze}from"./startup-services.js";import{TOOL_MANIFEST as Ke}from"./tool-manifest.js";import{DOCS_SECTIONS as Je,matchDocsSections as Qe,formatDocsIndex as Ye}from"./docs-sections.js";import{renderRoleList as Ge}from"./list-roles-render.js";import{filterToolsForRole as Xe}from"./tool-scope.js";import{getPackageVersion as v,getOnDiskVersion as Ze,handleVersionFlag as et}from"./version.js";import{renderStreamStatus as tt,checkInboxMonitorHealthy as U,formatWakePathPrefix as ot,shouldShowWakePathWarning as rt}from"./stream-status.js";import{formatRoleAgentLabel as nt,formatWorkingRepoLabel as st,renderRoster as it}from"./roster-render.js";import{resolveWorkingRepo as K}from"./working-repo.js";import{resolveDroneIdByLabel as at,isUuidShape as ct}from"./evict-drone.js";import{authRecoveryMessage as lt}from"./auth-recovery.js";import{DroneEvictedError as dt,DroneFrozenError as ut,formatEvictedToolResult as pt,formatFrozenToolResult as mt}from"./drone-lifecycle.js";import{classifyInSessionAssimilate as bt,reattachOnlyRefusal as ft,reattachFailureMessage as gt}from"./assimilate-guard.js";import{gateAllowsActivation as ht,borgSessionToolNotice as _t}from"./launch-gate.js";import{renderSyncRolesResult as yt}from"./sync-roles-render.js";import{initConsolePrefix as wt,consolePrefix as k}from"./console-prefix.js";import{isCodexRemoteWakeEnabled as xt,resolveSessionAgentKind as M,probeCodexBridgeArmed as $t}from"./codex-app-wake.js";import{connectOpenCodeDrone as vt,injectOpenCodeEntry as kt,probeOpenCodeDroneArmed as Rt,computeOpenCodePort as Et}from"./opencode-drone.js";import{installBorgPlugin as Ct}from"./opencode-plugin.js";import{setModuleInjectOpenCode as St}from"./log-stream.js";import{lifecycleSignalForMessage as Tt,recordLifecycleLog as J,shouldSuppressLifecycleLog as It}from"./lifecycle-log-guard.js";import{normalizeDirectLogRecipients as qt}from"./direct-log.js";import Q from"open";import Dt from"os";function Pt(){try{const b=Dt.hostname();return b&&b.trim()?b.trim().slice(0,255):null}catch{return null}}async function Y(b,y){return await Te(b,y.name)}async function _(){const b=await h();if(!b)throw new Error("Not assimilated to a cube. Use borg_assimilate <cube-name> first.");return b}async function Ut(){et();const b=Ve();await ze(b,{sessionStartHook:()=>{Ne()},auditHook:()=>{Ae()},sseStream:()=>{We()},openCode:async()=>{Ct();const l=await h();if(l&&process.env.BORG_OPENCODE==="1"){const o=`http://127.0.0.1:${Et(l.droneId)}`;await vt({serverUrl:o,directory:process.cwd(),droneLabel:l.droneLabel,cubeName:l.name}),St(kt)}},healthBeat:()=>{Pe({getActiveCube:h,getStreamConnected:()=>P().connected,getInboxPath:l=>q(l.cubeId,l.droneId),checkMonitor:l=>U(l,D(I())),isCodexRemoteWake:xt,probeBridgeArmed:l=>$t({cubeId:l.cubeId,droneId:l.droneId}),probeOpenCodeDrone:()=>Rt(),resolveAgentKind:M,resolveHostname:Pt,resolveVersion:v,getToken:De,fetchImpl:globalThis.fetch.bind(globalThis)})}});const y=new ee({name:"borg-mcp-client",version:v()},{capabilities:{tools:{},prompts:{}}}),j=Ke;y.setRequestHandler(re,async()=>{let l=null;try{const u=await h();u&&(l={roleName:u.roleName,roleClass:u.roleClass,isHumanSeat:u.isHumanSeat})}catch{l=null}return{tools:Xe(j,l)}}),y.setRequestHandler(oe,async l=>{let{name:u,arguments:o}=l.params;if(u==="borg_describe-tool"){const e=typeof o?.name=="string"?o.name:"",t=j.find(r=>r.name===e);return t?{content:[{type:"text",text:JSON.stringify({name:t.name,description:t.description,inputSchema:t.inputSchema},null,2)}]}:{content:[{type:"text",text:`Unknown borg tool: ${e||"(none)"}. Pass { name: "<borg_tool>" }.`}],isError:!0}}if(u==="borg_tool"){const e=typeof o?.name=="string"?o.name:"";if(!e||e==="borg_tool"||e==="borg_describe-tool")return{content:[{type:"text",text:'borg_tool: pass { name: "<borg_tool>", arguments: {...} } naming a real borg tool (not the dispatcher itself).'}],isError:!0};o=o?.arguments&&typeof o.arguments=="object"?o.arguments:{},u=e}if(!ht(`tool ${u}`))return{content:[{type:"text",text:_t(u)}],isError:!0};try{switch(u){case"borg_regen":{const e=await h();if(!e)return{content:[{type:"text",text:'Not connected to a cube. Use `borg_assimilate cube_name="<name>"` to join one.'}]};const t=typeof o?.since=="string"?o.since:void 0,r=o?.mode==="lite"?"lite":"full",n=typeof o?.model=="string"?o.model:void 0,i=await O(e.sessionToken,e.apiUrl,{since:t,reportedModel:n,workingRepo:K()}),s=H(e,i);s!==e&&await W(s);const a=P(),c=q(s.cubeId,s.droneId),m=D(I()),f=M()==="claude"?U(c,m):!0,d=rt(a,f)?ot({inboxPath:c,monitorStateRoot:m,droneLabel:He(i,s.droneLabel),cubeName:s.name}):"";let g="";try{const w=v(),x=Ze();if(w!=="unknown"&&x!=="unknown"&&x!==w){const[R,N,X]=w.split(".").map(Number),[E,A,Z]=x.split(".").map(Number);(E>R||E===R&&A>N||E===R&&A===N&&Z>X)&&(g=`## \u{1F504} borgmcp ${x} installed \u2014 run /mcp and reconnect (or restart Claude Code) to apply. Currently running ${w}.
|
|
3
3
|
|
|
4
|
-
`)}}catch{}return{content:[{type:"text",text:
|
|
5
|
-
`)+
|
|
4
|
+
`)}}catch{}return{content:[{type:"text",text:g+d+z(i,{mode:r})}]}}case"borg_subscribe":return{content:[{type:"text",text:`Complete your subscription at: ${await Ce()}`}]};case"borg_upgrade-subscription":{const e=await Ee();try{await Q(e)}catch{}return{content:[{type:"text",text:`Manage your Borg MCP subscription at: ${e}`}]}}case"borg_subscription_status":{const e=await Re();return{content:[{type:"text",text:JSON.stringify(e,null,2)}]}}case"borg_open_dashboard":{const e="https://borgmcp.ai/dashboard";return await Q(e),{content:[{type:"text",text:`\u25FC Opened dashboard in browser: ${e}`}]}}case"borg_assimilate":{const e=o?.cube_name;if(!e)throw new Error("cube_name is required");const t=await h(),r=bt(t,e);if(r.kind!=="reattach")return{content:[{type:"text",text:ft(r,e)}],isError:!0};try{const n=await O(t.sessionToken,t.apiUrl,{workingRepo:K()}),i=H(t,n);return i!==t&&await W(i),{content:[{type:"text",text:[`# Re-attached to cube: ${i.name}`,"",`**Drone label:** ${i.droneLabel}`,"**Seat:** existing identity reused \u2014 no new drone minted (gh#780)","",""].join(`
|
|
5
|
+
`)+z(n,{mode:"full"})}]}}catch(n){const i=gt(n??{});if(!i)throw n;return{content:[{type:"text",text:i}],isError:!0}}}case"borg_version":return{content:[{type:"text",text:`borgmcp ${v()}`}]};case"borg_playbook":return{content:[{type:"text",text:Be()}]};case"borg_docs":{const e=typeof o?.topic=="string"?o.topic.trim():"",t=e?Qe(e):[],r=t.length>0?t:Je;return{content:[{type:"text",text:`${e&&t.length>0?`Best-matching docs section(s) for "${e}" \u2014 WebFetch the URL for the full page:`:e?`No exact match for "${e}". Full Borg MCP docs index \u2014 WebFetch the URL you need:`:"Borg MCP docs index \u2014 WebFetch the URL of the section you need:"}
|
|
6
6
|
|
|
7
|
-
${
|
|
8
|
-
`)}]}}case"borg_role":{const e=await
|
|
9
|
-
`)}]}}const{role:r}=await
|
|
10
|
-
`)}]}}case"borg_role-rationale":{const e=await
|
|
11
|
-
`)}]}}case"borg_roster":{const e=await
|
|
7
|
+
${Ye(r)}`}]}}case"borg_whoami":{const e=await _(),t=await Ie(e.sessionToken,e.apiUrl);return{content:[{type:"text",text:JSON.stringify(t,null,2)}]}}case"borg_cube":{const e=await _(),[{cube:t,roles:r}]=await Promise.all([ae(e.sessionToken,e.apiUrl),L(e.sessionToken,e.apiUrl)]),n=[];n.push(`# Cube: ${t.name}`),n.push(""),n.push("## Cube directive"),n.push(t.cube_directive||"_(none)_"),n.push("");const i=Fe(t.message_taxonomy);if(i&&(n.push(i),n.push("")),n.push("## Roles in this cube"),!r.length)n.push("_(no roles defined)_");else{for(const s of r){const a=[s.role_class==="queen"?"Queen":null,s.is_human_seat?"human-seat":null,s.is_default?"default":null,s.is_mandatory?"mandatory":null].filter(Boolean).join(", "),c=a?` (${a})`:"",m=s.short_description||"_(no description)_";n.push(`- **${s.name}**${c} \u2014 ${m}`)}n.push(""),n.push("_(Coordinator-class drones can fetch role IDs via `borg_list-roles` for use with `borg_reassign-drone`.)_")}return n.push(""),n.push(Oe()),{content:[{type:"text",text:n.join(`
|
|
8
|
+
`)}]}}case"borg_role":{const e=await _(),t=typeof o?.role=="string"?o.role.trim():"";if(t){const{role:i}=await ce(e.sessionToken,e.apiUrl,t);return ie(t,i),{content:[{type:"text",text:[`# Role: ${i.name}`,"",i.detailed_description||"_(no detailed description set)_"].join(`
|
|
9
|
+
`)}]}}const{role:r}=await L(e.sessionToken,e.apiUrl);return{content:[{type:"text",text:[`# Your role: ${r.name}`,"",r.detailed_description||"_(no detailed description set)_"].join(`
|
|
10
|
+
`)}]}}case"borg_role-rationale":{const e=await _(),t=typeof o?.role=="string"?o.role:"",r=typeof o?.section=="string"?o.section:"",n=await qe(e.sessionToken,e.apiUrl,t,r);return{content:[{type:"text",text:[`# Role rationale: ${n.role} \u2014 ${n.section}`,"",n.body||"_(empty)_"].join(`
|
|
11
|
+
`)}]}}case"borg_roster":{const e=await _(),t=typeof o?.since=="string"?o.since:void 0,{drones:r,roles:n,since:i}=await le(e.sessionToken,e.apiUrl,t);return{content:[{type:"text",text:it({cubeName:e.name,drones:r,roles:n,resolvedSince:i??null,humanAgo:V})}]}}case"borg_stream-status":{const e=P(),t=await h(),r=t?q(t.cubeId,t.droneId):null,n=t?D(I()):null,i=t&&M()!=="claude",s=t?i?!0:U(r,n):null;let a="";e.runLoopHealth==="silent-inert"&&(a=`## \u26A0 SSE stream loop silent-inert \u2014 run /mcp and reconnect to restart
|
|
12
12
|
|
|
13
13
|
The log-stream consumer started but never connected. This drone will not receive real-time cube events.
|
|
14
14
|
|
|
15
|
-
`);const c=
|
|
16
|
-
`)}]}}case"borg_log":{const e=o?.message;if(!e||typeof e!="string")throw new Error("message is required");const t=await
|
|
17
|
-
${c.routing.message}`:"",
|
|
18
|
-
\u26A0 ${c.unreachableRecipients.length} directed recipient(s) currently unreachable (wake-path:deaf): ${c.unreachableRecipients.map(
|
|
15
|
+
`);const c=tt({status:e,inboxMonitorHealthy:s,inboxPath:r,monitorStateRoot:n,droneLabel:t?.droneLabel??null,cubeName:t?.name??null,humanAgo:V});return{content:[{type:"text",text:a+c}]}}case"borg_read-log":{const e=await _(),t=typeof o?.since=="string"?o.since:void 0,r=typeof o?.limit=="number"?o.limit:void 0,n=o?.unread_only===!0||o?.unread_only==="true",{entries:i,drones:s,roles:a,behind_by:c,has_more:m}=await de(e.sessionToken,e.apiUrl,{since:t,limit:r,unreadOnly:n}),p=new Map;for(const g of s)p.set(g.id,g);const f=new Map;for(const g of a)f.set(g.id,g);const d=[];if(d.push(`# Activity log: ${e.name}`),d.push(""),!i.length)d.push("_(no entries)_");else for(const g of i)d.push(Le(g,p,f));return m===!0?(d.push(""),d.push("\u26A0 has_more: true \u2014 call `borg_read-log unread_only=true` again until has_more=false so you finish draining unread entries.")):typeof c=="number"&&c>0&&(d.push(""),d.push(`\u26A0 behind_by: ${c} more unread ${c===1?"entry":"entries"} addressed to you \u2014 call \`borg_read-log unread_only=true\` again until behind_by=0 so you don't skip messages.`)),{content:[{type:"text",text:d.join(`
|
|
16
|
+
`)}]}}case"borg_log":{const e=o?.message;if(!e||typeof e!="string")throw new Error("message is required");const t=await h();if(!t)throw new Error("Not assimilated to a cube. Use borg_assimilate <cube-name> first.");if(Tt(e)){const d=await It(t,e);if(d.suppress)return await J(t,e),{content:[{type:"text",text:`Suppressed duplicate ${d.signal?.toUpperCase()} lifecycle log for ${t.droneLabel}; recent cube log already contains this signal.`}]}}const r=Object.prototype.hasOwnProperty.call(o??{},"to"),n=r?qt(o?.to):void 0,i=typeof o?.class=="string"?o.class:void 0,s=o?.visibility==="broadcast"||o?.visibility==="direct"?o.visibility:void 0,a={...i?{class:i}:{},...r?{to:n??[]}:{},...s?{visibility:s}:{}},c=await ue(t.sessionToken,t.apiUrl,e,a);await J(t,e);const m=c.routing?.message?`
|
|
17
|
+
${c.routing.message}`:"",p=c.unreachableRecipients?.length?`
|
|
18
|
+
\u26A0 ${c.unreachableRecipients.length} directed recipient(s) currently unreachable (wake-path:deaf): ${c.unreachableRecipients.map(d=>d.label).join(", ")}. Message delivered \u2014 they'll read it when they return.`:"";return{content:[{type:"text",text:`Logged to cube "${t.name}" as ${t.droneLabel}. (entry id: ${c.entry.id})${m}${p}`}]}}case"borg_report-friction":{const e=o?.message;if(!e||typeof e!="string")throw new Error("message is required");const t=await h();if(!t)throw new Error("Not assimilated to a cube. Use borg_assimilate <cube-name> first.");const r=o?.kind==="bug"?"bug":"friction",n=o?.metadata&&typeof o.metadata=="object"&&!Array.isArray(o.metadata)?o.metadata:void 0;return{content:[{type:"text",text:(await pe(t.sessionToken,t.apiUrl,{kind:r,message:e,metadata:n})).ok?"Report submitted \u2014 thank you. The borgmcp team will see it. (Write-only: you cannot read reports back.)":"Report did not submit. Try again, or raise it in the cube log."}]}}case"borg_reports":{const e=await me();if(e.forbidden)return{content:[{type:"text",text:"Reports triage is builder/dogfooder-tier only. Your account is not on the dogfooder (builder) tier, so the friction-reports store is not readable. (Server-enforced gate.)"}]};if(!e.reports.length)return{content:[{type:"text",text:"No reports yet. Submissions via borg_report-friction will appear here, newest first."}]};const t=e.reports.map(r=>{const n=r.metadata&&Object.keys(r.metadata).length?" \xB7 "+Object.entries(r.metadata).map(([s,a])=>`${s}=${a}`).join(", "):"",i=r.redacted?" \xB7 [secrets-scrubbed]":"";return`**[${r.kind}]** ${r.created_at} \xB7 ${r.reporter_email}${n}${i}
|
|
19
19
|
${r.message}`});return{content:[{type:"text",text:`Reports (${e.reports.length}, newest first):
|
|
20
20
|
|
|
21
21
|
${t.join(`
|
|
22
22
|
|
|
23
23
|
---
|
|
24
24
|
|
|
25
|
-
`)}`}]}}case"borg_ack":{const e=o?.entry_id;if(!e||typeof e!="string")throw new Error("entry_id is required");const t=o?.kind==="claim"?"claim":"ack",r=await
|
|
26
|
-
`)}]}}case"borg_list-cubes":{const{cubes:e}=await
|
|
25
|
+
`)}`}]}}case"borg_ack":{const e=o?.entry_id;if(!e||typeof e!="string")throw new Error("entry_id is required");const t=o?.kind==="claim"?"claim":"ack",r=await _();return await be(r.sessionToken,r.apiUrl,e,t),{content:[{type:"text",text:t==="claim"?`Claimed entry ${e} in cube "${r.name}" (advisory \u2014 merge stays keyed on REVIEW-APPROVED).`:`Acked entry ${e} in cube "${r.name}".`}]}}case"borg_decide":{const e=o?.topic,t=o?.decision;if(!e||typeof e!="string")throw new Error("topic is required");if(!t||typeof t!="string")throw new Error("decision is required");const r=typeof o?.rationale=="string"?o.rationale:void 0,n=await _(),{decision:i}=await fe(n.sessionToken,n.apiUrl,{topic:e,decision:t,...r!==void 0?{rationale:r}:{}}),s=i?.supersedes?" (superseded the prior decision on this topic)":"";return{content:[{type:"text",text:`Recorded ratified decision on "${e}" in cube "${n.name}"${s}. Cite it via borg_decisions; it surfaces in borg_regen.`}]}}case"borg_decisions":{const e=typeof o?.topic=="string"?o.topic:void 0,t=await _(),{decisions:r}=await ge(t.sessionToken,t.apiUrl,e);return{content:[{type:"text",text:r.length===0?e?`No active ratified decision on "${e}" in cube "${t.name}".`:`No active ratified decisions in cube "${t.name}".`:r.map(i=>`**${i.topic}:** ${i.decision}${i.rationale?` \u2014 ${i.rationale}`:""}`).join(`
|
|
26
|
+
`)}]}}case"borg_list-cubes":{const{cubes:e}=await he();if(!e.length)return{content:[{type:"text",text:"No cubes yet. Use borg_create-cube to make your first one."}]};const t=e.map(r=>`- **${r.name}** (id: ${r.id})
|
|
27
27
|
${(r.cube_directive||"_(no directive set)_").split(`
|
|
28
28
|
`)[0].slice(0,120)}`);return{content:[{type:"text",text:`Your cubes (${e.length}):
|
|
29
29
|
|
|
30
30
|
${t.join(`
|
|
31
31
|
|
|
32
|
-
`)}`}]}}case"borg_create-cube":{const e=o?.name,t=o?.cube_directive,r=o?.template;if(!e)throw new Error("name is required");if(t===void 0)throw new Error("cube_directive is required (pass empty string if none)");let n=null;if(r&&(n=
|
|
32
|
+
`)}`}]}}case"borg_create-cube":{const e=o?.name,t=o?.cube_directive,r=o?.template;if(!e)throw new Error("name is required");if(t===void 0)throw new Error("cube_directive is required (pass empty string if none)");let n=null;if(r&&(n=S(r),!n))throw new Error(`Unknown template "${r}". Available: ${T().join(", ")}`);const i=Ue(t,n),s=je(void 0,n),a=await _e(e,i,{message_taxonomy:s});if(n){const m=await Y(a.id,n),p=i!==t?" Template cube directive applied (operator passed empty).":"";return{content:[{type:"text",text:`Created cube **${a.name}** (id: ${a.id}) with template **${r}** applied \u2014 ${m.created} role(s) created, ${m.updated} updated.${p} Use borg_assimilate ${a.name} to join as a drone.`}]}}return{content:[{type:"text",text:`Created cube **${a.name}** (id: ${a.id}). A default "Drone" role was seeded \u2014 rename or replace it via borg_update-role / borg_create-role / borg_delete-role. Use borg_assimilate ${a.name} to join as a drone.`}]}}case"borg_update-cube":{const e=o?.cube_id;if(!e)throw new Error("cube_id is required");const t={};if(typeof o?.name=="string"&&(t.name=o.name),typeof o?.cube_directive=="string"&&(t.cube_directive=o.cube_directive),Array.isArray(o?.message_taxonomy)&&(t.message_taxonomy=o.message_taxonomy),Object.keys(t).length===0)throw new Error("Pass at least one of: name, cube_directive, message_taxonomy.");const{cube:r}=await B(e,t);return{content:[{type:"text",text:`Updated cube **${r.name}** (id: ${r.id}).`}]}}case"borg_patch-taxonomy-class":{const e=o?.cube_id;if(!e)throw new Error("cube_id is required");const t=o?.action;if(t!=="add"&&t!=="replace"&&t!=="remove")throw new Error("action must be one of: add, replace, remove.");let r,n;if(t==="remove"){const s=o?.class;if(!s)throw new Error("class is required for remove.");({cube:r}=await F(e,{action:t,class:s})),n=s}else{const s=o?.class_def;if(s==null||typeof s!="object"||Array.isArray(s))throw new Error("class_def (object) is required for add/replace.");({cube:r}=await F(e,{action:t,class_def:s})),n=String(s.class??"")}return{content:[{type:"text",text:`${t==="add"?"Added":t==="replace"?"Replaced":"Removed"} taxonomy class **${n}** in cube **${r.name}** (id: ${r.id}).`}]}}case"borg_delete-cube":{const e=o?.cube_id;if(!e)throw new Error("cube_id is required");return await ye(e),{content:[{type:"text",text:`Deleted cube ${e} (and all its roles, drones, log entries).`}]}}case"borg_create-role":{const e=o?.cube_id,t=o?.name,r=o?.short_description,n=o?.detailed_description;if(!e)throw new Error("cube_id is required");if(!t)throw new Error("name is required");if(r===void 0)throw new Error("short_description is required (pass empty string if none)");if(n===void 0)throw new Error("detailed_description is required (pass empty string if none)");const i=o?.is_default===!0,s=o?.is_mandatory===!0,a=o?.is_human_seat===!0,c=o?.can_broadcast===!0,m=o?.receives_all_direct===!0,{role:p}=await we(e,{name:t,short_description:r,detailed_description:n,is_default:i,is_mandatory:s,is_human_seat:a,can_broadcast:c,receives_all_direct:m,...typeof o?.default_model=="string"?{default_model:o.default_model}:{}}),f=[p.role_class==="queen"?"Queen":null,p.is_human_seat?"human-seat":null,p.is_default?"default":null,p.is_mandatory?"mandatory":null].filter(Boolean).join(", "),d=f?` (${f})`:"";return{content:[{type:"text",text:`Created role **${p.name}**${d} (id: ${p.id}) in cube ${e}.`}]}}case"borg_update-role":{const e=o?.role_id;if(!e)throw new Error("role_id is required");const t={};if(typeof o?.name=="string"&&(t.name=o.name),typeof o?.short_description=="string"&&(t.short_description=o.short_description),typeof o?.detailed_description=="string"&&(t.detailed_description=o.detailed_description),typeof o?.is_default=="boolean"&&(t.is_default=o.is_default),typeof o?.is_mandatory=="boolean"&&(t.is_mandatory=o.is_mandatory),typeof o?.is_human_seat=="boolean"&&(t.is_human_seat=o.is_human_seat),typeof o?.can_broadcast=="boolean"&&(t.can_broadcast=o.can_broadcast),typeof o?.receives_all_direct=="boolean"&&(t.receives_all_direct=o.receives_all_direct),typeof o?.default_model=="string"&&(t.default_model=o.default_model),Object.keys(t).length===0)throw new Error("Pass at least one of: name, short_description, detailed_description, is_default, is_mandatory, is_human_seat, can_broadcast, receives_all_direct, default_model.");const{role:r}=await xe(e,t),n=[r.role_class==="queen"?"Queen":null,r.is_human_seat?"human-seat":null,r.is_default?"default":null,r.is_mandatory?"mandatory":null].filter(Boolean).join(", "),i=n?` (${n})`:"";return{content:[{type:"text",text:`Updated role **${r.name}**${i} (id: ${r.id}).`}]}}case"borg_patch-role-section":{const e=o?.role_id;if(!e)throw new Error("role_id is required");const t=o?.action;if(t!=="replace"&&t!=="insert"&&t!=="delete")throw new Error("action must be one of: replace, insert, delete.");const r=o?.heading;if(!r)throw new Error("heading is required");let n;if(t==="delete")({role:n}=await C(e,{action:t,heading:r}));else{const s=o?.body;if(typeof s!="string")throw new Error("body is required for replace/insert (pass empty string for an empty section).");if(t==="insert"){const a=typeof o?.after=="string"?o.after:null;({role:n}=await C(e,{action:t,heading:r,body:s,after:a}))}else({role:n}=await C(e,{action:t,heading:r,body:s}))}return{content:[{type:"text",text:`${t==="replace"?"Replaced":t==="insert"?"Inserted":"Deleted"} section **${r}** in role **${n.name}** (id: ${n.id}).`}]}}case"borg_delete-role":{const e=o?.role_id;if(!e)throw new Error("role_id is required");return await $e(e),{content:[{type:"text",text:`Deleted role ${e}.`}]}}case"borg_reassign-drone":{const e=o?.drone_id,t=o?.role_id;if(!e)throw new Error("drone_id is required");if(!t)throw new Error("role_id is required");const{drone:r}=await ve(e,t);return{content:[{type:"text",text:`Reassigned drone ${r.label} (${r.id}) to role ${r.role_id}.`}]}}case"borg_evict-drone":{const e=o?.drone_id?.trim(),t=o?.label?.trim(),r=o?.cube_id?.trim();let n,i;if(e){if(!ct(e))throw new Error(`drone_id "${e}" is not a UUID \u2014 if that's a drone label, pass it as label + cube_id instead.`);n=e,i=e}else if(t){if(!r)throw new Error("cube_id is required when evicting by label");const{drones:s}=await $(r),a=at(s,t);if(!a)throw new Error(`No active drone labelled "${t}" in cube ${r} (it may already be evicted; check borg_list-drones).`);n=a.id,i=a.label}else throw new Error("Provide drone_id, or label + cube_id, to identify the drone to evict");return await ke(n),{content:[{type:"text",text:`Evicted drone ${i} (${n}). Soft-deleted: removed from the roster and freed its seat; log history preserved with anonymized attribution.`}]}}case"borg_list-drones":{const e=o?.cube_id;if(!e)throw new Error("cube_id is required");const{drones:t,roles:r}=await $(e);if(!t.length)return{content:[{type:"text",text:"No drones in this cube yet."}]};const n=new Map(r.map(s=>[s.id,s])),i=t.map(s=>{const a=n.get(s.role_id),c=nt(a?.name??"?",s.agent_kind),m=s.wake_path_alert_class&&s.wake_path_alert_class!=="independent"?` \u2014 wake-path-class: ${s.wake_path_alert_class}`:"",p=s.reported_model?` \u2014 Reported model: ${s.reported_model}`:" \u2014 Reported model: not reported",f=st(s),d=f?` \u2014 ${f}`:"";return`- **${s.label}** (id: ${s.id}) \u2014 role: ${c} (${s.role_id}) \u2014 last seen ${s.last_seen}${m}${p}${d}`});return{content:[{type:"text",text:`Drones in cube ${e} (${t.length}):
|
|
33
33
|
|
|
34
|
-
${
|
|
35
|
-
`)}`}]}}case"borg_list-roles":{const e=o?.cube_id;if(!e)throw new Error("cube_id is required");const{roles:t}=await
|
|
34
|
+
${i.join(`
|
|
35
|
+
`)}`}]}}case"borg_list-roles":{const e=o?.cube_id;if(!e)throw new Error("cube_id is required");const{roles:t}=await $(e);return{content:[{type:"text",text:Ge(t,e)}]}}case"borg_list-templates":return{content:[{type:"text",text:`Available templates:
|
|
36
36
|
|
|
37
|
-
${
|
|
38
|
-
`)}`}]};case"borg_sync-roles":{const e=o?.cube_id,t=o?.template_name||"software-dev",r=o?.apply===!0,n=o?.decisions&&typeof o.decisions=="object"?o.decisions:void 0;if(!e)throw new Error("cube_id is required");const
|
|
37
|
+
${T().map(r=>{const n=S(r);return`- **${r}**: ${n.description}`}).join(`
|
|
38
|
+
`)}`}]};case"borg_sync-roles":{const e=o?.cube_id,t=o?.template_name||"software-dev",r=o?.apply===!0,n=o?.decisions&&typeof o.decisions=="object"?o.decisions:void 0;if(!e)throw new Error("cube_id is required");const i=await Se(e,t,r,n);return{content:[{type:"text",text:yt(i,t)}]}}case"borg_apply-template":{const e=o?.cube_id,t=o?.template_name;if(!e)throw new Error("cube_id is required");if(!t)throw new Error("template_name is required");const r=S(t);if(!r)throw new Error(`Unknown template "${t}". Available: ${T().join(", ")}`);const n=await Y(e,r);let i="";const s=await $(e),a=Me(s.cube_directive,r);return a!==null&&(await B(e,{cube_directive:a}),i=" Template cube directive applied (cube directive was empty)."),{content:[{type:"text",text:`Applied template **${t}** to cube ${e} \u2014 ${n.created} role(s) created, ${n.updated} updated.${i}`}]}}default:throw new Error(`Unknown tool: ${u}`)}}catch(e){if(e instanceof dt)return{content:[{type:"text",text:pt(e.message)}],isError:!0};if(e instanceof ut)return{content:[{type:"text",text:mt(e.message)}],isError:!0};const t=lt(e??{});return t?{content:[{type:"text",text:t}],isError:!0}:{content:[{type:"text",text:`Error: ${e.message}`}],isError:!0}}}),y.setRequestHandler(ne,async()=>({prompts:[{name:"borg_subscribe",description:"Set up Borg MCP Cube tier subscription ($1/month per cube; each cube adds 8 pooled agent sessions + 1000 req/hr). Free tier is permanent (1 cube + 3 agent sessions + 100 req/hr); no trial."},{name:"dashboard",description:"Open Borg MCP dashboard to manage cubes"}]})),y.setRequestHandler(se,async l=>{const{name:u}=l.params;switch(u){case"borg_subscribe":return{description:"Set up Borg MCP Cube tier subscription ($1/month per cube; each cube adds 8 pooled agent sessions + 1000 req/hr). Free tier is permanent (1 cube + 3 agent sessions + 100 req/hr); no trial.",messages:[{role:"user",content:{type:"text",text:"Please help me set up a Borg MCP subscription using the subscribe tool."}}]};case"dashboard":return{description:"Open Borg MCP dashboard to manage cubes",messages:[{role:"user",content:{type:"text",text:"Please open the Borg MCP dashboard using the borg_open_dashboard tool."}}]};default:throw new Error(`Unknown prompt: ${u}`)}});const G=new te;await y.connect(G),await wt(),console.error(`${k()}\u25FC Borg MCP Client started`),console.error(`${k()}\u25FC Use borg_assimilate <cube-name> to join a cube as a drone`),console.error(`${k()}\u25FC Manage your cubes at https://borgmcp.ai/dashboard`)}Ut().catch(b=>{console.error(`${k()}Fatal error:`,b),process.exit(1)});
|
|
@@ -12,6 +12,7 @@ export interface RoleForRender {
|
|
|
12
12
|
name: string;
|
|
13
13
|
short_description?: string;
|
|
14
14
|
is_default?: boolean;
|
|
15
|
+
is_mandatory?: boolean;
|
|
15
16
|
is_human_seat?: boolean;
|
|
16
17
|
can_broadcast?: boolean;
|
|
17
18
|
receives_all_direct?: boolean;
|
|
@@ -25,7 +26,7 @@ export interface RoleForRender {
|
|
|
25
26
|
* Each role line shape:
|
|
26
27
|
* - **<name>**(<tags>) `<uuid>` — <description>
|
|
27
28
|
*
|
|
28
|
-
* Tags collected in order: Queen, human-seat, default, can-broadcast,
|
|
29
|
+
* Tags collected in order: Queen, human-seat, default, mandatory, can-broadcast,
|
|
29
30
|
* receives-all-direct. Joined with `, `; suffix omitted entirely when no tags apply.
|
|
30
31
|
*/
|
|
31
32
|
export declare function renderRoleList(roles: RoleForRender[], cubeId: string): string;
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
function i(n,
|
|
1
|
+
function i(n,a){if(!n.length)return"No roles in this cube yet.";const l=n.map(e=>{const t=[e.role_class==="queen"?"Queen":null,e.is_human_seat?"human-seat":null,e.is_default?"default":null,e.is_mandatory?"mandatory":null,e.can_broadcast?"can-broadcast":null,e.receives_all_direct?"receives-all-direct":null].filter(Boolean).join(", "),o=t?` (${t})`:"",s=e.short_description||"_(no description)_";return`- **${e.name}**${o} \`${e.id}\` \u2014 ${s}`});return`Roles in cube ${a} (${n.length}):
|
|
2
2
|
|
|
3
3
|
${l.join(`
|
|
4
4
|
`)}
|
package/dist/opencode-drone.d.ts
CHANGED
|
@@ -4,13 +4,24 @@ interface ConnectDeps {
|
|
|
4
4
|
droneLabel: string;
|
|
5
5
|
cubeName: string;
|
|
6
6
|
}
|
|
7
|
+
export interface OpenCodeLaunchKickoff {
|
|
8
|
+
prompt: string;
|
|
9
|
+
nonce: string;
|
|
10
|
+
}
|
|
11
|
+
/**
|
|
12
|
+
* Add a launch-unique identity to the OpenCode-only copy of the shared
|
|
13
|
+
* kickoff. The prompt is what OpenCode records as its first user message, so
|
|
14
|
+
* the launcher can later bind the MCP child to this precise launch instead of
|
|
15
|
+
* guessing from a repeated kickoff's text or timestamp.
|
|
16
|
+
*/
|
|
17
|
+
export declare function createOpenCodeLaunchKickoff(kickoff: string, nonce?: string): OpenCodeLaunchKickoff;
|
|
7
18
|
export declare function connectOpenCodeDrone(deps: ConnectDeps): Promise<void>;
|
|
8
19
|
/**
|
|
9
|
-
* Wait for the
|
|
10
|
-
*
|
|
11
|
-
* process
|
|
20
|
+
* Wait for the OpenCode HTTP server, then capture the session that received
|
|
21
|
+
* this launch's nonce-bearing `--prompt` kickoff. The binding survives the separate
|
|
22
|
+
* MCP-child process, which must never fall back to a newest-session heuristic.
|
|
12
23
|
*/
|
|
13
|
-
export declare function injectInitialKickoff(
|
|
24
|
+
export declare function injectInitialKickoff(launch: OpenCodeLaunchKickoff): Promise<boolean>;
|
|
14
25
|
/**
|
|
15
26
|
* Inject a silent context entry (noReply) into our session.
|
|
16
27
|
* Falls through silently — caller falls back to inbox write.
|
|
@@ -24,5 +35,7 @@ export declare function getOpenCodeConnectionState(): {
|
|
|
24
35
|
totalEntriesInjected: number;
|
|
25
36
|
};
|
|
26
37
|
export declare function computeOpenCodePort(droneId: string, base?: number): number;
|
|
38
|
+
/** Test-only cleanup for module state and the local cross-process binding. */
|
|
39
|
+
export declare function __resetOpenCodeDroneForTests(): void;
|
|
27
40
|
export {};
|
|
28
41
|
//# sourceMappingURL=opencode-drone.d.ts.map
|
package/dist/opencode-drone.js
CHANGED
|
@@ -1,2 +1,4 @@
|
|
|
1
|
-
import{appendFileSync as
|
|
2
|
-
`;try{
|
|
1
|
+
import{appendFileSync as v,existsSync as A,readFileSync as T,renameSync as L,unlinkSync as I,writeFileSync as U}from"fs";import{createHash as x,randomUUID as E}from"crypto";import{join as h}from"path";import{tmpdir as S}from"os";const j=h(S(),"borg-opencode-drone.log");function a(e){const n=`[${new Date().toISOString()}] ${e}
|
|
2
|
+
`;try{v(j,n)}catch{}}let s=null;const b="borg-opencode-correlation:";function Q(e,n=E()){return{prompt:`${e}
|
|
3
|
+
|
|
4
|
+
<!-- ${b}${n} -->`,nonce:n}}const f=new Set;async function V(e){s={serverUrl:e.serverUrl,sessionId:null,sessionCreatedAt:null,knownRootSessionIds:[],directory:e.directory,droneLabel:e.droneLabel,cubeName:e.cubeName,connected:!0,totalEntriesInjected:0},a(`connected url=${e.serverUrl} dir=${e.directory}`)}function k(e){return`${s.serverUrl.replace(/\/+$/,"")}${e}${e.includes("?")?"&":"?"}directory=${encodeURIComponent(s.directory)}`}const g=1e4;async function y(e){const n=k(e),t=new AbortController,o=setTimeout(()=>t.abort(),g);try{const i=await fetch(n,{signal:t.signal}),r=await i.text();return{status:i.status,body:r}}finally{clearTimeout(o)}}async function _(e,n){const t=k(e),o=new AbortController,i=setTimeout(()=>o.abort(),g);try{const r=await fetch(t,{method:"POST",headers:{"Content-Type":"application/json"},signal:o.signal,body:JSON.stringify(n)}),c=await r.text();return{status:r.status,body:c}}finally{clearTimeout(i)}}async function m(){const{status:e,body:n}=await y("/session");if(e!==200)throw new Error(`OpenCode sessions request failed (${e})`);return JSON.parse(n)}async function C(e){try{const{status:n,body:t}=await y(`/session/${e}`);return n!==200?null:JSON.parse(t)}catch{return null}}async function P(e){const{status:n,body:t}=await y(`/session/${e}/message`);if(n!==200)throw new Error(`OpenCode session messages request failed (${n})`);return JSON.parse(t)}async function $(e,n){const{status:t}=await _(`/session/${e}/prompt_async`,n);return t===200||t===204}function p(){const e=s,n=[e.serverUrl,e.directory,e.cubeName,e.droneLabel].join("\0"),t=x("sha256").update(n).digest("hex").slice(0,24),o=h(S(),`borg-opencode-session-${t}.json`);return f.add(o),o}function D(e){const n=s;return e.version===2&&e.serverUrl===n.serverUrl&&e.directory===n.directory&&e.droneLabel===n.droneLabel&&e.cubeName===n.cubeName&&typeof e.sessionId=="string"&&typeof e.sessionCreatedAt=="number"&&Array.isArray(e.knownRootSessionIds)&&e.knownRootSessionIds.every(t=>typeof t=="string")}function F(){try{const e=p();if(!A(e))return null;const n=JSON.parse(T(e,"utf-8"));return D(n)?n:null}catch{return null}}function u(){if(!s)return;const e=p();s.sessionId=null,s.sessionCreatedAt=null,s.knownRootSessionIds=[];try{I(e)}catch{}}function d(e,n){const t=s,o={version:2,sessionId:e.id,sessionCreatedAt:e.time.created,knownRootSessionIds:n,serverUrl:t.serverUrl,directory:t.directory,droneLabel:t.droneLabel,cubeName:t.cubeName};t.sessionId=o.sessionId,t.sessionCreatedAt=o.sessionCreatedAt,t.knownRootSessionIds=o.knownRootSessionIds;try{const i=p(),r=`${i}.${process.pid}.tmp`;U(r,JSON.stringify(o),{mode:384}),L(r,i)}catch(i){a(`session binding write failed: ${i}`)}}function O(){if(!s)return null;if(s.sessionId&&s.sessionCreatedAt!==null)return{version:2,sessionId:s.sessionId,sessionCreatedAt:s.sessionCreatedAt,knownRootSessionIds:s.knownRootSessionIds,serverUrl:s.serverUrl,directory:s.directory,droneLabel:s.droneLabel,cubeName:s.cubeName};const e=F();return e?(s.sessionId=e.sessionId,s.sessionCreatedAt=e.sessionCreatedAt,s.knownRootSessionIds=e.knownRootSessionIds,e):null}function R(e,n){return e.id===n.sessionId&&e.directory===s.directory}function N(e){return!e.parentID}async function w(e){try{const t=(await m()).filter(r=>r.directory===s.directory&&N(r)),o=t.filter(r=>!e.includes(r.id));return o.length===0?null:{session:o.reduce((r,c)=>r.time.created>c.time.created?r:c),knownRootSessionIds:t.map(r=>r.id)}}catch{return null}}function M(e,n){let t=null;for(const o of e){if(o.info?.role&&o.info.role!=="user"||!o.parts?.some(c=>c.type==="text"&&c.text?.includes(`${b}${n}`)))continue;const r=o.info?.time?.created??0;t=t===null?r:Math.max(t,r)}return t}async function J(e){try{const n=(await m()).filter(c=>c.directory===s.directory),t=n.filter(N).map(c=>c.id),i=(await Promise.all(n.map(async c=>{try{const l=M(await P(c.id),e);return l===null?null:{session:c,messageTime:l}}catch{return null}}))).filter(c=>c!==null);return i.length===0?null:{session:i.reduce((c,l)=>l.messageTime>c.messageTime?l:c).session,knownRootSessionIds:t}}catch{return null}}async function B(){const e=O();if(!e)return null;const n=await C(e.sessionId);if(!n||!R(n,e)){u();const o=await w(e.knownRootSessionIds);return o?(d(o.session,o.knownRootSessionIds),o.session):null}const t=await w(e.knownRootSessionIds);return t?(d(t.session,t.knownRootSessionIds),t.session):n}async function H(e){const n=await w(e);return n?(d(n.session,n.knownRootSessionIds),n.session):null}async function W(e){if(!s?.connected)return a("kickoff: not connected"),!1;try{for(let n=0;n<30;n++){try{await m(),a(`kickoff: server ready (attempt ${n+1})`);break}catch{}await new Promise(t=>setTimeout(t,1e3))}for(let n=0;n<30;n++){const t=await J(e.nonce);if(t)return d(t.session,t.knownRootSessionIds),a(`kickoff: bound session ${t.session.id.slice(0,8)}\u2026`),!0;await new Promise(o=>setTimeout(o,1e3))}return a("kickoff: no session found"),!1}catch(n){return a(`kickoff error: ${n}`),!1}}async function X(e){if(!s?.connected)return!1;try{const n=await B();if(!n)return!1;const t={parts:[{type:"text",text:e}]};if(await $(n.id,t))return s.totalEntriesInjected++,!0;const o=[...s.knownRootSessionIds];u();const i=await H(o);return i?await $(i.id,t)?(s.totalEntriesInjected++,!0):(u(),!1):!1}catch(n){return a(`entry error: ${n}`),u(),!1}}async function Y(){if(!s?.connected)return null;const e=O();if(!e)return!1;try{const n=await C(e.sessionId);return n&&R(n,e)?!0:(u(),!1)}catch{return u(),!1}}function Z(){s=null}function ee(){return{connected:s?.connected??!1,sessionId:s?.sessionId??null,totalEntriesInjected:s?.totalEntriesInjected??0}}function ne(e,n=14096){let t=0;for(let o=0;o<e.length;o++){const i=e.charCodeAt(o);t=(t<<5)-t+i,t|=0}return n+Math.abs(t)%1024}function te(){s=null;for(const e of f)try{I(e)}catch{}f.clear()}export{te as __resetOpenCodeDroneForTests,ne as computeOpenCodePort,V as connectOpenCodeDrone,Q as createOpenCodeLaunchKickoff,Z as disconnectOpenCodeDrone,ee as getOpenCodeConnectionState,W as injectInitialKickoff,X as injectOpenCodeEntry,Y as probeOpenCodeDroneArmed};
|