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.
Files changed (160) hide show
  1. package/README.md +5 -3
  2. package/dist/assimilate-cmd.js +39 -511
  3. package/dist/assimilate-deps.js +3 -177
  4. package/dist/assimilate-welcome.js +2 -24
  5. package/dist/auth-env.js +1 -107
  6. package/dist/auth.js +23 -612
  7. package/dist/claude.js +11 -281
  8. package/dist/cli-help.js +29 -50
  9. package/dist/cli-platform.js +4 -94
  10. package/dist/codex-app-server.js +4 -228
  11. package/dist/codex-app-wake.js +2 -122
  12. package/dist/codex-launch.js +1 -81
  13. package/dist/codex-remote.js +1 -250
  14. package/dist/config-utils.js +3 -385
  15. package/dist/config.js +1 -190
  16. package/dist/console-prefix.js +1 -86
  17. package/dist/cube-name.js +1 -65
  18. package/dist/cubes.js +4 -269
  19. package/dist/debug.js +1 -71
  20. package/dist/device-auth.js +1 -167
  21. package/dist/direct-log.js +1 -11
  22. package/dist/health-beat.js +1 -168
  23. package/dist/inbox-monitor.js +1 -129
  24. package/dist/index.js +26 -1378
  25. package/dist/lifecycle-log-guard.js +2 -93
  26. package/dist/list-roles-render.js +6 -39
  27. package/dist/log-audit.js +3 -186
  28. package/dist/log-stream.js +9 -848
  29. package/dist/name-validator.js +1 -22
  30. package/dist/parse-assimilate-args.js +1 -82
  31. package/dist/postinstall.js +8 -22
  32. package/dist/regen-format.js +11 -337
  33. package/dist/regen.js +5 -83
  34. package/dist/remote-client.d.ts +4 -7
  35. package/dist/remote-client.js +1 -695
  36. package/dist/role-resolver.js +1 -36
  37. package/dist/role-section.js +8 -208
  38. package/dist/roster-render.js +3 -96
  39. package/dist/setup.js +41 -251
  40. package/dist/shell-escape.js +1 -22
  41. package/dist/spawn.js +10 -29
  42. package/dist/stale-version-check.js +1 -102
  43. package/dist/stream-owner.js +2 -202
  44. package/dist/stream-status.js +3 -211
  45. package/dist/subscription-retry.js +1 -23
  46. package/dist/sync-roles-render.js +3 -118
  47. package/dist/sync.js +22 -286
  48. package/dist/templates.js +120 -626
  49. package/dist/terminal-title.js +1 -68
  50. package/dist/token-crypto.js +1 -91
  51. package/dist/token-store.js +1 -222
  52. package/dist/types.d.ts +0 -5
  53. package/dist/types.js +0 -5
  54. package/dist/version.js +2 -78
  55. package/dist/worktree-lifecycle.js +2 -173
  56. package/package.json +12 -2
  57. package/dist/assimilate-cmd.d.ts.map +0 -1
  58. package/dist/assimilate-cmd.js.map +0 -1
  59. package/dist/assimilate-deps.d.ts.map +0 -1
  60. package/dist/assimilate-deps.js.map +0 -1
  61. package/dist/assimilate-welcome.d.ts.map +0 -1
  62. package/dist/assimilate-welcome.js.map +0 -1
  63. package/dist/auth-env.d.ts.map +0 -1
  64. package/dist/auth-env.js.map +0 -1
  65. package/dist/auth.d.ts.map +0 -1
  66. package/dist/auth.js.map +0 -1
  67. package/dist/claude.d.ts.map +0 -1
  68. package/dist/claude.js.map +0 -1
  69. package/dist/cli-help.d.ts.map +0 -1
  70. package/dist/cli-help.js.map +0 -1
  71. package/dist/cli-platform.d.ts.map +0 -1
  72. package/dist/cli-platform.js.map +0 -1
  73. package/dist/codex-app-server.d.ts.map +0 -1
  74. package/dist/codex-app-server.js.map +0 -1
  75. package/dist/codex-app-wake.d.ts.map +0 -1
  76. package/dist/codex-app-wake.js.map +0 -1
  77. package/dist/codex-launch.d.ts.map +0 -1
  78. package/dist/codex-launch.js.map +0 -1
  79. package/dist/codex-remote.d.ts.map +0 -1
  80. package/dist/codex-remote.js.map +0 -1
  81. package/dist/config-utils.d.ts.map +0 -1
  82. package/dist/config-utils.js.map +0 -1
  83. package/dist/config.d.ts.map +0 -1
  84. package/dist/config.js.map +0 -1
  85. package/dist/console-prefix.d.ts.map +0 -1
  86. package/dist/console-prefix.js.map +0 -1
  87. package/dist/cube-name.d.ts.map +0 -1
  88. package/dist/cube-name.js.map +0 -1
  89. package/dist/cubes.d.ts.map +0 -1
  90. package/dist/cubes.js.map +0 -1
  91. package/dist/debug.d.ts.map +0 -1
  92. package/dist/debug.js.map +0 -1
  93. package/dist/device-auth.d.ts.map +0 -1
  94. package/dist/device-auth.js.map +0 -1
  95. package/dist/direct-log.d.ts.map +0 -1
  96. package/dist/direct-log.js.map +0 -1
  97. package/dist/health-beat.d.ts.map +0 -1
  98. package/dist/health-beat.js.map +0 -1
  99. package/dist/inbox-monitor.d.ts.map +0 -1
  100. package/dist/inbox-monitor.js.map +0 -1
  101. package/dist/index.d.ts.map +0 -1
  102. package/dist/index.js.map +0 -1
  103. package/dist/lifecycle-log-guard.d.ts.map +0 -1
  104. package/dist/lifecycle-log-guard.js.map +0 -1
  105. package/dist/list-roles-render.d.ts.map +0 -1
  106. package/dist/list-roles-render.js.map +0 -1
  107. package/dist/log-audit.d.ts.map +0 -1
  108. package/dist/log-audit.js.map +0 -1
  109. package/dist/log-stream.d.ts.map +0 -1
  110. package/dist/log-stream.js.map +0 -1
  111. package/dist/name-validator.d.ts.map +0 -1
  112. package/dist/name-validator.js.map +0 -1
  113. package/dist/parse-assimilate-args.d.ts.map +0 -1
  114. package/dist/parse-assimilate-args.js.map +0 -1
  115. package/dist/postinstall.d.ts.map +0 -1
  116. package/dist/postinstall.js.map +0 -1
  117. package/dist/regen-format.d.ts.map +0 -1
  118. package/dist/regen-format.js.map +0 -1
  119. package/dist/regen.d.ts.map +0 -1
  120. package/dist/regen.js.map +0 -1
  121. package/dist/remote-client.d.ts.map +0 -1
  122. package/dist/remote-client.js.map +0 -1
  123. package/dist/role-resolver.d.ts.map +0 -1
  124. package/dist/role-resolver.js.map +0 -1
  125. package/dist/role-section.d.ts.map +0 -1
  126. package/dist/role-section.js.map +0 -1
  127. package/dist/roster-render.d.ts.map +0 -1
  128. package/dist/roster-render.js.map +0 -1
  129. package/dist/setup.d.ts.map +0 -1
  130. package/dist/setup.js.map +0 -1
  131. package/dist/shell-escape.d.ts.map +0 -1
  132. package/dist/shell-escape.js.map +0 -1
  133. package/dist/spawn.d.ts.map +0 -1
  134. package/dist/spawn.js.map +0 -1
  135. package/dist/stale-version-check.d.ts.map +0 -1
  136. package/dist/stale-version-check.js.map +0 -1
  137. package/dist/stream-owner.d.ts.map +0 -1
  138. package/dist/stream-owner.js.map +0 -1
  139. package/dist/stream-status.d.ts.map +0 -1
  140. package/dist/stream-status.js.map +0 -1
  141. package/dist/subscription-retry.d.ts.map +0 -1
  142. package/dist/subscription-retry.js.map +0 -1
  143. package/dist/sync-roles-render.d.ts.map +0 -1
  144. package/dist/sync-roles-render.js.map +0 -1
  145. package/dist/sync.d.ts.map +0 -1
  146. package/dist/sync.js.map +0 -1
  147. package/dist/templates.d.ts.map +0 -1
  148. package/dist/templates.js.map +0 -1
  149. package/dist/terminal-title.d.ts.map +0 -1
  150. package/dist/terminal-title.js.map +0 -1
  151. package/dist/token-crypto.d.ts.map +0 -1
  152. package/dist/token-crypto.js.map +0 -1
  153. package/dist/token-store.d.ts.map +0 -1
  154. package/dist/token-store.js.map +0 -1
  155. package/dist/types.d.ts.map +0 -1
  156. package/dist/types.js.map +0 -1
  157. package/dist/version.d.ts.map +0 -1
  158. package/dist/version.js.map +0 -1
  159. package/dist/worktree-lifecycle.d.ts.map +0 -1
  160. package/dist/worktree-lifecycle.js.map +0 -1
@@ -1,228 +1,4 @@
1
- import net from 'node:net';
2
- import crypto from 'node:crypto';
3
- export class CodexAppServerClient {
4
- socketPath;
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};
@@ -1,122 +1,2 @@
1
- import { getActiveCube, getCodexWakeTarget } from './cubes.js';
2
- import { CodexAppServerClient } from './codex-app-server.js';
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};
@@ -1,81 +1 @@
1
- export function buildAgentKickoffPrompt(options) {
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};