codeam-cli 2.51.2 → 2.52.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 +145 -14
- 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.51.3] — 2026-06-29
|
|
8
|
+
|
|
9
|
+
### Fixed
|
|
10
|
+
|
|
11
|
+
- **cli:** Stop retrying on terminal enroll-token 4xx (ENROLL_TOKEN_EXPIRED / ENROLL_TOKEN_INVALID)
|
|
12
|
+
|
|
13
|
+
## [2.51.2] — 2026-06-29
|
|
14
|
+
|
|
15
|
+
### Fixed
|
|
16
|
+
|
|
17
|
+
- **headroom:** Route budget relaunch through supervisor when headroom install is active
|
|
18
|
+
|
|
7
19
|
## [2.51.1] — 2026-06-29
|
|
8
20
|
|
|
9
21
|
### 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.
|
|
5400
|
+
cliVersion: true ? "2.52.0" : "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.
|
|
5581
|
+
version: "2.52.0",
|
|
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",
|
|
@@ -5893,6 +5893,30 @@ async function postBeadsEvent(input) {
|
|
|
5893
5893
|
};
|
|
5894
5894
|
}
|
|
5895
5895
|
}
|
|
5896
|
+
async function postCliUpdateEvent(input) {
|
|
5897
|
+
try {
|
|
5898
|
+
const payload = { phase: input.phase };
|
|
5899
|
+
if (input.error) payload.error = input.error;
|
|
5900
|
+
await _transport.postJsonAuthed(
|
|
5901
|
+
`${API_BASE}/api/agents/cli-update/events`,
|
|
5902
|
+
{
|
|
5903
|
+
sessionId: input.sessionId,
|
|
5904
|
+
pluginId: input.pluginId,
|
|
5905
|
+
phase: input.phase,
|
|
5906
|
+
...input.error ? { error: input.error } : {}
|
|
5907
|
+
},
|
|
5908
|
+
input.pluginAuthToken
|
|
5909
|
+
);
|
|
5910
|
+
return { ok: true };
|
|
5911
|
+
} catch (err) {
|
|
5912
|
+
const e = err;
|
|
5913
|
+
return {
|
|
5914
|
+
ok: false,
|
|
5915
|
+
status: typeof e.statusCode === "number" ? e.statusCode : 0,
|
|
5916
|
+
message: e.message || "unknown"
|
|
5917
|
+
};
|
|
5918
|
+
}
|
|
5919
|
+
}
|
|
5896
5920
|
async function postBeadsProvisioning(input) {
|
|
5897
5921
|
try {
|
|
5898
5922
|
await _transport.postJsonAuthed(
|
|
@@ -13984,13 +14008,16 @@ function saveHostIdentity(identity) {
|
|
|
13984
14008
|
});
|
|
13985
14009
|
fs27.chmodSync(file, 384);
|
|
13986
14010
|
}
|
|
14011
|
+
var TERMINAL_ENROLL_CODES = /* @__PURE__ */ new Set(["ENROLL_TOKEN_EXPIRED", "ENROLL_TOKEN_INVALID"]);
|
|
13987
14012
|
var HostHttpError = class extends Error {
|
|
13988
|
-
constructor(message, status2) {
|
|
14013
|
+
constructor(message, status2, code) {
|
|
13989
14014
|
super(message);
|
|
13990
14015
|
this.status = status2;
|
|
14016
|
+
this.code = code;
|
|
13991
14017
|
this.name = "HostHttpError";
|
|
13992
14018
|
}
|
|
13993
14019
|
status;
|
|
14020
|
+
code;
|
|
13994
14021
|
/**
|
|
13995
14022
|
* True iff this is a genuine auth-rejection of the host identity:
|
|
13996
14023
|
* 401 (token invalid), 403 (forbidden), 404 (host not found / deleted).
|
|
@@ -13999,10 +14026,25 @@ var HostHttpError = class extends Error {
|
|
|
13999
14026
|
get isAuthRejection() {
|
|
14000
14027
|
return this.status === 401 || this.status === 403 || this.status === 404;
|
|
14001
14028
|
}
|
|
14029
|
+
/**
|
|
14030
|
+
* True iff the enroll token is permanently unusable: the backend returned
|
|
14031
|
+
* a terminal 4xx (410 Gone for expired/replayed, 400 Bad Request for
|
|
14032
|
+
* malformed/invalid) with a stable `ENROLL_TOKEN_*` error code.
|
|
14033
|
+
*
|
|
14034
|
+
* These failures must NOT be retried — the token will never become valid
|
|
14035
|
+
* again. The host-agent must surface a clear message and exit so the user
|
|
14036
|
+
* knows to generate a new token in the app.
|
|
14037
|
+
*/
|
|
14038
|
+
get isTerminalEnrollError() {
|
|
14039
|
+
return typeof this.code === "string" && TERMINAL_ENROLL_CODES.has(this.code);
|
|
14040
|
+
}
|
|
14002
14041
|
};
|
|
14003
14042
|
function isHostAuthRejection(err) {
|
|
14004
14043
|
return err instanceof HostHttpError && err.isAuthRejection;
|
|
14005
14044
|
}
|
|
14045
|
+
function isTerminalEnrollError(err) {
|
|
14046
|
+
return err instanceof HostHttpError && err.isTerminalEnrollError;
|
|
14047
|
+
}
|
|
14006
14048
|
function deleteHostIdentity() {
|
|
14007
14049
|
try {
|
|
14008
14050
|
fs27.rmSync(hostIdentityPath(), { force: true });
|
|
@@ -14017,10 +14059,11 @@ async function postJson(pathname, body) {
|
|
|
14017
14059
|
});
|
|
14018
14060
|
const json = await res.json();
|
|
14019
14061
|
if (!res.ok || !json.success) {
|
|
14020
|
-
const
|
|
14062
|
+
const errBody = !json.success ? json.error : void 0;
|
|
14021
14063
|
throw new HostHttpError(
|
|
14022
|
-
`${pathname} failed (${
|
|
14023
|
-
res.status
|
|
14064
|
+
`${pathname} failed (${errBody?.code ?? `HTTP_${res.status}`}): ${errBody?.message ?? res.statusText}`,
|
|
14065
|
+
res.status,
|
|
14066
|
+
errBody?.code
|
|
14024
14067
|
);
|
|
14025
14068
|
}
|
|
14026
14069
|
return json.data;
|
|
@@ -14810,7 +14853,7 @@ async function autoUpgradeBeforeCriticalCommand() {
|
|
|
14810
14853
|
if (process.env.NODE_ENV === "test") return;
|
|
14811
14854
|
if (process.env.CODEAM_DISABLE_UPDATE_CHECK === "1") return;
|
|
14812
14855
|
if (process.env.CI) return;
|
|
14813
|
-
const current = true ? "2.
|
|
14856
|
+
const current = true ? "2.52.0" : null;
|
|
14814
14857
|
if (!current) return;
|
|
14815
14858
|
const cache = readCache();
|
|
14816
14859
|
const fresh = cache && Date.now() - cache.fetchedAt < TTL_MS;
|
|
@@ -14827,7 +14870,7 @@ function checkForUpdates() {
|
|
|
14827
14870
|
if (process.env.CODEAM_DISABLE_UPDATE_CHECK === "1") return;
|
|
14828
14871
|
if (process.env.CI) return;
|
|
14829
14872
|
if (!process.stdout.isTTY) return;
|
|
14830
|
-
const current = true ? "2.
|
|
14873
|
+
const current = true ? "2.52.0" : null;
|
|
14831
14874
|
if (!current) return;
|
|
14832
14875
|
const cache = readCache();
|
|
14833
14876
|
const fresh = cache && Date.now() - cache.fetchedAt < TTL_MS;
|
|
@@ -15529,7 +15572,7 @@ var defaultSpawner = (env, cwd, args2 = []) => (0, import_node_child_process15.s
|
|
|
15529
15572
|
detached: false
|
|
15530
15573
|
});
|
|
15531
15574
|
function currentCliVersion() {
|
|
15532
|
-
return true ? "2.
|
|
15575
|
+
return true ? "2.52.0" : null;
|
|
15533
15576
|
}
|
|
15534
15577
|
function runCmd(cmd, args2, timeoutMs) {
|
|
15535
15578
|
return new Promise((resolve7) => {
|
|
@@ -16193,6 +16236,12 @@ async function resolveHostIdentity(enrollToken) {
|
|
|
16193
16236
|
);
|
|
16194
16237
|
return identity;
|
|
16195
16238
|
} catch (err) {
|
|
16239
|
+
if (isTerminalEnrollError(err)) {
|
|
16240
|
+
throw new Error(
|
|
16241
|
+
"Enrollment token expired or invalid \u2014 generate a new one in the app (Settings \u203A Servers \u203A Add server). Tokens expire after 15 minutes and are single-use.",
|
|
16242
|
+
{ cause: err }
|
|
16243
|
+
);
|
|
16244
|
+
}
|
|
16196
16245
|
if (existing) {
|
|
16197
16246
|
log.trace(
|
|
16198
16247
|
"host-agent",
|
|
@@ -18921,6 +18970,87 @@ var beadsConfigureH = async (ctx, cmd, parsed) => {
|
|
|
18921
18970
|
const result = await configureBeads(action, { agent: rawAgentId, cwd: process.cwd(), pluginAuthToken: ctx.pluginAuthToken }, deps);
|
|
18922
18971
|
await ctx.relay.sendResult(cmd.id, "completed", result);
|
|
18923
18972
|
};
|
|
18973
|
+
var defaultCliUpdateDeps = {
|
|
18974
|
+
install: () => runNpmInstallLatest(),
|
|
18975
|
+
// "Don't self-relaunch" gate — leave this process running and let the
|
|
18976
|
+
// on-disk update apply on the next natural restart, instead of killing it:
|
|
18977
|
+
// - self-hosted (CODEAM_AUTO_APPROVE=1): the HostAgentSupervisor does NOT
|
|
18978
|
+
// auto-restart children on exit, so exiting would orphan the session.
|
|
18979
|
+
// - codespace (CODESPACES=true): the pair-auto daemon owns a daemon lock +
|
|
18980
|
+
// the relay connection; a naive detached re-exec does NOT replicate the
|
|
18981
|
+
// careful setsid/lock-aware restart the install flow uses, so a failed
|
|
18982
|
+
// respawn would leave the mobile session permanently OFFLINE. Codespaces
|
|
18983
|
+
// sleep/resume often, so the update lands on next wake — safe + non-fatal.
|
|
18984
|
+
// Only a truly local `codeam start` (neither marker) re-execs in place below.
|
|
18985
|
+
isSupervised: () => process.env["CODEAM_AUTO_APPROVE"] === "1" || process.env["CODESPACES"] === "true",
|
|
18986
|
+
relaunch: (args2) => {
|
|
18987
|
+
const entry = process.argv[1];
|
|
18988
|
+
const child = (0, import_child_process21.spawn)(process.execPath, [entry, ...args2], {
|
|
18989
|
+
detached: true,
|
|
18990
|
+
stdio: "inherit"
|
|
18991
|
+
});
|
|
18992
|
+
child.unref();
|
|
18993
|
+
process.exit(0);
|
|
18994
|
+
}
|
|
18995
|
+
};
|
|
18996
|
+
var CLI_UPDATE_INSTALL_TIMEOUT_MS = 18e4;
|
|
18997
|
+
var CLI_UPDATE_MAX_ATTEMPTS = 3;
|
|
18998
|
+
async function runNpmInstallLatest() {
|
|
18999
|
+
let lastError = "";
|
|
19000
|
+
for (let attempt = 1; attempt <= CLI_UPDATE_MAX_ATTEMPTS; attempt++) {
|
|
19001
|
+
const result = await new Promise((resolve7) => {
|
|
19002
|
+
(0, import_child_process21.execFile)(
|
|
19003
|
+
"npm",
|
|
19004
|
+
["install", "-g", "codeam-cli@latest"],
|
|
19005
|
+
{ timeout: CLI_UPDATE_INSTALL_TIMEOUT_MS },
|
|
19006
|
+
(err, _stdout, stderr) => {
|
|
19007
|
+
if (!err) {
|
|
19008
|
+
resolve7({ ok: true });
|
|
19009
|
+
} else {
|
|
19010
|
+
resolve7({ ok: false, error: (stderr || err.message).slice(0, 300) });
|
|
19011
|
+
}
|
|
19012
|
+
}
|
|
19013
|
+
);
|
|
19014
|
+
});
|
|
19015
|
+
if (result.ok) return { ok: true };
|
|
19016
|
+
lastError = result.error ?? "unknown";
|
|
19017
|
+
if (attempt < CLI_UPDATE_MAX_ATTEMPTS) {
|
|
19018
|
+
await new Promise((r) => setTimeout(r, 1e3 * attempt));
|
|
19019
|
+
}
|
|
19020
|
+
}
|
|
19021
|
+
return { ok: false, error: lastError };
|
|
19022
|
+
}
|
|
19023
|
+
var cliSelfUpdateH = (deps = defaultCliUpdateDeps) => async (ctx, cmd) => {
|
|
19024
|
+
const report = async (phase, error) => {
|
|
19025
|
+
if (!ctx.pluginAuthToken) return;
|
|
19026
|
+
try {
|
|
19027
|
+
await postCliUpdateEvent({
|
|
19028
|
+
sessionId: ctx.sessionId,
|
|
19029
|
+
pluginId: ctx.pluginId,
|
|
19030
|
+
pluginAuthToken: ctx.pluginAuthToken,
|
|
19031
|
+
phase,
|
|
19032
|
+
error
|
|
19033
|
+
});
|
|
19034
|
+
} catch {
|
|
19035
|
+
}
|
|
19036
|
+
};
|
|
19037
|
+
await report("updating");
|
|
19038
|
+
const result = await deps.install();
|
|
19039
|
+
if (!result.ok) {
|
|
19040
|
+
await report("failed", result.error?.slice(0, 200));
|
|
19041
|
+
await ctx.relay.sendResult(cmd.id, "completed", { updated: false });
|
|
19042
|
+
return;
|
|
19043
|
+
}
|
|
19044
|
+
await report("relaunching");
|
|
19045
|
+
await ctx.relay.sendResult(cmd.id, "completed", { updated: true });
|
|
19046
|
+
if (deps.isSupervised()) {
|
|
19047
|
+
log.info("cli-update", "supervised context \u2014 update installed, applies on next restart");
|
|
19048
|
+
return;
|
|
19049
|
+
}
|
|
19050
|
+
const origArgs = process.argv.slice(2);
|
|
19051
|
+
log.info("cli-update", `re-execing ${process.argv[1]} with args: ${origArgs.join(" ")}`);
|
|
19052
|
+
deps.relaunch(origArgs);
|
|
19053
|
+
};
|
|
18924
19054
|
var terminalOpenH = async (ctx, cmd, parsed) => {
|
|
18925
19055
|
const r = openTerminal({
|
|
18926
19056
|
cols: typeof parsed.cols === "number" ? parsed.cols : void 0,
|
|
@@ -19908,7 +20038,8 @@ var handlers = {
|
|
|
19908
20038
|
env_write: envWriteH,
|
|
19909
20039
|
headroom_configure: headroomConfigureH,
|
|
19910
20040
|
headroom_budget: headroomBudgetH,
|
|
19911
|
-
beads_configure: beadsConfigureH
|
|
20041
|
+
beads_configure: beadsConfigureH,
|
|
20042
|
+
cli_self_update: cliSelfUpdateH()
|
|
19912
20043
|
};
|
|
19913
20044
|
async function dispatchCommand(ctx, cmd) {
|
|
19914
20045
|
if (cmd.type === "beads_action") {
|
|
@@ -30777,9 +30908,9 @@ async function probeCodeamPair(provider, workspace) {
|
|
|
30777
30908
|
}
|
|
30778
30909
|
async function stopWorkspaceFromLocal(target) {
|
|
30779
30910
|
if (target.provider.id === "github-codespaces") {
|
|
30780
|
-
const { execFile:
|
|
30911
|
+
const { execFile: execFile14 } = await import("child_process");
|
|
30781
30912
|
const { promisify: promisify11 } = await import("util");
|
|
30782
|
-
const execFileP10 = promisify11(
|
|
30913
|
+
const execFileP10 = promisify11(execFile14);
|
|
30783
30914
|
await execFileP10("gh", ["codespace", "stop", "-c", target.id], { maxBuffer: 8 * 1024 * 1024 });
|
|
30784
30915
|
return;
|
|
30785
30916
|
}
|
|
@@ -31061,7 +31192,7 @@ function checkChokidar() {
|
|
|
31061
31192
|
}
|
|
31062
31193
|
async function doctor(args2 = []) {
|
|
31063
31194
|
const json = args2.includes("--json");
|
|
31064
|
-
const cliVersion = true ? "2.
|
|
31195
|
+
const cliVersion = true ? "2.52.0" : "0.0.0-dev";
|
|
31065
31196
|
const apiBase2 = resolveApiBaseUrl();
|
|
31066
31197
|
const diagnosticId = (0, import_node_crypto8.randomUUID)();
|
|
31067
31198
|
log.info("doctor", `run id=${diagnosticId} cli=${cliVersion}`);
|
|
@@ -31260,7 +31391,7 @@ async function completion(args2) {
|
|
|
31260
31391
|
// src/commands/version.ts
|
|
31261
31392
|
var import_picocolors15 = __toESM(require("picocolors"));
|
|
31262
31393
|
function version2() {
|
|
31263
|
-
const v = true ? "2.
|
|
31394
|
+
const v = true ? "2.52.0" : "unknown";
|
|
31264
31395
|
console.log(`${import_picocolors15.default.bold("codeam-cli")} ${import_picocolors15.default.cyan(v)}`);
|
|
31265
31396
|
}
|
|
31266
31397
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "codeam-cli",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.52.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",
|