codeam-cli 2.39.30 → 2.39.31
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 +6 -0
- package/dist/index.js +94 -17
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -4,6 +4,12 @@ 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.39.30] — 2026-06-18
|
|
8
|
+
|
|
9
|
+
### Added
|
|
10
|
+
|
|
11
|
+
- **cli:** Report real system metrics in host-agent heartbeat
|
|
12
|
+
|
|
7
13
|
## [2.39.29] — 2026-06-18
|
|
8
14
|
|
|
9
15
|
### Added
|
package/dist/index.js
CHANGED
|
@@ -498,7 +498,7 @@ var import_qrcode_terminal = __toESM(require("qrcode-terminal"));
|
|
|
498
498
|
// package.json
|
|
499
499
|
var package_default = {
|
|
500
500
|
name: "codeam-cli",
|
|
501
|
-
version: "2.39.
|
|
501
|
+
version: "2.39.31",
|
|
502
502
|
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.",
|
|
503
503
|
type: "commonjs",
|
|
504
504
|
main: "dist/index.js",
|
|
@@ -5908,7 +5908,7 @@ function readAnonId() {
|
|
|
5908
5908
|
}
|
|
5909
5909
|
function superProperties() {
|
|
5910
5910
|
return {
|
|
5911
|
-
cliVersion: true ? "2.39.
|
|
5911
|
+
cliVersion: true ? "2.39.31" : "0.0.0-dev",
|
|
5912
5912
|
nodeVersion: process.version,
|
|
5913
5913
|
platform: process.platform,
|
|
5914
5914
|
arch: process.arch,
|
|
@@ -26283,6 +26283,31 @@ function saveHostIdentity(identity) {
|
|
|
26283
26283
|
});
|
|
26284
26284
|
fs41.chmodSync(file, 384);
|
|
26285
26285
|
}
|
|
26286
|
+
var HostHttpError = class extends Error {
|
|
26287
|
+
constructor(message, status2) {
|
|
26288
|
+
super(message);
|
|
26289
|
+
this.status = status2;
|
|
26290
|
+
this.name = "HostHttpError";
|
|
26291
|
+
}
|
|
26292
|
+
status;
|
|
26293
|
+
/**
|
|
26294
|
+
* True iff this is a genuine auth-rejection of the host identity:
|
|
26295
|
+
* 401 (token invalid), 403 (forbidden), 404 (host not found / deleted).
|
|
26296
|
+
* Transient failures (5xx, timeouts, network errors) are NOT rejections.
|
|
26297
|
+
*/
|
|
26298
|
+
get isAuthRejection() {
|
|
26299
|
+
return this.status === 401 || this.status === 403 || this.status === 404;
|
|
26300
|
+
}
|
|
26301
|
+
};
|
|
26302
|
+
function isHostAuthRejection(err) {
|
|
26303
|
+
return err instanceof HostHttpError && err.isAuthRejection;
|
|
26304
|
+
}
|
|
26305
|
+
function deleteHostIdentity() {
|
|
26306
|
+
try {
|
|
26307
|
+
fs41.rmSync(hostIdentityPath(), { force: true });
|
|
26308
|
+
} catch {
|
|
26309
|
+
}
|
|
26310
|
+
}
|
|
26286
26311
|
async function postJson(pathname, body) {
|
|
26287
26312
|
const res = await fetch(`${apiBase()}${pathname}`, {
|
|
26288
26313
|
method: "POST",
|
|
@@ -26292,8 +26317,9 @@ async function postJson(pathname, body) {
|
|
|
26292
26317
|
const json = await res.json();
|
|
26293
26318
|
if (!res.ok || !json.success) {
|
|
26294
26319
|
const err = !json.success ? json.error : void 0;
|
|
26295
|
-
throw new
|
|
26296
|
-
`${pathname} failed (${err?.code ?? `HTTP_${res.status}`}): ${err?.message ?? res.statusText}
|
|
26320
|
+
throw new HostHttpError(
|
|
26321
|
+
`${pathname} failed (${err?.code ?? `HTTP_${res.status}`}): ${err?.message ?? res.statusText}`,
|
|
26322
|
+
res.status
|
|
26297
26323
|
);
|
|
26298
26324
|
}
|
|
26299
26325
|
return json.data;
|
|
@@ -26555,6 +26581,17 @@ var defaultSpawner = (env, cwd, args2 = []) => (0, import_node_child_process13.s
|
|
|
26555
26581
|
stdio: "ignore",
|
|
26556
26582
|
detached: false
|
|
26557
26583
|
});
|
|
26584
|
+
var defaultOnIdentityRejected = () => {
|
|
26585
|
+
deleteHostIdentity();
|
|
26586
|
+
log.warn("host-agent", "host identity rejected by backend \u2014 wiped sealed identity, exiting");
|
|
26587
|
+
process.exit(1);
|
|
26588
|
+
};
|
|
26589
|
+
var defaultDisableService = () => {
|
|
26590
|
+
try {
|
|
26591
|
+
(0, import_node_child_process13.execFileSync)("systemctl", ["disable", "--now", "codeam-host-agent"], { stdio: "ignore" });
|
|
26592
|
+
} catch {
|
|
26593
|
+
}
|
|
26594
|
+
};
|
|
26558
26595
|
var HostAgentSupervisor = class {
|
|
26559
26596
|
constructor(identity, deps = {}) {
|
|
26560
26597
|
this.identity = identity;
|
|
@@ -26562,6 +26599,8 @@ var HostAgentSupervisor = class {
|
|
|
26562
26599
|
this.spawnChild = deps.spawnChild ?? defaultSpawner;
|
|
26563
26600
|
this.resolveAgentAuth = deps.resolveAgentAuth ?? unsealAgentAuth;
|
|
26564
26601
|
this.metrics = deps.metricsCollector ?? new MetricsCollector();
|
|
26602
|
+
this.onIdentityRejected = deps.onIdentityRejected ?? defaultOnIdentityRejected;
|
|
26603
|
+
this.disableService = deps.disableService ?? defaultDisableService;
|
|
26565
26604
|
}
|
|
26566
26605
|
identity;
|
|
26567
26606
|
deps;
|
|
@@ -26574,6 +26613,12 @@ var HostAgentSupervisor = class {
|
|
|
26574
26613
|
reportedConnected = false;
|
|
26575
26614
|
/** Live-metrics collector — stateful across beats (CPU delta + latency). */
|
|
26576
26615
|
metrics;
|
|
26616
|
+
/** Self-heal action when the backend rejects this identity. */
|
|
26617
|
+
onIdentityRejected;
|
|
26618
|
+
/** Best-effort systemd de-provision used by `self_hosted_wipe`. */
|
|
26619
|
+
disableService;
|
|
26620
|
+
/** Guards against firing the self-heal more than once. */
|
|
26621
|
+
healing = false;
|
|
26577
26622
|
/** Open the control channel (reusing the relay) + start heartbeats. */
|
|
26578
26623
|
start() {
|
|
26579
26624
|
const make = this.deps.makeRelay ?? ((pluginId, onCommand, meta) => new CommandRelayService(pluginId, onCommand, meta));
|
|
@@ -26622,6 +26667,14 @@ var HostAgentSupervisor = class {
|
|
|
26622
26667
|
);
|
|
26623
26668
|
}
|
|
26624
26669
|
} catch (err) {
|
|
26670
|
+
if (isHostAuthRejection(err)) {
|
|
26671
|
+
if (!this.healing) {
|
|
26672
|
+
this.healing = true;
|
|
26673
|
+
log.warn("host-agent", "heartbeat rejected \u2014 host deleted/revoked, self-healing", err);
|
|
26674
|
+
this.onIdentityRejected();
|
|
26675
|
+
}
|
|
26676
|
+
return;
|
|
26677
|
+
}
|
|
26625
26678
|
log.trace("host-agent", "heartbeat failed", err);
|
|
26626
26679
|
}
|
|
26627
26680
|
}
|
|
@@ -26651,6 +26704,16 @@ var HostAgentSupervisor = class {
|
|
|
26651
26704
|
this.stopChild(cmd.payload.sessionId);
|
|
26652
26705
|
return;
|
|
26653
26706
|
}
|
|
26707
|
+
if (cmd.type === "self_hosted_wipe") {
|
|
26708
|
+
log.warn("host-agent", `self_hosted_wipe received id=${cmd.id} \u2014 de-provisioning`);
|
|
26709
|
+
this.stop();
|
|
26710
|
+
this.disableService();
|
|
26711
|
+
if (!this.healing) {
|
|
26712
|
+
this.healing = true;
|
|
26713
|
+
this.onIdentityRejected();
|
|
26714
|
+
}
|
|
26715
|
+
return;
|
|
26716
|
+
}
|
|
26654
26717
|
log.trace("host-agent", `ignoring unsupported command type=${cmd.type}`);
|
|
26655
26718
|
}
|
|
26656
26719
|
/**
|
|
@@ -26725,17 +26788,31 @@ var HostAgentSupervisor = class {
|
|
|
26725
26788
|
};
|
|
26726
26789
|
async function resolveHostIdentity(enrollToken) {
|
|
26727
26790
|
const existing = loadHostIdentity();
|
|
26791
|
+
if (enrollToken) {
|
|
26792
|
+
await reportProgress({ enrollToken }, "redeeming", "redeeming enrollment token\u2026");
|
|
26793
|
+
try {
|
|
26794
|
+
const identity = await redeemEnrollToken(enrollToken);
|
|
26795
|
+
saveHostIdentity(identity);
|
|
26796
|
+
await reportProgress(
|
|
26797
|
+
{ hostId: identity.hostId, hostToken: identity.hostToken },
|
|
26798
|
+
"enrolled",
|
|
26799
|
+
"host enrolled"
|
|
26800
|
+
);
|
|
26801
|
+
return identity;
|
|
26802
|
+
} catch (err) {
|
|
26803
|
+
if (existing) {
|
|
26804
|
+
log.trace(
|
|
26805
|
+
"host-agent",
|
|
26806
|
+
"enroll-token redeem failed; reusing sealed identity (likely a restart)",
|
|
26807
|
+
err
|
|
26808
|
+
);
|
|
26809
|
+
return existing;
|
|
26810
|
+
}
|
|
26811
|
+
throw err;
|
|
26812
|
+
}
|
|
26813
|
+
}
|
|
26728
26814
|
if (existing) return existing;
|
|
26729
|
-
|
|
26730
|
-
await reportProgress({ enrollToken }, "redeeming", "redeeming enrollment token\u2026");
|
|
26731
|
-
const identity = await redeemEnrollToken(enrollToken);
|
|
26732
|
-
saveHostIdentity(identity);
|
|
26733
|
-
await reportProgress(
|
|
26734
|
-
{ hostId: identity.hostId, hostToken: identity.hostToken },
|
|
26735
|
-
"enrolled",
|
|
26736
|
-
"host enrolled"
|
|
26737
|
-
);
|
|
26738
|
-
return identity;
|
|
26815
|
+
return null;
|
|
26739
26816
|
}
|
|
26740
26817
|
async function hostAgent(args2 = []) {
|
|
26741
26818
|
const tokenArg = args2.find((a) => a.startsWith("--token="));
|
|
@@ -26931,7 +27008,7 @@ function checkChokidar() {
|
|
|
26931
27008
|
}
|
|
26932
27009
|
async function doctor(args2 = []) {
|
|
26933
27010
|
const json = args2.includes("--json");
|
|
26934
|
-
const cliVersion = true ? "2.39.
|
|
27011
|
+
const cliVersion = true ? "2.39.31" : "0.0.0-dev";
|
|
26935
27012
|
const apiBase2 = resolveApiBaseUrl();
|
|
26936
27013
|
const diagnosticId = (0, import_node_crypto8.randomUUID)();
|
|
26937
27014
|
log.info("doctor", `run id=${diagnosticId} cli=${cliVersion}`);
|
|
@@ -27130,7 +27207,7 @@ async function completion(args2) {
|
|
|
27130
27207
|
// src/commands/version.ts
|
|
27131
27208
|
var import_picocolors13 = __toESM(require("picocolors"));
|
|
27132
27209
|
function version2() {
|
|
27133
|
-
const v = true ? "2.39.
|
|
27210
|
+
const v = true ? "2.39.31" : "unknown";
|
|
27134
27211
|
console.log(`${import_picocolors13.default.bold("codeam-cli")} ${import_picocolors13.default.cyan(v)}`);
|
|
27135
27212
|
}
|
|
27136
27213
|
|
|
@@ -27416,7 +27493,7 @@ function checkForUpdates() {
|
|
|
27416
27493
|
if (process.env.CODEAM_DISABLE_UPDATE_CHECK === "1") return;
|
|
27417
27494
|
if (process.env.CI) return;
|
|
27418
27495
|
if (!process.stdout.isTTY) return;
|
|
27419
|
-
const current = true ? "2.39.
|
|
27496
|
+
const current = true ? "2.39.31" : null;
|
|
27420
27497
|
if (!current) return;
|
|
27421
27498
|
const cache = readCache();
|
|
27422
27499
|
const fresh = cache && Date.now() - cache.fetchedAt < TTL_MS;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "codeam-cli",
|
|
3
|
-
"version": "2.39.
|
|
3
|
+
"version": "2.39.31",
|
|
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",
|