borgmcp 1.0.6 → 1.0.7
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/assimilate-cmd.js +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.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 +36 -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.js +0 -5
- package/dist/version.js +2 -78
- package/dist/worktree-lifecycle.js +2 -173
- package/package.json +11 -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/codex-app-server.js
CHANGED
|
@@ -1,228 +1,4 @@
|
|
|
1
|
-
import
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
socket = null;
|
|
6
|
-
buffer = Buffer.alloc(0);
|
|
7
|
-
handshaken = false;
|
|
8
|
-
nextId = 1;
|
|
9
|
-
pending = new Map();
|
|
10
|
-
constructor(socketPath) {
|
|
11
|
-
this.socketPath = socketPath;
|
|
12
|
-
}
|
|
13
|
-
async connect() {
|
|
14
|
-
if (this.socket)
|
|
15
|
-
return;
|
|
16
|
-
this.socket = net.createConnection(this.socketPath);
|
|
17
|
-
this.socket.on('data', (data) => {
|
|
18
|
-
this.buffer = Buffer.concat([this.buffer, data]);
|
|
19
|
-
this.parseIncoming();
|
|
20
|
-
});
|
|
21
|
-
this.socket.on('error', (error) => {
|
|
22
|
-
for (const pending of this.pending.values())
|
|
23
|
-
pending.reject(error);
|
|
24
|
-
this.pending.clear();
|
|
25
|
-
});
|
|
26
|
-
await new Promise((resolve, reject) => {
|
|
27
|
-
const onError = (error) => reject(error);
|
|
28
|
-
this.socket.once('error', onError);
|
|
29
|
-
this.socket.once('connect', () => {
|
|
30
|
-
this.socket.off('error', onError);
|
|
31
|
-
const key = crypto.randomBytes(16).toString('base64');
|
|
32
|
-
this.socket.write([
|
|
33
|
-
'GET / HTTP/1.1',
|
|
34
|
-
'Host: localhost',
|
|
35
|
-
'Upgrade: websocket',
|
|
36
|
-
'Connection: Upgrade',
|
|
37
|
-
`Sec-WebSocket-Key: ${key}`,
|
|
38
|
-
'Sec-WebSocket-Version: 13',
|
|
39
|
-
'',
|
|
40
|
-
'',
|
|
41
|
-
].join('\r\n'));
|
|
42
|
-
resolve();
|
|
43
|
-
});
|
|
44
|
-
});
|
|
45
|
-
await this.waitForHandshake();
|
|
46
|
-
await this.request('initialize', {
|
|
47
|
-
clientInfo: { name: 'borgmcp', version: '0' },
|
|
48
|
-
capabilities: { experimentalApi: true, requestAttestation: false },
|
|
49
|
-
});
|
|
50
|
-
this.notify('initialized', {});
|
|
51
|
-
}
|
|
52
|
-
close() {
|
|
53
|
-
this.socket?.end();
|
|
54
|
-
this.socket = null;
|
|
55
|
-
}
|
|
56
|
-
async loadedThreadIds() {
|
|
57
|
-
const result = await this.request('thread/loaded/list', {});
|
|
58
|
-
return Array.isArray(result?.data) ? result.data.filter((id) => typeof id === 'string') : [];
|
|
59
|
-
}
|
|
60
|
-
async readThread(threadId) {
|
|
61
|
-
const result = await this.request('thread/read', { threadId, includeTurns: false });
|
|
62
|
-
const thread = result?.thread;
|
|
63
|
-
if (!thread || typeof thread.id !== 'string')
|
|
64
|
-
return null;
|
|
65
|
-
return {
|
|
66
|
-
id: thread.id,
|
|
67
|
-
cwd: thread.cwd,
|
|
68
|
-
preview: thread.preview,
|
|
69
|
-
status: thread.status,
|
|
70
|
-
updatedAt: thread.updatedAt,
|
|
71
|
-
};
|
|
72
|
-
}
|
|
73
|
-
async startTurn(threadId, text) {
|
|
74
|
-
await this.request('turn/start', {
|
|
75
|
-
threadId,
|
|
76
|
-
input: [{ type: 'text', text, text_elements: [] }],
|
|
77
|
-
});
|
|
78
|
-
}
|
|
79
|
-
waitForHandshake() {
|
|
80
|
-
return new Promise((resolve, reject) => {
|
|
81
|
-
const started = Date.now();
|
|
82
|
-
const tick = () => {
|
|
83
|
-
if (this.handshaken)
|
|
84
|
-
return resolve();
|
|
85
|
-
if (Date.now() - started > 5_000)
|
|
86
|
-
return reject(new Error('Timed out waiting for Codex app-server websocket handshake'));
|
|
87
|
-
setTimeout(tick, 25);
|
|
88
|
-
};
|
|
89
|
-
tick();
|
|
90
|
-
});
|
|
91
|
-
}
|
|
92
|
-
request(method, params) {
|
|
93
|
-
const id = this.nextId++;
|
|
94
|
-
this.writeJson({ id, method, params });
|
|
95
|
-
return new Promise((resolve, reject) => {
|
|
96
|
-
this.pending.set(id, { resolve, reject });
|
|
97
|
-
setTimeout(() => {
|
|
98
|
-
if (!this.pending.delete(id))
|
|
99
|
-
return;
|
|
100
|
-
reject(new Error(`Timed out waiting for Codex app-server response to ${method}`));
|
|
101
|
-
}, 5_000);
|
|
102
|
-
});
|
|
103
|
-
}
|
|
104
|
-
notify(method, params) {
|
|
105
|
-
this.writeJson({ method, params });
|
|
106
|
-
}
|
|
107
|
-
writeJson(value) {
|
|
108
|
-
if (!this.socket)
|
|
109
|
-
throw new Error('Codex app-server socket is not connected');
|
|
110
|
-
this.socket.write(encodeWebSocketTextFrame(JSON.stringify(value)));
|
|
111
|
-
}
|
|
112
|
-
parseIncoming() {
|
|
113
|
-
if (!this.handshaken) {
|
|
114
|
-
const end = this.buffer.indexOf('\r\n\r\n');
|
|
115
|
-
if (end < 0)
|
|
116
|
-
return;
|
|
117
|
-
this.buffer = this.buffer.slice(end + 4);
|
|
118
|
-
this.handshaken = true;
|
|
119
|
-
}
|
|
120
|
-
while (this.buffer.length >= 2) {
|
|
121
|
-
const parsed = decodeWebSocketTextFrame(this.buffer);
|
|
122
|
-
if (!parsed)
|
|
123
|
-
return;
|
|
124
|
-
this.buffer = this.buffer.slice(parsed.consumed);
|
|
125
|
-
let message;
|
|
126
|
-
try {
|
|
127
|
-
message = JSON.parse(parsed.text);
|
|
128
|
-
}
|
|
129
|
-
catch {
|
|
130
|
-
continue;
|
|
131
|
-
}
|
|
132
|
-
if (typeof message.id === 'number' && this.pending.has(message.id)) {
|
|
133
|
-
const pending = this.pending.get(message.id);
|
|
134
|
-
this.pending.delete(message.id);
|
|
135
|
-
if (message.error) {
|
|
136
|
-
pending.reject(new Error(message.error.message ?? 'Codex app-server request failed'));
|
|
137
|
-
}
|
|
138
|
-
else {
|
|
139
|
-
pending.resolve(message.result);
|
|
140
|
-
}
|
|
141
|
-
}
|
|
142
|
-
}
|
|
143
|
-
}
|
|
144
|
-
}
|
|
145
|
-
export async function findLoadedCodexThread(options) {
|
|
146
|
-
const client = new CodexAppServerClient(options.socketPath);
|
|
147
|
-
await client.connect();
|
|
148
|
-
try {
|
|
149
|
-
const ids = await client.loadedThreadIds();
|
|
150
|
-
let best = null;
|
|
151
|
-
for (const id of ids) {
|
|
152
|
-
const thread = await client.readThread(id);
|
|
153
|
-
if (!thread)
|
|
154
|
-
continue;
|
|
155
|
-
if (thread.cwd !== options.cwd)
|
|
156
|
-
continue;
|
|
157
|
-
if (!thread.preview.includes(options.previewIncludes))
|
|
158
|
-
continue;
|
|
159
|
-
if (thread.updatedAt < options.updatedAfter)
|
|
160
|
-
continue;
|
|
161
|
-
if (!best || thread.updatedAt > best.updatedAt)
|
|
162
|
-
best = thread;
|
|
163
|
-
}
|
|
164
|
-
return best?.id ?? null;
|
|
165
|
-
}
|
|
166
|
-
finally {
|
|
167
|
-
client.close();
|
|
168
|
-
}
|
|
169
|
-
}
|
|
170
|
-
function encodeWebSocketTextFrame(text) {
|
|
171
|
-
const payload = Buffer.from(text);
|
|
172
|
-
let header;
|
|
173
|
-
if (payload.length < 126) {
|
|
174
|
-
header = Buffer.alloc(2);
|
|
175
|
-
header[1] = 0x80 | payload.length;
|
|
176
|
-
}
|
|
177
|
-
else if (payload.length <= 0xffff) {
|
|
178
|
-
header = Buffer.alloc(4);
|
|
179
|
-
header[1] = 0x80 | 126;
|
|
180
|
-
header.writeUInt16BE(payload.length, 2);
|
|
181
|
-
}
|
|
182
|
-
else {
|
|
183
|
-
header = Buffer.alloc(10);
|
|
184
|
-
header[1] = 0x80 | 127;
|
|
185
|
-
header.writeBigUInt64BE(BigInt(payload.length), 2);
|
|
186
|
-
}
|
|
187
|
-
header[0] = 0x81;
|
|
188
|
-
const mask = crypto.randomBytes(4);
|
|
189
|
-
const framed = Buffer.alloc(header.length + mask.length + payload.length);
|
|
190
|
-
header.copy(framed, 0);
|
|
191
|
-
mask.copy(framed, header.length);
|
|
192
|
-
for (let i = 0; i < payload.length; i += 1) {
|
|
193
|
-
framed[header.length + mask.length + i] = payload[i] ^ mask[i % mask.length];
|
|
194
|
-
}
|
|
195
|
-
return framed;
|
|
196
|
-
}
|
|
197
|
-
function decodeWebSocketTextFrame(buffer) {
|
|
198
|
-
let payloadLength = buffer[1] & 0x7f;
|
|
199
|
-
let offset = 2;
|
|
200
|
-
if (payloadLength === 126) {
|
|
201
|
-
if (buffer.length < 4)
|
|
202
|
-
return null;
|
|
203
|
-
payloadLength = buffer.readUInt16BE(2);
|
|
204
|
-
offset = 4;
|
|
205
|
-
}
|
|
206
|
-
else if (payloadLength === 127) {
|
|
207
|
-
if (buffer.length < 10)
|
|
208
|
-
return null;
|
|
209
|
-
payloadLength = Number(buffer.readBigUInt64BE(2));
|
|
210
|
-
offset = 10;
|
|
211
|
-
}
|
|
212
|
-
const masked = (buffer[1] & 0x80) !== 0;
|
|
213
|
-
let mask = null;
|
|
214
|
-
if (masked) {
|
|
215
|
-
if (buffer.length < offset + 4)
|
|
216
|
-
return null;
|
|
217
|
-
mask = buffer.slice(offset, offset + 4);
|
|
218
|
-
offset += 4;
|
|
219
|
-
}
|
|
220
|
-
if (buffer.length < offset + payloadLength)
|
|
221
|
-
return null;
|
|
222
|
-
let payload = buffer.slice(offset, offset + payloadLength);
|
|
223
|
-
if (mask) {
|
|
224
|
-
payload = Buffer.from(payload.map((byte, index) => byte ^ mask[index % 4]));
|
|
225
|
-
}
|
|
226
|
-
return { text: payload.toString('utf8'), consumed: offset + payloadLength };
|
|
227
|
-
}
|
|
228
|
-
//# sourceMappingURL=codex-app-server.js.map
|
|
1
|
+
import d from"node:net";import o from"node:crypto";class l{socketPath;socket=null;buffer=Buffer.alloc(0);handshaken=!1;nextId=1;pending=new Map;constructor(e){this.socketPath=e}async connect(){this.socket||(this.socket=d.createConnection(this.socketPath),this.socket.on("data",e=>{this.buffer=Buffer.concat([this.buffer,e]),this.parseIncoming()}),this.socket.on("error",e=>{for(const t of this.pending.values())t.reject(e);this.pending.clear()}),await new Promise((e,t)=>{const n=r=>t(r);this.socket.once("error",n),this.socket.once("connect",()=>{this.socket.off("error",n);const r=o.randomBytes(16).toString("base64");this.socket.write(["GET / HTTP/1.1","Host: localhost","Upgrade: websocket","Connection: Upgrade",`Sec-WebSocket-Key: ${r}`,"Sec-WebSocket-Version: 13","",""].join(`\r
|
|
2
|
+
`)),e()})}),await this.waitForHandshake(),await this.request("initialize",{clientInfo:{name:"borgmcp",version:"0"},capabilities:{experimentalApi:!0,requestAttestation:!1}}),this.notify("initialized",{}))}close(){this.socket?.end(),this.socket=null}async loadedThreadIds(){const e=await this.request("thread/loaded/list",{});return Array.isArray(e?.data)?e.data.filter(t=>typeof t=="string"):[]}async readThread(e){const n=(await this.request("thread/read",{threadId:e,includeTurns:!1}))?.thread;return!n||typeof n.id!="string"?null:{id:n.id,cwd:n.cwd,preview:n.preview,status:n.status,updatedAt:n.updatedAt}}async startTurn(e,t){await this.request("turn/start",{threadId:e,input:[{type:"text",text:t,text_elements:[]}]})}waitForHandshake(){return new Promise((e,t)=>{const n=Date.now(),r=()=>{if(this.handshaken)return e();if(Date.now()-n>5e3)return t(new Error("Timed out waiting for Codex app-server websocket handshake"));setTimeout(r,25)};r()})}request(e,t){const n=this.nextId++;return this.writeJson({id:n,method:e,params:t}),new Promise((r,i)=>{this.pending.set(n,{resolve:r,reject:i}),setTimeout(()=>{this.pending.delete(n)&&i(new Error(`Timed out waiting for Codex app-server response to ${e}`))},5e3)})}notify(e,t){this.writeJson({method:e,params:t})}writeJson(e){if(!this.socket)throw new Error("Codex app-server socket is not connected");this.socket.write(h(JSON.stringify(e)))}parseIncoming(){if(!this.handshaken){const e=this.buffer.indexOf(`\r
|
|
3
|
+
\r
|
|
4
|
+
`);if(e<0)return;this.buffer=this.buffer.slice(e+4),this.handshaken=!0}for(;this.buffer.length>=2;){const e=f(this.buffer);if(!e)return;this.buffer=this.buffer.slice(e.consumed);let t;try{t=JSON.parse(e.text)}catch{continue}if(typeof t.id=="number"&&this.pending.has(t.id)){const n=this.pending.get(t.id);this.pending.delete(t.id),t.error?n.reject(new Error(t.error.message??"Codex app-server request failed")):n.resolve(t.result)}}}}async function g(s){const e=new l(s.socketPath);await e.connect();try{const t=await e.loadedThreadIds();let n=null;for(const r of t){const i=await e.readThread(r);i&&i.cwd===s.cwd&&i.preview.includes(s.previewIncludes)&&(i.updatedAt<s.updatedAfter||(!n||i.updatedAt>n.updatedAt)&&(n=i))}return n?.id??null}finally{e.close()}}function h(s){const e=Buffer.from(s);let t;e.length<126?(t=Buffer.alloc(2),t[1]=128|e.length):e.length<=65535?(t=Buffer.alloc(4),t[1]=254,t.writeUInt16BE(e.length,2)):(t=Buffer.alloc(10),t[1]=255,t.writeBigUInt64BE(BigInt(e.length),2)),t[0]=129;const n=o.randomBytes(4),r=Buffer.alloc(t.length+n.length+e.length);t.copy(r,0),n.copy(r,t.length);for(let i=0;i<e.length;i+=1)r[t.length+n.length+i]=e[i]^n[i%n.length];return r}function f(s){let e=s[1]&127,t=2;if(e===126){if(s.length<4)return null;e=s.readUInt16BE(2),t=4}else if(e===127){if(s.length<10)return null;e=Number(s.readBigUInt64BE(2)),t=10}const n=(s[1]&128)!==0;let r=null;if(n){if(s.length<t+4)return null;r=s.slice(t,t+4),t+=4}if(s.length<t+e)return null;let i=s.slice(t,t+e);return r&&(i=Buffer.from(i.map((a,c)=>a^r[c%4]))),{text:i.toString("utf8"),consumed:t+e}}export{l as CodexAppServerClient,g as findLoadedCodexThread};
|
package/dist/codex-app-wake.js
CHANGED
|
@@ -1,122 +1,2 @@
|
|
|
1
|
-
import
|
|
2
|
-
|
|
3
|
-
import { checkCodexBridgeHealthy } from './codex-remote.js';
|
|
4
|
-
import { recordEventReceipt } from './health-beat.js';
|
|
5
|
-
export const CODEX_WAKE_PROMPT = 'New Borg cube-log activity arrived.';
|
|
6
|
-
export function formatCodexWakePrompt(inboxLine) {
|
|
7
|
-
return `New Borg cube-log activity arrived:\n${inboxLine}`;
|
|
8
|
-
}
|
|
9
|
-
export function isCodexRemoteWakeEnabled(env = process.env) {
|
|
10
|
-
return env.BORG_CODEX_REMOTE_WAKE === '1';
|
|
11
|
-
}
|
|
12
|
-
export function resolveSessionAgentKind(env = process.env) {
|
|
13
|
-
return isCodexRemoteWakeEnabled(env) ? 'codex' : 'claude';
|
|
14
|
-
}
|
|
15
|
-
export function resolveCodexWakeTarget(env = process.env) {
|
|
16
|
-
if (!isCodexRemoteWakeEnabled(env)) {
|
|
17
|
-
return { enabled: false };
|
|
18
|
-
}
|
|
19
|
-
return { enabled: true };
|
|
20
|
-
}
|
|
21
|
-
/**
|
|
22
|
-
* gh#633: resolve a codex drone's transport-agnostic "wake-path-armed" signal
|
|
23
|
-
* from its OWN runtime wake mechanism — the app-server bridge's process
|
|
24
|
-
* liveness, the codex analogue of the claude tail-F Monitor health. Fed into
|
|
25
|
-
* the health beat so the HOP-2 wake-path-deaf classifier reads a
|
|
26
|
-
* transport-agnostic armed signal instead of the claude-shaped monitor_armed
|
|
27
|
-
* (which is false-by-design for codex and falsely flagged them, gh#633).
|
|
28
|
-
*
|
|
29
|
-
* Tri-state (boolean|null; caller maps null→armed for false-deaf-avoidance):
|
|
30
|
-
* - false ONLY on a positively-dead bridge: no wake target registered (the
|
|
31
|
-
* bridge cannot deliver wakes), OR the app-server pid is dead.
|
|
32
|
-
* - true when the wake target resolves AND the app-server pid is alive.
|
|
33
|
-
* - null when the bridge health is indeterminate (target read or pid check
|
|
34
|
-
* could not resolve) → armed (don't false-flag on uncertainty).
|
|
35
|
-
*/
|
|
36
|
-
export async function probeCodexBridgeArmed(active, deps = {}) {
|
|
37
|
-
try {
|
|
38
|
-
const resolve = deps.getCodexWakeTarget ?? getCodexWakeTarget;
|
|
39
|
-
const target = await resolve(active.cubeId, active.droneId);
|
|
40
|
-
// No registered wake target → the bridge cannot deliver a wake → not armed.
|
|
41
|
-
if (!target)
|
|
42
|
-
return false;
|
|
43
|
-
const check = deps.checkBridge ?? checkCodexBridgeHealthy;
|
|
44
|
-
return check(target.socketPath);
|
|
45
|
-
}
|
|
46
|
-
catch {
|
|
47
|
-
return null;
|
|
48
|
-
}
|
|
49
|
-
}
|
|
50
|
-
let wakeInFlight = false;
|
|
51
|
-
const pendingWakeRequests = [];
|
|
52
|
-
const deliveredWakeKeys = new Set();
|
|
53
|
-
const deliveredWakeKeyOrder = [];
|
|
54
|
-
const DELIVERED_WAKE_KEY_CAP = 100;
|
|
55
|
-
export function wakeCodexViaAppServer(reason = CODEX_WAKE_PROMPT, env = process.env, deps = {}) {
|
|
56
|
-
const target = resolveCodexWakeTarget(env);
|
|
57
|
-
if (!target.enabled)
|
|
58
|
-
return;
|
|
59
|
-
pendingWakeRequests.push({ reason, deps });
|
|
60
|
-
if (wakeInFlight)
|
|
61
|
-
return;
|
|
62
|
-
wakeInFlight = true;
|
|
63
|
-
void drainCodexWakeQueue().finally(() => {
|
|
64
|
-
wakeInFlight = false;
|
|
65
|
-
});
|
|
66
|
-
}
|
|
67
|
-
async function drainCodexWakeQueue() {
|
|
68
|
-
while (pendingWakeRequests.length > 0) {
|
|
69
|
-
const request = pendingWakeRequests.shift();
|
|
70
|
-
await wakeCodexTargeted(request.reason, request.deps);
|
|
71
|
-
}
|
|
72
|
-
}
|
|
73
|
-
async function wakeCodexTargeted(reason, deps) {
|
|
74
|
-
try {
|
|
75
|
-
const active = await (deps.getActiveCube ?? getActiveCube)();
|
|
76
|
-
if (!active)
|
|
77
|
-
return;
|
|
78
|
-
const target = await (deps.getCodexWakeTarget ?? getCodexWakeTarget)(active.cubeId, active.droneId);
|
|
79
|
-
if (!target)
|
|
80
|
-
return;
|
|
81
|
-
const wakeKey = `${target.threadId}\0${reason}`;
|
|
82
|
-
if (deliveredWakeKeys.has(wakeKey))
|
|
83
|
-
return;
|
|
84
|
-
const client = deps.createClient
|
|
85
|
-
? deps.createClient(target.socketPath)
|
|
86
|
-
: new CodexAppServerClient(target.socketPath);
|
|
87
|
-
await client.connect();
|
|
88
|
-
try {
|
|
89
|
-
const thread = await client.readThread(target.threadId);
|
|
90
|
-
if (thread?.status?.type === 'active') {
|
|
91
|
-
return;
|
|
92
|
-
}
|
|
93
|
-
await client.startTurn(target.threadId, reason);
|
|
94
|
-
recordEventReceipt();
|
|
95
|
-
rememberDeliveredWake(wakeKey);
|
|
96
|
-
}
|
|
97
|
-
finally {
|
|
98
|
-
client.close();
|
|
99
|
-
}
|
|
100
|
-
}
|
|
101
|
-
catch {
|
|
102
|
-
// Best-effort wake path: never break the Borg SSE stream.
|
|
103
|
-
}
|
|
104
|
-
}
|
|
105
|
-
export function resetCodexWakeForTests() {
|
|
106
|
-
wakeInFlight = false;
|
|
107
|
-
pendingWakeRequests.length = 0;
|
|
108
|
-
deliveredWakeKeys.clear();
|
|
109
|
-
deliveredWakeKeyOrder.length = 0;
|
|
110
|
-
}
|
|
111
|
-
function rememberDeliveredWake(key) {
|
|
112
|
-
if (deliveredWakeKeys.has(key))
|
|
113
|
-
return;
|
|
114
|
-
deliveredWakeKeys.add(key);
|
|
115
|
-
deliveredWakeKeyOrder.push(key);
|
|
116
|
-
while (deliveredWakeKeyOrder.length > DELIVERED_WAKE_KEY_CAP) {
|
|
117
|
-
const oldKey = deliveredWakeKeyOrder.shift();
|
|
118
|
-
if (oldKey)
|
|
119
|
-
deliveredWakeKeys.delete(oldKey);
|
|
120
|
-
}
|
|
121
|
-
}
|
|
122
|
-
//# sourceMappingURL=codex-app-wake.js.map
|
|
1
|
+
import{getActiveCube as f,getCodexWakeTarget as u}from"./cubes.js";import{CodexAppServerClient as h}from"./codex-app-server.js";import{checkCodexBridgeHealthy as g}from"./codex-remote.js";import{recordEventReceipt as p}from"./health-beat.js";const v="New Borg cube-log activity arrived.";function T(e){return`New Borg cube-log activity arrived:
|
|
2
|
+
${e}`}function l(e=process.env){return e.BORG_CODEX_REMOTE_WAKE==="1"}function I(e=process.env){return l(e)?"codex":"claude"}function x(e=process.env){return l(e)?{enabled:!0}:{enabled:!1}}async function K(e,t={}){try{const r=await(t.getCodexWakeTarget??u)(e.cubeId,e.droneId);return r?(t.checkBridge??g)(r.socketPath):!1}catch{return null}}let c=!1;const i=[],o=new Set,s=[],k=100;function _(e=v,t=process.env,n={}){x(t).enabled&&(i.push({reason:e,deps:n}),!c&&(c=!0,C().finally(()=>{c=!1})))}async function C(){for(;i.length>0;){const e=i.shift();await w(e.reason,e.deps)}}async function w(e,t){try{const n=await(t.getActiveCube??f)();if(!n)return;const r=await(t.getCodexWakeTarget??u)(n.cubeId,n.droneId);if(!r)return;const d=`${r.threadId}\0${e}`;if(o.has(d))return;const a=t.createClient?t.createClient(r.socketPath):new h(r.socketPath);await a.connect();try{if((await a.readThread(r.threadId))?.status?.type==="active")return;await a.startTurn(r.threadId,e),p(),W(d)}finally{a.close()}}catch{}}function P(){c=!1,i.length=0,o.clear(),s.length=0}function W(e){if(!o.has(e))for(o.add(e),s.push(e);s.length>k;){const t=s.shift();t&&o.delete(t)}}export{v as CODEX_WAKE_PROMPT,T as formatCodexWakePrompt,l as isCodexRemoteWakeEnabled,K as probeCodexBridgeArmed,P as resetCodexWakeForTests,x as resolveCodexWakeTarget,I as resolveSessionAgentKind,_ as wakeCodexViaAppServer};
|
package/dist/codex-launch.js
CHANGED
|
@@ -1,81 +1 @@
|
|
|
1
|
-
|
|
2
|
-
const codexNonceClause = options.codexWakeNonce
|
|
3
|
-
? `Wake target nonce: ${options.codexWakeNonce}. `
|
|
4
|
-
: '';
|
|
5
|
-
const loopFreshnessClause = `On every Monitor wake and every ScheduleWakeup heartbeat, triage with borg:read-log unread_only=true first — it returns only entries since your last read, advancing your server-side read cursor (oldest-unread first), so you never skip a burst; and it keeps last_seen fresh. DRAIN: if it returns a full set (count == limit) or borg:roster shows behind_by>0, call read-log unread_only=true again until the return is < limit. Do NOT triage with a manual since cursor or a bare limit window — since is strict-after (skips the boundary entry) and a limit-bounded read skips older-unread during bursts (and codex has no per-entry inbox-Monitor backstop to catch them); reserve limit for explicit bounded reads. Call borg:regen only when you are about to act and need full cube/role/roster context, on session start, after context compaction, or periodically every 4-5 wakes / 15-30 minutes. Never reflexively call borg:regen for routine text-only wakes. `;
|
|
6
|
-
const codexWakePathClause = options.codexWakePathClause ??
|
|
7
|
-
`Codex Borg wakeups use remote-control when available; if no wake arrives, run borg:regen manually when returning to the session.`;
|
|
8
|
-
return (`${options.cli === 'claude' ? '/loop ' : ''}Call borg:regen and follow the playbook in its response. ` +
|
|
9
|
-
codexNonceClause +
|
|
10
|
-
`Note: at session start the borg MCP server is still spinning up in ` +
|
|
11
|
-
`parallel — if a system reminder claims "MCP server disconnected" or ` +
|
|
12
|
-
`the borg: tools are not yet registered, do NOT bail. Recover via ` +
|
|
13
|
-
`\`ToolSearch({query: "select:mcp__borg__borg_regen,mcp__borg__borg_log,Monitor", max_results: 3})\` ` +
|
|
14
|
-
`to load the bootstrap tools in one call, then call borg:regen. ` +
|
|
15
|
-
`The server typically becomes available within a few seconds. ` +
|
|
16
|
-
options.monitorClause +
|
|
17
|
-
`Coordinator/Queen seats: before posting bare \`Standing.\`, run the ` +
|
|
18
|
-
`anti-passive-Standing stale-check per your role text — for every ` +
|
|
19
|
-
`in-flight dispatch / REVIEW-READY / synthesis-pending, compare ` +
|
|
20
|
-
`elapsed-since-last-transition against the cadence-table PING ` +
|
|
21
|
-
`thresholds; escalate per the 4-step ladder (PING / probe-liveness / ` +
|
|
22
|
-
`reassign / suspect-systemic) when any row is overdue. Append the ` +
|
|
23
|
-
`awaited signal when you do post Standing (\`Standing for X\`, never ` +
|
|
24
|
-
`bare). ` +
|
|
25
|
-
loopFreshnessClause +
|
|
26
|
-
(options.cli === 'claude'
|
|
27
|
-
? `Wake-path capability check: if borg:regen shows a wake-path warning, arm the Monitor before starting work. Use a 30-minute (1800s) fallback heartbeat for ScheduleWakeup.`
|
|
28
|
-
: codexWakePathClause));
|
|
29
|
-
}
|
|
30
|
-
export function socketPathFromRemoteArgs(args) {
|
|
31
|
-
const index = args.indexOf('--remote');
|
|
32
|
-
if (index < 0)
|
|
33
|
-
return null;
|
|
34
|
-
const value = args[index + 1];
|
|
35
|
-
if (!value?.startsWith('unix://'))
|
|
36
|
-
return null;
|
|
37
|
-
return value.slice('unix://'.length);
|
|
38
|
-
}
|
|
39
|
-
export function threadIdFromPassthroughArgs(args) {
|
|
40
|
-
if (args[0] === 'resume' && args[1] && !args[1].startsWith('-'))
|
|
41
|
-
return args[1];
|
|
42
|
-
const resumeIndex = args.indexOf('--resume');
|
|
43
|
-
if (resumeIndex >= 0 && args[resumeIndex + 1])
|
|
44
|
-
return args[resumeIndex + 1];
|
|
45
|
-
return null;
|
|
46
|
-
}
|
|
47
|
-
export async function recordCodexWakeTarget(options) {
|
|
48
|
-
try {
|
|
49
|
-
const explicitThreadId = options.passthroughArgs
|
|
50
|
-
? threadIdFromPassthroughArgs(options.passthroughArgs)
|
|
51
|
-
: null;
|
|
52
|
-
if (explicitThreadId) {
|
|
53
|
-
await options.deps.setCodexWakeTarget(options.cubeId, options.droneId, {
|
|
54
|
-
threadId: explicitThreadId,
|
|
55
|
-
socketPath: options.socketPath,
|
|
56
|
-
});
|
|
57
|
-
return;
|
|
58
|
-
}
|
|
59
|
-
const deadline = Date.now() + 15_000;
|
|
60
|
-
while (Date.now() < deadline) {
|
|
61
|
-
const threadId = await options.deps.findLoadedCodexThread({
|
|
62
|
-
socketPath: options.socketPath,
|
|
63
|
-
cwd: options.cwd,
|
|
64
|
-
previewIncludes: options.previewNeedle,
|
|
65
|
-
updatedAfter: options.launchedAtSeconds - 5,
|
|
66
|
-
});
|
|
67
|
-
if (threadId) {
|
|
68
|
-
await options.deps.setCodexWakeTarget(options.cubeId, options.droneId, {
|
|
69
|
-
threadId,
|
|
70
|
-
socketPath: options.socketPath,
|
|
71
|
-
});
|
|
72
|
-
return;
|
|
73
|
-
}
|
|
74
|
-
await new Promise((resolve) => setTimeout(resolve, 500));
|
|
75
|
-
}
|
|
76
|
-
}
|
|
77
|
-
catch {
|
|
78
|
-
// Best-effort mapping: launch still succeeds and manual regen remains available.
|
|
79
|
-
}
|
|
80
|
-
}
|
|
81
|
-
//# sourceMappingURL=codex-launch.js.map
|
|
1
|
+
function s(e){const r=e.codexWakeNonce?`Wake target nonce: ${e.codexWakeNonce}. `:"",t="On every Monitor wake and every ScheduleWakeup heartbeat, triage with borg:read-log unread_only=true first \u2014 it returns only entries since your last read, advancing your server-side read cursor (oldest-unread first), so you never skip a burst; and it keeps last_seen fresh. DRAIN: if it returns a full set (count == limit) or borg:roster shows behind_by>0, call read-log unread_only=true again until the return is < limit. Do NOT triage with a manual since cursor or a bare limit window \u2014 since is strict-after (skips the boundary entry) and a limit-bounded read skips older-unread during bursts (and codex has no per-entry inbox-Monitor backstop to catch them); reserve limit for explicit bounded reads. Call borg:regen only when you are about to act and need full cube/role/roster context, on session start, after context compaction, or periodically every 4-5 wakes / 15-30 minutes. Never reflexively call borg:regen for routine text-only wakes. ",a=e.codexWakePathClause??"Codex Borg wakeups use remote-control when available; if no wake arrives, run borg:regen manually when returning to the session.";return`${e.cli==="claude"?"/loop ":""}Call borg:regen and follow the playbook in its response. `+r+'Note: at session start the borg MCP server is still spinning up in parallel \u2014 if a system reminder claims "MCP server disconnected" or the borg: tools are not yet registered, do NOT bail. Recover via `ToolSearch({query: "select:mcp__borg__borg_regen,mcp__borg__borg_log,Monitor", max_results: 3})` to load the bootstrap tools in one call, then call borg:regen. The server typically becomes available within a few seconds. '+e.monitorClause+"Coordinator/Queen seats: before posting bare `Standing.`, run the anti-passive-Standing stale-check per your role text \u2014 for every in-flight dispatch / REVIEW-READY / synthesis-pending, compare elapsed-since-last-transition against the cadence-table PING thresholds; escalate per the 4-step ladder (PING / probe-liveness / reassign / suspect-systemic) when any row is overdue. Append the awaited signal when you do post Standing (`Standing for X`, never bare). "+t+(e.cli==="claude"?"Wake-path capability check: if borg:regen shows a wake-path warning, arm the Monitor before starting work. Use a 30-minute (1800s) fallback heartbeat for ScheduleWakeup.":a)}function i(e){const r=e.indexOf("--remote");if(r<0)return null;const t=e[r+1];return t?.startsWith("unix://")?t.slice(7):null}function o(e){if(e[0]==="resume"&&e[1]&&!e[1].startsWith("-"))return e[1];const r=e.indexOf("--resume");return r>=0&&e[r+1]?e[r+1]:null}async function l(e){try{const r=e.passthroughArgs?o(e.passthroughArgs):null;if(r){await e.deps.setCodexWakeTarget(e.cubeId,e.droneId,{threadId:r,socketPath:e.socketPath});return}const t=Date.now()+15e3;for(;Date.now()<t;){const a=await e.deps.findLoadedCodexThread({socketPath:e.socketPath,cwd:e.cwd,previewIncludes:e.previewNeedle,updatedAfter:e.launchedAtSeconds-5});if(a){await e.deps.setCodexWakeTarget(e.cubeId,e.droneId,{threadId:a,socketPath:e.socketPath});return}await new Promise(n=>setTimeout(n,500))}}catch{}}export{s as buildAgentKickoffPrompt,l as recordCodexWakeTarget,i as socketPathFromRemoteArgs,o as threadIdFromPassthroughArgs};
|