codeam-cli 2.75.42 → 2.75.43

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 CHANGED
@@ -4,6 +4,12 @@ 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.75.42] — 2026-09-27
8
+
9
+ ### Fixed
10
+
11
+ - **cli:** A CodeAgent Box reports its label, not its container id (#849)
12
+
7
13
  ## [2.75.41] — 2026-09-27
8
14
 
9
15
  ### Fixed
package/dist/index.js CHANGED
@@ -3437,6 +3437,18 @@ function resolveLogDir() {
3437
3437
  var LOG_DIR = resolveLogDir();
3438
3438
  var debugFilePath = path.join(LOG_DIR, `debug-${process.pid}.log`);
3439
3439
  var fileInitialized = false;
3440
+ var consoleBroken = false;
3441
+ process.stderr.on("error", () => {
3442
+ consoleBroken = true;
3443
+ });
3444
+ function writeConsole(line) {
3445
+ if (consoleBroken) return;
3446
+ try {
3447
+ process.stderr.write(line);
3448
+ } catch {
3449
+ consoleBroken = true;
3450
+ }
3451
+ }
3440
3452
  var LOG_RETENTION_MS = 7 * 24 * 60 * 60 * 1e3;
3441
3453
  var LOG_DIR_BUDGET_BYTES = 200 * 1024 * 1024;
3442
3454
  function pidAlive(pid) {
@@ -3556,7 +3568,7 @@ function emit(level, tag, msg, err) {
3556
3568
  appendToFile(jsonMode ? json : `${(/* @__PURE__ */ new Date()).toISOString()} ${text}`);
3557
3569
  }
3558
3570
  if (LEVELS[level] <= currentLevel()) {
3559
- process.stderr.write(jsonMode ? json : text);
3571
+ writeConsole(jsonMode ? json : text);
3560
3572
  }
3561
3573
  }
3562
3574
  var log = {
@@ -10565,7 +10577,7 @@ function readAnonId() {
10565
10577
  }
10566
10578
  function superProperties() {
10567
10579
  return {
10568
- cliVersion: true ? "2.75.42" : "0.0.0-dev",
10580
+ cliVersion: true ? "2.75.43" : "0.0.0-dev",
10569
10581
  nodeVersion: process.version,
10570
10582
  platform: process.platform,
10571
10583
  arch: process.arch,
@@ -10810,7 +10822,7 @@ var http = __toESM(require("http"));
10810
10822
  // package.json
10811
10823
  var package_default = {
10812
10824
  name: "codeam-cli",
10813
- version: "2.75.42",
10825
+ version: "2.75.43",
10814
10826
  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.",
10815
10827
  type: "commonjs",
10816
10828
  main: "dist/index.js",
@@ -12144,7 +12156,7 @@ var CommandRelayService = class _CommandRelayService {
12144
12156
  // fresh + clear the "CLI update available" banner after a self-update
12145
12157
  // (a codespace that reinstalls @latest reconnects via heartbeat, not
12146
12158
  // pair/reconnect). Older backends ignore the extra field.
12147
- ..."2.75.42" ? { ideVersion: "2.75.42" } : {}
12159
+ ..."2.75.43" ? { ideVersion: "2.75.43" } : {}
12148
12160
  };
12149
12161
  try {
12150
12162
  const res = await _postJson(`${API_BASE2}/api/plugin/heartbeat`, body);
@@ -27706,13 +27718,13 @@ function resolveGlobalNodeModulesDir(opts) {
27706
27718
  var STALE_STAGING_AGE_MS = CLI_UPDATE_INSTALL_TIMEOUT_MS;
27707
27719
  function sweepStaleCliStagingDirs(nodeModulesDir, now = Date.now(), deps) {
27708
27720
  if (!nodeModulesDir) return 0;
27709
- const readdirSync15 = deps?.readdirSync ?? fs51.readdirSync;
27721
+ const readdirSync16 = deps?.readdirSync ?? fs51.readdirSync;
27710
27722
  const statSync16 = deps?.statSync ?? fs51.statSync;
27711
27723
  const rmSync10 = deps?.rmSync ?? fs51.rmSync;
27712
27724
  let removed = 0;
27713
27725
  let entries;
27714
27726
  try {
27715
- entries = readdirSync15(nodeModulesDir);
27727
+ entries = readdirSync16(nodeModulesDir);
27716
27728
  } catch {
27717
27729
  return 0;
27718
27730
  }
@@ -28532,6 +28544,9 @@ function installRelayCrashGuards() {
28532
28544
  process.on("uncaughtException", (err) => {
28533
28545
  log.error("process", `uncaughtException \u2014 relay kept alive \u2014 ${describeReason(err)}`);
28534
28546
  });
28547
+ process.stdout.on("error", (err) => {
28548
+ if (err.code !== "EPIPE") log.warn("process", `stdout error \u2014 ${describeReason(err)}`);
28549
+ });
28535
28550
  }
28536
28551
  function describeReason(reason) {
28537
28552
  if (reason instanceof Error) {
@@ -28923,6 +28938,59 @@ function releaseOwnLock(lockPath) {
28923
28938
  } catch {
28924
28939
  }
28925
28940
  }
28941
+ function procPpid(pid) {
28942
+ const stat3 = readProcFile(`${pid}/stat`);
28943
+ if (!stat3) return void 0;
28944
+ const ppid = Number(stat3.slice(stat3.lastIndexOf(")") + 2).trim().split(/\s+/)[1]);
28945
+ return Number.isInteger(ppid) ? ppid : void 0;
28946
+ }
28947
+ function orphanedSupervisorDaemon(sessionId) {
28948
+ const rec = readLockRecord(daemonLockPath(sessionId));
28949
+ if (!rec || !isLiveLockHolder(rec)) return void 0;
28950
+ if (process.pid === 1 || procPpid(rec.pid) !== 1) return void 0;
28951
+ const env = readProcFile(`${rec.pid}/environ`)?.split("\0") ?? [];
28952
+ const spawnedBySupervisor = env.some(
28953
+ (v) => v === `CODEAM_RESUME_SESSION_ID=${sessionId}` || v.startsWith("CODEAM_AUTO_TOKEN=")
28954
+ );
28955
+ return spawnedBySupervisor ? rec.pid : void 0;
28956
+ }
28957
+ function snapshotProcessTree(root) {
28958
+ const children = /* @__PURE__ */ new Map();
28959
+ let entries = [];
28960
+ try {
28961
+ entries = fs52.readdirSync(procRoot).filter((e) => /^\d+$/.test(e));
28962
+ } catch {
28963
+ }
28964
+ for (const e of entries) {
28965
+ const pid = Number(e);
28966
+ const ppid = procPpid(pid);
28967
+ if (ppid === void 0) continue;
28968
+ const list = children.get(ppid) ?? [];
28969
+ list.push(pid);
28970
+ children.set(ppid, list);
28971
+ }
28972
+ const tree = [];
28973
+ const queue = [root];
28974
+ const seen2 = /* @__PURE__ */ new Set();
28975
+ while (queue.length > 0) {
28976
+ const pid = queue.shift();
28977
+ if (seen2.has(pid)) continue;
28978
+ seen2.add(pid);
28979
+ const start2 = procStartTicks(pid);
28980
+ tree.push(start2 ? { pid, start: start2 } : { pid });
28981
+ queue.push(...children.get(pid) ?? []);
28982
+ }
28983
+ return tree;
28984
+ }
28985
+ function signalProcessTree(refs, signal) {
28986
+ for (const ref of refs) {
28987
+ if (ref.start !== void 0 && procStartTicks(ref.pid) !== ref.start) continue;
28988
+ try {
28989
+ process.kill(ref.pid, signal);
28990
+ } catch {
28991
+ }
28992
+ }
28993
+ }
28926
28994
  function daemonLockPath(sessionId) {
28927
28995
  const safe = sessionId.replace(/[^a-zA-Z0-9_-]/g, "_");
28928
28996
  return path60.join(os40.homedir(), ".codeam", `daemon-${safe}.lock`);
@@ -48895,7 +48963,7 @@ async function autoUpgradeBeforeCriticalCommand() {
48895
48963
  if (process.env.NODE_ENV === "test") return;
48896
48964
  if (process.env.CODEAM_DISABLE_UPDATE_CHECK === "1") return;
48897
48965
  if (process.env.CI) return;
48898
- const current2 = true ? "2.75.42" : null;
48966
+ const current2 = true ? "2.75.43" : null;
48899
48967
  if (!current2) return;
48900
48968
  const cache = readCache();
48901
48969
  const fresh = cache && Date.now() - cache.fetchedAt < TTL_MS;
@@ -48912,7 +48980,7 @@ function checkForUpdates() {
48912
48980
  if (process.env.CODEAM_DISABLE_UPDATE_CHECK === "1") return;
48913
48981
  if (process.env.CI) return;
48914
48982
  if (!process.stdout.isTTY) return;
48915
- const current2 = true ? "2.75.42" : null;
48983
+ const current2 = true ? "2.75.43" : null;
48916
48984
  if (!current2) return;
48917
48985
  const cache = readCache();
48918
48986
  const fresh = cache && Date.now() - cache.fetchedAt < TTL_MS;
@@ -48935,7 +49003,7 @@ var SELF_UPDATE_INSTALL_TIMEOUT_MS = 18e4;
48935
49003
  var SELF_UPDATE_LS_TIMEOUT_MS = 15e3;
48936
49004
  var SUDO_PREFLIGHT_TIMEOUT_MS = 5e3;
48937
49005
  function currentCliVersion() {
48938
- return true ? "2.75.42" : null;
49006
+ return true ? "2.75.43" : null;
48939
49007
  }
48940
49008
  async function installedVersion(deps) {
48941
49009
  const ls = await deps.run(
@@ -49275,6 +49343,8 @@ var defaultOnIdentityRejected = () => {
49275
49343
  );
49276
49344
  process.exit(1);
49277
49345
  };
49346
+ var ORPHAN_TERM_GRACE_MS = 3e3;
49347
+ var ORPHAN_KILL_SETTLE_MS = 500;
49278
49348
  var defaultOnUpdated = (version3) => {
49279
49349
  log.info("host-agent", `self-update: installed ${version3}, restarting`);
49280
49350
  process.exit(0);
@@ -49287,6 +49357,9 @@ var HostAgentSupervisor = class {
49287
49357
  this.resumeSpawner = deps.resumeSpawner ?? defaultResumeSpawner;
49288
49358
  this.sessionStore = deps.sessionStore ?? fileSessionChildStore();
49289
49359
  this.listSavedSessions = deps.listSavedSessions ?? (() => loadCliConfig().sessions);
49360
+ this.orphanedDaemonFor = deps.orphanedDaemonFor ?? orphanedSupervisorDaemon;
49361
+ this.snapshotTree = deps.snapshotProcessTree ?? snapshotProcessTree;
49362
+ this.signalTree = deps.signalProcessTree ?? signalProcessTree;
49290
49363
  this.resolveAgentAuth = deps.resolveAgentAuth ?? unsealAgentAuth;
49291
49364
  this.metrics = deps.metricsCollector ?? new MetricsCollector();
49292
49365
  this.onIdentityRejected = deps.onIdentityRejected ?? defaultOnIdentityRejected;
@@ -49341,6 +49414,11 @@ var HostAgentSupervisor = class {
49341
49414
  postResumeFailure;
49342
49415
  sessionStore;
49343
49416
  listSavedSessions;
49417
+ orphanedDaemonFor;
49418
+ snapshotTree;
49419
+ signalTree;
49420
+ /** Resumes waiting for a left-behind daemon to die (see replaceOrphanedDaemon). */
49421
+ pendingOrphanReplacements = 0;
49344
49422
  /**
49345
49423
  * Per-session resume bookkeeping (codeagent-v07a: N sessions resume on
49346
49424
  * boot, each with its OWN bounded retries / exhaustion / re-probe throttle —
@@ -50211,7 +50289,7 @@ var HostAgentSupervisor = class {
50211
50289
  const records = this.sessionStore.load();
50212
50290
  if (records === null) {
50213
50291
  const target = this.fallbackResumeTarget();
50214
- if (target) this.resumeOne(target);
50292
+ if (target) this.resumeOrReplace(target);
50215
50293
  return;
50216
50294
  }
50217
50295
  if (records.length === 0) return;
@@ -50238,14 +50316,16 @@ var HostAgentSupervisor = class {
50238
50316
  );
50239
50317
  continue;
50240
50318
  }
50241
- this.resumeOne({ deployId: rec.deployId, cwd: rec.cwd, session, agent: rec.agent });
50319
+ this.resumeOrReplace({ deployId: rec.deployId, cwd: rec.cwd, session, agent: rec.agent });
50242
50320
  resumed += 1;
50243
50321
  }
50244
50322
  log.info(
50245
50323
  "host-agent",
50246
50324
  `resume: ${resumed} session(s) resumed, ${dropped.length} ended (bound ${max}), ${resume.length - resumed} skipped`
50247
50325
  );
50248
- if (this.children.size === 0) this.sessionStore.save([]);
50326
+ if (this.children.size === 0 && this.pendingOrphanReplacements === 0) {
50327
+ this.sessionStore.save([]);
50328
+ }
50249
50329
  } catch (err) {
50250
50330
  log.warn(
50251
50331
  "host-agent",
@@ -50352,6 +50432,36 @@ var HostAgentSupervisor = class {
50352
50432
  return [];
50353
50433
  }
50354
50434
  }
50435
+ /**
50436
+ * Resume `target` — first retiring a daemon the PREVIOUS host-agent left
50437
+ * holding the session, if there is one. Without this the resume child
50438
+ * defers to that orphan (it holds the daemon lock and is alive), so the
50439
+ * session keeps running the old code with dead stdio pipes — on 2.75.41 and
50440
+ * older that wedged it offline for good (see `orphanedSupervisorDaemon`).
50441
+ * Replacing it matches what systemd already does on a self-hosted box,
50442
+ * where a unit restart kills the whole control group.
50443
+ */
50444
+ resumeOrReplace(target) {
50445
+ const orphan = this.orphanedDaemonFor(target.session.id);
50446
+ if (orphan === void 0) {
50447
+ this.resumeOne(target);
50448
+ return;
50449
+ }
50450
+ log.warn(
50451
+ "host-agent",
50452
+ `resume: session ${target.session.id.slice(0, 8)} is held by pid ${orphan}, left behind by the previous host-agent \u2014 replacing it`
50453
+ );
50454
+ this.pendingOrphanReplacements += 1;
50455
+ const tree = this.snapshotTree(orphan);
50456
+ this.signalTree(tree.slice(0, 1), "SIGTERM");
50457
+ setTimeout(() => {
50458
+ this.signalTree(tree, "SIGKILL");
50459
+ setTimeout(() => {
50460
+ this.pendingOrphanReplacements -= 1;
50461
+ if (!this.stopped) this.resumeOne(target);
50462
+ }, ORPHAN_KILL_SETTLE_MS);
50463
+ }, ORPHAN_TERM_GRACE_MS);
50464
+ }
50355
50465
  /** Spawn ONE resume child for `target` (boot, retry, or re-probe). */
50356
50466
  resumeOne(target) {
50357
50467
  const { session, deployId } = target;
@@ -50911,7 +51021,7 @@ function checkChokidar() {
50911
51021
  }
50912
51022
  async function doctor(args2 = []) {
50913
51023
  const json = args2.includes("--json");
50914
- const cliVersion = true ? "2.75.42" : "0.0.0-dev";
51024
+ const cliVersion = true ? "2.75.43" : "0.0.0-dev";
50915
51025
  const apiBase2 = resolveApiBaseUrl();
50916
51026
  const diagnosticId = (0, import_node_crypto13.randomUUID)();
50917
51027
  log.info("doctor", `run id=${diagnosticId} cli=${cliVersion}`);
@@ -51435,7 +51545,7 @@ async function mcpWarm(argv = [], opts = {}) {
51435
51545
  // src/commands/version.ts
51436
51546
  var import_picocolors15 = __toESM(require("picocolors"));
51437
51547
  function version2() {
51438
- const v = true ? "2.75.42" : "unknown";
51548
+ const v = true ? "2.75.43" : "unknown";
51439
51549
  console.log(`${import_picocolors15.default.bold("codeam-cli")} ${import_picocolors15.default.cyan(v)}`);
51440
51550
  }
51441
51551
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "codeam-cli",
3
- "version": "2.75.42",
3
+ "version": "2.75.43",
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",