borgmcp 1.0.6 → 1.0.8
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 -3
- package/dist/assimilate-cmd.js +39 -511
- package/dist/assimilate-deps.js +3 -177
- package/dist/assimilate-welcome.js +2 -24
- package/dist/auth-env.js +1 -107
- package/dist/auth.js +23 -612
- package/dist/claude.js +11 -281
- package/dist/cli-help.js +29 -50
- package/dist/cli-platform.js +4 -94
- package/dist/codex-app-server.js +4 -228
- package/dist/codex-app-wake.js +2 -122
- package/dist/codex-launch.js +1 -81
- package/dist/codex-remote.js +1 -250
- package/dist/config-utils.js +3 -385
- package/dist/config.js +1 -190
- package/dist/console-prefix.js +1 -86
- package/dist/cube-name.js +1 -65
- package/dist/cubes.js +4 -269
- package/dist/debug.js +1 -71
- package/dist/device-auth.js +1 -167
- package/dist/direct-log.js +1 -11
- package/dist/health-beat.js +1 -168
- package/dist/inbox-monitor.js +1 -129
- package/dist/index.js +26 -1378
- package/dist/lifecycle-log-guard.js +2 -93
- package/dist/list-roles-render.js +6 -39
- package/dist/log-audit.js +3 -186
- package/dist/log-stream.js +9 -848
- package/dist/name-validator.js +1 -22
- package/dist/parse-assimilate-args.js +1 -82
- package/dist/postinstall.js +8 -22
- package/dist/regen-format.js +11 -337
- package/dist/regen.js +5 -83
- package/dist/remote-client.d.ts +4 -7
- package/dist/remote-client.js +1 -695
- package/dist/role-resolver.js +1 -36
- package/dist/role-section.js +8 -208
- package/dist/roster-render.js +3 -96
- package/dist/setup.js +41 -251
- package/dist/shell-escape.js +1 -22
- package/dist/spawn.js +10 -29
- package/dist/stale-version-check.js +1 -102
- package/dist/stream-owner.js +2 -202
- package/dist/stream-status.js +3 -211
- package/dist/subscription-retry.js +1 -23
- package/dist/sync-roles-render.js +3 -118
- package/dist/sync.js +22 -286
- package/dist/templates.js +120 -626
- package/dist/terminal-title.js +1 -68
- package/dist/token-crypto.js +1 -91
- package/dist/token-store.js +1 -222
- package/dist/types.d.ts +0 -5
- package/dist/types.js +0 -5
- package/dist/version.js +2 -78
- package/dist/worktree-lifecycle.js +2 -173
- package/package.json +12 -2
- package/dist/assimilate-cmd.d.ts.map +0 -1
- package/dist/assimilate-cmd.js.map +0 -1
- package/dist/assimilate-deps.d.ts.map +0 -1
- package/dist/assimilate-deps.js.map +0 -1
- package/dist/assimilate-welcome.d.ts.map +0 -1
- package/dist/assimilate-welcome.js.map +0 -1
- package/dist/auth-env.d.ts.map +0 -1
- package/dist/auth-env.js.map +0 -1
- package/dist/auth.d.ts.map +0 -1
- package/dist/auth.js.map +0 -1
- package/dist/claude.d.ts.map +0 -1
- package/dist/claude.js.map +0 -1
- package/dist/cli-help.d.ts.map +0 -1
- package/dist/cli-help.js.map +0 -1
- package/dist/cli-platform.d.ts.map +0 -1
- package/dist/cli-platform.js.map +0 -1
- package/dist/codex-app-server.d.ts.map +0 -1
- package/dist/codex-app-server.js.map +0 -1
- package/dist/codex-app-wake.d.ts.map +0 -1
- package/dist/codex-app-wake.js.map +0 -1
- package/dist/codex-launch.d.ts.map +0 -1
- package/dist/codex-launch.js.map +0 -1
- package/dist/codex-remote.d.ts.map +0 -1
- package/dist/codex-remote.js.map +0 -1
- package/dist/config-utils.d.ts.map +0 -1
- package/dist/config-utils.js.map +0 -1
- package/dist/config.d.ts.map +0 -1
- package/dist/config.js.map +0 -1
- package/dist/console-prefix.d.ts.map +0 -1
- package/dist/console-prefix.js.map +0 -1
- package/dist/cube-name.d.ts.map +0 -1
- package/dist/cube-name.js.map +0 -1
- package/dist/cubes.d.ts.map +0 -1
- package/dist/cubes.js.map +0 -1
- package/dist/debug.d.ts.map +0 -1
- package/dist/debug.js.map +0 -1
- package/dist/device-auth.d.ts.map +0 -1
- package/dist/device-auth.js.map +0 -1
- package/dist/direct-log.d.ts.map +0 -1
- package/dist/direct-log.js.map +0 -1
- package/dist/health-beat.d.ts.map +0 -1
- package/dist/health-beat.js.map +0 -1
- package/dist/inbox-monitor.d.ts.map +0 -1
- package/dist/inbox-monitor.js.map +0 -1
- package/dist/index.d.ts.map +0 -1
- package/dist/index.js.map +0 -1
- package/dist/lifecycle-log-guard.d.ts.map +0 -1
- package/dist/lifecycle-log-guard.js.map +0 -1
- package/dist/list-roles-render.d.ts.map +0 -1
- package/dist/list-roles-render.js.map +0 -1
- package/dist/log-audit.d.ts.map +0 -1
- package/dist/log-audit.js.map +0 -1
- package/dist/log-stream.d.ts.map +0 -1
- package/dist/log-stream.js.map +0 -1
- package/dist/name-validator.d.ts.map +0 -1
- package/dist/name-validator.js.map +0 -1
- package/dist/parse-assimilate-args.d.ts.map +0 -1
- package/dist/parse-assimilate-args.js.map +0 -1
- package/dist/postinstall.d.ts.map +0 -1
- package/dist/postinstall.js.map +0 -1
- package/dist/regen-format.d.ts.map +0 -1
- package/dist/regen-format.js.map +0 -1
- package/dist/regen.d.ts.map +0 -1
- package/dist/regen.js.map +0 -1
- package/dist/remote-client.d.ts.map +0 -1
- package/dist/remote-client.js.map +0 -1
- package/dist/role-resolver.d.ts.map +0 -1
- package/dist/role-resolver.js.map +0 -1
- package/dist/role-section.d.ts.map +0 -1
- package/dist/role-section.js.map +0 -1
- package/dist/roster-render.d.ts.map +0 -1
- package/dist/roster-render.js.map +0 -1
- package/dist/setup.d.ts.map +0 -1
- package/dist/setup.js.map +0 -1
- package/dist/shell-escape.d.ts.map +0 -1
- package/dist/shell-escape.js.map +0 -1
- package/dist/spawn.d.ts.map +0 -1
- package/dist/spawn.js.map +0 -1
- package/dist/stale-version-check.d.ts.map +0 -1
- package/dist/stale-version-check.js.map +0 -1
- package/dist/stream-owner.d.ts.map +0 -1
- package/dist/stream-owner.js.map +0 -1
- package/dist/stream-status.d.ts.map +0 -1
- package/dist/stream-status.js.map +0 -1
- package/dist/subscription-retry.d.ts.map +0 -1
- package/dist/subscription-retry.js.map +0 -1
- package/dist/sync-roles-render.d.ts.map +0 -1
- package/dist/sync-roles-render.js.map +0 -1
- package/dist/sync.d.ts.map +0 -1
- package/dist/sync.js.map +0 -1
- package/dist/templates.d.ts.map +0 -1
- package/dist/templates.js.map +0 -1
- package/dist/terminal-title.d.ts.map +0 -1
- package/dist/terminal-title.js.map +0 -1
- package/dist/token-crypto.d.ts.map +0 -1
- package/dist/token-crypto.js.map +0 -1
- package/dist/token-store.d.ts.map +0 -1
- package/dist/token-store.js.map +0 -1
- package/dist/types.d.ts.map +0 -1
- package/dist/types.js.map +0 -1
- package/dist/version.d.ts.map +0 -1
- package/dist/version.js.map +0 -1
- package/dist/worktree-lifecycle.d.ts.map +0 -1
- package/dist/worktree-lifecycle.js.map +0 -1
package/dist/assimilate-deps.js
CHANGED
|
@@ -1,177 +1,3 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
* client modules (remote-client HTTP, cubes.ts persistence, auth.ts
|
|
5
|
-
* setup wizard, terminal-title helper).
|
|
6
|
-
*
|
|
7
|
-
* Test code never calls this — tests construct stub deps directly
|
|
8
|
-
* (see `client/__tests__/assimilate-cmd.test.ts:makeStubDeps`).
|
|
9
|
-
*/
|
|
10
|
-
import { spawnSync, spawn as spawnChild } from 'node:child_process';
|
|
11
|
-
import { existsSync } from 'node:fs';
|
|
12
|
-
import { hostname as osHostname } from 'node:os';
|
|
13
|
-
import { createInterface } from 'node:readline/promises';
|
|
14
|
-
import { API_URL, getValidToken, listCubes as remoteListCubes, getCube as remoteGetCube, createCube as remoteCreateCube, assimilate as remoteAssimilate, listTemplates as remoteListTemplates, } from './remote-client.js';
|
|
15
|
-
import { findProjectRoot as cubesFindProjectRoot, getActiveCube as cubesGetActive, setActiveCube as cubesSetActive, inboxPathForDrone, setCodexWakeTarget, } from './cubes.js';
|
|
16
|
-
import { authenticateWithGoogle } from './auth.js';
|
|
17
|
-
import { setTerminalTitle as setTitle } from './terminal-title.js';
|
|
18
|
-
import { defaultCliChoiceDeps, resolveCliChoice } from './cli-platform.js';
|
|
19
|
-
import { prepareCodexRemoteLaunch, defaultCodexRemoteDeps } from './codex-remote.js';
|
|
20
|
-
import { findLoadedCodexThread } from './codex-app-server.js';
|
|
21
|
-
export function buildDefaultAssimilateDeps() {
|
|
22
|
-
return {
|
|
23
|
-
runSync: (cmd, args, cwd) => {
|
|
24
|
-
const r = spawnSync(cmd, args, { cwd, encoding: 'utf-8' });
|
|
25
|
-
return {
|
|
26
|
-
status: r.status,
|
|
27
|
-
stdout: r.stdout ?? '',
|
|
28
|
-
stderr: r.stderr ?? '',
|
|
29
|
-
};
|
|
30
|
-
},
|
|
31
|
-
pathExists: (p) => existsSync(p),
|
|
32
|
-
cwd: () => process.cwd(),
|
|
33
|
-
chdir: (p) => process.chdir(p),
|
|
34
|
-
exec: (cmd, args, cwd, env) => new Promise((resolveExit, rejectExit) => {
|
|
35
|
-
const child = spawnChild(cmd, args, {
|
|
36
|
-
cwd,
|
|
37
|
-
stdio: 'inherit',
|
|
38
|
-
shell: false,
|
|
39
|
-
env: env ? { ...process.env, ...env } : process.env,
|
|
40
|
-
});
|
|
41
|
-
child.on('error', rejectExit);
|
|
42
|
-
child.on('exit', (code) => resolveExit(code ?? 0));
|
|
43
|
-
}),
|
|
44
|
-
stderr: (line) => process.stderr.write(line),
|
|
45
|
-
stdout: (line) => process.stdout.write(line),
|
|
46
|
-
prompt: async (message) => {
|
|
47
|
-
const rl = createInterface({ input: process.stdin, output: process.stdout });
|
|
48
|
-
try {
|
|
49
|
-
return await rl.question(message);
|
|
50
|
-
}
|
|
51
|
-
finally {
|
|
52
|
-
rl.close();
|
|
53
|
-
}
|
|
54
|
-
},
|
|
55
|
-
isTTY: () => process.stdin.isTTY === true,
|
|
56
|
-
getHostname: () => osHostname(),
|
|
57
|
-
setTerminalTitle: (label, cubeName) => {
|
|
58
|
-
setTitle({ label, cubeName }, cubeName);
|
|
59
|
-
},
|
|
60
|
-
getActiveCube: () => cubesGetActive(),
|
|
61
|
-
setActiveCube: (a) => cubesSetActive(a),
|
|
62
|
-
findProjectRoot: (cwd) => cubesFindProjectRoot(cwd),
|
|
63
|
-
getCachedAuth: async () => {
|
|
64
|
-
try {
|
|
65
|
-
const token = await getValidToken();
|
|
66
|
-
return { token, apiUrl: API_URL };
|
|
67
|
-
}
|
|
68
|
-
catch {
|
|
69
|
-
return null;
|
|
70
|
-
}
|
|
71
|
-
},
|
|
72
|
-
runSetup: async () => {
|
|
73
|
-
await authenticateWithGoogle();
|
|
74
|
-
const token = await getValidToken();
|
|
75
|
-
return { token, apiUrl: API_URL };
|
|
76
|
-
},
|
|
77
|
-
listCubes: async (_apiUrl, _token) => {
|
|
78
|
-
const { cubes } = await remoteListCubes();
|
|
79
|
-
return cubes.map((c) => ({ id: c.id, name: c.name }));
|
|
80
|
-
},
|
|
81
|
-
getCube: async (_apiUrl, _token, cubeId) => {
|
|
82
|
-
// BUG-2 fix (v0.9.2): remote-client now unwraps the server's
|
|
83
|
-
// `{cube, roles, drones}` shape, so the returned object is
|
|
84
|
-
// already flat with id/name/roles at the top level.
|
|
85
|
-
const cube = await remoteGetCube(cubeId);
|
|
86
|
-
return { id: cube.id, name: cube.name, roles: cube.roles };
|
|
87
|
-
},
|
|
88
|
-
createCube: async (_apiUrl, _token, params) => {
|
|
89
|
-
const cube = await remoteCreateCube(params.name, '', params.template ? { template: params.template } : undefined);
|
|
90
|
-
return { id: cube.id, name: cube.name, roles: cube.roles };
|
|
91
|
-
},
|
|
92
|
-
assimilate: async (_apiUrl, _token, params) => {
|
|
93
|
-
const result = await remoteAssimilate({ cube_id: params.cube_id, role_id: params.role_id }, undefined, params.hostname ?? null);
|
|
94
|
-
return {
|
|
95
|
-
cube_id: result.cube.id,
|
|
96
|
-
drone_id: result.drone.id,
|
|
97
|
-
drone_label: result.drone.label,
|
|
98
|
-
session_token: result.sessionToken,
|
|
99
|
-
role_id: result.role.id,
|
|
100
|
-
};
|
|
101
|
-
},
|
|
102
|
-
listTemplates: async (_apiUrl, _token) => {
|
|
103
|
-
const { templates } = await remoteListTemplates();
|
|
104
|
-
return templates.map((t) => ({ name: t.name, description: t.description }));
|
|
105
|
-
},
|
|
106
|
-
// CR-PE-F1 wiring (Phase E merge brought this seam in): compute the
|
|
107
|
-
// inbox file path used by the borg-inbox-monitor command in step 8
|
|
108
|
-
// kickoff. Pure helper from cubes.ts; same computation claude.ts uses.
|
|
109
|
-
getInboxPath: (cubeId, droneId) => inboxPathForDrone(cubeId, droneId),
|
|
110
|
-
// BUG-5 / v0.9.3 wiring: spawn `borg-mcp` as a stdio child, send
|
|
111
|
-
// an initialize request, await a response (or timeout), then kill
|
|
112
|
-
// the child. Probe success indicates the MCP server starts cleanly
|
|
113
|
-
// and tools/list is reachable; failure means the launched Claude
|
|
114
|
-
// session will hit the race and need the kickoff prompt's
|
|
115
|
-
// ToolSearch recovery clause.
|
|
116
|
-
probeMcpReady: () => new Promise((resolveProbe) => {
|
|
117
|
-
const child = spawnChild('borg-mcp', [], { stdio: ['pipe', 'pipe', 'pipe'], shell: false });
|
|
118
|
-
let buffer = '';
|
|
119
|
-
let settled = false;
|
|
120
|
-
const settle = (result) => {
|
|
121
|
-
if (settled)
|
|
122
|
-
return;
|
|
123
|
-
settled = true;
|
|
124
|
-
try {
|
|
125
|
-
child.kill('SIGTERM');
|
|
126
|
-
}
|
|
127
|
-
catch { /* ignore */ }
|
|
128
|
-
resolveProbe(result);
|
|
129
|
-
};
|
|
130
|
-
const timeout = setTimeout(() => settle(false), 2000);
|
|
131
|
-
child.on('error', () => { clearTimeout(timeout); settle(false); });
|
|
132
|
-
child.on('exit', () => { clearTimeout(timeout); settle(settled); });
|
|
133
|
-
child.stdout?.on('data', (chunk) => {
|
|
134
|
-
buffer += chunk.toString('utf-8');
|
|
135
|
-
// initialize response contains "result" with "protocolVersion"
|
|
136
|
-
// and "capabilities". Light parse: line-buffered JSON-RPC.
|
|
137
|
-
for (const line of buffer.split('\n')) {
|
|
138
|
-
if (line.includes('"protocolVersion"') && line.includes('"result"')) {
|
|
139
|
-
clearTimeout(timeout);
|
|
140
|
-
settle(true);
|
|
141
|
-
return;
|
|
142
|
-
}
|
|
143
|
-
}
|
|
144
|
-
});
|
|
145
|
-
const initReq = JSON.stringify({
|
|
146
|
-
jsonrpc: '2.0',
|
|
147
|
-
id: 1,
|
|
148
|
-
method: 'initialize',
|
|
149
|
-
params: {
|
|
150
|
-
protocolVersion: '2024-11-05',
|
|
151
|
-
capabilities: {},
|
|
152
|
-
clientInfo: { name: 'borg-assimilate-probe', version: '0.9.3' },
|
|
153
|
-
},
|
|
154
|
-
});
|
|
155
|
-
try {
|
|
156
|
-
child.stdin?.write(initReq + '\n');
|
|
157
|
-
}
|
|
158
|
-
catch {
|
|
159
|
-
clearTimeout(timeout);
|
|
160
|
-
settle(false);
|
|
161
|
-
}
|
|
162
|
-
}),
|
|
163
|
-
resolveCli: (explicit) => resolveCliChoice(explicit, defaultCliChoiceDeps(async (message) => {
|
|
164
|
-
const rl = createInterface({ input: process.stdin, output: process.stdout });
|
|
165
|
-
try {
|
|
166
|
-
return await rl.question(message);
|
|
167
|
-
}
|
|
168
|
-
finally {
|
|
169
|
-
rl.close();
|
|
170
|
-
}
|
|
171
|
-
}, () => process.stdin.isTTY === true)),
|
|
172
|
-
prepareCodexRemoteLaunch: () => prepareCodexRemoteLaunch(defaultCodexRemoteDeps()),
|
|
173
|
-
setCodexWakeTarget,
|
|
174
|
-
findLoadedCodexThread,
|
|
175
|
-
};
|
|
176
|
-
}
|
|
177
|
-
//# sourceMappingURL=assimilate-deps.js.map
|
|
1
|
+
import{spawnSync as m,spawn as l}from"node:child_process";import{existsSync as f}from"node:fs";import{hostname as b}from"node:os";import{createInterface as u}from"node:readline/promises";import{API_URL as d,getValidToken as p,listCubes as h,getCube as C,createCube as T,assimilate as y,listTemplates as w}from"./remote-client.js";import{findProjectRoot as _,getActiveCube as g,setActiveCube as k,inboxPathForDrone as x,setCodexWakeTarget as v}from"./cubes.js";import{authenticateWithGoogle as A}from"./auth.js";import{setTerminalTitle as R}from"./terminal-title.js";import{defaultCliChoiceDeps as P,resolveCliChoice as S}from"./cli-platform.js";import{prepareCodexRemoteLaunch as U,defaultCodexRemoteDeps as L}from"./codex-remote.js";import{findLoadedCodexThread as D}from"./codex-app-server.js";function z(){return{runSync:(e,s,o)=>{const t=m(e,s,{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),exec:(e,s,o,t)=>new Promise((r,i)=>{const a=l(e,s,{cwd:o,stdio:"inherit",shell:!1,env:t?{...process.env,...t}:process.env});a.on("error",i),a.on("exit",n=>r(n??0))}),stderr:e=>process.stderr.write(e),stdout:e=>process.stdout.write(e),prompt:async e=>{const s=u({input:process.stdin,output:process.stdout});try{return await s.question(e)}finally{s.close()}},isTTY:()=>process.stdin.isTTY===!0,getHostname:()=>b(),setTerminalTitle:(e,s)=>{R({label:e,cubeName:s},s)},getActiveCube:()=>g(),setActiveCube:e=>k(e),findProjectRoot:e=>_(e),getCachedAuth:async()=>{try{return{token:await p(),apiUrl:d}}catch{return null}},runSetup:async()=>(await A(),{token:await p(),apiUrl:d}),listCubes:async(e,s)=>{const{cubes:o}=await h();return o.map(t=>({id:t.id,name:t.name}))},getCube:async(e,s,o)=>{const t=await C(o);return{id:t.id,name:t.name,roles:t.roles}},createCube:async(e,s,o)=>{const t=await T(o.name,"",o.template?{template:o.template}:void 0);return{id:t.id,name:t.name,roles:t.roles}},assimilate:async(e,s,o)=>{const t=await y({cube_id:o.cube_id,role_id:o.role_id},void 0,o.hostname??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}},listTemplates:async(e,s)=>{const{templates:o}=await w();return o.map(t=>({name:t.name,description:t.description}))},getInboxPath:(e,s)=>x(e,s),probeMcpReady:()=>new Promise(e=>{const s=l("borg-mcp",[],{stdio:["pipe","pipe","pipe"],shell:!1});let o="",t=!1;const r=n=>{if(!t){t=!0;try{s.kill("SIGTERM")}catch{}e(n)}},i=setTimeout(()=>r(!1),2e3);s.on("error",()=>{clearTimeout(i),r(!1)}),s.on("exit",()=>{clearTimeout(i),r(t)}),s.stdout?.on("data",n=>{o+=n.toString("utf-8");for(const c of o.split(`
|
|
2
|
+
`))if(c.includes('"protocolVersion"')&&c.includes('"result"')){clearTimeout(i),r(!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{s.stdin?.write(a+`
|
|
3
|
+
`)}catch{clearTimeout(i),r(!1)}}),resolveCli:e=>S(e,P(async s=>{const o=u({input:process.stdin,output:process.stdout});try{return await o.question(s)}finally{o.close()}},()=>process.stdin.isTTY===!0)),prepareCodexRemoteLaunch:()=>U(L()),setCodexWakeTarget:v,findLoadedCodexThread:D}}export{z as buildDefaultAssimilateDeps};
|
|
@@ -1,24 +1,2 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
// the user at borg:role / borg:cube / borg:roster / borg:regen /
|
|
4
|
-
// borg:read-log rather than embedding role-specific text — so cubes
|
|
5
|
-
// using non-default templates (writers-room, ops, etc.) render
|
|
6
|
-
// identically to software-dev cubes.
|
|
7
|
-
//
|
|
8
|
-
// Color: ANSI green on the ✓ glyph only; gated on the caller's useColor
|
|
9
|
-
// boolean (computed from process.stdout.isTTY && !NO_COLOR && !CI in the
|
|
10
|
-
// assimilate-cmd call site). Body text carries no ANSI by design — color
|
|
11
|
-
// is hierarchy-cueing, not decoration.
|
|
12
|
-
const GREEN = '\x1b[32m';
|
|
13
|
-
const RESET = '\x1b[0m';
|
|
14
|
-
export function renderAssimilationWelcome(roleName, cubeName, useColor) {
|
|
15
|
-
const check = useColor ? `${GREEN}✓${RESET}` : '✓';
|
|
16
|
-
return [
|
|
17
|
-
`${check} Joined as \`${roleName}\` in cube \`${cubeName}\`.`,
|
|
18
|
-
``,
|
|
19
|
-
`Next: run \`borg:regen\` inside Claude to see your cube.`,
|
|
20
|
-
`You're set up — your team can now see you in the cube.`,
|
|
21
|
-
``,
|
|
22
|
-
].join('\n');
|
|
23
|
-
}
|
|
24
|
-
//# sourceMappingURL=assimilate-welcome.js.map
|
|
1
|
+
const c="\x1B[32m",t="\x1B[0m";function r(e,n,o){return[`${o?`${c}\u2713${t}`:"\u2713"} Joined as \`${e}\` in cube \`${n}\`.`,"","Next: run `borg:regen` inside Claude to see your cube.","You're set up \u2014 your team can now see you in the cube.",""].join(`
|
|
2
|
+
`)}export{r as renderAssimilationWelcome};
|
package/dist/auth-env.js
CHANGED
|
@@ -1,107 +1 @@
|
|
|
1
|
-
|
|
2
|
-
* gh#557 — environment / capability detection for remote-terminal auth.
|
|
3
|
-
*
|
|
4
|
-
* The default OAuth path (auth.ts) opens a browser and listens on a
|
|
5
|
-
* localhost-loopback callback server. Both assumptions break on a remote
|
|
6
|
-
* terminal: there's no browser to open, and the loopback URL Google would
|
|
7
|
-
* redirect to is unreachable from the user's actual browser on another
|
|
8
|
-
* machine. These pure helpers decide when to fall back to the RFC 8628
|
|
9
|
-
* device-grant flow (no browser) and when the OS keychain is unavailable
|
|
10
|
-
* (so a keychain-less token store must be used instead).
|
|
11
|
-
*
|
|
12
|
-
* Kept free of side effects (env + platform are injected) so the decision
|
|
13
|
-
* logic is unit-testable without a real display / SSH session / keychain.
|
|
14
|
-
*/
|
|
15
|
-
import { AsyncEntry } from '@napi-rs/keyring';
|
|
16
|
-
/**
|
|
17
|
-
* A BORG_* toggle is "on" only when present and not one of the falsy
|
|
18
|
-
* spellings. Mirrors how the rest of the client reads boolean env vars:
|
|
19
|
-
* an unset var and the explicit "0"/"false"/"" spellings are all off.
|
|
20
|
-
*/
|
|
21
|
-
function envToggleOn(value) {
|
|
22
|
-
if (value === undefined)
|
|
23
|
-
return false;
|
|
24
|
-
const v = value.trim().toLowerCase();
|
|
25
|
-
return v !== '' && v !== '0' && v !== 'false' && v !== 'no';
|
|
26
|
-
}
|
|
27
|
-
/**
|
|
28
|
-
* Decide whether the current environment lacks a usable local browser, so
|
|
29
|
-
* the loopback OAuth flow can't work and the device-grant flow should be
|
|
30
|
-
* used instead.
|
|
31
|
-
*
|
|
32
|
-
* Decision order (first match wins):
|
|
33
|
-
* 1. BORG_FORCE_BROWSER on → false (escape hatch: operator has an
|
|
34
|
-
* SSH-forwarded / X-forwarded browser and wants the loopback flow)
|
|
35
|
-
* 2. BORG_NO_BROWSER on → true (explicit opt-in to device flow)
|
|
36
|
-
* 3. SSH session → true (remote terminal — even on a Mac the
|
|
37
|
-
* browser that opens is on the *server*, unreachable to the user)
|
|
38
|
-
* 4. container marker → true (headless by construction)
|
|
39
|
-
* 5. Linux without any display (no DISPLAY, no WAYLAND_DISPLAY) → true
|
|
40
|
-
* 6. otherwise → false (desktop macOS/Windows, or Linux with
|
|
41
|
-
* a display — the loopback flow works)
|
|
42
|
-
*
|
|
43
|
-
* The `--no-browser` / `--device` CLI flag is surfaced by the caller as
|
|
44
|
-
* BORG_NO_BROWSER (or by passing an env with it set) so there's a single
|
|
45
|
-
* decision funnel.
|
|
46
|
-
*/
|
|
47
|
-
export function isNoBrowserEnv(probe) {
|
|
48
|
-
const env = probe?.env ?? process.env;
|
|
49
|
-
const platform = probe?.platform ?? process.platform;
|
|
50
|
-
// 1. Explicit force-browser escape hatch wins over every no-browser signal.
|
|
51
|
-
if (envToggleOn(env.BORG_FORCE_BROWSER))
|
|
52
|
-
return false;
|
|
53
|
-
// 2. Explicit opt-in to the no-browser/device flow.
|
|
54
|
-
if (envToggleOn(env.BORG_NO_BROWSER))
|
|
55
|
-
return true;
|
|
56
|
-
// 3. SSH session — the terminal is remote, so any browser we open is on
|
|
57
|
-
// the far end and the loopback redirect is unreachable to the user.
|
|
58
|
-
if (env.SSH_TTY || env.SSH_CONNECTION || env.SSH_CLIENT)
|
|
59
|
-
return true;
|
|
60
|
-
// 4. Container (systemd/Docker/podman set `container=`); headless by build.
|
|
61
|
-
if (env.container)
|
|
62
|
-
return true;
|
|
63
|
-
// 5. Headless Linux: no X11 and no Wayland display = no browser to open.
|
|
64
|
-
if (platform === 'linux' && !env.DISPLAY && !env.WAYLAND_DISPLAY)
|
|
65
|
-
return true;
|
|
66
|
-
// 6. Desktop (macOS/Windows) or Linux-with-display: loopback flow is fine.
|
|
67
|
-
return false;
|
|
68
|
-
}
|
|
69
|
-
/**
|
|
70
|
-
* The probe round-trip used to decide whether the OS keychain is usable.
|
|
71
|
-
* Performs a real set/get/delete against a throwaway account so a missing
|
|
72
|
-
* Secret Service (headless Linux), a locked keychain, or any other backend
|
|
73
|
-
* failure surfaces as a thrown error rather than a later mid-auth crash.
|
|
74
|
-
*/
|
|
75
|
-
async function defaultKeyringRoundTrip() {
|
|
76
|
-
const PROBE_SERVICE = 'borg-mcp';
|
|
77
|
-
const PROBE_ACCOUNT = '__borg_keyring_probe__';
|
|
78
|
-
const entry = new AsyncEntry(PROBE_SERVICE, PROBE_ACCOUNT);
|
|
79
|
-
await entry.setPassword('probe');
|
|
80
|
-
await entry.getPassword();
|
|
81
|
-
// Best-effort cleanup; a failed delete still proves the keychain works.
|
|
82
|
-
try {
|
|
83
|
-
await entry.deletePassword();
|
|
84
|
-
}
|
|
85
|
-
catch {
|
|
86
|
-
/* leave the probe entry — its presence is harmless */
|
|
87
|
-
}
|
|
88
|
-
}
|
|
89
|
-
/**
|
|
90
|
-
* Returns true when the OS keychain can be written to and read from. The
|
|
91
|
-
* round-trip is injectable so callers/tests can supply a deterministic
|
|
92
|
-
* probe; in production the default probe touches the real keychain.
|
|
93
|
-
*
|
|
94
|
-
* Any thrown error from the probe (no Secret Service, locked keychain,
|
|
95
|
-
* permission denial) is treated as "unavailable" — the caller then selects
|
|
96
|
-
* the encrypted-file fallback store.
|
|
97
|
-
*/
|
|
98
|
-
export async function isKeyringAvailable(roundTrip = defaultKeyringRoundTrip) {
|
|
99
|
-
try {
|
|
100
|
-
await roundTrip();
|
|
101
|
-
return true;
|
|
102
|
-
}
|
|
103
|
-
catch {
|
|
104
|
-
return false;
|
|
105
|
-
}
|
|
106
|
-
}
|
|
107
|
-
//# sourceMappingURL=auth-env.js.map
|
|
1
|
+
import{AsyncEntry as o}from"@napi-rs/keyring";function n(t){if(t===void 0)return!1;const r=t.trim().toLowerCase();return r!==""&&r!=="0"&&r!=="false"&&r!=="no"}function i(t){const r=t?.env??process.env,e=t?.platform??process.platform;return n(r.BORG_FORCE_BROWSER)?!1:!!(n(r.BORG_NO_BROWSER)||r.SSH_TTY||r.SSH_CONNECTION||r.SSH_CLIENT||r.container||e==="linux"&&!r.DISPLAY&&!r.WAYLAND_DISPLAY)}async function s(){const t="borg-mcp",r="__borg_keyring_probe__",e=new o(t,r);await e.setPassword("probe"),await e.getPassword();try{await e.deletePassword()}catch{}}async function u(t=s){try{return await t(),!0}catch{return!1}}export{u as isKeyringAvailable,i as isNoBrowserEnv};
|