codeam-cli 2.29.0 → 2.30.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 +6 -0
  2. package/dist/index.js +240 -103
  3. package/package.json +1 -1
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.29.0] — 2026-06-06
8
+
9
+ ### Added
10
+
11
+ - **cli:** Forward originator HMAC to /api/pairing/code for QR-ready SSE
12
+
7
13
  ## [2.28.1] — 2026-06-06
8
14
 
9
15
  ### 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.30.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,14 +682,6 @@ 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;
@@ -741,59 +733,6 @@ async function fetchCurrentPluginAuthToken(sessionId, pluginId) {
741
733
  return null;
742
734
  }
743
735
  }
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
736
  var _transport = {
798
737
  postJson: _postJson,
799
738
  getJson: _getJson,
@@ -1032,6 +971,14 @@ async function _getJson(url) {
1032
971
  });
1033
972
  }
1034
973
 
974
+ // src/lib/poll-delay.ts
975
+ var MAX_DELAY_MS = 3e4;
976
+ function computePollDelay({ baseMs, failures }) {
977
+ const exp = Math.min(MAX_DELAY_MS, baseMs * Math.pow(2, failures));
978
+ const jitter = exp * (0.9 + Math.random() * 0.2);
979
+ return Math.round(jitter);
980
+ }
981
+
1035
982
  // src/services/logger.ts
1036
983
  var fs2 = __toESM(require("fs"));
1037
984
  var os3 = __toESM(require("os"));
@@ -5902,7 +5849,7 @@ function readAnonId() {
5902
5849
  }
5903
5850
  function superProperties() {
5904
5851
  return {
5905
- cliVersion: true ? "2.29.0" : "0.0.0-dev",
5852
+ cliVersion: true ? "2.30.0" : "0.0.0-dev",
5906
5853
  nodeVersion: process.version,
5907
5854
  platform: process.platform,
5908
5855
  arch: process.arch,
@@ -14660,18 +14607,27 @@ var AcpClient = class {
14660
14607
  * a ceiling the relay command sits "pending" forever and mobile
14661
14608
  * shows a permanent "Thinking…" spinner with no way to recover.
14662
14609
  */
14663
- async prompt(text) {
14610
+ async prompt(input) {
14664
14611
  if (!this.connection || !this.sessionId) {
14665
14612
  throw new Error("AcpClient.prompt called before start()");
14666
14613
  }
14614
+ const blocks = typeof input === "string" ? [{ type: "text", text: input }] : input;
14615
+ const textLen = blocks.reduce(
14616
+ (n, b) => b.type === "text" ? n + b.text.length : n,
14617
+ 0
14618
+ );
14619
+ const imageCount = blocks.reduce(
14620
+ (n, b) => b.type === "image" ? n + 1 : n,
14621
+ 0
14622
+ );
14667
14623
  log.info(
14668
14624
  "acpClient",
14669
- `prompt \u2192 session=${this.sessionId.slice(0, 8)} chars=${text.length}`
14625
+ `prompt \u2192 session=${this.sessionId.slice(0, 8)} textChars=${textLen} imageBlocks=${imageCount}`
14670
14626
  );
14671
14627
  const t0 = Date.now();
14672
14628
  const send = this.connection.prompt({
14673
14629
  sessionId: this.sessionId,
14674
- prompt: [{ type: "text", text }]
14630
+ prompt: blocks
14675
14631
  });
14676
14632
  let timeoutId;
14677
14633
  const timeout = new Promise((_resolve, reject) => {
@@ -15143,6 +15099,59 @@ var AcpPublisher = class {
15143
15099
  }
15144
15100
  };
15145
15101
 
15102
+ // src/agents/acp/buildAcpPromptBlocks.ts
15103
+ var MIME_FROM_EXT = {
15104
+ png: "image/png",
15105
+ jpg: "image/jpeg",
15106
+ jpeg: "image/jpeg",
15107
+ gif: "image/gif",
15108
+ webp: "image/webp",
15109
+ heic: "image/heic",
15110
+ heif: "image/heif"
15111
+ };
15112
+ function inferMime(filename) {
15113
+ const ext = filename.toLowerCase().split(".").pop() ?? "";
15114
+ return MIME_FROM_EXT[ext] ?? "application/octet-stream";
15115
+ }
15116
+ var PLACEHOLDER_PROMPT = "Please review the attached image(s) and let me know what you find.";
15117
+ function buildAcpPromptBlocks(payload) {
15118
+ const blocks = [];
15119
+ for (const file of payload.files ?? []) {
15120
+ if (!file.base64 || file.base64.length === 0) continue;
15121
+ blocks.push({
15122
+ type: "image",
15123
+ mimeType: file.mimeType ?? inferMime(file.filename),
15124
+ data: file.base64
15125
+ });
15126
+ }
15127
+ const text = (payload.prompt ?? "").trim();
15128
+ if (text.length > 0) {
15129
+ blocks.push({ type: "text", text });
15130
+ } else if (blocks.length > 0) {
15131
+ blocks.push({ type: "text", text: PLACEHOLDER_PROMPT });
15132
+ }
15133
+ return blocks;
15134
+ }
15135
+
15136
+ // src/agents/acp/promptEcho.ts
15137
+ var MAX_PROMPT_CHARS = 200;
15138
+ function formatPromptEchoLine(payload) {
15139
+ const rawText = (payload.prompt ?? "").replace(/\s+/g, " ").trim();
15140
+ const imageCount = (payload.files ?? []).filter(
15141
+ (f) => typeof f.base64 === "string" && f.base64.length > 0
15142
+ ).length;
15143
+ if (rawText.length === 0 && imageCount === 0) return "";
15144
+ const truncatedText = rawText.length > MAX_PROMPT_CHARS ? rawText.slice(0, MAX_PROMPT_CHARS) + "\u2026" : rawText;
15145
+ const parts = [];
15146
+ if (truncatedText.length > 0) {
15147
+ parts.push(`Mobile: ${truncatedText}`);
15148
+ }
15149
+ if (imageCount > 0) {
15150
+ parts.push(`+ ${imageCount} image${imageCount === 1 ? "" : "s"}`);
15151
+ }
15152
+ return `\u203A ${parts.join(" ")}`;
15153
+ }
15154
+
15146
15155
  // src/services/terminal-ops.service.ts
15147
15156
  var import_child_process8 = require("child_process");
15148
15157
  var import_crypto2 = require("crypto");
@@ -16320,6 +16329,125 @@ async function selectSession(sessions3, activeId) {
16320
16329
  return result;
16321
16330
  }
16322
16331
 
16332
+ // src/services/pair-completion-subscriber.ts
16333
+ var https4 = __toESM(require("https"));
16334
+ var http4 = __toESM(require("http"));
16335
+ var API_BASE3 = resolveApiBaseUrl();
16336
+ var PAIR_TIMEOUT_MS = 5 * 60 * 1e3;
16337
+ var dispatchers = /* @__PURE__ */ new Set();
16338
+ function subscribeToPairCompletion(pluginId, onPaired, onTimeout) {
16339
+ let stopped = false;
16340
+ let req = null;
16341
+ let timeoutTimer = null;
16342
+ const dispatch = (cmd) => {
16343
+ if (stopped) return;
16344
+ if (cmd.pluginId !== pluginId) return;
16345
+ if (cmd.type !== "pair_completed") return;
16346
+ const payload = cmd.payload ?? {};
16347
+ const info = {
16348
+ sessionId: typeof payload.sessionId === "string" ? payload.sessionId : cmd.sessionId,
16349
+ userId: typeof payload.userId === "string" ? payload.userId : void 0,
16350
+ userName: typeof payload.userName === "string" ? payload.userName : "",
16351
+ userEmail: typeof payload.userEmail === "string" ? payload.userEmail : "",
16352
+ plan: typeof payload.plan === "string" ? payload.plan : "FREE",
16353
+ pluginAuthToken: typeof payload.pluginAuthToken === "string" ? payload.pluginAuthToken : void 0
16354
+ };
16355
+ stop();
16356
+ onPaired(info);
16357
+ };
16358
+ dispatchers.add(dispatch);
16359
+ const connect = () => {
16360
+ if (stopped) return;
16361
+ const url = new URL(`${API_BASE3}/api/commands/pending/stream`);
16362
+ url.searchParams.set("pluginId", pluginId);
16363
+ const transport = url.protocol === "https:" ? https4 : http4;
16364
+ req = transport.request(
16365
+ {
16366
+ hostname: url.hostname,
16367
+ port: url.port || (url.protocol === "https:" ? 443 : 80),
16368
+ path: `${url.pathname}${url.search}`,
16369
+ method: "GET",
16370
+ headers: {
16371
+ Accept: "text/event-stream",
16372
+ "Cache-Control": "no-cache",
16373
+ ...vercelBypassHeader()
16374
+ },
16375
+ timeout: 35e3
16376
+ },
16377
+ (res) => {
16378
+ if (res.statusCode !== 200) {
16379
+ log.trace(
16380
+ "pairSubscribe",
16381
+ `sse status=${res.statusCode}; will reconnect in 1s`
16382
+ );
16383
+ res.resume();
16384
+ setTimeout(connect, 1e3);
16385
+ return;
16386
+ }
16387
+ let buf = "";
16388
+ res.setEncoding("utf8");
16389
+ res.on("data", (chunk) => {
16390
+ buf += chunk;
16391
+ let idx;
16392
+ while ((idx = buf.indexOf("\n\n")) >= 0) {
16393
+ const frame = buf.slice(0, idx);
16394
+ buf = buf.slice(idx + 2);
16395
+ parseFrame(frame, dispatch);
16396
+ }
16397
+ });
16398
+ res.on("end", () => {
16399
+ if (!stopped) setTimeout(connect, 250);
16400
+ });
16401
+ res.on("error", () => {
16402
+ if (!stopped) setTimeout(connect, 1e3);
16403
+ });
16404
+ }
16405
+ );
16406
+ req.on("error", () => {
16407
+ if (!stopped) setTimeout(connect, 1e3);
16408
+ });
16409
+ req.on("timeout", () => {
16410
+ req?.destroy();
16411
+ if (!stopped) setTimeout(connect, 250);
16412
+ });
16413
+ req.end();
16414
+ };
16415
+ connect();
16416
+ timeoutTimer = setTimeout(() => {
16417
+ stop();
16418
+ onTimeout();
16419
+ }, PAIR_TIMEOUT_MS);
16420
+ function stop() {
16421
+ if (stopped) return;
16422
+ stopped = true;
16423
+ dispatchers.delete(dispatch);
16424
+ if (timeoutTimer) {
16425
+ clearTimeout(timeoutTimer);
16426
+ timeoutTimer = null;
16427
+ }
16428
+ if (req) {
16429
+ req.destroy();
16430
+ req = null;
16431
+ }
16432
+ }
16433
+ return stop;
16434
+ }
16435
+ function parseFrame(frame, dispatch) {
16436
+ let eventType = "message";
16437
+ let data = "";
16438
+ for (const line of frame.split("\n")) {
16439
+ if (line.startsWith("event:")) eventType = line.slice(6).trim();
16440
+ else if (line.startsWith("data:")) data += line.slice(5).trimStart();
16441
+ }
16442
+ if (eventType !== "commands") return;
16443
+ try {
16444
+ const parsed = JSON.parse(data);
16445
+ if (!Array.isArray(parsed.commands)) return;
16446
+ for (const cmd of parsed.commands) dispatch(cmd);
16447
+ } catch {
16448
+ }
16449
+ }
16450
+
16323
16451
  // src/commands/link.ts
16324
16452
  function buildLinkContext(agentId) {
16325
16453
  const runtime = createRuntimeStrategy(agentId);
@@ -16413,7 +16541,7 @@ async function link(args2 = []) {
16413
16541
  stopPoll?.();
16414
16542
  reject(new Error("cancelled"));
16415
16543
  };
16416
- stopPoll = pollStatus(
16544
+ stopPoll = subscribeToPairCompletion(
16417
16545
  pluginId,
16418
16546
  (info) => {
16419
16547
  process.removeListener("SIGINT", sigint);
@@ -18012,8 +18140,8 @@ function isIgnoredFilePath(filePath) {
18012
18140
  }
18013
18141
 
18014
18142
  // src/services/file-watcher/transport.ts
18015
- var http4 = __toESM(require("http"));
18016
- var https4 = __toESM(require("https"));
18143
+ var http5 = __toESM(require("http"));
18144
+ var https5 = __toESM(require("https"));
18017
18145
  var _transport3 = {
18018
18146
  post: _post2
18019
18147
  };
@@ -18021,7 +18149,7 @@ function _post2(url, headers, payload) {
18021
18149
  return new Promise((resolve6, reject) => {
18022
18150
  let settled = false;
18023
18151
  const u2 = new URL(url);
18024
- const lib = u2.protocol === "https:" ? https4 : http4;
18152
+ const lib = u2.protocol === "https:" ? https5 : http5;
18025
18153
  const req = lib.request(
18026
18154
  {
18027
18155
  hostname: u2.hostname,
@@ -18061,7 +18189,7 @@ function _post2(url, headers, payload) {
18061
18189
  }
18062
18190
 
18063
18191
  // src/services/file-watcher.service.ts
18064
- var API_BASE3 = resolveApiBaseUrl();
18192
+ var API_BASE4 = resolveApiBaseUrl();
18065
18193
  var DEBOUNCE_MS = 250;
18066
18194
  var COALESCE_WINDOW_MS = 250;
18067
18195
  var COALESCE_MAX_HOLD_MS = 2e3;
@@ -18134,7 +18262,7 @@ function _defaultFindGitRoot(startDir) {
18134
18262
  var FileWatcherService = class {
18135
18263
  constructor(opts) {
18136
18264
  this.opts = opts;
18137
- this.apiBase = opts.apiBaseUrl ?? API_BASE3;
18265
+ this.apiBase = opts.apiBaseUrl ?? API_BASE4;
18138
18266
  }
18139
18267
  opts;
18140
18268
  watcher = null;
@@ -19085,13 +19213,13 @@ function homeDir() {
19085
19213
  }
19086
19214
 
19087
19215
  // src/services/turn-files/turn-file-aggregator.ts
19088
- var API_BASE4 = resolveApiBaseUrl();
19216
+ var API_BASE5 = resolveApiBaseUrl();
19089
19217
  var ENDPOINT = "/api/files/batch";
19090
19218
  var MAX_BATCH_SIZE = 1e3;
19091
19219
  var TurnFileAggregator = class {
19092
19220
  constructor(opts) {
19093
19221
  this.opts = opts;
19094
- this.apiBase = opts.apiBaseUrl ?? API_BASE4;
19222
+ this.apiBase = opts.apiBaseUrl ?? API_BASE5;
19095
19223
  this.outbox = new FilesOutbox({
19096
19224
  sessionId: opts.sessionId,
19097
19225
  baseDir: opts.outboxDir,
@@ -19731,17 +19859,26 @@ async function handleCommand(cmd, client2, relay, acpSessionId, models, streamin
19731
19859
  switch (cmd.type) {
19732
19860
  case "start_task": {
19733
19861
  const payload = cmd.payload;
19734
- const prompt = payload?.prompt?.trim();
19735
- if (!prompt) {
19736
- log.warn("acpRunner", "start_task with empty prompt; ignoring");
19862
+ const blocks = buildAcpPromptBlocks(payload ?? {});
19863
+ if (blocks.length === 0) {
19864
+ log.warn("acpRunner", "start_task with empty prompt + no attachments; ignoring");
19737
19865
  await relay.sendResult(cmd.id, "failed", { error: "empty prompt" });
19738
19866
  return;
19739
19867
  }
19740
- log.info("acpRunner", `start_task \u2192 forwarding prompt chars=${prompt.length} id=${cmd.id.slice(0, 8)}`);
19868
+ const promptText = blocks.filter((b) => b.type === "text").map((b) => b.text).join("\n");
19869
+ const imageCount = blocks.filter((b) => b.type === "image").length;
19870
+ log.info(
19871
+ "acpRunner",
19872
+ `start_task \u2192 forwarding textChars=${promptText.length} imageBlocks=${imageCount} id=${cmd.id.slice(0, 8)}`
19873
+ );
19874
+ const echoLine = formatPromptEchoLine(payload ?? {});
19875
+ if (echoLine.length > 0) {
19876
+ showInfo(echoLine);
19877
+ }
19741
19878
  await streaming.beginTurn();
19742
- history.appendUserPrompt(prompt);
19879
+ history.appendUserPrompt(promptText);
19743
19880
  try {
19744
- const reply = await client2.prompt(prompt);
19881
+ const reply = await client2.prompt(blocks);
19745
19882
  const finalText = streaming.getCurrentText();
19746
19883
  await streaming.closeTurnWithInteractiveDetection();
19747
19884
  history.appendAgentReply(finalText);
@@ -20076,13 +20213,13 @@ var ChromeStepTracker = class {
20076
20213
  };
20077
20214
 
20078
20215
  // src/services/output/chunk-emitter.ts
20079
- var https5 = __toESM(require("https"));
20080
- var http5 = __toESM(require("http"));
20081
- var API_BASE5 = resolveApiBaseUrl();
20216
+ var https6 = __toESM(require("https"));
20217
+ var http6 = __toESM(require("http"));
20218
+ var API_BASE6 = resolveApiBaseUrl();
20082
20219
  async function refreshAuthToken(sessionId, pluginId) {
20083
20220
  try {
20084
20221
  const { statusCode, body } = await _transport4.post(
20085
- `${API_BASE5}/api/pairing/reconnect`,
20222
+ `${API_BASE6}/api/pairing/reconnect`,
20086
20223
  {
20087
20224
  "Content-Type": "application/json",
20088
20225
  "X-Codeam-Protocol-Version": PROTOCOL_VERSION,
@@ -20123,7 +20260,7 @@ var ChunkEmitter = class {
20123
20260
  }
20124
20261
  }
20125
20262
  opts;
20126
- url = `${API_BASE5}/api/commands/output`;
20263
+ url = `${API_BASE6}/api/commands/output`;
20127
20264
  headers;
20128
20265
  /**
20129
20266
  * Send a chunk. `body` is the chunk fields minus `sessionId` /
@@ -20204,7 +20341,7 @@ function _post3(url, headers, payload) {
20204
20341
  return new Promise((resolve6, reject) => {
20205
20342
  let settled = false;
20206
20343
  const u2 = new URL(url);
20207
- const transport = u2.protocol === "https:" ? https5 : http5;
20344
+ const transport = u2.protocol === "https:" ? https6 : http6;
20208
20345
  const req = transport.request(
20209
20346
  {
20210
20347
  hostname: u2.hostname,
@@ -20802,8 +20939,8 @@ var OutputService = class _OutputService {
20802
20939
  var fs32 = __toESM(require("fs"));
20803
20940
  var path38 = __toESM(require("path"));
20804
20941
  var os25 = __toESM(require("os"));
20805
- var https6 = __toESM(require("https"));
20806
- var http6 = __toESM(require("http"));
20942
+ var https7 = __toESM(require("https"));
20943
+ var http7 = __toESM(require("http"));
20807
20944
  var import_zod2 = require("zod");
20808
20945
  var historyRecordSchema = import_zod2.z.object({
20809
20946
  type: import_zod2.z.string().optional(),
@@ -20815,7 +20952,7 @@ var historyRecordSchema = import_zod2.z.object({
20815
20952
  content: import_zod2.z.union([import_zod2.z.string(), import_zod2.z.array(import_zod2.z.unknown())]).optional()
20816
20953
  }).passthrough().optional()
20817
20954
  }).passthrough();
20818
- var API_BASE6 = resolveApiBaseUrl();
20955
+ var API_BASE7 = resolveApiBaseUrl();
20819
20956
  function extractText3(content) {
20820
20957
  if (typeof content === "string") return content;
20821
20958
  if (Array.isArray(content)) {
@@ -20867,8 +21004,8 @@ function parseJsonl(filePath) {
20867
21004
  function post(endpoint, body) {
20868
21005
  return new Promise((resolve6) => {
20869
21006
  const payload = JSON.stringify(body);
20870
- const u2 = new URL(`${API_BASE6}${endpoint}`);
20871
- const transport = u2.protocol === "https:" ? https6 : http6;
21007
+ const u2 = new URL(`${API_BASE7}${endpoint}`);
21008
+ const transport = u2.protocol === "https:" ? https7 : http7;
20872
21009
  const req = transport.request(
20873
21010
  {
20874
21011
  hostname: u2.hostname,
@@ -21316,7 +21453,7 @@ var RepoDirtyTracker = class {
21316
21453
 
21317
21454
  // src/services/streaming-emitter.service.ts
21318
21455
  var import_crypto5 = require("crypto");
21319
- var API_BASE7 = resolveApiBaseUrl();
21456
+ var API_BASE8 = resolveApiBaseUrl();
21320
21457
  var TICK_MS = 50;
21321
21458
  var ANSWER_POLL_MS = 1500;
21322
21459
  var SELECTOR_STABLE_MS = 800;
@@ -21327,7 +21464,7 @@ var TAIL_KEEP_BYTES2 = 1.5 * 1024 * 1024;
21327
21464
  var StreamingEmitterService = class {
21328
21465
  constructor(opts) {
21329
21466
  this.opts = opts;
21330
- this.apiBase = opts.apiBaseUrl ?? API_BASE7;
21467
+ this.apiBase = opts.apiBaseUrl ?? API_BASE8;
21331
21468
  this.headers = {
21332
21469
  "Content-Type": "application/json",
21333
21470
  "X-Codeam-Protocol-Version": "2.0.0",
@@ -22046,7 +22183,7 @@ async function pair(args2 = []) {
22046
22183
  console.log("");
22047
22184
  process.exit(0);
22048
22185
  }
22049
- stopPolling = pollStatus(
22186
+ stopPolling = subscribeToPairCompletion(
22050
22187
  pluginId,
22051
22188
  (info) => {
22052
22189
  process.removeListener("SIGINT", sigintHandler);
@@ -22292,7 +22429,7 @@ async function startInfraOnly(agentId) {
22292
22429
  }
22293
22430
 
22294
22431
  // src/commands/pair-auto.ts
22295
- var API_BASE8 = resolveApiBaseUrl();
22432
+ var API_BASE9 = resolveApiBaseUrl();
22296
22433
  function fail(msg) {
22297
22434
  console.error(`
22298
22435
  ${msg}
@@ -22332,7 +22469,7 @@ function networkError(msg, cause) {
22332
22469
  return err;
22333
22470
  }
22334
22471
  async function claimOnce(token, pluginId) {
22335
- const url = `${API_BASE8}/api/pairing/claim-auto-token`;
22472
+ const url = `${API_BASE9}/api/pairing/claim-auto-token`;
22336
22473
  const body = {
22337
22474
  token,
22338
22475
  pluginId,
@@ -22535,7 +22672,7 @@ function status() {
22535
22672
 
22536
22673
  // src/commands/logout.ts
22537
22674
  var import_picocolors7 = __toESM(require("picocolors"));
22538
- var API_BASE9 = resolveApiBaseUrl();
22675
+ var API_BASE10 = resolveApiBaseUrl();
22539
22676
  async function notifyBackendOffline() {
22540
22677
  const cfg = loadCliConfig();
22541
22678
  const pluginIds = /* @__PURE__ */ new Set([
@@ -22547,7 +22684,7 @@ async function notifyBackendOffline() {
22547
22684
  try {
22548
22685
  await Promise.all(
22549
22686
  Array.from(pluginIds).map(
22550
- (pluginId) => _postJson(`${API_BASE9}/api/plugin/heartbeat`, {
22687
+ (pluginId) => _postJson(`${API_BASE10}/api/plugin/heartbeat`, {
22551
22688
  pluginId,
22552
22689
  online: false
22553
22690
  }).catch((err) => {
@@ -24689,7 +24826,7 @@ function checkChokidar() {
24689
24826
  }
24690
24827
  async function doctor(args2 = []) {
24691
24828
  const json = args2.includes("--json");
24692
- const cliVersion = true ? "2.29.0" : "0.0.0-dev";
24829
+ const cliVersion = true ? "2.30.0" : "0.0.0-dev";
24693
24830
  const apiBase = resolveApiBaseUrl();
24694
24831
  const diagnosticId = (0, import_node_crypto8.randomUUID)();
24695
24832
  log.info("doctor", `run id=${diagnosticId} cli=${cliVersion}`);
@@ -24888,7 +25025,7 @@ async function completion(args2) {
24888
25025
  // src/commands/version.ts
24889
25026
  var import_picocolors13 = __toESM(require("picocolors"));
24890
25027
  function version2() {
24891
- const v = true ? "2.29.0" : "unknown";
25028
+ const v = true ? "2.30.0" : "unknown";
24892
25029
  console.log(`${import_picocolors13.default.bold("codeam-cli")} ${import_picocolors13.default.cyan(v)}`);
24893
25030
  }
24894
25031
 
@@ -25019,7 +25156,7 @@ var _subcommandHelpKeys = Object.keys(HELPS);
25019
25156
  var fs35 = __toESM(require("fs"));
25020
25157
  var os27 = __toESM(require("os"));
25021
25158
  var path44 = __toESM(require("path"));
25022
- var https7 = __toESM(require("https"));
25159
+ var https8 = __toESM(require("https"));
25023
25160
  var import_node_child_process12 = require("child_process");
25024
25161
  var import_picocolors16 = __toESM(require("picocolors"));
25025
25162
  var PKG_NAME = "codeam-cli";
@@ -25065,7 +25202,7 @@ function compareSemver(a, b) {
25065
25202
  }
25066
25203
  function fetchLatest() {
25067
25204
  return new Promise((resolve6) => {
25068
- const req = https7.get(
25205
+ const req = https8.get(
25069
25206
  REGISTRY_URL,
25070
25207
  { headers: { Accept: "application/json" }, timeout: REQUEST_TIMEOUT_MS },
25071
25208
  (res) => {
@@ -25174,7 +25311,7 @@ function checkForUpdates() {
25174
25311
  if (process.env.CODEAM_DISABLE_UPDATE_CHECK === "1") return;
25175
25312
  if (process.env.CI) return;
25176
25313
  if (!process.stdout.isTTY) return;
25177
- const current = true ? "2.29.0" : null;
25314
+ const current = true ? "2.30.0" : null;
25178
25315
  if (!current) return;
25179
25316
  const cache = readCache();
25180
25317
  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.30.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",