codeam-cli 2.29.0 → 2.31.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.
Files changed (3) hide show
  1. package/CHANGELOG.md +19 -0
  2. package/dist/index.js +243 -116
  3. package/package.json +1 -1
package/CHANGELOG.md CHANGED
@@ -4,6 +4,25 @@ 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.30.0] — 2026-06-06
8
+
9
+ ### Added
10
+
11
+ - **vsc-plugin:** Banner recommends update when marketplace ships a fix
12
+ - **jetbrains-plugin:** Banner recommends update when marketplace ships a fix
13
+
14
+ ### Fixed
15
+
16
+ - **cli:** Forward mobile image attachments to ACP as image blocks (QA #290)
17
+ - **cli, vsc-plugin:** Echo mobile prompts + clear Reconnecting UX (QA #287/#291)
18
+ - **cli:** Replace pair pollStatus with SSE pair_completed event (QA #285)
19
+
20
+ ## [2.29.0] — 2026-06-06
21
+
22
+ ### Added
23
+
24
+ - **cli:** Forward originator HMAC to /api/pairing/code for QR-ready SSE
25
+
7
26
  ## [2.28.1] — 2026-06-06
8
27
 
9
28
  ### 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.29.0",
501
+ version: "2.31.0",
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",
@@ -682,18 +682,10 @@ var _execSeam = {
682
682
  }
683
683
  };
684
684
 
685
- // src/lib/poll-delay.ts
686
- var MAX_DELAY_MS = 3e4;
687
- function computePollDelay({ baseMs, failures }) {
688
- const exp = Math.min(MAX_DELAY_MS, baseMs * Math.pow(2, failures));
689
- const jitter = exp * (0.9 + Math.random() * 0.2);
690
- return Math.round(jitter);
691
- }
692
-
693
685
  // src/services/pairing.service.ts
694
686
  var API_BASE = resolveApiBaseUrl();
695
687
  var REQUEST_CODE_TIMEOUT_MS = 1e4;
696
- async function requestCode(pluginId, options = {}) {
688
+ async function requestCode(pluginId) {
697
689
  try {
698
690
  const runtime = process.env.CODESPACES === "true" ? "github-codespaces" : "local";
699
691
  const codespaceName = process.env.CODESPACE_NAME;
@@ -705,12 +697,7 @@ async function requestCode(pluginId, options = {}) {
705
697
  hostname: os2.hostname(),
706
698
  runtime,
707
699
  branch,
708
- ...codespaceName ? { codespaceName } : {},
709
- ...options.originatorSessionId && options.originatorPluginId && options.originatorAuthToken ? {
710
- originatorSessionId: options.originatorSessionId,
711
- originatorPluginId: options.originatorPluginId,
712
- originatorAuthToken: options.originatorAuthToken
713
- } : {}
700
+ ...codespaceName ? { codespaceName } : {}
714
701
  });
715
702
  let timer;
716
703
  const timeoutSentinel = /* @__PURE__ */ Symbol("request-code-timeout");
@@ -741,59 +728,6 @@ async function fetchCurrentPluginAuthToken(sessionId, pluginId) {
741
728
  return null;
742
729
  }
743
730
  }
744
- function pollStatus(pluginId, onPaired, onTimeout) {
745
- let stopped = false;
746
- let pollTimer = null;
747
- let consecutiveFailures = 0;
748
- const tick = async () => {
749
- if (stopped) return;
750
- try {
751
- const result = await _transport.getJson(
752
- `${API_BASE}/api/pairing/status?pluginId=${pluginId}`
753
- );
754
- consecutiveFailures = 0;
755
- const data = result?.data;
756
- if (data?.paired) {
757
- stop();
758
- const user = data.user ?? {};
759
- const rawToken = data.pluginAuthToken;
760
- onPaired({
761
- sessionId: data.sessionId,
762
- userId: typeof user.id === "string" && user.id.length > 0 ? user.id : void 0,
763
- userName: user.name || "",
764
- userEmail: user.email || "",
765
- plan: user.plan || "FREE",
766
- pluginAuthToken: typeof rawToken === "string" && rawToken.length > 0 ? rawToken : void 0
767
- });
768
- return;
769
- }
770
- } catch {
771
- consecutiveFailures += 1;
772
- }
773
- if (stopped) return;
774
- const delay = computePollDelay({ baseMs: 3e3, failures: consecutiveFailures });
775
- pollTimer = setTimeout(() => {
776
- void tick();
777
- }, delay);
778
- };
779
- const initialDelay = computePollDelay({ baseMs: 3e3, failures: 0 });
780
- pollTimer = setTimeout(() => {
781
- void tick();
782
- }, initialDelay);
783
- const timeout = setTimeout(() => {
784
- stop();
785
- onTimeout();
786
- }, 3e5);
787
- function stop() {
788
- stopped = true;
789
- if (pollTimer) {
790
- clearTimeout(pollTimer);
791
- pollTimer = null;
792
- }
793
- clearTimeout(timeout);
794
- }
795
- return stop;
796
- }
797
731
  var _transport = {
798
732
  postJson: _postJson,
799
733
  getJson: _getJson,
@@ -1032,6 +966,14 @@ async function _getJson(url) {
1032
966
  });
1033
967
  }
1034
968
 
969
+ // src/lib/poll-delay.ts
970
+ var MAX_DELAY_MS = 3e4;
971
+ function computePollDelay({ baseMs, failures }) {
972
+ const exp = Math.min(MAX_DELAY_MS, baseMs * Math.pow(2, failures));
973
+ const jitter = exp * (0.9 + Math.random() * 0.2);
974
+ return Math.round(jitter);
975
+ }
976
+
1035
977
  // src/services/logger.ts
1036
978
  var fs2 = __toESM(require("fs"));
1037
979
  var os3 = __toESM(require("os"));
@@ -5902,7 +5844,7 @@ function readAnonId() {
5902
5844
  }
5903
5845
  function superProperties() {
5904
5846
  return {
5905
- cliVersion: true ? "2.29.0" : "0.0.0-dev",
5847
+ cliVersion: true ? "2.31.0" : "0.0.0-dev",
5906
5848
  nodeVersion: process.version,
5907
5849
  platform: process.platform,
5908
5850
  arch: process.arch,
@@ -14660,18 +14602,27 @@ var AcpClient = class {
14660
14602
  * a ceiling the relay command sits "pending" forever and mobile
14661
14603
  * shows a permanent "Thinking…" spinner with no way to recover.
14662
14604
  */
14663
- async prompt(text) {
14605
+ async prompt(input) {
14664
14606
  if (!this.connection || !this.sessionId) {
14665
14607
  throw new Error("AcpClient.prompt called before start()");
14666
14608
  }
14609
+ const blocks = typeof input === "string" ? [{ type: "text", text: input }] : input;
14610
+ const textLen = blocks.reduce(
14611
+ (n, b) => b.type === "text" ? n + b.text.length : n,
14612
+ 0
14613
+ );
14614
+ const imageCount = blocks.reduce(
14615
+ (n, b) => b.type === "image" ? n + 1 : n,
14616
+ 0
14617
+ );
14667
14618
  log.info(
14668
14619
  "acpClient",
14669
- `prompt \u2192 session=${this.sessionId.slice(0, 8)} chars=${text.length}`
14620
+ `prompt \u2192 session=${this.sessionId.slice(0, 8)} textChars=${textLen} imageBlocks=${imageCount}`
14670
14621
  );
14671
14622
  const t0 = Date.now();
14672
14623
  const send = this.connection.prompt({
14673
14624
  sessionId: this.sessionId,
14674
- prompt: [{ type: "text", text }]
14625
+ prompt: blocks
14675
14626
  });
14676
14627
  let timeoutId;
14677
14628
  const timeout = new Promise((_resolve, reject) => {
@@ -15143,6 +15094,59 @@ var AcpPublisher = class {
15143
15094
  }
15144
15095
  };
15145
15096
 
15097
+ // src/agents/acp/buildAcpPromptBlocks.ts
15098
+ var MIME_FROM_EXT = {
15099
+ png: "image/png",
15100
+ jpg: "image/jpeg",
15101
+ jpeg: "image/jpeg",
15102
+ gif: "image/gif",
15103
+ webp: "image/webp",
15104
+ heic: "image/heic",
15105
+ heif: "image/heif"
15106
+ };
15107
+ function inferMime(filename) {
15108
+ const ext = filename.toLowerCase().split(".").pop() ?? "";
15109
+ return MIME_FROM_EXT[ext] ?? "application/octet-stream";
15110
+ }
15111
+ var PLACEHOLDER_PROMPT = "Please review the attached image(s) and let me know what you find.";
15112
+ function buildAcpPromptBlocks(payload) {
15113
+ const blocks = [];
15114
+ for (const file of payload.files ?? []) {
15115
+ if (!file.base64 || file.base64.length === 0) continue;
15116
+ blocks.push({
15117
+ type: "image",
15118
+ mimeType: file.mimeType ?? inferMime(file.filename),
15119
+ data: file.base64
15120
+ });
15121
+ }
15122
+ const text = (payload.prompt ?? "").trim();
15123
+ if (text.length > 0) {
15124
+ blocks.push({ type: "text", text });
15125
+ } else if (blocks.length > 0) {
15126
+ blocks.push({ type: "text", text: PLACEHOLDER_PROMPT });
15127
+ }
15128
+ return blocks;
15129
+ }
15130
+
15131
+ // src/agents/acp/promptEcho.ts
15132
+ var MAX_PROMPT_CHARS = 200;
15133
+ function formatPromptEchoLine(payload) {
15134
+ const rawText = (payload.prompt ?? "").replace(/\s+/g, " ").trim();
15135
+ const imageCount = (payload.files ?? []).filter(
15136
+ (f) => typeof f.base64 === "string" && f.base64.length > 0
15137
+ ).length;
15138
+ if (rawText.length === 0 && imageCount === 0) return "";
15139
+ const truncatedText = rawText.length > MAX_PROMPT_CHARS ? rawText.slice(0, MAX_PROMPT_CHARS) + "\u2026" : rawText;
15140
+ const parts = [];
15141
+ if (truncatedText.length > 0) {
15142
+ parts.push(`Mobile: ${truncatedText}`);
15143
+ }
15144
+ if (imageCount > 0) {
15145
+ parts.push(`+ ${imageCount} image${imageCount === 1 ? "" : "s"}`);
15146
+ }
15147
+ return `\u203A ${parts.join(" ")}`;
15148
+ }
15149
+
15146
15150
  // src/services/terminal-ops.service.ts
15147
15151
  var import_child_process8 = require("child_process");
15148
15152
  var import_crypto2 = require("crypto");
@@ -16320,6 +16324,125 @@ async function selectSession(sessions3, activeId) {
16320
16324
  return result;
16321
16325
  }
16322
16326
 
16327
+ // src/services/pair-completion-subscriber.ts
16328
+ var https4 = __toESM(require("https"));
16329
+ var http4 = __toESM(require("http"));
16330
+ var API_BASE3 = resolveApiBaseUrl();
16331
+ var PAIR_TIMEOUT_MS = 5 * 60 * 1e3;
16332
+ var dispatchers = /* @__PURE__ */ new Set();
16333
+ function subscribeToPairCompletion(pluginId, onPaired, onTimeout) {
16334
+ let stopped = false;
16335
+ let req = null;
16336
+ let timeoutTimer = null;
16337
+ const dispatch = (cmd) => {
16338
+ if (stopped) return;
16339
+ if (cmd.pluginId !== pluginId) return;
16340
+ if (cmd.type !== "pair_completed") return;
16341
+ const payload = cmd.payload ?? {};
16342
+ const info = {
16343
+ sessionId: typeof payload.sessionId === "string" ? payload.sessionId : cmd.sessionId,
16344
+ userId: typeof payload.userId === "string" ? payload.userId : void 0,
16345
+ userName: typeof payload.userName === "string" ? payload.userName : "",
16346
+ userEmail: typeof payload.userEmail === "string" ? payload.userEmail : "",
16347
+ plan: typeof payload.plan === "string" ? payload.plan : "FREE",
16348
+ pluginAuthToken: typeof payload.pluginAuthToken === "string" ? payload.pluginAuthToken : void 0
16349
+ };
16350
+ stop();
16351
+ onPaired(info);
16352
+ };
16353
+ dispatchers.add(dispatch);
16354
+ const connect = () => {
16355
+ if (stopped) return;
16356
+ const url = new URL(`${API_BASE3}/api/commands/pending/stream`);
16357
+ url.searchParams.set("pluginId", pluginId);
16358
+ const transport = url.protocol === "https:" ? https4 : http4;
16359
+ req = transport.request(
16360
+ {
16361
+ hostname: url.hostname,
16362
+ port: url.port || (url.protocol === "https:" ? 443 : 80),
16363
+ path: `${url.pathname}${url.search}`,
16364
+ method: "GET",
16365
+ headers: {
16366
+ Accept: "text/event-stream",
16367
+ "Cache-Control": "no-cache",
16368
+ ...vercelBypassHeader()
16369
+ },
16370
+ timeout: 35e3
16371
+ },
16372
+ (res) => {
16373
+ if (res.statusCode !== 200) {
16374
+ log.trace(
16375
+ "pairSubscribe",
16376
+ `sse status=${res.statusCode}; will reconnect in 1s`
16377
+ );
16378
+ res.resume();
16379
+ setTimeout(connect, 1e3);
16380
+ return;
16381
+ }
16382
+ let buf = "";
16383
+ res.setEncoding("utf8");
16384
+ res.on("data", (chunk) => {
16385
+ buf += chunk;
16386
+ let idx;
16387
+ while ((idx = buf.indexOf("\n\n")) >= 0) {
16388
+ const frame = buf.slice(0, idx);
16389
+ buf = buf.slice(idx + 2);
16390
+ parseFrame(frame, dispatch);
16391
+ }
16392
+ });
16393
+ res.on("end", () => {
16394
+ if (!stopped) setTimeout(connect, 250);
16395
+ });
16396
+ res.on("error", () => {
16397
+ if (!stopped) setTimeout(connect, 1e3);
16398
+ });
16399
+ }
16400
+ );
16401
+ req.on("error", () => {
16402
+ if (!stopped) setTimeout(connect, 1e3);
16403
+ });
16404
+ req.on("timeout", () => {
16405
+ req?.destroy();
16406
+ if (!stopped) setTimeout(connect, 250);
16407
+ });
16408
+ req.end();
16409
+ };
16410
+ connect();
16411
+ timeoutTimer = setTimeout(() => {
16412
+ stop();
16413
+ onTimeout();
16414
+ }, PAIR_TIMEOUT_MS);
16415
+ function stop() {
16416
+ if (stopped) return;
16417
+ stopped = true;
16418
+ dispatchers.delete(dispatch);
16419
+ if (timeoutTimer) {
16420
+ clearTimeout(timeoutTimer);
16421
+ timeoutTimer = null;
16422
+ }
16423
+ if (req) {
16424
+ req.destroy();
16425
+ req = null;
16426
+ }
16427
+ }
16428
+ return stop;
16429
+ }
16430
+ function parseFrame(frame, dispatch) {
16431
+ let eventType = "message";
16432
+ let data = "";
16433
+ for (const line of frame.split("\n")) {
16434
+ if (line.startsWith("event:")) eventType = line.slice(6).trim();
16435
+ else if (line.startsWith("data:")) data += line.slice(5).trimStart();
16436
+ }
16437
+ if (eventType !== "commands") return;
16438
+ try {
16439
+ const parsed = JSON.parse(data);
16440
+ if (!Array.isArray(parsed.commands)) return;
16441
+ for (const cmd of parsed.commands) dispatch(cmd);
16442
+ } catch {
16443
+ }
16444
+ }
16445
+
16323
16446
  // src/commands/link.ts
16324
16447
  function buildLinkContext(agentId) {
16325
16448
  const runtime = createRuntimeStrategy(agentId);
@@ -16386,12 +16509,7 @@ async function link(args2 = []) {
16386
16509
  const pluginId = (0, import_node_crypto6.randomUUID)();
16387
16510
  const spin = dist_exports.spinner();
16388
16511
  spin.start("Requesting pairing code...");
16389
- const originator = getActiveSession();
16390
- const pairing = await requestCode(pluginId, {
16391
- originatorSessionId: originator?.id,
16392
- originatorPluginId: originator?.pluginId,
16393
- originatorAuthToken: originator?.pluginAuthToken
16394
- });
16512
+ const pairing = await requestCode(pluginId);
16395
16513
  if (!pairing) {
16396
16514
  spin.stop("Failed");
16397
16515
  showError("Could not reach the server. Check your connection and try again.");
@@ -16413,7 +16531,7 @@ async function link(args2 = []) {
16413
16531
  stopPoll?.();
16414
16532
  reject(new Error("cancelled"));
16415
16533
  };
16416
- stopPoll = pollStatus(
16534
+ stopPoll = subscribeToPairCompletion(
16417
16535
  pluginId,
16418
16536
  (info) => {
16419
16537
  process.removeListener("SIGINT", sigint);
@@ -18012,8 +18130,8 @@ function isIgnoredFilePath(filePath) {
18012
18130
  }
18013
18131
 
18014
18132
  // src/services/file-watcher/transport.ts
18015
- var http4 = __toESM(require("http"));
18016
- var https4 = __toESM(require("https"));
18133
+ var http5 = __toESM(require("http"));
18134
+ var https5 = __toESM(require("https"));
18017
18135
  var _transport3 = {
18018
18136
  post: _post2
18019
18137
  };
@@ -18021,7 +18139,7 @@ function _post2(url, headers, payload) {
18021
18139
  return new Promise((resolve6, reject) => {
18022
18140
  let settled = false;
18023
18141
  const u2 = new URL(url);
18024
- const lib = u2.protocol === "https:" ? https4 : http4;
18142
+ const lib = u2.protocol === "https:" ? https5 : http5;
18025
18143
  const req = lib.request(
18026
18144
  {
18027
18145
  hostname: u2.hostname,
@@ -18061,7 +18179,7 @@ function _post2(url, headers, payload) {
18061
18179
  }
18062
18180
 
18063
18181
  // src/services/file-watcher.service.ts
18064
- var API_BASE3 = resolveApiBaseUrl();
18182
+ var API_BASE4 = resolveApiBaseUrl();
18065
18183
  var DEBOUNCE_MS = 250;
18066
18184
  var COALESCE_WINDOW_MS = 250;
18067
18185
  var COALESCE_MAX_HOLD_MS = 2e3;
@@ -18134,7 +18252,7 @@ function _defaultFindGitRoot(startDir) {
18134
18252
  var FileWatcherService = class {
18135
18253
  constructor(opts) {
18136
18254
  this.opts = opts;
18137
- this.apiBase = opts.apiBaseUrl ?? API_BASE3;
18255
+ this.apiBase = opts.apiBaseUrl ?? API_BASE4;
18138
18256
  }
18139
18257
  opts;
18140
18258
  watcher = null;
@@ -19085,13 +19203,13 @@ function homeDir() {
19085
19203
  }
19086
19204
 
19087
19205
  // src/services/turn-files/turn-file-aggregator.ts
19088
- var API_BASE4 = resolveApiBaseUrl();
19206
+ var API_BASE5 = resolveApiBaseUrl();
19089
19207
  var ENDPOINT = "/api/files/batch";
19090
19208
  var MAX_BATCH_SIZE = 1e3;
19091
19209
  var TurnFileAggregator = class {
19092
19210
  constructor(opts) {
19093
19211
  this.opts = opts;
19094
- this.apiBase = opts.apiBaseUrl ?? API_BASE4;
19212
+ this.apiBase = opts.apiBaseUrl ?? API_BASE5;
19095
19213
  this.outbox = new FilesOutbox({
19096
19214
  sessionId: opts.sessionId,
19097
19215
  baseDir: opts.outboxDir,
@@ -19731,17 +19849,26 @@ async function handleCommand(cmd, client2, relay, acpSessionId, models, streamin
19731
19849
  switch (cmd.type) {
19732
19850
  case "start_task": {
19733
19851
  const payload = cmd.payload;
19734
- const prompt = payload?.prompt?.trim();
19735
- if (!prompt) {
19736
- log.warn("acpRunner", "start_task with empty prompt; ignoring");
19852
+ const blocks = buildAcpPromptBlocks(payload ?? {});
19853
+ if (blocks.length === 0) {
19854
+ log.warn("acpRunner", "start_task with empty prompt + no attachments; ignoring");
19737
19855
  await relay.sendResult(cmd.id, "failed", { error: "empty prompt" });
19738
19856
  return;
19739
19857
  }
19740
- log.info("acpRunner", `start_task \u2192 forwarding prompt chars=${prompt.length} id=${cmd.id.slice(0, 8)}`);
19858
+ const promptText = blocks.filter((b) => b.type === "text").map((b) => b.text).join("\n");
19859
+ const imageCount = blocks.filter((b) => b.type === "image").length;
19860
+ log.info(
19861
+ "acpRunner",
19862
+ `start_task \u2192 forwarding textChars=${promptText.length} imageBlocks=${imageCount} id=${cmd.id.slice(0, 8)}`
19863
+ );
19864
+ const echoLine = formatPromptEchoLine(payload ?? {});
19865
+ if (echoLine.length > 0) {
19866
+ showInfo(echoLine);
19867
+ }
19741
19868
  await streaming.beginTurn();
19742
- history.appendUserPrompt(prompt);
19869
+ history.appendUserPrompt(promptText);
19743
19870
  try {
19744
- const reply = await client2.prompt(prompt);
19871
+ const reply = await client2.prompt(blocks);
19745
19872
  const finalText = streaming.getCurrentText();
19746
19873
  await streaming.closeTurnWithInteractiveDetection();
19747
19874
  history.appendAgentReply(finalText);
@@ -20076,13 +20203,13 @@ var ChromeStepTracker = class {
20076
20203
  };
20077
20204
 
20078
20205
  // src/services/output/chunk-emitter.ts
20079
- var https5 = __toESM(require("https"));
20080
- var http5 = __toESM(require("http"));
20081
- var API_BASE5 = resolveApiBaseUrl();
20206
+ var https6 = __toESM(require("https"));
20207
+ var http6 = __toESM(require("http"));
20208
+ var API_BASE6 = resolveApiBaseUrl();
20082
20209
  async function refreshAuthToken(sessionId, pluginId) {
20083
20210
  try {
20084
20211
  const { statusCode, body } = await _transport4.post(
20085
- `${API_BASE5}/api/pairing/reconnect`,
20212
+ `${API_BASE6}/api/pairing/reconnect`,
20086
20213
  {
20087
20214
  "Content-Type": "application/json",
20088
20215
  "X-Codeam-Protocol-Version": PROTOCOL_VERSION,
@@ -20123,7 +20250,7 @@ var ChunkEmitter = class {
20123
20250
  }
20124
20251
  }
20125
20252
  opts;
20126
- url = `${API_BASE5}/api/commands/output`;
20253
+ url = `${API_BASE6}/api/commands/output`;
20127
20254
  headers;
20128
20255
  /**
20129
20256
  * Send a chunk. `body` is the chunk fields minus `sessionId` /
@@ -20204,7 +20331,7 @@ function _post3(url, headers, payload) {
20204
20331
  return new Promise((resolve6, reject) => {
20205
20332
  let settled = false;
20206
20333
  const u2 = new URL(url);
20207
- const transport = u2.protocol === "https:" ? https5 : http5;
20334
+ const transport = u2.protocol === "https:" ? https6 : http6;
20208
20335
  const req = transport.request(
20209
20336
  {
20210
20337
  hostname: u2.hostname,
@@ -20802,8 +20929,8 @@ var OutputService = class _OutputService {
20802
20929
  var fs32 = __toESM(require("fs"));
20803
20930
  var path38 = __toESM(require("path"));
20804
20931
  var os25 = __toESM(require("os"));
20805
- var https6 = __toESM(require("https"));
20806
- var http6 = __toESM(require("http"));
20932
+ var https7 = __toESM(require("https"));
20933
+ var http7 = __toESM(require("http"));
20807
20934
  var import_zod2 = require("zod");
20808
20935
  var historyRecordSchema = import_zod2.z.object({
20809
20936
  type: import_zod2.z.string().optional(),
@@ -20815,7 +20942,7 @@ var historyRecordSchema = import_zod2.z.object({
20815
20942
  content: import_zod2.z.union([import_zod2.z.string(), import_zod2.z.array(import_zod2.z.unknown())]).optional()
20816
20943
  }).passthrough().optional()
20817
20944
  }).passthrough();
20818
- var API_BASE6 = resolveApiBaseUrl();
20945
+ var API_BASE7 = resolveApiBaseUrl();
20819
20946
  function extractText3(content) {
20820
20947
  if (typeof content === "string") return content;
20821
20948
  if (Array.isArray(content)) {
@@ -20867,8 +20994,8 @@ function parseJsonl(filePath) {
20867
20994
  function post(endpoint, body) {
20868
20995
  return new Promise((resolve6) => {
20869
20996
  const payload = JSON.stringify(body);
20870
- const u2 = new URL(`${API_BASE6}${endpoint}`);
20871
- const transport = u2.protocol === "https:" ? https6 : http6;
20997
+ const u2 = new URL(`${API_BASE7}${endpoint}`);
20998
+ const transport = u2.protocol === "https:" ? https7 : http7;
20872
20999
  const req = transport.request(
20873
21000
  {
20874
21001
  hostname: u2.hostname,
@@ -21316,7 +21443,7 @@ var RepoDirtyTracker = class {
21316
21443
 
21317
21444
  // src/services/streaming-emitter.service.ts
21318
21445
  var import_crypto5 = require("crypto");
21319
- var API_BASE7 = resolveApiBaseUrl();
21446
+ var API_BASE8 = resolveApiBaseUrl();
21320
21447
  var TICK_MS = 50;
21321
21448
  var ANSWER_POLL_MS = 1500;
21322
21449
  var SELECTOR_STABLE_MS = 800;
@@ -21327,7 +21454,7 @@ var TAIL_KEEP_BYTES2 = 1.5 * 1024 * 1024;
21327
21454
  var StreamingEmitterService = class {
21328
21455
  constructor(opts) {
21329
21456
  this.opts = opts;
21330
- this.apiBase = opts.apiBaseUrl ?? API_BASE7;
21457
+ this.apiBase = opts.apiBaseUrl ?? API_BASE8;
21331
21458
  this.headers = {
21332
21459
  "Content-Type": "application/json",
21333
21460
  "X-Codeam-Protocol-Version": "2.0.0",
@@ -22046,7 +22173,7 @@ async function pair(args2 = []) {
22046
22173
  console.log("");
22047
22174
  process.exit(0);
22048
22175
  }
22049
- stopPolling = pollStatus(
22176
+ stopPolling = subscribeToPairCompletion(
22050
22177
  pluginId,
22051
22178
  (info) => {
22052
22179
  process.removeListener("SIGINT", sigintHandler);
@@ -22292,7 +22419,7 @@ async function startInfraOnly(agentId) {
22292
22419
  }
22293
22420
 
22294
22421
  // src/commands/pair-auto.ts
22295
- var API_BASE8 = resolveApiBaseUrl();
22422
+ var API_BASE9 = resolveApiBaseUrl();
22296
22423
  function fail(msg) {
22297
22424
  console.error(`
22298
22425
  ${msg}
@@ -22332,7 +22459,7 @@ function networkError(msg, cause) {
22332
22459
  return err;
22333
22460
  }
22334
22461
  async function claimOnce(token, pluginId) {
22335
- const url = `${API_BASE8}/api/pairing/claim-auto-token`;
22462
+ const url = `${API_BASE9}/api/pairing/claim-auto-token`;
22336
22463
  const body = {
22337
22464
  token,
22338
22465
  pluginId,
@@ -22535,7 +22662,7 @@ function status() {
22535
22662
 
22536
22663
  // src/commands/logout.ts
22537
22664
  var import_picocolors7 = __toESM(require("picocolors"));
22538
- var API_BASE9 = resolveApiBaseUrl();
22665
+ var API_BASE10 = resolveApiBaseUrl();
22539
22666
  async function notifyBackendOffline() {
22540
22667
  const cfg = loadCliConfig();
22541
22668
  const pluginIds = /* @__PURE__ */ new Set([
@@ -22547,7 +22674,7 @@ async function notifyBackendOffline() {
22547
22674
  try {
22548
22675
  await Promise.all(
22549
22676
  Array.from(pluginIds).map(
22550
- (pluginId) => _postJson(`${API_BASE9}/api/plugin/heartbeat`, {
22677
+ (pluginId) => _postJson(`${API_BASE10}/api/plugin/heartbeat`, {
22551
22678
  pluginId,
22552
22679
  online: false
22553
22680
  }).catch((err) => {
@@ -24689,7 +24816,7 @@ function checkChokidar() {
24689
24816
  }
24690
24817
  async function doctor(args2 = []) {
24691
24818
  const json = args2.includes("--json");
24692
- const cliVersion = true ? "2.29.0" : "0.0.0-dev";
24819
+ const cliVersion = true ? "2.31.0" : "0.0.0-dev";
24693
24820
  const apiBase = resolveApiBaseUrl();
24694
24821
  const diagnosticId = (0, import_node_crypto8.randomUUID)();
24695
24822
  log.info("doctor", `run id=${diagnosticId} cli=${cliVersion}`);
@@ -24888,7 +25015,7 @@ async function completion(args2) {
24888
25015
  // src/commands/version.ts
24889
25016
  var import_picocolors13 = __toESM(require("picocolors"));
24890
25017
  function version2() {
24891
- const v = true ? "2.29.0" : "unknown";
25018
+ const v = true ? "2.31.0" : "unknown";
24892
25019
  console.log(`${import_picocolors13.default.bold("codeam-cli")} ${import_picocolors13.default.cyan(v)}`);
24893
25020
  }
24894
25021
 
@@ -25019,7 +25146,7 @@ var _subcommandHelpKeys = Object.keys(HELPS);
25019
25146
  var fs35 = __toESM(require("fs"));
25020
25147
  var os27 = __toESM(require("os"));
25021
25148
  var path44 = __toESM(require("path"));
25022
- var https7 = __toESM(require("https"));
25149
+ var https8 = __toESM(require("https"));
25023
25150
  var import_node_child_process12 = require("child_process");
25024
25151
  var import_picocolors16 = __toESM(require("picocolors"));
25025
25152
  var PKG_NAME = "codeam-cli";
@@ -25065,7 +25192,7 @@ function compareSemver(a, b) {
25065
25192
  }
25066
25193
  function fetchLatest() {
25067
25194
  return new Promise((resolve6) => {
25068
- const req = https7.get(
25195
+ const req = https8.get(
25069
25196
  REGISTRY_URL,
25070
25197
  { headers: { Accept: "application/json" }, timeout: REQUEST_TIMEOUT_MS },
25071
25198
  (res) => {
@@ -25174,7 +25301,7 @@ function checkForUpdates() {
25174
25301
  if (process.env.CODEAM_DISABLE_UPDATE_CHECK === "1") return;
25175
25302
  if (process.env.CI) return;
25176
25303
  if (!process.stdout.isTTY) return;
25177
- const current = true ? "2.29.0" : null;
25304
+ const current = true ? "2.31.0" : null;
25178
25305
  if (!current) return;
25179
25306
  const cache = readCache();
25180
25307
  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.29.0",
3
+ "version": "2.31.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",