codeam-cli 2.75.41 → 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.
Files changed (3) hide show
  1. package/CHANGELOG.md +12 -0
  2. package/dist/index.js +133 -16
  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.75.42] — 2026-09-27
8
+
9
+ ### Fixed
10
+
11
+ - **cli:** A CodeAgent Box reports its label, not its container id (#849)
12
+
13
+ ## [2.75.41] — 2026-09-27
14
+
15
+ ### Fixed
16
+
17
+ - **cli:** Prune debug logs of dead processes (7 days / 200 MB) (#845)
18
+
7
19
  ## [2.75.40] — 2026-09-27
8
20
 
9
21
  ### 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.41" : "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,
@@ -10740,7 +10752,10 @@ var path4 = __toESM(require("path"));
10740
10752
  var import_child_process2 = require("child_process");
10741
10753
  var WRAPPER_REPO_NAME = "codeam-codespace";
10742
10754
  function resolveSessionHostname(cwd = process.cwd()) {
10743
- if (process.env.CODESPACES !== "true") return _osSeam.hostname();
10755
+ if (process.env.CODESPACES !== "true") {
10756
+ const label = process.env.CODEAM_HOST_LABEL?.trim();
10757
+ return label ? label.slice(0, 80) : _osSeam.hostname();
10758
+ }
10744
10759
  return codespaceSessionLabel(cwd) ?? _osSeam.hostname();
10745
10760
  }
10746
10761
  var _osSeam = {
@@ -10807,7 +10822,7 @@ var http = __toESM(require("http"));
10807
10822
  // package.json
10808
10823
  var package_default = {
10809
10824
  name: "codeam-cli",
10810
- version: "2.75.41",
10825
+ version: "2.75.43",
10811
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.",
10812
10827
  type: "commonjs",
10813
10828
  main: "dist/index.js",
@@ -10980,7 +10995,11 @@ async function fetchCurrentPluginAuthToken(sessionId, pluginId, pollSecret) {
10980
10995
  // after the CLI restarted on a new release and took the reconnect
10981
10996
  // path (which previously never updated ideVersion). Backends older
10982
10997
  // than the matching fix ignore the unknown field (backward-compat).
10983
- ideVersion: package_default.version
10998
+ ideVersion: package_default.version,
10999
+ // Same for the machine label: a session paired while a CodeAgent Box
11000
+ // reported its container id picks up the box label on reconnect.
11001
+ // Older backends strip the unknown field (whitelist, not forbid).
11002
+ hostname: resolveSessionHostname()
10984
11003
  },
10985
11004
  // SEC: prove possession so the gated /reconnect returns the token.
10986
11005
  // Omitted for legacy sessions (no secret) → backend legacy path.
@@ -12137,7 +12156,7 @@ var CommandRelayService = class _CommandRelayService {
12137
12156
  // fresh + clear the "CLI update available" banner after a self-update
12138
12157
  // (a codespace that reinstalls @latest reconnects via heartbeat, not
12139
12158
  // pair/reconnect). Older backends ignore the extra field.
12140
- ..."2.75.41" ? { ideVersion: "2.75.41" } : {}
12159
+ ..."2.75.43" ? { ideVersion: "2.75.43" } : {}
12141
12160
  };
12142
12161
  try {
12143
12162
  const res = await _postJson(`${API_BASE2}/api/plugin/heartbeat`, body);
@@ -27699,13 +27718,13 @@ function resolveGlobalNodeModulesDir(opts) {
27699
27718
  var STALE_STAGING_AGE_MS = CLI_UPDATE_INSTALL_TIMEOUT_MS;
27700
27719
  function sweepStaleCliStagingDirs(nodeModulesDir, now = Date.now(), deps) {
27701
27720
  if (!nodeModulesDir) return 0;
27702
- const readdirSync15 = deps?.readdirSync ?? fs51.readdirSync;
27721
+ const readdirSync16 = deps?.readdirSync ?? fs51.readdirSync;
27703
27722
  const statSync16 = deps?.statSync ?? fs51.statSync;
27704
27723
  const rmSync10 = deps?.rmSync ?? fs51.rmSync;
27705
27724
  let removed = 0;
27706
27725
  let entries;
27707
27726
  try {
27708
- entries = readdirSync15(nodeModulesDir);
27727
+ entries = readdirSync16(nodeModulesDir);
27709
27728
  } catch {
27710
27729
  return 0;
27711
27730
  }
@@ -28525,6 +28544,9 @@ function installRelayCrashGuards() {
28525
28544
  process.on("uncaughtException", (err) => {
28526
28545
  log.error("process", `uncaughtException \u2014 relay kept alive \u2014 ${describeReason(err)}`);
28527
28546
  });
28547
+ process.stdout.on("error", (err) => {
28548
+ if (err.code !== "EPIPE") log.warn("process", `stdout error \u2014 ${describeReason(err)}`);
28549
+ });
28528
28550
  }
28529
28551
  function describeReason(reason) {
28530
28552
  if (reason instanceof Error) {
@@ -28916,6 +28938,59 @@ function releaseOwnLock(lockPath) {
28916
28938
  } catch {
28917
28939
  }
28918
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
+ }
28919
28994
  function daemonLockPath(sessionId) {
28920
28995
  const safe = sessionId.replace(/[^a-zA-Z0-9_-]/g, "_");
28921
28996
  return path60.join(os40.homedir(), ".codeam", `daemon-${safe}.lock`);
@@ -48888,7 +48963,7 @@ async function autoUpgradeBeforeCriticalCommand() {
48888
48963
  if (process.env.NODE_ENV === "test") return;
48889
48964
  if (process.env.CODEAM_DISABLE_UPDATE_CHECK === "1") return;
48890
48965
  if (process.env.CI) return;
48891
- const current2 = true ? "2.75.41" : null;
48966
+ const current2 = true ? "2.75.43" : null;
48892
48967
  if (!current2) return;
48893
48968
  const cache = readCache();
48894
48969
  const fresh = cache && Date.now() - cache.fetchedAt < TTL_MS;
@@ -48905,7 +48980,7 @@ function checkForUpdates() {
48905
48980
  if (process.env.CODEAM_DISABLE_UPDATE_CHECK === "1") return;
48906
48981
  if (process.env.CI) return;
48907
48982
  if (!process.stdout.isTTY) return;
48908
- const current2 = true ? "2.75.41" : null;
48983
+ const current2 = true ? "2.75.43" : null;
48909
48984
  if (!current2) return;
48910
48985
  const cache = readCache();
48911
48986
  const fresh = cache && Date.now() - cache.fetchedAt < TTL_MS;
@@ -48928,7 +49003,7 @@ var SELF_UPDATE_INSTALL_TIMEOUT_MS = 18e4;
48928
49003
  var SELF_UPDATE_LS_TIMEOUT_MS = 15e3;
48929
49004
  var SUDO_PREFLIGHT_TIMEOUT_MS = 5e3;
48930
49005
  function currentCliVersion() {
48931
- return true ? "2.75.41" : null;
49006
+ return true ? "2.75.43" : null;
48932
49007
  }
48933
49008
  async function installedVersion(deps) {
48934
49009
  const ls = await deps.run(
@@ -49268,6 +49343,8 @@ var defaultOnIdentityRejected = () => {
49268
49343
  );
49269
49344
  process.exit(1);
49270
49345
  };
49346
+ var ORPHAN_TERM_GRACE_MS = 3e3;
49347
+ var ORPHAN_KILL_SETTLE_MS = 500;
49271
49348
  var defaultOnUpdated = (version3) => {
49272
49349
  log.info("host-agent", `self-update: installed ${version3}, restarting`);
49273
49350
  process.exit(0);
@@ -49280,6 +49357,9 @@ var HostAgentSupervisor = class {
49280
49357
  this.resumeSpawner = deps.resumeSpawner ?? defaultResumeSpawner;
49281
49358
  this.sessionStore = deps.sessionStore ?? fileSessionChildStore();
49282
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;
49283
49363
  this.resolveAgentAuth = deps.resolveAgentAuth ?? unsealAgentAuth;
49284
49364
  this.metrics = deps.metricsCollector ?? new MetricsCollector();
49285
49365
  this.onIdentityRejected = deps.onIdentityRejected ?? defaultOnIdentityRejected;
@@ -49334,6 +49414,11 @@ var HostAgentSupervisor = class {
49334
49414
  postResumeFailure;
49335
49415
  sessionStore;
49336
49416
  listSavedSessions;
49417
+ orphanedDaemonFor;
49418
+ snapshotTree;
49419
+ signalTree;
49420
+ /** Resumes waiting for a left-behind daemon to die (see replaceOrphanedDaemon). */
49421
+ pendingOrphanReplacements = 0;
49337
49422
  /**
49338
49423
  * Per-session resume bookkeeping (codeagent-v07a: N sessions resume on
49339
49424
  * boot, each with its OWN bounded retries / exhaustion / re-probe throttle —
@@ -50204,7 +50289,7 @@ var HostAgentSupervisor = class {
50204
50289
  const records = this.sessionStore.load();
50205
50290
  if (records === null) {
50206
50291
  const target = this.fallbackResumeTarget();
50207
- if (target) this.resumeOne(target);
50292
+ if (target) this.resumeOrReplace(target);
50208
50293
  return;
50209
50294
  }
50210
50295
  if (records.length === 0) return;
@@ -50231,14 +50316,16 @@ var HostAgentSupervisor = class {
50231
50316
  );
50232
50317
  continue;
50233
50318
  }
50234
- 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 });
50235
50320
  resumed += 1;
50236
50321
  }
50237
50322
  log.info(
50238
50323
  "host-agent",
50239
50324
  `resume: ${resumed} session(s) resumed, ${dropped.length} ended (bound ${max}), ${resume.length - resumed} skipped`
50240
50325
  );
50241
- if (this.children.size === 0) this.sessionStore.save([]);
50326
+ if (this.children.size === 0 && this.pendingOrphanReplacements === 0) {
50327
+ this.sessionStore.save([]);
50328
+ }
50242
50329
  } catch (err) {
50243
50330
  log.warn(
50244
50331
  "host-agent",
@@ -50345,6 +50432,36 @@ var HostAgentSupervisor = class {
50345
50432
  return [];
50346
50433
  }
50347
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
+ }
50348
50465
  /** Spawn ONE resume child for `target` (boot, retry, or re-probe). */
50349
50466
  resumeOne(target) {
50350
50467
  const { session, deployId } = target;
@@ -50904,7 +51021,7 @@ function checkChokidar() {
50904
51021
  }
50905
51022
  async function doctor(args2 = []) {
50906
51023
  const json = args2.includes("--json");
50907
- const cliVersion = true ? "2.75.41" : "0.0.0-dev";
51024
+ const cliVersion = true ? "2.75.43" : "0.0.0-dev";
50908
51025
  const apiBase2 = resolveApiBaseUrl();
50909
51026
  const diagnosticId = (0, import_node_crypto13.randomUUID)();
50910
51027
  log.info("doctor", `run id=${diagnosticId} cli=${cliVersion}`);
@@ -51428,7 +51545,7 @@ async function mcpWarm(argv = [], opts = {}) {
51428
51545
  // src/commands/version.ts
51429
51546
  var import_picocolors15 = __toESM(require("picocolors"));
51430
51547
  function version2() {
51431
- const v = true ? "2.75.41" : "unknown";
51548
+ const v = true ? "2.75.43" : "unknown";
51432
51549
  console.log(`${import_picocolors15.default.bold("codeam-cli")} ${import_picocolors15.default.cyan(v)}`);
51433
51550
  }
51434
51551
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "codeam-cli",
3
- "version": "2.75.41",
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",