codeam-cli 2.73.3 → 2.73.5

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 +140 -25
  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.73.4] — 2026-09-03
8
+
9
+ ### Fixed
10
+
11
+ - **cli:** MCP startup budget that fits our shim + stop the baton mirror replaying history as live turns (#686)
12
+
13
+ ## [2.73.3] — 2026-09-02
14
+
15
+ ### Fixed
16
+
17
+ - **cli:** Debounce MCP reprovisions so a run of changes costs ONE respawn (#684)
18
+
7
19
  ## [2.73.2] — 2026-09-02
8
20
 
9
21
  ### CI
package/dist/index.js CHANGED
@@ -1872,8 +1872,27 @@ var INTEGRATION_REGISTRY = {
1872
1872
  mcp: {
1873
1873
  // Postman's OFFICIAL stdio MCP server (Node). BYO key via POSTMAN_API_KEY
1874
1874
  // (env only). Version PINNED; bump only after re-verifying headless.
1875
+ //
1876
+ // ⚠️ `--ignore-scripts` IS LOAD-BEARING — without it this integration
1877
+ // does not work at all, on any box, and never did. The package declares
1878
+ // a `preinstall` guard of `npx only-allow pnpm`, so npm REFUSES to
1879
+ // install it:
1880
+ // info run @postman/postman-mcp-server@2.11.2 preinstall npx only-allow pnpm
1881
+ // info run @postman/postman-mcp-server@2.11.2 preinstall { code: 1 }
1882
+ // and `npx -y @postman/postman-mcp-server@2.11.2` exits 1 before the
1883
+ // server is ever reached — which the agent reports as
1884
+ // `Server "postman" is not connected`, indistinguishable from a slow
1885
+ // start (found while pre-warming the box image, 2026-09-03; the user's
1886
+ // report was "Postman: idem", alongside clickup/trello, whose cause was
1887
+ // entirely different).
1888
+ //
1889
+ // Skipping scripts is safe here: the guard is the only preinstall step
1890
+ // and the published tarball already ships the built
1891
+ // `dist/src/index.js` its `bin` points at. Verified on node:22-slim:
1892
+ // `npx -y <pkg>` → exit 1; `npx -y --ignore-scripts <pkg>` with a key in
1893
+ // the env → starts; and warmed once, the bin resolves offline.
1875
1894
  command: "npx",
1876
- args: ["-y", "@postman/postman-mcp-server@2.11.2"],
1895
+ args: ["-y", "--ignore-scripts", "@postman/postman-mcp-server@2.11.2"],
1877
1896
  envMapping: {
1878
1897
  POSTMAN_API_KEY: "accessToken"
1879
1898
  }
@@ -8097,7 +8116,7 @@ function readAnonId() {
8097
8116
  }
8098
8117
  function superProperties() {
8099
8118
  return {
8100
- cliVersion: true ? "2.73.3" : "0.0.0-dev",
8119
+ cliVersion: true ? "2.73.5" : "0.0.0-dev",
8101
8120
  nodeVersion: process.version,
8102
8121
  platform: process.platform,
8103
8122
  arch: process.arch,
@@ -8339,7 +8358,7 @@ var http = __toESM(require("http"));
8339
8358
  // package.json
8340
8359
  var package_default = {
8341
8360
  name: "codeam-cli",
8342
- version: "2.73.3",
8361
+ version: "2.73.5",
8343
8362
  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.",
8344
8363
  type: "commonjs",
8345
8364
  main: "dist/index.js",
@@ -9932,7 +9951,7 @@ var CommandRelayService = class _CommandRelayService {
9932
9951
  // fresh + clear the "CLI update available" banner after a self-update
9933
9952
  // (a codespace that reinstalls @latest reconnects via heartbeat, not
9934
9953
  // pair/reconnect). Older backends ignore the extra field.
9935
- ..."2.73.3" ? { ideVersion: "2.73.3" } : {}
9954
+ ..."2.73.5" ? { ideVersion: "2.73.5" } : {}
9936
9955
  }).then(() => log.trace("relay", `heartbeat ok online=${online}`)).catch((err) => log.trace("relay", `heartbeat failed online=${online}`, err));
9937
9956
  }
9938
9957
  /**
@@ -22208,7 +22227,7 @@ async function autoUpgradeBeforeCriticalCommand() {
22208
22227
  if (process.env.NODE_ENV === "test") return;
22209
22228
  if (process.env.CODEAM_DISABLE_UPDATE_CHECK === "1") return;
22210
22229
  if (process.env.CI) return;
22211
- const current2 = true ? "2.73.3" : null;
22230
+ const current2 = true ? "2.73.5" : null;
22212
22231
  if (!current2) return;
22213
22232
  const cache = readCache();
22214
22233
  const fresh = cache && Date.now() - cache.fetchedAt < TTL_MS;
@@ -22225,7 +22244,7 @@ function checkForUpdates() {
22225
22244
  if (process.env.CODEAM_DISABLE_UPDATE_CHECK === "1") return;
22226
22245
  if (process.env.CI) return;
22227
22246
  if (!process.stdout.isTTY) return;
22228
- const current2 = true ? "2.73.3" : null;
22247
+ const current2 = true ? "2.73.5" : null;
22229
22248
  if (!current2) return;
22230
22249
  const cache = readCache();
22231
22250
  const fresh = cache && Date.now() - cache.fetchedAt < TTL_MS;
@@ -22248,7 +22267,7 @@ var SELF_UPDATE_INSTALL_TIMEOUT_MS = 18e4;
22248
22267
  var SELF_UPDATE_LS_TIMEOUT_MS = 15e3;
22249
22268
  var SUDO_PREFLIGHT_TIMEOUT_MS = 5e3;
22250
22269
  function currentCliVersion() {
22251
- return true ? "2.73.3" : null;
22270
+ return true ? "2.73.5" : null;
22252
22271
  }
22253
22272
  async function installedVersion(deps) {
22254
22273
  const ls = await deps.run(
@@ -35677,6 +35696,7 @@ var _acpStartSeam = {
35677
35696
  var PROTOCOL_VERSION3 = 1;
35678
35697
  var FATAL_STARTUP_RE = /IneligibleTierError|UNSUPPORTED_CLIENT|no longer supported for Gemini Code Assist|not eligible for Gemini Code Assist|Error authenticating|ProjectIdRequiredError/i;
35679
35698
  var NEWSESSION_TIMEOUT_MS = 12e4;
35699
+ var MCP_STARTUP_TIMEOUT_MS = NEWSESSION_TIMEOUT_MS - 3e4;
35680
35700
  var LOADSESSION_TIMEOUT_MS = 6e4;
35681
35701
  var PROMPT_IDLE_TIMEOUT_MS = 9e4;
35682
35702
  var PROMPT_ACTIVE_IDLE_TIMEOUT_MS = 6e5;
@@ -35855,7 +35875,15 @@ var AcpClient = class {
35855
35875
  // extraEnv (e.g. CLAUDE_CODE_DISABLE_1M_CONTEXT=1 on an on-demand
35856
35876
  // re-spawn) layers over process.env; PATH stays last so the augmented
35857
35877
  // PATH always wins.
35858
- env: { ...process.env, ...this.opts.extraEnv ?? {}, PATH: augmentedPath },
35878
+ //
35879
+ // MCP_TIMEOUT comes FIRST so the caller can still override it, and it is
35880
+ // load-bearing — see MCP_STARTUP_TIMEOUT_MS.
35881
+ env: {
35882
+ ...process.env,
35883
+ MCP_TIMEOUT: String(MCP_STARTUP_TIMEOUT_MS),
35884
+ ...this.opts.extraEnv ?? {},
35885
+ PATH: augmentedPath
35886
+ },
35859
35887
  stdio: ["pipe", "pipe", "pipe"]
35860
35888
  });
35861
35889
  this.child = child;
@@ -43702,12 +43730,22 @@ var TranscriptMirror = class {
43702
43730
  unwatch = null;
43703
43731
  pollHandle = null;
43704
43732
  attached = false;
43733
+ /** True while `start()`'s own synchronous attach attempt is running, so
43734
+ * `tryAttach` can tell "the file was already there" from "it appeared while
43735
+ * we waited". The distinction is the whole basis for whether the first
43736
+ * batch is history or live content. */
43737
+ attachingAtStart = false;
43705
43738
  pollIntervalMs;
43706
43739
  waitTimeoutMs;
43707
43740
  setIntervalFn;
43708
43741
  clearIntervalFn;
43709
43742
  start() {
43710
- if (this.tryAttach()) return;
43743
+ this.attachingAtStart = true;
43744
+ try {
43745
+ if (this.tryAttach()) return;
43746
+ } finally {
43747
+ this.attachingAtStart = false;
43748
+ }
43711
43749
  let waited = 0;
43712
43750
  this.pollHandle = this.setIntervalFn(() => {
43713
43751
  waited += this.pollIntervalMs;
@@ -43730,7 +43768,7 @@ var TranscriptMirror = class {
43730
43768
  const file = this.deps.runtime.resolveHistoryFile?.(this.deps.cwd, this.deps.conversationId);
43731
43769
  if (!file) return false;
43732
43770
  this.attached = true;
43733
- this.emit(file);
43771
+ this.emit(file, this.attachingAtStart);
43734
43772
  const watch4 = this.deps.watch ?? defaultWatch;
43735
43773
  this.unwatch = watch4(file, () => this.emit(file));
43736
43774
  return true;
@@ -43741,7 +43779,7 @@ var TranscriptMirror = class {
43741
43779
  this.pollHandle = null;
43742
43780
  }
43743
43781
  }
43744
- emit(file) {
43782
+ emit(file, preexisting = false) {
43745
43783
  let all;
43746
43784
  try {
43747
43785
  all = this.deps.runtime.parseHistoryFile(file);
@@ -43751,7 +43789,7 @@ var TranscriptMirror = class {
43751
43789
  if (all.length <= this.emitted) return;
43752
43790
  const delta = all.slice(this.emitted);
43753
43791
  this.emitted = all.length;
43754
- this.deps.onNewMessages(delta);
43792
+ this.deps.onNewMessages(delta, { preexisting });
43755
43793
  }
43756
43794
  };
43757
43795
  function defaultWatch(file, onChange) {
@@ -43803,10 +43841,9 @@ async function publishMirroredTurnsLive(publisher, messages) {
43803
43841
  }
43804
43842
  }
43805
43843
  function makeMirrorOnNewMessages(deps) {
43806
- let isFirstEmit = true;
43807
43844
  let publishChain = Promise.resolve();
43808
43845
  const conversation = [];
43809
- return (messages) => {
43846
+ return (messages, meta) => {
43810
43847
  const relevant = messages.filter((m) => m.role !== "system");
43811
43848
  if (relevant.length === 0) return;
43812
43849
  const mapped = relevant.map((m) => ({
@@ -43821,7 +43858,7 @@ function makeMirrorOnNewMessages(deps) {
43821
43858
  sessionId: deps.conversationId,
43822
43859
  messages: conversation.slice()
43823
43860
  });
43824
- if (deps.fresh || !isFirstEmit) {
43861
+ if (meta?.preexisting !== true) {
43825
43862
  publishChain = publishChain.then(() => publishMirroredTurnsLive(deps.publisher, relevant)).catch((err) => {
43826
43863
  log.warn(
43827
43864
  "wireBaton",
@@ -43829,7 +43866,6 @@ function makeMirrorOnNewMessages(deps) {
43829
43866
  );
43830
43867
  });
43831
43868
  }
43832
- isFirstEmit = false;
43833
43869
  };
43834
43870
  }
43835
43871
  function makeSerializedBatonPoster(post2) {
@@ -43956,9 +43992,8 @@ async function runBatonSession(opts) {
43956
43992
  onConversationSwitch: (id) => controller.switchConversation(id)
43957
43993
  });
43958
43994
  let mirror = null;
43959
- let firstLocalDrive = true;
43960
43995
  let lastPublished = null;
43961
- const startMirror = (conversationId, fresh) => {
43996
+ const startMirror = (conversationId) => {
43962
43997
  mirror?.stop();
43963
43998
  mirror = new TranscriptMirror({
43964
43999
  runtime,
@@ -43967,8 +44002,7 @@ async function runBatonSession(opts) {
43967
44002
  onNewMessages: makeMirrorOnNewMessages({
43968
44003
  publisher,
43969
44004
  agentId: opts.agent,
43970
- conversationId,
43971
- fresh
44005
+ conversationId
43972
44006
  })
43973
44007
  });
43974
44008
  mirror.start();
@@ -43990,9 +44024,7 @@ async function runBatonSession(opts) {
43990
44024
  publishState: (state, driver, conversationId) => {
43991
44025
  publishBatonState(state, driver, conversationId);
43992
44026
  if (state === "LOCAL_DRIVE" && conversationId) {
43993
- const switched = lastPublished === "LOCAL_DRIVE";
43994
- startMirror(conversationId, firstLocalDrive || switched);
43995
- firstLocalDrive = false;
44027
+ startMirror(conversationId);
43996
44028
  } else if (state !== "LOCAL_DRIVE") {
43997
44029
  mirror?.stop();
43998
44030
  }
@@ -47086,7 +47118,7 @@ function checkChokidar() {
47086
47118
  }
47087
47119
  async function doctor(args2 = []) {
47088
47120
  const json = args2.includes("--json");
47089
- const cliVersion = true ? "2.73.3" : "0.0.0-dev";
47121
+ const cliVersion = true ? "2.73.5" : "0.0.0-dev";
47090
47122
  const apiBase2 = resolveApiBaseUrl();
47091
47123
  const diagnosticId = (0, import_node_crypto13.randomUUID)();
47092
47124
  log.info("doctor", `run id=${diagnosticId} cli=${cliVersion}`);
@@ -47474,10 +47506,90 @@ async function mcpRun(args2) {
47474
47506
  await proxy.start();
47475
47507
  }
47476
47508
 
47509
+ // src/integrations/mcp-warm.ts
47510
+ var import_node_child_process35 = require("child_process");
47511
+ function specOf(delivery) {
47512
+ if (!delivery.command) return null;
47513
+ const spec = (delivery.args ?? []).find((a) => !a.startsWith("-"));
47514
+ return spec ?? null;
47515
+ }
47516
+ function launcherFlagsOf(delivery) {
47517
+ const args2 = delivery.args ?? [];
47518
+ const specIndex = args2.findIndex((a) => !a.startsWith("-"));
47519
+ return specIndex === -1 ? [...args2] : args2.slice(0, specIndex);
47520
+ }
47521
+ function warmTargets() {
47522
+ const bySpec = /* @__PURE__ */ new Map();
47523
+ for (const integration of getEnabledIntegrations()) {
47524
+ const delivery = integration.delivery?.mcp;
47525
+ if (!delivery) continue;
47526
+ const spec = specOf(delivery);
47527
+ if (!spec) continue;
47528
+ if (!bySpec.has(spec)) {
47529
+ bySpec.set(spec, {
47530
+ id: integration.id,
47531
+ launcher: delivery.command,
47532
+ flags: launcherFlagsOf(delivery),
47533
+ spec
47534
+ });
47535
+ }
47536
+ }
47537
+ return [...bySpec.values()];
47538
+ }
47539
+ function warmCommand(target) {
47540
+ if (target.launcher === "npx") {
47541
+ const extra = target.flags.filter((f) => f !== "-y");
47542
+ return { command: "npx", args: ["-y", ...extra, "--package", target.spec, "-c", "true"] };
47543
+ }
47544
+ if (target.launcher === "uvx") {
47545
+ return { command: "uv", args: ["tool", "install", target.spec] };
47546
+ }
47547
+ return null;
47548
+ }
47549
+ async function mcpWarm(argv = []) {
47550
+ const timeoutMs = 3e5;
47551
+ const targets = warmTargets();
47552
+ const only = argv.filter((a) => !a.startsWith("-"));
47553
+ const selected = only.length > 0 ? targets.filter((t2) => only.includes(t2.id)) : targets;
47554
+ const result = { warmed: [], skipped: [], failed: [] };
47555
+ console.log(`[codeam mcp-warm] ${selected.length} MCP package(s) to pre-fetch`);
47556
+ for (const target of selected) {
47557
+ const cmd = warmCommand(target);
47558
+ if (!cmd) {
47559
+ result.skipped.push(`${target.spec} (no warm recipe for '${target.launcher}')`);
47560
+ continue;
47561
+ }
47562
+ const started = Date.now();
47563
+ const run = (0, import_node_child_process35.spawnSync)(cmd.command, cmd.args, {
47564
+ stdio: ["ignore", "ignore", "pipe"],
47565
+ timeout: timeoutMs,
47566
+ encoding: "utf8"
47567
+ });
47568
+ const secs = ((Date.now() - started) / 1e3).toFixed(1);
47569
+ if (run.status === 0) {
47570
+ result.warmed.push(target.spec);
47571
+ console.log(` OK ${target.spec} (${secs}s)`);
47572
+ } else {
47573
+ const noise = /^npm (warn|notice)\b/;
47574
+ const lines = (run.stderr ?? "").split("\n").map((l) => l.trim()).filter((l) => l.length > 0 && !noise.test(l));
47575
+ const reason = run.error?.message ?? lines[lines.length - 1] ?? `exit ${String(run.status)} (no stderr)`;
47576
+ result.failed.push({ spec: target.spec, reason });
47577
+ console.log(` MISS ${target.spec} (${secs}s) \u2014 fetched at runtime instead: ${reason}`);
47578
+ }
47579
+ }
47580
+ console.log(
47581
+ `[codeam mcp-warm] warmed=${result.warmed.length} failed=${result.failed.length} skipped=${result.skipped.length}`
47582
+ );
47583
+ log.info(
47584
+ "mcpWarm",
47585
+ `warmed=${result.warmed.length} failed=${result.failed.length} skipped=${result.skipped.length}`
47586
+ );
47587
+ }
47588
+
47477
47589
  // src/commands/version.ts
47478
47590
  var import_picocolors15 = __toESM(require("picocolors"));
47479
47591
  function version2() {
47480
- const v = true ? "2.73.3" : "unknown";
47592
+ const v = true ? "2.73.5" : "unknown";
47481
47593
  console.log(`${import_picocolors15.default.bold("codeam-cli")} ${import_picocolors15.default.cyan(v)}`);
47482
47594
  }
47483
47595
 
@@ -47678,6 +47790,9 @@ async function main() {
47678
47790
  if (command === "mcp-run") {
47679
47791
  return mcpRun(args);
47680
47792
  }
47793
+ if (command === "mcp-warm") {
47794
+ return mcpWarm(args);
47795
+ }
47681
47796
  if (command === "deploy") {
47682
47797
  if (args[0] === "ls" || args[0] === "list") return deployList();
47683
47798
  if (args[0] === "stop" || args[0] === "remove") return deployStop();
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "codeam-cli",
3
- "version": "2.73.3",
3
+ "version": "2.73.5",
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",