codeam-cli 2.49.3 → 2.49.5

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,21 @@ 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.49.4] — 2026-06-27
8
+
9
+ ### Fixed
10
+
11
+ - **cli:** Serialize Headroom SSE event POSTs to preserve emit order
12
+
13
+ ## [2.49.3] — 2026-06-27
14
+
15
+ ### Fixed
16
+
17
+ - **cli:** Resolve a Python ≥3.10 for Headroom install (avoid macOS Xcode py3.9)
18
+ - **cli:** Auto-install Python ≥3.10 for Headroom when none present
19
+ - **cli:** Capture stdout in headroom runner so the python ≥3.10 probe works
20
+ - **cli:** Require pip in the headroom python probe (skip pip-less newest python)
21
+
7
22
  ## [2.49.2] — 2026-06-27
8
23
 
9
24
  ### Fixed
package/dist/index.js CHANGED
@@ -5397,7 +5397,7 @@ function readAnonId() {
5397
5397
  }
5398
5398
  function superProperties() {
5399
5399
  return {
5400
- cliVersion: true ? "2.49.3" : "0.0.0-dev",
5400
+ cliVersion: true ? "2.49.5" : "0.0.0-dev",
5401
5401
  nodeVersion: process.version,
5402
5402
  platform: process.platform,
5403
5403
  arch: process.arch,
@@ -5578,7 +5578,7 @@ var os4 = __toESM(require("os"));
5578
5578
  // package.json
5579
5579
  var package_default = {
5580
5580
  name: "codeam-cli",
5581
- version: "2.49.3",
5581
+ version: "2.49.5",
5582
5582
  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.",
5583
5583
  type: "commonjs",
5584
5584
  main: "dist/index.js",
@@ -14747,7 +14747,7 @@ async function autoUpgradeBeforeCriticalCommand() {
14747
14747
  if (process.env.NODE_ENV === "test") return;
14748
14748
  if (process.env.CODEAM_DISABLE_UPDATE_CHECK === "1") return;
14749
14749
  if (process.env.CI) return;
14750
- const current = true ? "2.49.3" : null;
14750
+ const current = true ? "2.49.5" : null;
14751
14751
  if (!current) return;
14752
14752
  const cache = readCache();
14753
14753
  const fresh = cache && Date.now() - cache.fetchedAt < TTL_MS;
@@ -14764,7 +14764,7 @@ function checkForUpdates() {
14764
14764
  if (process.env.CODEAM_DISABLE_UPDATE_CHECK === "1") return;
14765
14765
  if (process.env.CI) return;
14766
14766
  if (!process.stdout.isTTY) return;
14767
- const current = true ? "2.49.3" : null;
14767
+ const current = true ? "2.49.5" : null;
14768
14768
  if (!current) return;
14769
14769
  const cache = readCache();
14770
14770
  const fresh = cache && Date.now() - cache.fetchedAt < TTL_MS;
@@ -14827,6 +14827,51 @@ function maybeStartHeadroomReporter(ctx) {
14827
14827
  return null;
14828
14828
  }
14829
14829
  }
14830
+ function maybeResumeLocalHeadroomReporter(ctx) {
14831
+ if (process.env["HEADROOM_ENABLED"] === "1") return null;
14832
+ try {
14833
+ const file = headroomConfigPath();
14834
+ if (!fs31.existsSync(file)) return null;
14835
+ const cfg = JSON.parse(fs31.readFileSync(file, "utf8"));
14836
+ if (!cfg?.enabled) return null;
14837
+ const agent = cfg.agent ?? "claude";
14838
+ const ingestUrl = `${resolveApiBaseUrl()}/api/sessions/${ctx.sessionId}/headroom-savings`;
14839
+ const reporter = new HeadroomStatsReporter({
14840
+ inputPricePerMillionUsd: resolveInputPricePerMillion(agent),
14841
+ fetchStats: async () => {
14842
+ const res = await fetch("http://localhost:8787/stats");
14843
+ return res.json();
14844
+ },
14845
+ // Body MUST match HeadroomSavingsDto + PluginAuthGuard (sessionId +
14846
+ // pluginId in the body) — same shape as the codespace reporter above and
14847
+ // the on-demand `startReporter` in handlers.ts.
14848
+ postSavings: async (delta) => {
14849
+ await fetch(ingestUrl, {
14850
+ method: "POST",
14851
+ headers: {
14852
+ "Content-Type": "application/json",
14853
+ "X-Plugin-Auth-Token": ctx.pluginAuthToken
14854
+ },
14855
+ body: JSON.stringify({
14856
+ sessionId: ctx.sessionId,
14857
+ pluginId: ctx.pluginId,
14858
+ agentId: agent,
14859
+ savings: delta
14860
+ })
14861
+ });
14862
+ }
14863
+ });
14864
+ reporter.start();
14865
+ log.info("headroom", "resumed on-demand local savings reporter from config");
14866
+ return reporter;
14867
+ } catch (err) {
14868
+ log.warn(
14869
+ "headroom",
14870
+ `failed to resume local Headroom reporter (best-effort): ${err instanceof Error ? err.message : String(err)}`
14871
+ );
14872
+ return null;
14873
+ }
14874
+ }
14830
14875
  function isRefreshCredentialsPayload(p2) {
14831
14876
  return typeof p2.agentId === "string" && typeof p2.sealedAgentAuth === "string";
14832
14877
  }
@@ -15400,7 +15445,7 @@ var defaultSpawner = (env, cwd, args2 = []) => (0, import_node_child_process14.s
15400
15445
  detached: false
15401
15446
  });
15402
15447
  function currentCliVersion() {
15403
- return true ? "2.49.3" : null;
15448
+ return true ? "2.49.5" : null;
15404
15449
  }
15405
15450
  function runCmd(cmd, args2, timeoutMs) {
15406
15451
  return new Promise((resolve7) => {
@@ -18315,6 +18360,7 @@ var envWriteH = async (ctx, cmd, parsed) => {
18315
18360
  }
18316
18361
  };
18317
18362
  var _activeReporter = null;
18363
+ var _headroomEmitChain = Promise.resolve();
18318
18364
  var headroomConfigureH = async (ctx, cmd, parsed) => {
18319
18365
  const action = parsed.action;
18320
18366
  if (action !== "enable" && action !== "disable" && action !== "status") {
@@ -18382,7 +18428,19 @@ var headroomConfigureH = async (ctx, cmd, parsed) => {
18382
18428
  "Content-Type": "application/json",
18383
18429
  ...opts.pluginAuthToken ? { "X-Plugin-Auth-Token": opts.pluginAuthToken } : {}
18384
18430
  },
18385
- body: JSON.stringify({ agentId: opts.agent, ...delta })
18431
+ // Body MUST match HeadroomSavingsDto + PluginAuthGuard, which read
18432
+ // `sessionId` + `pluginId` from the body and 401 if either is
18433
+ // missing. Mirror the self-hosted reporter in host-agent.ts exactly.
18434
+ // Previously this sent `{ agentId, ...delta }` (no sessionId/pluginId,
18435
+ // delta spread flat instead of nested under `savings`) → every POST
18436
+ // was rejected 401 at the guard and silently swallowed (fetch status
18437
+ // unchecked), so local on-demand savings never reached the backend.
18438
+ body: JSON.stringify({
18439
+ sessionId: ctx.sessionId,
18440
+ pluginId: ctx.pluginId,
18441
+ agentId: opts.agent,
18442
+ savings: delta
18443
+ })
18386
18444
  });
18387
18445
  }
18388
18446
  });
@@ -18407,14 +18465,17 @@ var headroomConfigureH = async (ctx, cmd, parsed) => {
18407
18465
  }
18408
18466
  },
18409
18467
  emit: (event) => {
18410
- if (!ctx.pluginAuthToken) return;
18411
- void postHeadroomEvent({
18412
- sessionId: ctx.sessionId,
18413
- pluginId: ctx.pluginId,
18414
- pluginAuthToken: ctx.pluginAuthToken,
18415
- type: event.type,
18416
- payload: "step" in event ? { step: event.step } : { state: event.state }
18417
- });
18468
+ const token = ctx.pluginAuthToken;
18469
+ if (!token) return;
18470
+ _headroomEmitChain = _headroomEmitChain.then(
18471
+ () => postHeadroomEvent({
18472
+ sessionId: ctx.sessionId,
18473
+ pluginId: ctx.pluginId,
18474
+ pluginAuthToken: token,
18475
+ type: event.type,
18476
+ payload: "step" in event ? { step: event.step } : { state: event.state }
18477
+ })
18478
+ );
18418
18479
  }
18419
18480
  });
18420
18481
  await ctx.relay.sendResult(cmd.id, "completed", result);
@@ -27564,6 +27625,14 @@ async function start(requestedAgent) {
27564
27625
  process.once("exit", () => {
27565
27626
  headroomReporter?.stop();
27566
27627
  });
27628
+ const localHeadroomReporter = session.pluginAuthToken ? maybeResumeLocalHeadroomReporter({
27629
+ sessionId: session.id,
27630
+ pluginId,
27631
+ pluginAuthToken: session.pluginAuthToken
27632
+ }) : null;
27633
+ process.once("exit", () => {
27634
+ localHeadroomReporter?.stop();
27635
+ });
27567
27636
  if (process.env.CODESPACES === "true") ensureClaudeOnboarded();
27568
27637
  let beads = null;
27569
27638
  const getBeads = () => beads;
@@ -30342,7 +30411,7 @@ function checkChokidar() {
30342
30411
  }
30343
30412
  async function doctor(args2 = []) {
30344
30413
  const json = args2.includes("--json");
30345
- const cliVersion = true ? "2.49.3" : "0.0.0-dev";
30414
+ const cliVersion = true ? "2.49.5" : "0.0.0-dev";
30346
30415
  const apiBase2 = resolveApiBaseUrl();
30347
30416
  const diagnosticId = (0, import_node_crypto8.randomUUID)();
30348
30417
  log.info("doctor", `run id=${diagnosticId} cli=${cliVersion}`);
@@ -30541,7 +30610,7 @@ async function completion(args2) {
30541
30610
  // src/commands/version.ts
30542
30611
  var import_picocolors15 = __toESM(require("picocolors"));
30543
30612
  function version2() {
30544
- const v = true ? "2.49.3" : "unknown";
30613
+ const v = true ? "2.49.5" : "unknown";
30545
30614
  console.log(`${import_picocolors15.default.bold("codeam-cli")} ${import_picocolors15.default.cyan(v)}`);
30546
30615
  }
30547
30616
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "codeam-cli",
3
- "version": "2.49.3",
3
+ "version": "2.49.5",
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",