codeam-cli 2.49.2 → 2.49.4

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 +15 -0
  2. package/dist/index.js +167 -22
  3. package/package.json +1 -1
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.3] — 2026-06-27
8
+
9
+ ### Fixed
10
+
11
+ - **cli:** Resolve a Python ≥3.10 for Headroom install (avoid macOS Xcode py3.9)
12
+ - **cli:** Auto-install Python ≥3.10 for Headroom when none present
13
+ - **cli:** Capture stdout in headroom runner so the python ≥3.10 probe works
14
+ - **cli:** Require pip in the headroom python probe (skip pip-less newest python)
15
+
16
+ ## [2.49.2] — 2026-06-27
17
+
18
+ ### Fixed
19
+
20
+ - **cli:** Set agentId in ACP HandlerContext so headroom enable works for claude/codex
21
+
7
22
  ## [2.49.1] — 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.2" : "0.0.0-dev",
5400
+ cliVersion: true ? "2.49.4" : "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.2",
5581
+ version: "2.49.4",
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.2" : null;
14750
+ const current = true ? "2.49.4" : 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.2" : null;
14767
+ const current = true ? "2.49.4" : null;
14768
14768
  if (!current) return;
14769
14769
  const cache = readCache();
14770
14770
  const fresh = cache && Date.now() - cache.fetchedAt < TTL_MS;
@@ -14884,14 +14884,17 @@ var defaultHeadroomRunner = {
14884
14884
  const spawnEnv = opts.env ?? process.env;
14885
14885
  const child = (0, import_node_child_process14.spawn)(cmd, args2, { stdio: ["ignore", "pipe", "pipe"], env: spawnEnv });
14886
14886
  let stderrBuf = "";
14887
+ let stdoutBuf = "";
14887
14888
  let settled = false;
14888
14889
  const done = (code) => {
14889
14890
  if (settled) return;
14890
14891
  settled = true;
14891
- resolve7({ code, stderr: stderrBuf });
14892
+ resolve7({ code, stderr: stderrBuf, stdout: stdoutBuf });
14892
14893
  };
14893
14894
  child.stdout?.on("data", (b) => {
14894
- const line = b.toString().replace(/\n+$/, "");
14895
+ const chunk = b.toString();
14896
+ stdoutBuf += chunk;
14897
+ const line = chunk.replace(/\n+$/, "");
14895
14898
  if (line) log.info("host-agent", `headroom[${cmd}]: ${line}`);
14896
14899
  });
14897
14900
  child.stderr?.on("data", (b) => {
@@ -14979,6 +14982,10 @@ function detectPackageManager(runner) {
14979
14982
  }
14980
14983
  return null;
14981
14984
  }
14985
+ function escalateCommand(argv) {
14986
+ const isRoot = process.getuid?.() === 0;
14987
+ return isRoot ? { cmd: argv[0], args: argv.slice(1) } : { cmd: "sudo", args: argv };
14988
+ }
14982
14989
  async function ensurePip(runner) {
14983
14990
  if (runner.which("pip") || runner.which("pip3")) {
14984
14991
  return true;
@@ -14991,8 +14998,7 @@ async function ensurePip(runner) {
14991
14998
  );
14992
14999
  return false;
14993
15000
  }
14994
- const isRoot = process.getuid?.() === 0;
14995
- const escalate = (argv) => isRoot ? { cmd: argv[0], args: argv.slice(1) } : { cmd: "sudo", args: argv };
15001
+ const escalate = escalateCommand;
14996
15002
  const recipe = PROVISION_RECIPES[pm];
14997
15003
  log.info(
14998
15004
  "host-agent",
@@ -15157,6 +15163,132 @@ async function getFreeDiskBytes(dir) {
15157
15163
  return null;
15158
15164
  }
15159
15165
  }
15166
+ async function resolveHeadroomPython(runner) {
15167
+ const PROBE_TIMEOUT_MS = 5e3;
15168
+ const SUFFIXES = ["python3.13", "python3.12", "python3.11", "python3.10"];
15169
+ const PREFIX_DIRS = ["/opt/homebrew/bin", "/usr/local/bin"];
15170
+ const probe = async (candidate) => {
15171
+ try {
15172
+ const r = await runner.run(
15173
+ candidate,
15174
+ ["-c", 'import sys; print("%d.%d" % sys.version_info[:2])'],
15175
+ { timeoutMs: PROBE_TIMEOUT_MS }
15176
+ );
15177
+ if (r.code !== 0) return false;
15178
+ const parts = (r.stdout ?? "").trim().split(".");
15179
+ const major = parseInt(parts[0] ?? "", 10);
15180
+ const minor = parseInt(parts[1] ?? "", 10);
15181
+ if (!(major === 3 && minor >= 10)) return false;
15182
+ const pipCheck = await runner.run(candidate, ["-m", "pip", "--version"], {
15183
+ timeoutMs: PROBE_TIMEOUT_MS
15184
+ });
15185
+ return pipCheck.code === 0;
15186
+ } catch {
15187
+ return false;
15188
+ }
15189
+ };
15190
+ for (const suffix of SUFFIXES) {
15191
+ try {
15192
+ if (await probe(suffix)) return suffix;
15193
+ } catch {
15194
+ }
15195
+ }
15196
+ for (const suffix of SUFFIXES) {
15197
+ for (const dir of PREFIX_DIRS) {
15198
+ const candidate = `${dir}/${suffix}`;
15199
+ try {
15200
+ if (await probe(candidate)) return candidate;
15201
+ } catch {
15202
+ }
15203
+ }
15204
+ }
15205
+ try {
15206
+ if (await probe("python3")) return "python3";
15207
+ } catch {
15208
+ }
15209
+ return null;
15210
+ }
15211
+ var PY_INSTALL_TIMEOUT_MS = 6e5;
15212
+ var MODERN_PYTHON_RECIPES = {
15213
+ "apt-get": [
15214
+ ["apt-get", "install", "-y", "python3.12"],
15215
+ ["apt-get", "install", "-y", "python3.11"],
15216
+ ["apt-get", "install", "-y", "python3"]
15217
+ ],
15218
+ dnf: [
15219
+ ["dnf", "install", "-y", "python3.12"],
15220
+ ["dnf", "install", "-y", "python3"]
15221
+ ],
15222
+ yum: [
15223
+ ["yum", "install", "-y", "python3.12"],
15224
+ ["yum", "install", "-y", "python3"]
15225
+ ],
15226
+ apk: [["apk", "add", "--no-cache", "python3"]],
15227
+ pacman: [["pacman", "-Sy", "--noconfirm", "python"]],
15228
+ zypper: [
15229
+ ["zypper", "--non-interactive", "install", "python311"],
15230
+ ["zypper", "--non-interactive", "install", "python3"]
15231
+ ]
15232
+ };
15233
+ async function ensureModernPython(runner) {
15234
+ let py = await resolveHeadroomPython(runner);
15235
+ if (py !== null) return py;
15236
+ try {
15237
+ if (process.platform === "darwin") {
15238
+ if (runner.which("brew")) {
15239
+ log.info("host-agent", "no Python \u22653.10 found \u2014 installing python@3.12 via Homebrew");
15240
+ const r = await runner.run("brew", ["install", "python@3.12"], {
15241
+ timeoutMs: PY_INSTALL_TIMEOUT_MS
15242
+ });
15243
+ if (r.code !== 0) {
15244
+ log.warn(
15245
+ "host-agent",
15246
+ `brew install python@3.12 exited ${String(r.code)} \u2014 will re-probe anyway`
15247
+ );
15248
+ }
15249
+ } else {
15250
+ log.warn(
15251
+ "host-agent",
15252
+ "no Python \u22653.10 and Homebrew is absent \u2014 cannot auto-install Python on macOS"
15253
+ );
15254
+ }
15255
+ } else if (process.platform === "linux") {
15256
+ const pm = detectPackageManager(runner);
15257
+ if (!pm) {
15258
+ log.warn(
15259
+ "host-agent",
15260
+ "no Python \u22653.10 and no known package manager (apt-get/apk/dnf/yum/pacman/zypper) \u2014 cannot auto-install Python"
15261
+ );
15262
+ } else {
15263
+ log.info("host-agent", `no Python \u22653.10 found \u2014 installing a modern Python via ${pm}`);
15264
+ for (const argv of MODERN_PYTHON_RECIPES[pm]) {
15265
+ const { cmd, args: args2 } = escalateCommand(argv);
15266
+ const r = await runner.run(cmd, args2, { timeoutMs: PY_INSTALL_TIMEOUT_MS });
15267
+ if (r.code === 0) {
15268
+ log.info("host-agent", `installed Python package via ${pm}: ${argv.join(" ")}`);
15269
+ break;
15270
+ }
15271
+ log.warn(
15272
+ "host-agent",
15273
+ `${pm} install '${argv.join(" ")}' exited ${String(r.code)} \u2014 trying next`
15274
+ );
15275
+ }
15276
+ }
15277
+ } else {
15278
+ log.warn(
15279
+ "host-agent",
15280
+ `no Python \u22653.10 and platform '${process.platform}' has no auto-install path`
15281
+ );
15282
+ }
15283
+ } catch (e) {
15284
+ log.warn(
15285
+ "host-agent",
15286
+ `Python auto-install threw unexpectedly (best-effort): ${e instanceof Error ? e.message : String(e)}`
15287
+ );
15288
+ }
15289
+ py = await resolveHeadroomPython(runner);
15290
+ return py;
15291
+ }
15160
15292
  async function setupHeadroomForSelfHosted(agent, runner = defaultHeadroomRunner, opts = {}) {
15161
15293
  const extras = opts.extras ?? ["proxy", "code"];
15162
15294
  const onProgress = opts.onProgress ?? (() => {
@@ -15166,12 +15298,21 @@ async function setupHeadroomForSelfHosted(agent, runner = defaultHeadroomRunner,
15166
15298
  if (!pipAvailable) {
15167
15299
  return false;
15168
15300
  }
15301
+ const py = await ensureModernPython(runner);
15302
+ if (py === null) {
15303
+ log.warn(
15304
+ "host-agent",
15305
+ "Headroom needs Python \u22653.10 and auto-install failed (no brew on macOS / package manager couldn\u2019t provide it) \u2014 skipping Headroom"
15306
+ );
15307
+ return false;
15308
+ }
15309
+ log.info("host-agent", `Headroom will use interpreter: ${py}`);
15169
15310
  const pipInstall = async (pkgs, extraArgs, timeoutMs) => {
15170
15311
  const base = ["-m", "pip", "install", "--quiet", ...extraArgs, ...pkgs];
15171
- const r = await runner.run("python3", base, { timeoutMs });
15312
+ const r = await runner.run(py, base, { timeoutMs });
15172
15313
  if (r.code === 0) return true;
15173
15314
  if (r.stderr.includes(PEP668_MARKER)) {
15174
- const r2 = await runner.run("python3", [...base, "--break-system-packages"], { timeoutMs });
15315
+ const r2 = await runner.run(py, [...base, "--break-system-packages"], { timeoutMs });
15175
15316
  return r2.code === 0;
15176
15317
  }
15177
15318
  return false;
@@ -15190,7 +15331,7 @@ async function setupHeadroomForSelfHosted(agent, runner = defaultHeadroomRunner,
15190
15331
  'snapshot_download("chopratejas/kompress-v2-base", allow_patterns=["*.json","onnx/*.onnx","kompress-int8-wo.onnx"])',
15191
15332
  'snapshot_download("answerdotai/ModernBERT-base", allow_patterns=["*.json","tokenizer*","*.txt","vocab*","merges*"])'
15192
15333
  ].join("\n");
15193
- const dl = await runner.run("python3", ["-c", predownloadPy], {
15334
+ const dl = await runner.run(py, ["-c", predownloadPy], {
15194
15335
  timeoutMs: ENGINE_INSTALL_TIMEOUT_MS
15195
15336
  });
15196
15337
  log.info(
@@ -15259,7 +15400,7 @@ var defaultSpawner = (env, cwd, args2 = []) => (0, import_node_child_process14.s
15259
15400
  detached: false
15260
15401
  });
15261
15402
  function currentCliVersion() {
15262
- return true ? "2.49.2" : null;
15403
+ return true ? "2.49.4" : null;
15263
15404
  }
15264
15405
  function runCmd(cmd, args2, timeoutMs) {
15265
15406
  return new Promise((resolve7) => {
@@ -18174,6 +18315,7 @@ var envWriteH = async (ctx, cmd, parsed) => {
18174
18315
  }
18175
18316
  };
18176
18317
  var _activeReporter = null;
18318
+ var _headroomEmitChain = Promise.resolve();
18177
18319
  var headroomConfigureH = async (ctx, cmd, parsed) => {
18178
18320
  const action = parsed.action;
18179
18321
  if (action !== "enable" && action !== "disable" && action !== "status") {
@@ -18266,14 +18408,17 @@ var headroomConfigureH = async (ctx, cmd, parsed) => {
18266
18408
  }
18267
18409
  },
18268
18410
  emit: (event) => {
18269
- if (!ctx.pluginAuthToken) return;
18270
- void postHeadroomEvent({
18271
- sessionId: ctx.sessionId,
18272
- pluginId: ctx.pluginId,
18273
- pluginAuthToken: ctx.pluginAuthToken,
18274
- type: event.type,
18275
- payload: "step" in event ? { step: event.step } : { state: event.state }
18276
- });
18411
+ const token = ctx.pluginAuthToken;
18412
+ if (!token) return;
18413
+ _headroomEmitChain = _headroomEmitChain.then(
18414
+ () => postHeadroomEvent({
18415
+ sessionId: ctx.sessionId,
18416
+ pluginId: ctx.pluginId,
18417
+ pluginAuthToken: token,
18418
+ type: event.type,
18419
+ payload: "step" in event ? { step: event.step } : { state: event.state }
18420
+ })
18421
+ );
18277
18422
  }
18278
18423
  });
18279
18424
  await ctx.relay.sendResult(cmd.id, "completed", result);
@@ -30201,7 +30346,7 @@ function checkChokidar() {
30201
30346
  }
30202
30347
  async function doctor(args2 = []) {
30203
30348
  const json = args2.includes("--json");
30204
- const cliVersion = true ? "2.49.2" : "0.0.0-dev";
30349
+ const cliVersion = true ? "2.49.4" : "0.0.0-dev";
30205
30350
  const apiBase2 = resolveApiBaseUrl();
30206
30351
  const diagnosticId = (0, import_node_crypto8.randomUUID)();
30207
30352
  log.info("doctor", `run id=${diagnosticId} cli=${cliVersion}`);
@@ -30400,7 +30545,7 @@ async function completion(args2) {
30400
30545
  // src/commands/version.ts
30401
30546
  var import_picocolors15 = __toESM(require("picocolors"));
30402
30547
  function version2() {
30403
- const v = true ? "2.49.2" : "unknown";
30548
+ const v = true ? "2.49.4" : "unknown";
30404
30549
  console.log(`${import_picocolors15.default.bold("codeam-cli")} ${import_picocolors15.default.cyan(v)}`);
30405
30550
  }
30406
30551
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "codeam-cli",
3
- "version": "2.49.2",
3
+ "version": "2.49.4",
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",