codeam-cli 2.39.12 → 2.39.14
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 +44 -19
- 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.13] — 2026-06-14
|
|
8
|
+
|
|
9
|
+
### Added
|
|
10
|
+
|
|
11
|
+
- **cli:** Proof-of-possession secret for /status + /reconnect (SEC crit1)
|
|
12
|
+
|
|
13
|
+
## [2.39.12] — 2026-06-13
|
|
14
|
+
|
|
15
|
+
### Fixed
|
|
16
|
+
|
|
17
|
+
- **vsc-plugin,jetbrains-plugin:** Auto-reconnect the last session on IDE startup
|
|
18
|
+
|
|
7
19
|
## [2.39.11] — 2026-06-13
|
|
8
20
|
|
|
9
21
|
### Fixed
|
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.14",
|
|
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",
|
|
@@ -694,7 +694,7 @@ var _execSeam = {
|
|
|
694
694
|
// src/services/pairing.service.ts
|
|
695
695
|
var API_BASE = resolveApiBaseUrl();
|
|
696
696
|
var REQUEST_CODE_TIMEOUT_MS = 1e4;
|
|
697
|
-
async function requestCode(pluginId) {
|
|
697
|
+
async function requestCode(pluginId, pluginSecretHash) {
|
|
698
698
|
try {
|
|
699
699
|
const runtime = process.env.CODESPACES === "true" ? "github-codespaces" : "local";
|
|
700
700
|
const codespaceName = process.env.CODESPACE_NAME;
|
|
@@ -706,7 +706,11 @@ async function requestCode(pluginId) {
|
|
|
706
706
|
hostname: os2.hostname(),
|
|
707
707
|
runtime,
|
|
708
708
|
branch,
|
|
709
|
-
...codespaceName ? { codespaceName } : {}
|
|
709
|
+
...codespaceName ? { codespaceName } : {},
|
|
710
|
+
// SEC: proof-of-possession enrollment. Backend carries this hash
|
|
711
|
+
// onto the session and requires the raw secret on /status +
|
|
712
|
+
// /reconnect. Older backends ignore the unknown field.
|
|
713
|
+
...pluginSecretHash ? { pluginSecretHash } : {}
|
|
710
714
|
});
|
|
711
715
|
let timer;
|
|
712
716
|
const timeoutSentinel = /* @__PURE__ */ Symbol("request-code-timeout");
|
|
@@ -738,11 +742,14 @@ async function requestCode(pluginId) {
|
|
|
738
742
|
return { ok: false, reason: "network" };
|
|
739
743
|
}
|
|
740
744
|
}
|
|
741
|
-
async function fetchCurrentPluginAuthToken(sessionId, pluginId) {
|
|
745
|
+
async function fetchCurrentPluginAuthToken(sessionId, pluginId, pollSecret) {
|
|
742
746
|
try {
|
|
743
747
|
const result = await _transport.postJson(
|
|
744
748
|
`${API_BASE}/api/pairing/reconnect`,
|
|
745
|
-
{ sessionId, pluginId }
|
|
749
|
+
{ sessionId, pluginId },
|
|
750
|
+
// SEC: prove possession so the gated /reconnect returns the token.
|
|
751
|
+
// Omitted for legacy sessions (no secret) → backend legacy path.
|
|
752
|
+
pollSecret ? { "X-Plugin-Poll-Secret": pollSecret } : void 0
|
|
746
753
|
);
|
|
747
754
|
const data = result?.data;
|
|
748
755
|
if (!data?.paired) return null;
|
|
@@ -935,7 +942,7 @@ function makeHttpError(statusCode, retryAfterHeader, responseBody) {
|
|
|
935
942
|
if (typeof retryAfterSeconds === "number") err.retryAfterSeconds = retryAfterSeconds;
|
|
936
943
|
return err;
|
|
937
944
|
}
|
|
938
|
-
async function _postJson(url, body) {
|
|
945
|
+
async function _postJson(url, body, extraHeaders) {
|
|
939
946
|
return new Promise((resolve7, reject) => {
|
|
940
947
|
const data = JSON.stringify(body);
|
|
941
948
|
const u2 = new URL(url);
|
|
@@ -949,7 +956,8 @@ async function _postJson(url, body) {
|
|
|
949
956
|
headers: {
|
|
950
957
|
"Content-Type": "application/json",
|
|
951
958
|
"Content-Length": Buffer.byteLength(data),
|
|
952
|
-
...vercelBypassHeader()
|
|
959
|
+
...vercelBypassHeader(),
|
|
960
|
+
...extraHeaders ?? {}
|
|
953
961
|
},
|
|
954
962
|
timeout: 1e4
|
|
955
963
|
},
|
|
@@ -5900,7 +5908,7 @@ function readAnonId() {
|
|
|
5900
5908
|
}
|
|
5901
5909
|
function superProperties() {
|
|
5902
5910
|
return {
|
|
5903
|
-
cliVersion: true ? "2.39.
|
|
5911
|
+
cliVersion: true ? "2.39.14" : "0.0.0-dev",
|
|
5904
5912
|
nodeVersion: process.version,
|
|
5905
5913
|
platform: process.platform,
|
|
5906
5914
|
arch: process.arch,
|
|
@@ -23938,7 +23946,11 @@ async function start(requestedAgent) {
|
|
|
23938
23946
|
requestedAgent: requestedAgent ?? null
|
|
23939
23947
|
});
|
|
23940
23948
|
const cwd = process.cwd();
|
|
23941
|
-
const refreshed = await fetchCurrentPluginAuthToken(
|
|
23949
|
+
const refreshed = await fetchCurrentPluginAuthToken(
|
|
23950
|
+
session.id,
|
|
23951
|
+
pluginId,
|
|
23952
|
+
session.pollSecret
|
|
23953
|
+
);
|
|
23942
23954
|
if (refreshed && refreshed !== session.pluginAuthToken) {
|
|
23943
23955
|
addSession({ ...session, pluginAuthToken: refreshed });
|
|
23944
23956
|
session.pluginAuthToken = refreshed;
|
|
@@ -24223,10 +24235,12 @@ async function pair(args2 = []) {
|
|
|
24223
24235
|
const agentId = dryRun ? flagAgent ?? config.preferredAgent ?? "claude" : flagAgent ?? await promptForAgent(config.preferredAgent ?? "claude");
|
|
24224
24236
|
showIntro();
|
|
24225
24237
|
const pluginId = (0, import_crypto6.randomUUID)();
|
|
24238
|
+
const pollSecret = (0, import_crypto6.randomBytes)(32).toString("base64url");
|
|
24239
|
+
const pluginSecretHash = (0, import_crypto6.createHash)("sha256").update(pollSecret).digest("hex");
|
|
24226
24240
|
capture("pair_started", { agentId, pluginId, dryRun });
|
|
24227
24241
|
const spin = dist_exports.spinner();
|
|
24228
24242
|
spin.start("Requesting pairing code...");
|
|
24229
|
-
const result = await requestCode(pluginId);
|
|
24243
|
+
const result = await requestCode(pluginId, pluginSecretHash);
|
|
24230
24244
|
if (!result.ok) {
|
|
24231
24245
|
spin.stop("Failed");
|
|
24232
24246
|
if (result.reason === "rate-limited") {
|
|
@@ -24289,6 +24303,9 @@ async function pair(args2 = []) {
|
|
|
24289
24303
|
plan: info.plan,
|
|
24290
24304
|
pairedAt: Date.now(),
|
|
24291
24305
|
pluginAuthToken: info.pluginAuthToken,
|
|
24306
|
+
// SEC: persist the PoP secret so boot-time /reconnect can prove
|
|
24307
|
+
// possession and refresh the token on the gated endpoint.
|
|
24308
|
+
pollSecret,
|
|
24292
24309
|
agent: agentId
|
|
24293
24310
|
});
|
|
24294
24311
|
saveCliConfig({ ...loadCliConfig(), preferredAgent: agentId });
|
|
@@ -24588,7 +24605,7 @@ function networkError(msg, cause) {
|
|
|
24588
24605
|
if (cause !== void 0) err.cause = cause;
|
|
24589
24606
|
return err;
|
|
24590
24607
|
}
|
|
24591
|
-
async function claimOnce(token, pluginId) {
|
|
24608
|
+
async function claimOnce(token, pluginId, pluginSecretHash) {
|
|
24592
24609
|
const url = `${API_BASE10}/api/pairing/claim-auto-token`;
|
|
24593
24610
|
const body = {
|
|
24594
24611
|
token,
|
|
@@ -24600,7 +24617,11 @@ async function claimOnce(token, pluginId) {
|
|
|
24600
24617
|
// Current git branch of the codespace's working directory, so the
|
|
24601
24618
|
// backend can populate `PairedSession.branch` for the codespace pair.
|
|
24602
24619
|
// `null` when detached HEAD / not a git repo.
|
|
24603
|
-
branch: detectCurrentBranch()
|
|
24620
|
+
branch: detectCurrentBranch(),
|
|
24621
|
+
// SEC crit1 (#813): enroll the PoP hash so /status + /reconnect for
|
|
24622
|
+
// this codespace session require the raw secret. Older backends
|
|
24623
|
+
// ignore the unknown field.
|
|
24624
|
+
...pluginSecretHash ? { pluginSecretHash } : {}
|
|
24604
24625
|
};
|
|
24605
24626
|
const controller = new AbortController();
|
|
24606
24627
|
const timer = setTimeout(() => controller.abort(), CLAIM_TIMEOUT_MS);
|
|
@@ -24637,14 +24658,14 @@ async function claimOnce(token, pluginId) {
|
|
|
24637
24658
|
}
|
|
24638
24659
|
return ok.data;
|
|
24639
24660
|
}
|
|
24640
|
-
async function claim(token, pluginId) {
|
|
24661
|
+
async function claim(token, pluginId, pluginSecretHash) {
|
|
24641
24662
|
try {
|
|
24642
|
-
return await claimOnce(token, pluginId);
|
|
24663
|
+
return await claimOnce(token, pluginId, pluginSecretHash);
|
|
24643
24664
|
} catch (err) {
|
|
24644
24665
|
if (err.code !== "NETWORK") throw err;
|
|
24645
24666
|
await new Promise((r) => setTimeout(r, RETRY_BACKOFF_MS3));
|
|
24646
24667
|
try {
|
|
24647
|
-
return await claimOnce(token, pluginId);
|
|
24668
|
+
return await claimOnce(token, pluginId, pluginSecretHash);
|
|
24648
24669
|
} catch (retryErr) {
|
|
24649
24670
|
const netErr = retryErr;
|
|
24650
24671
|
fail(`Auto-pair failed (NETWORK): ${netErr.message}`);
|
|
@@ -24700,9 +24721,11 @@ async function pairAuto(args2) {
|
|
|
24700
24721
|
}
|
|
24701
24722
|
const token = readTokenFromArgs(args2);
|
|
24702
24723
|
const pluginId = (0, import_crypto7.randomUUID)();
|
|
24724
|
+
const pollSecret = (0, import_crypto7.randomBytes)(32).toString("base64url");
|
|
24725
|
+
const pluginSecretHash = (0, import_crypto7.createHash)("sha256").update(pollSecret).digest("hex");
|
|
24703
24726
|
capture("pair_auto_started", { pluginId });
|
|
24704
24727
|
console.log(" Claiming pairing token\u2026");
|
|
24705
|
-
const claimed = await claim(token, pluginId);
|
|
24728
|
+
const claimed = await claim(token, pluginId, pluginSecretHash);
|
|
24706
24729
|
if (!isKnownAgentId(claimed.agent)) {
|
|
24707
24730
|
fail(
|
|
24708
24731
|
`agent "${claimed.agent}" is not supported in this codeam-cli version. Upgrade with 'npm i -g codeam-cli@latest'.`
|
|
@@ -24716,6 +24739,8 @@ async function pairAuto(args2) {
|
|
|
24716
24739
|
plan: claimed.user.plan,
|
|
24717
24740
|
pairedAt: Date.now(),
|
|
24718
24741
|
pluginAuthToken: claimed.pluginAuthToken,
|
|
24742
|
+
// SEC crit1 (#813): persist so boot-time /reconnect proves possession.
|
|
24743
|
+
pollSecret,
|
|
24719
24744
|
agent: claimed.agent
|
|
24720
24745
|
});
|
|
24721
24746
|
identifyUser({
|
|
@@ -26992,7 +27017,7 @@ function checkChokidar() {
|
|
|
26992
27017
|
}
|
|
26993
27018
|
async function doctor(args2 = []) {
|
|
26994
27019
|
const json = args2.includes("--json");
|
|
26995
|
-
const cliVersion = true ? "2.39.
|
|
27020
|
+
const cliVersion = true ? "2.39.14" : "0.0.0-dev";
|
|
26996
27021
|
const apiBase = resolveApiBaseUrl();
|
|
26997
27022
|
const diagnosticId = (0, import_node_crypto8.randomUUID)();
|
|
26998
27023
|
log.info("doctor", `run id=${diagnosticId} cli=${cliVersion}`);
|
|
@@ -27191,7 +27216,7 @@ async function completion(args2) {
|
|
|
27191
27216
|
// src/commands/version.ts
|
|
27192
27217
|
var import_picocolors13 = __toESM(require("picocolors"));
|
|
27193
27218
|
function version2() {
|
|
27194
|
-
const v = true ? "2.39.
|
|
27219
|
+
const v = true ? "2.39.14" : "unknown";
|
|
27195
27220
|
console.log(`${import_picocolors13.default.bold("codeam-cli")} ${import_picocolors13.default.cyan(v)}`);
|
|
27196
27221
|
}
|
|
27197
27222
|
|
|
@@ -27477,7 +27502,7 @@ function checkForUpdates() {
|
|
|
27477
27502
|
if (process.env.CODEAM_DISABLE_UPDATE_CHECK === "1") return;
|
|
27478
27503
|
if (process.env.CI) return;
|
|
27479
27504
|
if (!process.stdout.isTTY) return;
|
|
27480
|
-
const current = true ? "2.39.
|
|
27505
|
+
const current = true ? "2.39.14" : null;
|
|
27481
27506
|
if (!current) return;
|
|
27482
27507
|
const cache = readCache();
|
|
27483
27508
|
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.14",
|
|
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",
|