codeam-cli 2.31.0 → 2.32.1

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 +278 -179
  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.1",
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.1" : "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.`
@@ -12036,7 +12061,15 @@ var REGISTRY = {
12036
12061
  // resolved from PATH at spawn time instead of being absolute.
12037
12062
  gemini: () => ({
12038
12063
  command: "gemini",
12039
- args: ["--acp"],
12064
+ // `--skip-trust` bypasses Gemini's headless-mode workspace-trust
12065
+ // gate. Without it the CLI refuses to start in `--acp` mode with
12066
+ // "Gemini CLI is not running in a trusted directory" and the
12067
+ // ACP newSession call never returns, leaving mobile chat stuck
12068
+ // on "thinking…" forever. The equivalent env var is
12069
+ // `GEMINI_CLI_TRUST_WORKSPACE=true`; passing the flag is
12070
+ // cleaner because it survives whatever shell env the parent
12071
+ // codeam was launched from.
12072
+ args: ["--skip-trust", "--acp"],
12040
12073
  requiresAgentBinary: "gemini"
12041
12074
  })
12042
12075
  };
@@ -12051,6 +12084,9 @@ var import_node_crypto7 = require("crypto");
12051
12084
  // src/agents/acp/client.ts
12052
12085
  var import_node_child_process11 = require("child_process");
12053
12086
  var fs21 = __toESM(require("fs/promises"));
12087
+ var fsSync = __toESM(require("fs"));
12088
+ var os22 = __toESM(require("os"));
12089
+ var path26 = __toESM(require("path"));
12054
12090
  var import_node_stream = require("stream");
12055
12091
 
12056
12092
  // ../../node_modules/@agentclientprotocol/sdk/dist/acp.js
@@ -14529,13 +14565,14 @@ var AcpClient = class {
14529
14565
  async start() {
14530
14566
  if (this.child) throw new Error("AcpClient already started");
14531
14567
  const { adapter, cwd } = this.opts;
14568
+ const augmentedPath = expandPathForAgentBinaries(process.env.PATH ?? "");
14532
14569
  log.info(
14533
14570
  "acpClient",
14534
14571
  `spawn cmd=${adapter.command} args=[${adapter.args.join(",")}] cwd=${cwd}`
14535
14572
  );
14536
14573
  const child = (0, import_node_child_process11.spawn)(adapter.command, adapter.args, {
14537
14574
  cwd,
14538
- env: process.env,
14575
+ env: { ...process.env, PATH: augmentedPath },
14539
14576
  stdio: ["pipe", "pipe", "pipe"]
14540
14577
  });
14541
14578
  this.child = child;
@@ -14812,6 +14849,48 @@ function applyLineRange(content, line, limit) {
14812
14849
  const end = limit !== null ? start2 + limit : lines.length;
14813
14850
  return { content: lines.slice(start2, end).join("\n") };
14814
14851
  }
14852
+ function knownAgentBinaryDirs() {
14853
+ const home = os22.homedir();
14854
+ const out2 = [];
14855
+ out2.push("/tmp/codeam-node20/bin");
14856
+ for (const root of [
14857
+ "/usr/local/share/nvm/versions/node",
14858
+ path26.join(home, ".nvm/versions/node")
14859
+ ]) {
14860
+ try {
14861
+ for (const child of fsSync.readdirSync(root)) {
14862
+ out2.push(path26.join(root, child, "bin"));
14863
+ }
14864
+ } catch {
14865
+ }
14866
+ }
14867
+ out2.push(path26.join(home, ".volta/bin"));
14868
+ out2.push("/usr/local/bin");
14869
+ out2.push("/usr/bin");
14870
+ out2.push(path26.join(home, ".local/bin"));
14871
+ out2.push(path26.join(home, "bin"));
14872
+ return out2.filter((p2) => {
14873
+ try {
14874
+ return fsSync.statSync(p2).isDirectory();
14875
+ } catch {
14876
+ return false;
14877
+ }
14878
+ });
14879
+ }
14880
+ function expandPathForAgentBinaries(existingPath) {
14881
+ const existing = new Set(
14882
+ existingPath.split(path26.delimiter).filter((p2) => p2.length > 0)
14883
+ );
14884
+ const additions = [];
14885
+ for (const dir of knownAgentBinaryDirs()) {
14886
+ if (!existing.has(dir)) {
14887
+ additions.push(dir);
14888
+ existing.add(dir);
14889
+ }
14890
+ }
14891
+ if (additions.length === 0) return existingPath;
14892
+ return [...additions, existingPath].filter((p2) => p2.length > 0).join(path26.delimiter);
14893
+ }
14815
14894
 
14816
14895
  // src/services/streaming/transport.ts
14817
14896
  var http3 = __toESM(require("http"));
@@ -15597,8 +15676,8 @@ function extractSelectPrompt(text) {
15597
15676
 
15598
15677
  // src/commands/start/handlers.ts
15599
15678
  var fs29 = __toESM(require("fs"));
15600
- var os23 = __toESM(require("os"));
15601
- var path34 = __toESM(require("path"));
15679
+ var os24 = __toESM(require("os"));
15680
+ var path35 = __toESM(require("path"));
15602
15681
  var import_crypto3 = require("crypto");
15603
15682
  var import_child_process12 = require("child_process");
15604
15683
 
@@ -15723,7 +15802,7 @@ function parsePayload2(schema, raw) {
15723
15802
 
15724
15803
  // src/services/file-ops.service.ts
15725
15804
  var fs22 = __toESM(require("fs/promises"));
15726
- var path27 = __toESM(require("path"));
15805
+ var path28 = __toESM(require("path"));
15727
15806
  var MAX_FILE_BYTES = 5 * 1024 * 1024;
15728
15807
  var MAX_WALK_DEPTH = 6;
15729
15808
  var MAX_VISITED_DIRS = 5e3;
@@ -15758,8 +15837,8 @@ var SUBDIR_IGNORE = /* @__PURE__ */ new Set([
15758
15837
  "__pycache__"
15759
15838
  ]);
15760
15839
  function isUnder(parent, candidate) {
15761
- const rel = path27.relative(parent, candidate);
15762
- return rel === "" || !rel.startsWith("..") && !path27.isAbsolute(rel);
15840
+ const rel = path28.relative(parent, candidate);
15841
+ return rel === "" || !rel.startsWith("..") && !path28.isAbsolute(rel);
15763
15842
  }
15764
15843
  async function isExistingFile(absPath) {
15765
15844
  try {
@@ -15782,7 +15861,7 @@ async function walkForSuffix(dir, needleVariants, depth, ctx) {
15782
15861
  }
15783
15862
  for (const e of entries) {
15784
15863
  if (!e.isFile()) continue;
15785
- const full = path27.join(dir, e.name);
15864
+ const full = path28.join(dir, e.name);
15786
15865
  if (needleVariants.some((needle) => full.endsWith(needle))) {
15787
15866
  ctx.matches.push(full);
15788
15867
  if (ctx.matches.length >= ctx.cap) return;
@@ -15792,21 +15871,21 @@ async function walkForSuffix(dir, needleVariants, depth, ctx) {
15792
15871
  if (!e.isDirectory()) continue;
15793
15872
  if (SUBDIR_IGNORE.has(e.name)) continue;
15794
15873
  if (e.name.startsWith(".") && SUBDIR_IGNORE.has(e.name)) continue;
15795
- await walkForSuffix(path27.join(dir, e.name), needleVariants, depth + 1, ctx);
15874
+ await walkForSuffix(path28.join(dir, e.name), needleVariants, depth + 1, ctx);
15796
15875
  if (ctx.matches.length >= ctx.cap) return;
15797
15876
  }
15798
15877
  }
15799
15878
  async function findFile(rawPath) {
15800
15879
  const cwd = process.cwd();
15801
- if (path27.isAbsolute(rawPath)) {
15802
- const abs = path27.normalize(rawPath);
15880
+ if (path28.isAbsolute(rawPath)) {
15881
+ const abs = path28.normalize(rawPath);
15803
15882
  if (isUnder(cwd, abs) && await isExistingFile(abs)) return abs;
15804
15883
  }
15805
- const direct = path27.resolve(cwd, rawPath);
15884
+ const direct = path28.resolve(cwd, rawPath);
15806
15885
  if (isUnder(cwd, direct) && await isExistingFile(direct)) return direct;
15807
- const normalized = path27.normalize(rawPath).replace(/^[./\\]+/, "");
15886
+ const normalized = path28.normalize(rawPath).replace(/^[./\\]+/, "");
15808
15887
  const needles = [
15809
- `${path27.sep}${normalized}`,
15888
+ `${path28.sep}${normalized}`,
15810
15889
  `/${normalized}`
15811
15890
  ].filter((v, i, a) => a.indexOf(v) === i);
15812
15891
  const ctx = { visited: 0, matches: [], cap: 16 };
@@ -15820,7 +15899,7 @@ async function findWriteTarget(rawPath) {
15820
15899
  const found = await findFile(rawPath);
15821
15900
  if (found) return found;
15822
15901
  const cwd = process.cwd();
15823
- const fallback = path27.isAbsolute(rawPath) ? path27.normalize(rawPath) : path27.resolve(cwd, rawPath);
15902
+ const fallback = path28.isAbsolute(rawPath) ? path28.normalize(rawPath) : path28.resolve(cwd, rawPath);
15824
15903
  if (!isUnder(cwd, fallback)) return null;
15825
15904
  return fallback;
15826
15905
  }
@@ -15860,7 +15939,7 @@ async function writeProjectFile(rawPath, content) {
15860
15939
  if (Buffer.byteLength(content, "utf-8") > MAX_FILE_BYTES) {
15861
15940
  return { error: "Content too large." };
15862
15941
  }
15863
- await fs22.mkdir(path27.dirname(abs), { recursive: true });
15942
+ await fs22.mkdir(path28.dirname(abs), { recursive: true });
15864
15943
  await fs22.writeFile(abs, content, "utf-8");
15865
15944
  return { ok: true };
15866
15945
  } catch (e) {
@@ -15873,7 +15952,7 @@ async function writeProjectFile(rawPath, content) {
15873
15952
  var import_child_process9 = require("child_process");
15874
15953
  var import_util2 = require("util");
15875
15954
  var fs23 = __toESM(require("fs/promises"));
15876
- var path28 = __toESM(require("path"));
15955
+ var path29 = __toESM(require("path"));
15877
15956
  var execFileP3 = (0, import_util2.promisify)(import_child_process9.execFile);
15878
15957
  var PROJECT_IGNORE = /* @__PURE__ */ new Set([
15879
15958
  "node_modules",
@@ -15931,12 +16010,12 @@ async function listProjectFiles(opts = {}) {
15931
16010
  return;
15932
16011
  }
15933
16012
  if (PROJECT_IGNORE.has(e.name)) continue;
15934
- const full = path28.join(dir, e.name);
16013
+ const full = path29.join(dir, e.name);
15935
16014
  if (e.isDirectory()) {
15936
16015
  if (depth >= 12) continue;
15937
16016
  await walk(full, depth + 1);
15938
16017
  } else if (e.isFile()) {
15939
- const rel = path28.relative(root, full);
16018
+ const rel = path29.relative(root, full);
15940
16019
  if (q2 && !rel.toLowerCase().includes(q2) && !e.name.toLowerCase().includes(q2)) {
15941
16020
  continue;
15942
16021
  }
@@ -16044,7 +16123,7 @@ async function gitStatus(cwd) {
16044
16123
  let hasMergeInProgress = false;
16045
16124
  try {
16046
16125
  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");
16126
+ const mergeHead = path29.isAbsolute(gitDir) ? path29.join(gitDir, "MERGE_HEAD") : path29.join(root, gitDir, "MERGE_HEAD");
16048
16127
  await fs23.access(mergeHead);
16049
16128
  hasMergeInProgress = true;
16050
16129
  } catch {
@@ -16191,7 +16270,7 @@ async function jsSearchFiles(opts, cwd, cap) {
16191
16270
  }
16192
16271
  let content = "";
16193
16272
  try {
16194
- content = await fs23.readFile(path28.join(cwd, f.path), "utf8");
16273
+ content = await fs23.readFile(path29.join(cwd, f.path), "utf8");
16195
16274
  } catch {
16196
16275
  continue;
16197
16276
  }
@@ -16216,13 +16295,13 @@ async function jsSearchFiles(opts, cwd, cap) {
16216
16295
  // src/services/apply-file-review.service.ts
16217
16296
  var import_child_process10 = require("child_process");
16218
16297
  var fs24 = __toESM(require("fs"));
16219
- var path29 = __toESM(require("path"));
16298
+ var path30 = __toESM(require("path"));
16220
16299
  async function applyFileReview(workingDir, filePath, action) {
16221
- if (filePath.includes("..") || path29.isAbsolute(filePath)) {
16300
+ if (filePath.includes("..") || path30.isAbsolute(filePath)) {
16222
16301
  return { ok: false, action, filePath, error: "invalid file path" };
16223
16302
  }
16224
- const absFile = path29.resolve(workingDir, filePath);
16225
- const repoRoot = findGitRoot(path29.dirname(absFile));
16303
+ const absFile = path30.resolve(workingDir, filePath);
16304
+ const repoRoot = findGitRoot(path30.dirname(absFile));
16226
16305
  if (!repoRoot) {
16227
16306
  return {
16228
16307
  ok: false,
@@ -16231,7 +16310,7 @@ async function applyFileReview(workingDir, filePath, action) {
16231
16310
  error: `no enclosing git repo for ${filePath}`
16232
16311
  };
16233
16312
  }
16234
- const relInRepo = path29.relative(repoRoot, absFile);
16313
+ const relInRepo = path30.relative(repoRoot, absFile);
16235
16314
  if (!relInRepo || relInRepo.startsWith("..")) {
16236
16315
  return { ok: false, action, filePath, error: "path escapes repo root" };
16237
16316
  }
@@ -16280,17 +16359,17 @@ function runGit(cwd, args2) {
16280
16359
  });
16281
16360
  }
16282
16361
  function findGitRoot(startDir) {
16283
- let dir = path29.resolve(startDir);
16362
+ let dir = path30.resolve(startDir);
16284
16363
  const seen = /* @__PURE__ */ new Set();
16285
16364
  for (let i = 0; i < 256; i++) {
16286
16365
  if (seen.has(dir)) return null;
16287
16366
  seen.add(dir);
16288
16367
  try {
16289
- const stat3 = fs24.statSync(path29.join(dir, ".git"), { throwIfNoEntry: false });
16368
+ const stat3 = fs24.statSync(path30.join(dir, ".git"), { throwIfNoEntry: false });
16290
16369
  if (stat3 && (stat3.isDirectory() || stat3.isFile())) return dir;
16291
16370
  } catch {
16292
16371
  }
16293
- const parent = path29.dirname(dir);
16372
+ const parent = path30.dirname(dir);
16294
16373
  if (parent === dir) return null;
16295
16374
  dir = parent;
16296
16375
  }
@@ -16300,7 +16379,7 @@ function findGitRoot(startDir) {
16300
16379
  // src/commands/link.ts
16301
16380
  var import_node_crypto6 = require("crypto");
16302
16381
  var fs25 = __toESM(require("fs"));
16303
- var path30 = __toESM(require("path"));
16382
+ var path31 = __toESM(require("path"));
16304
16383
  var import_chokidar = __toESM(require("chokidar"));
16305
16384
  var import_picocolors2 = __toESM(require("picocolors"));
16306
16385
 
@@ -16479,7 +16558,7 @@ function parseLinkArgs(args2) {
16479
16558
  if (apiKeyFileArg) {
16480
16559
  const filePath = apiKeyFileArg.slice("--api-key-file=".length);
16481
16560
  try {
16482
- apiKey = fs25.readFileSync(path30.resolve(filePath), "utf8").trim();
16561
+ apiKey = fs25.readFileSync(path31.resolve(filePath), "utf8").trim();
16483
16562
  } catch (err) {
16484
16563
  throw new Error(`Could not read --api-key-file ${filePath}: ${err.message}`);
16485
16564
  }
@@ -16510,9 +16589,19 @@ async function link(args2 = []) {
16510
16589
  const spin = dist_exports.spinner();
16511
16590
  spin.start("Requesting pairing code...");
16512
16591
  const pairing = await requestCode(pluginId);
16513
- if (!pairing) {
16592
+ if (!pairing.ok) {
16514
16593
  spin.stop("Failed");
16515
- showError("Could not reach the server. Check your connection and try again.");
16594
+ if (pairing.reason === "rate-limited") {
16595
+ showError(
16596
+ `Server is rate-limiting this request (HTTP 429). Retry in ${pairing.retryAfterSeconds}s.`
16597
+ );
16598
+ } else if (pairing.reason === "timeout") {
16599
+ showError("Server took too long to respond. Check your connection and try again.");
16600
+ } else if (pairing.reason === "http") {
16601
+ showError(`Server returned HTTP ${pairing.status}. Try again later.`);
16602
+ } else {
16603
+ showError("Could not reach the server. Check your connection and try again.");
16604
+ }
16516
16605
  process.exit(1);
16517
16606
  }
16518
16607
  spin.stop("Got pairing code");
@@ -16573,7 +16662,7 @@ async function link(args2 = []) {
16573
16662
  return;
16574
16663
  }
16575
16664
  if (parsed.tokenFile) {
16576
- const credential = fs25.readFileSync(path30.resolve(parsed.tokenFile), "utf8").trim();
16665
+ const credential = fs25.readFileSync(path31.resolve(parsed.tokenFile), "utf8").trim();
16577
16666
  if (!credential) {
16578
16667
  showError(`--token-file ${parsed.tokenFile} is empty.`);
16579
16668
  process.exit(1);
@@ -17092,7 +17181,7 @@ function cleanupAttachmentTempFiles() {
17092
17181
  function saveFilesTemp(files) {
17093
17182
  return files.filter(({ base64 }) => base64 && base64.length > 0).map(({ filename, base64 }) => {
17094
17183
  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}`);
17184
+ const tmpPath = path35.join(os24.tmpdir(), `codeam-${(0, import_crypto3.randomUUID)()}-${safeName}`);
17096
17185
  fs29.writeFileSync(tmpPath, Buffer.from(base64, "base64"));
17097
17186
  pendingAttachmentFiles.add(tmpPath);
17098
17187
  return tmpPath;
@@ -18039,8 +18128,8 @@ async function dispatchCommand(ctx, cmd) {
18039
18128
  // src/services/file-watcher.service.ts
18040
18129
  var import_child_process13 = require("child_process");
18041
18130
  var fs30 = __toESM(require("fs"));
18042
- var os24 = __toESM(require("os"));
18043
- var path35 = __toESM(require("path"));
18131
+ var os25 = __toESM(require("os"));
18132
+ var path36 = __toESM(require("path"));
18044
18133
  var import_ignore = __toESM(require("ignore"));
18045
18134
 
18046
18135
  // src/services/file-watcher/diff-parser.ts
@@ -18199,10 +18288,10 @@ var WINDOWS_LEGACY_JUNCTIONS = [
18199
18288
  /[\\/]Start Menu([\\/]|$)/i,
18200
18289
  /[\\/]Templates([\\/]|$)/i
18201
18290
  ];
18202
- function isUnsafeWindowsWatchRoot(dir, homedir20) {
18291
+ function isUnsafeWindowsWatchRoot(dir, homedir21) {
18203
18292
  const norm = (p2) => p2.replace(/\//g, "\\").replace(/\\+$/, "").toLowerCase();
18204
18293
  const cwd = norm(dir);
18205
- const home = norm(homedir20);
18294
+ const home = norm(homedir21);
18206
18295
  if (cwd === home) return true;
18207
18296
  if (/^[a-z]:$/.test(cwd)) return true;
18208
18297
  const sysRoots = [
@@ -18232,18 +18321,18 @@ var _findGitRootSeam = {
18232
18321
  resolve: _defaultFindGitRoot
18233
18322
  };
18234
18323
  function _defaultFindGitRoot(startDir) {
18235
- let dir = path35.resolve(startDir);
18324
+ let dir = path36.resolve(startDir);
18236
18325
  const seen = /* @__PURE__ */ new Set();
18237
18326
  for (let i = 0; i < 256; i++) {
18238
18327
  if (seen.has(dir)) return null;
18239
18328
  seen.add(dir);
18240
18329
  try {
18241
- const gitPath = path35.join(dir, ".git");
18330
+ const gitPath = path36.join(dir, ".git");
18242
18331
  const stat3 = fs30.statSync(gitPath, { throwIfNoEntry: false });
18243
18332
  if (stat3 && (stat3.isDirectory() || stat3.isFile())) return dir;
18244
18333
  } catch {
18245
18334
  }
18246
- const parent = path35.dirname(dir);
18335
+ const parent = path36.dirname(dir);
18247
18336
  if (parent === dir) return null;
18248
18337
  dir = parent;
18249
18338
  }
@@ -18301,7 +18390,7 @@ var FileWatcherService = class {
18301
18390
  throw new Error("FileWatcherService has already been stopped \u2014 re-instantiate to restart.");
18302
18391
  }
18303
18392
  const isWin = process.platform === "win32";
18304
- if (isWin && isUnsafeWindowsWatchRoot(this.opts.workingDir, os24.homedir())) {
18393
+ if (isWin && isUnsafeWindowsWatchRoot(this.opts.workingDir, os25.homedir())) {
18305
18394
  log.warn(
18306
18395
  "fileWatcher",
18307
18396
  `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 +18577,7 @@ var FileWatcherService = class {
18488
18577
  }
18489
18578
  async emitForFile(absPath, changeType) {
18490
18579
  if (this.stopped) return;
18491
- const fileDir = path35.dirname(absPath);
18580
+ const fileDir = path36.dirname(absPath);
18492
18581
  let gitRoot = this.gitRootByDir.get(fileDir);
18493
18582
  if (gitRoot === void 0) {
18494
18583
  gitRoot = findGitRoot2(fileDir);
@@ -18501,19 +18590,19 @@ var FileWatcherService = class {
18501
18590
  );
18502
18591
  return;
18503
18592
  }
18504
- const relPathInRepo = path35.relative(gitRoot, absPath);
18593
+ const relPathInRepo = path36.relative(gitRoot, absPath);
18505
18594
  if (!relPathInRepo || relPathInRepo.startsWith("..")) return;
18506
18595
  const matcher = this.getGitIgnoreMatcher(gitRoot);
18507
18596
  if (matcher && matcher.ignores(relPathInRepo)) {
18508
18597
  log.trace(
18509
18598
  "fileWatcher",
18510
- `${relPathInRepo} ignored by ${path35.basename(gitRoot)}/.gitignore \u2014 suppressing emit`
18599
+ `${relPathInRepo} ignored by ${path36.basename(gitRoot)}/.gitignore \u2014 suppressing emit`
18511
18600
  );
18512
18601
  return;
18513
18602
  }
18514
18603
  this.opts.onRepoDirty?.(gitRoot);
18515
- const repoPath = path35.relative(this.opts.workingDir, gitRoot);
18516
- const repoName = path35.basename(gitRoot);
18604
+ const repoPath = path36.relative(this.opts.workingDir, gitRoot);
18605
+ const repoName = path36.basename(gitRoot);
18517
18606
  let diffText = "";
18518
18607
  let fileStatus = "modified";
18519
18608
  if (changeType === "unlink") {
@@ -18697,16 +18786,16 @@ var FileWatcherService = class {
18697
18786
  );
18698
18787
  if (gitignoreEntry) {
18699
18788
  try {
18700
- const body = fs30.readFileSync(path35.join(dir, ".gitignore"), "utf8");
18701
- const rel = path35.relative(repoRoot, dir).replace(/\\/g, "/");
18789
+ const body = fs30.readFileSync(path36.join(dir, ".gitignore"), "utf8");
18790
+ const rel = path36.relative(repoRoot, dir).replace(/\\/g, "/");
18702
18791
  const prefixed = body.split(/\r?\n/).map((line) => {
18703
18792
  const trimmed = line.trim();
18704
18793
  if (!trimmed || trimmed.startsWith("#")) return line;
18705
18794
  if (!rel) return line;
18706
18795
  if (trimmed.startsWith("!")) {
18707
- return "!" + path35.posix.join(rel, trimmed.slice(1));
18796
+ return "!" + path36.posix.join(rel, trimmed.slice(1));
18708
18797
  }
18709
- return path35.posix.join(rel, trimmed);
18798
+ return path36.posix.join(rel, trimmed);
18710
18799
  }).join("\n");
18711
18800
  matcher.add(prefixed);
18712
18801
  } catch {
@@ -18715,7 +18804,7 @@ var FileWatcherService = class {
18715
18804
  for (const entry of entries) {
18716
18805
  if (!entry.isDirectory()) continue;
18717
18806
  if (entry.name === ".git") continue;
18718
- const childAbs = path35.join(dir, entry.name);
18807
+ const childAbs = path36.join(dir, entry.name);
18719
18808
  if (isIgnoredFilePath(childAbs)) continue;
18720
18809
  this.collectGitignoreFiles(repoRoot, childAbs, matcher);
18721
18810
  }
@@ -18885,7 +18974,7 @@ var import_crypto4 = require("crypto");
18885
18974
 
18886
18975
  // src/services/turn-files/git-changeset.ts
18887
18976
  var import_child_process14 = require("child_process");
18888
- var path36 = __toESM(require("path"));
18977
+ var path37 = __toESM(require("path"));
18889
18978
  async function collectRepoChangeset(opts) {
18890
18979
  const status2 = await runGit3(opts.repoRoot, ["status", "--porcelain=v1", "-z"]);
18891
18980
  if (status2 === null) return null;
@@ -19015,8 +19104,8 @@ async function discoverRepos(workingDir, maxDepth = 4) {
19015
19104
  if (hasGit) {
19016
19105
  out2.push({
19017
19106
  repoRoot: dir,
19018
- repoPath: path36.relative(workingDir, dir),
19019
- repoName: path36.basename(dir)
19107
+ repoPath: path37.relative(workingDir, dir),
19108
+ repoName: path37.basename(dir)
19020
19109
  });
19021
19110
  return;
19022
19111
  }
@@ -19024,14 +19113,14 @@ async function discoverRepos(workingDir, maxDepth = 4) {
19024
19113
  if (!entry.isDirectory) continue;
19025
19114
  if (entry.name === "node_modules") continue;
19026
19115
  if (entry.name === "dist" || entry.name === "build") continue;
19027
- await walk(path36.join(dir, entry.name), depth + 1);
19116
+ await walk(path37.join(dir, entry.name), depth + 1);
19028
19117
  }
19029
19118
  }
19030
19119
  }
19031
19120
 
19032
19121
  // src/services/turn-files/files-outbox.ts
19033
19122
  var fs31 = __toESM(require("fs/promises"));
19034
- var path37 = __toESM(require("path"));
19123
+ var path38 = __toESM(require("path"));
19035
19124
  var import_os7 = require("os");
19036
19125
  var HOME_OUTBOX_DIR = ".codeam/outbox";
19037
19126
  var MAX_AGE_MS = 24 * 60 * 60 * 1e3;
@@ -19064,15 +19153,15 @@ var FilesOutbox = class {
19064
19153
  backoffIndex = 0;
19065
19154
  stopped = false;
19066
19155
  constructor(opts) {
19067
- const base = opts.baseDir ?? path37.join(homeDir(), HOME_OUTBOX_DIR);
19068
- this.filePath = path37.join(base, `${opts.sessionId}.jsonl`);
19156
+ const base = opts.baseDir ?? path38.join(homeDir(), HOME_OUTBOX_DIR);
19157
+ this.filePath = path38.join(base, `${opts.sessionId}.jsonl`);
19069
19158
  this.post = opts.post;
19070
19159
  this.autoSchedule = opts.autoSchedule !== false;
19071
19160
  }
19072
19161
  /** Persist the entry to disk and trigger a flush. Returns once the
19073
19162
  * line is durable on disk (not once the POST succeeds). */
19074
19163
  async enqueue(entry) {
19075
- await fs31.mkdir(path37.dirname(this.filePath), { recursive: true });
19164
+ await fs31.mkdir(path38.dirname(this.filePath), { recursive: true });
19076
19165
  await fs31.appendFile(this.filePath, JSON.stringify(entry) + "\n", "utf8");
19077
19166
  this.backoffIndex = 0;
19078
19167
  if (this.autoSchedule) this.scheduleFlush(0);
@@ -20927,8 +21016,8 @@ var OutputService = class _OutputService {
20927
21016
 
20928
21017
  // src/services/history.service.ts
20929
21018
  var fs32 = __toESM(require("fs"));
20930
- var path38 = __toESM(require("path"));
20931
- var os25 = __toESM(require("os"));
21019
+ var path39 = __toESM(require("path"));
21020
+ var os26 = __toESM(require("os"));
20932
21021
  var https7 = __toESM(require("https"));
20933
21022
  var http7 = __toESM(require("http"));
20934
21023
  var import_zod2 = require("zod");
@@ -21090,7 +21179,7 @@ var HistoryService = class _HistoryService {
21090
21179
  return this._quotaPercent === null || Date.now() - this._quotaFetchedAt > ttlMs;
21091
21180
  }
21092
21181
  get projectDir() {
21093
- return this.runtime.resolveHistoryDir(this.cwd) ?? path38.join(os25.homedir(), ".claude", "projects", encodeCwd(this.cwd));
21182
+ return this.runtime.resolveHistoryDir(this.cwd) ?? path39.join(os26.homedir(), ".claude", "projects", encodeCwd(this.cwd));
21094
21183
  }
21095
21184
  /** Set the current Claude conversation ID (extracted from /cost command or session start) */
21096
21185
  setCurrentConversationId(id) {
@@ -21102,7 +21191,7 @@ var HistoryService = class _HistoryService {
21102
21191
  /** Return the current message count in the active conversation. */
21103
21192
  getCurrentMessageCount() {
21104
21193
  if (!this.currentConversationId) return 0;
21105
- const filePath = path38.join(this.projectDir, `${this.currentConversationId}.jsonl`);
21194
+ const filePath = path39.join(this.projectDir, `${this.currentConversationId}.jsonl`);
21106
21195
  return parseJsonl(filePath).length;
21107
21196
  }
21108
21197
  /**
@@ -21113,7 +21202,7 @@ var HistoryService = class _HistoryService {
21113
21202
  const deadline = Date.now() + timeoutMs;
21114
21203
  while (Date.now() < deadline) {
21115
21204
  if (!this.currentConversationId) return null;
21116
- const filePath = path38.join(this.projectDir, `${this.currentConversationId}.jsonl`);
21205
+ const filePath = path39.join(this.projectDir, `${this.currentConversationId}.jsonl`);
21117
21206
  const messages = parseJsonl(filePath);
21118
21207
  if (messages.length > previousCount) {
21119
21208
  for (let i = messages.length - 1; i >= previousCount; i--) {
@@ -21141,14 +21230,14 @@ var HistoryService = class _HistoryService {
21141
21230
  try {
21142
21231
  const files = fs32.readdirSync(dir, { withFileTypes: true }).filter((e) => e.isFile() && e.name.endsWith(".jsonl")).map((e) => {
21143
21232
  try {
21144
- const stat3 = fs32.statSync(path38.join(dir, e.name));
21233
+ const stat3 = fs32.statSync(path39.join(dir, e.name));
21145
21234
  return { name: e.name, mtime: stat3.mtimeMs, birthtime: stat3.birthtimeMs };
21146
21235
  } catch {
21147
21236
  return { name: e.name, mtime: 0, birthtime: 0 };
21148
21237
  }
21149
21238
  }).filter((f) => f.birthtime >= cutoff).sort((a, b) => b.mtime - a.mtime);
21150
21239
  if (files.length > 0) {
21151
- this.currentConversationId = path38.basename(files[0].name, ".jsonl");
21240
+ this.currentConversationId = path39.basename(files[0].name, ".jsonl");
21152
21241
  }
21153
21242
  } catch {
21154
21243
  }
@@ -21188,7 +21277,7 @@ var HistoryService = class _HistoryService {
21188
21277
  }
21189
21278
  const files = entries.filter((e) => e.isFile() && e.name.endsWith(".jsonl")).map((e) => {
21190
21279
  try {
21191
- const stat3 = fs32.statSync(path38.join(dir, e.name));
21280
+ const stat3 = fs32.statSync(path39.join(dir, e.name));
21192
21281
  return { name: e.name, mtime: stat3.mtimeMs, birthtime: stat3.birthtimeMs };
21193
21282
  } catch {
21194
21283
  return { name: e.name, mtime: 0, birthtime: 0 };
@@ -21197,7 +21286,7 @@ var HistoryService = class _HistoryService {
21197
21286
  if (files.length === 0) return null;
21198
21287
  const targetFile = this.currentConversationId ? `${this.currentConversationId}.jsonl` : files[0].name;
21199
21288
  if (!files.some((f) => f.name === targetFile)) return null;
21200
- return this.extractUsageFromFile(path38.join(dir, targetFile));
21289
+ return this.extractUsageFromFile(path39.join(dir, targetFile));
21201
21290
  }
21202
21291
  extractUsageFromFile(filePath) {
21203
21292
  let raw;
@@ -21249,7 +21338,7 @@ var HistoryService = class _HistoryService {
21249
21338
  try {
21250
21339
  files = fs32.readdirSync(projectDir).filter((f) => f.endsWith(".jsonl")).filter((f) => {
21251
21340
  try {
21252
- return fs32.statSync(path38.join(projectDir, f)).mtimeMs >= monthStartMs;
21341
+ return fs32.statSync(path39.join(projectDir, f)).mtimeMs >= monthStartMs;
21253
21342
  } catch {
21254
21343
  return false;
21255
21344
  }
@@ -21260,7 +21349,7 @@ var HistoryService = class _HistoryService {
21260
21349
  for (const file of files) {
21261
21350
  let raw;
21262
21351
  try {
21263
- raw = fs32.readFileSync(path38.join(projectDir, file), "utf8");
21352
+ raw = fs32.readFileSync(path39.join(projectDir, file), "utf8");
21264
21353
  } catch {
21265
21354
  continue;
21266
21355
  }
@@ -21324,7 +21413,7 @@ var HistoryService = class _HistoryService {
21324
21413
  * showing an empty conversation.
21325
21414
  */
21326
21415
  async loadConversation(sessionId) {
21327
- const filePath = path38.join(this.projectDir, `${sessionId}.jsonl`);
21416
+ const filePath = path39.join(this.projectDir, `${sessionId}.jsonl`);
21328
21417
  const messages = parseJsonl(filePath);
21329
21418
  if (messages.length === 0) return;
21330
21419
  const totalBatches = Math.ceil(messages.length / CONVERSATION_BATCH_SIZE);
@@ -21378,7 +21467,7 @@ var HistoryService = class _HistoryService {
21378
21467
  if (!this.currentConversationId) return 0;
21379
21468
  }
21380
21469
  const sessionId = this.currentConversationId;
21381
- const filePath = path38.join(this.projectDir, `${sessionId}.jsonl`);
21470
+ const filePath = path39.join(this.projectDir, `${sessionId}.jsonl`);
21382
21471
  const messages = parseJsonl(filePath);
21383
21472
  if (messages.length === 0) return 0;
21384
21473
  const marker = this.lastUploadedUuid.get(sessionId);
@@ -22135,9 +22224,19 @@ async function pair(args2 = []) {
22135
22224
  const spin = dist_exports.spinner();
22136
22225
  spin.start("Requesting pairing code...");
22137
22226
  const result = await requestCode(pluginId);
22138
- if (!result) {
22227
+ if (!result.ok) {
22139
22228
  spin.stop("Failed");
22140
- showError("Could not reach the server. Check your connection and try again.");
22229
+ if (result.reason === "rate-limited") {
22230
+ showError(
22231
+ `Server is rate-limiting this request (HTTP 429). Retry in ${result.retryAfterSeconds}s.`
22232
+ );
22233
+ } else if (result.reason === "timeout") {
22234
+ showError("Server took too long to respond. Check your connection and try again.");
22235
+ } else if (result.reason === "http") {
22236
+ showError(`Server returned HTTP ${result.status}. Try again later.`);
22237
+ } else {
22238
+ showError("Could not reach the server. Check your connection and try again.");
22239
+ }
22141
22240
  process.exit(1);
22142
22241
  }
22143
22242
  spin.stop("Got pairing code");
@@ -22266,7 +22365,7 @@ async function autoLinkAfterPair(opts) {
22266
22365
 
22267
22366
  // src/commands/pair-auto.ts
22268
22367
  var fs33 = __toESM(require("fs"));
22269
- var os26 = __toESM(require("os"));
22368
+ var os27 = __toESM(require("os"));
22270
22369
  var import_crypto7 = require("crypto");
22271
22370
 
22272
22371
  // src/commands/start-infra-only.ts
@@ -22434,12 +22533,12 @@ function readTokenFromArgs(args2) {
22434
22533
  }
22435
22534
  const fileFlag = args2.find((a) => a.startsWith("--token-file="));
22436
22535
  if (fileFlag) {
22437
- const path45 = fileFlag.slice("--token-file=".length);
22536
+ const path46 = fileFlag.slice("--token-file=".length);
22438
22537
  try {
22439
- const content = fs33.readFileSync(path45, "utf8").trim();
22440
- if (content.length === 0) fail(`--token-file ${path45} is empty`);
22538
+ const content = fs33.readFileSync(path46, "utf8").trim();
22539
+ if (content.length === 0) fail(`--token-file ${path46} is empty`);
22441
22540
  try {
22442
- fs33.unlinkSync(path45);
22541
+ fs33.unlinkSync(path46);
22443
22542
  } catch {
22444
22543
  }
22445
22544
  return content;
@@ -22465,7 +22564,7 @@ async function claimOnce(token, pluginId) {
22465
22564
  pluginId,
22466
22565
  ideName: "codeam-cli (codespace)",
22467
22566
  ideVersion: process.env.npm_package_version ?? "unknown",
22468
- hostname: os26.hostname(),
22567
+ hostname: os27.hostname(),
22469
22568
  codespaceName: process.env.CODESPACE_NAME ?? "",
22470
22569
  // Current git branch of the codespace's working directory, so the
22471
22570
  // backend can populate `PairedSession.branch` for the codespace pair.
@@ -22705,7 +22804,7 @@ var import_picocolors10 = __toESM(require("picocolors"));
22705
22804
  var import_child_process16 = require("child_process");
22706
22805
  var import_util4 = require("util");
22707
22806
  var import_picocolors8 = __toESM(require("picocolors"));
22708
- var path39 = __toESM(require("path"));
22807
+ var path40 = __toESM(require("path"));
22709
22808
  var execFileP5 = (0, import_util4.promisify)(import_child_process16.execFile);
22710
22809
  var MAX_BUFFER = 8 * 1024 * 1024;
22711
22810
  function resetStdinForChild() {
@@ -23194,7 +23293,7 @@ var GitHubCodespacesProvider = class {
23194
23293
  });
23195
23294
  }
23196
23295
  async uploadFile(workspaceId, remotePath, contents, options = {}) {
23197
- const remoteDir = path39.posix.dirname(remotePath);
23296
+ const remoteDir = path40.posix.dirname(remotePath);
23198
23297
  const parts = [
23199
23298
  `mkdir -p ${shellQuote(remoteDir)}`,
23200
23299
  `cat > ${shellQuote(remotePath)}`
@@ -23264,7 +23363,7 @@ function shellQuote(s) {
23264
23363
  // src/services/providers/gitpod.ts
23265
23364
  var import_child_process17 = require("child_process");
23266
23365
  var import_util5 = require("util");
23267
- var path40 = __toESM(require("path"));
23366
+ var path41 = __toESM(require("path"));
23268
23367
  var import_picocolors9 = __toESM(require("picocolors"));
23269
23368
  var execFileP6 = (0, import_util5.promisify)(import_child_process17.execFile);
23270
23369
  var MAX_BUFFER2 = 8 * 1024 * 1024;
@@ -23504,7 +23603,7 @@ var GitpodProvider = class {
23504
23603
  });
23505
23604
  }
23506
23605
  async uploadFile(workspaceId, remotePath, contents, options = {}) {
23507
- const remoteDir = path40.posix.dirname(remotePath);
23606
+ const remoteDir = path41.posix.dirname(remotePath);
23508
23607
  const parts = [
23509
23608
  `mkdir -p ${shellQuote2(remoteDir)}`,
23510
23609
  `cat > ${shellQuote2(remotePath)}`
@@ -23540,7 +23639,7 @@ function shellQuote2(s) {
23540
23639
  // src/services/providers/gitlab-workspaces.ts
23541
23640
  var import_child_process18 = require("child_process");
23542
23641
  var import_util6 = require("util");
23543
- var path41 = __toESM(require("path"));
23642
+ var path42 = __toESM(require("path"));
23544
23643
  var execFileP7 = (0, import_util6.promisify)(import_child_process18.execFile);
23545
23644
  var MAX_BUFFER3 = 8 * 1024 * 1024;
23546
23645
  var GITLAB_API_BASE = process.env.CODEAM_GITLAB_API_URL ?? "https://gitlab.com/api/v4";
@@ -23800,7 +23899,7 @@ Docs: https://docs.gitlab.com/ee/user/workspace/configuration.html`
23800
23899
  }
23801
23900
  async uploadFile(workspaceId, remotePath, contents, options = {}) {
23802
23901
  const sshHost = process.env.CODEAM_GITLAB_SSH_HOST ?? "workspaces.gitlab.com";
23803
- const remoteDir = path41.posix.dirname(remotePath);
23902
+ const remoteDir = path42.posix.dirname(remotePath);
23804
23903
  const parts = [`mkdir -p ${shellQuote3(remoteDir)}`, `cat > ${shellQuote3(remotePath)}`];
23805
23904
  if (options.mode != null) {
23806
23905
  parts.push(`chmod ${options.mode.toString(8)} ${shellQuote3(remotePath)}`);
@@ -23868,7 +23967,7 @@ function shellQuote3(s) {
23868
23967
  // src/services/providers/railway.ts
23869
23968
  var import_child_process19 = require("child_process");
23870
23969
  var import_util7 = require("util");
23871
- var path42 = __toESM(require("path"));
23970
+ var path43 = __toESM(require("path"));
23872
23971
  var execFileP8 = (0, import_util7.promisify)(import_child_process19.execFile);
23873
23972
  var MAX_BUFFER4 = 8 * 1024 * 1024;
23874
23973
  function resetStdinForChild4() {
@@ -24104,7 +24203,7 @@ var RailwayProvider = class {
24104
24203
  if (!projectId || !serviceId) {
24105
24204
  throw new Error("Invalid Railway workspace id (expected projectId/serviceId).");
24106
24205
  }
24107
- const remoteDir = path42.posix.dirname(remotePath);
24206
+ const remoteDir = path43.posix.dirname(remotePath);
24108
24207
  const parts = [`mkdir -p ${shellQuote4(remoteDir)}`, `cat > ${shellQuote4(remotePath)}`];
24109
24208
  if (options.mode != null) {
24110
24209
  parts.push(`chmod ${options.mode.toString(8)} ${shellQuote4(remotePath)}`);
@@ -24648,7 +24747,7 @@ var import_node_dns = require("dns");
24648
24747
  var import_node_util4 = require("util");
24649
24748
  var import_node_crypto8 = require("crypto");
24650
24749
  var fs34 = __toESM(require("fs"));
24651
- var path43 = __toESM(require("path"));
24750
+ var path44 = __toESM(require("path"));
24652
24751
  var import_picocolors12 = __toESM(require("picocolors"));
24653
24752
  var dnsResolveP = (0, import_node_util4.promisify)(import_node_dns.resolve);
24654
24753
  async function checkDns(apiBase) {
@@ -24704,10 +24803,10 @@ async function checkHealth(apiBase) {
24704
24803
  }
24705
24804
  }
24706
24805
  function checkConfigDir() {
24707
- const dir = path43.join(require("os").homedir(), ".codeam");
24806
+ const dir = path44.join(require("os").homedir(), ".codeam");
24708
24807
  try {
24709
24808
  fs34.mkdirSync(dir, { recursive: true, mode: 448 });
24710
- const probe = path43.join(dir, ".doctor-probe");
24809
+ const probe = path44.join(dir, ".doctor-probe");
24711
24810
  fs34.writeFileSync(probe, "ok", { mode: 384 });
24712
24811
  const read = fs34.readFileSync(probe, "utf8");
24713
24812
  fs34.unlinkSync(probe);
@@ -24750,9 +24849,9 @@ function checkSessions() {
24750
24849
  }
24751
24850
  }
24752
24851
  function checkAgentBinaries() {
24753
- const os28 = createOsStrategy();
24852
+ const os29 = createOsStrategy();
24754
24853
  return getEnabledAgents().map((meta) => {
24755
- const found = os28.findInPath(meta.binaryName);
24854
+ const found = os29.findInPath(meta.binaryName);
24756
24855
  return {
24757
24856
  id: `agent-${meta.id}`,
24758
24857
  label: `Agent binary: ${meta.displayName} (${meta.binaryName})`,
@@ -24774,7 +24873,7 @@ function checkNodePty() {
24774
24873
  detail: "not required on this platform"
24775
24874
  };
24776
24875
  }
24777
- const vendoredPath = path43.join(__dirname, "vendor", "node-pty");
24876
+ const vendoredPath = path44.join(__dirname, "vendor", "node-pty");
24778
24877
  for (const target of [vendoredPath, "node-pty"]) {
24779
24878
  try {
24780
24879
  require(target);
@@ -24816,7 +24915,7 @@ function checkChokidar() {
24816
24915
  }
24817
24916
  async function doctor(args2 = []) {
24818
24917
  const json = args2.includes("--json");
24819
- const cliVersion = true ? "2.31.0" : "0.0.0-dev";
24918
+ const cliVersion = true ? "2.32.1" : "0.0.0-dev";
24820
24919
  const apiBase = resolveApiBaseUrl();
24821
24920
  const diagnosticId = (0, import_node_crypto8.randomUUID)();
24822
24921
  log.info("doctor", `run id=${diagnosticId} cli=${cliVersion}`);
@@ -25015,7 +25114,7 @@ async function completion(args2) {
25015
25114
  // src/commands/version.ts
25016
25115
  var import_picocolors13 = __toESM(require("picocolors"));
25017
25116
  function version2() {
25018
- const v = true ? "2.31.0" : "unknown";
25117
+ const v = true ? "2.32.1" : "unknown";
25019
25118
  console.log(`${import_picocolors13.default.bold("codeam-cli")} ${import_picocolors13.default.cyan(v)}`);
25020
25119
  }
25021
25120
 
@@ -25144,8 +25243,8 @@ var _subcommandHelpKeys = Object.keys(HELPS);
25144
25243
 
25145
25244
  // src/lib/updateNotifier.ts
25146
25245
  var fs35 = __toESM(require("fs"));
25147
- var os27 = __toESM(require("os"));
25148
- var path44 = __toESM(require("path"));
25246
+ var os28 = __toESM(require("os"));
25247
+ var path45 = __toESM(require("path"));
25149
25248
  var https8 = __toESM(require("https"));
25150
25249
  var import_node_child_process12 = require("child_process");
25151
25250
  var import_picocolors16 = __toESM(require("picocolors"));
@@ -25154,8 +25253,8 @@ var REGISTRY_URL = `https://registry.npmjs.org/${PKG_NAME}/latest`;
25154
25253
  var TTL_MS = 24 * 60 * 60 * 1e3;
25155
25254
  var REQUEST_TIMEOUT_MS = 1500;
25156
25255
  function cachePath() {
25157
- const dir = path44.join(os27.homedir(), ".codeam");
25158
- return path44.join(dir, "update-check.json");
25256
+ const dir = path45.join(os28.homedir(), ".codeam");
25257
+ return path45.join(dir, "update-check.json");
25159
25258
  }
25160
25259
  function readCache() {
25161
25260
  try {
@@ -25170,7 +25269,7 @@ function readCache() {
25170
25269
  function writeCache(cache) {
25171
25270
  try {
25172
25271
  const file = cachePath();
25173
- fs35.mkdirSync(path44.dirname(file), { recursive: true });
25272
+ fs35.mkdirSync(path45.dirname(file), { recursive: true });
25174
25273
  const tmp = `${file}.${process.pid}.tmp`;
25175
25274
  fs35.writeFileSync(tmp, JSON.stringify(cache));
25176
25275
  fs35.renameSync(tmp, file);
@@ -25247,7 +25346,7 @@ function isLinkedInstall() {
25247
25346
  timeout: 2e3
25248
25347
  }).trim();
25249
25348
  if (!root) return false;
25250
- const pkgPath = path44.join(root, PKG_NAME);
25349
+ const pkgPath = path45.join(root, PKG_NAME);
25251
25350
  return fs35.lstatSync(pkgPath).isSymbolicLink();
25252
25351
  } catch {
25253
25352
  return false;
@@ -25301,7 +25400,7 @@ function checkForUpdates() {
25301
25400
  if (process.env.CODEAM_DISABLE_UPDATE_CHECK === "1") return;
25302
25401
  if (process.env.CI) return;
25303
25402
  if (!process.stdout.isTTY) return;
25304
- const current = true ? "2.31.0" : null;
25403
+ const current = true ? "2.32.1" : null;
25305
25404
  if (!current) return;
25306
25405
  const cache = readCache();
25307
25406
  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.1",
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",