borgmcp 1.1.4 → 1.1.6
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/README.md +5 -4
- package/dist/assimilate-cmd.js +18 -18
- package/dist/claude.js +3 -3
- package/dist/config-utils.d.ts +0 -7
- package/dist/config-utils.js +3 -4
- package/dist/opencode-drone.d.ts +6 -12
- package/dist/opencode-drone.js +2 -1
- package/package.json +1 -2
package/README.md
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
Multi-agent coordination for AI coding agents.
|
|
4
4
|
|
|
5
|
-
Borg MCP lets Claude Code and
|
|
5
|
+
Borg MCP lets Claude Code, Codex, and OpenCode sessions join the same project coordination space, take on named roles, and coordinate through one shared activity log. Borg MCP is an MCP server, so install Claude Code, Codex, or OpenCode first. That shared space is a **cube**. Each running agent session is a **drone**. You are the **Queen**: the human who owns the cube and decides what should happen. Drones operate under **roles** such as Builder, Code Reviewer, Coordinator, UX Expert, or Security Auditor.
|
|
6
6
|
|
|
7
7
|
## What you get
|
|
8
8
|
|
|
@@ -10,7 +10,7 @@ Borg MCP lets Claude Code and Codex sessions join the same project coordination
|
|
|
10
10
|
- Role-based coordination: assign drones to roles and keep their instructions in one place.
|
|
11
11
|
- Live activity log: post status, dispatches, review gates, and findings without copy-paste handoffs.
|
|
12
12
|
- Wake-path support: inbox monitoring and stream diagnostics help keep agent sessions responsive.
|
|
13
|
-
- Claude Code and
|
|
13
|
+
- Claude Code, Codex, and OpenCode launcher support: start one or more agent sessions from the same repo.
|
|
14
14
|
- Dashboard support: manage cubes, roles, drones, and logs at <https://borgmcp.ai/dashboard>.
|
|
15
15
|
|
|
16
16
|
## Pricing
|
|
@@ -46,13 +46,14 @@ borg setup
|
|
|
46
46
|
The wizard signs you in with Google, checks your subscription access, and registers Borg MCP with the supported agent CLIs installed on your machine.
|
|
47
47
|
|
|
48
48
|
`borg ...` commands are terminal commands. `borg_...` commands are MCP tools
|
|
49
|
-
you ask your agent to run inside Claude Code or
|
|
49
|
+
you ask your agent to run inside Claude Code, Codex, or OpenCode.
|
|
50
50
|
|
|
51
|
-
If
|
|
51
|
+
If multiple CLIs are installed, use `--cli` when launching or assimilating if you want to choose explicitly:
|
|
52
52
|
|
|
53
53
|
```bash
|
|
54
54
|
borg assimilate --cli claude
|
|
55
55
|
borg assimilate --cli codex
|
|
56
|
+
borg assimilate --cli opencode
|
|
56
57
|
```
|
|
57
58
|
|
|
58
59
|
## Join a cube
|
package/dist/assimilate-cmd.js
CHANGED
|
@@ -1,48 +1,48 @@
|
|
|
1
|
-
import{dirname as z,basename as R}from"node:path";import{randomUUID as ae}from"node:crypto";import{roleSlug as le,matchRoleByName as se,pickDefaultRole as ce}from"./role-resolver.js";import{deriveCubeName as ue,parseGitRemote as me,sanitizeRemoteUrl as de}from"./cube-name.js";import{validateName as J}from"./name-validator.js";import{renderAssimilationWelcome as fe}from"./assimilate-welcome.js";import{shellEscape as he}from"./shell-escape.js";import{withCodexCwdArg as ge}from"./codex-remote.js";import{buildAgentKickoffPrompt as we,buildKickoffWakePathClause as be,recordCodexWakeTarget as ke,socketPathFromRemoteArgs as ye}from"./codex-launch.js";import{perWorktreeBranchName as
|
|
1
|
+
import{dirname as z,basename as R}from"node:path";import{randomUUID as ae}from"node:crypto";import{roleSlug as le,matchRoleByName as se,pickDefaultRole as ce}from"./role-resolver.js";import{deriveCubeName as ue,parseGitRemote as me,sanitizeRemoteUrl as de}from"./cube-name.js";import{validateName as J}from"./name-validator.js";import{renderAssimilationWelcome as fe}from"./assimilate-welcome.js";import{shellEscape as he}from"./shell-escape.js";import{withCodexCwdArg as ge}from"./codex-remote.js";import{buildAgentKickoffPrompt as we,buildKickoffWakePathClause as be,recordCodexWakeTarget as ke,socketPathFromRemoteArgs as ye}from"./codex-launch.js";import{perWorktreeBranchName as B,adoptWorktree as pe,computeWorktreePath as V,localBranchExists as X,isMerged as ve}from"./worktree-lifecycle.js";import{DroneEvictedError as $e}from"./drone-lifecycle.js";import{codexBorgSessionConfigArgs as xe}from"./launch-gate.js";import{inboxPathForDrone as Se}from"./cubes.js";import{resolveLaunchEnv as Ce,resolveOllamaBaseUrl as Re,parseModel as _e}from"./model-presets.js";import{unlinkSync as Ee}from"node:fs";import{gcOrphanInboxesForCube as Ie,defaultListInboxLogs as Ne,defaultInboxLivenessDeps as Ae,isInboxLive as Pe,ORPHAN_INBOX_STALE_MS as Le}from"./gc-orphan-inboxes.js";import{installBorgPlugin as Te}from"./opencode-plugin.js";import{computeOpenCodePort as De,connectOpenCodeDrone as Ue,injectInitialKickoff as Oe}from"./opencode-drone.js";async function ct(r,e){if(r.role!==void 0){const t=J(r.role);if(!t.ok)return e.stderr(t.error+`
|
|
2
2
|
`),1}if(r.flags.worktree!==void 0){const t=J(r.flags.worktree);if(!t.ok)return e.stderr(t.error+`
|
|
3
3
|
`),1}let a=await e.getCachedAuth();if(!a){if(!e.isTTY()&&!r.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(r.flags.cubeName)i=r.flags.cubeName;else{const t=e.runSync("git",["remote","get-url","origin"],o),n=t.status===0?t.stdout:null;if(i=ue(o,n),n){const c=de(n),f=c?me(c):null;c&&!f&&i&&e.stderr(`couldn't parse git remote '${c}' \u2014 using directory name '${i}' as cube name
|
|
4
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.cwd();e.stderr(`Checking your cubes\u2026
|
|
5
|
-
`);let
|
|
6
|
-
`),a=await e.runSetup(),
|
|
7
|
-
`),1;let u,
|
|
5
|
+
`);let I;try{I=await e.listCubes(a.apiUrl,a.token)}catch(t){const n=t instanceof Error?t.message:String(t);if(n.includes("Authentication required")||n.includes("Authentication expired"))e.stderr(`Re-authenticating...
|
|
6
|
+
`),a=await e.runSetup(),I=await e.listCubes(a.apiUrl,a.token);else throw t}const N=I.find(t=>t.name===i);if(!N&&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,A;if(N)u=await e.getCube(a.apiUrl,a.token,N.id),A=!1;else{let t;if(r.flags.template)t=r.flags.template;else if(r.flags.noTemplate)t=void 0;else if(e.isTTY())if(r.flags.yes)t="starter";else{const n=await e.listTemplates(a.apiUrl,a.token),c=["First drone joining a new cube. Apply a template?"];n.forEach((v,$)=>{const E=$===0?" (default)":"";c.push(` ${$+1}) ${v.name}${E} \u2014 ${v.description}`)}),c.push(` ${n.length+1}) skip \u2014 no template`);const f=(await e.prompt(c.join(`
|
|
8
8
|
`)+`
|
|
9
9
|
[1]: `)).trim(),y=f===""?1:parseInt(f,10);if(Number.isNaN(y)||y<1||y>n.length+1)return e.stderr(`invalid choice "${f}"
|
|
10
10
|
`),1;t=y<=n.length?n[y-1].name:void 0}else{if(!r.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}),A=!0}let g;if(r.role!==void 0){if(g=se(u.roles,r.role),!g){const t=u.roles.map(f=>f.name).join(", "),n=Be(r.role,u.roles.map(f=>f.name)),c=n?` Did you mean "${n}"?`:"";return e.stderr(`no role matching "${r.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(n=>n.role_id));if(g=ce(u.roles,{isFirstDrone:
|
|
16
|
+
`),1}}else{const t=new Set((u.drones??[]).map(n=>n.role_id));if(g=ce(u.roles,{isFirstDrone:A,occupiedRoleIds:t}),!g)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
17
|
`),1}const x=await e.getActiveCube();let S;if(x&&r.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
|
|
18
|
+
`),1;const H=r.flags.worktree!==void 0||x!==null&&!r.flags.here,Z=S??x?.droneId??null,P=H?null:await e.getLaunchModel(u.id,o,Z),b=r.flags.model??P?.model??g.default_model??null,L=Re(process.env,b!=null&&b===P?.model?P?.ollamaBaseUrl:void 0);if(b){const t=await e.checkModelReachable(b,e.fetch,L);if(!t.ok)return e.stderr(`${t.message}
|
|
19
19
|
`),1}const m=await e.resolveCli(r.flags.cli);e.stderr(`Joining cube '${u.name}' as ${g.name}\u2026
|
|
20
20
|
`);let s;try{s=await e.assimilate(a.apiUrl,a.token,{cube_id:u.id,role_id:g.id,hostname:e.getHostname(),agent_kind:m,model:b,...S?{prior_drone_id:S}:{}})}catch(t){if(t instanceof $e&&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.
|
|
21
21
|
`),1;const n=t instanceof Error?t.message:String(t);return e.stderr(`assimilate failed: ${n}
|
|
22
22
|
`),1}const k=u.roles.find(t=>t.id===s.role_id)??g;s.reattached?e.stderr(`re-attached to existing seat ${s.drone_label} (session token rotated, no new drone minted)
|
|
23
23
|
`):k.id!==g.id&&e.stderr(`Note: your invite didn't grant the "${g.name}" role \u2014 assimilated as "${k.name}" instead.
|
|
24
|
-
`);const ee=
|
|
24
|
+
`);const ee=H;let w=null;if(ee){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
25
|
Fix: create at least one commit (\`git commit --allow-empty -m "initial"\`)
|
|
26
26
|
OR: pass --here to skip the sibling spawn and use the current directory
|
|
27
27
|
`),1;e.runSync("git",["fetch","origin"],o);let n="origin/main";e.runSync("git",["rev-parse","--verify","origin/main"],o).status!==0&&e.runSync("git",["rev-parse","--verify","origin/master"],o).status===0&&(n="origin/master");const f=t.stdout.trim(),y=e.runSync("git",["rev-parse",n],o).stdout.trim();f!==y&&e.stderr(`note: local HEAD (${f.slice(0,7)}) differs from ${n} (${y.slice(0,7)}); new worktree will start on ${n}
|
|
28
28
|
`);const v=R(o),$=r.flags.worktree??le(k.name);if($.length===0)return e.stderr(`cannot derive a worktree name from role "${k.name}"; pass an explicit --worktree <name>
|
|
29
|
-
`),1;const
|
|
29
|
+
`),1;const E=e.homedir();let h=V(E,v,$),p=B(R(h),v),G=2;for(;e.pathExists(h)||Me(e,o,h)||X(e.runSync,o,p)&&!ve(e.runSync,o,p,n);)h=V(E,v,$,G),p=B(R(h),v),G++;e.mkdirp(z(h));const q=X(e.runSync,o,p)?e.runSync("git",["worktree","add",h,p],o):e.runSync("git",["worktree","add","-b",p,h,n],o);if(q.status!==0)return e.stderr(`git worktree add failed: ${Q(q.stderr)}
|
|
30
30
|
`),1;e.stderr(`spawned sibling worktree at ${h} on branch ${p} (${n}); original dir is registered as active (edit ~/.config/borgmcp/cubes.json if stale).
|
|
31
|
-
`),e.chdir(h),e.stderr(
|
|
31
|
+
`),e.chdir(h),e.stderr(je(h,p,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 n=t instanceof Error?t.message:String(t);if(e.stderr(`setActiveCube failed: ${n}
|
|
32
32
|
`),w){const c=e.runSync("git",["worktree","remove","--force",w],o);c.status===0?e.stderr(`rolled back spawned worktree at ${w}
|
|
33
33
|
`):e.stderr(`manual cleanup needed: \`git worktree remove --force ${w}\` (rollback attempt failed: ${Q(c.stderr).trim()||"unknown"})
|
|
34
34
|
`)}return 1}try{const t=Ae(),n=z(Se(s.cube_id,s.drone_id));Ie({cubeInboxDir:n,selfDroneId:s.drone_id,deps:{listInboxLogs:Ne,isLive:c=>Pe(c,t),droneState:()=>"absent",unlink:c=>Ee(c),now:t.now,staleMs:Le}})}catch{}e.setTerminalTitle(s.drone_label,u.name);const te=e.isTTY()&&!process.env.NO_COLOR&&!process.env.CI;e.stdout(fe(k.name,u.name,te));const d=e.cwd();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
|
|
35
|
-
`)}if(!w){e.runSync("git",["fetch","origin","--prune"],d);const t=
|
|
36
|
-
`),e.stderr(
|
|
35
|
+
`)}if(!w){e.runSync("git",["fetch","origin","--prune"],d);const t=B(R(d),R(o)),n=pe(e.runSync,d,t,"origin/main");n.action==="adopted"?(e.stderr(`worktree: adopted branch ${t} at origin/main
|
|
36
|
+
`),e.stderr(We(d,t))):n.message&&e.stderr(`worktree sync: ${n.message}
|
|
37
37
|
`)}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 re=e.getInboxPath(s.cube_id,s.drone_id),
|
|
39
|
-
`),
|
|
38
|
+
`);const re=e.getInboxPath(s.cube_id,s.drone_id),T=m==="codex"?`borg-wake-${ae()}`:null,ne=be(m,m==="claude"?re:null);let D,F=[],C,U=null,O=null;const K=e.findProjectRoot(d);b?await e.setLaunchModel(s.cube_id,K,{model:b,ollamaBaseUrl:_e(b).kind==="ollama"?L:null}):await e.clearLaunchModel(s.cube_id,K);const Y=Ce(b,L),j={...process.env,...Y.set,BORG_SESSION:"1"};for(const t of Y.unset)delete j[t];if(m==="codex"){const t=await e.prepareCodexRemoteLaunch();t.warning?(e.stderr(`warning: ${t.warning}
|
|
39
|
+
`),D="\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."):D="Codex wake-path capability check passed: remote-control socket established for this session.",F=t.args,Object.keys(t.env).length>0&&Object.assign(j,t.env),U=ye(t.args),O=t.server?.cleanup??null}const W=we({cli:m,codexWakeNonce:T,monitorClause:ne,codexWakePathClause:D});let M;C=[W],m==="codex"?C=[...xe(),...F,...ge(C,d)]:m==="opencode"&&(M=De(s.drone_id),Te(),C=[d,"--port",String(M),"--auto","--prompt",W]);const oe=e.exec(m,C,d,j);if(m==="codex"&&U&&T&&ke({deps:e,cubeId:s.cube_id,droneId:s.drone_id,socketPath:U,cwd:d,previewNeedle:T,launchedAtSeconds:Math.floor(Date.now()/1e3)}),m==="opencode"){const t=`http://127.0.0.1:${M}`;Ue({serverUrl:t,directory:d,droneLabel:s.drone_label,cubeName:i??"borg"}).then(()=>Oe(W)).catch(()=>{})}const ie=await oe;if(O)try{O()}catch{}return w&&_!==w&&e.stderr(`
|
|
40
40
|
Agent exited. You were working in ${w}; your shell is back in ${_}.
|
|
41
41
|
To return:
|
|
42
42
|
cd ${he(w)}
|
|
43
|
-
`),ie}function
|
|
43
|
+
`),ie}function je(r,e,a){return`
|
|
44
44
|
WORKTREE STEERING: You are in worktree ${r} 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).
|
|
45
|
-
`}function
|
|
45
|
+
`}function We(r,e){return`
|
|
46
46
|
WORKTREE STEERING: This checkout is now on branch ${e}. Do ALL work HERE in ${r} \u2014 cut your feature branch (fix/.../feat/...) off ${e}, use relative paths / your cwd.
|
|
47
|
-
`}function Q(r){return r.replace(/[\x00-\x1F\x7F]/g,"")}function
|
|
48
|
-
`).some(i=>i===`worktree ${a}`)}function
|
|
47
|
+
`}function Q(r){return r.replace(/[\x00-\x1F\x7F]/g,"")}function Me(r,e,a){const o=r.runSync("git",["worktree","list","--porcelain"],e);return o.status!==0?!1:o.stdout.split(`
|
|
48
|
+
`).some(i=>i===`worktree ${a}`)}function Be(r,e){if(e.length===0)return null;const a=r.toLowerCase();let o=null;for(const i of e){const l=He(a,i.toLowerCase());l<=2&&(o===null||l<o.distance)&&(o={name:i,distance:l})}return o?o.name:null}function He(r,e){if(r===e)return 0;if(r.length===0)return e.length;if(e.length===0)return r.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<=r.length;i++){o[0]=i;for(let l=1;l<=e.length;l++){const _=r[i-1]===e[l-1]?0:1;o[l]=Math.min(o[l-1]+1,a[l]+1,a[l-1]+_)}for(let l=0;l<=e.length;l++)a[l]=o[l]}return a[e.length]}export{ct as runAssimilate,Q as safeStderr,Be as suggestRoleName};
|
package/dist/claude.js
CHANGED
|
@@ -1,15 +1,15 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import{spawn as F}from"child_process";import{randomUUID as _}from"node:crypto";import{basename as W}from"node:path";import{createInterface as G}from"node:readline/promises";import i from"chalk";import{findProjectRoot as I,getActiveCube as U,getLaunchModel as Y,inboxPathForDrone as K,setCodexWakeTarget as V,pruneDeadCodexWakeTargets as j}from"./cubes.js";import{applyOllamaLaunchEnv as q,checkModelReachable as X}from"./model-presets.js";import{handleVersionFlag as z,getPackageVersion as w}from"./version.js";import{isHelpFlag as L,setupHelpText as J,topLevelHelpText as Q,assimilateHelpText as Z}from"./cli-help.js";import{runSpawn as ee}from"./spawn.js";import{buildClaudeLaunchArgs as re}from"./claude-launch-args.js";import{parseSyncArgs as oe,runSync as se}from"./sync.js";import{parseCleanupArgs as te,runCleanup as ie}from"./cleanup-cmd.js";import{parseAssimilateArgs as ae}from"./parse-assimilate-args.js";import{runAssimilate as ne}from"./assimilate-cmd.js";import{buildDefaultAssimilateDeps as ce}from"./assimilate-deps.js";import{parseLaunchAllArgs as R}from"./parse-launch-all-args.js";import{unknownSubcommand as le}from"./unknown-subcommand.js";import{runLaunchAll as O}from"./launch-all-cmd.js";import{buildDefaultLaunchAllDeps as b}from"./launch-all-deps.js";import{discoverDroneCandidates as de}from"./launch-all-discovery.js";import{explicitCliLaunchHint as pe,runBareLaunchMenu as ue,shouldResolveExplicitCliLaunchHintTargets as me,shouldShowLaunchMenu as fe}from"./bare-launch-menu.js";import{setTerminalTitle as ge}from"./terminal-title.js";import{initConsolePrefix as he,consolePrefix as t}from"./console-prefix.js";import{initDebugFromArgv as we}from"./debug.js";import{fetchLatestBorgmcpVersion as Ce,compareVersionsForStaleness as xe}from"./stale-version-check.js";import{defaultCliChoiceDeps as ve,detectCliAvailability as k,installedCliNames as D,parseCliFlag as be,resolveCliChoice as ke}from"./cli-platform.js";import{getRefreshToken as $e,getIdToken as Se}from"./config.js";import{composeGetStarted as ye,shouldShowGetStarted as Ae}from"./get-started.js";import{prepareCodexRemoteLaunch as Te,withCodexCwdArg as Pe,defaultCodexRemoteDeps as Ie,checkCodexBridgeHealthy as Le}from"./codex-remote.js";import{findLoadedCodexThread as Re}from"./codex-app-server.js";import{buildAgentKickoffPrompt as Oe,buildKickoffWakePathClause as De,recordCodexWakeTarget as
|
|
2
|
+
import{spawn as F}from"child_process";import{randomUUID as _}from"node:crypto";import{basename as W}from"node:path";import{createInterface as G}from"node:readline/promises";import i from"chalk";import{findProjectRoot as I,getActiveCube as U,getLaunchModel as Y,inboxPathForDrone as K,setCodexWakeTarget as V,pruneDeadCodexWakeTargets as j}from"./cubes.js";import{applyOllamaLaunchEnv as q,checkModelReachable as X}from"./model-presets.js";import{handleVersionFlag as z,getPackageVersion as w}from"./version.js";import{isHelpFlag as L,setupHelpText as J,topLevelHelpText as Q,assimilateHelpText as Z}from"./cli-help.js";import{runSpawn as ee}from"./spawn.js";import{buildClaudeLaunchArgs as re}from"./claude-launch-args.js";import{parseSyncArgs as oe,runSync as se}from"./sync.js";import{parseCleanupArgs as te,runCleanup as ie}from"./cleanup-cmd.js";import{parseAssimilateArgs as ae}from"./parse-assimilate-args.js";import{runAssimilate as ne}from"./assimilate-cmd.js";import{buildDefaultAssimilateDeps as ce}from"./assimilate-deps.js";import{parseLaunchAllArgs as R}from"./parse-launch-all-args.js";import{unknownSubcommand as le}from"./unknown-subcommand.js";import{runLaunchAll as O}from"./launch-all-cmd.js";import{buildDefaultLaunchAllDeps as b}from"./launch-all-deps.js";import{discoverDroneCandidates as de}from"./launch-all-discovery.js";import{explicitCliLaunchHint as pe,runBareLaunchMenu as ue,shouldResolveExplicitCliLaunchHintTargets as me,shouldShowLaunchMenu as fe}from"./bare-launch-menu.js";import{setTerminalTitle as ge}from"./terminal-title.js";import{initConsolePrefix as he,consolePrefix as t}from"./console-prefix.js";import{initDebugFromArgv as we}from"./debug.js";import{fetchLatestBorgmcpVersion as Ce,compareVersionsForStaleness as xe}from"./stale-version-check.js";import{defaultCliChoiceDeps as ve,detectCliAvailability as k,installedCliNames as D,parseCliFlag as be,resolveCliChoice as ke}from"./cli-platform.js";import{getRefreshToken as $e,getIdToken as Se}from"./config.js";import{composeGetStarted as ye,shouldShowGetStarted as Ae}from"./get-started.js";import{prepareCodexRemoteLaunch as Te,withCodexCwdArg as Pe,defaultCodexRemoteDeps as Ie,checkCodexBridgeHealthy as Le}from"./codex-remote.js";import{findLoadedCodexThread as Re}from"./codex-app-server.js";import{buildAgentKickoffPrompt as Oe,buildKickoffWakePathClause as De,recordCodexWakeTarget as Me,socketPathFromRemoteArgs as M}from"./codex-launch.js";import{codexBorgSessionConfigArgs as Ee}from"./launch-gate.js";import{addCodexMcpServer as He,addCodexSessionStartHook as Ne,addCodexUserPromptSubmitHook as Be,addMcpServer as Fe,addOpenCodeMcpServer as _e,addProjectSessionStartHook as We,addUserPromptSubmitHook as Ge,isCodexMcpServerConfigured as Ue,isMcpServerConfigured as Ye,isOpenCodeMcpServerConfigured as Ke,removeSessionStartHook as Ve}from"./config-utils.js";import{installBorgPlugin as je}from"./opencode-plugin.js";import{connectOpenCodeDrone as qe,computeOpenCodePort as E,injectInitialKickoff as Xe}from"./opencode-drone.js";async function ze(){we(process.argv),z(),await he();const c=(async()=>{if(!process.stderr.isTTY)return;const e=w(),r=await Ce();if(!r)return;const a=xe(e,r);a.stale&&a.message&&process.stderr.write(`${t()}${a.message}
|
|
3
3
|
`)})();if((process.argv[2]==="--help"||process.argv[2]==="-h")&&(process.stdout.write(Q(w())),process.exit(0)),process.argv[2]==="setup"){L(process.argv[3])&&(process.stdout.write(J(w())),process.exit(0)),await import("./setup.js");return}if(process.argv[2]==="assimilate"){process.argv.slice(3).some(L)&&(process.stdout.write(Z(w())),process.exit(0));const e=ae(process.argv.slice(3));e.ok||(process.stderr.write(i.red(`${t()}\u25FC borg assimilate: ${e.error}
|
|
4
4
|
`)),process.stderr.write("Run `borg --help` for usage.\n"),process.exit(1));const r=ce(),a=await ne({role:e.role,flags:e.flags},r);process.exit(a)}if(process.argv[2]==="spawn"){const e=await ee();process.exit(e)}if(process.argv[2]==="sync"){const e=oe(process.argv.slice(3));e.ok||(process.stderr.write(i.red(`${t()}\u25FC borg sync: ${e.error}
|
|
5
5
|
`)),process.stderr.write("Run `borg --help` for usage.\n"),process.exit(1));const r=await se({},e.options);process.exit(r)}if(process.argv[2]==="cleanup"){const e=te(process.argv.slice(3));e.ok||(process.stderr.write(i.red(`${t()}\u25FC borg cleanup: ${e.error}
|
|
6
6
|
`)),process.stderr.write("Run `borg --help` for usage.\n"),process.exit(1));const r=await ie({},e.options);process.exit(r)}if(process.argv[2]==="launch-all"){const e=R(process.argv.slice(3));e.ok||(process.stderr.write(i.red(`${t()}\u25FC borg launch-all: ${e.error}
|
|
7
7
|
`)),process.stderr.write("Run `borg --help` for usage.\n"),process.exit(1));const r=b(),a=await O(e.args,r);process.exit(a)}const n=le(process.argv[2]);if(n!==null&&(process.stderr.write(i.red(`${t()}\u25FC unknown command: ${n}
|
|
8
8
|
`)),process.stderr.write("Run `borg --help` for usage.\n"),process.exit(1)),Ae(await $e()!==null,await Se()!==null)){const e=D(k()).length>0;process.stdout.write(ye(e)),process.exit(0)}const d=be(process.argv.slice(2));d.error&&(process.stderr.write(i.red(`${t()}\u25FC ${d.error}
|
|
9
|
-
`)),process.stderr.write("Run `borg --help` for usage.\n"),process.exit(1));const $=async e=>{const r=G({input:process.stdin,output:process.stdout});try{return await r.question(e)}finally{r.close()}};let s=await ke(d.cli,ve($,()=>process.stdin.isTTY===!0));
|
|
9
|
+
`)),process.stderr.write("Run `borg --help` for usage.\n"),process.exit(1));const $=async e=>{const r=G({input:process.stdin,output:process.stdout});try{return await r.question(e)}finally{r.close()}};let s=await ke(d.cli,ve($,()=>process.stdin.isTTY===!0));Je();const o=await U();let C;const S=async()=>o?(C===void 0&&(C=(await de({targetCubeId:o.cubeId},b())).length>0),C):!1,x=process.stdin.isTTY===!0,v=process.stdout.isTTY===!0,y=pe({explicitCli:d.cli,stdinIsTTY:x,stdoutIsTTY:v,hasActiveCube:o!==null,hasLaunchAllTargets:me({explicitCli:d.cli,stdinIsTTY:x,stdoutIsTTY:v,hasActiveCube:o!==null})?await S():!1});if(y&&process.stderr.write(y),fe({extraArgs:process.argv.slice(2),stdinIsTTY:x,stdoutIsTTY:v})){const e=D(k()).filter(a=>a!==s),r=await ue({defaultCli:s,otherInstalledClis:e,hasLaunchAllTargets:await S()},$);if(r.kind==="launch-all"){const a=R([]),B=a.ok?await O(a.args,b()):1;process.exit(B)}s=r.cli}const l=d.rest;ge(o?{label:o.droneLabel,cubeName:o.name}:null,W(process.cwd()));const H=De(s,o&&s==="claude"?K(o.cubeId,o.droneId):null);await Promise.race([c,new Promise(e=>setTimeout(e,2e3))]);const A=s==="codex"?`borg-wake-${_()}`:null;let g,T=[],p={...process.env,BORG_SESSION:"1"},u=null,m=null;if(s==="codex"&&!l.includes("--remote")){console.error(`${t()}${i.gray("\u25FC Starting Codex remote-wake app-server\u2026")}`);const e=await Te(Ie());e.warning?(console.error(`${t()}${i.yellow(`warning: ${e.warning}`)}`),g="\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."):g="Codex wake-path capability check passed: remote-control socket established for this session.",T=e.args,p={...process.env,...e.env,BORG_SESSION:"1"},u=M(e.args),m=e.server?.cleanup??null}else s==="codex"&&l.includes("--remote")&&(g="Codex wake-path capability check: using caller-provided --remote socket; if no wake arrives, run borg_regen manually when returning to the session.",u=M(l),u&&(p={...process.env,BORG_CODEX_REMOTE_WAKE:"1",BORG_SESSION:"1"}));if(o){const e=await Y(o.cubeId,I(),o.droneId),r=q(p,e,process.env);if(p=r.env,r.probe){const a=await X(r.probe.descriptor,fetch,r.probe.baseUrl);a.ok||console.error(`${t()}${i.yellow(`warning: ${a.message}`)}`)}}const f=Oe({cli:s,codexWakeNonce:A,monitorClause:H,codexWakePathClause:g});let h;if(s==="codex")h=[...Ee(),...T,...Pe([...l,f],process.cwd())];else if(s==="opencode"){const e=o?E(o.droneId):14096;je(),h=[process.cwd(),"--port",String(e),"--auto","--prompt",f,...l]}else h=re(l,f);const N=s==="claude"?"Claude Code":s==="codex"?"Codex":"OpenCode";console.error(`${t()}${i.blue(`\u25FC Launching ${N}\u2026`)}`);const P=F(s,h,{stdio:"inherit",shell:!1,env:p});if(s==="opencode"){const r=`http://127.0.0.1:${o?E(o.droneId):14096}`;qe({serverUrl:r,directory:process.cwd(),droneLabel:o?.droneLabel??"opencode",cubeName:o?.name??"borg"}).then(()=>Xe(f)).catch(()=>{})}s==="codex"&&o&&u&&(Me({deps:{setCodexWakeTarget:V,findLoadedCodexThread:Re},cubeId:o.cubeId,droneId:o.droneId,socketPath:u,passthroughArgs:l,previewNeedle:A??f.slice(0,120),cwd:process.cwd(),launchedAtSeconds:Math.floor(Date.now()/1e3)}),j(e=>Le(e))),P.on("error",e=>{if(m)try{m()}catch{}if(e.code==="ENOENT"){console.error(`${t()}${i.red(`
|
|
10
10
|
\u25FC Failed to launch ${s}`)}`);const r=s==="opencode"?"opencode":s;console.error(`${t()}${i.gray(`Make sure ${r} is installed.
|
|
11
11
|
`)}`)}else console.error(`${t()}${i.red(`
|
|
12
12
|
\u25FC Failed to launch ${s}: ${e.message}
|
|
13
|
-
`)}`);process.exit(1)}),P.on("exit",e=>{if(m)try{m()}catch{}process.exit(e??0)})}function
|
|
13
|
+
`)}`);process.exit(1)}),P.on("exit",e=>{if(m)try{m()}catch{}process.exit(e??0)})}function Je(){const c=k();if(c.claude)try{Ye()||Fe(),We(I(process.cwd())),Ve(),Ge()}catch(n){console.error(`${t()}${i.yellow(`warning: Claude Code integration check failed: ${n?.message??n}`)}`)}if(c.codex)try{Ue()||He(),Ne(),Be()}catch(n){console.error(`${t()}${i.yellow(`warning: Codex integration check failed: ${n?.message??n}`)}`)}if(c.opencode)try{Ke()||_e()}catch(n){console.error(`${t()}${i.yellow(`warning: OpenCode integration check failed: ${n?.message??n}`)}`)}}ze().catch(c=>{console.error(`${t()}${i.red(`
|
|
14
14
|
\u25FC Error: ${c.message}
|
|
15
15
|
`)}`),process.exit(1)});
|
package/dist/config-utils.d.ts
CHANGED
|
@@ -125,11 +125,4 @@ export declare function isOpenCodeMcpServerConfigured(configPath?: string): bool
|
|
|
125
125
|
* only see pinned env, not parent process env).
|
|
126
126
|
*/
|
|
127
127
|
export declare function addOpenCodeMcpServer(): void;
|
|
128
|
-
/**
|
|
129
|
-
* Ensure the opencode MCP server config includes OPENCODE_SERVER_URL env var
|
|
130
|
-
* pointing at the local opencode HTTP server. Idempotent: updates in-place.
|
|
131
|
-
* Best-effort (failure is non-fatal — the MCP child discovers the server via
|
|
132
|
-
* port scan as a fallback).
|
|
133
|
-
*/
|
|
134
|
-
export declare function ensureOpenCodeServerEnvConfig(port: number): void;
|
|
135
128
|
//# sourceMappingURL=config-utils.d.ts.map
|
package/dist/config-utils.js
CHANGED
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
import{execSync as d}from"child_process";import n from"fs";import f from"os";import i from"path";import{fileURLToPath as
|
|
2
|
-
`,"utf-8")}function
|
|
3
|
-
`,"utf-8")}function $(){return
|
|
4
|
-
`,"utf-8")}catch{}}export{Z as addCodexMcpServer,ee as addCodexSessionStartHook,re as addCodexUserPromptSubmitHook,Y as addMcpServer,ne as addOpenCodeMcpServer,D as addProjectSessionStartHook,$ as addSessionStartHook,W as addUserPromptSubmitHook,ie as ensureOpenCodeServerEnvConfig,z as getBinaryPath,b as isCodexHookRegistered,q as isCodexMcpServerConfigured,oe as isCodexSessionStartHookRegistered,te as isCodexUserPromptSubmitHookRegistered,Q as isMcpServerConfigured,se as isOpenCodeMcpServerConfigured,J as isProjectSessionStartHookRegistered,T as isSessionStartHookRegistered,K as isUserPromptSubmitHookRegistered,X as removeSessionStartHook,V as removeUserPromptSubmitHook};
|
|
1
|
+
import{execSync as d}from"child_process";import n from"fs";import f from"os";import i from"path";import{fileURLToPath as b}from"url";import{dirname as R}from"path";const v=b(import.meta.url),E=R(v),a="borg-regen",m="borg-log-audit",U=i.join(f.homedir(),".claude.json"),H=i.join(f.homedir(),".codex","config.toml"),u=i.join(f.homedir(),".codex","hooks.json"),j=i.join(f.homedir(),".config","opencode","opencode.json"),M="borg";function h(){return i.join(f.homedir(),".claude","settings.json")}function p(){const e=h();if(!n.existsSync(e))return{};const r=n.readFileSync(e,"utf-8");return r.trim()?JSON.parse(r):{}}function y(e){const r=h();n.mkdirSync(i.dirname(r),{recursive:!0}),n.writeFileSync(r,JSON.stringify(e,null,2)+`
|
|
2
|
+
`,"utf-8")}function S(e){if(!n.existsSync(e))return{};const r=n.readFileSync(e,"utf-8");return r.trim()?JSON.parse(r):{}}function A(e,r){n.mkdirSync(i.dirname(e),{recursive:!0}),n.writeFileSync(e,JSON.stringify(r,null,2)+`
|
|
3
|
+
`,"utf-8")}function $(){return O(h())}function D(e){return O(x(e))}function T(e){return w(x(e))}function x(e){return i.join(e,".claude","settings.local.json")}function O(e){let r;try{r=S(e)}catch(t){return console.error(`\u26A0 Could not parse ${e}: ${t.message}. Skipping hook registration; you can add it manually.`),!1}return r.hooks??={},r.hooks.SessionStart??=[],r.hooks.SessionStart.some(t=>Array.isArray(t?.hooks)&&t.hooks.some(s=>s?.type==="command"&&s?.command===a))?!1:(r.hooks.SessionStart.push({matcher:"*",hooks:[{type:"command",command:a}]}),A(e,r),!0)}function w(e){let r;try{r=S(e)}catch{return!1}const o=r?.hooks?.SessionStart;return Array.isArray(o)?o.some(t=>Array.isArray(t?.hooks)&&t.hooks.some(s=>s?.type==="command"&&s?.command===a)):!1}function J(){let e;try{e=p()}catch{return!1}const r=e?.hooks?.SessionStart;return Array.isArray(r)?r.some(o=>Array.isArray(o?.hooks)&&o.hooks.some(t=>t?.type==="command"&&t?.command===a)):!1}function K(){let e;try{e=p()}catch{return!1}const r=e?.hooks?.UserPromptSubmit;return Array.isArray(r)?r.some(o=>Array.isArray(o?.hooks)&&o.hooks.some(t=>t?.type==="command"&&t?.command===m)):!1}function X(){let e;try{e=p()}catch{return!1}if(!e?.hooks?.SessionStart)return!1;let r=!1;return e.hooks.SessionStart=e.hooks.SessionStart.map(o=>{if(!Array.isArray(o?.hooks))return o;const t=o.hooks.filter(s=>!(s?.type==="command"&&s?.command===a));return t.length!==o.hooks.length?(r=!0,{...o,hooks:t}):o}).filter(o=>Array.isArray(o?.hooks)&&o.hooks.length>0),e.hooks.SessionStart.length===0&&delete e.hooks.SessionStart,Object.keys(e.hooks).length===0&&delete e.hooks,r&&y(e),r}function W(){let e;try{e=p()}catch(o){return console.error(`\u26A0 Could not parse ${h()}: ${o.message}. Skipping audit hook registration.`),!1}return e.hooks??={},e.hooks.UserPromptSubmit??=[],e.hooks.UserPromptSubmit.some(o=>Array.isArray(o?.hooks)&&o.hooks.some(t=>t?.type==="command"&&t?.command===m))?!1:(e.hooks.UserPromptSubmit.push({matcher:"*",hooks:[{type:"command",command:m}]}),y(e),!0)}function Q(){let e;try{e=p()}catch{return!1}if(!e?.hooks?.UserPromptSubmit)return!1;let r=!1;return e.hooks.UserPromptSubmit=e.hooks.UserPromptSubmit.map(o=>{if(!Array.isArray(o?.hooks))return o;const t=o.hooks.filter(s=>!(s?.type==="command"&&s?.command===m));return t.length!==o.hooks.length?(r=!0,{...o,hooks:t}):o}).filter(o=>Array.isArray(o?.hooks)&&o.hooks.length>0),e.hooks.UserPromptSubmit.length===0&&delete e.hooks.UserPromptSubmit,Object.keys(e.hooks).length===0&&delete e.hooks,r&&y(e),r}function V(e=U){try{if(!n.existsSync(e))return!1;const r=n.readFileSync(e,"utf-8");if(!r.trim())return!1;const o=JSON.parse(r);if(!o||typeof o!="object")return!1;const t=o.mcpServers;return!t||typeof t!="object"||Array.isArray(t)?!1:M in t}catch{return!1}}function q(e=H){try{if(!n.existsSync(e))return!1;const r=n.readFileSync(e,"utf-8");return/^\s*\[mcp_servers\.borg\]\s*$/m.test(r)&&/^\s*BORG_CODEX_REMOTE_WAKE\s*=\s*"1"\s*$/m.test(r)}catch{return!1}}function z(){return i.join(E,"index.js")}function Y(){try{try{d("claude mcp remove --scope user borg",{stdio:"ignore"})}catch{}d("claude mcp add --scope user borg borg-mcp",{stdio:"inherit",env:{...process.env,BORG_API_URL:process.env.BORG_API_URL||"https://api.borgmcp.ai"}})}catch(e){throw e.message?.includes("command not found")?new Error("Claude CLI not found. Please install Claude Code first."):new Error(`Failed to add MCP server: ${e.message}`)}}function Z(){try{try{d("codex mcp remove borg",{stdio:"ignore"})}catch{}d("codex mcp add borg --env BORG_API_URL="+P(process.env.BORG_API_URL||"https://api.borgmcp.ai")+" --env BORG_CODEX_REMOTE_WAKE=1 -- borg-mcp",{stdio:"inherit",env:{...process.env,BORG_API_URL:process.env.BORG_API_URL||"https://api.borgmcp.ai",BORG_CODEX_REMOTE_WAKE:"1"}})}catch(e){throw e.message?.includes("command not found")?new Error("Codex CLI not found. Please install Codex first."):new Error(`Failed to add MCP server to Codex: ${e.message}`)}}function P(e){return`'${e.replace(/'/g,"'\\''")}'`}function _(e,r,o={}){let t;try{t=S(u)}catch(l){return console.error(`\u26A0 Could not parse ${u}: ${l.message}. Skipping Codex hook registration.`),!1}t.hooks??={},t.hooks[e]??=[];const s=t.hooks[e];if(!Array.isArray(s)||s.some(l=>Array.isArray(l?.hooks)&&l.hooks.some(g=>g?.type==="command"&&g?.command===r)))return!1;const c={hooks:[{type:"command",command:r}]};return o.matcher&&(c.matcher=o.matcher),typeof o.timeout=="number"&&(c.hooks[0].timeout=o.timeout),s.push(c),A(u,t),!0}function ee(){return _("SessionStart",a,{matcher:"startup|resume",timeout:30})}function re(){return _("UserPromptSubmit",m,{timeout:10})}function C(e,r,o=u){try{const s=S(o)?.hooks?.[e];return Array.isArray(s)?s.some(k=>Array.isArray(k?.hooks)&&k.hooks.some(c=>c?.type==="command"&&c?.command===r)):!1}catch{return!1}}function oe(e=u){return C("SessionStart",a,e)}function te(e=u){return C("UserPromptSubmit",m,e)}function se(e=j){try{if(!n.existsSync(e))return!1;const r=n.readFileSync(e,"utf-8");if(!r.trim())return!1;const o=JSON.parse(r);if(!o||typeof o!="object")return!1;const t=o.mcp?.borg;return!t||typeof t!="object"?!1:t.type==="local"}catch{return!1}}function ne(){try{const e=process.env.BORG_API_URL||"https://api.borgmcp.ai";d(`opencode mcp add borg --env BORG_SESSION=1 --env BORG_OPENCODE=1 --env BORG_API_URL=${P(e)} -- borg-mcp`,{stdio:"inherit"})}catch(e){throw e.message?.includes("command not found")?new Error("opencode CLI not found. Please install opencode first."):new Error(`Failed to add MCP server to opencode: ${e.message}`)}}export{Z as addCodexMcpServer,ee as addCodexSessionStartHook,re as addCodexUserPromptSubmitHook,Y as addMcpServer,ne as addOpenCodeMcpServer,D as addProjectSessionStartHook,$ as addSessionStartHook,W as addUserPromptSubmitHook,z as getBinaryPath,C as isCodexHookRegistered,q as isCodexMcpServerConfigured,oe as isCodexSessionStartHookRegistered,te as isCodexUserPromptSubmitHookRegistered,V as isMcpServerConfigured,se as isOpenCodeMcpServerConfigured,T as isProjectSessionStartHookRegistered,J as isSessionStartHookRegistered,K as isUserPromptSubmitHookRegistered,X as removeSessionStartHook,Q as removeUserPromptSubmitHook};
|
package/dist/opencode-drone.d.ts
CHANGED
|
@@ -4,24 +4,18 @@ interface ConnectDeps {
|
|
|
4
4
|
droneLabel: string;
|
|
5
5
|
cubeName: string;
|
|
6
6
|
}
|
|
7
|
-
/**
|
|
8
|
-
* Connect to the opencode TUI HTTP server. Does NOT create a new session —
|
|
9
|
-
* instead it finds the TUI's auto-created session on first inject.
|
|
10
|
-
*/
|
|
11
7
|
export declare function connectOpenCodeDrone(deps: ConnectDeps): Promise<void>;
|
|
12
8
|
/**
|
|
13
|
-
*
|
|
14
|
-
*
|
|
15
|
-
*
|
|
16
|
-
* session are available. Best-effort — never throws.
|
|
9
|
+
* Wait for the opencode HTTP server to be ready, then find the session
|
|
10
|
+
* created by the `--prompt` CLI arg. The kickoff was already submitted at
|
|
11
|
+
* process startup — this just discovers the resulting session.
|
|
17
12
|
*/
|
|
18
|
-
export declare function injectInitialKickoff(
|
|
13
|
+
export declare function injectInitialKickoff(_kickoffText: string): Promise<boolean>;
|
|
19
14
|
/**
|
|
20
|
-
* Inject
|
|
21
|
-
* Falls through silently
|
|
15
|
+
* Inject a silent context entry (noReply) into our session.
|
|
16
|
+
* Falls through silently — caller falls back to inbox write.
|
|
22
17
|
*/
|
|
23
18
|
export declare function injectOpenCodeEntry(text: string): Promise<boolean>;
|
|
24
|
-
/** Check if the TUI server + session are healthy. */
|
|
25
19
|
export declare function probeOpenCodeDroneArmed(): Promise<boolean | null>;
|
|
26
20
|
export declare function disconnectOpenCodeDrone(): void;
|
|
27
21
|
export declare function getOpenCodeConnectionState(): {
|
package/dist/opencode-drone.js
CHANGED
|
@@ -1 +1,2 @@
|
|
|
1
|
-
import{
|
|
1
|
+
import{appendFileSync as p}from"fs";import{join as m}from"path";import{tmpdir as w}from"os";const h=m(w(),"borg-opencode-drone.log");function c(e){const t=`[${new Date().toISOString()}] ${e}
|
|
2
|
+
`;try{p(h,t)}catch{}}let r=null;async function O(e){r={serverUrl:e.serverUrl,sessionId:null,directory:e.directory,connected:!0,totalEntriesInjected:0},c(`connected url=${e.serverUrl} dir=${e.directory}`)}function a(e){return`${r.serverUrl.replace(/\/+$/,"")}${e}${e.includes("?")?"&":"?"}directory=${encodeURIComponent(r.directory)}`}const l=1e4;async function u(e){const t=a(e),n=new AbortController,o=setTimeout(()=>n.abort(),l);try{const s=await fetch(t,{signal:n.signal}),i=await s.text();return{status:s.status,body:i}}finally{clearTimeout(o)}}async function I(e,t){const n=a(e),o=new AbortController,s=setTimeout(()=>o.abort(),l);try{const i=await fetch(n,{method:"POST",headers:{"Content-Type":"application/json"},signal:o.signal,body:JSON.stringify(t)}),y=await i.text();return{status:i.status,body:y}}finally{clearTimeout(s)}}async function f(){const{body:e}=await u("/session");return JSON.parse(e)}async function b(e){try{const{body:t}=await u(`/session/${e}`);return JSON.parse(t)}catch{return null}}async function k(e,t){const{status:n}=await I(`/session/${e}/prompt_async`,t);return n===200||n===204}async function d(){try{const t=(await f()).filter(o=>o.directory===r.directory);return t.length===0?null:t.reduce((o,s)=>o.time.created>s.time.created?o:s).id}catch{return null}}async function S(e){if(!r?.connected)return c("kickoff: not connected"),!1;try{for(let t=0;t<30;t++){try{await f(),c(`kickoff: server ready (attempt ${t+1})`);break}catch{}await new Promise(n=>setTimeout(n,1e3))}for(let t=0;t<30;t++){const n=await d();if(n)return r.sessionId=n,c(`kickoff: found session ${n.slice(0,8)}\u2026`),!0;await new Promise(o=>setTimeout(o,1e3))}return c("kickoff: no session found"),!1}catch(t){return c(`kickoff error: ${t}`),!1}}async function T(e){if(!r?.connected)return!1;try{if(!r.sessionId){const n=await d();if(!n)return!1;r.sessionId=n}const t=await k(r.sessionId,{parts:[{type:"text",text:e}]});return t&&r.totalEntriesInjected++,t}catch(t){return c(`entry error: ${t}`),!1}}async function x(){if(!r?.connected)return null;if(!r.sessionId)return!1;try{return await b(r.sessionId)!==null}catch{return!1}}function j(){r=null}function E(){return{connected:r?.connected??!1,sessionId:r?.sessionId??null,totalEntriesInjected:r?.totalEntriesInjected??0}}function U(e,t=14096){let n=0;for(let o=0;o<e.length;o++){const s=e.charCodeAt(o);n=(n<<5)-n+s,n|=0}return t+Math.abs(n)%1024}export{U as computeOpenCodePort,O as connectOpenCodeDrone,j as disconnectOpenCodeDrone,E as getOpenCodeConnectionState,S as injectInitialKickoff,T as injectOpenCodeEntry,x as probeOpenCodeDroneArmed};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "borgmcp",
|
|
3
|
-
"version": "1.1.
|
|
3
|
+
"version": "1.1.6",
|
|
4
4
|
"description": "Coordinate AI coding agents in shared cubes. Works with Claude Code and Codex. Create projects, assign roles, and share a live activity log.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "dist/index.js",
|
|
@@ -53,7 +53,6 @@
|
|
|
53
53
|
"dependencies": {
|
|
54
54
|
"@modelcontextprotocol/sdk": "^1.0.4",
|
|
55
55
|
"@napi-rs/keyring": "^1.3.0",
|
|
56
|
-
"@opencode-ai/sdk": "^1.17.13",
|
|
57
56
|
"chalk": "^5.3.0",
|
|
58
57
|
"open": "^10.0.0",
|
|
59
58
|
"prompts": "^2.4.2",
|