codeam-cli 2.39.87 → 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 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.87] — 2026-06-23
8
+
9
+ ### Added
10
+
11
+ - **cli:** Link Claude via setup-token (dedicated non-rotating codespace credential)
12
+
7
13
  ## [2.39.86] — 2026-06-23
8
14
 
9
15
  ### 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.39.87" : "0.0.0-dev",
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.39.87",
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",
@@ -17749,7 +17749,7 @@ async function autoUpgradeBeforeCriticalCommand() {
17749
17749
  if (process.env.NODE_ENV === "test") return;
17750
17750
  if (process.env.CODEAM_DISABLE_UPDATE_CHECK === "1") return;
17751
17751
  if (process.env.CI) return;
17752
- const current = true ? "2.39.87" : null;
17752
+ const current = true ? "2.40.0" : null;
17753
17753
  if (!current) return;
17754
17754
  const cache = readCache();
17755
17755
  const fresh = cache && Date.now() - cache.fetchedAt < TTL_MS;
@@ -17766,7 +17766,7 @@ function checkForUpdates() {
17766
17766
  if (process.env.CODEAM_DISABLE_UPDATE_CHECK === "1") return;
17767
17767
  if (process.env.CI) return;
17768
17768
  if (!process.stdout.isTTY) return;
17769
- const current = true ? "2.39.87" : null;
17769
+ const current = true ? "2.40.0" : null;
17770
17770
  if (!current) return;
17771
17771
  const cache = readCache();
17772
17772
  const fresh = cache && Date.now() - cache.fetchedAt < TTL_MS;
@@ -18204,7 +18204,7 @@ var defaultSpawner = (env, cwd, args2 = []) => (0, import_node_child_process14.s
18204
18204
  detached: false
18205
18205
  });
18206
18206
  function currentCliVersion() {
18207
- return true ? "2.39.87" : null;
18207
+ return true ? "2.40.0" : null;
18208
18208
  }
18209
18209
  function runCmd(cmd, args2, timeoutMs) {
18210
18210
  return new Promise((resolve7) => {
@@ -23143,15 +23143,35 @@ var AcpPublisher = class {
23143
23143
  constructor(opts) {
23144
23144
  this.opts = opts;
23145
23145
  this.apiBase = opts.apiBaseUrl ?? resolveApiBaseUrl();
23146
- this.headers = {
23146
+ this.token = opts.pluginAuthToken;
23147
+ }
23148
+ opts;
23149
+ apiBase;
23150
+ token;
23151
+ authHeaders() {
23152
+ return {
23147
23153
  "Content-Type": "application/json",
23148
23154
  "X-Codeam-Protocol-Version": "2.0.0",
23149
- "X-Plugin-Auth-Token": opts.pluginAuthToken
23155
+ "X-Plugin-Auth-Token": this.token
23150
23156
  };
23151
23157
  }
23152
- opts;
23153
- apiBase;
23154
- headers;
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
+ }
23155
23175
  /**
23156
23176
  * Wrap the body with `sessionId` + `pluginId` at the top level.
23157
23177
  * The backend's `PluginAuthGuard` reads both fields from the JSON
@@ -23182,16 +23202,11 @@ var AcpPublisher = class {
23182
23202
  async publishOutput(body) {
23183
23203
  const url = `${this.apiBase}/api/commands/output`;
23184
23204
  try {
23185
- const { statusCode, body: resBody } = await _transport4.post(
23186
- url,
23187
- this.headers,
23188
- this.envelope(body)
23189
- );
23205
+ const { statusCode, body: resBody } = await this.postWithReauth(url, this.envelope(body));
23190
23206
  if (statusCode < 200 || statusCode >= 300) {
23191
- const tok = this.opts.pluginAuthToken;
23192
23207
  log.warn(
23193
23208
  "acpPublisher",
23194
- `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=${tok.length} tokenHead=${tok.slice(0, 12)} tokenTail=${tok.slice(-8)}`
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)}`
23195
23210
  );
23196
23211
  }
23197
23212
  } catch (err) {
@@ -23210,9 +23225,8 @@ var AcpPublisher = class {
23210
23225
  async publishAwaitingAnswer(event) {
23211
23226
  const url = `${this.apiBase}/api/sessions/${encodeURIComponent(this.opts.sessionId)}/awaiting-answer`;
23212
23227
  try {
23213
- const { statusCode, body } = await _transport4.post(
23228
+ const { statusCode, body } = await this.postWithReauth(
23214
23229
  url,
23215
- this.headers,
23216
23230
  this.envelope(event)
23217
23231
  );
23218
23232
  if (statusCode < 200 || statusCode >= 300) {
@@ -23244,9 +23258,8 @@ var AcpPublisher = class {
23244
23258
  async publishStreamingChunk(event) {
23245
23259
  const url = `${this.apiBase}/api/sessions/${encodeURIComponent(this.opts.sessionId)}/streaming-chunk`;
23246
23260
  try {
23247
- const { statusCode, body } = await _transport4.post(
23261
+ const { statusCode, body } = await this.postWithReauth(
23248
23262
  url,
23249
- this.headers,
23250
23263
  this.envelope(event)
23251
23264
  );
23252
23265
  if (statusCode < 200 || statusCode >= 300) {
@@ -23281,7 +23294,7 @@ var AcpPublisher = class {
23281
23294
  sessions: args2.sessions
23282
23295
  });
23283
23296
  try {
23284
- const { statusCode, body: resBody } = await _transport4.post(url, this.headers, body);
23297
+ const { statusCode, body: resBody } = await this.postWithReauth(url, body);
23285
23298
  if (statusCode < 200 || statusCode >= 300) {
23286
23299
  log.warn(
23287
23300
  "acpPublisher",
@@ -23316,7 +23329,7 @@ var AcpPublisher = class {
23316
23329
  mode: "replace"
23317
23330
  });
23318
23331
  try {
23319
- const { statusCode, body: resBody } = await _transport4.post(url, this.headers, body);
23332
+ const { statusCode, body: resBody } = await this.postWithReauth(url, body);
23320
23333
  if (statusCode < 200 || statusCode >= 300) {
23321
23334
  log.warn(
23322
23335
  "acpPublisher",
@@ -23447,6 +23460,7 @@ async function runOnboardingTurn(opts) {
23447
23460
 
23448
23461
  // src/agents/acp/promptEcho.ts
23449
23462
  var MAX_PROMPT_CHARS = 200;
23463
+ var MAX_AGENT_REPLY_CHARS = 280;
23450
23464
  function formatPromptEchoLine(payload) {
23451
23465
  const rawText = (payload.prompt ?? "").replace(/\s+/g, " ").trim();
23452
23466
  const imageCount = (payload.files ?? []).filter(
@@ -23463,6 +23477,12 @@ function formatPromptEchoLine(payload) {
23463
23477
  }
23464
23478
  return `\u203A ${parts.join(" ")}`;
23465
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
+ }
23466
23486
 
23467
23487
  // src/agents/acp/mappers.ts
23468
23488
  var import_node_crypto6 = require("crypto");
@@ -24174,6 +24194,32 @@ function baselineKey(entry) {
24174
24194
  return `${entry.repoPath}|${entry.filePath}`;
24175
24195
  }
24176
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
+
24177
24223
  // src/agents/acp/runner.ts
24178
24224
  var StreamingState = class {
24179
24225
  constructor(publisher) {
@@ -24608,7 +24654,8 @@ async function runAcpSession(opts) {
24608
24654
  const publisher = new AcpPublisher({
24609
24655
  sessionId: opts.sessionId,
24610
24656
  pluginId: opts.pluginId,
24611
- pluginAuthToken: opts.pluginAuthToken
24657
+ pluginAuthToken: opts.pluginAuthToken,
24658
+ refreshAuthToken: () => fetchCurrentPluginAuthToken(opts.sessionId, opts.pluginId, opts.pollSecret)
24612
24659
  });
24613
24660
  const streaming = new StreamingState(publisher);
24614
24661
  const recentStderr = [];
@@ -24682,14 +24729,20 @@ async function runAcpSession(opts) {
24682
24729
  if (authFail) {
24683
24730
  void reportCredentialInvalid(opts);
24684
24731
  }
24685
- void streaming.closeAll().then(
24686
- () => publisher.publishOutput({
24687
- type: "text",
24688
- content: authFail ? AUTH_FAILURE_MESSAGE : `Agent adapter exited unexpectedly (code=${code ?? "null"} signal=${signal ?? "null"}).`,
24689
- done: true
24690
- })
24691
- );
24692
- process.exit(1);
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
+ })();
24693
24746
  }
24694
24747
  });
24695
24748
  showInfo(`Starting ${opts.agent} via ACP adapter (${opts.adapter.requiresAgentBinary})\u2026`);
@@ -24851,6 +24904,10 @@ async function handleCommand(cmd, client2, relay, acpSessionId, models, streamin
24851
24904
  const reply = await client2.prompt(blocks);
24852
24905
  const finalText = streaming.getCurrentText();
24853
24906
  await streaming.closeTurnWithInteractiveDetection();
24907
+ const replyLine = formatAgentReplyLine(finalText);
24908
+ if (replyLine.length > 0) {
24909
+ showInfo(replyLine);
24910
+ }
24854
24911
  history.appendAgentReply(finalText);
24855
24912
  void history.flush();
24856
24913
  turnFiles.flushTurn().catch((err) => {
@@ -26353,6 +26410,7 @@ async function start(requestedAgent) {
26353
26410
  adapter,
26354
26411
  cwd,
26355
26412
  getBeads,
26413
+ pollSecret: session.pollSecret,
26356
26414
  // AUTO mode for headless, mobile-driven sessions: no human at the box
26357
26415
  // to answer permission prompts, so auto-approve them rather than stall
26358
26416
  // the turn (the agent-agnostic equivalent of
@@ -29024,7 +29082,7 @@ function checkChokidar() {
29024
29082
  }
29025
29083
  async function doctor(args2 = []) {
29026
29084
  const json = args2.includes("--json");
29027
- const cliVersion = true ? "2.39.87" : "0.0.0-dev";
29085
+ const cliVersion = true ? "2.40.0" : "0.0.0-dev";
29028
29086
  const apiBase2 = resolveApiBaseUrl();
29029
29087
  const diagnosticId = (0, import_node_crypto8.randomUUID)();
29030
29088
  log.info("doctor", `run id=${diagnosticId} cli=${cliVersion}`);
@@ -29223,7 +29281,7 @@ async function completion(args2) {
29223
29281
  // src/commands/version.ts
29224
29282
  var import_picocolors14 = __toESM(require("picocolors"));
29225
29283
  function version2() {
29226
- const v = true ? "2.39.87" : "unknown";
29284
+ const v = true ? "2.40.0" : "unknown";
29227
29285
  console.log(`${import_picocolors14.default.bold("codeam-cli")} ${import_picocolors14.default.cyan(v)}`);
29228
29286
  }
29229
29287
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "codeam-cli",
3
- "version": "2.39.87",
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",