codeam-cli 2.76.11 → 2.76.12
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 +10 -0
- package/dist/index.js +182 -15
- 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.76.11] — 2026-10-01
|
|
8
|
+
|
|
9
|
+
### Added
|
|
10
|
+
|
|
11
|
+
- **cli:** Host-agent startup self-update with versioned side-by-side installs (#895)
|
|
12
|
+
|
|
13
|
+
### CI
|
|
14
|
+
|
|
15
|
+
- **release:** Refresh and verify the wrapper Codespaces prebuild on every release (#894)
|
|
16
|
+
|
|
7
17
|
## [2.76.10] — 2026-10-01
|
|
8
18
|
|
|
9
19
|
### Fixed
|
package/dist/index.js
CHANGED
|
@@ -10579,7 +10579,7 @@ function readAnonId() {
|
|
|
10579
10579
|
}
|
|
10580
10580
|
function superProperties() {
|
|
10581
10581
|
return {
|
|
10582
|
-
cliVersion: true ? "2.76.
|
|
10582
|
+
cliVersion: true ? "2.76.12" : "0.0.0-dev",
|
|
10583
10583
|
nodeVersion: process.version,
|
|
10584
10584
|
platform: process.platform,
|
|
10585
10585
|
arch: process.arch,
|
|
@@ -10824,7 +10824,7 @@ var http = __toESM(require("http"));
|
|
|
10824
10824
|
// package.json
|
|
10825
10825
|
var package_default = {
|
|
10826
10826
|
name: "codeam-cli",
|
|
10827
|
-
version: "2.76.
|
|
10827
|
+
version: "2.76.12",
|
|
10828
10828
|
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.",
|
|
10829
10829
|
type: "commonjs",
|
|
10830
10830
|
main: "dist/index.js",
|
|
@@ -12160,7 +12160,7 @@ var CommandRelayService = class _CommandRelayService {
|
|
|
12160
12160
|
// fresh + clear the "CLI update available" banner after a self-update
|
|
12161
12161
|
// (a codespace that reinstalls @latest reconnects via heartbeat, not
|
|
12162
12162
|
// pair/reconnect). Older backends ignore the extra field.
|
|
12163
|
-
..."2.76.
|
|
12163
|
+
..."2.76.12" ? { ideVersion: "2.76.12" } : {}
|
|
12164
12164
|
};
|
|
12165
12165
|
try {
|
|
12166
12166
|
const res = await _postJson(`${API_BASE2}/api/plugin/heartbeat`, body);
|
|
@@ -50172,7 +50172,7 @@ async function autoUpgradeBeforeCriticalCommand() {
|
|
|
50172
50172
|
if (process.env.NODE_ENV === "test") return;
|
|
50173
50173
|
if (process.env.CODEAM_DISABLE_UPDATE_CHECK === "1") return;
|
|
50174
50174
|
if (process.env.CI) return;
|
|
50175
|
-
const current2 = true ? "2.76.
|
|
50175
|
+
const current2 = true ? "2.76.12" : null;
|
|
50176
50176
|
if (!current2) return;
|
|
50177
50177
|
const cache = readCache();
|
|
50178
50178
|
const fresh = cache && Date.now() - cache.fetchedAt < TTL_MS;
|
|
@@ -50189,7 +50189,7 @@ function checkForUpdates() {
|
|
|
50189
50189
|
if (process.env.CODEAM_DISABLE_UPDATE_CHECK === "1") return;
|
|
50190
50190
|
if (process.env.CI) return;
|
|
50191
50191
|
if (!process.stdout.isTTY) return;
|
|
50192
|
-
const current2 = true ? "2.76.
|
|
50192
|
+
const current2 = true ? "2.76.12" : null;
|
|
50193
50193
|
if (!current2) return;
|
|
50194
50194
|
const cache = readCache();
|
|
50195
50195
|
const fresh = cache && Date.now() - cache.fetchedAt < TTL_MS;
|
|
@@ -50460,7 +50460,7 @@ var SELF_UPDATE_VIEW_TIMEOUT_MS = 3e4;
|
|
|
50460
50460
|
var SELF_UPDATE_INSTALL_TIMEOUT_MS = 18e4;
|
|
50461
50461
|
var SELF_UPDATE_LS_TIMEOUT_MS = 15e3;
|
|
50462
50462
|
function currentCliVersion() {
|
|
50463
|
-
return true ? "2.76.
|
|
50463
|
+
return true ? "2.76.12" : null;
|
|
50464
50464
|
}
|
|
50465
50465
|
async function installedVersion(deps) {
|
|
50466
50466
|
const ls = await deps.run(
|
|
@@ -50655,7 +50655,7 @@ function signalHostReady(send = process.send?.bind(process)) {
|
|
|
50655
50655
|
}
|
|
50656
50656
|
function defaultDeps() {
|
|
50657
50657
|
return {
|
|
50658
|
-
ownVersion: true ? "2.76.
|
|
50658
|
+
ownVersion: true ? "2.76.12" : null,
|
|
50659
50659
|
root: cliVersionsRoot(),
|
|
50660
50660
|
args: process.argv.slice(3),
|
|
50661
50661
|
execPath: process.execPath,
|
|
@@ -50832,6 +50832,55 @@ var defaultDisableService = () => {
|
|
|
50832
50832
|
}
|
|
50833
50833
|
};
|
|
50834
50834
|
|
|
50835
|
+
// src/commands/host/fleet-image-gc.ts
|
|
50836
|
+
var FLEET_ROLLBACK_TAG = "rollback";
|
|
50837
|
+
var FLEET_IMAGE_INSPECT_FORMAT = '{{.Id}}|{{.Created}}|{{join .RepoTags ","}}|{{join .RootFS.Layers ","}}';
|
|
50838
|
+
function repoOfImageRef(ref) {
|
|
50839
|
+
const colon = ref.lastIndexOf(":");
|
|
50840
|
+
return colon > ref.lastIndexOf("/") ? ref.slice(0, colon) : ref;
|
|
50841
|
+
}
|
|
50842
|
+
function parseFleetImageInspect(stdout) {
|
|
50843
|
+
const out2 = [];
|
|
50844
|
+
for (const line of stdout.split("\n")) {
|
|
50845
|
+
const parts = line.trim().split("|");
|
|
50846
|
+
if (parts.length !== 4 || !parts[0].startsWith("sha256:")) continue;
|
|
50847
|
+
const [id, created, tags, layers] = parts;
|
|
50848
|
+
out2.push({
|
|
50849
|
+
id,
|
|
50850
|
+
created,
|
|
50851
|
+
repoTags: tags ? tags.split(",").filter((t2) => t2 && t2 !== "<none>:<none>") : [],
|
|
50852
|
+
layers: layers ? layers.split(",") : []
|
|
50853
|
+
});
|
|
50854
|
+
}
|
|
50855
|
+
return out2;
|
|
50856
|
+
}
|
|
50857
|
+
function planFleetBoxImageGc(input) {
|
|
50858
|
+
const { images, referenced, currentId, currentRef } = input;
|
|
50859
|
+
const current2 = images.find((i) => i.id === currentId);
|
|
50860
|
+
if (!current2) return { removeTags: [] };
|
|
50861
|
+
const currentRepo = repoOfImageRef(currentRef);
|
|
50862
|
+
const boxRepos = /* @__PURE__ */ new Set([currentRepo, "codeam-box"]);
|
|
50863
|
+
const boxTagsOf = (img) => img.repoTags.filter((t2) => boxRepos.has(repoOfImageRef(t2)));
|
|
50864
|
+
const baseLayer = current2.layers[0];
|
|
50865
|
+
const isBox = (img) => boxTagsOf(img).length > 0 || img.repoTags.length === 0 && !!baseLayer && img.layers[0] === baseLayer;
|
|
50866
|
+
const previous = images.filter((i) => i.id !== currentId && isBox(i)).sort((a, b) => Date.parse(b.created) - Date.parse(a.created));
|
|
50867
|
+
const rollback = previous[0];
|
|
50868
|
+
const rollbackTag = `${currentRepo}:${FLEET_ROLLBACK_TAG}`;
|
|
50869
|
+
const tagAs = rollback && boxTagsOf(rollback).length === 0 ? rollbackTag : void 0;
|
|
50870
|
+
const removeTags = [];
|
|
50871
|
+
for (const img of previous.slice(1)) {
|
|
50872
|
+
if (referenced.has(img.id)) continue;
|
|
50873
|
+
for (const tag of boxTagsOf(img)) {
|
|
50874
|
+
if (tagAs && tag === rollbackTag) continue;
|
|
50875
|
+
removeTags.push(tag);
|
|
50876
|
+
}
|
|
50877
|
+
}
|
|
50878
|
+
return {
|
|
50879
|
+
rollback: rollback ? { id: rollback.id, ...tagAs ? { tagAs } : {} } : void 0,
|
|
50880
|
+
removeTags
|
|
50881
|
+
};
|
|
50882
|
+
}
|
|
50883
|
+
|
|
50835
50884
|
// src/commands/host-agent.ts
|
|
50836
50885
|
var HEARTBEAT_INTERVAL_MS = 2e4;
|
|
50837
50886
|
var RESUME_RETRY_BACKOFF_MS = [15e3, 3e4, 6e4, 12e4];
|
|
@@ -50996,6 +51045,7 @@ function buildFleetBoxRunArgs(p2) {
|
|
|
50996
51045
|
}
|
|
50997
51046
|
var DOCKER_RUN_TIMEOUT_MS = 12e4;
|
|
50998
51047
|
var DOCKER_RUN_WITH_PULL_TIMEOUT_MS = 6e5;
|
|
51048
|
+
var DOCKER_PRUNE_TIMEOUT_MS = 6e5;
|
|
50999
51049
|
var defaultDockerRunner = {
|
|
51000
51050
|
run(args2, opts = {}) {
|
|
51001
51051
|
return new Promise((resolve14) => {
|
|
@@ -51767,22 +51817,42 @@ var HostAgentSupervisor = class {
|
|
|
51767
51817
|
* `fleet_prune_host` — reclaim Docker residue. Best-effort and idempotent: a
|
|
51768
51818
|
* prune with nothing to collect exits 0 with "Total reclaimed space: 0B".
|
|
51769
51819
|
*
|
|
51770
|
-
* ⚠️ Only
|
|
51771
|
-
* `container prune` (a stopped fleet container is a SLEEPING BOX) and
|
|
51772
|
-
* `volume prune` (a box's volume is the user's workspace and outlives
|
|
51773
|
-
* container by design). See `FleetPruneHostPayload`.
|
|
51820
|
+
* ⚠️ Only superseded Box images, `image prune` (dangling) and `builder prune`.
|
|
51821
|
+
* NEVER `container prune` (a stopped fleet container is a SLEEPING BOX) and
|
|
51822
|
+
* NEVER `volume prune` (a box's volume is the user's workspace and outlives
|
|
51823
|
+
* its container by design). See `FleetPruneHostPayload`.
|
|
51824
|
+
*
|
|
51825
|
+
* ⚠️ Every step runs on the LONG docker bound. Under the 120 s one the dangling
|
|
51826
|
+
* prune never finished on fleet-1: the prod and dev host-agents share one
|
|
51827
|
+
* daemon and both get this command at 15:45 UTC; dev's prune was SIGTERM'd at
|
|
51828
|
+
* 120 s (code=143) and prod's was refused with "a prune operation is already
|
|
51829
|
+
* running" — every day, so 17 GB of dangling Box images stayed (2026-10-02).
|
|
51830
|
+
* That refusal now means the other agent is doing the work, not a failure.
|
|
51774
51831
|
*/
|
|
51775
51832
|
async fleetPruneHost(payload) {
|
|
51776
51833
|
log.info(
|
|
51777
51834
|
"host-agent",
|
|
51778
51835
|
`fleet_prune_host images=${payload.images} buildCache=${payload.buildCache}`
|
|
51779
51836
|
);
|
|
51837
|
+
if (payload.images) await this.fleetCollectSupersededBoxImages();
|
|
51780
51838
|
const steps = [];
|
|
51781
51839
|
if (payload.images) steps.push({ label: "image", args: ["image", "prune", "-f"] });
|
|
51782
|
-
if (payload.buildCache)
|
|
51840
|
+
if (payload.buildCache) {
|
|
51841
|
+
steps.push({
|
|
51842
|
+
label: "builder",
|
|
51843
|
+
args: ["builder", "prune", "-f", "--all", "--filter", "until=72h"]
|
|
51844
|
+
});
|
|
51845
|
+
}
|
|
51783
51846
|
for (const step of steps) {
|
|
51784
|
-
const res = await this.docker.run(step.args);
|
|
51847
|
+
const res = await this.docker.run(step.args, { timeoutMs: DOCKER_PRUNE_TIMEOUT_MS });
|
|
51785
51848
|
if (res.code !== 0) {
|
|
51849
|
+
if (/prune operation is already running/i.test(res.stderr)) {
|
|
51850
|
+
log.info(
|
|
51851
|
+
"host-agent",
|
|
51852
|
+
`fleet_prune_host ${step.label} skipped: another prune is running on this daemon`
|
|
51853
|
+
);
|
|
51854
|
+
continue;
|
|
51855
|
+
}
|
|
51786
51856
|
log.warn(
|
|
51787
51857
|
"host-agent",
|
|
51788
51858
|
`fleet_prune_host ${step.label} failed (code=${res.code}): ${res.stderr.trim().slice(-300)}`
|
|
@@ -51796,6 +51866,103 @@ var HostAgentSupervisor = class {
|
|
|
51796
51866
|
);
|
|
51797
51867
|
}
|
|
51798
51868
|
}
|
|
51869
|
+
/**
|
|
51870
|
+
* Remove superseded Box images by tag: everything but the current `:latest`,
|
|
51871
|
+
* one previous release (kept, tagged `:rollback`) and any image a container
|
|
51872
|
+
* uses. Fail-SAFE: if the current image, the image list or the container
|
|
51873
|
+
* references cannot be read, it removes nothing.
|
|
51874
|
+
*/
|
|
51875
|
+
async fleetCollectSupersededBoxImages() {
|
|
51876
|
+
if (process.env.CODEAM_FLEET_BOX_IMAGE) return;
|
|
51877
|
+
const currentRef = resolveFleetBoxImage();
|
|
51878
|
+
const opts = { timeoutMs: DOCKER_PRUNE_TIMEOUT_MS };
|
|
51879
|
+
const lines = (out2) => [...new Set(out2.split("\n").map((l) => l.trim()).filter(Boolean))];
|
|
51880
|
+
const cur = await this.docker.run(
|
|
51881
|
+
["image", "inspect", "--format", "{{.Id}}", currentRef],
|
|
51882
|
+
opts
|
|
51883
|
+
);
|
|
51884
|
+
const currentId = cur.stdout.trim();
|
|
51885
|
+
if (cur.code !== 0 || !currentId) {
|
|
51886
|
+
log.warn("host-agent", `fleet_prune_host box-images skipped: ${currentRef} not present`);
|
|
51887
|
+
return;
|
|
51888
|
+
}
|
|
51889
|
+
const ids = await this.docker.run(["image", "ls", "-aq", "--no-trunc"], opts);
|
|
51890
|
+
if (ids.code !== 0) {
|
|
51891
|
+
log.warn(
|
|
51892
|
+
"host-agent",
|
|
51893
|
+
`fleet_prune_host box-images skipped: image ls failed (code=${ids.code})`
|
|
51894
|
+
);
|
|
51895
|
+
return;
|
|
51896
|
+
}
|
|
51897
|
+
const imageIds = lines(ids.stdout);
|
|
51898
|
+
if (imageIds.length === 0) return;
|
|
51899
|
+
const inspect = await this.docker.run(
|
|
51900
|
+
["image", "inspect", "--format", FLEET_IMAGE_INSPECT_FORMAT, ...imageIds],
|
|
51901
|
+
opts
|
|
51902
|
+
);
|
|
51903
|
+
if (inspect.code !== 0) {
|
|
51904
|
+
log.warn(
|
|
51905
|
+
"host-agent",
|
|
51906
|
+
`fleet_prune_host box-images skipped: image inspect failed (code=${inspect.code})`
|
|
51907
|
+
);
|
|
51908
|
+
return;
|
|
51909
|
+
}
|
|
51910
|
+
const containers = await this.docker.run(["ps", "-aq", "--no-trunc"], opts);
|
|
51911
|
+
if (containers.code !== 0) {
|
|
51912
|
+
log.warn(
|
|
51913
|
+
"host-agent",
|
|
51914
|
+
`fleet_prune_host box-images skipped: ps failed (code=${containers.code})`
|
|
51915
|
+
);
|
|
51916
|
+
return;
|
|
51917
|
+
}
|
|
51918
|
+
const containerIds = lines(containers.stdout);
|
|
51919
|
+
const referenced = /* @__PURE__ */ new Set();
|
|
51920
|
+
if (containerIds.length > 0) {
|
|
51921
|
+
const refs = await this.docker.run(
|
|
51922
|
+
["inspect", "--format", "{{.Image}}", ...containerIds],
|
|
51923
|
+
opts
|
|
51924
|
+
);
|
|
51925
|
+
if (refs.code !== 0) {
|
|
51926
|
+
log.warn(
|
|
51927
|
+
"host-agent",
|
|
51928
|
+
`fleet_prune_host box-images skipped: container inspect failed (code=${refs.code})`
|
|
51929
|
+
);
|
|
51930
|
+
return;
|
|
51931
|
+
}
|
|
51932
|
+
for (const id of lines(refs.stdout)) referenced.add(id);
|
|
51933
|
+
}
|
|
51934
|
+
const plan = planFleetBoxImageGc({
|
|
51935
|
+
images: parseFleetImageInspect(inspect.stdout),
|
|
51936
|
+
referenced,
|
|
51937
|
+
currentId,
|
|
51938
|
+
currentRef
|
|
51939
|
+
});
|
|
51940
|
+
if (plan.rollback?.tagAs) {
|
|
51941
|
+
const tag = await this.docker.run(["tag", plan.rollback.id, plan.rollback.tagAs], opts);
|
|
51942
|
+
if (tag.code !== 0) {
|
|
51943
|
+
log.warn(
|
|
51944
|
+
"host-agent",
|
|
51945
|
+
`fleet_prune_host box-images: tagging rollback failed (code=${tag.code}), removing nothing`
|
|
51946
|
+
);
|
|
51947
|
+
return;
|
|
51948
|
+
}
|
|
51949
|
+
}
|
|
51950
|
+
for (const ref of plan.removeTags) {
|
|
51951
|
+
const res = await this.docker.run(["rmi", ref], opts);
|
|
51952
|
+
if (res.code === 0 || /no such image/i.test(res.stderr)) {
|
|
51953
|
+
log.info("host-agent", `fleet_prune_host box-images removed ${ref}`);
|
|
51954
|
+
} else {
|
|
51955
|
+
log.warn(
|
|
51956
|
+
"host-agent",
|
|
51957
|
+
`fleet_prune_host box-images rmi ${ref} failed (code=${res.code}): ${res.stderr.trim().slice(-300)}`
|
|
51958
|
+
);
|
|
51959
|
+
}
|
|
51960
|
+
}
|
|
51961
|
+
log.info(
|
|
51962
|
+
"host-agent",
|
|
51963
|
+
`fleet_prune_host box-images current=${currentId.slice(7, 19)} rollback=${plan.rollback ? plan.rollback.id.slice(7, 19) : "none"} removed=${plan.removeTags.length}`
|
|
51964
|
+
);
|
|
51965
|
+
}
|
|
51799
51966
|
async fleetStopBox(payload) {
|
|
51800
51967
|
log.info("host-agent", `fleet_stop_box id=${payload.boxId} name=${payload.containerName}`);
|
|
51801
51968
|
const res = await this.docker.run(["stop", payload.containerName]);
|
|
@@ -52806,7 +52973,7 @@ function checkChokidar() {
|
|
|
52806
52973
|
}
|
|
52807
52974
|
async function doctor(args2 = []) {
|
|
52808
52975
|
const json = args2.includes("--json");
|
|
52809
|
-
const cliVersion = true ? "2.76.
|
|
52976
|
+
const cliVersion = true ? "2.76.12" : "0.0.0-dev";
|
|
52810
52977
|
const apiBase2 = resolveApiBaseUrl();
|
|
52811
52978
|
const diagnosticId = (0, import_node_crypto17.randomUUID)();
|
|
52812
52979
|
log.info("doctor", `run id=${diagnosticId} cli=${cliVersion}`);
|
|
@@ -53330,7 +53497,7 @@ async function mcpWarm(argv = [], opts = {}) {
|
|
|
53330
53497
|
// src/commands/version.ts
|
|
53331
53498
|
var import_picocolors15 = __toESM(require("picocolors"));
|
|
53332
53499
|
function version2() {
|
|
53333
|
-
const v = true ? "2.76.
|
|
53500
|
+
const v = true ? "2.76.12" : "unknown";
|
|
53334
53501
|
console.log(`${import_picocolors15.default.bold("codeam-cli")} ${import_picocolors15.default.cyan(v)}`);
|
|
53335
53502
|
}
|
|
53336
53503
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "codeam-cli",
|
|
3
|
-
"version": "2.76.
|
|
3
|
+
"version": "2.76.12",
|
|
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",
|