codeam-cli 2.39.68 → 2.39.71

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,22 @@ 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.69] — 2026-06-21
8
+
9
+ ### Added
10
+
11
+ - **cli:** Report Headroom prompt-cache savings, not just compression
12
+
13
+ ### Fixed
14
+
15
+ - **cli:** Make the pairing code more legible (letter-spacing + bold + contrast)
16
+
17
+ ## [2.39.68] — 2026-06-20
18
+
19
+ ### Tests
20
+
21
+ - **host-agent:** Don't leak a heartbeat timer in the self_hosted_stop test
22
+
7
23
  ## [2.39.66] — 2026-06-20
8
24
 
9
25
  ### Fixed
package/dist/index.js CHANGED
@@ -1084,12 +1084,12 @@ var PromiseQueue = class {
1084
1084
  return promise;
1085
1085
  }
1086
1086
  async join() {
1087
- let promises = Object.values(this.promiseByIds);
1088
- let length = promises.length;
1087
+ let promises2 = Object.values(this.promiseByIds);
1088
+ let length = promises2.length;
1089
1089
  while (length > 0) {
1090
- await Promise.all(promises);
1091
- promises = Object.values(this.promiseByIds);
1092
- length = promises.length;
1090
+ await Promise.all(promises2);
1091
+ promises2 = Object.values(this.promiseByIds);
1092
+ length = promises2.length;
1093
1093
  }
1094
1094
  }
1095
1095
  get length() {
@@ -1427,8 +1427,8 @@ function safeSetTimeout(fn, timeout) {
1427
1427
  return t2;
1428
1428
  }
1429
1429
  var isError = (x) => x instanceof Error;
1430
- function allSettled(promises) {
1431
- return Promise.all(promises.map((p2) => (p2 ?? Promise.resolve()).then((value) => ({
1430
+ function allSettled(promises2) {
1431
+ return Promise.all(promises2.map((p2) => (p2 ?? Promise.resolve()).then((value) => ({
1432
1432
  status: "fulfilled",
1433
1433
  value
1434
1434
  }), (reason) => ({
@@ -5388,7 +5388,7 @@ function readAnonId() {
5388
5388
  }
5389
5389
  function superProperties() {
5390
5390
  return {
5391
- cliVersion: true ? "2.39.68" : "0.0.0-dev",
5391
+ cliVersion: true ? "2.39.71" : "0.0.0-dev",
5392
5392
  nodeVersion: process.version,
5393
5393
  platform: process.platform,
5394
5394
  arch: process.arch,
@@ -5547,7 +5547,7 @@ var os4 = __toESM(require("os"));
5547
5547
  // package.json
5548
5548
  var package_default = {
5549
5549
  name: "codeam-cli",
5550
- version: "2.39.68",
5550
+ version: "2.39.71",
5551
5551
  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.",
5552
5552
  type: "commonjs",
5553
5553
  main: "dist/index.js",
@@ -8281,10 +8281,12 @@ function boxRow(content, visibleLength) {
8281
8281
  return ` \u2502${content}${pad}\u2502`;
8282
8282
  }
8283
8283
  function showPairingCode(code) {
8284
+ const spaced = code.split("").join(" ");
8284
8285
  console.log(BOX_BORDER_TOP);
8285
- const codeVisible = ` Code: ${code}`.length;
8286
+ const label = " Code: ";
8287
+ const codeVisible = `${label}${spaced}`.length;
8286
8288
  console.log(
8287
- boxRow(` Code: ${import_picocolors.default.bold(import_picocolors.default.yellow(code))}`, codeVisible)
8289
+ boxRow(`${label}${import_picocolors.default.bold(import_picocolors.default.yellow(spaced))}`, codeVisible)
8288
8290
  );
8289
8291
  console.log(BOX_BORDER_BOT);
8290
8292
  console.log("");
@@ -17181,7 +17183,14 @@ function provisionAgentCredentials(publicAgentId, auth, homeDir2 = os30.homedir(
17181
17183
  }
17182
17184
 
17183
17185
  // src/services/headroom/stats-reporter.ts
17184
- var ZERO = { rawTokensEst: 0, sentTokensEst: 0, cachedTokens: 0, retrieveHops: 0 };
17186
+ var ZERO = {
17187
+ rawTokensEst: 0,
17188
+ sentTokensEst: 0,
17189
+ cachedTokens: 0,
17190
+ retrieveHops: 0,
17191
+ cacheReadTokens: 0,
17192
+ cacheSavingsUsd: 0
17193
+ };
17185
17194
  function read(stats) {
17186
17195
  const totals = stats.agent_usage?.totals;
17187
17196
  const compression = stats.summary?.compression;
@@ -17191,7 +17200,9 @@ function read(stats) {
17191
17200
  rawTokensEst,
17192
17201
  sentTokensEst,
17193
17202
  cachedTokens: 0,
17194
- retrieveHops: stats.summary?.mcp?.retrievals ?? 0
17203
+ retrieveHops: stats.summary?.mcp?.retrievals ?? 0,
17204
+ cacheReadTokens: stats.prefix_cache?.totals?.cache_read_tokens ?? 0,
17205
+ cacheSavingsUsd: stats.summary?.cost?.breakdown?.cache_savings_usd ?? 0
17195
17206
  };
17196
17207
  }
17197
17208
  function mapStatsToSavings(stats, prev) {
@@ -17201,7 +17212,9 @@ function mapStatsToSavings(stats, prev) {
17201
17212
  rawTokensEst: d3(next.rawTokensEst, prev.rawTokensEst),
17202
17213
  sentTokensEst: d3(next.sentTokensEst, prev.sentTokensEst),
17203
17214
  cachedTokens: d3(next.cachedTokens, prev.cachedTokens),
17204
- retrieveHops: d3(next.retrieveHops, prev.retrieveHops)
17215
+ retrieveHops: d3(next.retrieveHops, prev.retrieveHops),
17216
+ cacheReadTokens: d3(next.cacheReadTokens, prev.cacheReadTokens),
17217
+ cacheSavingsUsd: d3(next.cacheSavingsUsd, prev.cacheSavingsUsd)
17205
17218
  };
17206
17219
  return { next, delta };
17207
17220
  }
@@ -17220,7 +17233,9 @@ var HeadroomStatsReporter = class {
17220
17233
  try {
17221
17234
  const { next, delta } = mapStatsToSavings(await this.deps.fetchStats(), this.prev);
17222
17235
  this.prev = next;
17223
- if (delta.rawTokensEst > 0) await this.deps.postSavings(delta);
17236
+ if (delta.rawTokensEst > 0 || delta.cacheReadTokens > 0 || delta.cacheSavingsUsd > 0) {
17237
+ await this.deps.postSavings(delta);
17238
+ }
17224
17239
  } catch {
17225
17240
  }
17226
17241
  }
@@ -17391,7 +17406,7 @@ function checkForUpdates() {
17391
17406
  if (process.env.CODEAM_DISABLE_UPDATE_CHECK === "1") return;
17392
17407
  if (process.env.CI) return;
17393
17408
  if (!process.stdout.isTTY) return;
17394
- const current = true ? "2.39.68" : null;
17409
+ const current = true ? "2.39.71" : null;
17395
17410
  if (!current) return;
17396
17411
  const cache = readCache();
17397
17412
  const fresh = cache && Date.now() - cache.fetchedAt < TTL_MS;
@@ -17484,7 +17499,8 @@ var CONTROL_AGENT_META = {
17484
17499
  };
17485
17500
  var PEP668_MARKER = "externally-managed-environment";
17486
17501
  var PM_INSTALL_TIMEOUT_MS = 18e4;
17487
- var PIP_INSTALL_TIMEOUT_MS = 12e4;
17502
+ var ENGINE_INSTALL_TIMEOUT_MS = 36e4;
17503
+ var HEADROOM_MIN_FREE_DISK_BYTES = 3 * 1024 * 1024 * 1024;
17488
17504
  var defaultHeadroomRunner = {
17489
17505
  which(cmd) {
17490
17506
  try {
@@ -17696,57 +17712,50 @@ function bundledClaudeBinDir() {
17696
17712
  }
17697
17713
  return null;
17698
17714
  }
17715
+ async function getFreeDiskBytes(dir) {
17716
+ try {
17717
+ const s = await fs39.promises.statfs(dir);
17718
+ return s.bsize * s.bavail;
17719
+ } catch {
17720
+ return null;
17721
+ }
17722
+ }
17699
17723
  async function setupHeadroomForSelfHosted(agent, runner = defaultHeadroomRunner) {
17700
- const PIP_PACKAGES = [
17701
- "headroom-ai",
17702
- "fastapi",
17703
- "uvicorn",
17704
- "httpx[http2]",
17705
- "websockets",
17706
- "zstandard"
17707
- ];
17724
+ const SERVER_DEPS = ["fastapi", "uvicorn", "httpx[http2]", "websockets", "zstandard"];
17708
17725
  const pipAvailable = await ensurePip(runner);
17709
17726
  if (!pipAvailable) {
17710
17727
  return false;
17711
17728
  }
17712
- const installOk = await (async () => {
17713
- const baseArgs = ["-m", "pip", "install", "--quiet", ...PIP_PACKAGES];
17714
- const firstResult = await runner.run("python3", baseArgs, {
17715
- timeoutMs: PIP_INSTALL_TIMEOUT_MS
17716
- });
17717
- if (firstResult.code === 0) {
17718
- log.info("host-agent", "headroom pip install succeeded");
17719
- return true;
17720
- }
17721
- if (firstResult.stderr.includes(PEP668_MARKER)) {
17722
- log.info(
17723
- "host-agent",
17724
- "PEP 668 externally-managed-environment detected \u2014 retrying with --break-system-packages"
17725
- );
17726
- const retryResult = await runner.run(
17727
- "python3",
17728
- [...baseArgs, "--break-system-packages"],
17729
- { timeoutMs: PIP_INSTALL_TIMEOUT_MS }
17730
- );
17731
- if (retryResult.code === 0) {
17732
- log.info("host-agent", "headroom pip install succeeded (--break-system-packages)");
17733
- return true;
17734
- }
17735
- log.warn(
17736
- "host-agent",
17737
- `headroom pip install failed even with --break-system-packages (code=${String(retryResult.code)}) \u2014 skipping Headroom`
17738
- );
17739
- return false;
17729
+ const pipInstall = async (pkgs, extraArgs, timeoutMs) => {
17730
+ const base = ["-m", "pip", "install", "--quiet", ...extraArgs, ...pkgs];
17731
+ const r = await runner.run("python3", base, { timeoutMs });
17732
+ if (r.code === 0) return true;
17733
+ if (r.stderr.includes(PEP668_MARKER)) {
17734
+ const r2 = await runner.run("python3", [...base, "--break-system-packages"], { timeoutMs });
17735
+ return r2.code === 0;
17740
17736
  }
17741
- log.warn(
17742
- "host-agent",
17743
- `headroom pip install exited code=${String(firstResult.code)} \u2014 skipping Headroom`
17744
- );
17745
17737
  return false;
17746
- })();
17738
+ };
17739
+ const torchOk = await pipInstall(
17740
+ ["torch"],
17741
+ ["--index-url", "https://download.pytorch.org/whl/cpu"],
17742
+ ENGINE_INSTALL_TIMEOUT_MS
17743
+ );
17744
+ log.info(
17745
+ "host-agent",
17746
+ torchOk ? "CPU-only PyTorch installed \u2014 Kompress (ML) compressor available" : "CPU torch install failed \u2014 installing code-aware engine only (Kompress unavailable)"
17747
+ );
17748
+ const headroomPkg = torchOk ? "headroom-ai[code,ml]" : "headroom-ai[code]";
17749
+ const installOk = await pipInstall(
17750
+ [headroomPkg, ...SERVER_DEPS],
17751
+ [],
17752
+ ENGINE_INSTALL_TIMEOUT_MS
17753
+ );
17747
17754
  if (!installOk) {
17755
+ log.warn("host-agent", `headroom engine install failed (${headroomPkg}) \u2014 skipping Headroom`);
17748
17756
  return false;
17749
17757
  }
17758
+ log.info("host-agent", `headroom + engines installed (${headroomPkg})`);
17750
17759
  if (!runner.which("headroom")) {
17751
17760
  log.warn("host-agent", "headroom not found on PATH after install \u2014 skipping init");
17752
17761
  return false;
@@ -17796,7 +17805,7 @@ var defaultSpawner = (env, cwd, args2 = []) => (0, import_node_child_process13.s
17796
17805
  detached: false
17797
17806
  });
17798
17807
  function currentCliVersion() {
17799
- return true ? "2.39.68" : null;
17808
+ return true ? "2.39.71" : null;
17800
17809
  }
17801
17810
  function runCmd(cmd, args2, timeoutMs) {
17802
17811
  return new Promise((resolve7) => {
@@ -18199,17 +18208,32 @@ var HostAgentSupervisor = class {
18199
18208
  }
18200
18209
  if (payload.headroomEnabled && payload.headroomAgent && payload.headroomSavingsIngestUrl) {
18201
18210
  report("headroom", "setting up Headroom proxy");
18202
- const headroomOk = await this.setupHeadroom(payload.headroomAgent);
18203
- if (headroomOk) {
18204
- persistHeadroomConfig({
18205
- enabled: true,
18206
- agent: agentIdToHeadroomKind(payload.headroomAgent),
18207
- ingestUrl: payload.headroomSavingsIngestUrl
18208
- });
18209
- log.info("host-agent", "Headroom proxy ready; persisted headroom config for child spawns");
18210
- } else {
18211
+ const freeBytes = await getFreeDiskBytes(os32.homedir());
18212
+ if (freeBytes !== null && freeBytes < HEADROOM_MIN_FREE_DISK_BYTES) {
18213
+ const freeGb = (freeBytes / 1e9).toFixed(1);
18214
+ const needGb = Math.round(HEADROOM_MIN_FREE_DISK_BYTES / 1e9);
18215
+ report(
18216
+ "headroom",
18217
+ `Token-saving optimizer skipped \u2014 needs ~${needGb} GB free, host has ${freeGb} GB. The agent runs normally without it.`
18218
+ );
18219
+ log.warn(
18220
+ "host-agent",
18221
+ `Headroom skipped: insufficient disk (free=${freeGb}GB < ${needGb}GB)`
18222
+ );
18211
18223
  persistHeadroomConfig({ enabled: false });
18212
- log.warn("host-agent", "Headroom setup failed (best-effort) \u2014 child will run without Headroom");
18224
+ } else {
18225
+ const headroomOk = await this.setupHeadroom(payload.headroomAgent);
18226
+ if (headroomOk) {
18227
+ persistHeadroomConfig({
18228
+ enabled: true,
18229
+ agent: agentIdToHeadroomKind(payload.headroomAgent),
18230
+ ingestUrl: payload.headroomSavingsIngestUrl
18231
+ });
18232
+ log.info("host-agent", "Headroom proxy ready; persisted headroom config for child spawns");
18233
+ } else {
18234
+ persistHeadroomConfig({ enabled: false });
18235
+ log.warn("host-agent", "Headroom setup failed (best-effort) \u2014 child will run without Headroom");
18236
+ }
18213
18237
  }
18214
18238
  } else if (payload.headroomEnabled === false) {
18215
18239
  persistHeadroomConfig({ enabled: false });
@@ -28435,7 +28459,7 @@ function checkChokidar() {
28435
28459
  }
28436
28460
  async function doctor(args2 = []) {
28437
28461
  const json = args2.includes("--json");
28438
- const cliVersion = true ? "2.39.68" : "0.0.0-dev";
28462
+ const cliVersion = true ? "2.39.71" : "0.0.0-dev";
28439
28463
  const apiBase2 = resolveApiBaseUrl();
28440
28464
  const diagnosticId = (0, import_node_crypto8.randomUUID)();
28441
28465
  log.info("doctor", `run id=${diagnosticId} cli=${cliVersion}`);
@@ -28634,7 +28658,7 @@ async function completion(args2) {
28634
28658
  // src/commands/version.ts
28635
28659
  var import_picocolors14 = __toESM(require("picocolors"));
28636
28660
  function version2() {
28637
- const v = true ? "2.39.68" : "unknown";
28661
+ const v = true ? "2.39.71" : "unknown";
28638
28662
  console.log(`${import_picocolors14.default.bold("codeam-cli")} ${import_picocolors14.default.cyan(v)}`);
28639
28663
  }
28640
28664
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "codeam-cli",
3
- "version": "2.39.68",
3
+ "version": "2.39.71",
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",