codeam-cli 2.39.86 → 2.40.0
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 +16 -0
- package/dist/index.js +138 -35
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -4,6 +4,22 @@ 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.87] — 2026-06-23
|
|
8
|
+
|
|
9
|
+
### Added
|
|
10
|
+
|
|
11
|
+
- **cli:** Link Claude via setup-token (dedicated non-rotating codespace credential)
|
|
12
|
+
|
|
13
|
+
## [2.39.86] — 2026-06-23
|
|
14
|
+
|
|
15
|
+
### Fixed
|
|
16
|
+
|
|
17
|
+
- **cli:** Report credential-invalid on mid-turn auth 401, not just adapter exit
|
|
18
|
+
|
|
19
|
+
### Tests
|
|
20
|
+
|
|
21
|
+
- **cli:** Reproduce-first guard for self-hosted house-agent 401
|
|
22
|
+
|
|
7
23
|
## [2.39.85] — 2026-06-23
|
|
8
24
|
|
|
9
25
|
### Fixed
|
package/dist/index.js
CHANGED
|
@@ -5390,7 +5390,7 @@ function readAnonId() {
|
|
|
5390
5390
|
}
|
|
5391
5391
|
function superProperties() {
|
|
5392
5392
|
return {
|
|
5393
|
-
cliVersion: true ? "2.
|
|
5393
|
+
cliVersion: true ? "2.40.0" : "0.0.0-dev",
|
|
5394
5394
|
nodeVersion: process.version,
|
|
5395
5395
|
platform: process.platform,
|
|
5396
5396
|
arch: process.arch,
|
|
@@ -5549,7 +5549,7 @@ var os4 = __toESM(require("os"));
|
|
|
5549
5549
|
// package.json
|
|
5550
5550
|
var package_default = {
|
|
5551
5551
|
name: "codeam-cli",
|
|
5552
|
-
version: "2.
|
|
5552
|
+
version: "2.40.0",
|
|
5553
5553
|
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.",
|
|
5554
5554
|
type: "commonjs",
|
|
5555
5555
|
main: "dist/index.js",
|
|
@@ -11230,6 +11230,37 @@ function claudeCredentialLocator() {
|
|
|
11230
11230
|
validate: validateClaudeToken
|
|
11231
11231
|
};
|
|
11232
11232
|
}
|
|
11233
|
+
var SETUP_TOKEN_RE = /sk-ant-oat01-[A-Za-z0-9_-]+/;
|
|
11234
|
+
function extractSetupTokenFromOutput(output) {
|
|
11235
|
+
const m = output.match(SETUP_TOKEN_RE);
|
|
11236
|
+
return m ? m[0] : null;
|
|
11237
|
+
}
|
|
11238
|
+
function captureClaudeSetupToken() {
|
|
11239
|
+
return new Promise((resolve7, reject) => {
|
|
11240
|
+
const child = (0, import_node_child_process2.spawn)("claude", ["setup-token"], {
|
|
11241
|
+
stdio: ["inherit", "pipe", "inherit"]
|
|
11242
|
+
});
|
|
11243
|
+
let out2 = "";
|
|
11244
|
+
child.stdout?.on("data", (chunk) => {
|
|
11245
|
+
const s = chunk.toString("utf8");
|
|
11246
|
+
out2 += s;
|
|
11247
|
+
process.stdout.write(s);
|
|
11248
|
+
});
|
|
11249
|
+
child.on("error", (err) => reject(err));
|
|
11250
|
+
child.on("exit", (code) => {
|
|
11251
|
+
const token = extractSetupTokenFromOutput(out2);
|
|
11252
|
+
if (token) {
|
|
11253
|
+
resolve7({ method: "setup_token", credential: token, source: "setup-token" });
|
|
11254
|
+
return;
|
|
11255
|
+
}
|
|
11256
|
+
reject(
|
|
11257
|
+
new Error(
|
|
11258
|
+
`\`claude setup-token\` exited (code=${code ?? "null"}) without producing a token. Complete the browser authorization and paste the code when prompted, then re-run.`
|
|
11259
|
+
)
|
|
11260
|
+
);
|
|
11261
|
+
});
|
|
11262
|
+
});
|
|
11263
|
+
}
|
|
11233
11264
|
function claudeLoginLauncher() {
|
|
11234
11265
|
return {
|
|
11235
11266
|
ensureInstalled: ensureClaudeInstalled,
|
|
@@ -11237,7 +11268,11 @@ function claudeLoginLauncher() {
|
|
|
11237
11268
|
const child = (0, import_node_child_process2.spawn)("claude", [], { stdio: ["pipe", "inherit", "inherit"] });
|
|
11238
11269
|
child.stdin?.write("/login\n");
|
|
11239
11270
|
return child;
|
|
11240
|
-
}
|
|
11271
|
+
},
|
|
11272
|
+
// Preferred path for Claude: a dedicated non-rotating setup-token,
|
|
11273
|
+
// so the codespace credential never shares a refresh chain with the
|
|
11274
|
+
// user's laptop session.
|
|
11275
|
+
captureSetupToken: captureClaudeSetupToken
|
|
11241
11276
|
};
|
|
11242
11277
|
}
|
|
11243
11278
|
|
|
@@ -13448,6 +13483,16 @@ async function link(args2 = []) {
|
|
|
13448
13483
|
process.exit(1);
|
|
13449
13484
|
}
|
|
13450
13485
|
installSpin.stop(`${ctx.displayName} is installed`);
|
|
13486
|
+
if (ctx.launcher.captureSetupToken && !parsed.reuseExisting) {
|
|
13487
|
+
showInfo(
|
|
13488
|
+
`Creating a dedicated ${ctx.displayName} token for your codespaces \u2014 complete the sign-in in your browser and paste the code when prompted.`
|
|
13489
|
+
);
|
|
13490
|
+
console.log("");
|
|
13491
|
+
const setupToken = await ctx.launcher.captureSetupToken();
|
|
13492
|
+
console.log("");
|
|
13493
|
+
await uploadAndSucceed(ctx, paired, pluginId, pluginAuthToken, setupToken);
|
|
13494
|
+
return;
|
|
13495
|
+
}
|
|
13451
13496
|
const existing = await ctx.locator.extract();
|
|
13452
13497
|
if (existing) {
|
|
13453
13498
|
if (await refuseIfStale(ctx, paired, pluginId, pluginAuthToken, existing)) return;
|
|
@@ -17704,7 +17749,7 @@ async function autoUpgradeBeforeCriticalCommand() {
|
|
|
17704
17749
|
if (process.env.NODE_ENV === "test") return;
|
|
17705
17750
|
if (process.env.CODEAM_DISABLE_UPDATE_CHECK === "1") return;
|
|
17706
17751
|
if (process.env.CI) return;
|
|
17707
|
-
const current = true ? "2.
|
|
17752
|
+
const current = true ? "2.40.0" : null;
|
|
17708
17753
|
if (!current) return;
|
|
17709
17754
|
const cache = readCache();
|
|
17710
17755
|
const fresh = cache && Date.now() - cache.fetchedAt < TTL_MS;
|
|
@@ -17721,7 +17766,7 @@ function checkForUpdates() {
|
|
|
17721
17766
|
if (process.env.CODEAM_DISABLE_UPDATE_CHECK === "1") return;
|
|
17722
17767
|
if (process.env.CI) return;
|
|
17723
17768
|
if (!process.stdout.isTTY) return;
|
|
17724
|
-
const current = true ? "2.
|
|
17769
|
+
const current = true ? "2.40.0" : null;
|
|
17725
17770
|
if (!current) return;
|
|
17726
17771
|
const cache = readCache();
|
|
17727
17772
|
const fresh = cache && Date.now() - cache.fetchedAt < TTL_MS;
|
|
@@ -18159,7 +18204,7 @@ var defaultSpawner = (env, cwd, args2 = []) => (0, import_node_child_process14.s
|
|
|
18159
18204
|
detached: false
|
|
18160
18205
|
});
|
|
18161
18206
|
function currentCliVersion() {
|
|
18162
|
-
return true ? "2.
|
|
18207
|
+
return true ? "2.40.0" : null;
|
|
18163
18208
|
}
|
|
18164
18209
|
function runCmd(cmd, args2, timeoutMs) {
|
|
18165
18210
|
return new Promise((resolve7) => {
|
|
@@ -23098,15 +23143,35 @@ var AcpPublisher = class {
|
|
|
23098
23143
|
constructor(opts) {
|
|
23099
23144
|
this.opts = opts;
|
|
23100
23145
|
this.apiBase = opts.apiBaseUrl ?? resolveApiBaseUrl();
|
|
23101
|
-
this.
|
|
23146
|
+
this.token = opts.pluginAuthToken;
|
|
23147
|
+
}
|
|
23148
|
+
opts;
|
|
23149
|
+
apiBase;
|
|
23150
|
+
token;
|
|
23151
|
+
authHeaders() {
|
|
23152
|
+
return {
|
|
23102
23153
|
"Content-Type": "application/json",
|
|
23103
23154
|
"X-Codeam-Protocol-Version": "2.0.0",
|
|
23104
|
-
"X-Plugin-Auth-Token":
|
|
23155
|
+
"X-Plugin-Auth-Token": this.token
|
|
23105
23156
|
};
|
|
23106
23157
|
}
|
|
23107
|
-
|
|
23108
|
-
|
|
23109
|
-
|
|
23158
|
+
/**
|
|
23159
|
+
* POST with the current plugin-auth token. On a 401/403 the token is
|
|
23160
|
+
* stale (JWT_SECRET rotated on deploy, or the session re-paired);
|
|
23161
|
+
* refresh it via the injected callback and retry ONCE. Any other
|
|
23162
|
+
* status (or a refresh that yields no token) returns the first
|
|
23163
|
+
* response unchanged — callers log non-2xx but never throw.
|
|
23164
|
+
*/
|
|
23165
|
+
async postWithReauth(url, payload) {
|
|
23166
|
+
const first = await _transport4.post(url, this.authHeaders(), payload);
|
|
23167
|
+
if (first.statusCode !== 401 && first.statusCode !== 403) return first;
|
|
23168
|
+
if (!this.opts.refreshAuthToken) return first;
|
|
23169
|
+
const fresh = await this.opts.refreshAuthToken();
|
|
23170
|
+
if (!fresh) return first;
|
|
23171
|
+
this.token = fresh;
|
|
23172
|
+
log.info("acpPublisher", `plugin-auth token refreshed after ${first.statusCode}; retrying POST`);
|
|
23173
|
+
return _transport4.post(url, this.authHeaders(), payload);
|
|
23174
|
+
}
|
|
23110
23175
|
/**
|
|
23111
23176
|
* Wrap the body with `sessionId` + `pluginId` at the top level.
|
|
23112
23177
|
* The backend's `PluginAuthGuard` reads both fields from the JSON
|
|
@@ -23137,16 +23202,11 @@ var AcpPublisher = class {
|
|
|
23137
23202
|
async publishOutput(body) {
|
|
23138
23203
|
const url = `${this.apiBase}/api/commands/output`;
|
|
23139
23204
|
try {
|
|
23140
|
-
const { statusCode, body: resBody } = await
|
|
23141
|
-
url,
|
|
23142
|
-
this.headers,
|
|
23143
|
-
this.envelope(body)
|
|
23144
|
-
);
|
|
23205
|
+
const { statusCode, body: resBody } = await this.postWithReauth(url, this.envelope(body));
|
|
23145
23206
|
if (statusCode < 200 || statusCode >= 300) {
|
|
23146
|
-
const tok = this.opts.pluginAuthToken;
|
|
23147
23207
|
log.warn(
|
|
23148
23208
|
"acpPublisher",
|
|
23149
|
-
`output type=${String(body.type)} done=${body.done === true} status=${statusCode} body=${resBody.slice(0, 200)} | sentSessionId=${this.opts.sessionId} sentPluginId=${this.opts.pluginId} tokenLen=${
|
|
23209
|
+
`output type=${String(body.type)} done=${body.done === true} status=${statusCode} body=${resBody.slice(0, 200)} | sentSessionId=${this.opts.sessionId} sentPluginId=${this.opts.pluginId} tokenLen=${this.token.length} tokenHead=${this.token.slice(0, 12)} tokenTail=${this.token.slice(-8)}`
|
|
23150
23210
|
);
|
|
23151
23211
|
}
|
|
23152
23212
|
} catch (err) {
|
|
@@ -23165,9 +23225,8 @@ var AcpPublisher = class {
|
|
|
23165
23225
|
async publishAwaitingAnswer(event) {
|
|
23166
23226
|
const url = `${this.apiBase}/api/sessions/${encodeURIComponent(this.opts.sessionId)}/awaiting-answer`;
|
|
23167
23227
|
try {
|
|
23168
|
-
const { statusCode, body } = await
|
|
23228
|
+
const { statusCode, body } = await this.postWithReauth(
|
|
23169
23229
|
url,
|
|
23170
|
-
this.headers,
|
|
23171
23230
|
this.envelope(event)
|
|
23172
23231
|
);
|
|
23173
23232
|
if (statusCode < 200 || statusCode >= 300) {
|
|
@@ -23199,9 +23258,8 @@ var AcpPublisher = class {
|
|
|
23199
23258
|
async publishStreamingChunk(event) {
|
|
23200
23259
|
const url = `${this.apiBase}/api/sessions/${encodeURIComponent(this.opts.sessionId)}/streaming-chunk`;
|
|
23201
23260
|
try {
|
|
23202
|
-
const { statusCode, body } = await
|
|
23261
|
+
const { statusCode, body } = await this.postWithReauth(
|
|
23203
23262
|
url,
|
|
23204
|
-
this.headers,
|
|
23205
23263
|
this.envelope(event)
|
|
23206
23264
|
);
|
|
23207
23265
|
if (statusCode < 200 || statusCode >= 300) {
|
|
@@ -23236,7 +23294,7 @@ var AcpPublisher = class {
|
|
|
23236
23294
|
sessions: args2.sessions
|
|
23237
23295
|
});
|
|
23238
23296
|
try {
|
|
23239
|
-
const { statusCode, body: resBody } = await
|
|
23297
|
+
const { statusCode, body: resBody } = await this.postWithReauth(url, body);
|
|
23240
23298
|
if (statusCode < 200 || statusCode >= 300) {
|
|
23241
23299
|
log.warn(
|
|
23242
23300
|
"acpPublisher",
|
|
@@ -23271,7 +23329,7 @@ var AcpPublisher = class {
|
|
|
23271
23329
|
mode: "replace"
|
|
23272
23330
|
});
|
|
23273
23331
|
try {
|
|
23274
|
-
const { statusCode, body: resBody } = await
|
|
23332
|
+
const { statusCode, body: resBody } = await this.postWithReauth(url, body);
|
|
23275
23333
|
if (statusCode < 200 || statusCode >= 300) {
|
|
23276
23334
|
log.warn(
|
|
23277
23335
|
"acpPublisher",
|
|
@@ -23402,6 +23460,7 @@ async function runOnboardingTurn(opts) {
|
|
|
23402
23460
|
|
|
23403
23461
|
// src/agents/acp/promptEcho.ts
|
|
23404
23462
|
var MAX_PROMPT_CHARS = 200;
|
|
23463
|
+
var MAX_AGENT_REPLY_CHARS = 280;
|
|
23405
23464
|
function formatPromptEchoLine(payload) {
|
|
23406
23465
|
const rawText = (payload.prompt ?? "").replace(/\s+/g, " ").trim();
|
|
23407
23466
|
const imageCount = (payload.files ?? []).filter(
|
|
@@ -23418,6 +23477,12 @@ function formatPromptEchoLine(payload) {
|
|
|
23418
23477
|
}
|
|
23419
23478
|
return `\u203A ${parts.join(" ")}`;
|
|
23420
23479
|
}
|
|
23480
|
+
function formatAgentReplyLine(text) {
|
|
23481
|
+
const collapsed = (text ?? "").replace(/\s+/g, " ").trim();
|
|
23482
|
+
if (collapsed.length === 0) return "";
|
|
23483
|
+
const truncated = collapsed.length > MAX_AGENT_REPLY_CHARS ? collapsed.slice(0, MAX_AGENT_REPLY_CHARS) + "\u2026" : collapsed;
|
|
23484
|
+
return `\u2039 Agent: ${truncated}`;
|
|
23485
|
+
}
|
|
23421
23486
|
|
|
23422
23487
|
// src/agents/acp/mappers.ts
|
|
23423
23488
|
var import_node_crypto6 = require("crypto");
|
|
@@ -24129,6 +24194,32 @@ function baselineKey(entry) {
|
|
|
24129
24194
|
return `${entry.repoPath}|${entry.filePath}`;
|
|
24130
24195
|
}
|
|
24131
24196
|
|
|
24197
|
+
// src/agents/acp/withTimeout.ts
|
|
24198
|
+
function withTimeout(p2, ms) {
|
|
24199
|
+
return new Promise((resolve7) => {
|
|
24200
|
+
let settled = false;
|
|
24201
|
+
const timer = setTimeout(() => {
|
|
24202
|
+
if (settled) return;
|
|
24203
|
+
settled = true;
|
|
24204
|
+
resolve7(void 0);
|
|
24205
|
+
}, ms);
|
|
24206
|
+
p2.then(
|
|
24207
|
+
(v) => {
|
|
24208
|
+
if (settled) return;
|
|
24209
|
+
settled = true;
|
|
24210
|
+
clearTimeout(timer);
|
|
24211
|
+
resolve7(v);
|
|
24212
|
+
},
|
|
24213
|
+
() => {
|
|
24214
|
+
if (settled) return;
|
|
24215
|
+
settled = true;
|
|
24216
|
+
clearTimeout(timer);
|
|
24217
|
+
resolve7(void 0);
|
|
24218
|
+
}
|
|
24219
|
+
);
|
|
24220
|
+
});
|
|
24221
|
+
}
|
|
24222
|
+
|
|
24132
24223
|
// src/agents/acp/runner.ts
|
|
24133
24224
|
var StreamingState = class {
|
|
24134
24225
|
constructor(publisher) {
|
|
@@ -24563,7 +24654,8 @@ async function runAcpSession(opts) {
|
|
|
24563
24654
|
const publisher = new AcpPublisher({
|
|
24564
24655
|
sessionId: opts.sessionId,
|
|
24565
24656
|
pluginId: opts.pluginId,
|
|
24566
|
-
pluginAuthToken: opts.pluginAuthToken
|
|
24657
|
+
pluginAuthToken: opts.pluginAuthToken,
|
|
24658
|
+
refreshAuthToken: () => fetchCurrentPluginAuthToken(opts.sessionId, opts.pluginId, opts.pollSecret)
|
|
24567
24659
|
});
|
|
24568
24660
|
const streaming = new StreamingState(publisher);
|
|
24569
24661
|
const recentStderr = [];
|
|
@@ -24637,14 +24729,20 @@ async function runAcpSession(opts) {
|
|
|
24637
24729
|
if (authFail) {
|
|
24638
24730
|
void reportCredentialInvalid(opts);
|
|
24639
24731
|
}
|
|
24640
|
-
void
|
|
24641
|
-
|
|
24642
|
-
|
|
24643
|
-
|
|
24644
|
-
|
|
24645
|
-
|
|
24646
|
-
|
|
24647
|
-
|
|
24732
|
+
void (async () => {
|
|
24733
|
+
await withTimeout(
|
|
24734
|
+
(async () => {
|
|
24735
|
+
await streaming.closeAll();
|
|
24736
|
+
await publisher.publishOutput({
|
|
24737
|
+
type: "text",
|
|
24738
|
+
content: authFail ? AUTH_FAILURE_MESSAGE : `Agent adapter exited unexpectedly (code=${code ?? "null"} signal=${signal ?? "null"}).`,
|
|
24739
|
+
done: true
|
|
24740
|
+
});
|
|
24741
|
+
})(),
|
|
24742
|
+
5e3
|
|
24743
|
+
);
|
|
24744
|
+
process.exit(1);
|
|
24745
|
+
})();
|
|
24648
24746
|
}
|
|
24649
24747
|
});
|
|
24650
24748
|
showInfo(`Starting ${opts.agent} via ACP adapter (${opts.adapter.requiresAgentBinary})\u2026`);
|
|
@@ -24806,6 +24904,10 @@ async function handleCommand(cmd, client2, relay, acpSessionId, models, streamin
|
|
|
24806
24904
|
const reply = await client2.prompt(blocks);
|
|
24807
24905
|
const finalText = streaming.getCurrentText();
|
|
24808
24906
|
await streaming.closeTurnWithInteractiveDetection();
|
|
24907
|
+
const replyLine = formatAgentReplyLine(finalText);
|
|
24908
|
+
if (replyLine.length > 0) {
|
|
24909
|
+
showInfo(replyLine);
|
|
24910
|
+
}
|
|
24809
24911
|
history.appendAgentReply(finalText);
|
|
24810
24912
|
void history.flush();
|
|
24811
24913
|
turnFiles.flushTurn().catch((err) => {
|
|
@@ -26308,6 +26410,7 @@ async function start(requestedAgent) {
|
|
|
26308
26410
|
adapter,
|
|
26309
26411
|
cwd,
|
|
26310
26412
|
getBeads,
|
|
26413
|
+
pollSecret: session.pollSecret,
|
|
26311
26414
|
// AUTO mode for headless, mobile-driven sessions: no human at the box
|
|
26312
26415
|
// to answer permission prompts, so auto-approve them rather than stall
|
|
26313
26416
|
// the turn (the agent-agnostic equivalent of
|
|
@@ -28979,7 +29082,7 @@ function checkChokidar() {
|
|
|
28979
29082
|
}
|
|
28980
29083
|
async function doctor(args2 = []) {
|
|
28981
29084
|
const json = args2.includes("--json");
|
|
28982
|
-
const cliVersion = true ? "2.
|
|
29085
|
+
const cliVersion = true ? "2.40.0" : "0.0.0-dev";
|
|
28983
29086
|
const apiBase2 = resolveApiBaseUrl();
|
|
28984
29087
|
const diagnosticId = (0, import_node_crypto8.randomUUID)();
|
|
28985
29088
|
log.info("doctor", `run id=${diagnosticId} cli=${cliVersion}`);
|
|
@@ -29178,7 +29281,7 @@ async function completion(args2) {
|
|
|
29178
29281
|
// src/commands/version.ts
|
|
29179
29282
|
var import_picocolors14 = __toESM(require("picocolors"));
|
|
29180
29283
|
function version2() {
|
|
29181
|
-
const v = true ? "2.
|
|
29284
|
+
const v = true ? "2.40.0" : "unknown";
|
|
29182
29285
|
console.log(`${import_picocolors14.default.bold("codeam-cli")} ${import_picocolors14.default.cyan(v)}`);
|
|
29183
29286
|
}
|
|
29184
29287
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "codeam-cli",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.40.0",
|
|
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",
|