codeam-cli 2.61.84 → 2.61.86

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 (3) hide show
  1. package/CHANGELOG.md +12 -0
  2. package/dist/index.js +300 -210
  3. 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.85] — 2026-08-06
8
+
9
+ ### Fixed
10
+
11
+ - **cli:** Resume the prior conversation on a warm re-launch into an existing workspace (#591)
12
+
13
+ ## [2.61.84] — 2026-08-05
14
+
15
+ ### Fixed
16
+
17
+ - **cli:** Internal-path guard no longer denies the self-hosted workspace (#587)
18
+
7
19
  ## [2.61.83] — 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.84" : "0.0.0-dev",
7803
+ cliVersion: true ? "2.61.86" : "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.84",
7984
+ version: "2.61.86",
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.84" ? { ideVersion: "2.61.84" } : {}
9260
+ ..."2.61.86" ? { ideVersion: "2.61.86" } : {}
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.84" : null;
20433
+ const current = true ? "2.61.86" : 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.84" : null;
20450
+ const current = true ? "2.61.86" : 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.84" : null;
20470
+ return true ? "2.61.86" : null;
20197
20471
  }
20198
20472
  function runCmd(cmd, args2, timeoutMs) {
20199
20473
  return new Promise((resolve9) => {
@@ -21283,6 +21557,21 @@ var HostAgentSupervisor = class {
21283
21557
  persistOrClearSkillsFromPayload(
21284
21558
  payload.skills
21285
21559
  );
21560
+ if (!payload.suppressOnboardingWelcome) {
21561
+ try {
21562
+ const cfgDir = childEnv.CLAUDE_CONFIG_DIR || path48.join(os39.homedir(), ".claude");
21563
+ const projectDir = path48.join(cfgDir, "projects", encodeCwd(cwd));
21564
+ const hasPriorConversation = fs45.existsSync(projectDir) && fs45.readdirSync(projectDir).some((f) => f.endsWith(".jsonl"));
21565
+ if (hasPriorConversation) {
21566
+ childEnv.CODEAM_RESUME_LATEST = "1";
21567
+ log.info(
21568
+ "host-agent",
21569
+ `deploy: prior conversation in ${projectDir} \u2014 resuming latest for continuity`
21570
+ );
21571
+ }
21572
+ } catch {
21573
+ }
21574
+ }
21286
21575
  report("spawning", "starting agent");
21287
21576
  const proc = this.spawnSessionChild(childEnv, cwd, extraArgs);
21288
21577
  const child = {
@@ -25499,13 +25788,13 @@ function resolveGlobalNodeModulesDir(opts) {
25499
25788
  var STALE_STAGING_AGE_MS = CLI_UPDATE_INSTALL_TIMEOUT_MS;
25500
25789
  function sweepStaleCliStagingDirs(nodeModulesDir, now = Date.now(), deps) {
25501
25790
  if (!nodeModulesDir) return 0;
25502
- const readdirSync12 = deps?.readdirSync ?? fs61.readdirSync;
25791
+ const readdirSync13 = deps?.readdirSync ?? fs61.readdirSync;
25503
25792
  const statSync17 = deps?.statSync ?? fs61.statSync;
25504
25793
  const rmSync9 = deps?.rmSync ?? fs61.rmSync;
25505
25794
  let removed = 0;
25506
25795
  let entries;
25507
25796
  try {
25508
- entries = readdirSync12(nodeModulesDir);
25797
+ entries = readdirSync13(nodeModulesDir);
25509
25798
  } catch {
25510
25799
  return 0;
25511
25800
  }
@@ -41198,7 +41487,7 @@ function checkChokidar() {
41198
41487
  }
41199
41488
  async function doctor(args2 = []) {
41200
41489
  const json = args2.includes("--json");
41201
- const cliVersion = true ? "2.61.84" : "0.0.0-dev";
41490
+ const cliVersion = true ? "2.61.86" : "0.0.0-dev";
41202
41491
  const apiBase2 = resolveApiBaseUrl();
41203
41492
  const diagnosticId = (0, import_node_crypto13.randomUUID)();
41204
41493
  log.info("doctor", `run id=${diagnosticId} cli=${cliVersion}`);
@@ -41450,207 +41739,8 @@ var IntegrationTokenClient = class {
41450
41739
  }
41451
41740
  };
41452
41741
 
41453
- // src/integrations/stdio-proxy.ts
41454
- var import_node_child_process31 = require("child_process");
41455
- var import_node_readline3 = __toESM(require("readline"));
41456
- var RESTART_CHECK_INTERVAL_MS = 3e4;
41457
- var SIGKILL_ESCALATION_MS = 2e3;
41458
- var TOOL_CALL_TIMEOUT_MS = (() => {
41459
- const raw = Number(process.env.CODEAM_MCP_TOOL_TIMEOUT_MS);
41460
- return Number.isFinite(raw) && raw > 0 ? raw : 12e4;
41461
- })();
41462
- var REPLAY_INIT_ID = "__codeam_replay_init__";
41463
- var RestartableStdioProxy = class {
41464
- constructor(opts) {
41465
- this.opts = opts;
41466
- }
41467
- opts;
41468
- child = null;
41469
- childRl = null;
41470
- initializeLine = null;
41471
- inflight = /* @__PURE__ */ new Set();
41472
- /** Per-`tools/call` request-id watchdog timers (see TOOL_CALL_TIMEOUT_MS). */
41473
- toolTimers = /* @__PURE__ */ new Map();
41474
- stdout = null;
41475
- swapping = false;
41476
- pendingClientLines = [];
41477
- ended = () => void 0;
41478
- async start() {
41479
- const stdin = this.opts.stdin ?? process.stdin;
41480
- const stdout = this.opts.stdout ?? process.stdout;
41481
- this.stdout = stdout;
41482
- await this.spawnChild(stdout);
41483
- const rl = import_node_readline3.default.createInterface({ input: stdin, crlfDelay: Infinity });
41484
- rl.on("line", (line) => this.onClientLine(line));
41485
- rl.on("close", () => this.child?.stdin?.end());
41486
- const timer = setInterval(() => this.checkRestart(stdout), RESTART_CHECK_INTERVAL_MS);
41487
- timer.unref();
41488
- return new Promise((resolve9) => {
41489
- this.ended = (code) => {
41490
- clearInterval(timer);
41491
- process.exitCode = code;
41492
- resolve9();
41493
- };
41494
- });
41495
- }
41496
- onClientLine(line) {
41497
- if (this.swapping) {
41498
- this.pendingClientLines.push(line);
41499
- return;
41500
- }
41501
- try {
41502
- const msg = JSON.parse(line);
41503
- if (msg.method === "initialize" && this.initializeLine === null) {
41504
- this.initializeLine = line;
41505
- }
41506
- if (msg.method === "notifications/cancelled") {
41507
- const requestId = msg.params?.requestId;
41508
- if (requestId !== void 0) {
41509
- this.inflight.delete(requestId);
41510
- this.clearToolTimeout(requestId);
41511
- }
41512
- }
41513
- if (msg.id !== void 0 && msg.method !== void 0) {
41514
- this.inflight.add(msg.id);
41515
- if (msg.method === "tools/call") this.armToolTimeout(msg.id);
41516
- }
41517
- } catch {
41518
- }
41519
- this.child?.stdin?.write(line + "\n");
41520
- }
41521
- onChildLine(line, stdout) {
41522
- try {
41523
- const msg = JSON.parse(line);
41524
- if (msg.id !== void 0 && msg.method === void 0) {
41525
- if (msg.id === REPLAY_INIT_ID) {
41526
- return;
41527
- }
41528
- this.inflight.delete(msg.id);
41529
- this.clearToolTimeout(msg.id);
41530
- }
41531
- } catch {
41532
- }
41533
- stdout.write(line + "\n");
41534
- if (this.inflight.size === 0) this.checkRestart(stdout);
41535
- }
41536
- /**
41537
- * Arm a per-`tools/call` watchdog. If the server never answers this request
41538
- * id within {@link TOOL_CALL_TIMEOUT_MS}, synthesize a JSON-RPC error response
41539
- * to the client so the agent's turn unblocks (clean tool error) instead of
41540
- * hanging forever with no Stop. Idempotent per id.
41541
- */
41542
- armToolTimeout(id) {
41543
- const existing = this.toolTimers.get(id);
41544
- if (existing) clearTimeout(existing);
41545
- const t2 = setTimeout(() => {
41546
- this.toolTimers.delete(id);
41547
- if (!this.inflight.has(id)) return;
41548
- this.inflight.delete(id);
41549
- process.stderr.write(
41550
- `[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
41551
- `
41552
- );
41553
- const errResponse = {
41554
- jsonrpc: "2.0",
41555
- id,
41556
- error: {
41557
- code: -32001,
41558
- 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\`).`
41559
- }
41560
- };
41561
- (this.stdout ?? process.stdout).write(JSON.stringify(errResponse) + "\n");
41562
- }, TOOL_CALL_TIMEOUT_MS);
41563
- t2.unref();
41564
- this.toolTimers.set(id, t2);
41565
- }
41566
- clearToolTimeout(id) {
41567
- const t2 = this.toolTimers.get(id);
41568
- if (t2) {
41569
- clearTimeout(t2);
41570
- this.toolTimers.delete(id);
41571
- }
41572
- }
41573
- clearAllToolTimeouts() {
41574
- for (const t2 of this.toolTimers.values()) clearTimeout(t2);
41575
- this.toolTimers.clear();
41576
- }
41577
- /**
41578
- * Failsafe wrapper for the fire-and-forget call sites (post-response check
41579
- * + the 30 s timer): `maybeRestart` handles the token-fetch failure itself,
41580
- * but nothing that escapes it may become an unhandled rejection — that
41581
- * would kill the whole shim process.
41582
- */
41583
- checkRestart(stdout) {
41584
- this.maybeRestart(stdout).catch((err) => {
41585
- process.stderr.write(
41586
- `[codeam mcp-run] restart check failed (will retry): ${err instanceof Error ? err.message : String(err)}
41587
- `
41588
- );
41589
- });
41590
- }
41591
- async maybeRestart(stdout) {
41592
- if (this.swapping || !this.opts.shouldRestartNow()) return;
41593
- if (this.inflight.size > 0 || this.initializeLine === null || !this.child) return;
41594
- this.swapping = true;
41595
- let spec;
41596
- try {
41597
- spec = await this.opts.spawnSpec();
41598
- } catch (err) {
41599
- process.stderr.write(
41600
- `[codeam mcp-run] token refresh failed, keeping current server (will retry): ${err instanceof Error ? err.message : String(err)}
41601
- `
41602
- );
41603
- this.swapping = false;
41604
- for (const l of this.pendingClientLines.splice(0)) this.onClientLine(l);
41605
- return;
41606
- }
41607
- const old = this.child;
41608
- const oldRl = this.childRl;
41609
- this.child = null;
41610
- this.childRl = null;
41611
- oldRl?.close();
41612
- old.kill("SIGTERM");
41613
- const escalation = setTimeout(() => {
41614
- if (!old.killed || old.exitCode === null) old.kill("SIGKILL");
41615
- }, SIGKILL_ESCALATION_MS);
41616
- escalation.unref();
41617
- try {
41618
- await this.spawnChild(stdout, spec);
41619
- const replayed = JSON.parse(this.initializeLine);
41620
- this.child.stdin.write(JSON.stringify({ ...replayed, id: REPLAY_INIT_ID }) + "\n");
41621
- this.child.stdin.write(
41622
- JSON.stringify({ jsonrpc: "2.0", method: "notifications/initialized" }) + "\n"
41623
- );
41624
- } finally {
41625
- this.swapping = false;
41626
- for (const l of this.pendingClientLines.splice(0)) this.onClientLine(l);
41627
- }
41628
- }
41629
- async spawnChild(stdout, preResolved) {
41630
- const spec = preResolved ?? await this.opts.spawnSpec();
41631
- const spawn44 = this.opts.spawnImpl ?? import_node_child_process31.spawn;
41632
- const child = spawn44(spec.command, spec.args, {
41633
- env: { ...process.env, ...spec.env },
41634
- // env only — never argv
41635
- stdio: ["pipe", "pipe", "inherit"]
41636
- });
41637
- this.child = child;
41638
- child.stdin?.on("error", () => void 0);
41639
- const rl = import_node_readline3.default.createInterface({ input: child.stdout, crlfDelay: Infinity });
41640
- this.childRl = rl;
41641
- rl.on("line", (line) => {
41642
- if (child !== this.child) return;
41643
- this.onChildLine(line, stdout);
41644
- });
41645
- child.on("exit", (code) => {
41646
- if (child !== this.child) return;
41647
- this.clearAllToolTimeouts();
41648
- this.ended(code ?? 1);
41649
- });
41650
- }
41651
- };
41652
-
41653
41742
  // src/integrations/mcp-run.ts
41743
+ init_stdio_proxy();
41654
41744
  var RESTART_AHEAD_MS = 5 * 60 * 1e3;
41655
41745
  function resolveDelivery(id) {
41656
41746
  const fromRegistry = isKnownIntegrationId(id) ? getIntegration(id).delivery.mcp ?? null : null;
@@ -41788,7 +41878,7 @@ async function mcpRun(args2) {
41788
41878
  // src/commands/version.ts
41789
41879
  var import_picocolors15 = __toESM(require("picocolors"));
41790
41880
  function version2() {
41791
- const v = true ? "2.61.84" : "unknown";
41881
+ const v = true ? "2.61.86" : "unknown";
41792
41882
  console.log(`${import_picocolors15.default.bold("codeam-cli")} ${import_picocolors15.default.cyan(v)}`);
41793
41883
  }
41794
41884
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "codeam-cli",
3
- "version": "2.61.84",
3
+ "version": "2.61.86",
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",