codeam-cli 2.61.85 → 2.61.87
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/CHANGELOG.md +12 -0
- package/dist/index.js +289 -210
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -4,6 +4,18 @@ All notable changes to `codeam-cli` are documented here.
|
|
|
4
4
|
|
|
5
5
|
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
6
6
|
|
|
7
|
+
## [2.61.86] — 2026-08-07
|
|
8
|
+
|
|
9
|
+
### Fixed
|
|
10
|
+
|
|
11
|
+
- **cli:** Per-tools/call watchdog on the httpUrl MCP relay (agent hang → clean tool error) (#592)
|
|
12
|
+
|
|
13
|
+
## [2.61.85] — 2026-08-06
|
|
14
|
+
|
|
15
|
+
### Fixed
|
|
16
|
+
|
|
17
|
+
- **cli:** Resume the prior conversation on a warm re-launch into an existing workspace (#591)
|
|
18
|
+
|
|
7
19
|
## [2.61.84] — 2026-08-05
|
|
8
20
|
|
|
9
21
|
### Fixed
|
package/dist/index.js
CHANGED
|
@@ -89,6 +89,212 @@ var require_src = __commonJS({
|
|
|
89
89
|
}
|
|
90
90
|
});
|
|
91
91
|
|
|
92
|
+
// src/integrations/stdio-proxy.ts
|
|
93
|
+
var import_node_child_process31, import_node_readline3, RESTART_CHECK_INTERVAL_MS, SIGKILL_ESCALATION_MS, TOOL_CALL_TIMEOUT_MS, REPLAY_INIT_ID, RestartableStdioProxy;
|
|
94
|
+
var init_stdio_proxy = __esm({
|
|
95
|
+
"src/integrations/stdio-proxy.ts"() {
|
|
96
|
+
"use strict";
|
|
97
|
+
import_node_child_process31 = require("child_process");
|
|
98
|
+
import_node_readline3 = __toESM(require("readline"));
|
|
99
|
+
RESTART_CHECK_INTERVAL_MS = 3e4;
|
|
100
|
+
SIGKILL_ESCALATION_MS = 2e3;
|
|
101
|
+
TOOL_CALL_TIMEOUT_MS = (() => {
|
|
102
|
+
const raw = Number(process.env.CODEAM_MCP_TOOL_TIMEOUT_MS);
|
|
103
|
+
return Number.isFinite(raw) && raw > 0 ? raw : 12e4;
|
|
104
|
+
})();
|
|
105
|
+
REPLAY_INIT_ID = "__codeam_replay_init__";
|
|
106
|
+
RestartableStdioProxy = class {
|
|
107
|
+
constructor(opts) {
|
|
108
|
+
this.opts = opts;
|
|
109
|
+
}
|
|
110
|
+
opts;
|
|
111
|
+
child = null;
|
|
112
|
+
childRl = null;
|
|
113
|
+
initializeLine = null;
|
|
114
|
+
inflight = /* @__PURE__ */ new Set();
|
|
115
|
+
/** Per-`tools/call` request-id watchdog timers (see TOOL_CALL_TIMEOUT_MS). */
|
|
116
|
+
toolTimers = /* @__PURE__ */ new Map();
|
|
117
|
+
stdout = null;
|
|
118
|
+
swapping = false;
|
|
119
|
+
pendingClientLines = [];
|
|
120
|
+
ended = () => void 0;
|
|
121
|
+
async start() {
|
|
122
|
+
const stdin = this.opts.stdin ?? process.stdin;
|
|
123
|
+
const stdout = this.opts.stdout ?? process.stdout;
|
|
124
|
+
this.stdout = stdout;
|
|
125
|
+
await this.spawnChild(stdout);
|
|
126
|
+
const rl = import_node_readline3.default.createInterface({ input: stdin, crlfDelay: Infinity });
|
|
127
|
+
rl.on("line", (line) => this.onClientLine(line));
|
|
128
|
+
rl.on("close", () => this.child?.stdin?.end());
|
|
129
|
+
const timer = setInterval(() => this.checkRestart(stdout), RESTART_CHECK_INTERVAL_MS);
|
|
130
|
+
timer.unref();
|
|
131
|
+
return new Promise((resolve9) => {
|
|
132
|
+
this.ended = (code) => {
|
|
133
|
+
clearInterval(timer);
|
|
134
|
+
process.exitCode = code;
|
|
135
|
+
resolve9();
|
|
136
|
+
};
|
|
137
|
+
});
|
|
138
|
+
}
|
|
139
|
+
onClientLine(line) {
|
|
140
|
+
if (this.swapping) {
|
|
141
|
+
this.pendingClientLines.push(line);
|
|
142
|
+
return;
|
|
143
|
+
}
|
|
144
|
+
try {
|
|
145
|
+
const msg = JSON.parse(line);
|
|
146
|
+
if (msg.method === "initialize" && this.initializeLine === null) {
|
|
147
|
+
this.initializeLine = line;
|
|
148
|
+
}
|
|
149
|
+
if (msg.method === "notifications/cancelled") {
|
|
150
|
+
const requestId = msg.params?.requestId;
|
|
151
|
+
if (requestId !== void 0) {
|
|
152
|
+
this.inflight.delete(requestId);
|
|
153
|
+
this.clearToolTimeout(requestId);
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
if (msg.id !== void 0 && msg.method !== void 0) {
|
|
157
|
+
this.inflight.add(msg.id);
|
|
158
|
+
if (msg.method === "tools/call") this.armToolTimeout(msg.id);
|
|
159
|
+
}
|
|
160
|
+
} catch {
|
|
161
|
+
}
|
|
162
|
+
this.child?.stdin?.write(line + "\n");
|
|
163
|
+
}
|
|
164
|
+
onChildLine(line, stdout) {
|
|
165
|
+
try {
|
|
166
|
+
const msg = JSON.parse(line);
|
|
167
|
+
if (msg.id !== void 0 && msg.method === void 0) {
|
|
168
|
+
if (msg.id === REPLAY_INIT_ID) {
|
|
169
|
+
return;
|
|
170
|
+
}
|
|
171
|
+
this.inflight.delete(msg.id);
|
|
172
|
+
this.clearToolTimeout(msg.id);
|
|
173
|
+
}
|
|
174
|
+
} catch {
|
|
175
|
+
}
|
|
176
|
+
stdout.write(line + "\n");
|
|
177
|
+
if (this.inflight.size === 0) this.checkRestart(stdout);
|
|
178
|
+
}
|
|
179
|
+
/**
|
|
180
|
+
* Arm a per-`tools/call` watchdog. If the server never answers this request
|
|
181
|
+
* id within {@link TOOL_CALL_TIMEOUT_MS}, synthesize a JSON-RPC error response
|
|
182
|
+
* to the client so the agent's turn unblocks (clean tool error) instead of
|
|
183
|
+
* hanging forever with no Stop. Idempotent per id.
|
|
184
|
+
*/
|
|
185
|
+
armToolTimeout(id) {
|
|
186
|
+
const existing = this.toolTimers.get(id);
|
|
187
|
+
if (existing) clearTimeout(existing);
|
|
188
|
+
const t2 = setTimeout(() => {
|
|
189
|
+
this.toolTimers.delete(id);
|
|
190
|
+
if (!this.inflight.has(id)) return;
|
|
191
|
+
this.inflight.delete(id);
|
|
192
|
+
process.stderr.write(
|
|
193
|
+
`[codeam mcp-run] tools/call id=${String(id)} timed out after ${TOOL_CALL_TIMEOUT_MS}ms \u2014 server did not respond; failing the call so the turn can proceed
|
|
194
|
+
`
|
|
195
|
+
);
|
|
196
|
+
const errResponse = {
|
|
197
|
+
jsonrpc: "2.0",
|
|
198
|
+
id,
|
|
199
|
+
error: {
|
|
200
|
+
code: -32001,
|
|
201
|
+
message: `MCP tool call timed out after ${Math.round(TOOL_CALL_TIMEOUT_MS / 1e3)}s \u2014 the server did not respond. The target service/deployment may be unreachable (e.g. a Convex dev deployment is only reachable while \`convex dev\` is running \u2014 use a production deploy key or start \`convex dev\`).`
|
|
202
|
+
}
|
|
203
|
+
};
|
|
204
|
+
(this.stdout ?? process.stdout).write(JSON.stringify(errResponse) + "\n");
|
|
205
|
+
}, TOOL_CALL_TIMEOUT_MS);
|
|
206
|
+
t2.unref();
|
|
207
|
+
this.toolTimers.set(id, t2);
|
|
208
|
+
}
|
|
209
|
+
clearToolTimeout(id) {
|
|
210
|
+
const t2 = this.toolTimers.get(id);
|
|
211
|
+
if (t2) {
|
|
212
|
+
clearTimeout(t2);
|
|
213
|
+
this.toolTimers.delete(id);
|
|
214
|
+
}
|
|
215
|
+
}
|
|
216
|
+
clearAllToolTimeouts() {
|
|
217
|
+
for (const t2 of this.toolTimers.values()) clearTimeout(t2);
|
|
218
|
+
this.toolTimers.clear();
|
|
219
|
+
}
|
|
220
|
+
/**
|
|
221
|
+
* Failsafe wrapper for the fire-and-forget call sites (post-response check
|
|
222
|
+
* + the 30 s timer): `maybeRestart` handles the token-fetch failure itself,
|
|
223
|
+
* but nothing that escapes it may become an unhandled rejection — that
|
|
224
|
+
* would kill the whole shim process.
|
|
225
|
+
*/
|
|
226
|
+
checkRestart(stdout) {
|
|
227
|
+
this.maybeRestart(stdout).catch((err) => {
|
|
228
|
+
process.stderr.write(
|
|
229
|
+
`[codeam mcp-run] restart check failed (will retry): ${err instanceof Error ? err.message : String(err)}
|
|
230
|
+
`
|
|
231
|
+
);
|
|
232
|
+
});
|
|
233
|
+
}
|
|
234
|
+
async maybeRestart(stdout) {
|
|
235
|
+
if (this.swapping || !this.opts.shouldRestartNow()) return;
|
|
236
|
+
if (this.inflight.size > 0 || this.initializeLine === null || !this.child) return;
|
|
237
|
+
this.swapping = true;
|
|
238
|
+
let spec;
|
|
239
|
+
try {
|
|
240
|
+
spec = await this.opts.spawnSpec();
|
|
241
|
+
} catch (err) {
|
|
242
|
+
process.stderr.write(
|
|
243
|
+
`[codeam mcp-run] token refresh failed, keeping current server (will retry): ${err instanceof Error ? err.message : String(err)}
|
|
244
|
+
`
|
|
245
|
+
);
|
|
246
|
+
this.swapping = false;
|
|
247
|
+
for (const l of this.pendingClientLines.splice(0)) this.onClientLine(l);
|
|
248
|
+
return;
|
|
249
|
+
}
|
|
250
|
+
const old = this.child;
|
|
251
|
+
const oldRl = this.childRl;
|
|
252
|
+
this.child = null;
|
|
253
|
+
this.childRl = null;
|
|
254
|
+
oldRl?.close();
|
|
255
|
+
old.kill("SIGTERM");
|
|
256
|
+
const escalation = setTimeout(() => {
|
|
257
|
+
if (!old.killed || old.exitCode === null) old.kill("SIGKILL");
|
|
258
|
+
}, SIGKILL_ESCALATION_MS);
|
|
259
|
+
escalation.unref();
|
|
260
|
+
try {
|
|
261
|
+
await this.spawnChild(stdout, spec);
|
|
262
|
+
const replayed = JSON.parse(this.initializeLine);
|
|
263
|
+
this.child.stdin.write(JSON.stringify({ ...replayed, id: REPLAY_INIT_ID }) + "\n");
|
|
264
|
+
this.child.stdin.write(
|
|
265
|
+
JSON.stringify({ jsonrpc: "2.0", method: "notifications/initialized" }) + "\n"
|
|
266
|
+
);
|
|
267
|
+
} finally {
|
|
268
|
+
this.swapping = false;
|
|
269
|
+
for (const l of this.pendingClientLines.splice(0)) this.onClientLine(l);
|
|
270
|
+
}
|
|
271
|
+
}
|
|
272
|
+
async spawnChild(stdout, preResolved) {
|
|
273
|
+
const spec = preResolved ?? await this.opts.spawnSpec();
|
|
274
|
+
const spawn44 = this.opts.spawnImpl ?? import_node_child_process31.spawn;
|
|
275
|
+
const child = spawn44(spec.command, spec.args, {
|
|
276
|
+
env: { ...process.env, ...spec.env },
|
|
277
|
+
// env only — never argv
|
|
278
|
+
stdio: ["pipe", "pipe", "inherit"]
|
|
279
|
+
});
|
|
280
|
+
this.child = child;
|
|
281
|
+
child.stdin?.on("error", () => void 0);
|
|
282
|
+
const rl = import_node_readline3.default.createInterface({ input: child.stdout, crlfDelay: Infinity });
|
|
283
|
+
this.childRl = rl;
|
|
284
|
+
rl.on("line", (line) => {
|
|
285
|
+
if (child !== this.child) return;
|
|
286
|
+
this.onChildLine(line, stdout);
|
|
287
|
+
});
|
|
288
|
+
child.on("exit", (code) => {
|
|
289
|
+
if (child !== this.child) return;
|
|
290
|
+
this.clearAllToolTimeouts();
|
|
291
|
+
this.ended(code ?? 1);
|
|
292
|
+
});
|
|
293
|
+
}
|
|
294
|
+
};
|
|
295
|
+
}
|
|
296
|
+
});
|
|
297
|
+
|
|
92
298
|
// src/integrations/convex-admin-mcp.ts
|
|
93
299
|
var convex_admin_mcp_exports = {};
|
|
94
300
|
__export(convex_admin_mcp_exports, {
|
|
@@ -248,6 +454,64 @@ var init_convex_admin_mcp = __esm({
|
|
|
248
454
|
}
|
|
249
455
|
});
|
|
250
456
|
|
|
457
|
+
// src/integrations/mcp-tool-watchdog.ts
|
|
458
|
+
function createToolCallWatchdog(deps) {
|
|
459
|
+
const setTimer = deps.setTimer ?? ((fn, ms) => {
|
|
460
|
+
const t2 = setTimeout(fn, ms);
|
|
461
|
+
t2.unref?.();
|
|
462
|
+
return t2;
|
|
463
|
+
});
|
|
464
|
+
const clearTimer = deps.clearTimer ?? ((t2) => clearTimeout(t2));
|
|
465
|
+
const timers = /* @__PURE__ */ new Map();
|
|
466
|
+
const answered = /* @__PURE__ */ new Set();
|
|
467
|
+
const clear = (id) => {
|
|
468
|
+
const t2 = timers.get(id);
|
|
469
|
+
if (t2) {
|
|
470
|
+
clearTimer(t2);
|
|
471
|
+
timers.delete(id);
|
|
472
|
+
}
|
|
473
|
+
};
|
|
474
|
+
return {
|
|
475
|
+
onClientMessage(msg) {
|
|
476
|
+
const m = msg;
|
|
477
|
+
if (m.method !== "tools/call" || m.id === void 0) return;
|
|
478
|
+
const id = m.id;
|
|
479
|
+
clear(id);
|
|
480
|
+
const timer = setTimer(() => {
|
|
481
|
+
timers.delete(id);
|
|
482
|
+
answered.add(id);
|
|
483
|
+
deps.sendToAgent({
|
|
484
|
+
jsonrpc: "2.0",
|
|
485
|
+
id,
|
|
486
|
+
error: {
|
|
487
|
+
code: -32001,
|
|
488
|
+
message: `The ${deps.integrationId} tool did not respond within ${Math.round(
|
|
489
|
+
deps.timeoutMs / 1e3
|
|
490
|
+
)}s (remote MCP unresponsive). The request was aborted \u2014 try again or use a different tool.`
|
|
491
|
+
}
|
|
492
|
+
});
|
|
493
|
+
}, deps.timeoutMs);
|
|
494
|
+
timers.set(id, timer);
|
|
495
|
+
},
|
|
496
|
+
onServerMessage(msg) {
|
|
497
|
+
const m = msg;
|
|
498
|
+
if (m.id === void 0 || m.method !== void 0) return false;
|
|
499
|
+
clear(m.id);
|
|
500
|
+
return answered.delete(m.id);
|
|
501
|
+
},
|
|
502
|
+
dispose() {
|
|
503
|
+
for (const t2 of timers.values()) clearTimer(t2);
|
|
504
|
+
timers.clear();
|
|
505
|
+
answered.clear();
|
|
506
|
+
}
|
|
507
|
+
};
|
|
508
|
+
}
|
|
509
|
+
var init_mcp_tool_watchdog = __esm({
|
|
510
|
+
"src/integrations/mcp-tool-watchdog.ts"() {
|
|
511
|
+
"use strict";
|
|
512
|
+
}
|
|
513
|
+
});
|
|
514
|
+
|
|
251
515
|
// src/integrations/http-relay.ts
|
|
252
516
|
var http_relay_exports = {};
|
|
253
517
|
__export(http_relay_exports, {
|
|
@@ -280,21 +544,29 @@ async function runHttpRelay(delivery, client3, id) {
|
|
|
280
544
|
const stdioTransport = new StdioServerTransport();
|
|
281
545
|
await new Promise((resolve9, reject) => {
|
|
282
546
|
let done = false;
|
|
547
|
+
const watchdog = createToolCallWatchdog({
|
|
548
|
+
timeoutMs: TOOL_CALL_TIMEOUT_MS,
|
|
549
|
+
integrationId: id,
|
|
550
|
+
sendToAgent: (m) => void stdioTransport.send(m).catch(() => void 0)
|
|
551
|
+
});
|
|
283
552
|
const finish = (err) => {
|
|
284
553
|
if (done) return;
|
|
285
554
|
done = true;
|
|
555
|
+
watchdog.dispose();
|
|
286
556
|
void httpTransport.close().catch(() => void 0);
|
|
287
557
|
void stdioTransport.close().catch(() => void 0);
|
|
288
558
|
if (err) reject(err);
|
|
289
559
|
else resolve9();
|
|
290
560
|
};
|
|
291
561
|
stdioTransport.onmessage = (msg) => {
|
|
562
|
+
watchdog.onClientMessage(msg);
|
|
292
563
|
void httpTransport.send(msg).catch((e) => {
|
|
293
564
|
process.stderr.write(`[mcp-run http] send\u2192remote failed: ${String(e)}
|
|
294
565
|
`);
|
|
295
566
|
});
|
|
296
567
|
};
|
|
297
568
|
httpTransport.onmessage = (msg) => {
|
|
569
|
+
if (watchdog.onServerMessage(msg)) return;
|
|
298
570
|
void stdioTransport.send(msg).catch(() => void 0);
|
|
299
571
|
};
|
|
300
572
|
stdioTransport.onclose = () => finish();
|
|
@@ -311,6 +583,8 @@ async function runHttpRelay(delivery, client3, id) {
|
|
|
311
583
|
var init_http_relay = __esm({
|
|
312
584
|
"src/integrations/http-relay.ts"() {
|
|
313
585
|
"use strict";
|
|
586
|
+
init_stdio_proxy();
|
|
587
|
+
init_mcp_tool_watchdog();
|
|
314
588
|
}
|
|
315
589
|
});
|
|
316
590
|
|
|
@@ -7526,7 +7800,7 @@ function readAnonId() {
|
|
|
7526
7800
|
}
|
|
7527
7801
|
function superProperties() {
|
|
7528
7802
|
return {
|
|
7529
|
-
cliVersion: true ? "2.61.
|
|
7803
|
+
cliVersion: true ? "2.61.87" : "0.0.0-dev",
|
|
7530
7804
|
nodeVersion: process.version,
|
|
7531
7805
|
platform: process.platform,
|
|
7532
7806
|
arch: process.arch,
|
|
@@ -7707,7 +7981,7 @@ var os4 = __toESM(require("os"));
|
|
|
7707
7981
|
// package.json
|
|
7708
7982
|
var package_default = {
|
|
7709
7983
|
name: "codeam-cli",
|
|
7710
|
-
version: "2.61.
|
|
7984
|
+
version: "2.61.87",
|
|
7711
7985
|
description: "Workflow-continuity bridge for AI coding agents. Wrap Claude Code or Codex in a PTY and supervise, approve, and redirect the session from any device \u2014 async. The terminal companion for CodeAgent Mobile.",
|
|
7712
7986
|
type: "commonjs",
|
|
7713
7987
|
main: "dist/index.js",
|
|
@@ -8983,7 +9257,7 @@ var CommandRelayService = class _CommandRelayService {
|
|
|
8983
9257
|
// fresh + clear the "CLI update available" banner after a self-update
|
|
8984
9258
|
// (a codespace that reinstalls @latest reconnects via heartbeat, not
|
|
8985
9259
|
// pair/reconnect). Older backends ignore the extra field.
|
|
8986
|
-
..."2.61.
|
|
9260
|
+
..."2.61.87" ? { ideVersion: "2.61.87" } : {}
|
|
8987
9261
|
}).then(() => log.trace("relay", `heartbeat ok online=${online}`)).catch((err) => log.trace("relay", `heartbeat failed online=${online}`, err));
|
|
8988
9262
|
}
|
|
8989
9263
|
/**
|
|
@@ -20156,7 +20430,7 @@ async function autoUpgradeBeforeCriticalCommand() {
|
|
|
20156
20430
|
if (process.env.NODE_ENV === "test") return;
|
|
20157
20431
|
if (process.env.CODEAM_DISABLE_UPDATE_CHECK === "1") return;
|
|
20158
20432
|
if (process.env.CI) return;
|
|
20159
|
-
const current = true ? "2.61.
|
|
20433
|
+
const current = true ? "2.61.87" : null;
|
|
20160
20434
|
if (!current) return;
|
|
20161
20435
|
const cache = readCache();
|
|
20162
20436
|
const fresh = cache && Date.now() - cache.fetchedAt < TTL_MS;
|
|
@@ -20173,7 +20447,7 @@ function checkForUpdates() {
|
|
|
20173
20447
|
if (process.env.CODEAM_DISABLE_UPDATE_CHECK === "1") return;
|
|
20174
20448
|
if (process.env.CI) return;
|
|
20175
20449
|
if (!process.stdout.isTTY) return;
|
|
20176
|
-
const current = true ? "2.61.
|
|
20450
|
+
const current = true ? "2.61.87" : null;
|
|
20177
20451
|
if (!current) return;
|
|
20178
20452
|
const cache = readCache();
|
|
20179
20453
|
const fresh = cache && Date.now() - cache.fetchedAt < TTL_MS;
|
|
@@ -20193,7 +20467,7 @@ var SELF_UPDATE_INTERVAL_MS = 60 * 60 * 1e3;
|
|
|
20193
20467
|
var SELF_UPDATE_VIEW_TIMEOUT_MS = 3e4;
|
|
20194
20468
|
var SELF_UPDATE_INSTALL_TIMEOUT_MS = 18e4;
|
|
20195
20469
|
function currentCliVersion() {
|
|
20196
|
-
return true ? "2.61.
|
|
20470
|
+
return true ? "2.61.87" : null;
|
|
20197
20471
|
}
|
|
20198
20472
|
function runCmd(cmd, args2, timeoutMs) {
|
|
20199
20473
|
return new Promise((resolve9) => {
|
|
@@ -20526,6 +20800,7 @@ function buildFleetBoxRunArgs(p2) {
|
|
|
20526
20800
|
];
|
|
20527
20801
|
}
|
|
20528
20802
|
var DOCKER_RUN_TIMEOUT_MS = 12e4;
|
|
20803
|
+
var DOCKER_RUN_WITH_PULL_TIMEOUT_MS = 6e5;
|
|
20529
20804
|
var defaultDockerRunner = {
|
|
20530
20805
|
run(args2, opts = {}) {
|
|
20531
20806
|
return new Promise((resolve9) => {
|
|
@@ -20979,7 +21254,9 @@ var HostAgentSupervisor = class {
|
|
|
20979
21254
|
}
|
|
20980
21255
|
const args2 = buildFleetBoxRunArgs(payload);
|
|
20981
21256
|
const res = await this.docker.run(args2, {
|
|
20982
|
-
|
|
21257
|
+
// `--pull=always` pulls the ~1 GB image inline — needs the pull bound, not
|
|
21258
|
+
// the 120 s fast bound (which SIGTERM'd the create mid-pull, code=143).
|
|
21259
|
+
timeoutMs: DOCKER_RUN_WITH_PULL_TIMEOUT_MS,
|
|
20983
21260
|
env: { CODEAM_ENROLL_TOKEN: payload.enrollToken }
|
|
20984
21261
|
});
|
|
20985
21262
|
if (res.code === 0) {
|
|
@@ -21019,7 +21296,8 @@ var HostAgentSupervisor = class {
|
|
|
21019
21296
|
limits: payload.limits
|
|
21020
21297
|
});
|
|
21021
21298
|
const res2 = await this.docker.run(args2, {
|
|
21022
|
-
|
|
21299
|
+
// Recreate also pulls `:latest` inline — use the pull bound, not 120 s.
|
|
21300
|
+
timeoutMs: DOCKER_RUN_WITH_PULL_TIMEOUT_MS,
|
|
21023
21301
|
env: { CODEAM_ENROLL_TOKEN: payload.enrollToken }
|
|
21024
21302
|
});
|
|
21025
21303
|
if (res2.code === 0) {
|
|
@@ -41213,7 +41491,7 @@ function checkChokidar() {
|
|
|
41213
41491
|
}
|
|
41214
41492
|
async function doctor(args2 = []) {
|
|
41215
41493
|
const json = args2.includes("--json");
|
|
41216
|
-
const cliVersion = true ? "2.61.
|
|
41494
|
+
const cliVersion = true ? "2.61.87" : "0.0.0-dev";
|
|
41217
41495
|
const apiBase2 = resolveApiBaseUrl();
|
|
41218
41496
|
const diagnosticId = (0, import_node_crypto13.randomUUID)();
|
|
41219
41497
|
log.info("doctor", `run id=${diagnosticId} cli=${cliVersion}`);
|
|
@@ -41465,207 +41743,8 @@ var IntegrationTokenClient = class {
|
|
|
41465
41743
|
}
|
|
41466
41744
|
};
|
|
41467
41745
|
|
|
41468
|
-
// src/integrations/stdio-proxy.ts
|
|
41469
|
-
var import_node_child_process31 = require("child_process");
|
|
41470
|
-
var import_node_readline3 = __toESM(require("readline"));
|
|
41471
|
-
var RESTART_CHECK_INTERVAL_MS = 3e4;
|
|
41472
|
-
var SIGKILL_ESCALATION_MS = 2e3;
|
|
41473
|
-
var TOOL_CALL_TIMEOUT_MS = (() => {
|
|
41474
|
-
const raw = Number(process.env.CODEAM_MCP_TOOL_TIMEOUT_MS);
|
|
41475
|
-
return Number.isFinite(raw) && raw > 0 ? raw : 12e4;
|
|
41476
|
-
})();
|
|
41477
|
-
var REPLAY_INIT_ID = "__codeam_replay_init__";
|
|
41478
|
-
var RestartableStdioProxy = class {
|
|
41479
|
-
constructor(opts) {
|
|
41480
|
-
this.opts = opts;
|
|
41481
|
-
}
|
|
41482
|
-
opts;
|
|
41483
|
-
child = null;
|
|
41484
|
-
childRl = null;
|
|
41485
|
-
initializeLine = null;
|
|
41486
|
-
inflight = /* @__PURE__ */ new Set();
|
|
41487
|
-
/** Per-`tools/call` request-id watchdog timers (see TOOL_CALL_TIMEOUT_MS). */
|
|
41488
|
-
toolTimers = /* @__PURE__ */ new Map();
|
|
41489
|
-
stdout = null;
|
|
41490
|
-
swapping = false;
|
|
41491
|
-
pendingClientLines = [];
|
|
41492
|
-
ended = () => void 0;
|
|
41493
|
-
async start() {
|
|
41494
|
-
const stdin = this.opts.stdin ?? process.stdin;
|
|
41495
|
-
const stdout = this.opts.stdout ?? process.stdout;
|
|
41496
|
-
this.stdout = stdout;
|
|
41497
|
-
await this.spawnChild(stdout);
|
|
41498
|
-
const rl = import_node_readline3.default.createInterface({ input: stdin, crlfDelay: Infinity });
|
|
41499
|
-
rl.on("line", (line) => this.onClientLine(line));
|
|
41500
|
-
rl.on("close", () => this.child?.stdin?.end());
|
|
41501
|
-
const timer = setInterval(() => this.checkRestart(stdout), RESTART_CHECK_INTERVAL_MS);
|
|
41502
|
-
timer.unref();
|
|
41503
|
-
return new Promise((resolve9) => {
|
|
41504
|
-
this.ended = (code) => {
|
|
41505
|
-
clearInterval(timer);
|
|
41506
|
-
process.exitCode = code;
|
|
41507
|
-
resolve9();
|
|
41508
|
-
};
|
|
41509
|
-
});
|
|
41510
|
-
}
|
|
41511
|
-
onClientLine(line) {
|
|
41512
|
-
if (this.swapping) {
|
|
41513
|
-
this.pendingClientLines.push(line);
|
|
41514
|
-
return;
|
|
41515
|
-
}
|
|
41516
|
-
try {
|
|
41517
|
-
const msg = JSON.parse(line);
|
|
41518
|
-
if (msg.method === "initialize" && this.initializeLine === null) {
|
|
41519
|
-
this.initializeLine = line;
|
|
41520
|
-
}
|
|
41521
|
-
if (msg.method === "notifications/cancelled") {
|
|
41522
|
-
const requestId = msg.params?.requestId;
|
|
41523
|
-
if (requestId !== void 0) {
|
|
41524
|
-
this.inflight.delete(requestId);
|
|
41525
|
-
this.clearToolTimeout(requestId);
|
|
41526
|
-
}
|
|
41527
|
-
}
|
|
41528
|
-
if (msg.id !== void 0 && msg.method !== void 0) {
|
|
41529
|
-
this.inflight.add(msg.id);
|
|
41530
|
-
if (msg.method === "tools/call") this.armToolTimeout(msg.id);
|
|
41531
|
-
}
|
|
41532
|
-
} catch {
|
|
41533
|
-
}
|
|
41534
|
-
this.child?.stdin?.write(line + "\n");
|
|
41535
|
-
}
|
|
41536
|
-
onChildLine(line, stdout) {
|
|
41537
|
-
try {
|
|
41538
|
-
const msg = JSON.parse(line);
|
|
41539
|
-
if (msg.id !== void 0 && msg.method === void 0) {
|
|
41540
|
-
if (msg.id === REPLAY_INIT_ID) {
|
|
41541
|
-
return;
|
|
41542
|
-
}
|
|
41543
|
-
this.inflight.delete(msg.id);
|
|
41544
|
-
this.clearToolTimeout(msg.id);
|
|
41545
|
-
}
|
|
41546
|
-
} catch {
|
|
41547
|
-
}
|
|
41548
|
-
stdout.write(line + "\n");
|
|
41549
|
-
if (this.inflight.size === 0) this.checkRestart(stdout);
|
|
41550
|
-
}
|
|
41551
|
-
/**
|
|
41552
|
-
* Arm a per-`tools/call` watchdog. If the server never answers this request
|
|
41553
|
-
* id within {@link TOOL_CALL_TIMEOUT_MS}, synthesize a JSON-RPC error response
|
|
41554
|
-
* to the client so the agent's turn unblocks (clean tool error) instead of
|
|
41555
|
-
* hanging forever with no Stop. Idempotent per id.
|
|
41556
|
-
*/
|
|
41557
|
-
armToolTimeout(id) {
|
|
41558
|
-
const existing = this.toolTimers.get(id);
|
|
41559
|
-
if (existing) clearTimeout(existing);
|
|
41560
|
-
const t2 = setTimeout(() => {
|
|
41561
|
-
this.toolTimers.delete(id);
|
|
41562
|
-
if (!this.inflight.has(id)) return;
|
|
41563
|
-
this.inflight.delete(id);
|
|
41564
|
-
process.stderr.write(
|
|
41565
|
-
`[codeam mcp-run] tools/call id=${String(id)} timed out after ${TOOL_CALL_TIMEOUT_MS}ms \u2014 server did not respond; failing the call so the turn can proceed
|
|
41566
|
-
`
|
|
41567
|
-
);
|
|
41568
|
-
const errResponse = {
|
|
41569
|
-
jsonrpc: "2.0",
|
|
41570
|
-
id,
|
|
41571
|
-
error: {
|
|
41572
|
-
code: -32001,
|
|
41573
|
-
message: `MCP tool call timed out after ${Math.round(TOOL_CALL_TIMEOUT_MS / 1e3)}s \u2014 the server did not respond. The target service/deployment may be unreachable (e.g. a Convex dev deployment is only reachable while \`convex dev\` is running \u2014 use a production deploy key or start \`convex dev\`).`
|
|
41574
|
-
}
|
|
41575
|
-
};
|
|
41576
|
-
(this.stdout ?? process.stdout).write(JSON.stringify(errResponse) + "\n");
|
|
41577
|
-
}, TOOL_CALL_TIMEOUT_MS);
|
|
41578
|
-
t2.unref();
|
|
41579
|
-
this.toolTimers.set(id, t2);
|
|
41580
|
-
}
|
|
41581
|
-
clearToolTimeout(id) {
|
|
41582
|
-
const t2 = this.toolTimers.get(id);
|
|
41583
|
-
if (t2) {
|
|
41584
|
-
clearTimeout(t2);
|
|
41585
|
-
this.toolTimers.delete(id);
|
|
41586
|
-
}
|
|
41587
|
-
}
|
|
41588
|
-
clearAllToolTimeouts() {
|
|
41589
|
-
for (const t2 of this.toolTimers.values()) clearTimeout(t2);
|
|
41590
|
-
this.toolTimers.clear();
|
|
41591
|
-
}
|
|
41592
|
-
/**
|
|
41593
|
-
* Failsafe wrapper for the fire-and-forget call sites (post-response check
|
|
41594
|
-
* + the 30 s timer): `maybeRestart` handles the token-fetch failure itself,
|
|
41595
|
-
* but nothing that escapes it may become an unhandled rejection — that
|
|
41596
|
-
* would kill the whole shim process.
|
|
41597
|
-
*/
|
|
41598
|
-
checkRestart(stdout) {
|
|
41599
|
-
this.maybeRestart(stdout).catch((err) => {
|
|
41600
|
-
process.stderr.write(
|
|
41601
|
-
`[codeam mcp-run] restart check failed (will retry): ${err instanceof Error ? err.message : String(err)}
|
|
41602
|
-
`
|
|
41603
|
-
);
|
|
41604
|
-
});
|
|
41605
|
-
}
|
|
41606
|
-
async maybeRestart(stdout) {
|
|
41607
|
-
if (this.swapping || !this.opts.shouldRestartNow()) return;
|
|
41608
|
-
if (this.inflight.size > 0 || this.initializeLine === null || !this.child) return;
|
|
41609
|
-
this.swapping = true;
|
|
41610
|
-
let spec;
|
|
41611
|
-
try {
|
|
41612
|
-
spec = await this.opts.spawnSpec();
|
|
41613
|
-
} catch (err) {
|
|
41614
|
-
process.stderr.write(
|
|
41615
|
-
`[codeam mcp-run] token refresh failed, keeping current server (will retry): ${err instanceof Error ? err.message : String(err)}
|
|
41616
|
-
`
|
|
41617
|
-
);
|
|
41618
|
-
this.swapping = false;
|
|
41619
|
-
for (const l of this.pendingClientLines.splice(0)) this.onClientLine(l);
|
|
41620
|
-
return;
|
|
41621
|
-
}
|
|
41622
|
-
const old = this.child;
|
|
41623
|
-
const oldRl = this.childRl;
|
|
41624
|
-
this.child = null;
|
|
41625
|
-
this.childRl = null;
|
|
41626
|
-
oldRl?.close();
|
|
41627
|
-
old.kill("SIGTERM");
|
|
41628
|
-
const escalation = setTimeout(() => {
|
|
41629
|
-
if (!old.killed || old.exitCode === null) old.kill("SIGKILL");
|
|
41630
|
-
}, SIGKILL_ESCALATION_MS);
|
|
41631
|
-
escalation.unref();
|
|
41632
|
-
try {
|
|
41633
|
-
await this.spawnChild(stdout, spec);
|
|
41634
|
-
const replayed = JSON.parse(this.initializeLine);
|
|
41635
|
-
this.child.stdin.write(JSON.stringify({ ...replayed, id: REPLAY_INIT_ID }) + "\n");
|
|
41636
|
-
this.child.stdin.write(
|
|
41637
|
-
JSON.stringify({ jsonrpc: "2.0", method: "notifications/initialized" }) + "\n"
|
|
41638
|
-
);
|
|
41639
|
-
} finally {
|
|
41640
|
-
this.swapping = false;
|
|
41641
|
-
for (const l of this.pendingClientLines.splice(0)) this.onClientLine(l);
|
|
41642
|
-
}
|
|
41643
|
-
}
|
|
41644
|
-
async spawnChild(stdout, preResolved) {
|
|
41645
|
-
const spec = preResolved ?? await this.opts.spawnSpec();
|
|
41646
|
-
const spawn44 = this.opts.spawnImpl ?? import_node_child_process31.spawn;
|
|
41647
|
-
const child = spawn44(spec.command, spec.args, {
|
|
41648
|
-
env: { ...process.env, ...spec.env },
|
|
41649
|
-
// env only — never argv
|
|
41650
|
-
stdio: ["pipe", "pipe", "inherit"]
|
|
41651
|
-
});
|
|
41652
|
-
this.child = child;
|
|
41653
|
-
child.stdin?.on("error", () => void 0);
|
|
41654
|
-
const rl = import_node_readline3.default.createInterface({ input: child.stdout, crlfDelay: Infinity });
|
|
41655
|
-
this.childRl = rl;
|
|
41656
|
-
rl.on("line", (line) => {
|
|
41657
|
-
if (child !== this.child) return;
|
|
41658
|
-
this.onChildLine(line, stdout);
|
|
41659
|
-
});
|
|
41660
|
-
child.on("exit", (code) => {
|
|
41661
|
-
if (child !== this.child) return;
|
|
41662
|
-
this.clearAllToolTimeouts();
|
|
41663
|
-
this.ended(code ?? 1);
|
|
41664
|
-
});
|
|
41665
|
-
}
|
|
41666
|
-
};
|
|
41667
|
-
|
|
41668
41746
|
// src/integrations/mcp-run.ts
|
|
41747
|
+
init_stdio_proxy();
|
|
41669
41748
|
var RESTART_AHEAD_MS = 5 * 60 * 1e3;
|
|
41670
41749
|
function resolveDelivery(id) {
|
|
41671
41750
|
const fromRegistry = isKnownIntegrationId(id) ? getIntegration(id).delivery.mcp ?? null : null;
|
|
@@ -41803,7 +41882,7 @@ async function mcpRun(args2) {
|
|
|
41803
41882
|
// src/commands/version.ts
|
|
41804
41883
|
var import_picocolors15 = __toESM(require("picocolors"));
|
|
41805
41884
|
function version2() {
|
|
41806
|
-
const v = true ? "2.61.
|
|
41885
|
+
const v = true ? "2.61.87" : "unknown";
|
|
41807
41886
|
console.log(`${import_picocolors15.default.bold("codeam-cli")} ${import_picocolors15.default.cyan(v)}`);
|
|
41808
41887
|
}
|
|
41809
41888
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "codeam-cli",
|
|
3
|
-
"version": "2.61.
|
|
3
|
+
"version": "2.61.87",
|
|
4
4
|
"description": "Workflow-continuity bridge for AI coding agents. Wrap Claude Code or Codex in a PTY and supervise, approve, and redirect the session from any device — async. The terminal companion for CodeAgent Mobile.",
|
|
5
5
|
"type": "commonjs",
|
|
6
6
|
"main": "dist/index.js",
|