codeam-cli 2.52.4 → 2.52.6
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 +112 -31
- 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.52.5] — 2026-07-02
|
|
8
|
+
|
|
9
|
+
### CI
|
|
10
|
+
|
|
11
|
+
- **workflow:** Free runner disk before the JetBrains publish job
|
|
12
|
+
|
|
7
13
|
## [2.52.3] — 2026-06-30
|
|
8
14
|
|
|
9
15
|
### Added
|
package/dist/index.js
CHANGED
|
@@ -5397,7 +5397,7 @@ function readAnonId() {
|
|
|
5397
5397
|
}
|
|
5398
5398
|
function superProperties() {
|
|
5399
5399
|
return {
|
|
5400
|
-
cliVersion: true ? "2.52.
|
|
5400
|
+
cliVersion: true ? "2.52.6" : "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.52.
|
|
5581
|
+
version: "2.52.6",
|
|
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",
|
|
@@ -6105,6 +6105,13 @@ function computePollDelay({ baseMs, failures }) {
|
|
|
6105
6105
|
}
|
|
6106
6106
|
|
|
6107
6107
|
// src/services/command-relay.service.ts
|
|
6108
|
+
function httpStatusOf(err) {
|
|
6109
|
+
if (typeof err === "object" && err !== null && "statusCode" in err) {
|
|
6110
|
+
const status2 = err.statusCode;
|
|
6111
|
+
if (typeof status2 === "number") return status2;
|
|
6112
|
+
}
|
|
6113
|
+
return null;
|
|
6114
|
+
}
|
|
6108
6115
|
var API_BASE2 = resolveApiBaseUrl();
|
|
6109
6116
|
var SSE_LIVENESS_TIMEOUT_MS = 45e3;
|
|
6110
6117
|
var SSE_WATCHDOG_INTERVAL_MS = 1e4;
|
|
@@ -6120,6 +6127,7 @@ var CommandRelayService = class {
|
|
|
6120
6127
|
agentMeta;
|
|
6121
6128
|
agentsOverride;
|
|
6122
6129
|
_running = false;
|
|
6130
|
+
pairingInvalid = false;
|
|
6123
6131
|
heartbeatTimer = null;
|
|
6124
6132
|
agentsTimer = null;
|
|
6125
6133
|
/** True once `/api/plugin/agents` has accepted at least one report. */
|
|
@@ -6197,7 +6205,22 @@ var CommandRelayService = class {
|
|
|
6197
6205
|
});
|
|
6198
6206
|
}
|
|
6199
6207
|
async sendResult(commandId, status2, result) {
|
|
6200
|
-
|
|
6208
|
+
if (this.pairingInvalid) return;
|
|
6209
|
+
try {
|
|
6210
|
+
await _postJson(`${API_BASE2}/api/commands/result`, { commandId, status: status2, result });
|
|
6211
|
+
} catch (err) {
|
|
6212
|
+
const statusCode = httpStatusOf(err);
|
|
6213
|
+
if (statusCode === 401 || statusCode === 403) {
|
|
6214
|
+
this.pairingInvalid = true;
|
|
6215
|
+
process.stderr.write(
|
|
6216
|
+
"[codeam] This pairing is no longer valid \u2014 run `codeam pair` again to reconnect this session.\n"
|
|
6217
|
+
);
|
|
6218
|
+
log.warn("relay", `pairing invalid (status=${statusCode}) \u2014 relay stopped`);
|
|
6219
|
+
this.stop();
|
|
6220
|
+
return;
|
|
6221
|
+
}
|
|
6222
|
+
throw err;
|
|
6223
|
+
}
|
|
6201
6224
|
}
|
|
6202
6225
|
// ─── SSE pull (primary) ──────────────────────────────────────────
|
|
6203
6226
|
connectSSE() {
|
|
@@ -7340,24 +7363,24 @@ async function refreshAuthToken(sessionId, pluginId) {
|
|
|
7340
7363
|
},
|
|
7341
7364
|
JSON.stringify({ sessionId, pluginId })
|
|
7342
7365
|
);
|
|
7343
|
-
if (statusCode === 404) {
|
|
7344
|
-
log.warn("chunkEmitter",
|
|
7345
|
-
return
|
|
7366
|
+
if (statusCode === 404 || statusCode === 401 || statusCode === 403) {
|
|
7367
|
+
log.warn("chunkEmitter", `[auth] reconnect ${statusCode} \u2014 session gone server-side`);
|
|
7368
|
+
return { kind: "gone" };
|
|
7346
7369
|
}
|
|
7347
7370
|
if (statusCode >= 400) {
|
|
7348
7371
|
log.warn("chunkEmitter", `[auth] reconnect failed status=${statusCode}`);
|
|
7349
|
-
return
|
|
7372
|
+
return { kind: "transient" };
|
|
7350
7373
|
}
|
|
7351
7374
|
const parsed = JSON.parse(body);
|
|
7352
7375
|
const fresh = parsed.data?.pluginAuthToken;
|
|
7353
7376
|
if (typeof fresh !== "string" || fresh.length === 0) {
|
|
7354
7377
|
log.warn("chunkEmitter", "[auth] reconnect response missing pluginAuthToken");
|
|
7355
|
-
return
|
|
7378
|
+
return { kind: "transient" };
|
|
7356
7379
|
}
|
|
7357
|
-
return fresh;
|
|
7380
|
+
return { kind: "fresh", token: fresh };
|
|
7358
7381
|
} catch (err) {
|
|
7359
7382
|
log.warn("chunkEmitter", `[auth] reconnect threw: ${String(err)}`);
|
|
7360
|
-
return
|
|
7383
|
+
return { kind: "transient" };
|
|
7361
7384
|
}
|
|
7362
7385
|
}
|
|
7363
7386
|
var ChunkEmitter = class {
|
|
@@ -7375,6 +7398,11 @@ var ChunkEmitter = class {
|
|
|
7375
7398
|
opts;
|
|
7376
7399
|
url = `${API_BASE4}/api/commands/output`;
|
|
7377
7400
|
headers;
|
|
7401
|
+
/** Latched when the pairing is unrecoverable (401/403 whose refresh
|
|
7402
|
+
* says the session is gone, or a fresh token still rejected) —
|
|
7403
|
+
* every later send short-circuits (2026-06-28 incident: 401 ×34
|
|
7404
|
+
* with a dead token while the user saw nothing). */
|
|
7405
|
+
pairingInvalid = false;
|
|
7378
7406
|
/**
|
|
7379
7407
|
* Send a chunk. `body` is the chunk fields minus `sessionId` /
|
|
7380
7408
|
* `pluginId` — the emitter splices those in. `critical = true`
|
|
@@ -7394,7 +7422,11 @@ var ChunkEmitter = class {
|
|
|
7394
7422
|
"chunkEmitter",
|
|
7395
7423
|
`send type=${body.type ?? "(clear)"} bytes=${payload.length} done=${body.done === true}`
|
|
7396
7424
|
);
|
|
7425
|
+
if (this.pairingInvalid) {
|
|
7426
|
+
return Promise.resolve({ dead: true });
|
|
7427
|
+
}
|
|
7397
7428
|
return new Promise((resolve7) => {
|
|
7429
|
+
let refreshedOnce = false;
|
|
7398
7430
|
const attempt = (attemptsLeft) => {
|
|
7399
7431
|
_transport3.post(this.url, this.headers, payload).then(({ statusCode, body: resBody }) => {
|
|
7400
7432
|
const tookMs = Date.now() - t0;
|
|
@@ -7404,18 +7436,27 @@ var ChunkEmitter = class {
|
|
|
7404
7436
|
resolve7({ dead: true });
|
|
7405
7437
|
return;
|
|
7406
7438
|
}
|
|
7407
|
-
if (statusCode === 401) {
|
|
7408
|
-
|
|
7439
|
+
if (statusCode === 401 || statusCode === 403) {
|
|
7440
|
+
if (refreshedOnce) {
|
|
7441
|
+
this.markPairingInvalid(statusCode, tookMs);
|
|
7442
|
+
resolve7({ dead: true });
|
|
7443
|
+
return;
|
|
7444
|
+
}
|
|
7445
|
+
log.warn("chunkEmitter", `auth ${statusCode} took=${tookMs}ms \u2014 attempting silent refresh`);
|
|
7409
7446
|
void (async () => {
|
|
7410
|
-
const
|
|
7411
|
-
if (fresh) {
|
|
7412
|
-
this.headers["X-Plugin-Auth-Token"] =
|
|
7413
|
-
this.opts.pluginAuthToken =
|
|
7447
|
+
const refresh = await refreshAuthToken(this.opts.sessionId, this.opts.pluginId);
|
|
7448
|
+
if (refresh.kind === "fresh") {
|
|
7449
|
+
this.headers["X-Plugin-Auth-Token"] = refresh.token;
|
|
7450
|
+
this.opts.pluginAuthToken = refresh.token;
|
|
7451
|
+
refreshedOnce = true;
|
|
7414
7452
|
log.info("chunkEmitter", "auth refreshed silently");
|
|
7415
|
-
|
|
7416
|
-
|
|
7417
|
-
|
|
7418
|
-
|
|
7453
|
+
attempt(Math.max(attemptsLeft, 1));
|
|
7454
|
+
return;
|
|
7455
|
+
}
|
|
7456
|
+
if (refresh.kind === "gone") {
|
|
7457
|
+
this.markPairingInvalid(statusCode, tookMs);
|
|
7458
|
+
resolve7({ dead: true });
|
|
7459
|
+
return;
|
|
7419
7460
|
}
|
|
7420
7461
|
resolve7({ dead: false });
|
|
7421
7462
|
})();
|
|
@@ -7446,6 +7487,17 @@ var ChunkEmitter = class {
|
|
|
7446
7487
|
attempt(maxRetries);
|
|
7447
7488
|
});
|
|
7448
7489
|
}
|
|
7490
|
+
markPairingInvalid(statusCode, tookMs) {
|
|
7491
|
+
if (this.pairingInvalid) return;
|
|
7492
|
+
this.pairingInvalid = true;
|
|
7493
|
+
process.stderr.write(
|
|
7494
|
+
"[codeam] This pairing is no longer valid \u2014 run `codeam pair` again to reconnect this session.\n"
|
|
7495
|
+
);
|
|
7496
|
+
log.warn(
|
|
7497
|
+
"chunkEmitter",
|
|
7498
|
+
`pairing invalid (status=${statusCode}) took=${tookMs}ms \u2014 emitter latched, no further posts`
|
|
7499
|
+
);
|
|
7500
|
+
}
|
|
7449
7501
|
};
|
|
7450
7502
|
var _transport3 = {
|
|
7451
7503
|
post: _post2
|
|
@@ -14876,7 +14928,7 @@ async function autoUpgradeBeforeCriticalCommand() {
|
|
|
14876
14928
|
if (process.env.NODE_ENV === "test") return;
|
|
14877
14929
|
if (process.env.CODEAM_DISABLE_UPDATE_CHECK === "1") return;
|
|
14878
14930
|
if (process.env.CI) return;
|
|
14879
|
-
const current = true ? "2.52.
|
|
14931
|
+
const current = true ? "2.52.6" : null;
|
|
14880
14932
|
if (!current) return;
|
|
14881
14933
|
const cache = readCache();
|
|
14882
14934
|
const fresh = cache && Date.now() - cache.fetchedAt < TTL_MS;
|
|
@@ -14893,7 +14945,7 @@ function checkForUpdates() {
|
|
|
14893
14945
|
if (process.env.CODEAM_DISABLE_UPDATE_CHECK === "1") return;
|
|
14894
14946
|
if (process.env.CI) return;
|
|
14895
14947
|
if (!process.stdout.isTTY) return;
|
|
14896
|
-
const current = true ? "2.52.
|
|
14948
|
+
const current = true ? "2.52.6" : null;
|
|
14897
14949
|
if (!current) return;
|
|
14898
14950
|
const cache = readCache();
|
|
14899
14951
|
const fresh = cache && Date.now() - cache.fetchedAt < TTL_MS;
|
|
@@ -15595,7 +15647,7 @@ var defaultSpawner = (env, cwd, args2 = []) => (0, import_node_child_process15.s
|
|
|
15595
15647
|
detached: false
|
|
15596
15648
|
});
|
|
15597
15649
|
function currentCliVersion() {
|
|
15598
|
-
return true ? "2.52.
|
|
15650
|
+
return true ? "2.52.6" : null;
|
|
15599
15651
|
}
|
|
15600
15652
|
function runCmd(cmd, args2, timeoutMs) {
|
|
15601
15653
|
return new Promise((resolve7) => {
|
|
@@ -24652,6 +24704,10 @@ var AcpPublisher = class {
|
|
|
24652
24704
|
opts;
|
|
24653
24705
|
apiBase;
|
|
24654
24706
|
token;
|
|
24707
|
+
/** Latched on an unrecoverable 401/403 — every surface stops posting
|
|
24708
|
+
* (2026-06-28 incident: a dead-token publisher spammed 401 ×34 while
|
|
24709
|
+
* the agent's replies silently never reached the phone). */
|
|
24710
|
+
pairingInvalid = false;
|
|
24655
24711
|
authHeaders() {
|
|
24656
24712
|
return {
|
|
24657
24713
|
"Content-Type": "application/json",
|
|
@@ -24667,14 +24723,39 @@ var AcpPublisher = class {
|
|
|
24667
24723
|
* response unchanged — callers log non-2xx but never throw.
|
|
24668
24724
|
*/
|
|
24669
24725
|
async postWithReauth(url, payload) {
|
|
24726
|
+
if (this.pairingInvalid) {
|
|
24727
|
+
return { statusCode: 401, body: "PAIRING_INVALID" };
|
|
24728
|
+
}
|
|
24670
24729
|
const first = await _transport4.post(url, this.authHeaders(), payload);
|
|
24671
24730
|
if (first.statusCode !== 401 && first.statusCode !== 403) return first;
|
|
24672
|
-
if (
|
|
24673
|
-
|
|
24674
|
-
|
|
24675
|
-
|
|
24676
|
-
|
|
24677
|
-
|
|
24731
|
+
if (this.opts.refreshAuthToken) {
|
|
24732
|
+
const fresh = await this.opts.refreshAuthToken();
|
|
24733
|
+
if (fresh) {
|
|
24734
|
+
this.token = fresh;
|
|
24735
|
+
log.info("acpPublisher", `plugin-auth token refreshed after ${first.statusCode}; retrying POST`);
|
|
24736
|
+
const second = await _transport4.post(url, this.authHeaders(), payload);
|
|
24737
|
+
if (second.statusCode !== 401 && second.statusCode !== 403) return second;
|
|
24738
|
+
this.markPairingInvalid(second.statusCode);
|
|
24739
|
+
return second;
|
|
24740
|
+
}
|
|
24741
|
+
}
|
|
24742
|
+
this.markPairingInvalid(first.statusCode);
|
|
24743
|
+
return first;
|
|
24744
|
+
}
|
|
24745
|
+
markPairingInvalid(statusCode) {
|
|
24746
|
+
if (this.pairingInvalid) return;
|
|
24747
|
+
this.pairingInvalid = true;
|
|
24748
|
+
process.stderr.write(
|
|
24749
|
+
"[codeam] This pairing is no longer valid \u2014 run `codeam pair` again to reconnect this session.\n"
|
|
24750
|
+
);
|
|
24751
|
+
log.warn(
|
|
24752
|
+
"acpPublisher",
|
|
24753
|
+
`pairing invalid (status=${statusCode}) \u2014 publisher latched, no further posts`
|
|
24754
|
+
);
|
|
24755
|
+
try {
|
|
24756
|
+
this.opts.onPairingInvalid?.();
|
|
24757
|
+
} catch {
|
|
24758
|
+
}
|
|
24678
24759
|
}
|
|
24679
24760
|
/**
|
|
24680
24761
|
* Wrap the body with `sessionId` + `pluginId` at the top level.
|
|
@@ -31207,7 +31288,7 @@ function checkChokidar() {
|
|
|
31207
31288
|
}
|
|
31208
31289
|
async function doctor(args2 = []) {
|
|
31209
31290
|
const json = args2.includes("--json");
|
|
31210
|
-
const cliVersion = true ? "2.52.
|
|
31291
|
+
const cliVersion = true ? "2.52.6" : "0.0.0-dev";
|
|
31211
31292
|
const apiBase2 = resolveApiBaseUrl();
|
|
31212
31293
|
const diagnosticId = (0, import_node_crypto8.randomUUID)();
|
|
31213
31294
|
log.info("doctor", `run id=${diagnosticId} cli=${cliVersion}`);
|
|
@@ -31406,7 +31487,7 @@ async function completion(args2) {
|
|
|
31406
31487
|
// src/commands/version.ts
|
|
31407
31488
|
var import_picocolors15 = __toESM(require("picocolors"));
|
|
31408
31489
|
function version2() {
|
|
31409
|
-
const v = true ? "2.52.
|
|
31490
|
+
const v = true ? "2.52.6" : "unknown";
|
|
31410
31491
|
console.log(`${import_picocolors15.default.bold("codeam-cli")} ${import_picocolors15.default.cyan(v)}`);
|
|
31411
31492
|
}
|
|
31412
31493
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "codeam-cli",
|
|
3
|
-
"version": "2.52.
|
|
3
|
+
"version": "2.52.6",
|
|
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",
|