codeam-cli 2.39.16 → 2.39.18
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 +62 -23
- 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.39.16] — 2026-06-14
|
|
8
|
+
|
|
9
|
+
### Added
|
|
10
|
+
|
|
11
|
+
- **jetbrains-plugin:** Proof-of-possession secret for /status + /reconnect (SEC crit1)
|
|
12
|
+
|
|
13
|
+
## [2.39.15] — 2026-06-14
|
|
14
|
+
|
|
15
|
+
### Added
|
|
16
|
+
|
|
17
|
+
- **vsc-plugin:** Proof-of-possession secret for /status + /reconnect (SEC crit1)
|
|
18
|
+
|
|
7
19
|
## [2.39.14] — 2026-06-14
|
|
8
20
|
|
|
9
21
|
### 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.18",
|
|
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",
|
|
@@ -989,7 +989,7 @@ async function _postJson(url, body, extraHeaders) {
|
|
|
989
989
|
req.end();
|
|
990
990
|
});
|
|
991
991
|
}
|
|
992
|
-
async function _getJson(url) {
|
|
992
|
+
async function _getJson(url, extraHeaders) {
|
|
993
993
|
return new Promise((resolve7, reject) => {
|
|
994
994
|
const u2 = new URL(url);
|
|
995
995
|
const transport = u2.protocol === "https:" ? https : http;
|
|
@@ -999,7 +999,7 @@ async function _getJson(url) {
|
|
|
999
999
|
port: u2.port || (u2.protocol === "https:" ? 443 : 80),
|
|
1000
1000
|
path: u2.pathname + u2.search,
|
|
1001
1001
|
method: "GET",
|
|
1002
|
-
headers: { ...vercelBypassHeader() },
|
|
1002
|
+
headers: { ...vercelBypassHeader(), ...extraHeaders ?? {} },
|
|
1003
1003
|
timeout: 1e4
|
|
1004
1004
|
},
|
|
1005
1005
|
(res) => {
|
|
@@ -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.18" : "0.0.0-dev",
|
|
5912
5912
|
nodeVersion: process.version,
|
|
5913
5913
|
platform: process.platform,
|
|
5914
5914
|
arch: process.arch,
|
|
@@ -6111,7 +6111,12 @@ var CommandRelayService = class {
|
|
|
6111
6111
|
port: url.port || (url.protocol === "https:" ? 443 : 80),
|
|
6112
6112
|
path: `${url.pathname}${url.search}`,
|
|
6113
6113
|
method: "GET",
|
|
6114
|
-
headers: {
|
|
6114
|
+
headers: {
|
|
6115
|
+
Accept: "text/event-stream",
|
|
6116
|
+
"Cache-Control": "no-cache",
|
|
6117
|
+
...vercelBypassHeader(),
|
|
6118
|
+
...this.pollSecretHeader()
|
|
6119
|
+
},
|
|
6115
6120
|
timeout: 35e3
|
|
6116
6121
|
},
|
|
6117
6122
|
(res) => {
|
|
@@ -6264,9 +6269,27 @@ var CommandRelayService = class {
|
|
|
6264
6269
|
this.pollTimer = setTimeout(() => this.pollLoop(), delay);
|
|
6265
6270
|
}
|
|
6266
6271
|
}
|
|
6272
|
+
// SEC crit1 (#8): the command-delivery endpoints are gated on the
|
|
6273
|
+
// per-pairing pollSecret when the backend enforces it. Look it up from
|
|
6274
|
+
// the persisted session (keyed by this relay's pluginId) and replay it
|
|
6275
|
+
// as X-Plugin-Poll-Secret. Empty {} for legacy sessions / older
|
|
6276
|
+
// backends (which ignore it).
|
|
6277
|
+
pollSecretHeader() {
|
|
6278
|
+
try {
|
|
6279
|
+
const secret = loadCliConfig().sessions.find(
|
|
6280
|
+
(s) => s.pluginId === this.pluginId
|
|
6281
|
+
)?.pollSecret;
|
|
6282
|
+
return secret ? { "X-Plugin-Poll-Secret": secret } : {};
|
|
6283
|
+
} catch {
|
|
6284
|
+
return {};
|
|
6285
|
+
}
|
|
6286
|
+
}
|
|
6267
6287
|
async pollOnce() {
|
|
6268
6288
|
try {
|
|
6269
|
-
const data = await _getJson(
|
|
6289
|
+
const data = await _getJson(
|
|
6290
|
+
`${API_BASE2}/api/commands/pending?pluginId=${this.pluginId}`,
|
|
6291
|
+
this.pollSecretHeader()
|
|
6292
|
+
);
|
|
6270
6293
|
const commands = data?.data;
|
|
6271
6294
|
this.pollFailures = 0;
|
|
6272
6295
|
if (!Array.isArray(commands) || commands.length === 0) {
|
|
@@ -16661,7 +16684,7 @@ var http4 = __toESM(require("http"));
|
|
|
16661
16684
|
var API_BASE3 = resolveApiBaseUrl();
|
|
16662
16685
|
var PAIR_TIMEOUT_MS = 5 * 60 * 1e3;
|
|
16663
16686
|
var dispatchers = /* @__PURE__ */ new Set();
|
|
16664
|
-
function subscribeToPairCompletion(pluginId, onPaired, onTimeout) {
|
|
16687
|
+
function subscribeToPairCompletion(pluginId, onPaired, onTimeout, pollSecret) {
|
|
16665
16688
|
let stopped = false;
|
|
16666
16689
|
let req = null;
|
|
16667
16690
|
let timeoutTimer = null;
|
|
@@ -16696,7 +16719,8 @@ function subscribeToPairCompletion(pluginId, onPaired, onTimeout) {
|
|
|
16696
16719
|
headers: {
|
|
16697
16720
|
Accept: "text/event-stream",
|
|
16698
16721
|
"Cache-Control": "no-cache",
|
|
16699
|
-
...vercelBypassHeader()
|
|
16722
|
+
...vercelBypassHeader(),
|
|
16723
|
+
...pollSecret ? { "X-Plugin-Poll-Secret": pollSecret } : {}
|
|
16700
16724
|
},
|
|
16701
16725
|
timeout: 35e3
|
|
16702
16726
|
},
|
|
@@ -23022,7 +23046,7 @@ function parseJsonl(filePath) {
|
|
|
23022
23046
|
}
|
|
23023
23047
|
return messages;
|
|
23024
23048
|
}
|
|
23025
|
-
function post(endpoint, body) {
|
|
23049
|
+
function post(endpoint, body, pluginAuthToken) {
|
|
23026
23050
|
return new Promise((resolve7) => {
|
|
23027
23051
|
const payload = JSON.stringify(body);
|
|
23028
23052
|
const u2 = new URL(`${API_BASE8}${endpoint}`);
|
|
@@ -23036,7 +23060,11 @@ function post(endpoint, body) {
|
|
|
23036
23060
|
headers: {
|
|
23037
23061
|
"Content-Type": "application/json",
|
|
23038
23062
|
"Content-Length": Buffer.byteLength(payload),
|
|
23039
|
-
...vercelBypassHeader()
|
|
23063
|
+
...vercelBypassHeader(),
|
|
23064
|
+
// SEC crit1 (#819): authenticate conversation-history writes so
|
|
23065
|
+
// the backend can verify the (sessionId, pluginId) ownership.
|
|
23066
|
+
// Older backends ignore the header.
|
|
23067
|
+
...pluginAuthToken ? { "X-Plugin-Auth-Token": pluginAuthToken } : {}
|
|
23040
23068
|
},
|
|
23041
23069
|
timeout: 15e3
|
|
23042
23070
|
},
|
|
@@ -23065,6 +23093,7 @@ var HistoryService = class _HistoryService {
|
|
|
23065
23093
|
this.pluginId = pluginId;
|
|
23066
23094
|
this.cwd = cwd;
|
|
23067
23095
|
this.runtime = runtime;
|
|
23096
|
+
this.pluginAuthToken = options?.pluginAuthToken;
|
|
23068
23097
|
this.bootTimeMs = options?.bootTimeMs ?? Date.now();
|
|
23069
23098
|
}
|
|
23070
23099
|
pluginId;
|
|
@@ -23101,6 +23130,7 @@ var HistoryService = class _HistoryService {
|
|
|
23101
23130
|
*/
|
|
23102
23131
|
static BIRTHTIME_GRACE_MS = 5e3;
|
|
23103
23132
|
runtime;
|
|
23133
|
+
pluginAuthToken;
|
|
23104
23134
|
/** Store rate limit reset info detected from Claude Code output */
|
|
23105
23135
|
setRateLimitReset(reset) {
|
|
23106
23136
|
this._rateLimitReset = reset;
|
|
@@ -23340,11 +23370,15 @@ var HistoryService = class _HistoryService {
|
|
|
23340
23370
|
}
|
|
23341
23371
|
const sessions3 = this.runtime.listResumableSessions(this.cwd);
|
|
23342
23372
|
if (sessions3.length === 0) return;
|
|
23343
|
-
await post(
|
|
23344
|
-
|
|
23345
|
-
|
|
23346
|
-
|
|
23347
|
-
|
|
23373
|
+
await post(
|
|
23374
|
+
"/api/sessions/list",
|
|
23375
|
+
{
|
|
23376
|
+
pluginId: this.pluginId,
|
|
23377
|
+
agentId: this.runtime.id,
|
|
23378
|
+
sessions: sessions3
|
|
23379
|
+
},
|
|
23380
|
+
this.pluginAuthToken
|
|
23381
|
+
);
|
|
23348
23382
|
}
|
|
23349
23383
|
/**
|
|
23350
23384
|
* Read a specific session's full conversation and POST it to the API in batches.
|
|
@@ -23374,10 +23408,10 @@ var HistoryService = class _HistoryService {
|
|
|
23374
23408
|
batchIndex: i,
|
|
23375
23409
|
totalBatches
|
|
23376
23410
|
};
|
|
23377
|
-
let ok = await post("/api/sessions/conversation", body);
|
|
23411
|
+
let ok = await post("/api/sessions/conversation", body, this.pluginAuthToken);
|
|
23378
23412
|
for (let attempt = 0; !ok && attempt < RETRY_DELAYS.length; attempt++) {
|
|
23379
23413
|
await new Promise((r) => setTimeout(r, RETRY_DELAYS[attempt]));
|
|
23380
|
-
ok = await post("/api/sessions/conversation", body);
|
|
23414
|
+
ok = await post("/api/sessions/conversation", body, this.pluginAuthToken);
|
|
23381
23415
|
}
|
|
23382
23416
|
if (!ok) {
|
|
23383
23417
|
throw new Error(`Failed to upload conversation batch ${i + 1}/${totalBatches} after all retries`);
|
|
@@ -23428,7 +23462,7 @@ var HistoryService = class _HistoryService {
|
|
|
23428
23462
|
messages: newMessages,
|
|
23429
23463
|
mode: "append"
|
|
23430
23464
|
};
|
|
23431
|
-
const ok = await post("/api/sessions/conversation", body);
|
|
23465
|
+
const ok = await post("/api/sessions/conversation", body, this.pluginAuthToken);
|
|
23432
23466
|
if (ok) {
|
|
23433
23467
|
const last = newMessages[newMessages.length - 1];
|
|
23434
23468
|
this.lastUploadedUuid.set(sessionId, last.id);
|
|
@@ -24018,7 +24052,9 @@ async function start(requestedAgent) {
|
|
|
24018
24052
|
showInfo("CODEAM_ACP_DISABLED is set \u2014 running the legacy PTY pipeline.");
|
|
24019
24053
|
}
|
|
24020
24054
|
const runtime = createRuntimeStrategy(session.agent);
|
|
24021
|
-
const historySvc = new HistoryService(runtime, pluginId, cwd
|
|
24055
|
+
const historySvc = new HistoryService(runtime, pluginId, cwd, {
|
|
24056
|
+
pluginAuthToken: session.pluginAuthToken
|
|
24057
|
+
});
|
|
24022
24058
|
const keepAliveCtx = {
|
|
24023
24059
|
inCodespace: process.env.CODESPACES === "true",
|
|
24024
24060
|
codespaceName: process.env.CODESPACE_NAME
|
|
@@ -24340,7 +24376,10 @@ async function pair(args2 = []) {
|
|
|
24340
24376
|
capture("pair_timed_out", { agentId, pluginId });
|
|
24341
24377
|
showError("Pairing timed out after 5 minutes. Run codeam pair to try again.");
|
|
24342
24378
|
process.exit(1);
|
|
24343
|
-
}
|
|
24379
|
+
},
|
|
24380
|
+
// SEC crit1 (#8): replay this pairing's secret on the pre-pair
|
|
24381
|
+
// subscription so it passes the command-endpoint gate when enforced.
|
|
24382
|
+
pollSecret
|
|
24344
24383
|
);
|
|
24345
24384
|
process.once("SIGINT", sigintHandler);
|
|
24346
24385
|
});
|
|
@@ -27017,7 +27056,7 @@ function checkChokidar() {
|
|
|
27017
27056
|
}
|
|
27018
27057
|
async function doctor(args2 = []) {
|
|
27019
27058
|
const json = args2.includes("--json");
|
|
27020
|
-
const cliVersion = true ? "2.39.
|
|
27059
|
+
const cliVersion = true ? "2.39.18" : "0.0.0-dev";
|
|
27021
27060
|
const apiBase = resolveApiBaseUrl();
|
|
27022
27061
|
const diagnosticId = (0, import_node_crypto8.randomUUID)();
|
|
27023
27062
|
log.info("doctor", `run id=${diagnosticId} cli=${cliVersion}`);
|
|
@@ -27216,7 +27255,7 @@ async function completion(args2) {
|
|
|
27216
27255
|
// src/commands/version.ts
|
|
27217
27256
|
var import_picocolors13 = __toESM(require("picocolors"));
|
|
27218
27257
|
function version2() {
|
|
27219
|
-
const v = true ? "2.39.
|
|
27258
|
+
const v = true ? "2.39.18" : "unknown";
|
|
27220
27259
|
console.log(`${import_picocolors13.default.bold("codeam-cli")} ${import_picocolors13.default.cyan(v)}`);
|
|
27221
27260
|
}
|
|
27222
27261
|
|
|
@@ -27502,7 +27541,7 @@ function checkForUpdates() {
|
|
|
27502
27541
|
if (process.env.CODEAM_DISABLE_UPDATE_CHECK === "1") return;
|
|
27503
27542
|
if (process.env.CI) return;
|
|
27504
27543
|
if (!process.stdout.isTTY) return;
|
|
27505
|
-
const current = true ? "2.39.
|
|
27544
|
+
const current = true ? "2.39.18" : null;
|
|
27506
27545
|
if (!current) return;
|
|
27507
27546
|
const cache = readCache();
|
|
27508
27547
|
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.18",
|
|
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",
|