codeam-cli 2.75.41 → 2.75.43
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 +133 -16
- 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.75.42] — 2026-09-27
|
|
8
|
+
|
|
9
|
+
### Fixed
|
|
10
|
+
|
|
11
|
+
- **cli:** A CodeAgent Box reports its label, not its container id (#849)
|
|
12
|
+
|
|
13
|
+
## [2.75.41] — 2026-09-27
|
|
14
|
+
|
|
15
|
+
### Fixed
|
|
16
|
+
|
|
17
|
+
- **cli:** Prune debug logs of dead processes (7 days / 200 MB) (#845)
|
|
18
|
+
|
|
7
19
|
## [2.75.40] — 2026-09-27
|
|
8
20
|
|
|
9
21
|
### Fixed
|
package/dist/index.js
CHANGED
|
@@ -3437,6 +3437,18 @@ function resolveLogDir() {
|
|
|
3437
3437
|
var LOG_DIR = resolveLogDir();
|
|
3438
3438
|
var debugFilePath = path.join(LOG_DIR, `debug-${process.pid}.log`);
|
|
3439
3439
|
var fileInitialized = false;
|
|
3440
|
+
var consoleBroken = false;
|
|
3441
|
+
process.stderr.on("error", () => {
|
|
3442
|
+
consoleBroken = true;
|
|
3443
|
+
});
|
|
3444
|
+
function writeConsole(line) {
|
|
3445
|
+
if (consoleBroken) return;
|
|
3446
|
+
try {
|
|
3447
|
+
process.stderr.write(line);
|
|
3448
|
+
} catch {
|
|
3449
|
+
consoleBroken = true;
|
|
3450
|
+
}
|
|
3451
|
+
}
|
|
3440
3452
|
var LOG_RETENTION_MS = 7 * 24 * 60 * 60 * 1e3;
|
|
3441
3453
|
var LOG_DIR_BUDGET_BYTES = 200 * 1024 * 1024;
|
|
3442
3454
|
function pidAlive(pid) {
|
|
@@ -3556,7 +3568,7 @@ function emit(level, tag, msg, err) {
|
|
|
3556
3568
|
appendToFile(jsonMode ? json : `${(/* @__PURE__ */ new Date()).toISOString()} ${text}`);
|
|
3557
3569
|
}
|
|
3558
3570
|
if (LEVELS[level] <= currentLevel()) {
|
|
3559
|
-
|
|
3571
|
+
writeConsole(jsonMode ? json : text);
|
|
3560
3572
|
}
|
|
3561
3573
|
}
|
|
3562
3574
|
var log = {
|
|
@@ -10565,7 +10577,7 @@ function readAnonId() {
|
|
|
10565
10577
|
}
|
|
10566
10578
|
function superProperties() {
|
|
10567
10579
|
return {
|
|
10568
|
-
cliVersion: true ? "2.75.
|
|
10580
|
+
cliVersion: true ? "2.75.43" : "0.0.0-dev",
|
|
10569
10581
|
nodeVersion: process.version,
|
|
10570
10582
|
platform: process.platform,
|
|
10571
10583
|
arch: process.arch,
|
|
@@ -10740,7 +10752,10 @@ var path4 = __toESM(require("path"));
|
|
|
10740
10752
|
var import_child_process2 = require("child_process");
|
|
10741
10753
|
var WRAPPER_REPO_NAME = "codeam-codespace";
|
|
10742
10754
|
function resolveSessionHostname(cwd = process.cwd()) {
|
|
10743
|
-
if (process.env.CODESPACES !== "true")
|
|
10755
|
+
if (process.env.CODESPACES !== "true") {
|
|
10756
|
+
const label = process.env.CODEAM_HOST_LABEL?.trim();
|
|
10757
|
+
return label ? label.slice(0, 80) : _osSeam.hostname();
|
|
10758
|
+
}
|
|
10744
10759
|
return codespaceSessionLabel(cwd) ?? _osSeam.hostname();
|
|
10745
10760
|
}
|
|
10746
10761
|
var _osSeam = {
|
|
@@ -10807,7 +10822,7 @@ var http = __toESM(require("http"));
|
|
|
10807
10822
|
// package.json
|
|
10808
10823
|
var package_default = {
|
|
10809
10824
|
name: "codeam-cli",
|
|
10810
|
-
version: "2.75.
|
|
10825
|
+
version: "2.75.43",
|
|
10811
10826
|
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.",
|
|
10812
10827
|
type: "commonjs",
|
|
10813
10828
|
main: "dist/index.js",
|
|
@@ -10980,7 +10995,11 @@ async function fetchCurrentPluginAuthToken(sessionId, pluginId, pollSecret) {
|
|
|
10980
10995
|
// after the CLI restarted on a new release and took the reconnect
|
|
10981
10996
|
// path (which previously never updated ideVersion). Backends older
|
|
10982
10997
|
// than the matching fix ignore the unknown field (backward-compat).
|
|
10983
|
-
ideVersion: package_default.version
|
|
10998
|
+
ideVersion: package_default.version,
|
|
10999
|
+
// Same for the machine label: a session paired while a CodeAgent Box
|
|
11000
|
+
// reported its container id picks up the box label on reconnect.
|
|
11001
|
+
// Older backends strip the unknown field (whitelist, not forbid).
|
|
11002
|
+
hostname: resolveSessionHostname()
|
|
10984
11003
|
},
|
|
10985
11004
|
// SEC: prove possession so the gated /reconnect returns the token.
|
|
10986
11005
|
// Omitted for legacy sessions (no secret) → backend legacy path.
|
|
@@ -12137,7 +12156,7 @@ var CommandRelayService = class _CommandRelayService {
|
|
|
12137
12156
|
// fresh + clear the "CLI update available" banner after a self-update
|
|
12138
12157
|
// (a codespace that reinstalls @latest reconnects via heartbeat, not
|
|
12139
12158
|
// pair/reconnect). Older backends ignore the extra field.
|
|
12140
|
-
..."2.75.
|
|
12159
|
+
..."2.75.43" ? { ideVersion: "2.75.43" } : {}
|
|
12141
12160
|
};
|
|
12142
12161
|
try {
|
|
12143
12162
|
const res = await _postJson(`${API_BASE2}/api/plugin/heartbeat`, body);
|
|
@@ -27699,13 +27718,13 @@ function resolveGlobalNodeModulesDir(opts) {
|
|
|
27699
27718
|
var STALE_STAGING_AGE_MS = CLI_UPDATE_INSTALL_TIMEOUT_MS;
|
|
27700
27719
|
function sweepStaleCliStagingDirs(nodeModulesDir, now = Date.now(), deps) {
|
|
27701
27720
|
if (!nodeModulesDir) return 0;
|
|
27702
|
-
const
|
|
27721
|
+
const readdirSync16 = deps?.readdirSync ?? fs51.readdirSync;
|
|
27703
27722
|
const statSync16 = deps?.statSync ?? fs51.statSync;
|
|
27704
27723
|
const rmSync10 = deps?.rmSync ?? fs51.rmSync;
|
|
27705
27724
|
let removed = 0;
|
|
27706
27725
|
let entries;
|
|
27707
27726
|
try {
|
|
27708
|
-
entries =
|
|
27727
|
+
entries = readdirSync16(nodeModulesDir);
|
|
27709
27728
|
} catch {
|
|
27710
27729
|
return 0;
|
|
27711
27730
|
}
|
|
@@ -28525,6 +28544,9 @@ function installRelayCrashGuards() {
|
|
|
28525
28544
|
process.on("uncaughtException", (err) => {
|
|
28526
28545
|
log.error("process", `uncaughtException \u2014 relay kept alive \u2014 ${describeReason(err)}`);
|
|
28527
28546
|
});
|
|
28547
|
+
process.stdout.on("error", (err) => {
|
|
28548
|
+
if (err.code !== "EPIPE") log.warn("process", `stdout error \u2014 ${describeReason(err)}`);
|
|
28549
|
+
});
|
|
28528
28550
|
}
|
|
28529
28551
|
function describeReason(reason) {
|
|
28530
28552
|
if (reason instanceof Error) {
|
|
@@ -28916,6 +28938,59 @@ function releaseOwnLock(lockPath) {
|
|
|
28916
28938
|
} catch {
|
|
28917
28939
|
}
|
|
28918
28940
|
}
|
|
28941
|
+
function procPpid(pid) {
|
|
28942
|
+
const stat3 = readProcFile(`${pid}/stat`);
|
|
28943
|
+
if (!stat3) return void 0;
|
|
28944
|
+
const ppid = Number(stat3.slice(stat3.lastIndexOf(")") + 2).trim().split(/\s+/)[1]);
|
|
28945
|
+
return Number.isInteger(ppid) ? ppid : void 0;
|
|
28946
|
+
}
|
|
28947
|
+
function orphanedSupervisorDaemon(sessionId) {
|
|
28948
|
+
const rec = readLockRecord(daemonLockPath(sessionId));
|
|
28949
|
+
if (!rec || !isLiveLockHolder(rec)) return void 0;
|
|
28950
|
+
if (process.pid === 1 || procPpid(rec.pid) !== 1) return void 0;
|
|
28951
|
+
const env = readProcFile(`${rec.pid}/environ`)?.split("\0") ?? [];
|
|
28952
|
+
const spawnedBySupervisor = env.some(
|
|
28953
|
+
(v) => v === `CODEAM_RESUME_SESSION_ID=${sessionId}` || v.startsWith("CODEAM_AUTO_TOKEN=")
|
|
28954
|
+
);
|
|
28955
|
+
return spawnedBySupervisor ? rec.pid : void 0;
|
|
28956
|
+
}
|
|
28957
|
+
function snapshotProcessTree(root) {
|
|
28958
|
+
const children = /* @__PURE__ */ new Map();
|
|
28959
|
+
let entries = [];
|
|
28960
|
+
try {
|
|
28961
|
+
entries = fs52.readdirSync(procRoot).filter((e) => /^\d+$/.test(e));
|
|
28962
|
+
} catch {
|
|
28963
|
+
}
|
|
28964
|
+
for (const e of entries) {
|
|
28965
|
+
const pid = Number(e);
|
|
28966
|
+
const ppid = procPpid(pid);
|
|
28967
|
+
if (ppid === void 0) continue;
|
|
28968
|
+
const list = children.get(ppid) ?? [];
|
|
28969
|
+
list.push(pid);
|
|
28970
|
+
children.set(ppid, list);
|
|
28971
|
+
}
|
|
28972
|
+
const tree = [];
|
|
28973
|
+
const queue = [root];
|
|
28974
|
+
const seen2 = /* @__PURE__ */ new Set();
|
|
28975
|
+
while (queue.length > 0) {
|
|
28976
|
+
const pid = queue.shift();
|
|
28977
|
+
if (seen2.has(pid)) continue;
|
|
28978
|
+
seen2.add(pid);
|
|
28979
|
+
const start2 = procStartTicks(pid);
|
|
28980
|
+
tree.push(start2 ? { pid, start: start2 } : { pid });
|
|
28981
|
+
queue.push(...children.get(pid) ?? []);
|
|
28982
|
+
}
|
|
28983
|
+
return tree;
|
|
28984
|
+
}
|
|
28985
|
+
function signalProcessTree(refs, signal) {
|
|
28986
|
+
for (const ref of refs) {
|
|
28987
|
+
if (ref.start !== void 0 && procStartTicks(ref.pid) !== ref.start) continue;
|
|
28988
|
+
try {
|
|
28989
|
+
process.kill(ref.pid, signal);
|
|
28990
|
+
} catch {
|
|
28991
|
+
}
|
|
28992
|
+
}
|
|
28993
|
+
}
|
|
28919
28994
|
function daemonLockPath(sessionId) {
|
|
28920
28995
|
const safe = sessionId.replace(/[^a-zA-Z0-9_-]/g, "_");
|
|
28921
28996
|
return path60.join(os40.homedir(), ".codeam", `daemon-${safe}.lock`);
|
|
@@ -48888,7 +48963,7 @@ async function autoUpgradeBeforeCriticalCommand() {
|
|
|
48888
48963
|
if (process.env.NODE_ENV === "test") return;
|
|
48889
48964
|
if (process.env.CODEAM_DISABLE_UPDATE_CHECK === "1") return;
|
|
48890
48965
|
if (process.env.CI) return;
|
|
48891
|
-
const current2 = true ? "2.75.
|
|
48966
|
+
const current2 = true ? "2.75.43" : null;
|
|
48892
48967
|
if (!current2) return;
|
|
48893
48968
|
const cache = readCache();
|
|
48894
48969
|
const fresh = cache && Date.now() - cache.fetchedAt < TTL_MS;
|
|
@@ -48905,7 +48980,7 @@ function checkForUpdates() {
|
|
|
48905
48980
|
if (process.env.CODEAM_DISABLE_UPDATE_CHECK === "1") return;
|
|
48906
48981
|
if (process.env.CI) return;
|
|
48907
48982
|
if (!process.stdout.isTTY) return;
|
|
48908
|
-
const current2 = true ? "2.75.
|
|
48983
|
+
const current2 = true ? "2.75.43" : null;
|
|
48909
48984
|
if (!current2) return;
|
|
48910
48985
|
const cache = readCache();
|
|
48911
48986
|
const fresh = cache && Date.now() - cache.fetchedAt < TTL_MS;
|
|
@@ -48928,7 +49003,7 @@ var SELF_UPDATE_INSTALL_TIMEOUT_MS = 18e4;
|
|
|
48928
49003
|
var SELF_UPDATE_LS_TIMEOUT_MS = 15e3;
|
|
48929
49004
|
var SUDO_PREFLIGHT_TIMEOUT_MS = 5e3;
|
|
48930
49005
|
function currentCliVersion() {
|
|
48931
|
-
return true ? "2.75.
|
|
49006
|
+
return true ? "2.75.43" : null;
|
|
48932
49007
|
}
|
|
48933
49008
|
async function installedVersion(deps) {
|
|
48934
49009
|
const ls = await deps.run(
|
|
@@ -49268,6 +49343,8 @@ var defaultOnIdentityRejected = () => {
|
|
|
49268
49343
|
);
|
|
49269
49344
|
process.exit(1);
|
|
49270
49345
|
};
|
|
49346
|
+
var ORPHAN_TERM_GRACE_MS = 3e3;
|
|
49347
|
+
var ORPHAN_KILL_SETTLE_MS = 500;
|
|
49271
49348
|
var defaultOnUpdated = (version3) => {
|
|
49272
49349
|
log.info("host-agent", `self-update: installed ${version3}, restarting`);
|
|
49273
49350
|
process.exit(0);
|
|
@@ -49280,6 +49357,9 @@ var HostAgentSupervisor = class {
|
|
|
49280
49357
|
this.resumeSpawner = deps.resumeSpawner ?? defaultResumeSpawner;
|
|
49281
49358
|
this.sessionStore = deps.sessionStore ?? fileSessionChildStore();
|
|
49282
49359
|
this.listSavedSessions = deps.listSavedSessions ?? (() => loadCliConfig().sessions);
|
|
49360
|
+
this.orphanedDaemonFor = deps.orphanedDaemonFor ?? orphanedSupervisorDaemon;
|
|
49361
|
+
this.snapshotTree = deps.snapshotProcessTree ?? snapshotProcessTree;
|
|
49362
|
+
this.signalTree = deps.signalProcessTree ?? signalProcessTree;
|
|
49283
49363
|
this.resolveAgentAuth = deps.resolveAgentAuth ?? unsealAgentAuth;
|
|
49284
49364
|
this.metrics = deps.metricsCollector ?? new MetricsCollector();
|
|
49285
49365
|
this.onIdentityRejected = deps.onIdentityRejected ?? defaultOnIdentityRejected;
|
|
@@ -49334,6 +49414,11 @@ var HostAgentSupervisor = class {
|
|
|
49334
49414
|
postResumeFailure;
|
|
49335
49415
|
sessionStore;
|
|
49336
49416
|
listSavedSessions;
|
|
49417
|
+
orphanedDaemonFor;
|
|
49418
|
+
snapshotTree;
|
|
49419
|
+
signalTree;
|
|
49420
|
+
/** Resumes waiting for a left-behind daemon to die (see replaceOrphanedDaemon). */
|
|
49421
|
+
pendingOrphanReplacements = 0;
|
|
49337
49422
|
/**
|
|
49338
49423
|
* Per-session resume bookkeeping (codeagent-v07a: N sessions resume on
|
|
49339
49424
|
* boot, each with its OWN bounded retries / exhaustion / re-probe throttle —
|
|
@@ -50204,7 +50289,7 @@ var HostAgentSupervisor = class {
|
|
|
50204
50289
|
const records = this.sessionStore.load();
|
|
50205
50290
|
if (records === null) {
|
|
50206
50291
|
const target = this.fallbackResumeTarget();
|
|
50207
|
-
if (target) this.
|
|
50292
|
+
if (target) this.resumeOrReplace(target);
|
|
50208
50293
|
return;
|
|
50209
50294
|
}
|
|
50210
50295
|
if (records.length === 0) return;
|
|
@@ -50231,14 +50316,16 @@ var HostAgentSupervisor = class {
|
|
|
50231
50316
|
);
|
|
50232
50317
|
continue;
|
|
50233
50318
|
}
|
|
50234
|
-
this.
|
|
50319
|
+
this.resumeOrReplace({ deployId: rec.deployId, cwd: rec.cwd, session, agent: rec.agent });
|
|
50235
50320
|
resumed += 1;
|
|
50236
50321
|
}
|
|
50237
50322
|
log.info(
|
|
50238
50323
|
"host-agent",
|
|
50239
50324
|
`resume: ${resumed} session(s) resumed, ${dropped.length} ended (bound ${max}), ${resume.length - resumed} skipped`
|
|
50240
50325
|
);
|
|
50241
|
-
if (this.children.size === 0
|
|
50326
|
+
if (this.children.size === 0 && this.pendingOrphanReplacements === 0) {
|
|
50327
|
+
this.sessionStore.save([]);
|
|
50328
|
+
}
|
|
50242
50329
|
} catch (err) {
|
|
50243
50330
|
log.warn(
|
|
50244
50331
|
"host-agent",
|
|
@@ -50345,6 +50432,36 @@ var HostAgentSupervisor = class {
|
|
|
50345
50432
|
return [];
|
|
50346
50433
|
}
|
|
50347
50434
|
}
|
|
50435
|
+
/**
|
|
50436
|
+
* Resume `target` — first retiring a daemon the PREVIOUS host-agent left
|
|
50437
|
+
* holding the session, if there is one. Without this the resume child
|
|
50438
|
+
* defers to that orphan (it holds the daemon lock and is alive), so the
|
|
50439
|
+
* session keeps running the old code with dead stdio pipes — on 2.75.41 and
|
|
50440
|
+
* older that wedged it offline for good (see `orphanedSupervisorDaemon`).
|
|
50441
|
+
* Replacing it matches what systemd already does on a self-hosted box,
|
|
50442
|
+
* where a unit restart kills the whole control group.
|
|
50443
|
+
*/
|
|
50444
|
+
resumeOrReplace(target) {
|
|
50445
|
+
const orphan = this.orphanedDaemonFor(target.session.id);
|
|
50446
|
+
if (orphan === void 0) {
|
|
50447
|
+
this.resumeOne(target);
|
|
50448
|
+
return;
|
|
50449
|
+
}
|
|
50450
|
+
log.warn(
|
|
50451
|
+
"host-agent",
|
|
50452
|
+
`resume: session ${target.session.id.slice(0, 8)} is held by pid ${orphan}, left behind by the previous host-agent \u2014 replacing it`
|
|
50453
|
+
);
|
|
50454
|
+
this.pendingOrphanReplacements += 1;
|
|
50455
|
+
const tree = this.snapshotTree(orphan);
|
|
50456
|
+
this.signalTree(tree.slice(0, 1), "SIGTERM");
|
|
50457
|
+
setTimeout(() => {
|
|
50458
|
+
this.signalTree(tree, "SIGKILL");
|
|
50459
|
+
setTimeout(() => {
|
|
50460
|
+
this.pendingOrphanReplacements -= 1;
|
|
50461
|
+
if (!this.stopped) this.resumeOne(target);
|
|
50462
|
+
}, ORPHAN_KILL_SETTLE_MS);
|
|
50463
|
+
}, ORPHAN_TERM_GRACE_MS);
|
|
50464
|
+
}
|
|
50348
50465
|
/** Spawn ONE resume child for `target` (boot, retry, or re-probe). */
|
|
50349
50466
|
resumeOne(target) {
|
|
50350
50467
|
const { session, deployId } = target;
|
|
@@ -50904,7 +51021,7 @@ function checkChokidar() {
|
|
|
50904
51021
|
}
|
|
50905
51022
|
async function doctor(args2 = []) {
|
|
50906
51023
|
const json = args2.includes("--json");
|
|
50907
|
-
const cliVersion = true ? "2.75.
|
|
51024
|
+
const cliVersion = true ? "2.75.43" : "0.0.0-dev";
|
|
50908
51025
|
const apiBase2 = resolveApiBaseUrl();
|
|
50909
51026
|
const diagnosticId = (0, import_node_crypto13.randomUUID)();
|
|
50910
51027
|
log.info("doctor", `run id=${diagnosticId} cli=${cliVersion}`);
|
|
@@ -51428,7 +51545,7 @@ async function mcpWarm(argv = [], opts = {}) {
|
|
|
51428
51545
|
// src/commands/version.ts
|
|
51429
51546
|
var import_picocolors15 = __toESM(require("picocolors"));
|
|
51430
51547
|
function version2() {
|
|
51431
|
-
const v = true ? "2.75.
|
|
51548
|
+
const v = true ? "2.75.43" : "unknown";
|
|
51432
51549
|
console.log(`${import_picocolors15.default.bold("codeam-cli")} ${import_picocolors15.default.cyan(v)}`);
|
|
51433
51550
|
}
|
|
51434
51551
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "codeam-cli",
|
|
3
|
-
"version": "2.75.
|
|
3
|
+
"version": "2.75.43",
|
|
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",
|