codeam-cli 2.65.16 → 2.66.0
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 +12 -0
- package/dist/index.js +94 -24
- package/package.json +1 -1
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.65.17] — 2026-08-21
|
|
8
|
+
|
|
9
|
+
### Fixed
|
|
10
|
+
|
|
11
|
+
- **cli:** Install Headroom without root by resolving uv before sudo (#648)
|
|
12
|
+
|
|
13
|
+
## [2.65.16] — 2026-08-20
|
|
14
|
+
|
|
15
|
+
### Added
|
|
16
|
+
|
|
17
|
+
- **cli:** House agent (CodeAgent Cloud) as an in-session switch target
|
|
18
|
+
|
|
7
19
|
## [2.65.15] — 2026-08-20
|
|
8
20
|
|
|
9
21
|
### Fixed
|
package/dist/index.js
CHANGED
|
@@ -8081,7 +8081,7 @@ function readAnonId() {
|
|
|
8081
8081
|
}
|
|
8082
8082
|
function superProperties() {
|
|
8083
8083
|
return {
|
|
8084
|
-
cliVersion: true ? "2.
|
|
8084
|
+
cliVersion: true ? "2.66.0" : "0.0.0-dev",
|
|
8085
8085
|
nodeVersion: process.version,
|
|
8086
8086
|
platform: process.platform,
|
|
8087
8087
|
arch: process.arch,
|
|
@@ -8323,7 +8323,7 @@ var http = __toESM(require("http"));
|
|
|
8323
8323
|
// package.json
|
|
8324
8324
|
var package_default = {
|
|
8325
8325
|
name: "codeam-cli",
|
|
8326
|
-
version: "2.
|
|
8326
|
+
version: "2.66.0",
|
|
8327
8327
|
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.",
|
|
8328
8328
|
type: "commonjs",
|
|
8329
8329
|
main: "dist/index.js",
|
|
@@ -9845,7 +9845,7 @@ var CommandRelayService = class _CommandRelayService {
|
|
|
9845
9845
|
// fresh + clear the "CLI update available" banner after a self-update
|
|
9846
9846
|
// (a codespace that reinstalls @latest reconnects via heartbeat, not
|
|
9847
9847
|
// pair/reconnect). Older backends ignore the extra field.
|
|
9848
|
-
..."2.
|
|
9848
|
+
..."2.66.0" ? { ideVersion: "2.66.0" } : {}
|
|
9849
9849
|
}).then(() => log.trace("relay", `heartbeat ok online=${online}`)).catch((err) => log.trace("relay", `heartbeat failed online=${online}`, err));
|
|
9850
9850
|
}
|
|
9851
9851
|
/**
|
|
@@ -16995,19 +16995,31 @@ function escalateCommand(argv) {
|
|
|
16995
16995
|
const isRoot = process.getuid?.() === 0;
|
|
16996
16996
|
return isRoot ? { cmd: argv[0], args: argv.slice(1) } : { cmd: "sudo", args: argv };
|
|
16997
16997
|
}
|
|
16998
|
-
async function
|
|
16998
|
+
async function ensurePythonInstaller(runner) {
|
|
16999
16999
|
if (runner.which("pip") || runner.which("pip3")) {
|
|
17000
|
-
return true;
|
|
17000
|
+
return { ok: true, installer: { kind: "pip" } };
|
|
17001
|
+
}
|
|
17002
|
+
if (runner.which("uv")) {
|
|
17003
|
+
log.info("host-agent", "pip absent but uv is available \u2014 installing without root");
|
|
17004
|
+
return { ok: true, installer: { kind: "uv", bin: "uv" } };
|
|
17001
17005
|
}
|
|
17002
17006
|
const pm = detectPackageManager(runner);
|
|
17003
17007
|
if (!pm) {
|
|
17004
|
-
|
|
17005
|
-
|
|
17006
|
-
"pip is
|
|
17007
|
-
|
|
17008
|
-
return false;
|
|
17008
|
+
return {
|
|
17009
|
+
ok: false,
|
|
17010
|
+
reason: "Python pip is not installed and no supported package manager (apt-get/apk/dnf/yum/pacman/zypper) was found. Install pip \u2014 or uv \u2014 on this machine, then retry."
|
|
17011
|
+
};
|
|
17009
17012
|
}
|
|
17010
17013
|
const escalate = escalateCommand;
|
|
17014
|
+
if (process.getuid?.() !== 0) {
|
|
17015
|
+
const probe = await runner.run("sudo", ["-n", "true"], { timeoutMs: 1e4 });
|
|
17016
|
+
if (probe.code !== 0) {
|
|
17017
|
+
return {
|
|
17018
|
+
ok: false,
|
|
17019
|
+
reason: "Python pip is not installed here and this session cannot install it: it runs as a non-root user and sudo requires a password (there is no terminal to type it into). Install python3-pip \u2014 or uv \u2014 on the machine, then retry."
|
|
17020
|
+
};
|
|
17021
|
+
}
|
|
17022
|
+
}
|
|
17011
17023
|
const recipe = PROVISION_RECIPES[pm];
|
|
17012
17024
|
log.info(
|
|
17013
17025
|
"host-agent",
|
|
@@ -17027,21 +17039,23 @@ async function ensurePip(runner) {
|
|
|
17027
17039
|
const { cmd, args: args2 } = escalate(recipe.install);
|
|
17028
17040
|
const installResult = await runner.run(cmd, args2, { timeoutMs: PM_INSTALL_TIMEOUT_MS });
|
|
17029
17041
|
if (installResult.code !== 0) {
|
|
17042
|
+
const detail = installResult.stderr.trim().split("\n").slice(-1)[0] ?? "";
|
|
17030
17043
|
log.warn(
|
|
17031
17044
|
"host-agent",
|
|
17032
17045
|
`${pm} bare-box provision failed (code=${String(installResult.code)}) \u2014 skipping Headroom`
|
|
17033
17046
|
);
|
|
17034
|
-
return
|
|
17047
|
+
return {
|
|
17048
|
+
ok: false,
|
|
17049
|
+
reason: `Installing python3-pip via ${pm} failed (exit ${String(installResult.code)})${detail ? `: ${detail}` : ""}.`
|
|
17050
|
+
};
|
|
17035
17051
|
}
|
|
17036
17052
|
} catch (e) {
|
|
17037
|
-
|
|
17038
|
-
|
|
17039
|
-
|
|
17040
|
-
);
|
|
17041
|
-
return false;
|
|
17053
|
+
const msg = e instanceof Error ? e.message : String(e);
|
|
17054
|
+
log.warn("host-agent", `bare-box provision threw unexpectedly: ${msg} \u2014 skipping Headroom`);
|
|
17055
|
+
return { ok: false, reason: `Installing python3-pip via ${pm} threw: ${msg}` };
|
|
17042
17056
|
}
|
|
17043
17057
|
log.info("host-agent", `bare box provisioned via ${pm} (python3+pip+ca-certificates+curl)`);
|
|
17044
|
-
return true;
|
|
17058
|
+
return { ok: true, installer: { kind: "pip" } };
|
|
17045
17059
|
}
|
|
17046
17060
|
async function resolveHeadroomPython(runner) {
|
|
17047
17061
|
const PROBE_TIMEOUT_MS = 5e3;
|
|
@@ -21693,10 +21707,12 @@ async function setupHeadroomForSelfHosted(agent, runner = defaultHeadroomRunner,
|
|
|
21693
21707
|
const onProgress = opts.onProgress ?? (() => {
|
|
21694
21708
|
});
|
|
21695
21709
|
const SERVER_DEPS = HEADROOM_PIP_COMPANIONS;
|
|
21696
|
-
const
|
|
21697
|
-
if (!
|
|
21710
|
+
const installerResult = await ensurePythonInstaller(runner);
|
|
21711
|
+
if (!installerResult.ok) {
|
|
21712
|
+
log.warn("host-agent", `Headroom: ${installerResult.reason}`);
|
|
21698
21713
|
return false;
|
|
21699
21714
|
}
|
|
21715
|
+
const installer = installerResult.installer;
|
|
21700
21716
|
const py = await ensureModernPython(runner);
|
|
21701
21717
|
if (py === null) {
|
|
21702
21718
|
log.warn(
|
|
@@ -21707,6 +21723,14 @@ async function setupHeadroomForSelfHosted(agent, runner = defaultHeadroomRunner,
|
|
|
21707
21723
|
}
|
|
21708
21724
|
log.info("host-agent", `Headroom will use interpreter: ${py}`);
|
|
21709
21725
|
const pipInstall = async (pkgs, extraArgs, timeoutMs) => {
|
|
21726
|
+
if (installer.kind === "uv") {
|
|
21727
|
+
const r2 = await runner.run(
|
|
21728
|
+
installer.bin,
|
|
21729
|
+
["pip", "install", "--quiet", "--python", py, "--break-system-packages", ...extraArgs, ...pkgs],
|
|
21730
|
+
{ timeoutMs }
|
|
21731
|
+
);
|
|
21732
|
+
return r2.code === 0;
|
|
21733
|
+
}
|
|
21710
21734
|
const base = ["-m", "pip", "install", "--quiet", ...extraArgs, ...pkgs];
|
|
21711
21735
|
const r = await runner.run(py, base, { timeoutMs });
|
|
21712
21736
|
if (r.code === 0) return true;
|
|
@@ -22046,7 +22070,7 @@ async function autoUpgradeBeforeCriticalCommand() {
|
|
|
22046
22070
|
if (process.env.NODE_ENV === "test") return;
|
|
22047
22071
|
if (process.env.CODEAM_DISABLE_UPDATE_CHECK === "1") return;
|
|
22048
22072
|
if (process.env.CI) return;
|
|
22049
|
-
const current2 = true ? "2.
|
|
22073
|
+
const current2 = true ? "2.66.0" : null;
|
|
22050
22074
|
if (!current2) return;
|
|
22051
22075
|
const cache = readCache();
|
|
22052
22076
|
const fresh = cache && Date.now() - cache.fetchedAt < TTL_MS;
|
|
@@ -22063,7 +22087,7 @@ function checkForUpdates() {
|
|
|
22063
22087
|
if (process.env.CODEAM_DISABLE_UPDATE_CHECK === "1") return;
|
|
22064
22088
|
if (process.env.CI) return;
|
|
22065
22089
|
if (!process.stdout.isTTY) return;
|
|
22066
|
-
const current2 = true ? "2.
|
|
22090
|
+
const current2 = true ? "2.66.0" : null;
|
|
22067
22091
|
if (!current2) return;
|
|
22068
22092
|
const cache = readCache();
|
|
22069
22093
|
const fresh = cache && Date.now() - cache.fetchedAt < TTL_MS;
|
|
@@ -22083,7 +22107,7 @@ var SELF_UPDATE_INTERVAL_MS = 60 * 60 * 1e3;
|
|
|
22083
22107
|
var SELF_UPDATE_VIEW_TIMEOUT_MS = 3e4;
|
|
22084
22108
|
var SELF_UPDATE_INSTALL_TIMEOUT_MS = 18e4;
|
|
22085
22109
|
function currentCliVersion() {
|
|
22086
|
-
return true ? "2.
|
|
22110
|
+
return true ? "2.66.0" : null;
|
|
22087
22111
|
}
|
|
22088
22112
|
function runCmd(cmd, args2, timeoutMs) {
|
|
22089
22113
|
return new Promise((resolve10) => {
|
|
@@ -22361,6 +22385,11 @@ function fleetUserIdFromContainerName(containerName) {
|
|
|
22361
22385
|
function isMissingContainerError(stderr) {
|
|
22362
22386
|
return /no such container/i.test(stderr);
|
|
22363
22387
|
}
|
|
22388
|
+
function isFleetPruneHostPayload(v) {
|
|
22389
|
+
if (typeof v !== "object" || v === null) return false;
|
|
22390
|
+
const p2 = v;
|
|
22391
|
+
return typeof p2.images === "boolean" && typeof p2.buildCache === "boolean";
|
|
22392
|
+
}
|
|
22364
22393
|
function isMissingVolumeError(stderr) {
|
|
22365
22394
|
return /no such volume/i.test(stderr);
|
|
22366
22395
|
}
|
|
@@ -22776,6 +22805,14 @@ var HostAgentSupervisor = class {
|
|
|
22776
22805
|
await this.fleetDeleteBox(cmd.payload);
|
|
22777
22806
|
return;
|
|
22778
22807
|
}
|
|
22808
|
+
if (cmd.type === "fleet_prune_host") {
|
|
22809
|
+
if (!isFleetPruneHostPayload(cmd.payload)) {
|
|
22810
|
+
log.warn("host-agent", `ignoring malformed fleet_prune_host id=${cmd.id}`);
|
|
22811
|
+
return;
|
|
22812
|
+
}
|
|
22813
|
+
await this.fleetPruneHost(cmd.payload);
|
|
22814
|
+
return;
|
|
22815
|
+
}
|
|
22779
22816
|
if (cmd.type === "self_hosted_deploy") {
|
|
22780
22817
|
if (!isDeployPayload(cmd.payload)) {
|
|
22781
22818
|
log.warn("host-agent", `ignoring malformed self_hosted_deploy id=${cmd.id}`);
|
|
@@ -22987,6 +23024,39 @@ var HostAgentSupervisor = class {
|
|
|
22987
23024
|
/** `fleet_stop_box` — sleep an idle box (`docker stop`). MUST be
|
|
22988
23025
|
* idempotent — the backend's reap sweeps may re-send a stop for a box
|
|
22989
23026
|
* the host already stopped/removed; "No such container" is success. */
|
|
23027
|
+
/**
|
|
23028
|
+
* `fleet_prune_host` — reclaim Docker residue. Best-effort and idempotent: a
|
|
23029
|
+
* prune with nothing to collect exits 0 with "Total reclaimed space: 0B".
|
|
23030
|
+
*
|
|
23031
|
+
* ⚠️ Only ever `image prune` (dangling) and `builder prune`. NEVER
|
|
23032
|
+
* `container prune` (a stopped fleet container is a SLEEPING BOX) and NEVER
|
|
23033
|
+
* `volume prune` (a box's volume is the user's workspace and outlives its
|
|
23034
|
+
* container by design). See `FleetPruneHostPayload`.
|
|
23035
|
+
*/
|
|
23036
|
+
async fleetPruneHost(payload) {
|
|
23037
|
+
log.info(
|
|
23038
|
+
"host-agent",
|
|
23039
|
+
`fleet_prune_host images=${payload.images} buildCache=${payload.buildCache}`
|
|
23040
|
+
);
|
|
23041
|
+
const steps = [];
|
|
23042
|
+
if (payload.images) steps.push({ label: "image", args: ["image", "prune", "-f"] });
|
|
23043
|
+
if (payload.buildCache) steps.push({ label: "builder", args: ["builder", "prune", "-f"] });
|
|
23044
|
+
for (const step of steps) {
|
|
23045
|
+
const res = await this.docker.run(step.args);
|
|
23046
|
+
if (res.code !== 0) {
|
|
23047
|
+
log.warn(
|
|
23048
|
+
"host-agent",
|
|
23049
|
+
`fleet_prune_host ${step.label} failed (code=${res.code}): ${res.stderr.trim().slice(-300)}`
|
|
23050
|
+
);
|
|
23051
|
+
continue;
|
|
23052
|
+
}
|
|
23053
|
+
const reclaimed = /Total reclaimed space:\s*(.+)/i.exec(res.stdout || "");
|
|
23054
|
+
log.info(
|
|
23055
|
+
"host-agent",
|
|
23056
|
+
`fleet_prune_host ${step.label} ok${reclaimed ? ` reclaimed=${reclaimed[1].trim()}` : ""}`
|
|
23057
|
+
);
|
|
23058
|
+
}
|
|
23059
|
+
}
|
|
22990
23060
|
async fleetStopBox(payload) {
|
|
22991
23061
|
log.info("host-agent", `fleet_stop_box id=${payload.boxId} name=${payload.containerName}`);
|
|
22992
23062
|
const res = await this.docker.run(["stop", payload.containerName]);
|
|
@@ -45564,7 +45634,7 @@ function checkChokidar() {
|
|
|
45564
45634
|
}
|
|
45565
45635
|
async function doctor(args2 = []) {
|
|
45566
45636
|
const json = args2.includes("--json");
|
|
45567
|
-
const cliVersion = true ? "2.
|
|
45637
|
+
const cliVersion = true ? "2.66.0" : "0.0.0-dev";
|
|
45568
45638
|
const apiBase2 = resolveApiBaseUrl();
|
|
45569
45639
|
const diagnosticId = (0, import_node_crypto13.randomUUID)();
|
|
45570
45640
|
log.info("doctor", `run id=${diagnosticId} cli=${cliVersion}`);
|
|
@@ -45955,7 +46025,7 @@ async function mcpRun(args2) {
|
|
|
45955
46025
|
// src/commands/version.ts
|
|
45956
46026
|
var import_picocolors15 = __toESM(require("picocolors"));
|
|
45957
46027
|
function version2() {
|
|
45958
|
-
const v = true ? "2.
|
|
46028
|
+
const v = true ? "2.66.0" : "unknown";
|
|
45959
46029
|
console.log(`${import_picocolors15.default.bold("codeam-cli")} ${import_picocolors15.default.cyan(v)}`);
|
|
45960
46030
|
}
|
|
45961
46031
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "codeam-cli",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.66.0",
|
|
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",
|