borgmcp 1.1.12 → 1.1.14
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.d.ts +2 -18
- package/dist/assimilate-cmd.js +40 -41
- package/dist/assimilate-deps.js +3 -3
- package/dist/claude.js +11 -11
- package/dist/clear-rewake-core.d.ts +12 -0
- package/dist/clear-rewake-core.js +2 -0
- package/dist/clear-rewake.d.ts +3 -0
- package/dist/clear-rewake.js +2 -0
- package/dist/cli-help.d.ts +1 -2
- package/dist/cli-help.js +6 -14
- package/dist/codex-app-wake.d.ts +4 -3
- package/dist/codex-remote.d.ts +12 -3
- package/dist/codex-remote.js +1 -1
- package/dist/config-utils.js +3 -3
- package/dist/cubes.d.ts +0 -17
- package/dist/cubes.js +4 -5
- package/dist/index.js +15 -15
- package/dist/launch-gate.d.ts +3 -2
- package/dist/model-presets.d.ts +5 -51
- package/dist/model-presets.js +1 -1
- package/dist/parse-assimilate-args.js +1 -1
- package/dist/regen-format.d.ts +9 -8
- package/dist/regen-format.js +13 -12
- package/dist/remote-client.d.ts +8 -0
- package/dist/remote-client.js +1 -1
- package/dist/role-resolver.d.ts +13 -4
- package/dist/role-resolver.js +1 -1
- package/dist/templates.d.ts +9 -8
- package/dist/templates.js +141 -189
- package/dist/tool-manifest.d.ts +3 -0
- package/dist/tool-manifest.js +1 -1
- package/dist/tool-scope.d.ts +1 -1
- package/dist/tool-scope.js +1 -1
- package/package.json +5 -4
package/dist/assimilate-cmd.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { Role } from './role-resolver.js';
|
|
1
|
+
import type { Role, RoleOccupant } from './role-resolver.js';
|
|
2
2
|
import { type CodexRemoteLaunch } from './codex-remote.js';
|
|
3
3
|
import type { BorgCli } from './cubes.js';
|
|
4
4
|
export interface AssimilateFlags {
|
|
@@ -23,9 +23,7 @@ export interface CubeDetail {
|
|
|
23
23
|
id: string;
|
|
24
24
|
name: string;
|
|
25
25
|
roles: Role[];
|
|
26
|
-
drones?:
|
|
27
|
-
role_id: string;
|
|
28
|
-
}>;
|
|
26
|
+
drones?: RoleOccupant[];
|
|
29
27
|
}
|
|
30
28
|
export interface AssimilateResult {
|
|
31
29
|
cube_id: string;
|
|
@@ -108,20 +106,6 @@ export interface AssimilateDeps {
|
|
|
108
106
|
previewIncludes: string;
|
|
109
107
|
updatedAfter: number;
|
|
110
108
|
}) => Promise<string | null>;
|
|
111
|
-
fetch: typeof fetch;
|
|
112
|
-
checkModelReachable: (descriptor: string | null, fetchImpl: typeof fetch, ollamaBaseUrl?: string) => Promise<{
|
|
113
|
-
ok: boolean;
|
|
114
|
-
message?: string;
|
|
115
|
-
}>;
|
|
116
|
-
getLaunchModel: (cubeId: string, worktreePath: string, priorDroneId: string | null) => Promise<{
|
|
117
|
-
model: string;
|
|
118
|
-
ollamaBaseUrl: string | null;
|
|
119
|
-
} | null>;
|
|
120
|
-
setLaunchModel: (cubeId: string, worktreePath: string, record: {
|
|
121
|
-
model: string;
|
|
122
|
-
ollamaBaseUrl: string | null;
|
|
123
|
-
}) => Promise<void>;
|
|
124
|
-
clearLaunchModel: (cubeId: string, worktreePath: string) => Promise<void>;
|
|
125
109
|
}
|
|
126
110
|
export declare function runAssimilate(args: AssimilateArgs, deps: AssimilateDeps): Promise<number>;
|
|
127
111
|
/**
|
package/dist/assimilate-cmd.js
CHANGED
|
@@ -1,49 +1,48 @@
|
|
|
1
|
-
import{dirname as
|
|
2
|
-
`),1}if(
|
|
3
|
-
`),1}let a=await e.getCachedAuth();if(!a){if(!e.isTTY()&&!
|
|
4
|
-
`)}}let
|
|
5
|
-
`);let
|
|
6
|
-
`),a=await e.runSetup(),
|
|
7
|
-
`),1;let u,
|
|
1
|
+
import{dirname as G,basename as x}from"node:path";import{randomUUID as oe}from"node:crypto";import{roleSlug as ie,matchRoleByName as ae,occupiedRoleIdsForAutoRole as se,pickDefaultRole as le}from"./role-resolver.js";import{deriveCubeName as ce,parseGitRemote as ue,sanitizeRemoteUrl as me}from"./cube-name.js";import{validateName as q}from"./name-validator.js";import{renderAssimilationWelcome as fe}from"./assimilate-welcome.js";import{shellEscape as de}from"./shell-escape.js";import{withCodexCwdArg as ge}from"./codex-remote.js";import{buildAgentKickoffPrompt as he,buildKickoffWakePathClause as we,recordCodexWakeTarget as be,socketPathFromRemoteArgs as ke}from"./codex-launch.js";import{perWorktreeBranchName as U,adoptWorktree as pe,computeWorktreePath as z,localBranchExists as J,isMerged as ye}from"./worktree-lifecycle.js";import{DroneEvictedError as ve}from"./drone-lifecycle.js";import{codexBorgSessionConfigArgs as $e}from"./launch-gate.js";import{codexAgentKindConfigArgs as xe,codexRemoteWakeConfigArgs as Ce,withAgentRuntimeEnv as Se}from"./agent-runtime.js";import{inboxPathForDrone as Re}from"./cubes.js";import{monitorStateRootForWorktree as Ee}from"./inbox-monitor.js";import{resolveLaunchEnv as Ae}from"./model-presets.js";import{unlinkSync as _e}from"node:fs";import{gcOrphanInboxesForCube as Ie,defaultListInboxLogs as Ne,defaultInboxLivenessDeps as Pe,isInboxLive as Te,ORPHAN_INBOX_STALE_MS as Le}from"./gc-orphan-inboxes.js";import{installBorgPlugin as De}from"./opencode-plugin.js";import{computeOpenCodePort as Oe,connectOpenCodeDrone as We,createOpenCodeLaunchKickoff as je,injectInitialKickoff as Ue}from"./opencode-drone.js";import{ensureCliMcpConfigured as He}from"./ensure-mcp-config.js";async function ht(r,e){if(r.role!==void 0){const t=q(r.role);if(!t.ok)return e.stderr(t.error+`
|
|
2
|
+
`),1}if(r.flags.worktree!==void 0){const t=q(r.flags.worktree);if(!t.ok)return e.stderr(t.error+`
|
|
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 i=e.findProjectRoot(e.cwd());let o;if(r.flags.cubeName)o=r.flags.cubeName;else{const t=e.runSync("git",["remote","get-url","origin"],i),n=t.status===0?t.stdout:null;if(o=ce(i,n),n){const c=me(n),d=c?ue(c):null;c&&!d&&o&&e.stderr(`couldn't parse git remote '${c}' \u2014 using directory name '${o}' as cube name
|
|
4
|
+
`)}}let s=null;if(o&&o.includes("@")&&o.includes(":")){const t=o.lastIndexOf(":");s={ownerEmail:o.substring(0,t),cubeName:o.substring(t+1)},o=s.cubeName}const C=e.cwd();e.stderr(`Checking your cubes\u2026
|
|
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===o);if(!N&&s)return e.stderr(`No cube named '${s.cubeName}' accessible to you owned by '${s.ownerEmail}'. Did you accept their invite? See borgmcp.ai/dashboard.
|
|
7
|
+
`),1;let u,P;if(N)u=await e.getCube(a.apiUrl,a.token,N.id),P=!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((y,v)=>{const _=v===0?" (default)":"";c.push(` ${v+1}) ${y.name}${_} \u2014 ${y.description}`)}),c.push(` ${n.length+1}) skip \u2014 no template`);const d=(await e.prompt(c.join(`
|
|
8
8
|
`)+`
|
|
9
|
-
[1]: `)).trim(),
|
|
10
|
-
`),1;t=
|
|
9
|
+
[1]: `)).trim(),k=d===""?1:parseInt(d,10);if(Number.isNaN(k)||k<1||k>n.length+1)return e.stderr(`invalid choice "${d}"
|
|
10
|
+
`),1;t=k<=n.length?n[k-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
|
-
`),1;t="starter"}e.stderr(
|
|
12
|
+
`),1;t="starter"}e.stderr(o?`Creating cube '${o}'\u2026
|
|
13
13
|
`:`Creating your cube\u2026
|
|
14
|
-
`),u=await e.createCube(a.apiUrl,a.token,t?{name:
|
|
14
|
+
`),u=await e.createCube(a.apiUrl,a.token,t?{name:o??void 0,template:t}:{name:o??void 0}),P=!0}let w;if(r.role!==void 0){if(w=ae(u.roles,r.role),!w){const t=u.roles.map(d=>d.name).join(", "),n=Be(r.role,u.roles.map(d=>d.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=
|
|
17
|
-
`),1}const
|
|
18
|
-
`),1;const
|
|
19
|
-
`),1}
|
|
20
|
-
`),
|
|
21
|
-
`);
|
|
22
|
-
`),1
|
|
23
|
-
`)
|
|
24
|
-
`)
|
|
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.
|
|
16
|
+
`),1}}else{const t=se(u.drones??[]);if(w=le(u.roles,{isFirstDrone:P,occupiedRoleIds:t}),!w)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 S=await e.getActiveCube();let R;if(S&&r.flags.here)if(S.cubeId===u.id)R=S.droneId;else return e.stderr(`this directory already hosts an active drone; remove --here or run from a fresh worktree
|
|
18
|
+
`),1;const H=r.flags.model??null,m=await e.resolveCli(r.flags.cli);try{He(m)}catch(t){const n=t instanceof Error?t.message:String(t);return e.stderr(`${m} MCP configuration failed: ${n}
|
|
19
|
+
`),1}e.stderr(`Joining cube '${u.name}' as ${w.name}\u2026
|
|
20
|
+
`);let l;try{l=await e.assimilate(a.apiUrl,a.token,{cube_id:u.id,role_id:w.id,hostname:e.getHostname(),agent_kind:m,model:H,...R?{prior_drone_id:R}:{}})}catch(t){if(t instanceof ve&&R!=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
|
+
`),1;const n=t instanceof Error?t.message:String(t);return e.stderr(`assimilate failed: ${n}
|
|
22
|
+
`),1}const b=u.roles.find(t=>t.id===l.role_id)??w;l.reattached?e.stderr(`re-attached to existing seat ${l.drone_label} (session token rotated, no new drone minted)
|
|
23
|
+
`):b.id!==w.id&&e.stderr(`Note: your invite didn't grant the "${w.name}" role \u2014 assimilated as "${b.name}" instead.
|
|
24
|
+
`);const X=r.flags.worktree!==void 0||S!==null&&!r.flags.here;let h=null;if(X){const t=e.runSync("git",["rev-parse","--verify","HEAD"],i);if(t.status!==0)return e.stderr(`sibling worktree spawn requires HEAD pointing at a commit.
|
|
26
25
|
Fix: create at least one commit (\`git commit --allow-empty -m "initial"\`)
|
|
27
26
|
OR: pass --here to skip the sibling spawn and use the current directory
|
|
28
|
-
`),1;e.runSync("git",["fetch","origin"],
|
|
29
|
-
`);const
|
|
30
|
-
`),1;const
|
|
31
|
-
`),1;e.stderr(`spawned sibling worktree at ${g} on branch ${
|
|
32
|
-
`),e.chdir(g),e.stderr(
|
|
33
|
-
`),
|
|
34
|
-
`):e.stderr(`manual cleanup needed: \`git worktree remove --force ${
|
|
35
|
-
`)}return 1}const
|
|
36
|
-
`)}if(!
|
|
37
|
-
`),e.stderr(
|
|
27
|
+
`),1;e.runSync("git",["fetch","origin"],i);let n="origin/main";e.runSync("git",["rev-parse","--verify","origin/main"],i).status!==0&&e.runSync("git",["rev-parse","--verify","origin/master"],i).status===0&&(n="origin/master");const d=t.stdout.trim(),k=e.runSync("git",["rev-parse",n],i).stdout.trim();d!==k&&e.stderr(`note: local HEAD (${d.slice(0,7)}) differs from ${n} (${k.slice(0,7)}); new worktree will start on ${n}
|
|
28
|
+
`);const y=x(i),v=r.flags.worktree??ie(b.name);if(v.length===0)return e.stderr(`cannot derive a worktree name from role "${b.name}"; pass an explicit --worktree <name>
|
|
29
|
+
`),1;const _=e.homedir();let g=z(_,y,v),p=U(x(g),y),B=2;for(;e.pathExists(g)||Ke(e,i,g)||J(e.runSync,i,p)&&!ye(e.runSync,i,p,n);)g=z(_,y,v,B),p=U(x(g),y),B++;e.mkdirp(G(g));const Y=J(e.runSync,i,p)?e.runSync("git",["worktree","add",g,p],i):e.runSync("git",["worktree","add","-b",p,g,n],i);if(Y.status!==0)return e.stderr(`git worktree add failed: ${V(Y.stderr)}
|
|
30
|
+
`),1;e.stderr(`spawned sibling worktree at ${g} on branch ${p} (${n}); original dir is registered as active (edit ~/.config/borgmcp/cubes.json if stale).
|
|
31
|
+
`),e.chdir(g),e.stderr(Fe(g,p,i)),h=e.cwd()}try{await e.setActiveCube({cubeId:l.cube_id,droneId:l.drone_id,name:u.name,sessionToken:l.session_token,droneLabel:l.drone_label,apiUrl:a.apiUrl,roleName:b.name,isHumanSeat:b.is_human_seat,...b.role_class?{roleClass:b.role_class}:{}})}catch(t){const n=t instanceof Error?t.message:String(t);if(e.stderr(`setActiveCube failed: ${n}
|
|
32
|
+
`),h){const c=e.runSync("git",["worktree","remove","--force",h],i);c.status===0?e.stderr(`rolled back spawned worktree at ${h}
|
|
33
|
+
`):e.stderr(`manual cleanup needed: \`git worktree remove --force ${h}\` (rollback attempt failed: ${V(c.stderr).trim()||"unknown"})
|
|
34
|
+
`)}return 1}const f=e.cwd(),Q=e.findProjectRoot(f),T=Ee(Q);try{const t=Pe(),n=G(Re(l.cube_id,l.drone_id));Ie({cubeInboxDir:n,selfDroneId:l.drone_id,deps:{listInboxLogs:Ne,isLive:c=>Te(c,t,T),droneState:()=>"absent",unlink:c=>_e(c),now:t.now,staleMs:Le},monitorStateRoot:T})}catch{}e.setTerminalTitle(l.drone_label,u.name);const Z=e.isTTY()&&!process.env.NO_COLOR&&!process.env.CI;e.stdout(fe(b.name,u.name,Z));try{e.installProjectSessionHook(f)}catch{e.stderr(`warning: could not install the project-local SessionStart hook in ${f}; it will be re-attempted on the next borg launch
|
|
35
|
+
`)}if(!h){e.runSync("git",["fetch","origin","--prune"],f);const t=U(x(f),x(i)),n=pe(e.runSync,f,t,"origin/main");n.action==="adopted"?(e.stderr(`worktree: adopted branch ${t} at origin/main
|
|
36
|
+
`),e.stderr(Me(f,t))):n.message&&e.stderr(`worktree sync: ${n.message}
|
|
38
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.
|
|
39
|
-
`);const
|
|
40
|
-
`),
|
|
41
|
-
Agent exited. You were working in ${
|
|
38
|
+
`);const ee=e.getInboxPath(l.cube_id,l.drone_id),L=m==="codex"?`borg-wake-${oe()}`:null,te=we(m,m==="claude"?ee:null,m==="claude"?T:null);let D,F=[],$,E=null,O=null;const M=Ae(H),W={...Se(process.env,m),...M.set,BORG_SESSION:"1"};for(const t of M.unset)delete W[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(W,t.env),E=ke(t.args),O=t.server?.cleanup??null}const K=he({cli:m,codexWakeNonce:L,monitorClause:te,codexWakePathClause:D});let A=null,j;if($=[K],m==="codex")$=[...$e(),...xe(),...Ce(E!==null),...F,...ge($,f)];else if(m==="opencode"){j=Oe(l.drone_id),De();const t=f;A=je(K),$=[t,"--port",String(j),"--auto","--prompt",A.prompt]}const re=e.exec(m,$,f,W);if(m==="codex"&&E&&L&&be({deps:e,cubeId:l.cube_id,droneId:l.drone_id,socketPath:E,cwd:f,previewNeedle:L,launchedAtSeconds:Math.floor(Date.now()/1e3)}),m==="opencode"&&A){const t=A,n=`http://127.0.0.1:${j}`;We({serverUrl:n,directory:f,droneLabel:l.drone_label,cubeName:o??"borg"}).then(()=>Ue(t)).catch(()=>{})}const ne=await re;if(O)try{O()}catch{}return h&&C!==h&&e.stderr(`
|
|
40
|
+
Agent exited. You were working in ${h}; your shell is back in ${C}.
|
|
42
41
|
To return:
|
|
43
|
-
cd ${
|
|
44
|
-
`),
|
|
45
|
-
WORKTREE STEERING: You are in worktree ${
|
|
46
|
-
`}function
|
|
47
|
-
WORKTREE STEERING: This checkout is now on branch ${e}. Do ALL work HERE in ${
|
|
48
|
-
`}function
|
|
49
|
-
`).some(
|
|
42
|
+
cd ${de(h)}
|
|
43
|
+
`),ne}function Fe(r,e,a){return`
|
|
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 Me(r,e){return`
|
|
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 V(r){return r.replace(/[\x00-\x1F\x7F]/g,"")}function Ke(r,e,a){const i=r.runSync("git",["worktree","list","--porcelain"],e);return i.status!==0?!1:i.stdout.split(`
|
|
48
|
+
`).some(o=>o===`worktree ${a}`)}function Be(r,e){if(e.length===0)return null;const a=r.toLowerCase();let i=null;for(const o of e){const s=Ye(a,o.toLowerCase());s<=2&&(i===null||s<i.distance)&&(i={name:o,distance:s})}return i?i.name:null}function Ye(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),i=new Array(e.length+1);for(let o=0;o<=e.length;o++)a[o]=o;for(let o=1;o<=r.length;o++){i[0]=o;for(let s=1;s<=e.length;s++){const C=r[o-1]===e[s-1]?0:1;i[s]=Math.min(i[s-1]+1,a[s]+1,a[s-1]+C)}for(let s=0;s<=e.length;s++)a[s]=i[s]}return a[e.length]}export{ht as runAssimilate,V as safeStderr,Be as suggestRoleName};
|
package/dist/assimilate-deps.js
CHANGED
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
import{spawnSync as p,spawn as
|
|
2
|
-
`))if(
|
|
3
|
-
`)}catch{clearTimeout(
|
|
1
|
+
import{spawnSync as p,spawn as c}from"node:child_process";import{existsSync as f,mkdirSync as b}from"node:fs";import{hostname as h,homedir as _}from"node:os";import{createInterface as d}from"node:readline/promises";import{readinessProbeEnv as C}from"./readiness-probe.js";import{API_URL as u,getValidToken as m,listCubes as T,getCube as y,createCube as w,assimilate as k,listTemplates as g}from"./remote-client.js";import{findProjectRoot as v,getActiveCube as x,setActiveCube as S,inboxPathForDrone as P,setCodexWakeTarget as A}from"./cubes.js";import{authenticateWithGoogle as R}from"./auth.js";import{addProjectSessionStartHook as U}from"./config-utils.js";import{setTerminalTitle as j}from"./terminal-title.js";import{defaultCliChoiceDeps as L,resolveCliChoice as D}from"./cli-platform.js";import{prepareCodexRemoteLaunch as H,defaultCodexRemoteDeps as I}from"./codex-remote.js";import{findLoadedCodexThread as G}from"./codex-app-server.js";function Q(){return{runSync:(e,r,o)=>{const t=p(e,r,{cwd:o,encoding:"utf-8"});return{status:t.status,stdout:t.stdout??"",stderr:t.stderr??""}},pathExists:e=>f(e),cwd:()=>process.cwd(),chdir:e=>process.chdir(e),homedir:()=>_(),mkdirp:e=>b(e,{recursive:!0}),exec:(e,r,o,t)=>new Promise((i,s)=>{const a=c(e,r,{cwd:o,stdio:"inherit",shell:!1,env:t??process.env});a.on("error",s),a.on("exit",n=>i(n??0))}),stderr:e=>process.stderr.write(e),stdout:e=>process.stdout.write(e),prompt:async e=>{const r=d({input:process.stdin,output:process.stdout});try{return await r.question(e)}finally{r.close()}},isTTY:()=>process.stdin.isTTY===!0,getHostname:()=>h(),setTerminalTitle:(e,r)=>{j({label:e,cubeName:r},r)},getActiveCube:()=>x(),setActiveCube:e=>S(e),findProjectRoot:e=>v(e),installProjectSessionHook:e=>{U(e)},getCachedAuth:async()=>{try{return{token:await m(),apiUrl:u}}catch{return null}},runSetup:async()=>(await R(),{token:await m(),apiUrl:u}),listCubes:async(e,r)=>{const{cubes:o}=await T();return o.map(t=>({id:t.id,name:t.name}))},getCube:async(e,r,o)=>{const t=await y(o);return{id:t.id,name:t.name,roles:t.roles,drones:(t.drones??[]).map(i=>({role_id:i.role_id}))}},createCube:async(e,r,o)=>{const t=await w(o.name,"",o.template?{template:o.template}:void 0);return{id:t.id,name:t.name,roles:t.roles}},assimilate:async(e,r,o)=>{const t=await k({cube_id:o.cube_id,role_id:o.role_id,...o.prior_drone_id?{prior_drone_id:o.prior_drone_id}:{},...o.model!=null?{model:o.model}:{}},void 0,o.hostname??null,o.agent_kind??null);return{cube_id:t.cube.id,drone_id:t.drone.id,drone_label:t.drone.label,session_token:t.sessionToken,role_id:t.role.id,reattached:t.reattached===!0}},listTemplates:async(e,r)=>{const{templates:o}=await g();return o.map(t=>({name:t.name,description:t.description}))},getInboxPath:(e,r)=>P(e,r),probeMcpReady:()=>new Promise(e=>{const r=c("borg-mcp",[],{stdio:["pipe","pipe","pipe"],shell:!1,env:C()});let o="",t=!1;const i=n=>{if(!t){t=!0;try{r.kill("SIGTERM")}catch{}e(n)}},s=setTimeout(()=>i(!1),2e3);r.on("error",()=>{clearTimeout(s),i(!1)}),r.on("exit",()=>{clearTimeout(s),i(t)}),r.stdout?.on("data",n=>{o+=n.toString("utf-8");for(const l of o.split(`
|
|
2
|
+
`))if(l.includes('"protocolVersion"')&&l.includes('"result"')){clearTimeout(s),i(!0);return}});const a=JSON.stringify({jsonrpc:"2.0",id:1,method:"initialize",params:{protocolVersion:"2024-11-05",capabilities:{},clientInfo:{name:"borg-assimilate-probe",version:"0.9.3"}}});try{r.stdin?.write(a+`
|
|
3
|
+
`)}catch{clearTimeout(s),i(!1)}}),resolveCli:e=>D(e,L(async r=>{const o=d({input:process.stdin,output:process.stdout});try{return await o.question(r)}finally{o.close()}},()=>process.stdin.isTTY===!0)),prepareCodexRemoteLaunch:()=>H(I()),setCodexWakeTarget:A,findLoadedCodexThread:G}}export{Q as buildDefaultAssimilateDeps};
|
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 ${r}`)}`);const
|
|
11
|
-
`)}`)}else console.error(`${t()}${
|
|
2
|
+
import{spawn as G}from"child_process";import{randomUUID as M}from"node:crypto";import{basename as Y}from"node:path";import{createInterface as U}from"node:readline/promises";import i from"chalk";import{findProjectRoot as D,getActiveCube as V,inboxPathForDrone as j,setCodexWakeTarget as q,pruneDeadCodexWakeTargets as X}from"./cubes.js";import{monitorStateRootForWorktree as z}from"./inbox-monitor.js";import{handleVersionFlag as J,getPackageVersion as w}from"./version.js";import{isHelpFlag as O,setupHelpText as Q,topLevelHelpText as Z,assimilateHelpText as ee}from"./cli-help.js";import{runSpawn as re}from"./spawn.js";import{buildClaudeLaunchArgs as oe}from"./claude-launch-args.js";import{parseSyncArgs as se,runSync as te}from"./sync.js";import{parseCleanupArgs as ie,runCleanup as ne}from"./cleanup-cmd.js";import{parseAssimilateArgs as ae}from"./parse-assimilate-args.js";import{runAssimilate as ce}from"./assimilate-cmd.js";import{buildDefaultAssimilateDeps as le}from"./assimilate-deps.js";import{parseLaunchAllArgs as E}from"./parse-launch-all-args.js";import{unknownSubcommand as de}from"./unknown-subcommand.js";import{runLaunchAll as H}from"./launch-all-cmd.js";import{buildDefaultLaunchAllDeps as v}from"./launch-all-deps.js";import{discoverDroneCandidates as pe}from"./launch-all-discovery.js";import{explicitCliLaunchHint as ue,runBareLaunchMenu as me,shouldResolveExplicitCliLaunchHintTargets as fe,shouldShowLaunchMenu as ge}from"./bare-launch-menu.js";import{setTerminalTitle as he}from"./terminal-title.js";import{initConsolePrefix as we,consolePrefix as t}from"./console-prefix.js";import{initDebugFromArgv as Ce}from"./debug.js";import{fetchLatestBorgmcpVersion as xe,compareVersionsForStaleness as ke}from"./stale-version-check.js";import{defaultCliChoiceDeps as be,detectCliAvailability as $,installedCliNames as N,parseCliFlag as ve,resolveCliChoice as $e}from"./cli-platform.js";import{getRefreshToken as Ae,getIdToken as Se}from"./config.js";import{composeGetStarted as ye,shouldShowGetStarted as Te}from"./get-started.js";import{prepareCodexRemoteLaunch as Pe,withCodexCwdArg as Re,defaultCodexRemoteDeps as Ie,checkCodexBridgeHealthy as Le}from"./codex-remote.js";import{BORG_CODEX_REMOTE_WAKE_ENV as De,codexAgentKindConfigArgs as Oe,codexRemoteWakeConfigArgs as Ee,withAgentRuntimeEnv as A}from"./agent-runtime.js";import{findLoadedCodexThread as He}from"./codex-app-server.js";import{buildAgentKickoffPrompt as Ne,buildKickoffWakePathClause as Fe,recordCodexWakeTarget as Be,socketPathFromRemoteArgs as F}from"./codex-launch.js";import{codexBorgSessionConfigArgs as We}from"./launch-gate.js";import{addCodexSessionStartHook as _e,addCodexUserPromptSubmitHook as Ke,addProjectSessionStartHook as Ge,addUserPromptSubmitHook as Me,removeSessionStartHook as Ye}from"./config-utils.js";import{ensureCliMcpConfigured as S}from"./ensure-mcp-config.js";import{installBorgPlugin as Ue}from"./opencode-plugin.js";import{connectOpenCodeDrone as Ve,computeOpenCodePort as B,createOpenCodeLaunchKickoff as je,injectInitialKickoff as qe}from"./opencode-drone.js";async function Xe(){Ce(process.argv),J(),await we();const c=(async()=>{if(!process.stderr.isTTY)return;const e=w(),s=await xe();if(!s)return;const n=ke(e,s);n.stale&&n.message&&process.stderr.write(`${t()}${n.message}
|
|
3
|
+
`)})();if((process.argv[2]==="--help"||process.argv[2]==="-h")&&(process.stdout.write(Z(w())),process.exit(0)),process.argv[2]==="setup"){O(process.argv[3])&&(process.stdout.write(Q(w())),process.exit(0)),await import("./setup.js");return}if(process.argv[2]==="assimilate"){process.argv.slice(3).some(O)&&(process.stdout.write(ee(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
|
+
`)),process.stderr.write("Run `borg --help` for usage.\n"),process.exit(1));const s=le(),n=await ce({role:e.role,flags:e.flags},s);process.exit(n)}if(process.argv[2]==="spawn"){const e=await re();process.exit(e)}if(process.argv[2]==="sync"){const e=se(process.argv.slice(3));e.ok||(process.stderr.write(i.red(`${t()}\u25FC borg sync: ${e.error}
|
|
5
|
+
`)),process.stderr.write("Run `borg --help` for usage.\n"),process.exit(1));const s=await te({},e.options);process.exit(s)}if(process.argv[2]==="cleanup"){const e=ie(process.argv.slice(3));e.ok||(process.stderr.write(i.red(`${t()}\u25FC borg cleanup: ${e.error}
|
|
6
|
+
`)),process.stderr.write("Run `borg --help` for usage.\n"),process.exit(1));const s=await ne({},e.options);process.exit(s)}if(process.argv[2]==="launch-all"){const e=E(process.argv.slice(3));e.ok||(process.stderr.write(i.red(`${t()}\u25FC borg launch-all: ${e.error}
|
|
7
|
+
`)),process.stderr.write("Run `borg --help` for usage.\n"),process.exit(1));const s=v(),n=await H(e.args,s);process.exit(n)}const a=de(process.argv[2]);if(a!==null&&(process.stderr.write(i.red(`${t()}\u25FC unknown command: ${a}
|
|
8
|
+
`)),process.stderr.write("Run `borg --help` for usage.\n"),process.exit(1)),Te(await Ae()!==null,await Se()!==null)){const e=N($()).length>0;process.stdout.write(ye(e)),process.exit(0)}const d=ve(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 y=async e=>{const s=U({input:process.stdin,output:process.stdout});try{return await s.question(e)}finally{s.close()}};let r=await $e(d.cli,be(y,()=>process.stdin.isTTY===!0));ze();const o=await V();let C;const T=async()=>o?(C===void 0&&(C=(await pe({targetCubeId:o.cubeId},v())).length>0),C):!1,x=process.stdin.isTTY===!0,k=process.stdout.isTTY===!0,P=ue({explicitCli:d.cli,stdinIsTTY:x,stdoutIsTTY:k,hasActiveCube:o!==null,hasLaunchAllTargets:fe({explicitCli:d.cli,stdinIsTTY:x,stdoutIsTTY:k,hasActiveCube:o!==null})?await T():!1});if(P&&process.stderr.write(P),ge({extraArgs:process.argv.slice(2),stdinIsTTY:x,stdoutIsTTY:k})){const e=N($()).filter(n=>n!==r),s=await me({defaultCli:r,otherInstalledClis:e,hasLaunchAllTargets:await T()},y);if(s.kind==="launch-all"){const n=E([]),K=n.ok?await H(n.args,v()):1;process.exit(K)}r=s.cli}const l=d.rest;he(o?{label:o.droneLabel,cubeName:o.name}:null,Y(process.cwd()));const W=Fe(r,o&&r==="claude"?j(o.cubeId,o.droneId):null,o&&r==="claude"?z(D(process.cwd())):null);await Promise.race([c,new Promise(e=>setTimeout(e,2e3))]);const R=r==="codex"?`borg-wake-${M()}`:null;let m,I=[],b={...A(process.env,r),BORG_SESSION:"1"},p=null,u=null;if(r==="codex"&&!l.includes("--remote")){console.error(`${t()}${i.gray("\u25FC Starting Codex remote-wake app-server\u2026")}`);const e=await Pe(Ie());e.warning?(console.error(`${t()}${i.yellow(`warning: ${e.warning}`)}`),m="\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."):m="Codex wake-path capability check passed: remote-control socket established for this session.",I=e.args,b={...A(process.env,r),...e.env,BORG_SESSION:"1"},p=F(e.args),u=e.server?.cleanup??null}else r==="codex"&&l.includes("--remote")&&(m="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&&(b={...A(process.env,r),[De]:"1",BORG_SESSION:"1"}));const f=Ne({cli:r,codexWakeNonce:R,monitorClause:W,codexWakePathClause:m});let g=null,h;if(r==="codex")h=[...We(),...Oe(),...Ee(p!==null),...I,...Re([...l,f],process.cwd())];else if(r==="opencode"){const e=o?B(o.droneId):14096;Ue(),g=je(f),h=[process.cwd(),"--port",String(e),"--auto","--prompt",g.prompt,...l]}else h=oe(l,f);const _=r==="claude"?"Claude Code":r==="codex"?"Codex":"OpenCode";console.error(`${t()}${i.blue(`\u25FC Launching ${_}\u2026`)}`);const L=G(r,h,{stdio:"inherit",shell:!1,env:b});if(r==="opencode"&&g){const e=g,n=`http://127.0.0.1:${o?B(o.droneId):14096}`;Ve({serverUrl:n,directory:process.cwd(),droneLabel:o?.droneLabel??"opencode",cubeName:o?.name??"borg"}).then(()=>qe(e)).catch(()=>{})}r==="codex"&&o&&p&&(Be({deps:{setCodexWakeTarget:q,findLoadedCodexThread:He},cubeId:o.cubeId,droneId:o.droneId,socketPath:p,passthroughArgs:l,previewNeedle:R??f.slice(0,120),cwd:process.cwd(),launchedAtSeconds:Math.floor(Date.now()/1e3)}),X(e=>Le(e))),L.on("error",e=>{if(u)try{u()}catch{}if(e.code==="ENOENT"){console.error(`${t()}${i.red(`
|
|
10
|
+
\u25FC Failed to launch ${r}`)}`);const s=r==="opencode"?"opencode":r;console.error(`${t()}${i.gray(`Make sure ${s} is installed.
|
|
11
|
+
`)}`)}else console.error(`${t()}${i.red(`
|
|
12
12
|
\u25FC Failed to launch ${r}: ${e.message}
|
|
13
|
-
`)}`);process.exit(1)}),
|
|
13
|
+
`)}`);process.exit(1)}),L.on("exit",e=>{if(u)try{u()}catch{}process.exit(e??0)})}function ze(){const c=$();if(c.claude)try{S("claude"),Ge(D(process.cwd())),Ye(),Me()}catch(a){console.error(`${t()}${i.yellow(`warning: Claude Code integration check failed: ${a?.message??a}`)}`)}if(c.codex)try{S("codex"),_e(),Ke()}catch(a){console.error(`${t()}${i.yellow(`warning: Codex integration check failed: ${a?.message??a}`)}`)}if(c.opencode)try{S("opencode")}catch(a){console.error(`${t()}${i.yellow(`warning: OpenCode integration check failed: ${a?.message??a}`)}`)}}Xe().catch(c=>{console.error(`${t()}${i.red(`
|
|
14
14
|
\u25FC Error: ${c.message}
|
|
15
15
|
`)}`),process.exit(1)});
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
export declare const CLEAR_REWAKE_REMINDER = "Post-/clear recovery: full regen, drain the unread log; handle actionable cube entries, otherwise resume the work from before this wake.";
|
|
2
|
+
export interface ClearRewakeResult {
|
|
3
|
+
exitCode: 0 | 2;
|
|
4
|
+
stderr: string;
|
|
5
|
+
}
|
|
6
|
+
/**
|
|
7
|
+
* Resolve the Claude `/clear` async-rewake response without consulting cube
|
|
8
|
+
* state. The reminder is deliberately static: hook input can contain local
|
|
9
|
+
* paths and session metadata, none of which may cross into stderr.
|
|
10
|
+
*/
|
|
11
|
+
export declare function evaluateClearRewake(raw: string, env?: NodeJS.ProcessEnv): ClearRewakeResult;
|
|
12
|
+
//# sourceMappingURL=clear-rewake-core.d.ts.map
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
import{isBorgSession as o}from"./launch-gate.js";const n="Post-/clear recovery: full regen, drain the unread log; handle actionable cube entries, otherwise resume the work from before this wake.";function i(r,t=process.env){if(!o(t))return{exitCode:0,stderr:""};let e;try{e=r.trim()?JSON.parse(r):null}catch{return{exitCode:0,stderr:""}}return e?.hook_event_name!=="SessionStart"||e?.source!=="clear"?{exitCode:0,stderr:""}:{exitCode:2,stderr:`${n}
|
|
2
|
+
`}}export{n as CLEAR_REWAKE_REMINDER,i as evaluateClearRewake};
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import{evaluateClearRewake as s}from"./clear-rewake-core.js";async function n(){if(process.stdin.isTTY)return"";try{const t=[];for await(const e of process.stdin)t.push(e);return Buffer.concat(t).toString("utf-8")}catch{return""}}const r=s(await n());r.stderr&&process.stderr.write(r.stderr),process.exitCode=r.exitCode;
|
package/dist/cli-help.d.ts
CHANGED
|
@@ -16,8 +16,7 @@ export declare function isHelpFlag(arg: string | undefined): boolean;
|
|
|
16
16
|
export declare function topLevelHelpText(version: string): string;
|
|
17
17
|
/**
|
|
18
18
|
* Help text for `borg assimilate --help` — the home for the full assimilate flag
|
|
19
|
-
* set
|
|
20
|
-
* top-level help only hints at.
|
|
19
|
+
* set. Model/provider configuration belongs to the selected agent CLI.
|
|
21
20
|
*/
|
|
22
21
|
export declare function assimilateHelpText(version: string): string;
|
|
23
22
|
/**
|
package/dist/cli-help.js
CHANGED
|
@@ -12,7 +12,6 @@ Usage:
|
|
|
12
12
|
borg setup --no-browser Set up from SSH/headless terminals
|
|
13
13
|
borg assimilate [role] Join a cube (creates one if needed)
|
|
14
14
|
borg assimilate --worktree <name> Spawn a worktree drone (in ~/.borg/worktrees/<repo>/<name>)
|
|
15
|
-
borg assimilate --model ollama:<model> Run the drone on an Ollama model (see \`borg assimilate --help\`)
|
|
16
15
|
borg sync [--prune] Sync this worktree's branch to origin/main
|
|
17
16
|
borg cleanup [--prune] Report (or --prune) worktrees orphaned by evicted drones
|
|
18
17
|
borg launch-all [cube] Launch all drone worktrees of a cube (default: active cube)
|
|
@@ -22,7 +21,7 @@ Usage:
|
|
|
22
21
|
borg --version Show installed version
|
|
23
22
|
|
|
24
23
|
All other arguments are passed through to the selected agent CLI.
|
|
25
|
-
`}function
|
|
24
|
+
`}function r(e){return`borg assimilate (borgmcp ${e}) \u2014 join a cube under a role (creates the cube if needed)
|
|
26
25
|
|
|
27
26
|
Usage:
|
|
28
27
|
borg assimilate [role] Join the active cube under [role] (default role if omitted)
|
|
@@ -38,19 +37,12 @@ Flags:
|
|
|
38
37
|
--template <name> Bootstrap a new cube from a bundled role template
|
|
39
38
|
--no-template Create the cube with no template roles
|
|
40
39
|
--cli claude|codex|opencode Agent CLI to launch (default: claude)
|
|
41
|
-
--model
|
|
40
|
+
--model claude:<model> Legacy Claude model override (configure models in the agent CLI)
|
|
42
41
|
--yes, -y Skip confirmation prompts
|
|
43
42
|
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
The model must be pulled (ollama pull <model>); borg verifies it
|
|
48
|
-
against the Ollama server before assimilating.
|
|
49
|
-
|
|
50
|
-
Remote Ollama: set BORG_OLLAMA_BASE_URL (default http://localhost:11434), e.g.
|
|
51
|
-
BORG_OLLAMA_BASE_URL=http://Mac-Studio.local:11434 \\
|
|
52
|
-
borg assimilate build --model ollama:qwen3-coder-next
|
|
53
|
-
`}function r(e){return`borg setup (borgmcp ${e}) \u2014 set up OAuth + register the borg MCP server
|
|
43
|
+
For local or provider-specific models, configure the selected agent CLI directly.
|
|
44
|
+
OpenCode supports Ollama and other providers through its own model configuration.
|
|
45
|
+
`}function t(e){return`borg setup (borgmcp ${e}) \u2014 set up OAuth + register the borg MCP server
|
|
54
46
|
|
|
55
47
|
Borg MCP needs Claude Code, Codex, or OpenCode installed first.
|
|
56
48
|
|
|
@@ -61,4 +53,4 @@ Usage:
|
|
|
61
53
|
for SSH / headless / container terminals. Alias: --device.
|
|
62
54
|
Auto-detected on SSH/headless; this forces it.
|
|
63
55
|
borg setup --help Show this help
|
|
64
|
-
`}export{
|
|
56
|
+
`}export{r as assimilateHelpText,n as isHelpFlag,t as setupHelpText,o as topLevelHelpText};
|
package/dist/codex-app-wake.d.ts
CHANGED
|
@@ -50,9 +50,10 @@ export interface CodexWakeDeps {
|
|
|
50
50
|
}
|
|
51
51
|
export declare function wakeCodexViaAppServer(reason?: string, env?: NodeJS.ProcessEnv, deps?: CodexWakeDeps): void;
|
|
52
52
|
/**
|
|
53
|
-
* gh#857 WI-2: codex /loop-equivalent heartbeat cadence.
|
|
54
|
-
*
|
|
55
|
-
*
|
|
53
|
+
* gh#857 WI-2: codex /loop-equivalent heartbeat cadence. Codex retains this
|
|
54
|
+
* independent 20-minute drain because it has no Claude-style per-entry inbox
|
|
55
|
+
* Monitor. Claude recovery is adaptive: 3h ±30m while the Monitor is healthy
|
|
56
|
+
* or indeterminate, and 15m ±3m only while it is explicitly broken.
|
|
56
57
|
*/
|
|
57
58
|
export declare const CODEX_HEARTBEAT_CADENCE_MS: number;
|
|
58
59
|
/**
|
package/dist/codex-remote.d.ts
CHANGED
|
@@ -4,8 +4,8 @@
|
|
|
4
4
|
* borg owns a per-launch DIRECT Codex app-server (`codex app-server --listen
|
|
5
5
|
* unix://<socket>`) as the primary wake path, instead of the standalone
|
|
6
6
|
* `codex app-server daemon start` (which only exists for standalone-installer
|
|
7
|
-
* Codex — npm-managed Codex has no daemon, so those sessions had
|
|
8
|
-
* and
|
|
7
|
+
* Codex — npm-managed Codex has no daemon, so those sessions had no push-wake
|
|
8
|
+
* and relied on periodic log-drain catch-up). Per-launch direct
|
|
9
9
|
* app-server works for both install kinds and starts fresh each launch (so it
|
|
10
10
|
* always loads the current borgmcp MCP — no daemon-restart version refresh
|
|
11
11
|
* needed). The TUI is then launched with `codex --remote unix://<socket>` and
|
|
@@ -27,6 +27,15 @@ export interface CodexRemoteLaunch {
|
|
|
27
27
|
export interface CodexChild {
|
|
28
28
|
pid: number | undefined;
|
|
29
29
|
kill: () => void;
|
|
30
|
+
/** Snapshot app-server exit state + bounded stderr captured by the production spawn. */
|
|
31
|
+
diagnostics?: () => CodexChildDiagnostics;
|
|
32
|
+
}
|
|
33
|
+
export interface CodexChildDiagnostics {
|
|
34
|
+
exited: boolean;
|
|
35
|
+
exitCode: number | null;
|
|
36
|
+
signal: string | null;
|
|
37
|
+
error?: string | null;
|
|
38
|
+
stderr: string;
|
|
30
39
|
}
|
|
31
40
|
export interface PrepareCodexRemoteDeps {
|
|
32
41
|
/** Spawn the long-lived `codex app-server --listen unix://<socketPath>` child. */
|
|
@@ -39,7 +48,7 @@ export interface PrepareCodexRemoteDeps {
|
|
|
39
48
|
runtimeDir?: string;
|
|
40
49
|
/** Unique socket id generator (default 32-hex). Injected for deterministic tests. */
|
|
41
50
|
socketId?: () => string;
|
|
42
|
-
/** Readiness timeout (default
|
|
51
|
+
/** Readiness timeout (default 30000ms) + poll interval (default 250ms). */
|
|
43
52
|
readyTimeoutMs?: number;
|
|
44
53
|
pollIntervalMs?: number;
|
|
45
54
|
/** Whether a pid is alive (default process.kill(pid, 0)). Injected for tests. */
|
package/dist/codex-remote.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
import{mkdirSync as
|
|
1
|
+
import{mkdirSync as R,chmodSync as k,readdirSync as D,rmSync as S,writeFileSync as v,readFileSync as _}from"node:fs";import{homedir as $}from"node:os";import{join as m}from"node:path";import{randomBytes as T}from"node:crypto";import{spawn as w}from"node:child_process";import{CodexAppServerClient as N}from"./codex-app-server.js";import{codexBorgSessionConfigArgs as M,BORG_SESSION_ENV as P}from"./launch-gate.js";import{codexAppServerSocketConfigArgs as O}from"./codex-wake-resolve.js";import{BORG_AGENT_KIND_ENV as I,BORG_CODEX_REMOTE_WAKE_ENV as y,codexAgentKindConfigArgs as z,codexRemoteWakeConfigArgs as B}from"./agent-runtime.js";const F=m($(),".config","borgmcp","codex-remote"),H=3e4,A=16384;function d(r){let e=String(r).replace(/\x1B\[[0-?]*[ -/]*[@-~]/g,"");return e=e.replace(/\b((?:authorization\s*:\s*)?bearer)\s+[^\s,;]+/gi,"$1 <REDACTED>"),e=e.replace(/\b([a-z][a-z0-9+.-]*:\/\/)([^/\s:@]+):([^@\s/]+)@/gi,"$1<REDACTED>@"),e=e.replace(/([?&](?:access_token|auth_token|id_token|refresh_token|session_token|token|api_key|apikey|secret|password)=)[^&#\s]*/gi,"$1<REDACTED>"),e=e.replace(/\b((?:[a-z0-9_-]*(?:token|secret|password)|api[_-]?key)\s*[:=]\s*)(?:"[^"\r\n]*"|'[^'\r\n]*'|[^\s,;]+)/gi,"$1<REDACTED>"),e=e.replace(/\beyJ[A-Za-z0-9_-]{6,}\.[A-Za-z0-9_-]{6,}\.[A-Za-z0-9_-]{6,}\b/g,"<REDACTED>"),e=e.replace(/(^|[\s("'=])(?:file:\/\/)?\/(?:Users|home|private|tmp|var|etc|opt|Volumes)\/[^\s"'`<>)}\]]+/gm,"$1<REDACTED_PATH>"),e=e.replace(/\b[A-Za-z]:\\(?:Users|Documents and Settings|ProgramData|Temp)\\[^\s"'`<>)}\]]+/g,"<REDACTED_PATH>"),e.slice(-A)}function Q(r,e){return U(r)?r:["--cd",e,...r]}function U(r){return r.some(e=>e==="--cd"||e.startsWith("--cd=")||e==="-C")}function C(r){try{return process.kill(r,0),!0}catch(e){return e?.code==="EPERM"}}function ee(r,e={}){if(!r)return null;const s=e.isAlive??C,o=e.readPidFile??(i=>_(i,"utf-8")),a=r.replace(/\.sock$/,".pid");try{const i=Number.parseInt(o(a).trim(),10);return Number.isNaN(i)?null:s(i)}catch{return null}}function p(r){try{S(r,{force:!0})}catch{}}function V(r,e){let s;try{s=D(r)}catch{return}for(const o of s){if(!o.endsWith(".pid"))continue;const a=m(r,o),i=m(r,o.replace(/\.pid$/,".sock"));let t;try{t=Number.parseInt(_(a,"utf-8").trim(),10)}catch{p(a);continue}(Number.isNaN(t)||!e(t))&&(p(i),p(a))}}function f(r){return{args:[],env:{},warning:r}}function X(r){return r.error?`process error: ${d(r.error)}`:r.exitCode!=null?`exit code ${r.exitCode}`:r.signal?`signal ${r.signal}`:"unknown exit status"}function b(r){const e=d(r).trim();return e?` Stderr: ${e}`:""}async function re(r){const e=r.runtimeDir??F,s=r.isAlive??C,o=r.readyTimeoutMs??H,a=r.pollIntervalMs??250;try{R(e,{recursive:!0,mode:448}),k(e,448),V(e,s)}catch(n){return f(`Codex remote-wake disabled: could not prepare ${e} (${n?.message??n}); run borg_regen manually.`)}const i=(r.socketId??(()=>T(16).toString("hex")))(),t=m(e,`${i}.sock`),l=m(e,`${i}.pid`);let c;try{c=r.spawnAppServer(t)}catch(n){return p(t),f(`Codex remote-wake disabled: could not start \`codex app-server\` (${d(String(n?.message??n))}) \u2014 confirm the Codex executable is installed and available on PATH. Remote wake is unavailable for this session; run borg_regen manually to catch up.`)}if(c.pid!=null)try{v(l,String(c.pid))}catch{}const g=()=>{try{c.kill()}catch{}p(t),p(l)},x=Math.max(1,Math.ceil(o/a));let u=!1,h;for(let n=0;n<x&&!u;n++){try{u=await r.probeReady(t)}catch{u=!1}if(!u){const E=c.diagnostics?.();if(E?.exited){h=E;break}}!u&&n<x-1&&await r.sleep(a)}if(!u){const n=h??c.diagnostics?.();return g(),n?.exited?f(`Codex remote-wake disabled: \`codex app-server\` exited before becoming ready (${X(n)}).${b(n.stderr)} Run borg_regen manually to catch up.`):f(`Codex remote-wake disabled: \`codex app-server\` remained running but did not become ready at ${d(t)} within ${o}ms.${b(n?.stderr??"")} Run borg_regen manually to catch up.`)}return{args:["--remote",`unix://${t}`],env:{[y]:"1"},server:{pid:c.pid,socketPath:t,cleanup:g}}}function te(){return{spawnAppServer:r=>{const e=w("codex",["app-server",...M(),...z(),...B(),...O(r),"--listen",`unix://${r}`],{stdio:["ignore","ignore","pipe"],shell:!1,env:{...process.env,[P]:"1",[I]:"codex",[y]:"1"}});let s="",o=!1,a=null,i=null,t=null;return e.stderr?.setEncoding("utf8"),e.stderr?.on("data",l=>{s=`${s}${String(l)}`.slice(-A)}),e.once("close",(l,c)=>{o=!0,a=l,i=c}),e.once("error",l=>{o=!0,t=d(l.message)}),{pid:e.pid,kill:()=>{try{e.kill()}catch{}},diagnostics:()=>({exited:o,exitCode:a,signal:i,error:t==null?null:d(t),stderr:d(s)})}},probeReady:async r=>{const e=new N(r);try{return await e.connect(),await e.loadedThreadIds(),!0}catch{return!1}finally{try{e.close()}catch{}}},sleep:r=>new Promise(e=>setTimeout(e,r))}}export{F as DEFAULT_CODEX_REMOTE_DIR,ee as checkCodexBridgeHealthy,te as defaultCodexRemoteDeps,C as defaultIsAlive,re as prepareCodexRemoteLaunch,Q as withCodexCwdArg};
|
package/dist/config-utils.js
CHANGED
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
import{execSync as
|
|
2
|
-
`,"utf-8")}function S(e){if(!n.existsSync(e))return{};const
|
|
3
|
-
`,"utf-8")}function
|
|
1
|
+
import{execSync as f}from"child_process";import n from"fs";import l from"os";import i from"path";import{fileURLToPath as w}from"url";import{dirname as H}from"path";import{BORG_AGENT_KIND_ENV as x,BORG_CODEX_REMOTE_WAKE_ENV as j,withAgentRuntimeEnv as N}from"./agent-runtime.js";const I=w(import.meta.url),M=H(I),a="borg-regen",C="borg-clear-rewake",m="borg-log-audit",G=i.join(l.homedir(),".claude.json"),$=i.join(l.homedir(),".codex","config.toml"),u=i.join(l.homedir(),".codex","hooks.json"),B=i.join(l.homedir(),".config","opencode","opencode.json"),F="borg";function k(){return i.join(l.homedir(),".claude","settings.json")}function h(){const e=k();if(!n.existsSync(e))return{};const t=n.readFileSync(e,"utf-8");return t.trim()?JSON.parse(t):{}}function g(e){const t=k();n.mkdirSync(i.dirname(t),{recursive:!0}),n.writeFileSync(t,JSON.stringify(e,null,2)+`
|
|
2
|
+
`,"utf-8")}function S(e){if(!n.existsSync(e))return{};const t=n.readFileSync(e,"utf-8");return t.trim()?JSON.parse(t):{}}function O(e,t){n.mkdirSync(i.dirname(e),{recursive:!0}),n.writeFileSync(e,JSON.stringify(t,null,2)+`
|
|
3
|
+
`,"utf-8")}function Q(){return P(k())}function q(e){return P(_(e),!0)}function Y(e){return D(_(e),!0)}function _(e){return i.join(e,".claude","settings.local.json")}function P(e,t=!1){let o;try{o=S(e)}catch(s){return console.error(`\u26A0 Could not parse ${e}: ${s.message}. Skipping hook registration; you can add it manually.`),!1}o.hooks??={},o.hooks.SessionStart??=[];let r=!1;if(b(o.hooks.SessionStart,a)||(o.hooks.SessionStart.push({matcher:"*",hooks:[{type:"command",command:a}]}),r=!0),t){const s=R(o.hooks.SessionStart);s&&(o.hooks.SessionStart=s,r=!0)}return r?(O(e,o),!0):!1}function b(e,t){return e.some(o=>Array.isArray(o?.hooks)&&o.hooks.some(r=>r?.type==="command"&&r?.command===t))}function y(e){return e?.type==="command"&&e?.command===C}function L(e){return e?.matcher==="clear"&&Array.isArray(e?.hooks)&&e.hooks.length===1&&y(e.hooks[0])&&e.hooks[0].asyncRewake===!0}function R(e){if(e.reduce((r,s)=>r+(Array.isArray(s?.hooks)?s.hooks.filter(y).length:0),0)===1&&e.some(L))return null;const o=e.flatMap(r=>{if(!Array.isArray(r?.hooks))return[r];const s=r.hooks.filter(d=>!y(d));return s.length>0?[{...r,hooks:s}]:[]});return o.push({matcher:"clear",hooks:[{type:"command",command:C,asyncRewake:!0}]}),o}function D(e,t=!1){let o;try{o=S(e)}catch{return!1}const r=o?.hooks?.SessionStart;return Array.isArray(r)?b(r,a)&&(!t||R(r)===null):!1}function Z(){let e;try{e=h()}catch{return!1}const t=e?.hooks?.SessionStart;return Array.isArray(t)?t.some(o=>Array.isArray(o?.hooks)&&o.hooks.some(r=>r?.type==="command"&&r?.command===a)):!1}function ee(){let e;try{e=h()}catch{return!1}const t=e?.hooks?.UserPromptSubmit;return Array.isArray(t)?t.some(o=>Array.isArray(o?.hooks)&&o.hooks.some(r=>r?.type==="command"&&r?.command===m)):!1}function oe(){let e;try{e=h()}catch{return!1}if(!e?.hooks?.SessionStart)return!1;let t=!1;return e.hooks.SessionStart=e.hooks.SessionStart.map(o=>{if(!Array.isArray(o?.hooks))return o;const r=o.hooks.filter(s=>!(s?.type==="command"&&s?.command===a));return r.length!==o.hooks.length?(t=!0,{...o,hooks:r}):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,t&&g(e),t}function re(){let e;try{e=h()}catch(o){return console.error(`\u26A0 Could not parse ${k()}: ${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(r=>r?.type==="command"&&r?.command===m))?!1:(e.hooks.UserPromptSubmit.push({matcher:"*",hooks:[{type:"command",command:m}]}),g(e),!0)}function te(){let e;try{e=h()}catch{return!1}if(!e?.hooks?.UserPromptSubmit)return!1;let t=!1;return e.hooks.UserPromptSubmit=e.hooks.UserPromptSubmit.map(o=>{if(!Array.isArray(o?.hooks))return o;const r=o.hooks.filter(s=>!(s?.type==="command"&&s?.command===m));return r.length!==o.hooks.length?(t=!0,{...o,hooks:r}):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,t&&g(e),t}function se(e=G){try{if(!n.existsSync(e))return!1;const t=n.readFileSync(e,"utf-8");if(!t.trim())return!1;const o=JSON.parse(t);if(!o||typeof o!="object")return!1;const r=o.mcpServers;return!r||typeof r!="object"||Array.isArray(r)?!1:F in r}catch{return!1}}function ne(e=$){try{if(!n.existsSync(e))return!1;const t=n.readFileSync(e,"utf-8"),o=/^\s*\[mcp_servers\.borg\]\s*$/m.test(t),r=new RegExp(`^\\s*${x}\\s*=\\s*"codex"\\s*$`,"m").test(t),s=new RegExp(`^\\s*${j}\\s*=\\s*"1"\\s*$`,"m").test(t);return o&&(r||s)}catch{return!1}}function ie(){return i.join(M,"index.js")}function ae(){try{try{f("claude mcp remove --scope user borg",{stdio:"ignore"})}catch{}f("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 ce(){try{try{f("codex mcp remove borg",{stdio:"ignore"})}catch{}const e=process.env.BORG_API_URL||"https://api.borgmcp.ai",t=N(process.env,"codex");f("codex mcp add borg --env BORG_API_URL="+v(e)+` --env ${x}=codex -- borg-mcp`,{stdio:"inherit",env:{...t,BORG_API_URL:e}})}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 v(e){return`'${e.replace(/'/g,"'\\''")}'`}function E(e,t,o={}){let r;try{r=S(u)}catch(p){return console.error(`\u26A0 Could not parse ${u}: ${p.message}. Skipping Codex hook registration.`),!1}r.hooks??={},r.hooks[e]??=[];const s=r.hooks[e];if(!Array.isArray(s)||s.some(p=>Array.isArray(p?.hooks)&&p.hooks.some(A=>A?.type==="command"&&A?.command===t)))return!1;const c={hooks:[{type:"command",command:t}]};return o.matcher&&(c.matcher=o.matcher),typeof o.timeout=="number"&&(c.hooks[0].timeout=o.timeout),s.push(c),O(u,r),!0}function me(){return E("SessionStart",a,{matcher:"startup|resume",timeout:30})}function ue(){return E("UserPromptSubmit",m,{timeout:10})}function U(e,t,o=u){try{const s=S(o)?.hooks?.[e];return Array.isArray(s)?s.some(d=>Array.isArray(d?.hooks)&&d.hooks.some(c=>c?.type==="command"&&c?.command===t)):!1}catch{return!1}}function de(e=u){return U("SessionStart",a,e)}function fe(e=u){return U("UserPromptSubmit",m,e)}function le(e=B){try{if(!n.existsSync(e))return!1;const t=n.readFileSync(e,"utf-8");if(!t.trim())return!1;const o=JSON.parse(t);if(!o||typeof o!="object")return!1;const r=o.mcp?.borg;return!r||typeof r!="object"?!1:r.type==="local"}catch{return!1}}function he(){try{const e=process.env.BORG_API_URL||"https://api.borgmcp.ai";f(`opencode mcp add borg --env BORG_SESSION=1 --env BORG_AGENT_KIND=opencode --env BORG_OPENCODE=1 --env BORG_API_URL=${v(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{ce as addCodexMcpServer,me as addCodexSessionStartHook,ue as addCodexUserPromptSubmitHook,ae as addMcpServer,he as addOpenCodeMcpServer,q as addProjectSessionStartHook,Q as addSessionStartHook,re as addUserPromptSubmitHook,ie as getBinaryPath,U as isCodexHookRegistered,ne as isCodexMcpServerConfigured,de as isCodexSessionStartHookRegistered,fe as isCodexUserPromptSubmitHookRegistered,se as isMcpServerConfigured,le as isOpenCodeMcpServerConfigured,Y as isProjectSessionStartHookRegistered,Z as isSessionStartHookRegistered,ee as isUserPromptSubmitHookRegistered,oe as removeSessionStartHook,te as removeUserPromptSubmitHook};
|
package/dist/cubes.d.ts
CHANGED
|
@@ -104,21 +104,4 @@ export declare function getCodexWakeTarget(cubeId: string, droneId: string): Pro
|
|
|
104
104
|
* cubes.ts stays free of the codex-remote dependency.
|
|
105
105
|
*/
|
|
106
106
|
export declare function pruneDeadCodexWakeTargets(socketLiveness: (socketPath: string) => boolean | null): Promise<void>;
|
|
107
|
-
export interface LaunchModelRecord {
|
|
108
|
-
/** Model descriptor, e.g. `ollama:qwen3-coder-next` or `claude:claude-opus-4-8`. */
|
|
109
|
-
model: string;
|
|
110
|
-
/** Resolved Ollama host for an ollama model; null for claude. */
|
|
111
|
-
ollamaBaseUrl: string | null;
|
|
112
|
-
}
|
|
113
|
-
export declare function setLaunchModel(cubeId: string, worktreePath: string, record: LaunchModelRecord, file?: string): Promise<void>;
|
|
114
|
-
/**
|
|
115
|
-
* Read the launch model for a worktree. On a worktree-key miss, if `priorDroneId`
|
|
116
|
-
* is supplied (the seat that previously occupied this worktree, derived from
|
|
117
|
-
* cubes.json), a legacy droneId-keyed entry is migrated ON READ to the worktree
|
|
118
|
-
* key and the old key dropped (a move, not a copy — no silent loss). Without a
|
|
119
|
-
* prior droneId the legacy entry is unreachable but LEFT INTACT for a one-time
|
|
120
|
-
* operator re-pin (HARD constraint: never silently destroy a stored entry).
|
|
121
|
-
*/
|
|
122
|
-
export declare function getLaunchModel(cubeId: string, worktreePath: string, priorDroneId?: string | null, file?: string): Promise<LaunchModelRecord | null>;
|
|
123
|
-
export declare function clearLaunchModel(cubeId: string, worktreePath: string, file?: string): Promise<void>;
|
|
124
107
|
//# sourceMappingURL=cubes.d.ts.map
|