codeam-cli 2.28.1 → 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 +12 -0
  2. package/dist/index.js +253 -106
  3. 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.29.0] — 2026-06-06
8
+
9
+ ### Added
10
+
11
+ - **cli:** Forward originator HMAC to /api/pairing/code for QR-ready SSE
12
+
13
+ ## [2.28.1] — 2026-06-06
14
+
15
+ ### Fixed
16
+
17
+ - **cli:** RequestCode times out at 10s instead of hanging forever
18
+
7
19
  ## [2.28.0] — 2026-06-06
8
20
 
9
21
  ### Added
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.28.1",
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,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) {
688
+ async function requestCode(pluginId, options = {}) {
697
689
  try {
698
690
  const runtime = process.env.CODESPACES === "true" ? "github-codespaces" : "local";
699
691
  const codespaceName = process.env.CODESPACE_NAME;
@@ -705,7 +697,12 @@ async function requestCode(pluginId) {
705
697
  hostname: os2.hostname(),
706
698
  runtime,
707
699
  branch,
708
- ...codespaceName ? { codespaceName } : {}
700
+ ...codespaceName ? { codespaceName } : {},
701
+ ...options.originatorSessionId && options.originatorPluginId && options.originatorAuthToken ? {
702
+ originatorSessionId: options.originatorSessionId,
703
+ originatorPluginId: options.originatorPluginId,
704
+ originatorAuthToken: options.originatorAuthToken
705
+ } : {}
709
706
  });
710
707
  let timer;
711
708
  const timeoutSentinel = /* @__PURE__ */ Symbol("request-code-timeout");
@@ -736,59 +733,6 @@ async function fetchCurrentPluginAuthToken(sessionId, pluginId) {
736
733
  return null;
737
734
  }
738
735
  }
739
- function pollStatus(pluginId, onPaired, onTimeout) {
740
- let stopped = false;
741
- let pollTimer = null;
742
- let consecutiveFailures = 0;
743
- const tick = async () => {
744
- if (stopped) return;
745
- try {
746
- const result = await _transport.getJson(
747
- `${API_BASE}/api/pairing/status?pluginId=${pluginId}`
748
- );
749
- consecutiveFailures = 0;
750
- const data = result?.data;
751
- if (data?.paired) {
752
- stop();
753
- const user = data.user ?? {};
754
- const rawToken = data.pluginAuthToken;
755
- onPaired({
756
- sessionId: data.sessionId,
757
- userId: typeof user.id === "string" && user.id.length > 0 ? user.id : void 0,
758
- userName: user.name || "",
759
- userEmail: user.email || "",
760
- plan: user.plan || "FREE",
761
- pluginAuthToken: typeof rawToken === "string" && rawToken.length > 0 ? rawToken : void 0
762
- });
763
- return;
764
- }
765
- } catch {
766
- consecutiveFailures += 1;
767
- }
768
- if (stopped) return;
769
- const delay = computePollDelay({ baseMs: 3e3, failures: consecutiveFailures });
770
- pollTimer = setTimeout(() => {
771
- void tick();
772
- }, delay);
773
- };
774
- const initialDelay = computePollDelay({ baseMs: 3e3, failures: 0 });
775
- pollTimer = setTimeout(() => {
776
- void tick();
777
- }, initialDelay);
778
- const timeout = setTimeout(() => {
779
- stop();
780
- onTimeout();
781
- }, 3e5);
782
- function stop() {
783
- stopped = true;
784
- if (pollTimer) {
785
- clearTimeout(pollTimer);
786
- pollTimer = null;
787
- }
788
- clearTimeout(timeout);
789
- }
790
- return stop;
791
- }
792
736
  var _transport = {
793
737
  postJson: _postJson,
794
738
  getJson: _getJson,
@@ -1027,6 +971,14 @@ async function _getJson(url) {
1027
971
  });
1028
972
  }
1029
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
+
1030
982
  // src/services/logger.ts
1031
983
  var fs2 = __toESM(require("fs"));
1032
984
  var os3 = __toESM(require("os"));
@@ -5897,7 +5849,7 @@ function readAnonId() {
5897
5849
  }
5898
5850
  function superProperties() {
5899
5851
  return {
5900
- cliVersion: true ? "2.28.1" : "0.0.0-dev",
5852
+ cliVersion: true ? "2.30.0" : "0.0.0-dev",
5901
5853
  nodeVersion: process.version,
5902
5854
  platform: process.platform,
5903
5855
  arch: process.arch,
@@ -14655,18 +14607,27 @@ var AcpClient = class {
14655
14607
  * a ceiling the relay command sits "pending" forever and mobile
14656
14608
  * shows a permanent "Thinking…" spinner with no way to recover.
14657
14609
  */
14658
- async prompt(text) {
14610
+ async prompt(input) {
14659
14611
  if (!this.connection || !this.sessionId) {
14660
14612
  throw new Error("AcpClient.prompt called before start()");
14661
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
+ );
14662
14623
  log.info(
14663
14624
  "acpClient",
14664
- `prompt \u2192 session=${this.sessionId.slice(0, 8)} chars=${text.length}`
14625
+ `prompt \u2192 session=${this.sessionId.slice(0, 8)} textChars=${textLen} imageBlocks=${imageCount}`
14665
14626
  );
14666
14627
  const t0 = Date.now();
14667
14628
  const send = this.connection.prompt({
14668
14629
  sessionId: this.sessionId,
14669
- prompt: [{ type: "text", text }]
14630
+ prompt: blocks
14670
14631
  });
14671
14632
  let timeoutId;
14672
14633
  const timeout = new Promise((_resolve, reject) => {
@@ -15138,6 +15099,59 @@ var AcpPublisher = class {
15138
15099
  }
15139
15100
  };
15140
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
+
15141
15155
  // src/services/terminal-ops.service.ts
15142
15156
  var import_child_process8 = require("child_process");
15143
15157
  var import_crypto2 = require("crypto");
@@ -16315,6 +16329,125 @@ async function selectSession(sessions3, activeId) {
16315
16329
  return result;
16316
16330
  }
16317
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
+
16318
16451
  // src/commands/link.ts
16319
16452
  function buildLinkContext(agentId) {
16320
16453
  const runtime = createRuntimeStrategy(agentId);
@@ -16381,7 +16514,12 @@ async function link(args2 = []) {
16381
16514
  const pluginId = (0, import_node_crypto6.randomUUID)();
16382
16515
  const spin = dist_exports.spinner();
16383
16516
  spin.start("Requesting pairing code...");
16384
- const pairing = await requestCode(pluginId);
16517
+ const originator = getActiveSession();
16518
+ const pairing = await requestCode(pluginId, {
16519
+ originatorSessionId: originator?.id,
16520
+ originatorPluginId: originator?.pluginId,
16521
+ originatorAuthToken: originator?.pluginAuthToken
16522
+ });
16385
16523
  if (!pairing) {
16386
16524
  spin.stop("Failed");
16387
16525
  showError("Could not reach the server. Check your connection and try again.");
@@ -16403,7 +16541,7 @@ async function link(args2 = []) {
16403
16541
  stopPoll?.();
16404
16542
  reject(new Error("cancelled"));
16405
16543
  };
16406
- stopPoll = pollStatus(
16544
+ stopPoll = subscribeToPairCompletion(
16407
16545
  pluginId,
16408
16546
  (info) => {
16409
16547
  process.removeListener("SIGINT", sigint);
@@ -18002,8 +18140,8 @@ function isIgnoredFilePath(filePath) {
18002
18140
  }
18003
18141
 
18004
18142
  // src/services/file-watcher/transport.ts
18005
- var http4 = __toESM(require("http"));
18006
- var https4 = __toESM(require("https"));
18143
+ var http5 = __toESM(require("http"));
18144
+ var https5 = __toESM(require("https"));
18007
18145
  var _transport3 = {
18008
18146
  post: _post2
18009
18147
  };
@@ -18011,7 +18149,7 @@ function _post2(url, headers, payload) {
18011
18149
  return new Promise((resolve6, reject) => {
18012
18150
  let settled = false;
18013
18151
  const u2 = new URL(url);
18014
- const lib = u2.protocol === "https:" ? https4 : http4;
18152
+ const lib = u2.protocol === "https:" ? https5 : http5;
18015
18153
  const req = lib.request(
18016
18154
  {
18017
18155
  hostname: u2.hostname,
@@ -18051,7 +18189,7 @@ function _post2(url, headers, payload) {
18051
18189
  }
18052
18190
 
18053
18191
  // src/services/file-watcher.service.ts
18054
- var API_BASE3 = resolveApiBaseUrl();
18192
+ var API_BASE4 = resolveApiBaseUrl();
18055
18193
  var DEBOUNCE_MS = 250;
18056
18194
  var COALESCE_WINDOW_MS = 250;
18057
18195
  var COALESCE_MAX_HOLD_MS = 2e3;
@@ -18124,7 +18262,7 @@ function _defaultFindGitRoot(startDir) {
18124
18262
  var FileWatcherService = class {
18125
18263
  constructor(opts) {
18126
18264
  this.opts = opts;
18127
- this.apiBase = opts.apiBaseUrl ?? API_BASE3;
18265
+ this.apiBase = opts.apiBaseUrl ?? API_BASE4;
18128
18266
  }
18129
18267
  opts;
18130
18268
  watcher = null;
@@ -19075,13 +19213,13 @@ function homeDir() {
19075
19213
  }
19076
19214
 
19077
19215
  // src/services/turn-files/turn-file-aggregator.ts
19078
- var API_BASE4 = resolveApiBaseUrl();
19216
+ var API_BASE5 = resolveApiBaseUrl();
19079
19217
  var ENDPOINT = "/api/files/batch";
19080
19218
  var MAX_BATCH_SIZE = 1e3;
19081
19219
  var TurnFileAggregator = class {
19082
19220
  constructor(opts) {
19083
19221
  this.opts = opts;
19084
- this.apiBase = opts.apiBaseUrl ?? API_BASE4;
19222
+ this.apiBase = opts.apiBaseUrl ?? API_BASE5;
19085
19223
  this.outbox = new FilesOutbox({
19086
19224
  sessionId: opts.sessionId,
19087
19225
  baseDir: opts.outboxDir,
@@ -19721,17 +19859,26 @@ async function handleCommand(cmd, client2, relay, acpSessionId, models, streamin
19721
19859
  switch (cmd.type) {
19722
19860
  case "start_task": {
19723
19861
  const payload = cmd.payload;
19724
- const prompt = payload?.prompt?.trim();
19725
- if (!prompt) {
19726
- 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");
19727
19865
  await relay.sendResult(cmd.id, "failed", { error: "empty prompt" });
19728
19866
  return;
19729
19867
  }
19730
- 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
+ }
19731
19878
  await streaming.beginTurn();
19732
- history.appendUserPrompt(prompt);
19879
+ history.appendUserPrompt(promptText);
19733
19880
  try {
19734
- const reply = await client2.prompt(prompt);
19881
+ const reply = await client2.prompt(blocks);
19735
19882
  const finalText = streaming.getCurrentText();
19736
19883
  await streaming.closeTurnWithInteractiveDetection();
19737
19884
  history.appendAgentReply(finalText);
@@ -20066,13 +20213,13 @@ var ChromeStepTracker = class {
20066
20213
  };
20067
20214
 
20068
20215
  // src/services/output/chunk-emitter.ts
20069
- var https5 = __toESM(require("https"));
20070
- var http5 = __toESM(require("http"));
20071
- var API_BASE5 = resolveApiBaseUrl();
20216
+ var https6 = __toESM(require("https"));
20217
+ var http6 = __toESM(require("http"));
20218
+ var API_BASE6 = resolveApiBaseUrl();
20072
20219
  async function refreshAuthToken(sessionId, pluginId) {
20073
20220
  try {
20074
20221
  const { statusCode, body } = await _transport4.post(
20075
- `${API_BASE5}/api/pairing/reconnect`,
20222
+ `${API_BASE6}/api/pairing/reconnect`,
20076
20223
  {
20077
20224
  "Content-Type": "application/json",
20078
20225
  "X-Codeam-Protocol-Version": PROTOCOL_VERSION,
@@ -20113,7 +20260,7 @@ var ChunkEmitter = class {
20113
20260
  }
20114
20261
  }
20115
20262
  opts;
20116
- url = `${API_BASE5}/api/commands/output`;
20263
+ url = `${API_BASE6}/api/commands/output`;
20117
20264
  headers;
20118
20265
  /**
20119
20266
  * Send a chunk. `body` is the chunk fields minus `sessionId` /
@@ -20194,7 +20341,7 @@ function _post3(url, headers, payload) {
20194
20341
  return new Promise((resolve6, reject) => {
20195
20342
  let settled = false;
20196
20343
  const u2 = new URL(url);
20197
- const transport = u2.protocol === "https:" ? https5 : http5;
20344
+ const transport = u2.protocol === "https:" ? https6 : http6;
20198
20345
  const req = transport.request(
20199
20346
  {
20200
20347
  hostname: u2.hostname,
@@ -20792,8 +20939,8 @@ var OutputService = class _OutputService {
20792
20939
  var fs32 = __toESM(require("fs"));
20793
20940
  var path38 = __toESM(require("path"));
20794
20941
  var os25 = __toESM(require("os"));
20795
- var https6 = __toESM(require("https"));
20796
- var http6 = __toESM(require("http"));
20942
+ var https7 = __toESM(require("https"));
20943
+ var http7 = __toESM(require("http"));
20797
20944
  var import_zod2 = require("zod");
20798
20945
  var historyRecordSchema = import_zod2.z.object({
20799
20946
  type: import_zod2.z.string().optional(),
@@ -20805,7 +20952,7 @@ var historyRecordSchema = import_zod2.z.object({
20805
20952
  content: import_zod2.z.union([import_zod2.z.string(), import_zod2.z.array(import_zod2.z.unknown())]).optional()
20806
20953
  }).passthrough().optional()
20807
20954
  }).passthrough();
20808
- var API_BASE6 = resolveApiBaseUrl();
20955
+ var API_BASE7 = resolveApiBaseUrl();
20809
20956
  function extractText3(content) {
20810
20957
  if (typeof content === "string") return content;
20811
20958
  if (Array.isArray(content)) {
@@ -20857,8 +21004,8 @@ function parseJsonl(filePath) {
20857
21004
  function post(endpoint, body) {
20858
21005
  return new Promise((resolve6) => {
20859
21006
  const payload = JSON.stringify(body);
20860
- const u2 = new URL(`${API_BASE6}${endpoint}`);
20861
- const transport = u2.protocol === "https:" ? https6 : http6;
21007
+ const u2 = new URL(`${API_BASE7}${endpoint}`);
21008
+ const transport = u2.protocol === "https:" ? https7 : http7;
20862
21009
  const req = transport.request(
20863
21010
  {
20864
21011
  hostname: u2.hostname,
@@ -21306,7 +21453,7 @@ var RepoDirtyTracker = class {
21306
21453
 
21307
21454
  // src/services/streaming-emitter.service.ts
21308
21455
  var import_crypto5 = require("crypto");
21309
- var API_BASE7 = resolveApiBaseUrl();
21456
+ var API_BASE8 = resolveApiBaseUrl();
21310
21457
  var TICK_MS = 50;
21311
21458
  var ANSWER_POLL_MS = 1500;
21312
21459
  var SELECTOR_STABLE_MS = 800;
@@ -21317,7 +21464,7 @@ var TAIL_KEEP_BYTES2 = 1.5 * 1024 * 1024;
21317
21464
  var StreamingEmitterService = class {
21318
21465
  constructor(opts) {
21319
21466
  this.opts = opts;
21320
- this.apiBase = opts.apiBaseUrl ?? API_BASE7;
21467
+ this.apiBase = opts.apiBaseUrl ?? API_BASE8;
21321
21468
  this.headers = {
21322
21469
  "Content-Type": "application/json",
21323
21470
  "X-Codeam-Protocol-Version": "2.0.0",
@@ -22036,7 +22183,7 @@ async function pair(args2 = []) {
22036
22183
  console.log("");
22037
22184
  process.exit(0);
22038
22185
  }
22039
- stopPolling = pollStatus(
22186
+ stopPolling = subscribeToPairCompletion(
22040
22187
  pluginId,
22041
22188
  (info) => {
22042
22189
  process.removeListener("SIGINT", sigintHandler);
@@ -22282,7 +22429,7 @@ async function startInfraOnly(agentId) {
22282
22429
  }
22283
22430
 
22284
22431
  // src/commands/pair-auto.ts
22285
- var API_BASE8 = resolveApiBaseUrl();
22432
+ var API_BASE9 = resolveApiBaseUrl();
22286
22433
  function fail(msg) {
22287
22434
  console.error(`
22288
22435
  ${msg}
@@ -22322,7 +22469,7 @@ function networkError(msg, cause) {
22322
22469
  return err;
22323
22470
  }
22324
22471
  async function claimOnce(token, pluginId) {
22325
- const url = `${API_BASE8}/api/pairing/claim-auto-token`;
22472
+ const url = `${API_BASE9}/api/pairing/claim-auto-token`;
22326
22473
  const body = {
22327
22474
  token,
22328
22475
  pluginId,
@@ -22525,7 +22672,7 @@ function status() {
22525
22672
 
22526
22673
  // src/commands/logout.ts
22527
22674
  var import_picocolors7 = __toESM(require("picocolors"));
22528
- var API_BASE9 = resolveApiBaseUrl();
22675
+ var API_BASE10 = resolveApiBaseUrl();
22529
22676
  async function notifyBackendOffline() {
22530
22677
  const cfg = loadCliConfig();
22531
22678
  const pluginIds = /* @__PURE__ */ new Set([
@@ -22537,7 +22684,7 @@ async function notifyBackendOffline() {
22537
22684
  try {
22538
22685
  await Promise.all(
22539
22686
  Array.from(pluginIds).map(
22540
- (pluginId) => _postJson(`${API_BASE9}/api/plugin/heartbeat`, {
22687
+ (pluginId) => _postJson(`${API_BASE10}/api/plugin/heartbeat`, {
22541
22688
  pluginId,
22542
22689
  online: false
22543
22690
  }).catch((err) => {
@@ -24679,7 +24826,7 @@ function checkChokidar() {
24679
24826
  }
24680
24827
  async function doctor(args2 = []) {
24681
24828
  const json = args2.includes("--json");
24682
- const cliVersion = true ? "2.28.1" : "0.0.0-dev";
24829
+ const cliVersion = true ? "2.30.0" : "0.0.0-dev";
24683
24830
  const apiBase = resolveApiBaseUrl();
24684
24831
  const diagnosticId = (0, import_node_crypto8.randomUUID)();
24685
24832
  log.info("doctor", `run id=${diagnosticId} cli=${cliVersion}`);
@@ -24878,7 +25025,7 @@ async function completion(args2) {
24878
25025
  // src/commands/version.ts
24879
25026
  var import_picocolors13 = __toESM(require("picocolors"));
24880
25027
  function version2() {
24881
- const v = true ? "2.28.1" : "unknown";
25028
+ const v = true ? "2.30.0" : "unknown";
24882
25029
  console.log(`${import_picocolors13.default.bold("codeam-cli")} ${import_picocolors13.default.cyan(v)}`);
24883
25030
  }
24884
25031
 
@@ -25009,7 +25156,7 @@ var _subcommandHelpKeys = Object.keys(HELPS);
25009
25156
  var fs35 = __toESM(require("fs"));
25010
25157
  var os27 = __toESM(require("os"));
25011
25158
  var path44 = __toESM(require("path"));
25012
- var https7 = __toESM(require("https"));
25159
+ var https8 = __toESM(require("https"));
25013
25160
  var import_node_child_process12 = require("child_process");
25014
25161
  var import_picocolors16 = __toESM(require("picocolors"));
25015
25162
  var PKG_NAME = "codeam-cli";
@@ -25055,7 +25202,7 @@ function compareSemver(a, b) {
25055
25202
  }
25056
25203
  function fetchLatest() {
25057
25204
  return new Promise((resolve6) => {
25058
- const req = https7.get(
25205
+ const req = https8.get(
25059
25206
  REGISTRY_URL,
25060
25207
  { headers: { Accept: "application/json" }, timeout: REQUEST_TIMEOUT_MS },
25061
25208
  (res) => {
@@ -25164,7 +25311,7 @@ function checkForUpdates() {
25164
25311
  if (process.env.CODEAM_DISABLE_UPDATE_CHECK === "1") return;
25165
25312
  if (process.env.CI) return;
25166
25313
  if (!process.stdout.isTTY) return;
25167
- const current = true ? "2.28.1" : null;
25314
+ const current = true ? "2.30.0" : null;
25168
25315
  if (!current) return;
25169
25316
  const cache = readCache();
25170
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.28.1",
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",