codeam-cli 2.31.0 → 2.32.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 +269 -178
  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.31.0] — 2026-06-07
8
+
9
+ ### Chore
10
+
11
+ - **cli:** Drop originator-HMAC fields from /api/pairing/code
12
+
7
13
  ## [2.30.0] — 2026-06-06
8
14
 
9
15
  ### 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.31.0",
501
+ version: "2.32.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",
@@ -706,12 +706,27 @@ async function requestCode(pluginId) {
706
706
  });
707
707
  const result = await Promise.race([post2, timeoutPromise]);
708
708
  clearTimeout(timer);
709
- if (result === timeoutSentinel) return null;
709
+ if (result === timeoutSentinel) return { ok: false, reason: "timeout" };
710
710
  const data = result?.data;
711
- if (!data?.code) return null;
712
- return { code: data.code, expiresAt: data.expiresAt };
713
- } catch {
714
- return null;
711
+ if (!data?.code) return { ok: false, reason: "network" };
712
+ return {
713
+ ok: true,
714
+ code: data.code,
715
+ expiresAt: data.expiresAt
716
+ };
717
+ } catch (err) {
718
+ const e = err;
719
+ if (e.statusCode === 429) {
720
+ return {
721
+ ok: false,
722
+ reason: "rate-limited",
723
+ retryAfterSeconds: typeof e.retryAfterSeconds === "number" && e.retryAfterSeconds > 0 ? e.retryAfterSeconds : 60
724
+ };
725
+ }
726
+ if (typeof e.statusCode === "number") {
727
+ return { ok: false, reason: "http", status: e.statusCode };
728
+ }
729
+ return { ok: false, reason: "network" };
715
730
  }
716
731
  }
717
732
  async function fetchCurrentPluginAuthToken(sessionId, pluginId) {
@@ -857,9 +872,9 @@ async function _postJsonAuthed(url, body, pluginAuthToken) {
857
872
  });
858
873
  res.on("end", () => {
859
874
  if (res.statusCode && res.statusCode >= 400) {
860
- const err = new Error(`HTTP ${res.statusCode}: ${responseBody.slice(0, 200)}`);
861
- err.statusCode = res.statusCode;
862
- reject(err);
875
+ reject(
876
+ makeHttpError(res.statusCode, res.headers["retry-after"], responseBody)
877
+ );
863
878
  return;
864
879
  }
865
880
  try {
@@ -879,6 +894,16 @@ async function _postJsonAuthed(url, body, pluginAuthToken) {
879
894
  req.end();
880
895
  });
881
896
  }
897
+ function makeHttpError(statusCode, retryAfterHeader, responseBody) {
898
+ const raw = Array.isArray(retryAfterHeader) ? retryAfterHeader[0] : retryAfterHeader;
899
+ const retryAfterSeconds = raw && /^\d+$/.test(raw.trim()) ? Number.parseInt(raw, 10) : void 0;
900
+ const err = new Error(
901
+ `HTTP ${statusCode}${responseBody ? ": " + responseBody.slice(0, 200) : ""}`
902
+ );
903
+ err.statusCode = statusCode;
904
+ if (typeof retryAfterSeconds === "number") err.retryAfterSeconds = retryAfterSeconds;
905
+ return err;
906
+ }
882
907
  async function _postJson(url, body) {
883
908
  return new Promise((resolve6, reject) => {
884
909
  const data = JSON.stringify(body);
@@ -905,7 +930,7 @@ async function _postJson(url, body) {
905
930
  });
906
931
  res.on("end", () => {
907
932
  if (res.statusCode && res.statusCode >= 400) {
908
- reject(new Error(`HTTP ${res.statusCode}`));
933
+ reject(makeHttpError(res.statusCode, res.headers["retry-after"], body2));
909
934
  return;
910
935
  }
911
936
  try {
@@ -946,7 +971,7 @@ async function _getJson(url) {
946
971
  });
947
972
  res.on("end", () => {
948
973
  if (res.statusCode && res.statusCode >= 400) {
949
- reject(new Error(`HTTP ${res.statusCode}`));
974
+ reject(makeHttpError(res.statusCode, res.headers["retry-after"], body));
950
975
  return;
951
976
  }
952
977
  try {
@@ -1130,8 +1155,8 @@ function createGetModuleFromFilename(basePath = process.argv[1] ? (0, import_pat
1130
1155
  return decodedFile;
1131
1156
  };
1132
1157
  }
1133
- function normalizeWindowsPath(path45) {
1134
- return path45.replace(/^[A-Z]:/, "").replace(/\\/g, "/");
1158
+ function normalizeWindowsPath(path46) {
1159
+ return path46.replace(/^[A-Z]:/, "").replace(/\\/g, "/");
1135
1160
  }
1136
1161
 
1137
1162
  // ../../node_modules/@posthog/core/dist/featureFlagUtils.mjs
@@ -3611,9 +3636,9 @@ async function addSourceContext(frames) {
3611
3636
  LRU_FILE_CONTENTS_CACHE.reduce();
3612
3637
  return frames;
3613
3638
  }
3614
- function getContextLinesFromFile(path45, ranges, output) {
3639
+ function getContextLinesFromFile(path46, ranges, output) {
3615
3640
  return new Promise((resolve6) => {
3616
- const stream = (0, import_node_fs.createReadStream)(path45);
3641
+ const stream = (0, import_node_fs.createReadStream)(path46);
3617
3642
  const lineReaded = (0, import_node_readline.createInterface)({
3618
3643
  input: stream
3619
3644
  });
@@ -3628,7 +3653,7 @@ function getContextLinesFromFile(path45, ranges, output) {
3628
3653
  let rangeStart = range[0];
3629
3654
  let rangeEnd = range[1];
3630
3655
  function onStreamError() {
3631
- LRU_FILE_CONTENTS_FS_READ_FAILED.set(path45, 1);
3656
+ LRU_FILE_CONTENTS_FS_READ_FAILED.set(path46, 1);
3632
3657
  lineReaded.close();
3633
3658
  lineReaded.removeAllListeners();
3634
3659
  destroyStreamAndResolve();
@@ -3689,8 +3714,8 @@ function clearLineContext(frame) {
3689
3714
  delete frame.context_line;
3690
3715
  delete frame.post_context;
3691
3716
  }
3692
- function shouldSkipContextLinesForFile(path45) {
3693
- return path45.startsWith("node:") || path45.endsWith(".min.js") || path45.endsWith(".min.cjs") || path45.endsWith(".min.mjs") || path45.startsWith("data:");
3717
+ function shouldSkipContextLinesForFile(path46) {
3718
+ return path46.startsWith("node:") || path46.endsWith(".min.js") || path46.endsWith(".min.cjs") || path46.endsWith(".min.mjs") || path46.startsWith("data:");
3694
3719
  }
3695
3720
  function shouldSkipContextLinesForFrame(frame) {
3696
3721
  if (void 0 !== frame.lineno && frame.lineno > MAX_CONTEXTLINES_LINENO) return true;
@@ -5844,7 +5869,7 @@ function readAnonId() {
5844
5869
  }
5845
5870
  function superProperties() {
5846
5871
  return {
5847
- cliVersion: true ? "2.31.0" : "0.0.0-dev",
5872
+ cliVersion: true ? "2.32.0" : "0.0.0-dev",
5848
5873
  nodeVersion: process.version,
5849
5874
  platform: process.platform,
5850
5875
  arch: process.arch,
@@ -7186,10 +7211,10 @@ function buildForPlatform(platform2) {
7186
7211
  var import_node_crypto4 = require("crypto");
7187
7212
 
7188
7213
  // src/agents/claude/resolver.ts
7189
- function buildClaudeLaunch(extraArgs = [], os28 = createOsStrategy()) {
7190
- const found = os28.findInPath("claude") ?? os28.findInPath("claude-code");
7214
+ function buildClaudeLaunch(extraArgs = [], os29 = createOsStrategy()) {
7215
+ const found = os29.findInPath("claude") ?? os29.findInPath("claude-code");
7191
7216
  if (!found) return null;
7192
- return os28.buildLaunch(found, extraArgs);
7217
+ return os29.buildLaunch(found, extraArgs);
7193
7218
  }
7194
7219
 
7195
7220
  // src/agents/claude/installer.ts
@@ -9916,13 +9941,13 @@ function detectStartupBanner(lines) {
9916
9941
  while (artStart > 0 && BANNER_ART_RE.test(lines[artStart - 1])) artStart--;
9917
9942
  if (metaIdx - artStart < 2) return null;
9918
9943
  const pathLine = (lines[metaIdx + 1] ?? "").trim();
9919
- const path45 = pathLine && !BANNER_ART_RE.test(pathLine) ? pathLine : "";
9944
+ const path46 = pathLine && !BANNER_ART_RE.test(pathLine) ? pathLine : "";
9920
9945
  return {
9921
9946
  title: "",
9922
9947
  subtitle: lines[metaIdx].trim(),
9923
- path: path45,
9948
+ path: path46,
9924
9949
  startIdx: artStart,
9925
- endIdx: metaIdx + (path45 ? 1 : 0)
9950
+ endIdx: metaIdx + (path46 ? 1 : 0)
9926
9951
  };
9927
9952
  }
9928
9953
 
@@ -9932,8 +9957,8 @@ var ClaudeRuntimeStrategy = class {
9932
9957
  meta = getAgent("claude");
9933
9958
  mode = "interactive";
9934
9959
  os;
9935
- constructor(os28) {
9936
- this.os = os28;
9960
+ constructor(os29) {
9961
+ this.os = os29;
9937
9962
  }
9938
9963
  /**
9939
9964
  * Claude Code's react-ink TUI enables bracketed-paste mode at
@@ -10941,8 +10966,8 @@ function codexCredentialLocator() {
10941
10966
  function codexLoginLauncher() {
10942
10967
  return {
10943
10968
  async ensureInstalled() {
10944
- const os28 = createOsStrategy();
10945
- return os28.findInPath("codex") !== null;
10969
+ const os29 = createOsStrategy();
10970
+ return os29.findInPath("codex") !== null;
10946
10971
  },
10947
10972
  launch() {
10948
10973
  return (0, import_node_child_process3.spawn)("codex", ["login"], { stdio: "inherit" });
@@ -10965,8 +10990,8 @@ var CodexRuntimeStrategy = class {
10965
10990
  meta = getAgent("codex");
10966
10991
  mode = "interactive";
10967
10992
  os;
10968
- constructor(os28) {
10969
- this.os = os28;
10993
+ constructor(os29) {
10994
+ this.os = os29;
10970
10995
  }
10971
10996
  async prepareLaunch() {
10972
10997
  let binary = this.os.findInPath("codex");
@@ -11072,12 +11097,12 @@ var CodexRuntimeStrategy = class {
11072
11097
  });
11073
11098
  }
11074
11099
  };
11075
- function resolveNpm(os28) {
11076
- return os28.id === "win32" ? "npm.cmd" : "npm";
11100
+ function resolveNpm(os29) {
11101
+ return os29.id === "win32" ? "npm.cmd" : "npm";
11077
11102
  }
11078
- async function installCodexViaNpm(os28) {
11103
+ async function installCodexViaNpm(os29) {
11079
11104
  return new Promise((resolve6, reject) => {
11080
- const proc = (0, import_node_child_process4.spawn)(resolveNpm(os28), ["install", "-g", "@openai/codex"], {
11105
+ const proc = (0, import_node_child_process4.spawn)(resolveNpm(os29), ["install", "-g", "@openai/codex"], {
11081
11106
  stdio: "inherit"
11082
11107
  });
11083
11108
  proc.on("close", (code) => {
@@ -11094,16 +11119,16 @@ async function installCodexViaNpm(os28) {
11094
11119
  });
11095
11120
  });
11096
11121
  }
11097
- function augmentNpmGlobalBin(os28) {
11122
+ function augmentNpmGlobalBin(os29) {
11098
11123
  try {
11099
- const result = (0, import_node_child_process4.spawnSync)(resolveNpm(os28), ["prefix", "-g"], {
11124
+ const result = (0, import_node_child_process4.spawnSync)(resolveNpm(os29), ["prefix", "-g"], {
11100
11125
  stdio: ["ignore", "pipe", "ignore"]
11101
11126
  });
11102
11127
  if (result.status !== 0) return;
11103
11128
  const prefix = result.stdout.toString().trim();
11104
11129
  if (!prefix) return;
11105
- const binDir = os28.id === "win32" ? prefix : path17.join(prefix, "bin");
11106
- os28.augmentPath([binDir]);
11130
+ const binDir = os29.id === "win32" ? prefix : path17.join(prefix, "bin");
11131
+ os29.augmentPath([binDir]);
11107
11132
  } catch {
11108
11133
  }
11109
11134
  }
@@ -11187,9 +11212,9 @@ var import_node_child_process7 = require("child_process");
11187
11212
  // src/agents/coderabbit/installer.ts
11188
11213
  var import_node_child_process5 = require("child_process");
11189
11214
  var INSTALL_URL = "https://cli.coderabbit.ai/install.sh";
11190
- async function ensureCoderabbitInstalled(os28) {
11191
- if (os28.findInPath("coderabbit")) return true;
11192
- if (os28.id === "win32") {
11215
+ async function ensureCoderabbitInstalled(os29) {
11216
+ if (os29.findInPath("coderabbit")) return true;
11217
+ if (os29.id === "win32") {
11193
11218
  console.error(
11194
11219
  "\n \u2717 CodeRabbit on Windows requires WSL.\n Install the CLI inside your WSL distribution\n (curl -fsSL https://cli.coderabbit.ai/install.sh | sh)\n then re-run `codeam link coderabbit` from WSL.\n"
11195
11220
  );
@@ -11204,8 +11229,8 @@ async function ensureCoderabbitInstalled(os28) {
11204
11229
  proc.on("error", () => resolve6(false));
11205
11230
  });
11206
11231
  if (!ok) return false;
11207
- os28.augmentPath([`${os28.homeDir()}/.local/bin`, "/opt/homebrew/bin"]);
11208
- return os28.findInPath("coderabbit") !== null;
11232
+ os29.augmentPath([`${os29.homeDir()}/.local/bin`, "/opt/homebrew/bin"]);
11233
+ return os29.findInPath("coderabbit") !== null;
11209
11234
  }
11210
11235
 
11211
11236
  // src/agents/coderabbit/link.ts
@@ -11232,10 +11257,10 @@ function coderabbitCredentialLocator() {
11232
11257
  extract: extractLocalCoderabbitToken
11233
11258
  };
11234
11259
  }
11235
- function coderabbitLoginLauncher(os28) {
11260
+ function coderabbitLoginLauncher(os29) {
11236
11261
  return {
11237
11262
  async ensureInstalled() {
11238
- return ensureCoderabbitInstalled(os28);
11263
+ return ensureCoderabbitInstalled(os29);
11239
11264
  },
11240
11265
  launch() {
11241
11266
  return (0, import_node_child_process6.spawn)("coderabbit", ["login"], { stdio: "inherit" });
@@ -11258,11 +11283,11 @@ function parseReview(stdout) {
11258
11283
  for (const line of lines) {
11259
11284
  const m = line.match(HUNK_LINE_RE);
11260
11285
  if (!m) continue;
11261
- const [, path45, lineNo, sevToken, message] = m;
11262
- if (!path45 || !lineNo || !message) continue;
11286
+ const [, path46, lineNo, sevToken, message] = m;
11287
+ if (!path46 || !lineNo || !message) continue;
11263
11288
  const cleanedMessage = message.trim().replace(/^[*-]\s+/, "");
11264
11289
  hunks.push({
11265
- path: path45.trim(),
11290
+ path: path46.trim(),
11266
11291
  line: Number(lineNo),
11267
11292
  severity: sevToken ? SEVERITY_MAP[sevToken.toLowerCase()] : void 0,
11268
11293
  message: cleanedMessage
@@ -11281,8 +11306,8 @@ var CoderabbitRuntimeStrategy = class {
11281
11306
  meta = getAgent("coderabbit");
11282
11307
  mode = "batch";
11283
11308
  os;
11284
- constructor(os28) {
11285
- this.os = os28;
11309
+ constructor(os29) {
11310
+ this.os = os29;
11286
11311
  }
11287
11312
  getDefaultArgs() {
11288
11313
  return ["review"];
@@ -11397,10 +11422,10 @@ function cursorCredentialLocator() {
11397
11422
  extract: extractLocalCursorToken
11398
11423
  };
11399
11424
  }
11400
- function cursorLoginLauncher(os28) {
11425
+ function cursorLoginLauncher(os29) {
11401
11426
  return {
11402
11427
  async ensureInstalled() {
11403
- if (os28.findInPath("cursor-agent")) return true;
11428
+ if (os29.findInPath("cursor-agent")) return true;
11404
11429
  console.error(
11405
11430
  "\n \u2717 cursor-agent binary not on PATH.\n Install Cursor (https://cursor.com/) and ensure the CLI\n plugin is enabled, then re-run `codeam link cursor`.\n"
11406
11431
  );
@@ -11462,8 +11487,8 @@ var CursorRuntimeStrategy = class {
11462
11487
  meta = getAgent("cursor");
11463
11488
  mode = "interactive";
11464
11489
  os;
11465
- constructor(os28) {
11466
- this.os = os28;
11490
+ constructor(os29) {
11491
+ this.os = os29;
11467
11492
  }
11468
11493
  async prepareLaunch() {
11469
11494
  const binary = this.os.findInPath("cursor-agent");
@@ -11583,10 +11608,10 @@ function aiderCredentialLocator() {
11583
11608
  extract: extractLocalAiderToken
11584
11609
  };
11585
11610
  }
11586
- function aiderLoginLauncher(os28) {
11611
+ function aiderLoginLauncher(os29) {
11587
11612
  return {
11588
11613
  async ensureInstalled() {
11589
- if (os28.findInPath("aider")) return true;
11614
+ if (os29.findInPath("aider")) return true;
11590
11615
  console.error(
11591
11616
  "\n \u2717 aider binary not on PATH.\n Install Aider:\n pip install aider-chat\n then re-run `codeam link aider`.\n"
11592
11617
  );
@@ -11596,7 +11621,7 @@ function aiderLoginLauncher(os28) {
11596
11621
  console.error(
11597
11622
  "\n Aider has no interactive login flow.\n Set ANTHROPIC_API_KEY or OPENAI_API_KEY in your shell,\n or re-run `codeam link aider --api-key=<your-key>`.\n"
11598
11623
  );
11599
- return (0, import_node_child_process9.spawn)(os28.id === "win32" ? "cmd.exe" : "sh", os28.id === "win32" ? ["/c", "exit", "0"] : ["-c", "exit 0"], {
11624
+ return (0, import_node_child_process9.spawn)(os29.id === "win32" ? "cmd.exe" : "sh", os29.id === "win32" ? ["/c", "exit", "0"] : ["-c", "exit 0"], {
11600
11625
  stdio: "ignore"
11601
11626
  });
11602
11627
  }
@@ -11668,8 +11693,8 @@ var AiderRuntimeStrategy = class {
11668
11693
  meta = getAgent("aider");
11669
11694
  mode = "interactive";
11670
11695
  os;
11671
- constructor(os28) {
11672
- this.os = os28;
11696
+ constructor(os29) {
11697
+ this.os = os29;
11673
11698
  }
11674
11699
  async prepareLaunch() {
11675
11700
  const binary = this.os.findInPath("aider");
@@ -11792,8 +11817,8 @@ function geminiCredentialLocator() {
11792
11817
  function geminiLoginLauncher() {
11793
11818
  return {
11794
11819
  async ensureInstalled() {
11795
- const os28 = createOsStrategy();
11796
- return os28.findInPath("gemini") !== null;
11820
+ const os29 = createOsStrategy();
11821
+ return os29.findInPath("gemini") !== null;
11797
11822
  },
11798
11823
  launch() {
11799
11824
  return (0, import_node_child_process10.spawn)("gemini", ["auth", "login"], { stdio: "inherit" });
@@ -11826,8 +11851,8 @@ var GeminiRuntimeStrategy = class {
11826
11851
  meta = getAgent("gemini");
11827
11852
  mode = "interactive";
11828
11853
  os;
11829
- constructor(os28) {
11830
- this.os = os28;
11854
+ constructor(os29) {
11855
+ this.os = os29;
11831
11856
  }
11832
11857
  async prepareLaunch() {
11833
11858
  const binary = this.os.findInPath("gemini");
@@ -11927,18 +11952,18 @@ var GeminiRuntimeStrategy = class {
11927
11952
 
11928
11953
  // src/agents/registry.ts
11929
11954
  var runtimeBuilders = {
11930
- claude: (os28) => new ClaudeRuntimeStrategy(os28),
11931
- codex: (os28) => new CodexRuntimeStrategy(os28),
11932
- coderabbit: (os28) => new CoderabbitRuntimeStrategy(os28),
11933
- cursor: (os28) => new CursorRuntimeStrategy(os28),
11934
- aider: (os28) => new AiderRuntimeStrategy(os28),
11935
- gemini: (os28) => new GeminiRuntimeStrategy(os28)
11955
+ claude: (os29) => new ClaudeRuntimeStrategy(os29),
11956
+ codex: (os29) => new CodexRuntimeStrategy(os29),
11957
+ coderabbit: (os29) => new CoderabbitRuntimeStrategy(os29),
11958
+ cursor: (os29) => new CursorRuntimeStrategy(os29),
11959
+ aider: (os29) => new AiderRuntimeStrategy(os29),
11960
+ gemini: (os29) => new GeminiRuntimeStrategy(os29)
11936
11961
  };
11937
11962
  var deployBuilders = {
11938
11963
  claude: () => new ClaudeDeployStrategy(),
11939
11964
  codex: () => new CodexDeployStrategy()
11940
11965
  };
11941
- function createAgentStrategy(agent, os28 = createOsStrategy()) {
11966
+ function createAgentStrategy(agent, os29 = createOsStrategy()) {
11942
11967
  if (!AGENT_REGISTRY[agent]?.enabled) {
11943
11968
  throw new Error(
11944
11969
  `Agent "${agent}" is not supported in this codeam-cli version. Upgrade with 'npm i -g codeam-cli@latest'.`
@@ -11948,10 +11973,10 @@ function createAgentStrategy(agent, os28 = createOsStrategy()) {
11948
11973
  if (!build) {
11949
11974
  throw new Error(`No runtime strategy registered for agent "${agent}"`);
11950
11975
  }
11951
- return build(os28);
11976
+ return build(os29);
11952
11977
  }
11953
- function createInteractiveAgentStrategy(agent, os28 = createOsStrategy()) {
11954
- const s = createAgentStrategy(agent, os28);
11978
+ function createInteractiveAgentStrategy(agent, os29 = createOsStrategy()) {
11979
+ const s = createAgentStrategy(agent, os29);
11955
11980
  if (s.mode !== "interactive") {
11956
11981
  throw new Error(
11957
11982
  `Agent "${agent}" is a batch agent; use createAgentStrategy + .runOneShot for one-shot reviews.`
@@ -12051,6 +12076,9 @@ var import_node_crypto7 = require("crypto");
12051
12076
  // src/agents/acp/client.ts
12052
12077
  var import_node_child_process11 = require("child_process");
12053
12078
  var fs21 = __toESM(require("fs/promises"));
12079
+ var fsSync = __toESM(require("fs"));
12080
+ var os22 = __toESM(require("os"));
12081
+ var path26 = __toESM(require("path"));
12054
12082
  var import_node_stream = require("stream");
12055
12083
 
12056
12084
  // ../../node_modules/@agentclientprotocol/sdk/dist/acp.js
@@ -14529,13 +14557,14 @@ var AcpClient = class {
14529
14557
  async start() {
14530
14558
  if (this.child) throw new Error("AcpClient already started");
14531
14559
  const { adapter, cwd } = this.opts;
14560
+ const augmentedPath = expandPathForAgentBinaries(process.env.PATH ?? "");
14532
14561
  log.info(
14533
14562
  "acpClient",
14534
14563
  `spawn cmd=${adapter.command} args=[${adapter.args.join(",")}] cwd=${cwd}`
14535
14564
  );
14536
14565
  const child = (0, import_node_child_process11.spawn)(adapter.command, adapter.args, {
14537
14566
  cwd,
14538
- env: process.env,
14567
+ env: { ...process.env, PATH: augmentedPath },
14539
14568
  stdio: ["pipe", "pipe", "pipe"]
14540
14569
  });
14541
14570
  this.child = child;
@@ -14812,6 +14841,48 @@ function applyLineRange(content, line, limit) {
14812
14841
  const end = limit !== null ? start2 + limit : lines.length;
14813
14842
  return { content: lines.slice(start2, end).join("\n") };
14814
14843
  }
14844
+ function knownAgentBinaryDirs() {
14845
+ const home = os22.homedir();
14846
+ const out2 = [];
14847
+ out2.push("/tmp/codeam-node20/bin");
14848
+ for (const root of [
14849
+ "/usr/local/share/nvm/versions/node",
14850
+ path26.join(home, ".nvm/versions/node")
14851
+ ]) {
14852
+ try {
14853
+ for (const child of fsSync.readdirSync(root)) {
14854
+ out2.push(path26.join(root, child, "bin"));
14855
+ }
14856
+ } catch {
14857
+ }
14858
+ }
14859
+ out2.push(path26.join(home, ".volta/bin"));
14860
+ out2.push("/usr/local/bin");
14861
+ out2.push("/usr/bin");
14862
+ out2.push(path26.join(home, ".local/bin"));
14863
+ out2.push(path26.join(home, "bin"));
14864
+ return out2.filter((p2) => {
14865
+ try {
14866
+ return fsSync.statSync(p2).isDirectory();
14867
+ } catch {
14868
+ return false;
14869
+ }
14870
+ });
14871
+ }
14872
+ function expandPathForAgentBinaries(existingPath) {
14873
+ const existing = new Set(
14874
+ existingPath.split(path26.delimiter).filter((p2) => p2.length > 0)
14875
+ );
14876
+ const additions = [];
14877
+ for (const dir of knownAgentBinaryDirs()) {
14878
+ if (!existing.has(dir)) {
14879
+ additions.push(dir);
14880
+ existing.add(dir);
14881
+ }
14882
+ }
14883
+ if (additions.length === 0) return existingPath;
14884
+ return [...additions, existingPath].filter((p2) => p2.length > 0).join(path26.delimiter);
14885
+ }
14815
14886
 
14816
14887
  // src/services/streaming/transport.ts
14817
14888
  var http3 = __toESM(require("http"));
@@ -15597,8 +15668,8 @@ function extractSelectPrompt(text) {
15597
15668
 
15598
15669
  // src/commands/start/handlers.ts
15599
15670
  var fs29 = __toESM(require("fs"));
15600
- var os23 = __toESM(require("os"));
15601
- var path34 = __toESM(require("path"));
15671
+ var os24 = __toESM(require("os"));
15672
+ var path35 = __toESM(require("path"));
15602
15673
  var import_crypto3 = require("crypto");
15603
15674
  var import_child_process12 = require("child_process");
15604
15675
 
@@ -15723,7 +15794,7 @@ function parsePayload2(schema, raw) {
15723
15794
 
15724
15795
  // src/services/file-ops.service.ts
15725
15796
  var fs22 = __toESM(require("fs/promises"));
15726
- var path27 = __toESM(require("path"));
15797
+ var path28 = __toESM(require("path"));
15727
15798
  var MAX_FILE_BYTES = 5 * 1024 * 1024;
15728
15799
  var MAX_WALK_DEPTH = 6;
15729
15800
  var MAX_VISITED_DIRS = 5e3;
@@ -15758,8 +15829,8 @@ var SUBDIR_IGNORE = /* @__PURE__ */ new Set([
15758
15829
  "__pycache__"
15759
15830
  ]);
15760
15831
  function isUnder(parent, candidate) {
15761
- const rel = path27.relative(parent, candidate);
15762
- return rel === "" || !rel.startsWith("..") && !path27.isAbsolute(rel);
15832
+ const rel = path28.relative(parent, candidate);
15833
+ return rel === "" || !rel.startsWith("..") && !path28.isAbsolute(rel);
15763
15834
  }
15764
15835
  async function isExistingFile(absPath) {
15765
15836
  try {
@@ -15782,7 +15853,7 @@ async function walkForSuffix(dir, needleVariants, depth, ctx) {
15782
15853
  }
15783
15854
  for (const e of entries) {
15784
15855
  if (!e.isFile()) continue;
15785
- const full = path27.join(dir, e.name);
15856
+ const full = path28.join(dir, e.name);
15786
15857
  if (needleVariants.some((needle) => full.endsWith(needle))) {
15787
15858
  ctx.matches.push(full);
15788
15859
  if (ctx.matches.length >= ctx.cap) return;
@@ -15792,21 +15863,21 @@ async function walkForSuffix(dir, needleVariants, depth, ctx) {
15792
15863
  if (!e.isDirectory()) continue;
15793
15864
  if (SUBDIR_IGNORE.has(e.name)) continue;
15794
15865
  if (e.name.startsWith(".") && SUBDIR_IGNORE.has(e.name)) continue;
15795
- await walkForSuffix(path27.join(dir, e.name), needleVariants, depth + 1, ctx);
15866
+ await walkForSuffix(path28.join(dir, e.name), needleVariants, depth + 1, ctx);
15796
15867
  if (ctx.matches.length >= ctx.cap) return;
15797
15868
  }
15798
15869
  }
15799
15870
  async function findFile(rawPath) {
15800
15871
  const cwd = process.cwd();
15801
- if (path27.isAbsolute(rawPath)) {
15802
- const abs = path27.normalize(rawPath);
15872
+ if (path28.isAbsolute(rawPath)) {
15873
+ const abs = path28.normalize(rawPath);
15803
15874
  if (isUnder(cwd, abs) && await isExistingFile(abs)) return abs;
15804
15875
  }
15805
- const direct = path27.resolve(cwd, rawPath);
15876
+ const direct = path28.resolve(cwd, rawPath);
15806
15877
  if (isUnder(cwd, direct) && await isExistingFile(direct)) return direct;
15807
- const normalized = path27.normalize(rawPath).replace(/^[./\\]+/, "");
15878
+ const normalized = path28.normalize(rawPath).replace(/^[./\\]+/, "");
15808
15879
  const needles = [
15809
- `${path27.sep}${normalized}`,
15880
+ `${path28.sep}${normalized}`,
15810
15881
  `/${normalized}`
15811
15882
  ].filter((v, i, a) => a.indexOf(v) === i);
15812
15883
  const ctx = { visited: 0, matches: [], cap: 16 };
@@ -15820,7 +15891,7 @@ async function findWriteTarget(rawPath) {
15820
15891
  const found = await findFile(rawPath);
15821
15892
  if (found) return found;
15822
15893
  const cwd = process.cwd();
15823
- const fallback = path27.isAbsolute(rawPath) ? path27.normalize(rawPath) : path27.resolve(cwd, rawPath);
15894
+ const fallback = path28.isAbsolute(rawPath) ? path28.normalize(rawPath) : path28.resolve(cwd, rawPath);
15824
15895
  if (!isUnder(cwd, fallback)) return null;
15825
15896
  return fallback;
15826
15897
  }
@@ -15860,7 +15931,7 @@ async function writeProjectFile(rawPath, content) {
15860
15931
  if (Buffer.byteLength(content, "utf-8") > MAX_FILE_BYTES) {
15861
15932
  return { error: "Content too large." };
15862
15933
  }
15863
- await fs22.mkdir(path27.dirname(abs), { recursive: true });
15934
+ await fs22.mkdir(path28.dirname(abs), { recursive: true });
15864
15935
  await fs22.writeFile(abs, content, "utf-8");
15865
15936
  return { ok: true };
15866
15937
  } catch (e) {
@@ -15873,7 +15944,7 @@ async function writeProjectFile(rawPath, content) {
15873
15944
  var import_child_process9 = require("child_process");
15874
15945
  var import_util2 = require("util");
15875
15946
  var fs23 = __toESM(require("fs/promises"));
15876
- var path28 = __toESM(require("path"));
15947
+ var path29 = __toESM(require("path"));
15877
15948
  var execFileP3 = (0, import_util2.promisify)(import_child_process9.execFile);
15878
15949
  var PROJECT_IGNORE = /* @__PURE__ */ new Set([
15879
15950
  "node_modules",
@@ -15931,12 +16002,12 @@ async function listProjectFiles(opts = {}) {
15931
16002
  return;
15932
16003
  }
15933
16004
  if (PROJECT_IGNORE.has(e.name)) continue;
15934
- const full = path28.join(dir, e.name);
16005
+ const full = path29.join(dir, e.name);
15935
16006
  if (e.isDirectory()) {
15936
16007
  if (depth >= 12) continue;
15937
16008
  await walk(full, depth + 1);
15938
16009
  } else if (e.isFile()) {
15939
- const rel = path28.relative(root, full);
16010
+ const rel = path29.relative(root, full);
15940
16011
  if (q2 && !rel.toLowerCase().includes(q2) && !e.name.toLowerCase().includes(q2)) {
15941
16012
  continue;
15942
16013
  }
@@ -16044,7 +16115,7 @@ async function gitStatus(cwd) {
16044
16115
  let hasMergeInProgress = false;
16045
16116
  try {
16046
16117
  const gitDir = (await git(["rev-parse", "--git-dir"], root)).stdout.trim();
16047
- const mergeHead = path28.isAbsolute(gitDir) ? path28.join(gitDir, "MERGE_HEAD") : path28.join(root, gitDir, "MERGE_HEAD");
16118
+ const mergeHead = path29.isAbsolute(gitDir) ? path29.join(gitDir, "MERGE_HEAD") : path29.join(root, gitDir, "MERGE_HEAD");
16048
16119
  await fs23.access(mergeHead);
16049
16120
  hasMergeInProgress = true;
16050
16121
  } catch {
@@ -16191,7 +16262,7 @@ async function jsSearchFiles(opts, cwd, cap) {
16191
16262
  }
16192
16263
  let content = "";
16193
16264
  try {
16194
- content = await fs23.readFile(path28.join(cwd, f.path), "utf8");
16265
+ content = await fs23.readFile(path29.join(cwd, f.path), "utf8");
16195
16266
  } catch {
16196
16267
  continue;
16197
16268
  }
@@ -16216,13 +16287,13 @@ async function jsSearchFiles(opts, cwd, cap) {
16216
16287
  // src/services/apply-file-review.service.ts
16217
16288
  var import_child_process10 = require("child_process");
16218
16289
  var fs24 = __toESM(require("fs"));
16219
- var path29 = __toESM(require("path"));
16290
+ var path30 = __toESM(require("path"));
16220
16291
  async function applyFileReview(workingDir, filePath, action) {
16221
- if (filePath.includes("..") || path29.isAbsolute(filePath)) {
16292
+ if (filePath.includes("..") || path30.isAbsolute(filePath)) {
16222
16293
  return { ok: false, action, filePath, error: "invalid file path" };
16223
16294
  }
16224
- const absFile = path29.resolve(workingDir, filePath);
16225
- const repoRoot = findGitRoot(path29.dirname(absFile));
16295
+ const absFile = path30.resolve(workingDir, filePath);
16296
+ const repoRoot = findGitRoot(path30.dirname(absFile));
16226
16297
  if (!repoRoot) {
16227
16298
  return {
16228
16299
  ok: false,
@@ -16231,7 +16302,7 @@ async function applyFileReview(workingDir, filePath, action) {
16231
16302
  error: `no enclosing git repo for ${filePath}`
16232
16303
  };
16233
16304
  }
16234
- const relInRepo = path29.relative(repoRoot, absFile);
16305
+ const relInRepo = path30.relative(repoRoot, absFile);
16235
16306
  if (!relInRepo || relInRepo.startsWith("..")) {
16236
16307
  return { ok: false, action, filePath, error: "path escapes repo root" };
16237
16308
  }
@@ -16280,17 +16351,17 @@ function runGit(cwd, args2) {
16280
16351
  });
16281
16352
  }
16282
16353
  function findGitRoot(startDir) {
16283
- let dir = path29.resolve(startDir);
16354
+ let dir = path30.resolve(startDir);
16284
16355
  const seen = /* @__PURE__ */ new Set();
16285
16356
  for (let i = 0; i < 256; i++) {
16286
16357
  if (seen.has(dir)) return null;
16287
16358
  seen.add(dir);
16288
16359
  try {
16289
- const stat3 = fs24.statSync(path29.join(dir, ".git"), { throwIfNoEntry: false });
16360
+ const stat3 = fs24.statSync(path30.join(dir, ".git"), { throwIfNoEntry: false });
16290
16361
  if (stat3 && (stat3.isDirectory() || stat3.isFile())) return dir;
16291
16362
  } catch {
16292
16363
  }
16293
- const parent = path29.dirname(dir);
16364
+ const parent = path30.dirname(dir);
16294
16365
  if (parent === dir) return null;
16295
16366
  dir = parent;
16296
16367
  }
@@ -16300,7 +16371,7 @@ function findGitRoot(startDir) {
16300
16371
  // src/commands/link.ts
16301
16372
  var import_node_crypto6 = require("crypto");
16302
16373
  var fs25 = __toESM(require("fs"));
16303
- var path30 = __toESM(require("path"));
16374
+ var path31 = __toESM(require("path"));
16304
16375
  var import_chokidar = __toESM(require("chokidar"));
16305
16376
  var import_picocolors2 = __toESM(require("picocolors"));
16306
16377
 
@@ -16479,7 +16550,7 @@ function parseLinkArgs(args2) {
16479
16550
  if (apiKeyFileArg) {
16480
16551
  const filePath = apiKeyFileArg.slice("--api-key-file=".length);
16481
16552
  try {
16482
- apiKey = fs25.readFileSync(path30.resolve(filePath), "utf8").trim();
16553
+ apiKey = fs25.readFileSync(path31.resolve(filePath), "utf8").trim();
16483
16554
  } catch (err) {
16484
16555
  throw new Error(`Could not read --api-key-file ${filePath}: ${err.message}`);
16485
16556
  }
@@ -16510,9 +16581,19 @@ async function link(args2 = []) {
16510
16581
  const spin = dist_exports.spinner();
16511
16582
  spin.start("Requesting pairing code...");
16512
16583
  const pairing = await requestCode(pluginId);
16513
- if (!pairing) {
16584
+ if (!pairing.ok) {
16514
16585
  spin.stop("Failed");
16515
- showError("Could not reach the server. Check your connection and try again.");
16586
+ if (pairing.reason === "rate-limited") {
16587
+ showError(
16588
+ `Server is rate-limiting this request (HTTP 429). Retry in ${pairing.retryAfterSeconds}s.`
16589
+ );
16590
+ } else if (pairing.reason === "timeout") {
16591
+ showError("Server took too long to respond. Check your connection and try again.");
16592
+ } else if (pairing.reason === "http") {
16593
+ showError(`Server returned HTTP ${pairing.status}. Try again later.`);
16594
+ } else {
16595
+ showError("Could not reach the server. Check your connection and try again.");
16596
+ }
16516
16597
  process.exit(1);
16517
16598
  }
16518
16599
  spin.stop("Got pairing code");
@@ -16573,7 +16654,7 @@ async function link(args2 = []) {
16573
16654
  return;
16574
16655
  }
16575
16656
  if (parsed.tokenFile) {
16576
- const credential = fs25.readFileSync(path30.resolve(parsed.tokenFile), "utf8").trim();
16657
+ const credential = fs25.readFileSync(path31.resolve(parsed.tokenFile), "utf8").trim();
16577
16658
  if (!credential) {
16578
16659
  showError(`--token-file ${parsed.tokenFile} is empty.`);
16579
16660
  process.exit(1);
@@ -17092,7 +17173,7 @@ function cleanupAttachmentTempFiles() {
17092
17173
  function saveFilesTemp(files) {
17093
17174
  return files.filter(({ base64 }) => base64 && base64.length > 0).map(({ filename, base64 }) => {
17094
17175
  const safeName = filename.replace(/[^a-zA-Z0-9._-]/g, "_").slice(0, 80);
17095
- const tmpPath = path34.join(os23.tmpdir(), `codeam-${(0, import_crypto3.randomUUID)()}-${safeName}`);
17176
+ const tmpPath = path35.join(os24.tmpdir(), `codeam-${(0, import_crypto3.randomUUID)()}-${safeName}`);
17096
17177
  fs29.writeFileSync(tmpPath, Buffer.from(base64, "base64"));
17097
17178
  pendingAttachmentFiles.add(tmpPath);
17098
17179
  return tmpPath;
@@ -18039,8 +18120,8 @@ async function dispatchCommand(ctx, cmd) {
18039
18120
  // src/services/file-watcher.service.ts
18040
18121
  var import_child_process13 = require("child_process");
18041
18122
  var fs30 = __toESM(require("fs"));
18042
- var os24 = __toESM(require("os"));
18043
- var path35 = __toESM(require("path"));
18123
+ var os25 = __toESM(require("os"));
18124
+ var path36 = __toESM(require("path"));
18044
18125
  var import_ignore = __toESM(require("ignore"));
18045
18126
 
18046
18127
  // src/services/file-watcher/diff-parser.ts
@@ -18199,10 +18280,10 @@ var WINDOWS_LEGACY_JUNCTIONS = [
18199
18280
  /[\\/]Start Menu([\\/]|$)/i,
18200
18281
  /[\\/]Templates([\\/]|$)/i
18201
18282
  ];
18202
- function isUnsafeWindowsWatchRoot(dir, homedir20) {
18283
+ function isUnsafeWindowsWatchRoot(dir, homedir21) {
18203
18284
  const norm = (p2) => p2.replace(/\//g, "\\").replace(/\\+$/, "").toLowerCase();
18204
18285
  const cwd = norm(dir);
18205
- const home = norm(homedir20);
18286
+ const home = norm(homedir21);
18206
18287
  if (cwd === home) return true;
18207
18288
  if (/^[a-z]:$/.test(cwd)) return true;
18208
18289
  const sysRoots = [
@@ -18232,18 +18313,18 @@ var _findGitRootSeam = {
18232
18313
  resolve: _defaultFindGitRoot
18233
18314
  };
18234
18315
  function _defaultFindGitRoot(startDir) {
18235
- let dir = path35.resolve(startDir);
18316
+ let dir = path36.resolve(startDir);
18236
18317
  const seen = /* @__PURE__ */ new Set();
18237
18318
  for (let i = 0; i < 256; i++) {
18238
18319
  if (seen.has(dir)) return null;
18239
18320
  seen.add(dir);
18240
18321
  try {
18241
- const gitPath = path35.join(dir, ".git");
18322
+ const gitPath = path36.join(dir, ".git");
18242
18323
  const stat3 = fs30.statSync(gitPath, { throwIfNoEntry: false });
18243
18324
  if (stat3 && (stat3.isDirectory() || stat3.isFile())) return dir;
18244
18325
  } catch {
18245
18326
  }
18246
- const parent = path35.dirname(dir);
18327
+ const parent = path36.dirname(dir);
18247
18328
  if (parent === dir) return null;
18248
18329
  dir = parent;
18249
18330
  }
@@ -18301,7 +18382,7 @@ var FileWatcherService = class {
18301
18382
  throw new Error("FileWatcherService has already been stopped \u2014 re-instantiate to restart.");
18302
18383
  }
18303
18384
  const isWin = process.platform === "win32";
18304
- if (isWin && isUnsafeWindowsWatchRoot(this.opts.workingDir, os24.homedir())) {
18385
+ if (isWin && isUnsafeWindowsWatchRoot(this.opts.workingDir, os25.homedir())) {
18305
18386
  log.warn(
18306
18387
  "fileWatcher",
18307
18388
  `refusing to watch ${this.opts.workingDir} \u2014 looks like a Windows user-profile or system path. Run codeam from your project folder to enable file change emission.`
@@ -18488,7 +18569,7 @@ var FileWatcherService = class {
18488
18569
  }
18489
18570
  async emitForFile(absPath, changeType) {
18490
18571
  if (this.stopped) return;
18491
- const fileDir = path35.dirname(absPath);
18572
+ const fileDir = path36.dirname(absPath);
18492
18573
  let gitRoot = this.gitRootByDir.get(fileDir);
18493
18574
  if (gitRoot === void 0) {
18494
18575
  gitRoot = findGitRoot2(fileDir);
@@ -18501,19 +18582,19 @@ var FileWatcherService = class {
18501
18582
  );
18502
18583
  return;
18503
18584
  }
18504
- const relPathInRepo = path35.relative(gitRoot, absPath);
18585
+ const relPathInRepo = path36.relative(gitRoot, absPath);
18505
18586
  if (!relPathInRepo || relPathInRepo.startsWith("..")) return;
18506
18587
  const matcher = this.getGitIgnoreMatcher(gitRoot);
18507
18588
  if (matcher && matcher.ignores(relPathInRepo)) {
18508
18589
  log.trace(
18509
18590
  "fileWatcher",
18510
- `${relPathInRepo} ignored by ${path35.basename(gitRoot)}/.gitignore \u2014 suppressing emit`
18591
+ `${relPathInRepo} ignored by ${path36.basename(gitRoot)}/.gitignore \u2014 suppressing emit`
18511
18592
  );
18512
18593
  return;
18513
18594
  }
18514
18595
  this.opts.onRepoDirty?.(gitRoot);
18515
- const repoPath = path35.relative(this.opts.workingDir, gitRoot);
18516
- const repoName = path35.basename(gitRoot);
18596
+ const repoPath = path36.relative(this.opts.workingDir, gitRoot);
18597
+ const repoName = path36.basename(gitRoot);
18517
18598
  let diffText = "";
18518
18599
  let fileStatus = "modified";
18519
18600
  if (changeType === "unlink") {
@@ -18697,16 +18778,16 @@ var FileWatcherService = class {
18697
18778
  );
18698
18779
  if (gitignoreEntry) {
18699
18780
  try {
18700
- const body = fs30.readFileSync(path35.join(dir, ".gitignore"), "utf8");
18701
- const rel = path35.relative(repoRoot, dir).replace(/\\/g, "/");
18781
+ const body = fs30.readFileSync(path36.join(dir, ".gitignore"), "utf8");
18782
+ const rel = path36.relative(repoRoot, dir).replace(/\\/g, "/");
18702
18783
  const prefixed = body.split(/\r?\n/).map((line) => {
18703
18784
  const trimmed = line.trim();
18704
18785
  if (!trimmed || trimmed.startsWith("#")) return line;
18705
18786
  if (!rel) return line;
18706
18787
  if (trimmed.startsWith("!")) {
18707
- return "!" + path35.posix.join(rel, trimmed.slice(1));
18788
+ return "!" + path36.posix.join(rel, trimmed.slice(1));
18708
18789
  }
18709
- return path35.posix.join(rel, trimmed);
18790
+ return path36.posix.join(rel, trimmed);
18710
18791
  }).join("\n");
18711
18792
  matcher.add(prefixed);
18712
18793
  } catch {
@@ -18715,7 +18796,7 @@ var FileWatcherService = class {
18715
18796
  for (const entry of entries) {
18716
18797
  if (!entry.isDirectory()) continue;
18717
18798
  if (entry.name === ".git") continue;
18718
- const childAbs = path35.join(dir, entry.name);
18799
+ const childAbs = path36.join(dir, entry.name);
18719
18800
  if (isIgnoredFilePath(childAbs)) continue;
18720
18801
  this.collectGitignoreFiles(repoRoot, childAbs, matcher);
18721
18802
  }
@@ -18885,7 +18966,7 @@ var import_crypto4 = require("crypto");
18885
18966
 
18886
18967
  // src/services/turn-files/git-changeset.ts
18887
18968
  var import_child_process14 = require("child_process");
18888
- var path36 = __toESM(require("path"));
18969
+ var path37 = __toESM(require("path"));
18889
18970
  async function collectRepoChangeset(opts) {
18890
18971
  const status2 = await runGit3(opts.repoRoot, ["status", "--porcelain=v1", "-z"]);
18891
18972
  if (status2 === null) return null;
@@ -19015,8 +19096,8 @@ async function discoverRepos(workingDir, maxDepth = 4) {
19015
19096
  if (hasGit) {
19016
19097
  out2.push({
19017
19098
  repoRoot: dir,
19018
- repoPath: path36.relative(workingDir, dir),
19019
- repoName: path36.basename(dir)
19099
+ repoPath: path37.relative(workingDir, dir),
19100
+ repoName: path37.basename(dir)
19020
19101
  });
19021
19102
  return;
19022
19103
  }
@@ -19024,14 +19105,14 @@ async function discoverRepos(workingDir, maxDepth = 4) {
19024
19105
  if (!entry.isDirectory) continue;
19025
19106
  if (entry.name === "node_modules") continue;
19026
19107
  if (entry.name === "dist" || entry.name === "build") continue;
19027
- await walk(path36.join(dir, entry.name), depth + 1);
19108
+ await walk(path37.join(dir, entry.name), depth + 1);
19028
19109
  }
19029
19110
  }
19030
19111
  }
19031
19112
 
19032
19113
  // src/services/turn-files/files-outbox.ts
19033
19114
  var fs31 = __toESM(require("fs/promises"));
19034
- var path37 = __toESM(require("path"));
19115
+ var path38 = __toESM(require("path"));
19035
19116
  var import_os7 = require("os");
19036
19117
  var HOME_OUTBOX_DIR = ".codeam/outbox";
19037
19118
  var MAX_AGE_MS = 24 * 60 * 60 * 1e3;
@@ -19064,15 +19145,15 @@ var FilesOutbox = class {
19064
19145
  backoffIndex = 0;
19065
19146
  stopped = false;
19066
19147
  constructor(opts) {
19067
- const base = opts.baseDir ?? path37.join(homeDir(), HOME_OUTBOX_DIR);
19068
- this.filePath = path37.join(base, `${opts.sessionId}.jsonl`);
19148
+ const base = opts.baseDir ?? path38.join(homeDir(), HOME_OUTBOX_DIR);
19149
+ this.filePath = path38.join(base, `${opts.sessionId}.jsonl`);
19069
19150
  this.post = opts.post;
19070
19151
  this.autoSchedule = opts.autoSchedule !== false;
19071
19152
  }
19072
19153
  /** Persist the entry to disk and trigger a flush. Returns once the
19073
19154
  * line is durable on disk (not once the POST succeeds). */
19074
19155
  async enqueue(entry) {
19075
- await fs31.mkdir(path37.dirname(this.filePath), { recursive: true });
19156
+ await fs31.mkdir(path38.dirname(this.filePath), { recursive: true });
19076
19157
  await fs31.appendFile(this.filePath, JSON.stringify(entry) + "\n", "utf8");
19077
19158
  this.backoffIndex = 0;
19078
19159
  if (this.autoSchedule) this.scheduleFlush(0);
@@ -20927,8 +21008,8 @@ var OutputService = class _OutputService {
20927
21008
 
20928
21009
  // src/services/history.service.ts
20929
21010
  var fs32 = __toESM(require("fs"));
20930
- var path38 = __toESM(require("path"));
20931
- var os25 = __toESM(require("os"));
21011
+ var path39 = __toESM(require("path"));
21012
+ var os26 = __toESM(require("os"));
20932
21013
  var https7 = __toESM(require("https"));
20933
21014
  var http7 = __toESM(require("http"));
20934
21015
  var import_zod2 = require("zod");
@@ -21090,7 +21171,7 @@ var HistoryService = class _HistoryService {
21090
21171
  return this._quotaPercent === null || Date.now() - this._quotaFetchedAt > ttlMs;
21091
21172
  }
21092
21173
  get projectDir() {
21093
- return this.runtime.resolveHistoryDir(this.cwd) ?? path38.join(os25.homedir(), ".claude", "projects", encodeCwd(this.cwd));
21174
+ return this.runtime.resolveHistoryDir(this.cwd) ?? path39.join(os26.homedir(), ".claude", "projects", encodeCwd(this.cwd));
21094
21175
  }
21095
21176
  /** Set the current Claude conversation ID (extracted from /cost command or session start) */
21096
21177
  setCurrentConversationId(id) {
@@ -21102,7 +21183,7 @@ var HistoryService = class _HistoryService {
21102
21183
  /** Return the current message count in the active conversation. */
21103
21184
  getCurrentMessageCount() {
21104
21185
  if (!this.currentConversationId) return 0;
21105
- const filePath = path38.join(this.projectDir, `${this.currentConversationId}.jsonl`);
21186
+ const filePath = path39.join(this.projectDir, `${this.currentConversationId}.jsonl`);
21106
21187
  return parseJsonl(filePath).length;
21107
21188
  }
21108
21189
  /**
@@ -21113,7 +21194,7 @@ var HistoryService = class _HistoryService {
21113
21194
  const deadline = Date.now() + timeoutMs;
21114
21195
  while (Date.now() < deadline) {
21115
21196
  if (!this.currentConversationId) return null;
21116
- const filePath = path38.join(this.projectDir, `${this.currentConversationId}.jsonl`);
21197
+ const filePath = path39.join(this.projectDir, `${this.currentConversationId}.jsonl`);
21117
21198
  const messages = parseJsonl(filePath);
21118
21199
  if (messages.length > previousCount) {
21119
21200
  for (let i = messages.length - 1; i >= previousCount; i--) {
@@ -21141,14 +21222,14 @@ var HistoryService = class _HistoryService {
21141
21222
  try {
21142
21223
  const files = fs32.readdirSync(dir, { withFileTypes: true }).filter((e) => e.isFile() && e.name.endsWith(".jsonl")).map((e) => {
21143
21224
  try {
21144
- const stat3 = fs32.statSync(path38.join(dir, e.name));
21225
+ const stat3 = fs32.statSync(path39.join(dir, e.name));
21145
21226
  return { name: e.name, mtime: stat3.mtimeMs, birthtime: stat3.birthtimeMs };
21146
21227
  } catch {
21147
21228
  return { name: e.name, mtime: 0, birthtime: 0 };
21148
21229
  }
21149
21230
  }).filter((f) => f.birthtime >= cutoff).sort((a, b) => b.mtime - a.mtime);
21150
21231
  if (files.length > 0) {
21151
- this.currentConversationId = path38.basename(files[0].name, ".jsonl");
21232
+ this.currentConversationId = path39.basename(files[0].name, ".jsonl");
21152
21233
  }
21153
21234
  } catch {
21154
21235
  }
@@ -21188,7 +21269,7 @@ var HistoryService = class _HistoryService {
21188
21269
  }
21189
21270
  const files = entries.filter((e) => e.isFile() && e.name.endsWith(".jsonl")).map((e) => {
21190
21271
  try {
21191
- const stat3 = fs32.statSync(path38.join(dir, e.name));
21272
+ const stat3 = fs32.statSync(path39.join(dir, e.name));
21192
21273
  return { name: e.name, mtime: stat3.mtimeMs, birthtime: stat3.birthtimeMs };
21193
21274
  } catch {
21194
21275
  return { name: e.name, mtime: 0, birthtime: 0 };
@@ -21197,7 +21278,7 @@ var HistoryService = class _HistoryService {
21197
21278
  if (files.length === 0) return null;
21198
21279
  const targetFile = this.currentConversationId ? `${this.currentConversationId}.jsonl` : files[0].name;
21199
21280
  if (!files.some((f) => f.name === targetFile)) return null;
21200
- return this.extractUsageFromFile(path38.join(dir, targetFile));
21281
+ return this.extractUsageFromFile(path39.join(dir, targetFile));
21201
21282
  }
21202
21283
  extractUsageFromFile(filePath) {
21203
21284
  let raw;
@@ -21249,7 +21330,7 @@ var HistoryService = class _HistoryService {
21249
21330
  try {
21250
21331
  files = fs32.readdirSync(projectDir).filter((f) => f.endsWith(".jsonl")).filter((f) => {
21251
21332
  try {
21252
- return fs32.statSync(path38.join(projectDir, f)).mtimeMs >= monthStartMs;
21333
+ return fs32.statSync(path39.join(projectDir, f)).mtimeMs >= monthStartMs;
21253
21334
  } catch {
21254
21335
  return false;
21255
21336
  }
@@ -21260,7 +21341,7 @@ var HistoryService = class _HistoryService {
21260
21341
  for (const file of files) {
21261
21342
  let raw;
21262
21343
  try {
21263
- raw = fs32.readFileSync(path38.join(projectDir, file), "utf8");
21344
+ raw = fs32.readFileSync(path39.join(projectDir, file), "utf8");
21264
21345
  } catch {
21265
21346
  continue;
21266
21347
  }
@@ -21324,7 +21405,7 @@ var HistoryService = class _HistoryService {
21324
21405
  * showing an empty conversation.
21325
21406
  */
21326
21407
  async loadConversation(sessionId) {
21327
- const filePath = path38.join(this.projectDir, `${sessionId}.jsonl`);
21408
+ const filePath = path39.join(this.projectDir, `${sessionId}.jsonl`);
21328
21409
  const messages = parseJsonl(filePath);
21329
21410
  if (messages.length === 0) return;
21330
21411
  const totalBatches = Math.ceil(messages.length / CONVERSATION_BATCH_SIZE);
@@ -21378,7 +21459,7 @@ var HistoryService = class _HistoryService {
21378
21459
  if (!this.currentConversationId) return 0;
21379
21460
  }
21380
21461
  const sessionId = this.currentConversationId;
21381
- const filePath = path38.join(this.projectDir, `${sessionId}.jsonl`);
21462
+ const filePath = path39.join(this.projectDir, `${sessionId}.jsonl`);
21382
21463
  const messages = parseJsonl(filePath);
21383
21464
  if (messages.length === 0) return 0;
21384
21465
  const marker = this.lastUploadedUuid.get(sessionId);
@@ -22135,9 +22216,19 @@ async function pair(args2 = []) {
22135
22216
  const spin = dist_exports.spinner();
22136
22217
  spin.start("Requesting pairing code...");
22137
22218
  const result = await requestCode(pluginId);
22138
- if (!result) {
22219
+ if (!result.ok) {
22139
22220
  spin.stop("Failed");
22140
- showError("Could not reach the server. Check your connection and try again.");
22221
+ if (result.reason === "rate-limited") {
22222
+ showError(
22223
+ `Server is rate-limiting this request (HTTP 429). Retry in ${result.retryAfterSeconds}s.`
22224
+ );
22225
+ } else if (result.reason === "timeout") {
22226
+ showError("Server took too long to respond. Check your connection and try again.");
22227
+ } else if (result.reason === "http") {
22228
+ showError(`Server returned HTTP ${result.status}. Try again later.`);
22229
+ } else {
22230
+ showError("Could not reach the server. Check your connection and try again.");
22231
+ }
22141
22232
  process.exit(1);
22142
22233
  }
22143
22234
  spin.stop("Got pairing code");
@@ -22266,7 +22357,7 @@ async function autoLinkAfterPair(opts) {
22266
22357
 
22267
22358
  // src/commands/pair-auto.ts
22268
22359
  var fs33 = __toESM(require("fs"));
22269
- var os26 = __toESM(require("os"));
22360
+ var os27 = __toESM(require("os"));
22270
22361
  var import_crypto7 = require("crypto");
22271
22362
 
22272
22363
  // src/commands/start-infra-only.ts
@@ -22434,12 +22525,12 @@ function readTokenFromArgs(args2) {
22434
22525
  }
22435
22526
  const fileFlag = args2.find((a) => a.startsWith("--token-file="));
22436
22527
  if (fileFlag) {
22437
- const path45 = fileFlag.slice("--token-file=".length);
22528
+ const path46 = fileFlag.slice("--token-file=".length);
22438
22529
  try {
22439
- const content = fs33.readFileSync(path45, "utf8").trim();
22440
- if (content.length === 0) fail(`--token-file ${path45} is empty`);
22530
+ const content = fs33.readFileSync(path46, "utf8").trim();
22531
+ if (content.length === 0) fail(`--token-file ${path46} is empty`);
22441
22532
  try {
22442
- fs33.unlinkSync(path45);
22533
+ fs33.unlinkSync(path46);
22443
22534
  } catch {
22444
22535
  }
22445
22536
  return content;
@@ -22465,7 +22556,7 @@ async function claimOnce(token, pluginId) {
22465
22556
  pluginId,
22466
22557
  ideName: "codeam-cli (codespace)",
22467
22558
  ideVersion: process.env.npm_package_version ?? "unknown",
22468
- hostname: os26.hostname(),
22559
+ hostname: os27.hostname(),
22469
22560
  codespaceName: process.env.CODESPACE_NAME ?? "",
22470
22561
  // Current git branch of the codespace's working directory, so the
22471
22562
  // backend can populate `PairedSession.branch` for the codespace pair.
@@ -22705,7 +22796,7 @@ var import_picocolors10 = __toESM(require("picocolors"));
22705
22796
  var import_child_process16 = require("child_process");
22706
22797
  var import_util4 = require("util");
22707
22798
  var import_picocolors8 = __toESM(require("picocolors"));
22708
- var path39 = __toESM(require("path"));
22799
+ var path40 = __toESM(require("path"));
22709
22800
  var execFileP5 = (0, import_util4.promisify)(import_child_process16.execFile);
22710
22801
  var MAX_BUFFER = 8 * 1024 * 1024;
22711
22802
  function resetStdinForChild() {
@@ -23194,7 +23285,7 @@ var GitHubCodespacesProvider = class {
23194
23285
  });
23195
23286
  }
23196
23287
  async uploadFile(workspaceId, remotePath, contents, options = {}) {
23197
- const remoteDir = path39.posix.dirname(remotePath);
23288
+ const remoteDir = path40.posix.dirname(remotePath);
23198
23289
  const parts = [
23199
23290
  `mkdir -p ${shellQuote(remoteDir)}`,
23200
23291
  `cat > ${shellQuote(remotePath)}`
@@ -23264,7 +23355,7 @@ function shellQuote(s) {
23264
23355
  // src/services/providers/gitpod.ts
23265
23356
  var import_child_process17 = require("child_process");
23266
23357
  var import_util5 = require("util");
23267
- var path40 = __toESM(require("path"));
23358
+ var path41 = __toESM(require("path"));
23268
23359
  var import_picocolors9 = __toESM(require("picocolors"));
23269
23360
  var execFileP6 = (0, import_util5.promisify)(import_child_process17.execFile);
23270
23361
  var MAX_BUFFER2 = 8 * 1024 * 1024;
@@ -23504,7 +23595,7 @@ var GitpodProvider = class {
23504
23595
  });
23505
23596
  }
23506
23597
  async uploadFile(workspaceId, remotePath, contents, options = {}) {
23507
- const remoteDir = path40.posix.dirname(remotePath);
23598
+ const remoteDir = path41.posix.dirname(remotePath);
23508
23599
  const parts = [
23509
23600
  `mkdir -p ${shellQuote2(remoteDir)}`,
23510
23601
  `cat > ${shellQuote2(remotePath)}`
@@ -23540,7 +23631,7 @@ function shellQuote2(s) {
23540
23631
  // src/services/providers/gitlab-workspaces.ts
23541
23632
  var import_child_process18 = require("child_process");
23542
23633
  var import_util6 = require("util");
23543
- var path41 = __toESM(require("path"));
23634
+ var path42 = __toESM(require("path"));
23544
23635
  var execFileP7 = (0, import_util6.promisify)(import_child_process18.execFile);
23545
23636
  var MAX_BUFFER3 = 8 * 1024 * 1024;
23546
23637
  var GITLAB_API_BASE = process.env.CODEAM_GITLAB_API_URL ?? "https://gitlab.com/api/v4";
@@ -23800,7 +23891,7 @@ Docs: https://docs.gitlab.com/ee/user/workspace/configuration.html`
23800
23891
  }
23801
23892
  async uploadFile(workspaceId, remotePath, contents, options = {}) {
23802
23893
  const sshHost = process.env.CODEAM_GITLAB_SSH_HOST ?? "workspaces.gitlab.com";
23803
- const remoteDir = path41.posix.dirname(remotePath);
23894
+ const remoteDir = path42.posix.dirname(remotePath);
23804
23895
  const parts = [`mkdir -p ${shellQuote3(remoteDir)}`, `cat > ${shellQuote3(remotePath)}`];
23805
23896
  if (options.mode != null) {
23806
23897
  parts.push(`chmod ${options.mode.toString(8)} ${shellQuote3(remotePath)}`);
@@ -23868,7 +23959,7 @@ function shellQuote3(s) {
23868
23959
  // src/services/providers/railway.ts
23869
23960
  var import_child_process19 = require("child_process");
23870
23961
  var import_util7 = require("util");
23871
- var path42 = __toESM(require("path"));
23962
+ var path43 = __toESM(require("path"));
23872
23963
  var execFileP8 = (0, import_util7.promisify)(import_child_process19.execFile);
23873
23964
  var MAX_BUFFER4 = 8 * 1024 * 1024;
23874
23965
  function resetStdinForChild4() {
@@ -24104,7 +24195,7 @@ var RailwayProvider = class {
24104
24195
  if (!projectId || !serviceId) {
24105
24196
  throw new Error("Invalid Railway workspace id (expected projectId/serviceId).");
24106
24197
  }
24107
- const remoteDir = path42.posix.dirname(remotePath);
24198
+ const remoteDir = path43.posix.dirname(remotePath);
24108
24199
  const parts = [`mkdir -p ${shellQuote4(remoteDir)}`, `cat > ${shellQuote4(remotePath)}`];
24109
24200
  if (options.mode != null) {
24110
24201
  parts.push(`chmod ${options.mode.toString(8)} ${shellQuote4(remotePath)}`);
@@ -24648,7 +24739,7 @@ var import_node_dns = require("dns");
24648
24739
  var import_node_util4 = require("util");
24649
24740
  var import_node_crypto8 = require("crypto");
24650
24741
  var fs34 = __toESM(require("fs"));
24651
- var path43 = __toESM(require("path"));
24742
+ var path44 = __toESM(require("path"));
24652
24743
  var import_picocolors12 = __toESM(require("picocolors"));
24653
24744
  var dnsResolveP = (0, import_node_util4.promisify)(import_node_dns.resolve);
24654
24745
  async function checkDns(apiBase) {
@@ -24704,10 +24795,10 @@ async function checkHealth(apiBase) {
24704
24795
  }
24705
24796
  }
24706
24797
  function checkConfigDir() {
24707
- const dir = path43.join(require("os").homedir(), ".codeam");
24798
+ const dir = path44.join(require("os").homedir(), ".codeam");
24708
24799
  try {
24709
24800
  fs34.mkdirSync(dir, { recursive: true, mode: 448 });
24710
- const probe = path43.join(dir, ".doctor-probe");
24801
+ const probe = path44.join(dir, ".doctor-probe");
24711
24802
  fs34.writeFileSync(probe, "ok", { mode: 384 });
24712
24803
  const read = fs34.readFileSync(probe, "utf8");
24713
24804
  fs34.unlinkSync(probe);
@@ -24750,9 +24841,9 @@ function checkSessions() {
24750
24841
  }
24751
24842
  }
24752
24843
  function checkAgentBinaries() {
24753
- const os28 = createOsStrategy();
24844
+ const os29 = createOsStrategy();
24754
24845
  return getEnabledAgents().map((meta) => {
24755
- const found = os28.findInPath(meta.binaryName);
24846
+ const found = os29.findInPath(meta.binaryName);
24756
24847
  return {
24757
24848
  id: `agent-${meta.id}`,
24758
24849
  label: `Agent binary: ${meta.displayName} (${meta.binaryName})`,
@@ -24774,7 +24865,7 @@ function checkNodePty() {
24774
24865
  detail: "not required on this platform"
24775
24866
  };
24776
24867
  }
24777
- const vendoredPath = path43.join(__dirname, "vendor", "node-pty");
24868
+ const vendoredPath = path44.join(__dirname, "vendor", "node-pty");
24778
24869
  for (const target of [vendoredPath, "node-pty"]) {
24779
24870
  try {
24780
24871
  require(target);
@@ -24816,7 +24907,7 @@ function checkChokidar() {
24816
24907
  }
24817
24908
  async function doctor(args2 = []) {
24818
24909
  const json = args2.includes("--json");
24819
- const cliVersion = true ? "2.31.0" : "0.0.0-dev";
24910
+ const cliVersion = true ? "2.32.0" : "0.0.0-dev";
24820
24911
  const apiBase = resolveApiBaseUrl();
24821
24912
  const diagnosticId = (0, import_node_crypto8.randomUUID)();
24822
24913
  log.info("doctor", `run id=${diagnosticId} cli=${cliVersion}`);
@@ -25015,7 +25106,7 @@ async function completion(args2) {
25015
25106
  // src/commands/version.ts
25016
25107
  var import_picocolors13 = __toESM(require("picocolors"));
25017
25108
  function version2() {
25018
- const v = true ? "2.31.0" : "unknown";
25109
+ const v = true ? "2.32.0" : "unknown";
25019
25110
  console.log(`${import_picocolors13.default.bold("codeam-cli")} ${import_picocolors13.default.cyan(v)}`);
25020
25111
  }
25021
25112
 
@@ -25144,8 +25235,8 @@ var _subcommandHelpKeys = Object.keys(HELPS);
25144
25235
 
25145
25236
  // src/lib/updateNotifier.ts
25146
25237
  var fs35 = __toESM(require("fs"));
25147
- var os27 = __toESM(require("os"));
25148
- var path44 = __toESM(require("path"));
25238
+ var os28 = __toESM(require("os"));
25239
+ var path45 = __toESM(require("path"));
25149
25240
  var https8 = __toESM(require("https"));
25150
25241
  var import_node_child_process12 = require("child_process");
25151
25242
  var import_picocolors16 = __toESM(require("picocolors"));
@@ -25154,8 +25245,8 @@ var REGISTRY_URL = `https://registry.npmjs.org/${PKG_NAME}/latest`;
25154
25245
  var TTL_MS = 24 * 60 * 60 * 1e3;
25155
25246
  var REQUEST_TIMEOUT_MS = 1500;
25156
25247
  function cachePath() {
25157
- const dir = path44.join(os27.homedir(), ".codeam");
25158
- return path44.join(dir, "update-check.json");
25248
+ const dir = path45.join(os28.homedir(), ".codeam");
25249
+ return path45.join(dir, "update-check.json");
25159
25250
  }
25160
25251
  function readCache() {
25161
25252
  try {
@@ -25170,7 +25261,7 @@ function readCache() {
25170
25261
  function writeCache(cache) {
25171
25262
  try {
25172
25263
  const file = cachePath();
25173
- fs35.mkdirSync(path44.dirname(file), { recursive: true });
25264
+ fs35.mkdirSync(path45.dirname(file), { recursive: true });
25174
25265
  const tmp = `${file}.${process.pid}.tmp`;
25175
25266
  fs35.writeFileSync(tmp, JSON.stringify(cache));
25176
25267
  fs35.renameSync(tmp, file);
@@ -25247,7 +25338,7 @@ function isLinkedInstall() {
25247
25338
  timeout: 2e3
25248
25339
  }).trim();
25249
25340
  if (!root) return false;
25250
- const pkgPath = path44.join(root, PKG_NAME);
25341
+ const pkgPath = path45.join(root, PKG_NAME);
25251
25342
  return fs35.lstatSync(pkgPath).isSymbolicLink();
25252
25343
  } catch {
25253
25344
  return false;
@@ -25301,7 +25392,7 @@ function checkForUpdates() {
25301
25392
  if (process.env.CODEAM_DISABLE_UPDATE_CHECK === "1") return;
25302
25393
  if (process.env.CI) return;
25303
25394
  if (!process.stdout.isTTY) return;
25304
- const current = true ? "2.31.0" : null;
25395
+ const current = true ? "2.32.0" : null;
25305
25396
  if (!current) return;
25306
25397
  const cache = readCache();
25307
25398
  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.31.0",
3
+ "version": "2.32.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",