codeam-cli 2.75.43 → 2.75.45

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,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.44] — 2026-09-27
8
+
9
+ ### Fixed
10
+
11
+ - **cli:** Host-agent comes back after a self-update on codespaces and fleet boxes (#854)
12
+
13
+ ## [2.75.43] — 2026-09-27
14
+
15
+ ### Fixed
16
+
17
+ - **cli:** Stop the EPIPE log loop and retire daemons a restarted host-agent left behind (#851)
18
+
7
19
  ## [2.75.42] — 2026-09-27
8
20
 
9
21
  ### Fixed
package/dist/index.js CHANGED
@@ -10577,7 +10577,7 @@ function readAnonId() {
10577
10577
  }
10578
10578
  function superProperties() {
10579
10579
  return {
10580
- cliVersion: true ? "2.75.43" : "0.0.0-dev",
10580
+ cliVersion: true ? "2.75.45" : "0.0.0-dev",
10581
10581
  nodeVersion: process.version,
10582
10582
  platform: process.platform,
10583
10583
  arch: process.arch,
@@ -10822,7 +10822,7 @@ var http = __toESM(require("http"));
10822
10822
  // package.json
10823
10823
  var package_default = {
10824
10824
  name: "codeam-cli",
10825
- version: "2.75.43",
10825
+ version: "2.75.45",
10826
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.",
10827
10827
  type: "commonjs",
10828
10828
  main: "dist/index.js",
@@ -12156,7 +12156,7 @@ var CommandRelayService = class _CommandRelayService {
12156
12156
  // fresh + clear the "CLI update available" banner after a self-update
12157
12157
  // (a codespace that reinstalls @latest reconnects via heartbeat, not
12158
12158
  // pair/reconnect). Older backends ignore the extra field.
12159
- ..."2.75.43" ? { ideVersion: "2.75.43" } : {}
12159
+ ..."2.75.45" ? { ideVersion: "2.75.45" } : {}
12160
12160
  };
12161
12161
  try {
12162
12162
  const res = await _postJson(`${API_BASE2}/api/plugin/heartbeat`, body);
@@ -48963,7 +48963,7 @@ async function autoUpgradeBeforeCriticalCommand() {
48963
48963
  if (process.env.NODE_ENV === "test") return;
48964
48964
  if (process.env.CODEAM_DISABLE_UPDATE_CHECK === "1") return;
48965
48965
  if (process.env.CI) return;
48966
- const current2 = true ? "2.75.43" : null;
48966
+ const current2 = true ? "2.75.45" : null;
48967
48967
  if (!current2) return;
48968
48968
  const cache = readCache();
48969
48969
  const fresh = cache && Date.now() - cache.fetchedAt < TTL_MS;
@@ -48980,7 +48980,7 @@ function checkForUpdates() {
48980
48980
  if (process.env.CODEAM_DISABLE_UPDATE_CHECK === "1") return;
48981
48981
  if (process.env.CI) return;
48982
48982
  if (!process.stdout.isTTY) return;
48983
- const current2 = true ? "2.75.43" : null;
48983
+ const current2 = true ? "2.75.45" : null;
48984
48984
  if (!current2) return;
48985
48985
  const cache = readCache();
48986
48986
  const fresh = cache && Date.now() - cache.fetchedAt < TTL_MS;
@@ -49003,7 +49003,7 @@ var SELF_UPDATE_INSTALL_TIMEOUT_MS = 18e4;
49003
49003
  var SELF_UPDATE_LS_TIMEOUT_MS = 15e3;
49004
49004
  var SUDO_PREFLIGHT_TIMEOUT_MS = 5e3;
49005
49005
  function currentCliVersion() {
49006
- return true ? "2.75.43" : null;
49006
+ return true ? "2.75.45" : null;
49007
49007
  }
49008
49008
  async function installedVersion(deps) {
49009
49009
  const ls = await deps.run(
@@ -49228,6 +49228,11 @@ function buildFleetBoxRunArgs(p2) {
49228
49228
  ...process.env.CODEAM_FLEET_BOX_IMAGE ? [] : ["--pull=always"],
49229
49229
  "--name",
49230
49230
  p2.containerName,
49231
+ // The box's host-agent is the container's pid 1 and its self-update restart
49232
+ // is an exit(0): without a restart policy that stopped the whole box until
49233
+ // the next wake. `docker stop` (the sleep sweep) still keeps it stopped.
49234
+ "--restart",
49235
+ "unless-stopped",
49231
49236
  "--cap-drop",
49232
49237
  "ALL",
49233
49238
  "--security-opt",
@@ -49345,8 +49350,31 @@ var defaultOnIdentityRejected = () => {
49345
49350
  };
49346
49351
  var ORPHAN_TERM_GRACE_MS = 3e3;
49347
49352
  var ORPHAN_KILL_SETTLE_MS = 500;
49353
+ function needsSelfRelaunch(env = process.env, pid = process.pid) {
49354
+ return !env.INVOCATION_ID && pid !== 1;
49355
+ }
49356
+ function relaunchArgv(execPath, argv) {
49357
+ return ["-c", 'sleep 2; exec "$0" "$@"', execPath, ...argv.slice(1)];
49358
+ }
49348
49359
  var defaultOnUpdated = (version3) => {
49349
- log.info("host-agent", `self-update: installed ${version3}, restarting`);
49360
+ const relaunch = needsSelfRelaunch();
49361
+ log.info(
49362
+ "host-agent",
49363
+ `self-update: installed ${version3}, restarting${relaunch ? " (self-relaunch, no supervisor)" : ""}`
49364
+ );
49365
+ if (relaunch) {
49366
+ try {
49367
+ (0, import_node_child_process30.spawn)("/bin/sh", relaunchArgv(process.execPath, process.argv), {
49368
+ cwd: process.cwd(),
49369
+ env: process.env,
49370
+ detached: true,
49371
+ stdio: "ignore"
49372
+ }).unref();
49373
+ } catch (err) {
49374
+ log.error("host-agent", "self-update: relaunch failed \u2014 staying on the old binary", err);
49375
+ return;
49376
+ }
49377
+ }
49350
49378
  process.exit(0);
49351
49379
  };
49352
49380
  var HostAgentSupervisor = class {
@@ -49419,6 +49447,7 @@ var HostAgentSupervisor = class {
49419
49447
  signalTree;
49420
49448
  /** Resumes waiting for a left-behind daemon to die (see replaceOrphanedDaemon). */
49421
49449
  pendingOrphanReplacements = 0;
49450
+ replacingOrphanFor = /* @__PURE__ */ new Set();
49422
49451
  /**
49423
49452
  * Per-session resume bookkeeping (codeagent-v07a: N sessions resume on
49424
49453
  * boot, each with its OWN bounded retries / exhaustion / re-probe throttle —
@@ -49959,6 +49988,7 @@ var HostAgentSupervisor = class {
49959
49988
  return;
49960
49989
  }
49961
49990
  log.info("host-agent", `fleet_start_box id=${payload.boxId} name=${containerName}`);
49991
+ await this.docker.run(["update", "--restart", "unless-stopped", containerName]);
49962
49992
  const res = await this.docker.run(["start", containerName]);
49963
49993
  if (res.code !== 0 && !isMissingContainerError(res.stderr)) {
49964
49994
  log.warn(
@@ -50289,7 +50319,7 @@ var HostAgentSupervisor = class {
50289
50319
  const records = this.sessionStore.load();
50290
50320
  if (records === null) {
50291
50321
  const target = this.fallbackResumeTarget();
50292
- if (target) this.resumeOrReplace(target);
50322
+ if (target) this.resumeOne(target);
50293
50323
  return;
50294
50324
  }
50295
50325
  if (records.length === 0) return;
@@ -50316,7 +50346,7 @@ var HostAgentSupervisor = class {
50316
50346
  );
50317
50347
  continue;
50318
50348
  }
50319
- this.resumeOrReplace({ deployId: rec.deployId, cwd: rec.cwd, session, agent: rec.agent });
50349
+ this.resumeOne({ deployId: rec.deployId, cwd: rec.cwd, session, agent: rec.agent });
50320
50350
  resumed += 1;
50321
50351
  }
50322
50352
  log.info(
@@ -50433,18 +50463,23 @@ var HostAgentSupervisor = class {
50433
50463
  }
50434
50464
  }
50435
50465
  /**
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
50466
+ * Resume `target` (boot, `self_hosted_resume`, retry or re-probe) — first
50467
+ * retiring a daemon the PREVIOUS host-agent left holding the session, if
50468
+ * there is one. ⚠️ Every entry point must come through here: the v2.75.43
50469
+ * check sat on the boot path only, and the QA codespace's session came back
50470
+ * through `self_hosted_resume` instead, which deferred to the orphan again. Without this the resume child
50438
50471
  * defers to that orphan (it holds the daemon lock and is alive), so the
50439
50472
  * session keeps running the old code with dead stdio pipes — on 2.75.41 and
50440
50473
  * older that wedged it offline for good (see `orphanedSupervisorDaemon`).
50441
50474
  * Replacing it matches what systemd already does on a self-hosted box,
50442
50475
  * where a unit restart kills the whole control group.
50443
50476
  */
50444
- resumeOrReplace(target) {
50477
+ resumeOne(target) {
50478
+ if (this.children.has(target.deployId)) return;
50479
+ if (this.replacingOrphanFor.has(target.deployId)) return;
50445
50480
  const orphan = this.orphanedDaemonFor(target.session.id);
50446
50481
  if (orphan === void 0) {
50447
- this.resumeOne(target);
50482
+ this.spawnResume(target);
50448
50483
  return;
50449
50484
  }
50450
50485
  log.warn(
@@ -50452,18 +50487,20 @@ var HostAgentSupervisor = class {
50452
50487
  `resume: session ${target.session.id.slice(0, 8)} is held by pid ${orphan}, left behind by the previous host-agent \u2014 replacing it`
50453
50488
  );
50454
50489
  this.pendingOrphanReplacements += 1;
50490
+ this.replacingOrphanFor.add(target.deployId);
50455
50491
  const tree = this.snapshotTree(orphan);
50456
50492
  this.signalTree(tree.slice(0, 1), "SIGTERM");
50457
50493
  setTimeout(() => {
50458
50494
  this.signalTree(tree, "SIGKILL");
50459
50495
  setTimeout(() => {
50460
50496
  this.pendingOrphanReplacements -= 1;
50461
- if (!this.stopped) this.resumeOne(target);
50497
+ this.replacingOrphanFor.delete(target.deployId);
50498
+ if (!this.stopped) this.spawnResume(target);
50462
50499
  }, ORPHAN_KILL_SETTLE_MS);
50463
50500
  }, ORPHAN_TERM_GRACE_MS);
50464
50501
  }
50465
- /** Spawn ONE resume child for `target` (boot, retry, or re-probe). */
50466
- resumeOne(target) {
50502
+ /** Spawn ONE resume child for `target`. Callers go through {@link resumeOne}. */
50503
+ spawnResume(target) {
50467
50504
  const { session, deployId } = target;
50468
50505
  if (this.children.has(deployId)) return;
50469
50506
  const st3 = this.resumeStateFor(target);
@@ -51021,7 +51058,7 @@ function checkChokidar() {
51021
51058
  }
51022
51059
  async function doctor(args2 = []) {
51023
51060
  const json = args2.includes("--json");
51024
- const cliVersion = true ? "2.75.43" : "0.0.0-dev";
51061
+ const cliVersion = true ? "2.75.45" : "0.0.0-dev";
51025
51062
  const apiBase2 = resolveApiBaseUrl();
51026
51063
  const diagnosticId = (0, import_node_crypto13.randomUUID)();
51027
51064
  log.info("doctor", `run id=${diagnosticId} cli=${cliVersion}`);
@@ -51545,7 +51582,7 @@ async function mcpWarm(argv = [], opts = {}) {
51545
51582
  // src/commands/version.ts
51546
51583
  var import_picocolors15 = __toESM(require("picocolors"));
51547
51584
  function version2() {
51548
- const v = true ? "2.75.43" : "unknown";
51585
+ const v = true ? "2.75.45" : "unknown";
51549
51586
  console.log(`${import_picocolors15.default.bold("codeam-cli")} ${import_picocolors15.default.cyan(v)}`);
51550
51587
  }
51551
51588
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "codeam-cli",
3
- "version": "2.75.43",
3
+ "version": "2.75.45",
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",