codeam-cli 2.39.76 → 2.39.78

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 +10 -0
  2. package/dist/index.js +109 -30
  3. package/package.json +1 -1
package/CHANGELOG.md CHANGED
@@ -4,6 +4,16 @@ 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.39.76] — 2026-06-21
8
+
9
+ ### Added
10
+
11
+ - **cli:** Report compression-$ savings (eliminated tokens × model input price)
12
+
13
+ ### Fixed
14
+
15
+ - **cli:** Smoke probe /health not /api/health
16
+
7
17
  ## [2.39.75] — 2026-06-21
8
18
 
9
19
  ### Fixed
package/dist/index.js CHANGED
@@ -5390,7 +5390,7 @@ function readAnonId() {
5390
5390
  }
5391
5391
  function superProperties() {
5392
5392
  return {
5393
- cliVersion: true ? "2.39.76" : "0.0.0-dev",
5393
+ cliVersion: true ? "2.39.78" : "0.0.0-dev",
5394
5394
  nodeVersion: process.version,
5395
5395
  platform: process.platform,
5396
5396
  arch: process.arch,
@@ -5549,7 +5549,7 @@ var os4 = __toESM(require("os"));
5549
5549
  // package.json
5550
5550
  var package_default = {
5551
5551
  name: "codeam-cli",
5552
- version: "2.39.76",
5552
+ version: "2.39.78",
5553
5553
  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.",
5554
5554
  type: "commonjs",
5555
5555
  main: "dist/index.js",
@@ -17448,7 +17448,7 @@ function checkForUpdates() {
17448
17448
  if (process.env.CODEAM_DISABLE_UPDATE_CHECK === "1") return;
17449
17449
  if (process.env.CI) return;
17450
17450
  if (!process.stdout.isTTY) return;
17451
- const current = true ? "2.39.76" : null;
17451
+ const current = true ? "2.39.78" : null;
17452
17452
  if (!current) return;
17453
17453
  const cache = readCache();
17454
17454
  const fresh = cache && Date.now() - cache.fetchedAt < TTL_MS;
@@ -17504,7 +17504,10 @@ function maybeStartHeadroomReporter(ctx) {
17504
17504
  reporter.start();
17505
17505
  return reporter;
17506
17506
  } catch (err) {
17507
- log.warn("headroom", `failed to start Headroom reporter (best-effort): ${err instanceof Error ? err.message : String(err)}`);
17507
+ log.warn(
17508
+ "headroom",
17509
+ `failed to start Headroom reporter (best-effort): ${err instanceof Error ? err.message : String(err)}`
17510
+ );
17508
17511
  return null;
17509
17512
  }
17510
17513
  }
@@ -17584,7 +17587,10 @@ var defaultHeadroomRunner = {
17584
17587
  let timer;
17585
17588
  if (timeoutMs !== void 0) {
17586
17589
  timer = setTimeout(() => {
17587
- log.warn("host-agent", `headroom[${cmd}] timed out after ${timeoutMs / 1e3}s \u2014 aborting`);
17590
+ log.warn(
17591
+ "host-agent",
17592
+ `headroom[${cmd}] timed out after ${timeoutMs / 1e3}s \u2014 aborting`
17593
+ );
17588
17594
  try {
17589
17595
  child.kill("SIGTERM");
17590
17596
  } catch {
@@ -17615,7 +17621,16 @@ var PACKAGE_MANAGERS = [
17615
17621
  var PROVISION_RECIPES = {
17616
17622
  "apt-get": {
17617
17623
  update: ["apt-get", "update"],
17618
- install: ["apt-get", "install", "-y", "python3", "python3-pip", "python3-venv", "ca-certificates", "curl"]
17624
+ install: [
17625
+ "apt-get",
17626
+ "install",
17627
+ "-y",
17628
+ "python3",
17629
+ "python3-pip",
17630
+ "python3-venv",
17631
+ "ca-certificates",
17632
+ "curl"
17633
+ ]
17619
17634
  },
17620
17635
  apk: {
17621
17636
  install: ["apk", "add", "--no-cache", "python3", "py3-pip", "ca-certificates", "curl"]
@@ -17630,7 +17645,15 @@ var PROVISION_RECIPES = {
17630
17645
  install: ["pacman", "-Sy", "--noconfirm", "python", "python-pip", "ca-certificates", "curl"]
17631
17646
  },
17632
17647
  zypper: {
17633
- install: ["zypper", "--non-interactive", "install", "python3", "python3-pip", "ca-certificates", "curl"]
17648
+ install: [
17649
+ "zypper",
17650
+ "--non-interactive",
17651
+ "install",
17652
+ "python3",
17653
+ "python3-pip",
17654
+ "ca-certificates",
17655
+ "curl"
17656
+ ]
17634
17657
  }
17635
17658
  };
17636
17659
  function detectPackageManager(runner) {
@@ -17821,17 +17844,22 @@ async function setupHeadroomForSelfHosted(agent, runner = defaultHeadroomRunner)
17821
17844
  }
17822
17845
  }
17823
17846
  const initOk = await new Promise((resolve7) => {
17824
- (0, import_node_child_process13.execFile)("headroom", ["init", "--global", initKind], { env: initEnv }, (initErr, stdout, stderr) => {
17825
- if (initErr) {
17826
- const detail = (stderr || initErr.message).replace(/\n+$/g, "");
17827
- log.warn("host-agent", `headroom init failed (best-effort): ${detail}`);
17828
- resolve7(false);
17829
- } else {
17830
- if (stdout.trim()) log.info("host-agent", `headroom init: ${stdout.trim()}`);
17831
- log.info("host-agent", "headroom init --global succeeded");
17832
- resolve7(true);
17847
+ (0, import_node_child_process13.execFile)(
17848
+ "headroom",
17849
+ ["init", "--global", initKind],
17850
+ { env: initEnv },
17851
+ (initErr, stdout, stderr) => {
17852
+ if (initErr) {
17853
+ const detail = (stderr || initErr.message).replace(/\n+$/g, "");
17854
+ log.warn("host-agent", `headroom init failed (best-effort): ${detail}`);
17855
+ resolve7(false);
17856
+ } else {
17857
+ if (stdout.trim()) log.info("host-agent", `headroom init: ${stdout.trim()}`);
17858
+ log.info("host-agent", "headroom init --global succeeded");
17859
+ resolve7(true);
17860
+ }
17833
17861
  }
17834
- });
17862
+ );
17835
17863
  });
17836
17864
  if (!initOk) {
17837
17865
  return false;
@@ -17844,7 +17872,10 @@ async function setupHeadroomForSelfHosted(agent, runner = defaultHeadroomRunner)
17844
17872
  });
17845
17873
  proxy.unref();
17846
17874
  } catch (e) {
17847
- log.warn("host-agent", `headroom proxy warm-start failed (best-effort): ${e instanceof Error ? e.message : String(e)}`);
17875
+ log.warn(
17876
+ "host-agent",
17877
+ `headroom proxy warm-start failed (best-effort): ${e instanceof Error ? e.message : String(e)}`
17878
+ );
17848
17879
  }
17849
17880
  return true;
17850
17881
  }
@@ -17855,7 +17886,7 @@ var defaultSpawner = (env, cwd, args2 = []) => (0, import_node_child_process13.s
17855
17886
  detached: false
17856
17887
  });
17857
17888
  function currentCliVersion() {
17858
- return true ? "2.39.76" : null;
17889
+ return true ? "2.39.78" : null;
17859
17890
  }
17860
17891
  function runCmd(cmd, args2, timeoutMs) {
17861
17892
  return new Promise((resolve7) => {
@@ -17872,7 +17903,11 @@ async function runSelfUpdate() {
17872
17903
  log.trace("host-agent", "self-update: no __CLI_VERSION__ \u2014 skipping");
17873
17904
  return { status: "skipped" };
17874
17905
  }
17875
- const view = await runCmd("npm", ["view", SELF_UPDATE_PKG, "version"], SELF_UPDATE_VIEW_TIMEOUT_MS);
17906
+ const view = await runCmd(
17907
+ "npm",
17908
+ ["view", SELF_UPDATE_PKG, "version"],
17909
+ SELF_UPDATE_VIEW_TIMEOUT_MS
17910
+ );
17876
17911
  if (view.code !== 0) {
17877
17912
  log.trace("host-agent", `self-update: npm view exited ${String(view.code)} \u2014 skipping`);
17878
17913
  return { status: "skipped" };
@@ -17900,7 +17935,11 @@ async function runSelfUpdate() {
17900
17935
  );
17901
17936
  return { status: "skipped" };
17902
17937
  }
17903
- const after = await runCmd("npm", ["view", SELF_UPDATE_PKG, "version"], SELF_UPDATE_VIEW_TIMEOUT_MS);
17938
+ const after = await runCmd(
17939
+ "npm",
17940
+ ["view", SELF_UPDATE_PKG, "version"],
17941
+ SELF_UPDATE_VIEW_TIMEOUT_MS
17942
+ );
17904
17943
  const installed = after.code === 0 ? after.stdout.trim() || latest : latest;
17905
17944
  return { status: "updated", version: installed };
17906
17945
  } catch (err) {
@@ -17947,6 +17986,8 @@ var HostAgentSupervisor = class {
17947
17986
  this.spawnChild = deps.spawnChild ?? defaultSpawner;
17948
17987
  this.resolveAgentAuth = deps.resolveAgentAuth ?? unsealAgentAuth;
17949
17988
  this.setupHeadroom = deps.setupHeadroom ?? setupHeadroomForSelfHosted;
17989
+ this.isHeadroomInstalled = deps.isHeadroomInstalled ?? (() => defaultHeadroomRunner.which("headroom"));
17990
+ this.getFreeDisk = deps.getFreeDisk ?? getFreeDiskBytes;
17950
17991
  this.metrics = deps.metricsCollector ?? new MetricsCollector();
17951
17992
  this.onIdentityRejected = deps.onIdentityRejected ?? defaultOnIdentityRejected;
17952
17993
  this.disableService = deps.disableService ?? defaultDisableService;
@@ -17960,6 +18001,10 @@ var HostAgentSupervisor = class {
17960
18001
  spawnChild;
17961
18002
  resolveAgentAuth;
17962
18003
  setupHeadroom;
18004
+ /** Probe whether Headroom is already installed (defaults to `which headroom`). */
18005
+ isHeadroomInstalled;
18006
+ /** Free-disk reader for the install preflight (defaults to getFreeDiskBytes). */
18007
+ getFreeDisk;
17963
18008
  relay = null;
17964
18009
  heartbeatTimer = null;
17965
18010
  /** Periodic self-update timer (npm check + install + restart). */
@@ -18258,8 +18303,9 @@ var HostAgentSupervisor = class {
18258
18303
  }
18259
18304
  if (payload.headroomEnabled && payload.headroomAgent && payload.headroomSavingsIngestUrl) {
18260
18305
  report("headroom", "setting up Headroom proxy");
18261
- const freeBytes = await getFreeDiskBytes(os32.homedir());
18262
- if (freeBytes !== null && freeBytes < HEADROOM_MIN_FREE_DISK_BYTES) {
18306
+ const freeBytes = await this.getFreeDisk(os32.homedir());
18307
+ const alreadyInstalled = this.isHeadroomInstalled();
18308
+ if (!alreadyInstalled && freeBytes !== null && freeBytes < HEADROOM_MIN_FREE_DISK_BYTES) {
18263
18309
  const freeGb = (freeBytes / 1e9).toFixed(1);
18264
18310
  const needGb = Math.round(HEADROOM_MIN_FREE_DISK_BYTES / 1e9);
18265
18311
  report(
@@ -18272,6 +18318,12 @@ var HostAgentSupervisor = class {
18272
18318
  );
18273
18319
  persistHeadroomConfig({ enabled: false });
18274
18320
  } else {
18321
+ if (alreadyInstalled && freeBytes !== null && freeBytes < HEADROOM_MIN_FREE_DISK_BYTES) {
18322
+ log.info(
18323
+ "host-agent",
18324
+ `Headroom already installed \u2014 bypassing install disk gate (free=${(freeBytes / 1e9).toFixed(1)}GB); reporting stays enabled`
18325
+ );
18326
+ }
18275
18327
  const headroomOk = await this.setupHeadroom(payload.headroomAgent);
18276
18328
  if (headroomOk) {
18277
18329
  persistHeadroomConfig({
@@ -18279,10 +18331,16 @@ var HostAgentSupervisor = class {
18279
18331
  agent: agentIdToHeadroomKind(payload.headroomAgent),
18280
18332
  ingestUrl: payload.headroomSavingsIngestUrl
18281
18333
  });
18282
- log.info("host-agent", "Headroom proxy ready; persisted headroom config for child spawns");
18334
+ log.info(
18335
+ "host-agent",
18336
+ "Headroom proxy ready; persisted headroom config for child spawns"
18337
+ );
18283
18338
  } else {
18284
18339
  persistHeadroomConfig({ enabled: false });
18285
- log.warn("host-agent", "Headroom setup failed (best-effort) \u2014 child will run without Headroom");
18340
+ log.warn(
18341
+ "host-agent",
18342
+ "Headroom setup failed (best-effort) \u2014 child will run without Headroom"
18343
+ );
18286
18344
  }
18287
18345
  }
18288
18346
  } else if (payload.headroomEnabled === false) {
@@ -18313,7 +18371,9 @@ var HostAgentSupervisor = class {
18313
18371
  void reportSessionEvent(
18314
18372
  { hostId: this.identity.hostId, hostToken: this.identity.hostToken },
18315
18373
  { event: "ended", deployId: payload.deployId }
18316
- ).catch((err) => log.trace("host-agent", "session ended report failed (best-effort)", err));
18374
+ ).catch(
18375
+ (err) => log.trace("host-agent", "session ended report failed (best-effort)", err)
18376
+ );
18317
18377
  }
18318
18378
  if (tracked && typeof code === "number" && code !== 0) {
18319
18379
  const detail = tail.trim().slice(-500);
@@ -18377,7 +18437,10 @@ var HostAgentSupervisor = class {
18377
18437
  resolve7();
18378
18438
  };
18379
18439
  const timer = setTimeout(() => {
18380
- log.warn("host-agent", "agent install timed out (180s) \u2014 preview detection may be unavailable");
18440
+ log.warn(
18441
+ "host-agent",
18442
+ "agent install timed out (180s) \u2014 preview detection may be unavailable"
18443
+ );
18381
18444
  try {
18382
18445
  child.kill("SIGTERM");
18383
18446
  } catch {
@@ -18387,7 +18450,10 @@ var HostAgentSupervisor = class {
18387
18450
  child.once("exit", (code) => {
18388
18451
  clearTimeout(timer);
18389
18452
  if (code !== 0) {
18390
- log.warn("host-agent", `agent install exited code=${code} \u2014 preview detection may be unavailable; agent still runs`);
18453
+ log.warn(
18454
+ "host-agent",
18455
+ `agent install exited code=${code} \u2014 preview detection may be unavailable; agent still runs`
18456
+ );
18391
18457
  } else {
18392
18458
  log.info("host-agent", "agent CLI installed");
18393
18459
  }
@@ -23692,6 +23758,19 @@ async function runAcpSession(opts) {
23692
23758
  onUnexpectedExit: (code, signal) => {
23693
23759
  log.warn("acpRunner", `adapter died code=${code} signal=${signal}; shutting down session`);
23694
23760
  const authFail = looksLikeAuthFailure(recentStderr.join("\n"));
23761
+ if (authFail) {
23762
+ void fetch(
23763
+ `${resolveApiBaseUrl()}/api/plugin/agents/${encodeURIComponent(opts.agent)}/credential-invalid`,
23764
+ {
23765
+ method: "POST",
23766
+ headers: {
23767
+ "Content-Type": "application/json",
23768
+ "X-Plugin-Auth-Token": opts.pluginAuthToken
23769
+ },
23770
+ body: JSON.stringify({ sessionId: opts.sessionId, pluginId: opts.pluginId })
23771
+ }
23772
+ ).catch(() => void 0);
23773
+ }
23695
23774
  void streaming.closeAll().then(
23696
23775
  () => publisher.publishOutput({
23697
23776
  type: "text",
@@ -28512,7 +28591,7 @@ function checkChokidar() {
28512
28591
  }
28513
28592
  async function doctor(args2 = []) {
28514
28593
  const json = args2.includes("--json");
28515
- const cliVersion = true ? "2.39.76" : "0.0.0-dev";
28594
+ const cliVersion = true ? "2.39.78" : "0.0.0-dev";
28516
28595
  const apiBase2 = resolveApiBaseUrl();
28517
28596
  const diagnosticId = (0, import_node_crypto8.randomUUID)();
28518
28597
  log.info("doctor", `run id=${diagnosticId} cli=${cliVersion}`);
@@ -28711,7 +28790,7 @@ async function completion(args2) {
28711
28790
  // src/commands/version.ts
28712
28791
  var import_picocolors14 = __toESM(require("picocolors"));
28713
28792
  function version2() {
28714
- const v = true ? "2.39.76" : "unknown";
28793
+ const v = true ? "2.39.78" : "unknown";
28715
28794
  console.log(`${import_picocolors14.default.bold("codeam-cli")} ${import_picocolors14.default.cyan(v)}`);
28716
28795
  }
28717
28796
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "codeam-cli",
3
- "version": "2.39.76",
3
+ "version": "2.39.78",
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",