codeam-cli 2.27.1 → 2.27.3

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 +13 -0
  2. package/dist/index.js +1341 -560
  3. package/package.json +5 -1
package/dist/index.js CHANGED
@@ -300,6 +300,18 @@ var AGENT_REGISTRY = {
300
300
  // this via the existing --api-key escape hatch in commands/link.ts.
301
301
  supportedAuthKinds: ["api_key"],
302
302
  preferredAuthKind: "api_key"
303
+ },
304
+ gemini: {
305
+ id: "gemini",
306
+ displayName: "Gemini CLI",
307
+ binaryName: "gemini",
308
+ enabled: true,
309
+ // OAuth via `gemini auth login` (captured by `codeam link gemini`
310
+ // from ~/.gemini/oauth_creds.json) AND GEMINI_API_KEY are both
311
+ // accepted by the backend's GeminiProvisioningStrategy and propagated
312
+ // into codespace deploys.
313
+ supportedAuthKinds: ["oauth_token", "api_key"],
314
+ preferredAuthKind: "oauth_token"
303
315
  }
304
316
  };
305
317
  function getEnabledAgents() {
@@ -311,7 +323,7 @@ function getAgent(id) {
311
323
  return meta;
312
324
  }
313
325
  function isKnownAgentId(id) {
314
- return id === "claude" || id === "codex" || id === "copilot" || id === "coderabbit" || id === "cursor" || id === "aider";
326
+ return id === "claude" || id === "codex" || id === "copilot" || id === "coderabbit" || id === "cursor" || id === "aider" || id === "gemini";
315
327
  }
316
328
 
317
329
  // ../../packages/shared/src/api-url.ts
@@ -486,7 +498,7 @@ var import_qrcode_terminal = __toESM(require("qrcode-terminal"));
486
498
  // package.json
487
499
  var package_default = {
488
500
  name: "codeam-cli",
489
- version: "2.27.1",
501
+ version: "2.27.3",
490
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.",
491
503
  type: "commonjs",
492
504
  main: "dist/index.js",
@@ -556,8 +568,12 @@ var package_default = {
556
568
  node: ">=18.0.0"
557
569
  },
558
570
  dependencies: {
571
+ "@agentclientprotocol/claude-agent-acp": "^0.42.0",
572
+ "@agentclientprotocol/codex-acp": "^0.0.45",
573
+ "@agentclientprotocol/sdk": "^0.25.0",
559
574
  "@clack/prompts": "^1.2.0",
560
575
  chokidar: "^3.6.0",
576
+ "cursor-agent-acp": "^0.1.1",
561
577
  picocolors: "^1.1.0",
562
578
  "qrcode-terminal": "^0.12.0",
563
579
  which: "^5.0.0",
@@ -853,7 +869,7 @@ async function postPreviewEvent(input) {
853
869
  }
854
870
  }
855
871
  async function _postJsonAuthed(url, body, pluginAuthToken) {
856
- return new Promise((resolve5, reject) => {
872
+ return new Promise((resolve6, reject) => {
857
873
  const data = JSON.stringify(body);
858
874
  const u2 = new URL(url);
859
875
  const transport = u2.protocol === "https:" ? https : http;
@@ -874,8 +890,8 @@ async function _postJsonAuthed(url, body, pluginAuthToken) {
874
890
  (res) => {
875
891
  res.on("error", reject);
876
892
  let responseBody = "";
877
- res.on("data", (chunk) => {
878
- responseBody += chunk.toString();
893
+ res.on("data", (chunk2) => {
894
+ responseBody += chunk2.toString();
879
895
  });
880
896
  res.on("end", () => {
881
897
  if (res.statusCode && res.statusCode >= 400) {
@@ -885,9 +901,9 @@ async function _postJsonAuthed(url, body, pluginAuthToken) {
885
901
  return;
886
902
  }
887
903
  try {
888
- resolve5(JSON.parse(responseBody));
904
+ resolve6(JSON.parse(responseBody));
889
905
  } catch {
890
- resolve5(null);
906
+ resolve6(null);
891
907
  }
892
908
  });
893
909
  }
@@ -902,7 +918,7 @@ async function _postJsonAuthed(url, body, pluginAuthToken) {
902
918
  });
903
919
  }
904
920
  async function _postJson(url, body) {
905
- return new Promise((resolve5, reject) => {
921
+ return new Promise((resolve6, reject) => {
906
922
  const data = JSON.stringify(body);
907
923
  const u2 = new URL(url);
908
924
  const transport = u2.protocol === "https:" ? https : http;
@@ -922,8 +938,8 @@ async function _postJson(url, body) {
922
938
  (res) => {
923
939
  res.on("error", reject);
924
940
  let body2 = "";
925
- res.on("data", (chunk) => {
926
- body2 += chunk.toString();
941
+ res.on("data", (chunk2) => {
942
+ body2 += chunk2.toString();
927
943
  });
928
944
  res.on("end", () => {
929
945
  if (res.statusCode && res.statusCode >= 400) {
@@ -931,9 +947,9 @@ async function _postJson(url, body) {
931
947
  return;
932
948
  }
933
949
  try {
934
- resolve5(JSON.parse(body2));
950
+ resolve6(JSON.parse(body2));
935
951
  } catch {
936
- resolve5(null);
952
+ resolve6(null);
937
953
  }
938
954
  });
939
955
  }
@@ -948,7 +964,7 @@ async function _postJson(url, body) {
948
964
  });
949
965
  }
950
966
  async function _getJson(url) {
951
- return new Promise((resolve5, reject) => {
967
+ return new Promise((resolve6, reject) => {
952
968
  const u2 = new URL(url);
953
969
  const transport = u2.protocol === "https:" ? https : http;
954
970
  const req = transport.request(
@@ -963,8 +979,8 @@ async function _getJson(url) {
963
979
  (res) => {
964
980
  res.on("error", reject);
965
981
  let body = "";
966
- res.on("data", (chunk) => {
967
- body += chunk.toString();
982
+ res.on("data", (chunk2) => {
983
+ body += chunk2.toString();
968
984
  });
969
985
  res.on("end", () => {
970
986
  if (res.statusCode && res.statusCode >= 400) {
@@ -972,9 +988,9 @@ async function _getJson(url) {
972
988
  return;
973
989
  }
974
990
  try {
975
- resolve5(JSON.parse(body));
991
+ resolve6(JSON.parse(body));
976
992
  } catch {
977
- resolve5(null);
993
+ resolve6(null);
978
994
  }
979
995
  });
980
996
  }
@@ -1144,8 +1160,8 @@ function createGetModuleFromFilename(basePath = process.argv[1] ? (0, import_pat
1144
1160
  return decodedFile;
1145
1161
  };
1146
1162
  }
1147
- function normalizeWindowsPath(path43) {
1148
- return path43.replace(/^[A-Z]:/, "").replace(/\\/g, "/");
1163
+ function normalizeWindowsPath(path45) {
1164
+ return path45.replace(/^[A-Z]:/, "").replace(/\\/g, "/");
1149
1165
  }
1150
1166
 
1151
1167
  // ../../node_modules/@posthog/core/dist/featureFlagUtils.mjs
@@ -3625,15 +3641,15 @@ async function addSourceContext(frames) {
3625
3641
  LRU_FILE_CONTENTS_CACHE.reduce();
3626
3642
  return frames;
3627
3643
  }
3628
- function getContextLinesFromFile(path43, ranges, output) {
3629
- return new Promise((resolve5) => {
3630
- const stream = (0, import_node_fs.createReadStream)(path43);
3644
+ function getContextLinesFromFile(path45, ranges, output) {
3645
+ return new Promise((resolve6) => {
3646
+ const stream = (0, import_node_fs.createReadStream)(path45);
3631
3647
  const lineReaded = (0, import_node_readline.createInterface)({
3632
3648
  input: stream
3633
3649
  });
3634
3650
  function destroyStreamAndResolve() {
3635
3651
  stream.destroy();
3636
- resolve5();
3652
+ resolve6();
3637
3653
  }
3638
3654
  let lineNumber = 0;
3639
3655
  let currentRangeIndex = 0;
@@ -3642,7 +3658,7 @@ function getContextLinesFromFile(path43, ranges, output) {
3642
3658
  let rangeStart = range[0];
3643
3659
  let rangeEnd = range[1];
3644
3660
  function onStreamError() {
3645
- LRU_FILE_CONTENTS_FS_READ_FAILED.set(path43, 1);
3661
+ LRU_FILE_CONTENTS_FS_READ_FAILED.set(path45, 1);
3646
3662
  lineReaded.close();
3647
3663
  lineReaded.removeAllListeners();
3648
3664
  destroyStreamAndResolve();
@@ -3703,8 +3719,8 @@ function clearLineContext(frame) {
3703
3719
  delete frame.context_line;
3704
3720
  delete frame.post_context;
3705
3721
  }
3706
- function shouldSkipContextLinesForFile(path43) {
3707
- return path43.startsWith("node:") || path43.endsWith(".min.js") || path43.endsWith(".min.cjs") || path43.endsWith(".min.mjs") || path43.startsWith("data:");
3722
+ function shouldSkipContextLinesForFile(path45) {
3723
+ return path45.startsWith("node:") || path45.endsWith(".min.js") || path45.endsWith(".min.cjs") || path45.endsWith(".min.mjs") || path45.startsWith("data:");
3708
3724
  }
3709
3725
  function shouldSkipContextLinesForFrame(frame) {
3710
3726
  if (void 0 !== frame.lineno && frame.lineno > MAX_CONTEXTLINES_LINENO) return true;
@@ -4920,9 +4936,9 @@ var PostHogBackendClient = class extends PostHogCoreStateless {
4920
4936
  if (!waitUntil) return;
4921
4937
  if (this.disabled || this.optedOut) return;
4922
4938
  if (!this._waitUntilCycle) {
4923
- let resolve5;
4939
+ let resolve6;
4924
4940
  const promise = new Promise((r) => {
4925
- resolve5 = r;
4941
+ resolve6 = r;
4926
4942
  });
4927
4943
  try {
4928
4944
  waitUntil(promise);
@@ -4930,7 +4946,7 @@ var PostHogBackendClient = class extends PostHogCoreStateless {
4930
4946
  return;
4931
4947
  }
4932
4948
  this._waitUntilCycle = {
4933
- resolve: resolve5,
4949
+ resolve: resolve6,
4934
4950
  startedAt: Date.now(),
4935
4951
  timer: void 0
4936
4952
  };
@@ -4954,12 +4970,12 @@ var PostHogBackendClient = class extends PostHogCoreStateless {
4954
4970
  return cycle?.resolve;
4955
4971
  }
4956
4972
  async resolveWaitUntilFlush() {
4957
- const resolve5 = this._consumeWaitUntilCycle();
4973
+ const resolve6 = this._consumeWaitUntilCycle();
4958
4974
  try {
4959
4975
  await super.flush();
4960
4976
  } catch {
4961
4977
  } finally {
4962
- resolve5?.();
4978
+ resolve6?.();
4963
4979
  }
4964
4980
  }
4965
4981
  getPersistedProperty(key) {
@@ -5051,15 +5067,15 @@ var PostHogBackendClient = class extends PostHogCoreStateless {
5051
5067
  async waitForLocalEvaluationReady(timeoutMs = THIRTY_SECONDS) {
5052
5068
  if (this.isLocalEvaluationReady()) return true;
5053
5069
  if (void 0 === this.featureFlagsPoller) return false;
5054
- return new Promise((resolve5) => {
5070
+ return new Promise((resolve6) => {
5055
5071
  const timeout = setTimeout(() => {
5056
5072
  cleanup();
5057
- resolve5(false);
5073
+ resolve6(false);
5058
5074
  }, timeoutMs);
5059
5075
  const cleanup = this._events.on("localEvaluationFlagsLoaded", (count) => {
5060
5076
  clearTimeout(timeout);
5061
5077
  cleanup();
5062
- resolve5(count > 0);
5078
+ resolve6(count > 0);
5063
5079
  });
5064
5080
  });
5065
5081
  }
@@ -5496,13 +5512,13 @@ var PostHogBackendClient = class extends PostHogCoreStateless {
5496
5512
  this.context?.enter(data, options);
5497
5513
  }
5498
5514
  async _shutdown(shutdownTimeoutMs) {
5499
- const resolve5 = this._consumeWaitUntilCycle();
5515
+ const resolve6 = this._consumeWaitUntilCycle();
5500
5516
  await this.featureFlagsPoller?.stopPoller(shutdownTimeoutMs);
5501
5517
  this.errorTracking.shutdown();
5502
5518
  try {
5503
5519
  return await super._shutdown(shutdownTimeoutMs);
5504
5520
  } finally {
5505
- resolve5?.();
5521
+ resolve6?.();
5506
5522
  }
5507
5523
  }
5508
5524
  async _requestRemoteConfigPayload(flagKey) {
@@ -5858,7 +5874,7 @@ function readAnonId() {
5858
5874
  }
5859
5875
  function superProperties() {
5860
5876
  return {
5861
- cliVersion: true ? "2.27.1" : "0.0.0-dev",
5877
+ cliVersion: true ? "2.27.3" : "0.0.0-dev",
5862
5878
  nodeVersion: process.version,
5863
5879
  platform: process.platform,
5864
5880
  arch: process.arch,
@@ -6065,9 +6081,9 @@ var CommandRelayService = class {
6065
6081
  this.armSseWatchdog();
6066
6082
  let buffer = "";
6067
6083
  res.setEncoding("utf8");
6068
- res.on("data", (chunk) => {
6084
+ res.on("data", (chunk2) => {
6069
6085
  this.sseLastByteAt = Date.now();
6070
- buffer += chunk;
6086
+ buffer += chunk2;
6071
6087
  let frameEnd;
6072
6088
  while ((frameEnd = buffer.indexOf("\n\n")) !== -1) {
6073
6089
  const frame = buffer.slice(0, frameEnd);
@@ -6736,9 +6752,9 @@ var UnixPtyStrategy = class {
6736
6752
  );
6737
6753
  process.exit(1);
6738
6754
  });
6739
- this.proc.stdout?.on("data", (chunk) => {
6740
- process.stdout.write(chunk);
6741
- this.opts.onData(chunk.toString("utf8"));
6755
+ this.proc.stdout?.on("data", (chunk2) => {
6756
+ process.stdout.write(chunk2);
6757
+ this.opts.onData(chunk2.toString("utf8"));
6742
6758
  });
6743
6759
  if (process.stdin.isTTY) process.stdin.setRawMode(true);
6744
6760
  process.stdin.resume();
@@ -6830,8 +6846,8 @@ var UnixPtyStrategy = class {
6830
6846
  }
6831
6847
  }
6832
6848
  }
6833
- stdinHandler = (chunk) => {
6834
- this.proc?.stdin?.write(chunk);
6849
+ stdinHandler = (chunk2) => {
6850
+ this.proc?.stdin?.write(chunk2);
6835
6851
  };
6836
6852
  handleResize = () => {
6837
6853
  if (this.proc?.pid) {
@@ -7017,8 +7033,8 @@ var WindowsConPtyStrategy = class _WindowsConPtyStrategy {
7017
7033
  }
7018
7034
  this.pty = null;
7019
7035
  }
7020
- stdinHandler = (chunk) => {
7021
- this.pty?.write(chunk.toString("utf8"));
7036
+ stdinHandler = (chunk2) => {
7037
+ this.pty?.write(chunk2.toString("utf8"));
7022
7038
  };
7023
7039
  };
7024
7040
 
@@ -7051,9 +7067,9 @@ var WindowsPtyStrategy = class {
7051
7067
  );
7052
7068
  process.exit(1);
7053
7069
  });
7054
- this.proc.stdout?.on("data", (chunk) => {
7055
- process.stdout.write(chunk);
7056
- this.opts.onData(chunk.toString("utf8"));
7070
+ this.proc.stdout?.on("data", (chunk2) => {
7071
+ process.stdout.write(chunk2);
7072
+ this.opts.onData(chunk2.toString("utf8"));
7057
7073
  });
7058
7074
  this.proc.stdin?.write("");
7059
7075
  if (process.stdin.isTTY) process.stdin.setRawMode(true);
@@ -7080,8 +7096,8 @@ var WindowsPtyStrategy = class {
7080
7096
  }
7081
7097
  }
7082
7098
  }
7083
- stdinHandler = (chunk) => {
7084
- this.proc?.stdin?.write(chunk);
7099
+ stdinHandler = (chunk2) => {
7100
+ this.proc?.stdin?.write(chunk2);
7085
7101
  };
7086
7102
  };
7087
7103
 
@@ -7200,10 +7216,10 @@ function buildForPlatform(platform2) {
7200
7216
  var import_node_crypto4 = require("crypto");
7201
7217
 
7202
7218
  // src/agents/claude/resolver.ts
7203
- function buildClaudeLaunch(extraArgs = [], os27 = createOsStrategy()) {
7204
- const found = os27.findInPath("claude") ?? os27.findInPath("claude-code");
7219
+ function buildClaudeLaunch(extraArgs = [], os28 = createOsStrategy()) {
7220
+ const found = os28.findInPath("claude") ?? os28.findInPath("claude-code");
7205
7221
  if (!found) return null;
7206
- return os27.buildLaunch(found, extraArgs);
7222
+ return os28.buildLaunch(found, extraArgs);
7207
7223
  }
7208
7224
 
7209
7225
  // src/agents/claude/installer.ts
@@ -9159,15 +9175,15 @@ function runInstaller() {
9159
9175
  "-Command",
9160
9176
  "irm https://claude.ai/install.ps1 | iex"
9161
9177
  ] : ["-c", "curl -fsSL https://claude.ai/install.sh | bash"];
9162
- return new Promise((resolve5) => {
9178
+ return new Promise((resolve6) => {
9163
9179
  const proc = (0, import_child_process4.spawn)(cmd, args2, { stdio: "inherit" });
9164
9180
  proc.on("error", (err) => {
9165
9181
  console.error(`
9166
9182
  \u2717 Installer failed to launch: ${err.message}`);
9167
- resolve5(false);
9183
+ resolve6(false);
9168
9184
  });
9169
9185
  proc.on("exit", (code) => {
9170
- resolve5(code === 0);
9186
+ resolve6(code === 0);
9171
9187
  });
9172
9188
  });
9173
9189
  }
@@ -9344,17 +9360,17 @@ function parseUsageOutput(raw) {
9344
9360
  return { percent, resetAt };
9345
9361
  }
9346
9362
  async function fetchClaudeQuota() {
9347
- return new Promise((resolve5) => {
9363
+ return new Promise((resolve6) => {
9348
9364
  const claudeCmd = findInPath("claude") ? "claude" : "claude-code";
9349
9365
  if (!claudeCmd) {
9350
- resolve5(null);
9366
+ resolve6(null);
9351
9367
  return;
9352
9368
  }
9353
9369
  const helperPath = path11.join(os10.tmpdir(), "codeam-quota-helper.py");
9354
9370
  fs8.writeFileSync(helperPath, HELPER_SCRIPT, { mode: 420 });
9355
9371
  const python = findInPath("python3") ?? findInPath("python");
9356
9372
  if (!python) {
9357
- resolve5(null);
9373
+ resolve6(null);
9358
9374
  return;
9359
9375
  }
9360
9376
  const proc = (0, import_child_process5.spawn)(python, [helperPath, claudeCmd, "--tools", ""], {
@@ -9364,8 +9380,8 @@ async function fetchClaudeQuota() {
9364
9380
  });
9365
9381
  let output = "";
9366
9382
  let resolved = false;
9367
- proc.stdout?.on("data", (chunk) => {
9368
- output += chunk.toString("utf8");
9383
+ proc.stdout?.on("data", (chunk2) => {
9384
+ output += chunk2.toString("utf8");
9369
9385
  });
9370
9386
  setTimeout(() => {
9371
9387
  proc.stdin?.write("/usage\r");
@@ -9381,13 +9397,13 @@ async function fetchClaudeQuota() {
9381
9397
  fs8.unlinkSync(helperPath);
9382
9398
  } catch {
9383
9399
  }
9384
- resolve5(result);
9400
+ resolve6(result);
9385
9401
  }, 5e3);
9386
9402
  }, 8e3);
9387
9403
  setTimeout(() => {
9388
9404
  if (!resolved) {
9389
9405
  resolved = true;
9390
- resolve5(null);
9406
+ resolve6(null);
9391
9407
  }
9392
9408
  try {
9393
9409
  proc.kill();
@@ -9418,12 +9434,12 @@ function killActiveSpawnAndCaptureChildren() {
9418
9434
  }
9419
9435
  async function spawnAndCapture(cmd, args2, opts = {}) {
9420
9436
  const timeoutMs = opts.timeoutMs ?? 6e4;
9421
- return new Promise((resolve5) => {
9437
+ return new Promise((resolve6) => {
9422
9438
  let settled = false;
9423
9439
  const settle = (value) => {
9424
9440
  if (settled) return;
9425
9441
  settled = true;
9426
- resolve5(value);
9442
+ resolve6(value);
9427
9443
  };
9428
9444
  let child;
9429
9445
  try {
@@ -9438,11 +9454,11 @@ async function spawnAndCapture(cmd, args2, opts = {}) {
9438
9454
  }
9439
9455
  activeChildren.add(child);
9440
9456
  let stdout = "";
9441
- child.stdout?.on("data", (chunk) => {
9442
- stdout += chunk.toString("utf8");
9457
+ child.stdout?.on("data", (chunk2) => {
9458
+ stdout += chunk2.toString("utf8");
9443
9459
  });
9444
- child.stderr?.on("data", (chunk) => {
9445
- opts.onStderr?.(chunk.toString("utf8"));
9460
+ child.stderr?.on("data", (chunk2) => {
9461
+ opts.onStderr?.(chunk2.toString("utf8"));
9446
9462
  });
9447
9463
  const timer = setTimeout(() => {
9448
9464
  try {
@@ -9930,13 +9946,13 @@ function detectStartupBanner(lines) {
9930
9946
  while (artStart > 0 && BANNER_ART_RE.test(lines[artStart - 1])) artStart--;
9931
9947
  if (metaIdx - artStart < 2) return null;
9932
9948
  const pathLine = (lines[metaIdx + 1] ?? "").trim();
9933
- const path43 = pathLine && !BANNER_ART_RE.test(pathLine) ? pathLine : "";
9949
+ const path45 = pathLine && !BANNER_ART_RE.test(pathLine) ? pathLine : "";
9934
9950
  return {
9935
9951
  title: "",
9936
9952
  subtitle: lines[metaIdx].trim(),
9937
- path: path43,
9953
+ path: path45,
9938
9954
  startIdx: artStart,
9939
- endIdx: metaIdx + (path43 ? 1 : 0)
9955
+ endIdx: metaIdx + (path45 ? 1 : 0)
9940
9956
  };
9941
9957
  }
9942
9958
 
@@ -9946,8 +9962,8 @@ var ClaudeRuntimeStrategy = class {
9946
9962
  meta = getAgent("claude");
9947
9963
  mode = "interactive";
9948
9964
  os;
9949
- constructor(os27) {
9950
- this.os = os27;
9965
+ constructor(os28) {
9966
+ this.os = os28;
9951
9967
  }
9952
9968
  /**
9953
9969
  * Claude Code's react-ink TUI enables bracketed-paste mode at
@@ -10955,8 +10971,8 @@ function codexCredentialLocator() {
10955
10971
  function codexLoginLauncher() {
10956
10972
  return {
10957
10973
  async ensureInstalled() {
10958
- const os27 = createOsStrategy();
10959
- return os27.findInPath("codex") !== null;
10974
+ const os28 = createOsStrategy();
10975
+ return os28.findInPath("codex") !== null;
10960
10976
  },
10961
10977
  launch() {
10962
10978
  return (0, import_node_child_process3.spawn)("codex", ["login"], { stdio: "inherit" });
@@ -10979,8 +10995,8 @@ var CodexRuntimeStrategy = class {
10979
10995
  meta = getAgent("codex");
10980
10996
  mode = "interactive";
10981
10997
  os;
10982
- constructor(os27) {
10983
- this.os = os27;
10998
+ constructor(os28) {
10999
+ this.os = os28;
10984
11000
  }
10985
11001
  async prepareLaunch() {
10986
11002
  let binary = this.os.findInPath("codex");
@@ -11086,16 +11102,16 @@ var CodexRuntimeStrategy = class {
11086
11102
  });
11087
11103
  }
11088
11104
  };
11089
- function resolveNpm(os27) {
11090
- return os27.id === "win32" ? "npm.cmd" : "npm";
11105
+ function resolveNpm(os28) {
11106
+ return os28.id === "win32" ? "npm.cmd" : "npm";
11091
11107
  }
11092
- async function installCodexViaNpm(os27) {
11093
- return new Promise((resolve5, reject) => {
11094
- const proc = (0, import_node_child_process4.spawn)(resolveNpm(os27), ["install", "-g", "@openai/codex"], {
11108
+ async function installCodexViaNpm(os28) {
11109
+ return new Promise((resolve6, reject) => {
11110
+ const proc = (0, import_node_child_process4.spawn)(resolveNpm(os28), ["install", "-g", "@openai/codex"], {
11095
11111
  stdio: "inherit"
11096
11112
  });
11097
11113
  proc.on("close", (code) => {
11098
- if (code === 0) resolve5();
11114
+ if (code === 0) resolve6();
11099
11115
  else reject(new Error(`npm install -g @openai/codex exited ${code}`));
11100
11116
  });
11101
11117
  proc.on("error", (err) => {
@@ -11108,16 +11124,16 @@ async function installCodexViaNpm(os27) {
11108
11124
  });
11109
11125
  });
11110
11126
  }
11111
- function augmentNpmGlobalBin(os27) {
11127
+ function augmentNpmGlobalBin(os28) {
11112
11128
  try {
11113
- const result = (0, import_node_child_process4.spawnSync)(resolveNpm(os27), ["prefix", "-g"], {
11129
+ const result = (0, import_node_child_process4.spawnSync)(resolveNpm(os28), ["prefix", "-g"], {
11114
11130
  stdio: ["ignore", "pipe", "ignore"]
11115
11131
  });
11116
11132
  if (result.status !== 0) return;
11117
11133
  const prefix = result.stdout.toString().trim();
11118
11134
  if (!prefix) return;
11119
- const binDir = os27.id === "win32" ? prefix : path17.join(prefix, "bin");
11120
- os27.augmentPath([binDir]);
11135
+ const binDir = os28.id === "win32" ? prefix : path17.join(prefix, "bin");
11136
+ os28.augmentPath([binDir]);
11121
11137
  } catch {
11122
11138
  }
11123
11139
  }
@@ -11201,25 +11217,25 @@ var import_node_child_process7 = require("child_process");
11201
11217
  // src/agents/coderabbit/installer.ts
11202
11218
  var import_node_child_process5 = require("child_process");
11203
11219
  var INSTALL_URL = "https://cli.coderabbit.ai/install.sh";
11204
- async function ensureCoderabbitInstalled(os27) {
11205
- if (os27.findInPath("coderabbit")) return true;
11206
- if (os27.id === "win32") {
11220
+ async function ensureCoderabbitInstalled(os28) {
11221
+ if (os28.findInPath("coderabbit")) return true;
11222
+ if (os28.id === "win32") {
11207
11223
  console.error(
11208
11224
  "\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"
11209
11225
  );
11210
11226
  return false;
11211
11227
  }
11212
11228
  console.log("\n CodeRabbit CLI not found \u2014 installing via the official script\u2026\n");
11213
- const ok = await new Promise((resolve5) => {
11229
+ const ok = await new Promise((resolve6) => {
11214
11230
  const proc = (0, import_node_child_process5.spawn)("sh", ["-c", `curl -fsSL ${INSTALL_URL} | sh`], {
11215
11231
  stdio: "inherit"
11216
11232
  });
11217
- proc.on("close", (code) => resolve5(code === 0));
11218
- proc.on("error", () => resolve5(false));
11233
+ proc.on("close", (code) => resolve6(code === 0));
11234
+ proc.on("error", () => resolve6(false));
11219
11235
  });
11220
11236
  if (!ok) return false;
11221
- os27.augmentPath([`${os27.homeDir()}/.local/bin`, "/opt/homebrew/bin"]);
11222
- return os27.findInPath("coderabbit") !== null;
11237
+ os28.augmentPath([`${os28.homeDir()}/.local/bin`, "/opt/homebrew/bin"]);
11238
+ return os28.findInPath("coderabbit") !== null;
11223
11239
  }
11224
11240
 
11225
11241
  // src/agents/coderabbit/link.ts
@@ -11246,10 +11262,10 @@ function coderabbitCredentialLocator() {
11246
11262
  extract: extractLocalCoderabbitToken
11247
11263
  };
11248
11264
  }
11249
- function coderabbitLoginLauncher(os27) {
11265
+ function coderabbitLoginLauncher(os28) {
11250
11266
  return {
11251
11267
  async ensureInstalled() {
11252
- return ensureCoderabbitInstalled(os27);
11268
+ return ensureCoderabbitInstalled(os28);
11253
11269
  },
11254
11270
  launch() {
11255
11271
  return (0, import_node_child_process6.spawn)("coderabbit", ["login"], { stdio: "inherit" });
@@ -11272,11 +11288,11 @@ function parseReview(stdout) {
11272
11288
  for (const line of lines) {
11273
11289
  const m = line.match(HUNK_LINE_RE);
11274
11290
  if (!m) continue;
11275
- const [, path43, lineNo, sevToken, message] = m;
11276
- if (!path43 || !lineNo || !message) continue;
11291
+ const [, path45, lineNo, sevToken, message] = m;
11292
+ if (!path45 || !lineNo || !message) continue;
11277
11293
  const cleanedMessage = message.trim().replace(/^[*-]\s+/, "");
11278
11294
  hunks.push({
11279
- path: path43.trim(),
11295
+ path: path45.trim(),
11280
11296
  line: Number(lineNo),
11281
11297
  severity: sevToken ? SEVERITY_MAP[sevToken.toLowerCase()] : void 0,
11282
11298
  message: cleanedMessage
@@ -11295,8 +11311,8 @@ var CoderabbitRuntimeStrategy = class {
11295
11311
  meta = getAgent("coderabbit");
11296
11312
  mode = "batch";
11297
11313
  os;
11298
- constructor(os27) {
11299
- this.os = os27;
11314
+ constructor(os28) {
11315
+ this.os = os28;
11300
11316
  }
11301
11317
  getDefaultArgs() {
11302
11318
  return ["review"];
@@ -11334,7 +11350,7 @@ var CoderabbitRuntimeStrategy = class {
11334
11350
  }
11335
11351
  async runOneShot(input) {
11336
11352
  const launch = await this.prepareInvocation(input);
11337
- return new Promise((resolve5, reject) => {
11353
+ return new Promise((resolve6, reject) => {
11338
11354
  const stdoutBuf = [];
11339
11355
  const stderrBuf = [];
11340
11356
  const proc = (0, import_node_child_process7.spawn)(launch.cmd, launch.args, {
@@ -11345,7 +11361,7 @@ var CoderabbitRuntimeStrategy = class {
11345
11361
  proc.stderr?.on("data", (b) => stderrBuf.push(b));
11346
11362
  proc.on("error", (err) => reject(err));
11347
11363
  proc.on("close", (code) => {
11348
- resolve5(
11364
+ resolve6(
11349
11365
  this.parseOutput({
11350
11366
  exitCode: code ?? 0,
11351
11367
  stdout: Buffer.concat(stdoutBuf).toString("utf8"),
@@ -11411,10 +11427,10 @@ function cursorCredentialLocator() {
11411
11427
  extract: extractLocalCursorToken
11412
11428
  };
11413
11429
  }
11414
- function cursorLoginLauncher(os27) {
11430
+ function cursorLoginLauncher(os28) {
11415
11431
  return {
11416
11432
  async ensureInstalled() {
11417
- if (os27.findInPath("cursor-agent")) return true;
11433
+ if (os28.findInPath("cursor-agent")) return true;
11418
11434
  console.error(
11419
11435
  "\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"
11420
11436
  );
@@ -11476,8 +11492,8 @@ var CursorRuntimeStrategy = class {
11476
11492
  meta = getAgent("cursor");
11477
11493
  mode = "interactive";
11478
11494
  os;
11479
- constructor(os27) {
11480
- this.os = os27;
11495
+ constructor(os28) {
11496
+ this.os = os28;
11481
11497
  }
11482
11498
  async prepareLaunch() {
11483
11499
  const binary = this.os.findInPath("cursor-agent");
@@ -11597,10 +11613,10 @@ function aiderCredentialLocator() {
11597
11613
  extract: extractLocalAiderToken
11598
11614
  };
11599
11615
  }
11600
- function aiderLoginLauncher(os27) {
11616
+ function aiderLoginLauncher(os28) {
11601
11617
  return {
11602
11618
  async ensureInstalled() {
11603
- if (os27.findInPath("aider")) return true;
11619
+ if (os28.findInPath("aider")) return true;
11604
11620
  console.error(
11605
11621
  "\n \u2717 aider binary not on PATH.\n Install Aider:\n pip install aider-chat\n then re-run `codeam link aider`.\n"
11606
11622
  );
@@ -11610,7 +11626,7 @@ function aiderLoginLauncher(os27) {
11610
11626
  console.error(
11611
11627
  "\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"
11612
11628
  );
11613
- return (0, import_node_child_process9.spawn)(os27.id === "win32" ? "cmd.exe" : "sh", os27.id === "win32" ? ["/c", "exit", "0"] : ["-c", "exit 0"], {
11629
+ return (0, import_node_child_process9.spawn)(os28.id === "win32" ? "cmd.exe" : "sh", os28.id === "win32" ? ["/c", "exit", "0"] : ["-c", "exit 0"], {
11614
11630
  stdio: "ignore"
11615
11631
  });
11616
11632
  }
@@ -11682,8 +11698,8 @@ var AiderRuntimeStrategy = class {
11682
11698
  meta = getAgent("aider");
11683
11699
  mode = "interactive";
11684
11700
  os;
11685
- constructor(os27) {
11686
- this.os = os27;
11701
+ constructor(os28) {
11702
+ this.os = os28;
11687
11703
  }
11688
11704
  async prepareLaunch() {
11689
11705
  const binary = this.os.findInPath("aider");
@@ -11750,19 +11766,184 @@ var AiderRuntimeStrategy = class {
11750
11766
  }
11751
11767
  };
11752
11768
 
11769
+ // src/agents/gemini/link.ts
11770
+ var import_node_child_process10 = require("child_process");
11771
+
11772
+ // src/agents/gemini/local-token.ts
11773
+ var fs20 = __toESM(require("fs"));
11774
+ var os21 = __toESM(require("os"));
11775
+ var path24 = __toESM(require("path"));
11776
+ function geminiCredentialsPath() {
11777
+ return path24.join(os21.homedir(), ".gemini", "oauth_creds.json");
11778
+ }
11779
+ function geminiCredentialsPaths() {
11780
+ return [geminiCredentialsPath()];
11781
+ }
11782
+ async function extractLocalGeminiToken() {
11783
+ const file = geminiCredentialsPath();
11784
+ if (!fs20.existsSync(file)) return null;
11785
+ const credential = fs20.readFileSync(file, "utf8").trim();
11786
+ if (credential.length === 0) return null;
11787
+ return { method: "oauth", credential, source: "flat-file" };
11788
+ }
11789
+ function validateLocalGeminiToken(credential) {
11790
+ let parsed;
11791
+ try {
11792
+ parsed = JSON.parse(credential);
11793
+ } catch {
11794
+ return { status: "unknown" };
11795
+ }
11796
+ if (typeof parsed.expiry_date !== "number") return { status: "unknown" };
11797
+ const expiresAt = parsed.expiry_date;
11798
+ if (Date.now() >= expiresAt) {
11799
+ return {
11800
+ status: "expired",
11801
+ reason: "Gemini OAuth access token expired",
11802
+ expiresAt
11803
+ };
11804
+ }
11805
+ return { status: "valid", expiresAt };
11806
+ }
11807
+
11808
+ // src/agents/gemini/link.ts
11809
+ function geminiCredentialLocator() {
11810
+ return {
11811
+ publicId: "gemini",
11812
+ vendor: "Google",
11813
+ hint: "~/.gemini/oauth_creds.json",
11814
+ watchPaths: geminiCredentialsPaths,
11815
+ extract: extractLocalGeminiToken,
11816
+ validate: (token) => {
11817
+ const result = validateLocalGeminiToken(token.credential);
11818
+ return { status: result.status, reason: result.reason };
11819
+ }
11820
+ };
11821
+ }
11822
+ function geminiLoginLauncher() {
11823
+ return {
11824
+ async ensureInstalled() {
11825
+ const os28 = createOsStrategy();
11826
+ return os28.findInPath("gemini") !== null;
11827
+ },
11828
+ launch() {
11829
+ return (0, import_node_child_process10.spawn)("gemini", ["auth", "login"], { stdio: "inherit" });
11830
+ }
11831
+ };
11832
+ }
11833
+
11834
+ // src/agents/gemini/runtime.ts
11835
+ var GEMINI_PRO_CONTEXT_WINDOW = 1e6;
11836
+ var GEMINI_FLASH_CONTEXT_WINDOW = 1e6;
11837
+ var GEMINI_MODELS = [
11838
+ {
11839
+ id: "gemini-2.5-pro",
11840
+ label: "Gemini 2.5 Pro",
11841
+ contextWindow: GEMINI_PRO_CONTEXT_WINDOW
11842
+ },
11843
+ {
11844
+ id: "gemini-2.5-flash",
11845
+ label: "Gemini 2.5 Flash",
11846
+ contextWindow: GEMINI_FLASH_CONTEXT_WINDOW
11847
+ },
11848
+ {
11849
+ id: "gemini-2.5-flash-lite",
11850
+ label: "Gemini 2.5 Flash-Lite",
11851
+ contextWindow: GEMINI_FLASH_CONTEXT_WINDOW
11852
+ }
11853
+ ];
11854
+ var GeminiRuntimeStrategy = class {
11855
+ id = "gemini";
11856
+ meta = getAgent("gemini");
11857
+ mode = "interactive";
11858
+ os;
11859
+ constructor(os28) {
11860
+ this.os = os28;
11861
+ }
11862
+ async prepareLaunch() {
11863
+ const binary = this.os.findInPath("gemini");
11864
+ if (!binary) {
11865
+ throw new Error(
11866
+ "Gemini CLI is not on PATH. Install it with:\n npm install -g @google/gemini-cli\n Then run `codeam pair` again.\n\n Tip: set CODEAM_ACP_ENABLED=1 before pairing to use the\n ACP runtime \u2014 it gives mobile typed messages instead of\n raw PTY output for Gemini."
11867
+ );
11868
+ }
11869
+ return this.os.buildLaunch(binary);
11870
+ }
11871
+ // Gemini's REPL has no documented "resume previous session" flag,
11872
+ // so a relaunch starts fresh. Returning an empty array is the
11873
+ // documented "no-op resume" path (Cursor / Aider do the same).
11874
+ resumeLaunchArgs(_sessionId, _opts) {
11875
+ return [];
11876
+ }
11877
+ resolveHistoryDir(_cwd) {
11878
+ return null;
11879
+ }
11880
+ parseHistoryFile(_filePath) {
11881
+ return [];
11882
+ }
11883
+ getCurrentUsage(_historyDir) {
11884
+ return null;
11885
+ }
11886
+ async fetchWeeklyUsage() {
11887
+ return null;
11888
+ }
11889
+ async listModels() {
11890
+ return GEMINI_MODELS;
11891
+ }
11892
+ /**
11893
+ * Gemini's `/model <id>` swaps the active model inside the REPL —
11894
+ * same shape as Claude / Codex / Cursor.
11895
+ */
11896
+ changeModelInstruction(modelId) {
11897
+ return { type: "pty", ptyInput: `/model ${modelId}\r` };
11898
+ }
11899
+ /**
11900
+ * `/compress` is Gemini's context-compression slash command (the
11901
+ * closest analogue to Claude's `/compact`). No auto-mode equivalent.
11902
+ */
11903
+ summarizeInstruction(_mode) {
11904
+ return { ptyInput: "/compress\r" };
11905
+ }
11906
+ /**
11907
+ * Pass-through filter. Gemini's TUI chrome isn't worth
11908
+ * hand-detecting — users who want clean output should use ACP
11909
+ * (`CODEAM_ACP_ENABLED=1`) instead. Returning the input verbatim
11910
+ * satisfies the contract's idempotency assertion and keeps mobile
11911
+ * showing whatever the REPL prints.
11912
+ */
11913
+ filterTuiOutput(lines) {
11914
+ return lines;
11915
+ }
11916
+ /**
11917
+ * No interactive-selector detection — Gemini's REPL uses input
11918
+ * lines, not arrow-key menus we'd need to detect. ACP surfaces
11919
+ * permission requests as typed messages, which is the path we want
11920
+ * users on anyway.
11921
+ */
11922
+ detectInteractivePrompt(_lines) {
11923
+ return null;
11924
+ }
11925
+ credentialLocator() {
11926
+ return geminiCredentialLocator();
11927
+ }
11928
+ loginLauncher() {
11929
+ return geminiLoginLauncher();
11930
+ }
11931
+ };
11932
+
11753
11933
  // src/agents/registry.ts
11754
11934
  var runtimeBuilders = {
11755
- claude: (os27) => new ClaudeRuntimeStrategy(os27),
11756
- codex: (os27) => new CodexRuntimeStrategy(os27),
11757
- coderabbit: (os27) => new CoderabbitRuntimeStrategy(os27),
11758
- cursor: (os27) => new CursorRuntimeStrategy(os27),
11759
- aider: (os27) => new AiderRuntimeStrategy(os27)
11935
+ claude: (os28) => new ClaudeRuntimeStrategy(os28),
11936
+ codex: (os28) => new CodexRuntimeStrategy(os28),
11937
+ coderabbit: (os28) => new CoderabbitRuntimeStrategy(os28),
11938
+ cursor: (os28) => new CursorRuntimeStrategy(os28),
11939
+ aider: (os28) => new AiderRuntimeStrategy(os28),
11940
+ gemini: (os28) => new GeminiRuntimeStrategy(os28)
11760
11941
  };
11761
11942
  var deployBuilders = {
11762
11943
  claude: () => new ClaudeDeployStrategy(),
11763
11944
  codex: () => new CodexDeployStrategy()
11764
11945
  };
11765
- function createAgentStrategy(agent, os27 = createOsStrategy()) {
11946
+ function createAgentStrategy(agent, os28 = createOsStrategy()) {
11766
11947
  if (!AGENT_REGISTRY[agent]?.enabled) {
11767
11948
  throw new Error(
11768
11949
  `Agent "${agent}" is not supported in this codeam-cli version. Upgrade with 'npm i -g codeam-cli@latest'.`
@@ -11772,10 +11953,10 @@ function createAgentStrategy(agent, os27 = createOsStrategy()) {
11772
11953
  if (!build) {
11773
11954
  throw new Error(`No runtime strategy registered for agent "${agent}"`);
11774
11955
  }
11775
- return build(os27);
11956
+ return build(os28);
11776
11957
  }
11777
- function createInteractiveAgentStrategy(agent, os27 = createOsStrategy()) {
11778
- const s = createAgentStrategy(agent, os27);
11958
+ function createInteractiveAgentStrategy(agent, os28 = createOsStrategy()) {
11959
+ const s = createAgentStrategy(agent, os28);
11779
11960
  if (s.mode !== "interactive") {
11780
11961
  throw new Error(
11781
11962
  `Agent "${agent}" is a batch agent; use createAgentStrategy + .runOneShot for one-shot reviews.`
@@ -11795,87 +11976,762 @@ function createDeployStrategy(agent) {
11795
11976
  return build();
11796
11977
  }
11797
11978
 
11798
- // src/services/output/chrome-tracker.ts
11799
- var ChromeStepTracker = class {
11800
- history = [];
11801
- sentCount = 0;
11802
- reset() {
11803
- this.history = [];
11804
- this.sentCount = 0;
11979
+ // src/agents/acp/adapters.ts
11980
+ var path25 = __toESM(require("path"));
11981
+ var require_ = require;
11982
+ function resolveBin(pkgName, binName) {
11983
+ try {
11984
+ const manifestPath = require_.resolve(`${pkgName}/package.json`);
11985
+ const manifest = require_(`${pkgName}/package.json`);
11986
+ const pkgDir = path25.dirname(manifestPath);
11987
+ const bin = manifest.bin;
11988
+ if (!bin) return null;
11989
+ if (typeof bin === "string") return path25.resolve(pkgDir, bin);
11990
+ const target = binName ?? Object.keys(bin)[0];
11991
+ if (!target || !bin[target]) return null;
11992
+ return path25.resolve(pkgDir, bin[target]);
11993
+ } catch {
11994
+ return null;
11995
+ }
11996
+ }
11997
+ var REGISTRY = {
11998
+ claude: () => {
11999
+ const bin = resolveBin("@agentclientprotocol/claude-agent-acp", "claude-agent-acp");
12000
+ if (!bin) return null;
12001
+ return {
12002
+ command: process.execPath,
12003
+ args: [bin],
12004
+ requiresAgentBinary: "claude"
12005
+ };
12006
+ },
12007
+ codex: () => {
12008
+ const bin = resolveBin("@agentclientprotocol/codex-acp", "codex-acp");
12009
+ if (!bin) return null;
12010
+ return {
12011
+ command: process.execPath,
12012
+ args: [bin],
12013
+ requiresAgentBinary: "codex"
12014
+ };
12015
+ },
12016
+ cursor: () => {
12017
+ const bin = resolveBin("cursor-agent-acp", "cursor-agent-acp");
12018
+ if (!bin) return null;
12019
+ return {
12020
+ command: process.execPath,
12021
+ args: [bin],
12022
+ requiresAgentBinary: "cursor-agent"
12023
+ };
12024
+ },
12025
+ // Gemini speaks ACP natively via `gemini --acp` — no npm adapter
12026
+ // package, just the user-installed `gemini` binary on PATH. Same
12027
+ // {@link AdapterSpec} shape; the only difference is `command` is
12028
+ // resolved from PATH at spawn time instead of being absolute.
12029
+ gemini: () => ({
12030
+ command: "gemini",
12031
+ args: ["--acp"],
12032
+ requiresAgentBinary: "gemini"
12033
+ })
12034
+ };
12035
+ function getAcpAdapter(agent) {
12036
+ const factory = REGISTRY[agent];
12037
+ return factory ? factory() : null;
12038
+ }
12039
+
12040
+ // src/agents/acp/runner.ts
12041
+ var import_node_crypto6 = require("crypto");
12042
+
12043
+ // src/agents/acp/client.ts
12044
+ var import_node_child_process11 = require("child_process");
12045
+ var fs21 = __toESM(require("fs/promises"));
12046
+ var import_node_stream = require("stream");
12047
+ var import_sdk = require("@agentclientprotocol/sdk");
12048
+ var PROTOCOL_VERSION2 = 1;
12049
+ var CLIENT_CAPABILITIES = {
12050
+ fs: { readTextFile: true, writeTextFile: true },
12051
+ terminal: false
12052
+ };
12053
+ var AcpClient = class {
12054
+ constructor(opts) {
12055
+ this.opts = opts;
12056
+ }
12057
+ opts;
12058
+ child = null;
12059
+ connection = null;
12060
+ stopping = false;
12061
+ sessionId = null;
12062
+ /**
12063
+ * Spawn the adapter + perform the initial handshake (initialize
12064
+ * → newSession). Returns the ACP-assigned sessionId so the
12065
+ * caller can route subsequent prompts.
12066
+ */
12067
+ async start() {
12068
+ if (this.child) throw new Error("AcpClient already started");
12069
+ const { adapter, cwd } = this.opts;
12070
+ const child = (0, import_node_child_process11.spawn)(adapter.command, adapter.args, {
12071
+ cwd,
12072
+ env: process.env,
12073
+ stdio: ["pipe", "pipe", "pipe"]
12074
+ });
12075
+ this.child = child;
12076
+ child.stderr?.setEncoding("utf8");
12077
+ child.stderr?.on("data", (chunk2) => {
12078
+ for (const line of chunk2.split(/\r?\n/)) {
12079
+ const trimmed = line.trim();
12080
+ if (trimmed) this.opts.onStderr?.(trimmed);
12081
+ }
12082
+ });
12083
+ child.on("exit", (code, signal) => {
12084
+ if (this.stopping) return;
12085
+ log.warn("acpClient", `adapter exited unexpectedly code=${code} signal=${signal}`);
12086
+ this.opts.onUnexpectedExit?.(code, signal);
12087
+ });
12088
+ if (!child.stdin || !child.stdout) {
12089
+ throw new Error("Spawned ACP adapter is missing stdio handles");
12090
+ }
12091
+ const input = import_node_stream.Readable.toWeb(child.stdout);
12092
+ const output = import_node_stream.Writable.toWeb(child.stdin);
12093
+ const stream = (0, import_sdk.ndJsonStream)(output, input);
12094
+ this.connection = new import_sdk.ClientSideConnection(
12095
+ (_agent) => this.buildClient(),
12096
+ stream
12097
+ );
12098
+ const initialize = await this.connection.initialize({
12099
+ protocolVersion: PROTOCOL_VERSION2,
12100
+ clientCapabilities: CLIENT_CAPABILITIES
12101
+ });
12102
+ const newSession = await this.connection.newSession({
12103
+ cwd,
12104
+ mcpServers: []
12105
+ });
12106
+ this.sessionId = newSession.sessionId;
12107
+ return { sessionId: newSession.sessionId, initialize };
11805
12108
  }
11806
12109
  /**
11807
- * Parse the rendered lines using the supplied per-agent parser,
11808
- * appending unseen steps to the cumulative history.
11809
- *
11810
- * @param lines Screen lines from `renderToLines`.
11811
- * @param parseLine Per-agent chrome parser — `runtime.parseTuiChrome`
11812
- * bound to the active strategy, or a no-op `() => null`
11813
- * when the agent doesn't surface tool-call chrome.
12110
+ * Send a user prompt to the active session. Returns the
12111
+ * {@link PromptResponse} which carries the agent's stop reason
12112
+ * once the turn finishes. Session/update notifications keep
12113
+ * arriving on `onSessionUpdate` while the turn streams.
11814
12114
  */
11815
- ingest(lines, parseLine2) {
11816
- const visible = lines.map((l) => parseLine2(l)).filter((s) => s !== null);
11817
- if (visible.length === 0) return;
11818
- for (const step of visible) {
11819
- const exists = this.history.some(
11820
- (s) => s.tool === step.tool && s.label === step.label
11821
- );
11822
- if (!exists) this.history.push(step);
12115
+ async prompt(text) {
12116
+ if (!this.connection || !this.sessionId) {
12117
+ throw new Error("AcpClient.prompt called before start()");
11823
12118
  }
12119
+ return this.connection.prompt({
12120
+ sessionId: this.sessionId,
12121
+ prompt: [{ type: "text", text }]
12122
+ });
11824
12123
  }
11825
12124
  /**
11826
- * Returns the steps that have NOT yet been shipped on the wire,
11827
- * marking them as shipped. Empty array means nothing new since
11828
- * the last call. Caller forwards this as the chunk's
11829
- * `appendSteps` payload.
12125
+ * Cancel the in-flight prompt turn. Notification no response.
12126
+ * Safe to call when nothing is in flight (the adapter no-ops).
11830
12127
  */
11831
- consumeDelta() {
11832
- if (this.history.length === this.sentCount) return [];
11833
- const delta = this.history.slice(this.sentCount);
11834
- this.sentCount = this.history.length;
11835
- return delta;
12128
+ async cancel() {
12129
+ if (!this.connection || !this.sessionId) return;
12130
+ await this.connection.cancel({ sessionId: this.sessionId });
11836
12131
  }
11837
- /** Snapshot of the cumulative unique-step history (debug + tests). */
11838
- get cumulativeHistory() {
11839
- return this.history;
12132
+ /**
12133
+ * Kill the adapter process and tear down the connection. Safe
12134
+ * to call multiple times. Suppresses the unexpected-exit
12135
+ * callback for this teardown.
12136
+ */
12137
+ async stop() {
12138
+ this.stopping = true;
12139
+ const child = this.child;
12140
+ if (!child) return;
12141
+ this.child = null;
12142
+ this.connection = null;
12143
+ this.sessionId = null;
12144
+ try {
12145
+ child.kill("SIGTERM");
12146
+ const grace = new Promise((resolve6) => {
12147
+ const t2 = setTimeout(() => {
12148
+ try {
12149
+ child.kill("SIGKILL");
12150
+ } catch {
12151
+ }
12152
+ resolve6();
12153
+ }, 2e3);
12154
+ child.once("exit", () => {
12155
+ clearTimeout(t2);
12156
+ resolve6();
12157
+ });
12158
+ });
12159
+ await grace;
12160
+ } catch (err) {
12161
+ log.trace("acpClient", "stop teardown error", err);
12162
+ }
12163
+ }
12164
+ // ─── Client surface (what the agent calls into) ───────────────────
12165
+ buildClient() {
12166
+ return {
12167
+ sessionUpdate: async (params) => {
12168
+ this.opts.onSessionUpdate(params);
12169
+ },
12170
+ requestPermission: (params) => {
12171
+ return this.opts.onRequestPermission(params);
12172
+ },
12173
+ readTextFile: async (params) => {
12174
+ const content = await fs21.readFile(params.path, "utf8");
12175
+ return applyLineRange(content, params.line ?? null, params.limit ?? null);
12176
+ },
12177
+ writeTextFile: async (params) => {
12178
+ await fs21.writeFile(params.path, params.content, "utf8");
12179
+ return {};
12180
+ },
12181
+ // Terminal capability is declared `false` above so adapters
12182
+ // shouldn't call these; provide explicit "not implemented"
12183
+ // stubs so a misbehaving adapter gets a clean error instead
12184
+ // of a hung promise.
12185
+ createTerminal: async () => {
12186
+ throw new Error("terminal capability not implemented in this client (Phase 1)");
12187
+ },
12188
+ terminalOutput: async () => {
12189
+ throw new Error("terminal capability not implemented in this client (Phase 1)");
12190
+ },
12191
+ releaseTerminal: async () => {
12192
+ throw new Error("terminal capability not implemented in this client (Phase 1)");
12193
+ },
12194
+ waitForTerminalExit: async () => {
12195
+ throw new Error("terminal capability not implemented in this client (Phase 1)");
12196
+ },
12197
+ killTerminal: async () => {
12198
+ throw new Error("terminal capability not implemented in this client (Phase 1)");
12199
+ }
12200
+ };
11840
12201
  }
11841
12202
  };
12203
+ function applyLineRange(content, line, limit) {
12204
+ if (line === null && limit === null) return { content };
12205
+ const lines = content.split("\n");
12206
+ const start2 = Math.max(0, (line ?? 1) - 1);
12207
+ const end = limit !== null ? start2 + limit : lines.length;
12208
+ return { content: lines.slice(start2, end).join("\n") };
12209
+ }
11842
12210
 
11843
- // src/services/output/chunk-emitter.ts
11844
- var https3 = __toESM(require("https"));
12211
+ // src/services/streaming/transport.ts
11845
12212
  var http3 = __toESM(require("http"));
11846
- var API_BASE3 = resolveApiBaseUrl();
11847
- async function refreshAuthToken(sessionId, pluginId) {
11848
- try {
11849
- const { statusCode, body } = await _transport2.post(
11850
- `${API_BASE3}/api/pairing/reconnect`,
12213
+ var https3 = __toESM(require("https"));
12214
+ var _transport2 = {
12215
+ post: _post,
12216
+ get: _get
12217
+ };
12218
+ function _post(url, headers, payload) {
12219
+ return new Promise((resolve6, reject) => {
12220
+ let settled = false;
12221
+ const u2 = new URL(url);
12222
+ const lib = u2.protocol === "https:" ? https3 : http3;
12223
+ const req = lib.request(
11851
12224
  {
11852
- "Content-Type": "application/json",
11853
- "X-Codeam-Protocol-Version": PROTOCOL_VERSION,
11854
- ...vercelBypassHeader()
12225
+ hostname: u2.hostname,
12226
+ port: u2.port || (u2.protocol === "https:" ? 443 : 80),
12227
+ path: u2.pathname + u2.search,
12228
+ method: "POST",
12229
+ headers: {
12230
+ ...headers,
12231
+ ...vercelBypassHeader(),
12232
+ "Content-Length": Buffer.byteLength(payload)
12233
+ },
12234
+ timeout: 8e3
11855
12235
  },
11856
- JSON.stringify({ sessionId, pluginId })
12236
+ (res) => {
12237
+ let body = "";
12238
+ res.on("data", (c2) => {
12239
+ body += c2.toString();
12240
+ });
12241
+ res.on("end", () => {
12242
+ if (settled) return;
12243
+ settled = true;
12244
+ resolve6({ statusCode: res.statusCode ?? 0, body });
12245
+ });
12246
+ }
11857
12247
  );
11858
- if (statusCode === 404) {
11859
- log.warn("chunkEmitter", "[auth] reconnect 404 \u2014 session gone server-side");
11860
- return null;
11861
- }
11862
- if (statusCode >= 400) {
11863
- log.warn("chunkEmitter", `[auth] reconnect failed status=${statusCode}`);
11864
- return null;
11865
- }
11866
- const parsed = JSON.parse(body);
11867
- const fresh = parsed.data?.pluginAuthToken;
11868
- if (typeof fresh !== "string" || fresh.length === 0) {
11869
- log.warn("chunkEmitter", "[auth] reconnect response missing pluginAuthToken");
11870
- return null;
11871
- }
11872
- return fresh;
11873
- } catch (err) {
11874
- log.warn("chunkEmitter", `[auth] reconnect threw: ${String(err)}`);
11875
- return null;
11876
- }
11877
- }
11878
- var ChunkEmitter = class {
12248
+ req.on("error", (err) => {
12249
+ if (settled) return;
12250
+ settled = true;
12251
+ reject(err);
12252
+ });
12253
+ req.on("timeout", () => {
12254
+ req.destroy();
12255
+ });
12256
+ req.write(payload);
12257
+ req.end();
12258
+ });
12259
+ }
12260
+ function _get(url, headers) {
12261
+ return new Promise((resolve6, reject) => {
12262
+ let settled = false;
12263
+ const u2 = new URL(url);
12264
+ const lib = u2.protocol === "https:" ? https3 : http3;
12265
+ const req = lib.request(
12266
+ {
12267
+ hostname: u2.hostname,
12268
+ port: u2.port || (u2.protocol === "https:" ? 443 : 80),
12269
+ path: u2.pathname + u2.search,
12270
+ method: "GET",
12271
+ headers: {
12272
+ ...headers,
12273
+ ...vercelBypassHeader()
12274
+ },
12275
+ timeout: 8e3
12276
+ },
12277
+ (res) => {
12278
+ let body = "";
12279
+ res.on("data", (c2) => {
12280
+ body += c2.toString();
12281
+ });
12282
+ res.on("end", () => {
12283
+ if (settled) return;
12284
+ settled = true;
12285
+ resolve6({ statusCode: res.statusCode ?? 0, body });
12286
+ });
12287
+ }
12288
+ );
12289
+ req.on("error", (err) => {
12290
+ if (settled) return;
12291
+ settled = true;
12292
+ reject(err);
12293
+ });
12294
+ req.on("timeout", () => {
12295
+ req.destroy();
12296
+ });
12297
+ req.end();
12298
+ });
12299
+ }
12300
+
12301
+ // src/agents/acp/publisher.ts
12302
+ var AcpPublisher = class {
12303
+ constructor(opts) {
12304
+ this.opts = opts;
12305
+ this.apiBase = opts.apiBaseUrl ?? resolveApiBaseUrl();
12306
+ this.headers = {
12307
+ "Content-Type": "application/json",
12308
+ "X-Codeam-Protocol-Version": "2.0.0",
12309
+ "X-Plugin-Auth-Token": opts.pluginAuthToken
12310
+ };
12311
+ }
12312
+ opts;
12313
+ apiBase;
12314
+ headers;
12315
+ /**
12316
+ * Fire-and-forget chunk POST. The backend's per-user SSE bus
12317
+ * forwards each chunk to mobile/landing within ~20 ms (PRO) /
12318
+ * ~80 ms (FREE). Errors are logged but never thrown — a missed
12319
+ * chunk shouldn't bring down the whole session.
12320
+ */
12321
+ async publishChunk(event) {
12322
+ const url = `${this.apiBase}/api/sessions/${encodeURIComponent(this.opts.sessionId)}/streaming-chunk`;
12323
+ try {
12324
+ const { statusCode, body } = await _transport2.post(
12325
+ url,
12326
+ this.headers,
12327
+ JSON.stringify(event)
12328
+ );
12329
+ if (statusCode < 200 || statusCode >= 300) {
12330
+ log.warn("acpPublisher", `chunk status=${statusCode} body=${body.slice(0, 200)}`);
12331
+ }
12332
+ } catch (err) {
12333
+ log.trace("acpPublisher", "chunk post failed", err);
12334
+ }
12335
+ }
12336
+ /**
12337
+ * Publish an awaiting-answer event so the mobile renders the
12338
+ * pending-prompt sheet. The CLI follows up with
12339
+ * {@link pollPendingAnswer} until the user replies (or the
12340
+ * 5 min Redis TTL expires upstream).
12341
+ */
12342
+ async publishAwaitingAnswer(event) {
12343
+ const url = `${this.apiBase}/api/sessions/${encodeURIComponent(this.opts.sessionId)}/awaiting-answer`;
12344
+ try {
12345
+ const { statusCode, body } = await _transport2.post(
12346
+ url,
12347
+ this.headers,
12348
+ JSON.stringify(event)
12349
+ );
12350
+ if (statusCode < 200 || statusCode >= 300) {
12351
+ log.warn("acpPublisher", `awaiting-answer status=${statusCode} body=${body.slice(0, 200)}`);
12352
+ }
12353
+ } catch (err) {
12354
+ log.trace("acpPublisher", "awaiting-answer post failed", err);
12355
+ }
12356
+ }
12357
+ /**
12358
+ * Drain the pending-answer endpoint. Returns the resolved answer
12359
+ * when the user has replied, `null` otherwise. The caller polls
12360
+ * this on a 1.5 s cadence (same as the legacy emitter) until a
12361
+ * non-null result lands.
12362
+ */
12363
+ async pollPendingAnswer(questionId) {
12364
+ const url = `${this.apiBase}/api/sessions/${encodeURIComponent(this.opts.sessionId)}/pending-answer?questionId=${encodeURIComponent(questionId)}&pluginId=${encodeURIComponent(this.opts.pluginId)}`;
12365
+ try {
12366
+ const { statusCode, body } = await _transport2.get(url, this.headers);
12367
+ if (statusCode === 204 || statusCode === 404) return null;
12368
+ if (statusCode < 200 || statusCode >= 300) {
12369
+ log.warn("acpPublisher", `pending-answer status=${statusCode} body=${body.slice(0, 200)}`);
12370
+ return null;
12371
+ }
12372
+ return parsePendingAnswerResponse(body, questionId);
12373
+ } catch (err) {
12374
+ log.trace("acpPublisher", "pending-answer poll failed", err);
12375
+ return null;
12376
+ }
12377
+ }
12378
+ };
12379
+ function parsePendingAnswerResponse(body, questionId) {
12380
+ if (!body) return null;
12381
+ let parsed;
12382
+ try {
12383
+ parsed = JSON.parse(body);
12384
+ } catch {
12385
+ return null;
12386
+ }
12387
+ if (typeof parsed !== "object" || parsed === null) return null;
12388
+ const root = parsed;
12389
+ const candidate = root.data && typeof root.data === "object" ? root.data : root;
12390
+ const qid = candidate.questionId;
12391
+ const answer = candidate.answer;
12392
+ const optionIndex = candidate.optionIndex;
12393
+ if (typeof qid !== "string" || qid !== questionId) return null;
12394
+ if (typeof answer !== "string") return null;
12395
+ const result = { questionId: qid, answer };
12396
+ if (typeof optionIndex === "number" && Number.isInteger(optionIndex)) {
12397
+ result.optionIndex = optionIndex;
12398
+ }
12399
+ return result;
12400
+ }
12401
+
12402
+ // src/agents/acp/mappers.ts
12403
+ var import_node_crypto5 = require("crypto");
12404
+ function mapSessionUpdate(notification) {
12405
+ const update = notification.update;
12406
+ switch (update.sessionUpdate) {
12407
+ case "agent_message_chunk": {
12408
+ const text = extractText2(update.content);
12409
+ if (!text) return [];
12410
+ return [chunk(messageChunkId(update.messageId), "text", text)];
12411
+ }
12412
+ case "agent_thought_chunk": {
12413
+ const text = extractText2(update.content);
12414
+ if (!text) return [];
12415
+ return [chunk(messageChunkId(update.messageId), "thinking", text)];
12416
+ }
12417
+ case "tool_call": {
12418
+ const summary = describeToolCall(update);
12419
+ if (!summary) return [];
12420
+ return [chunk(update.toolCallId, "tool_use", summary)];
12421
+ }
12422
+ case "tool_call_update": {
12423
+ if (update.status !== "completed" && update.status !== "failed") {
12424
+ return [];
12425
+ }
12426
+ const body = describeToolCallUpdate(update);
12427
+ if (!body) return [];
12428
+ const prefix = update.status === "failed" ? "[failed] " : "";
12429
+ return [chunk(update.toolCallId, "tool_result", prefix + body)];
12430
+ }
12431
+ case "user_message_chunk":
12432
+ return [];
12433
+ case "plan":
12434
+ case "plan_update":
12435
+ case "plan_removed":
12436
+ case "available_commands_update":
12437
+ case "current_mode_update":
12438
+ case "config_option_update":
12439
+ case "session_info_update":
12440
+ case "usage_update":
12441
+ return [];
12442
+ default:
12443
+ return [];
12444
+ }
12445
+ }
12446
+ function mapPermissionRequest(request) {
12447
+ const prompt = describeToolCall(request.toolCall) ?? "The agent requested permission to continue.";
12448
+ const optionIdByLabel = {};
12449
+ const kindByLabel = {};
12450
+ const labels = [];
12451
+ for (const opt of request.options) {
12452
+ const label = opt.name?.trim() || humanizeKind(opt.kind);
12453
+ if (label in optionIdByLabel) continue;
12454
+ optionIdByLabel[label] = opt.optionId;
12455
+ kindByLabel[label] = opt.kind;
12456
+ labels.push(label);
12457
+ }
12458
+ return {
12459
+ event: {
12460
+ questionId: (0, import_node_crypto5.randomUUID)(),
12461
+ prompt,
12462
+ options: labels.length > 0 ? labels : void 0
12463
+ },
12464
+ optionIdByLabel,
12465
+ kindByLabel
12466
+ };
12467
+ }
12468
+ function chunk(chunkId, kind, content) {
12469
+ return { chunkId, kind, content, isFinal: true };
12470
+ }
12471
+ function messageChunkId(messageId) {
12472
+ if (typeof messageId === "string" && messageId.length > 0) return messageId;
12473
+ return (0, import_node_crypto5.randomUUID)();
12474
+ }
12475
+ function extractText2(content) {
12476
+ if (!content || typeof content !== "object") return null;
12477
+ if ("type" in content && content.type === "text") {
12478
+ const t2 = content.text;
12479
+ return typeof t2 === "string" && t2.length > 0 ? t2 : null;
12480
+ }
12481
+ return null;
12482
+ }
12483
+ function describeToolCall(call) {
12484
+ const title = call.title?.trim();
12485
+ const kind = call.kind?.trim();
12486
+ if (title && title.length > 0) return title;
12487
+ if (kind && kind.length > 0) return kind;
12488
+ if (call.rawInput && typeof call.rawInput === "object") {
12489
+ try {
12490
+ const summary = JSON.stringify(call.rawInput);
12491
+ if (summary.length > 240) return `${summary.slice(0, 240)}\u2026`;
12492
+ return summary;
12493
+ } catch {
12494
+ return null;
12495
+ }
12496
+ }
12497
+ return null;
12498
+ }
12499
+ function describeToolCallUpdate(update) {
12500
+ const parts = [];
12501
+ if (Array.isArray(update.content)) {
12502
+ for (const item of update.content) {
12503
+ if (!item || typeof item !== "object") continue;
12504
+ if (item.type === "content" && item.content) {
12505
+ const text = extractText2(item.content);
12506
+ if (text) parts.push(text);
12507
+ } else if (item.type === "diff") {
12508
+ const p2 = item.path;
12509
+ parts.push(p2 ? `diff: ${p2}` : "diff");
12510
+ } else if (item.type === "terminal") {
12511
+ const id = item.terminalId;
12512
+ parts.push(id ? `terminal: ${id}` : "terminal");
12513
+ }
12514
+ }
12515
+ }
12516
+ if (parts.length > 0) return parts.join("\n");
12517
+ const title = update.title?.trim();
12518
+ return title && title.length > 0 ? title : null;
12519
+ }
12520
+ function humanizeKind(kind) {
12521
+ switch (kind) {
12522
+ case "allow_once":
12523
+ return "Allow once";
12524
+ case "allow_always":
12525
+ return "Always allow";
12526
+ case "reject_once":
12527
+ return "Reject";
12528
+ case "reject_always":
12529
+ return "Always reject";
12530
+ default:
12531
+ return kind;
12532
+ }
12533
+ }
12534
+
12535
+ // src/agents/acp/runner.ts
12536
+ var ANSWER_POLL_MS = 1500;
12537
+ var PERMISSION_TIMEOUT_MS = 5 * 60 * 1e3;
12538
+ async function runAcpSession(opts) {
12539
+ const publisher = new AcpPublisher({
12540
+ sessionId: opts.sessionId,
12541
+ pluginId: opts.pluginId,
12542
+ pluginAuthToken: opts.pluginAuthToken
12543
+ });
12544
+ const client2 = new AcpClient({
12545
+ adapter: opts.adapter,
12546
+ cwd: opts.cwd,
12547
+ onSessionUpdate: (notification) => {
12548
+ const chunks = mapSessionUpdate(notification);
12549
+ for (const chunk2 of chunks) {
12550
+ void publisher.publishChunk(chunk2);
12551
+ }
12552
+ },
12553
+ onRequestPermission: async (request) => {
12554
+ const { event, optionIdByLabel } = mapPermissionRequest(request);
12555
+ await publisher.publishAwaitingAnswer(event);
12556
+ const answer = await waitForAnswer(publisher, event.questionId);
12557
+ if (!answer) {
12558
+ return { outcome: { outcome: "cancelled" } };
12559
+ }
12560
+ const optionId = optionIdByLabel[answer.answer];
12561
+ if (!optionId) {
12562
+ log.warn(
12563
+ "acpRunner",
12564
+ `pending-answer label not in option map; reply="${answer.answer.slice(0, 80)}"`
12565
+ );
12566
+ return { outcome: { outcome: "cancelled" } };
12567
+ }
12568
+ return { outcome: { outcome: "selected", optionId } };
12569
+ },
12570
+ onStderr: (line) => {
12571
+ log.trace("acpAdapter", line);
12572
+ },
12573
+ onUnexpectedExit: (code, signal) => {
12574
+ log.warn("acpRunner", `adapter died code=${code} signal=${signal}; shutting down session`);
12575
+ void publisher.publishChunk({
12576
+ chunkId: (0, import_node_crypto6.randomUUID)(),
12577
+ kind: "text",
12578
+ content: `Agent adapter exited unexpectedly (code=${code ?? "null"} signal=${signal ?? "null"}).`,
12579
+ isFinal: true
12580
+ });
12581
+ process.exit(1);
12582
+ }
12583
+ });
12584
+ showInfo(`Starting ${opts.agent} via ACP adapter (${opts.adapter.requiresAgentBinary})\u2026`);
12585
+ const { sessionId: acpSessionId, initialize } = await client2.start();
12586
+ log.trace(
12587
+ "acpRunner",
12588
+ `adapter handshake ok protocolVersion=${initialize.protocolVersion} sessionId=${acpSessionId.slice(0, 8)}`
12589
+ );
12590
+ const relay = new CommandRelayService(
12591
+ opts.pluginId,
12592
+ async (cmd) => {
12593
+ await handleCommand(cmd, client2);
12594
+ },
12595
+ { id: opts.agent, name: opts.agent, displayName: opts.agent }
12596
+ );
12597
+ relay.start();
12598
+ const shutdown = async (signal) => {
12599
+ showInfo(`Shutting down ACP session (${signal})\u2026`);
12600
+ relay.stop();
12601
+ await client2.stop();
12602
+ process.exit(0);
12603
+ };
12604
+ process.once("SIGINT", () => void shutdown("SIGINT"));
12605
+ process.once("SIGTERM", () => void shutdown("SIGTERM"));
12606
+ process.once("SIGHUP", () => void shutdown("SIGHUP"));
12607
+ await new Promise(() => {
12608
+ });
12609
+ }
12610
+ async function handleCommand(cmd, client2) {
12611
+ switch (cmd.type) {
12612
+ case "start_task": {
12613
+ const payload = cmd.payload;
12614
+ const prompt = payload?.prompt?.trim();
12615
+ if (!prompt) {
12616
+ log.warn("acpRunner", "start_task with empty prompt; ignoring");
12617
+ return;
12618
+ }
12619
+ try {
12620
+ await client2.prompt(prompt);
12621
+ } catch (err) {
12622
+ log.warn("acpRunner", `prompt failed: ${describeError(err)}`);
12623
+ }
12624
+ return;
12625
+ }
12626
+ case "stop_task":
12627
+ case "escape_key": {
12628
+ try {
12629
+ await client2.cancel();
12630
+ } catch (err) {
12631
+ log.warn("acpRunner", `cancel failed: ${describeError(err)}`);
12632
+ }
12633
+ return;
12634
+ }
12635
+ default:
12636
+ log.trace("acpRunner", `command type "${cmd.type}" not supported in Phase 1 ACP mode`);
12637
+ return;
12638
+ }
12639
+ }
12640
+ async function waitForAnswer(publisher, questionId) {
12641
+ const deadline = Date.now() + PERMISSION_TIMEOUT_MS;
12642
+ while (Date.now() < deadline) {
12643
+ const reply = await publisher.pollPendingAnswer(questionId);
12644
+ if (reply) return { answer: reply.answer };
12645
+ await new Promise((r) => setTimeout(r, ANSWER_POLL_MS));
12646
+ }
12647
+ return null;
12648
+ }
12649
+ function describeError(err) {
12650
+ if (err instanceof Error) return err.message;
12651
+ return String(err);
12652
+ }
12653
+
12654
+ // src/services/output/chrome-tracker.ts
12655
+ var ChromeStepTracker = class {
12656
+ history = [];
12657
+ sentCount = 0;
12658
+ reset() {
12659
+ this.history = [];
12660
+ this.sentCount = 0;
12661
+ }
12662
+ /**
12663
+ * Parse the rendered lines using the supplied per-agent parser,
12664
+ * appending unseen steps to the cumulative history.
12665
+ *
12666
+ * @param lines Screen lines from `renderToLines`.
12667
+ * @param parseLine Per-agent chrome parser — `runtime.parseTuiChrome`
12668
+ * bound to the active strategy, or a no-op `() => null`
12669
+ * when the agent doesn't surface tool-call chrome.
12670
+ */
12671
+ ingest(lines, parseLine2) {
12672
+ const visible = lines.map((l) => parseLine2(l)).filter((s) => s !== null);
12673
+ if (visible.length === 0) return;
12674
+ for (const step of visible) {
12675
+ const exists = this.history.some(
12676
+ (s) => s.tool === step.tool && s.label === step.label
12677
+ );
12678
+ if (!exists) this.history.push(step);
12679
+ }
12680
+ }
12681
+ /**
12682
+ * Returns the steps that have NOT yet been shipped on the wire,
12683
+ * marking them as shipped. Empty array means nothing new since
12684
+ * the last call. Caller forwards this as the chunk's
12685
+ * `appendSteps` payload.
12686
+ */
12687
+ consumeDelta() {
12688
+ if (this.history.length === this.sentCount) return [];
12689
+ const delta = this.history.slice(this.sentCount);
12690
+ this.sentCount = this.history.length;
12691
+ return delta;
12692
+ }
12693
+ /** Snapshot of the cumulative unique-step history (debug + tests). */
12694
+ get cumulativeHistory() {
12695
+ return this.history;
12696
+ }
12697
+ };
12698
+
12699
+ // src/services/output/chunk-emitter.ts
12700
+ var https4 = __toESM(require("https"));
12701
+ var http4 = __toESM(require("http"));
12702
+ var API_BASE3 = resolveApiBaseUrl();
12703
+ async function refreshAuthToken(sessionId, pluginId) {
12704
+ try {
12705
+ const { statusCode, body } = await _transport3.post(
12706
+ `${API_BASE3}/api/pairing/reconnect`,
12707
+ {
12708
+ "Content-Type": "application/json",
12709
+ "X-Codeam-Protocol-Version": PROTOCOL_VERSION,
12710
+ ...vercelBypassHeader()
12711
+ },
12712
+ JSON.stringify({ sessionId, pluginId })
12713
+ );
12714
+ if (statusCode === 404) {
12715
+ log.warn("chunkEmitter", "[auth] reconnect 404 \u2014 session gone server-side");
12716
+ return null;
12717
+ }
12718
+ if (statusCode >= 400) {
12719
+ log.warn("chunkEmitter", `[auth] reconnect failed status=${statusCode}`);
12720
+ return null;
12721
+ }
12722
+ const parsed = JSON.parse(body);
12723
+ const fresh = parsed.data?.pluginAuthToken;
12724
+ if (typeof fresh !== "string" || fresh.length === 0) {
12725
+ log.warn("chunkEmitter", "[auth] reconnect response missing pluginAuthToken");
12726
+ return null;
12727
+ }
12728
+ return fresh;
12729
+ } catch (err) {
12730
+ log.warn("chunkEmitter", `[auth] reconnect threw: ${String(err)}`);
12731
+ return null;
12732
+ }
12733
+ }
12734
+ var ChunkEmitter = class {
11879
12735
  constructor(opts) {
11880
12736
  this.opts = opts;
11881
12737
  this.headers = {
@@ -11909,14 +12765,14 @@ var ChunkEmitter = class {
11909
12765
  "chunkEmitter",
11910
12766
  `send type=${body.type ?? "(clear)"} bytes=${payload.length} done=${body.done === true}`
11911
12767
  );
11912
- return new Promise((resolve5) => {
12768
+ return new Promise((resolve6) => {
11913
12769
  const attempt = (attemptsLeft) => {
11914
- _transport2.post(this.url, this.headers, payload).then(({ statusCode, body: resBody }) => {
12770
+ _transport3.post(this.url, this.headers, payload).then(({ statusCode, body: resBody }) => {
11915
12771
  const tookMs = Date.now() - t0;
11916
12772
  if (statusCode === 410 || statusCode === 404 && /SESSION_NOT_FOUND|SESSION_GONE/.test(resBody)) {
11917
12773
  process.stderr.write("[codeam] session was deleted/disconnected \u2014 stopping output stream.\n");
11918
12774
  log.info("chunkEmitter", `dead status=${statusCode} took=${tookMs}ms`);
11919
- resolve5({ dead: true });
12775
+ resolve6({ dead: true });
11920
12776
  return;
11921
12777
  }
11922
12778
  if (statusCode === 401) {
@@ -11932,7 +12788,7 @@ var ChunkEmitter = class {
11932
12788
  return;
11933
12789
  }
11934
12790
  }
11935
- resolve5({ dead: false });
12791
+ resolve6({ dead: false });
11936
12792
  })();
11937
12793
  return;
11938
12794
  }
@@ -11943,7 +12799,7 @@ var ChunkEmitter = class {
11943
12799
  } else {
11944
12800
  log.info("chunkEmitter", `ok status=${statusCode} took=${tookMs}ms`);
11945
12801
  }
11946
- resolve5({ dead: false });
12802
+ resolve6({ dead: false });
11947
12803
  }).catch((err) => {
11948
12804
  log.warn(
11949
12805
  "chunkEmitter",
@@ -11954,7 +12810,7 @@ var ChunkEmitter = class {
11954
12810
  const delay = 200 * (maxRetries - attemptsLeft + 1);
11955
12811
  setTimeout(() => attempt(attemptsLeft - 1), delay);
11956
12812
  } else {
11957
- resolve5({ dead: false });
12813
+ resolve6({ dead: false });
11958
12814
  }
11959
12815
  });
11960
12816
  };
@@ -11962,14 +12818,14 @@ var ChunkEmitter = class {
11962
12818
  });
11963
12819
  }
11964
12820
  };
11965
- var _transport2 = {
11966
- post: _post
12821
+ var _transport3 = {
12822
+ post: _post2
11967
12823
  };
11968
- function _post(url, headers, payload) {
11969
- return new Promise((resolve5, reject) => {
12824
+ function _post2(url, headers, payload) {
12825
+ return new Promise((resolve6, reject) => {
11970
12826
  let settled = false;
11971
12827
  const u2 = new URL(url);
11972
- const transport = u2.protocol === "https:" ? https3 : http3;
12828
+ const transport = u2.protocol === "https:" ? https4 : http4;
11973
12829
  const req = transport.request(
11974
12830
  {
11975
12831
  hostname: u2.hostname,
@@ -11990,7 +12846,7 @@ function _post(url, headers, payload) {
11990
12846
  res.on("end", () => {
11991
12847
  if (settled) return;
11992
12848
  settled = true;
11993
- resolve5({ statusCode: res.statusCode ?? 0, body: resData });
12849
+ resolve6({ statusCode: res.statusCode ?? 0, body: resData });
11994
12850
  });
11995
12851
  }
11996
12852
  );
@@ -12564,11 +13420,11 @@ var OutputService = class _OutputService {
12564
13420
  };
12565
13421
 
12566
13422
  // src/services/history.service.ts
12567
- var fs20 = __toESM(require("fs"));
12568
- var path24 = __toESM(require("path"));
12569
- var os21 = __toESM(require("os"));
12570
- var https4 = __toESM(require("https"));
12571
- var http4 = __toESM(require("http"));
13423
+ var fs22 = __toESM(require("fs"));
13424
+ var path26 = __toESM(require("path"));
13425
+ var os22 = __toESM(require("os"));
13426
+ var https5 = __toESM(require("https"));
13427
+ var http5 = __toESM(require("http"));
12572
13428
  var import_zod = require("zod");
12573
13429
  var historyRecordSchema = import_zod.z.object({
12574
13430
  type: import_zod.z.string().optional(),
@@ -12581,7 +13437,7 @@ var historyRecordSchema = import_zod.z.object({
12581
13437
  }).passthrough().optional()
12582
13438
  }).passthrough();
12583
13439
  var API_BASE4 = resolveApiBaseUrl();
12584
- function extractText2(content) {
13440
+ function extractText3(content) {
12585
13441
  if (typeof content === "string") return content;
12586
13442
  if (Array.isArray(content)) {
12587
13443
  return content.filter((b) => b["type"] === "text").map((b) => b["text"]).join("\n");
@@ -12593,7 +13449,7 @@ function parseJsonl(filePath) {
12593
13449
  const messages = [];
12594
13450
  let raw;
12595
13451
  try {
12596
- raw = fs20.readFileSync(filePath, "utf8");
13452
+ raw = fs22.readFileSync(filePath, "utf8");
12597
13453
  } catch (err) {
12598
13454
  if (err.code !== "ENOENT") {
12599
13455
  log.warn("history:parseJsonl", `read failed for ${filePath}`, err);
@@ -12620,20 +13476,20 @@ function parseJsonl(filePath) {
12620
13476
  const uuid = record.uuid ?? `${Date.now()}-${Math.random()}`;
12621
13477
  const msg = record.message;
12622
13478
  if (record.type === "user" && msg) {
12623
- const text = extractText2(msg.content).trim();
13479
+ const text = extractText3(msg.content).trim();
12624
13480
  if (text) messages.push({ id: uuid, role: "user", text, timestamp });
12625
13481
  } else if (record.type === "assistant" && msg) {
12626
- const text = extractText2(msg.content).trim();
13482
+ const text = extractText3(msg.content).trim();
12627
13483
  if (text) messages.push({ id: uuid, role: "agent", text, timestamp });
12628
13484
  }
12629
13485
  }
12630
13486
  return messages;
12631
13487
  }
12632
13488
  function post(endpoint, body) {
12633
- return new Promise((resolve5) => {
13489
+ return new Promise((resolve6) => {
12634
13490
  const payload = JSON.stringify(body);
12635
13491
  const u2 = new URL(`${API_BASE4}${endpoint}`);
12636
- const transport = u2.protocol === "https:" ? https4 : http4;
13492
+ const transport = u2.protocol === "https:" ? https5 : http5;
12637
13493
  const req = transport.request(
12638
13494
  {
12639
13495
  hostname: u2.hostname,
@@ -12651,17 +13507,17 @@ function post(endpoint, body) {
12651
13507
  res.resume();
12652
13508
  const ok = res.statusCode !== void 0 && res.statusCode >= 200 && res.statusCode < 300;
12653
13509
  if (!ok) log.warn("history:post", `${endpoint} \u2192 HTTP ${res.statusCode}`);
12654
- resolve5(ok);
13510
+ resolve6(ok);
12655
13511
  }
12656
13512
  );
12657
13513
  req.on("error", (err) => {
12658
13514
  log.warn("history:post", `${endpoint} network error`, err);
12659
- resolve5(false);
13515
+ resolve6(false);
12660
13516
  });
12661
13517
  req.on("timeout", () => {
12662
13518
  log.warn("history:post", `${endpoint} timeout after 15s`);
12663
13519
  req.destroy();
12664
- resolve5(false);
13520
+ resolve6(false);
12665
13521
  });
12666
13522
  req.write(payload);
12667
13523
  req.end();
@@ -12728,7 +13584,7 @@ var HistoryService = class _HistoryService {
12728
13584
  return this._quotaPercent === null || Date.now() - this._quotaFetchedAt > ttlMs;
12729
13585
  }
12730
13586
  get projectDir() {
12731
- return this.runtime.resolveHistoryDir(this.cwd) ?? path24.join(os21.homedir(), ".claude", "projects", encodeCwd(this.cwd));
13587
+ return this.runtime.resolveHistoryDir(this.cwd) ?? path26.join(os22.homedir(), ".claude", "projects", encodeCwd(this.cwd));
12732
13588
  }
12733
13589
  /** Set the current Claude conversation ID (extracted from /cost command or session start) */
12734
13590
  setCurrentConversationId(id) {
@@ -12740,7 +13596,7 @@ var HistoryService = class _HistoryService {
12740
13596
  /** Return the current message count in the active conversation. */
12741
13597
  getCurrentMessageCount() {
12742
13598
  if (!this.currentConversationId) return 0;
12743
- const filePath = path24.join(this.projectDir, `${this.currentConversationId}.jsonl`);
13599
+ const filePath = path26.join(this.projectDir, `${this.currentConversationId}.jsonl`);
12744
13600
  return parseJsonl(filePath).length;
12745
13601
  }
12746
13602
  /**
@@ -12751,7 +13607,7 @@ var HistoryService = class _HistoryService {
12751
13607
  const deadline = Date.now() + timeoutMs;
12752
13608
  while (Date.now() < deadline) {
12753
13609
  if (!this.currentConversationId) return null;
12754
- const filePath = path24.join(this.projectDir, `${this.currentConversationId}.jsonl`);
13610
+ const filePath = path26.join(this.projectDir, `${this.currentConversationId}.jsonl`);
12755
13611
  const messages = parseJsonl(filePath);
12756
13612
  if (messages.length > previousCount) {
12757
13613
  for (let i = messages.length - 1; i >= previousCount; i--) {
@@ -12777,16 +13633,16 @@ var HistoryService = class _HistoryService {
12777
13633
  const dir = this.projectDir;
12778
13634
  const cutoff = this.bootTimeMs - _HistoryService.BIRTHTIME_GRACE_MS;
12779
13635
  try {
12780
- const files = fs20.readdirSync(dir, { withFileTypes: true }).filter((e) => e.isFile() && e.name.endsWith(".jsonl")).map((e) => {
13636
+ const files = fs22.readdirSync(dir, { withFileTypes: true }).filter((e) => e.isFile() && e.name.endsWith(".jsonl")).map((e) => {
12781
13637
  try {
12782
- const stat3 = fs20.statSync(path24.join(dir, e.name));
13638
+ const stat3 = fs22.statSync(path26.join(dir, e.name));
12783
13639
  return { name: e.name, mtime: stat3.mtimeMs, birthtime: stat3.birthtimeMs };
12784
13640
  } catch {
12785
13641
  return { name: e.name, mtime: 0, birthtime: 0 };
12786
13642
  }
12787
13643
  }).filter((f) => f.birthtime >= cutoff).sort((a, b) => b.mtime - a.mtime);
12788
13644
  if (files.length > 0) {
12789
- this.currentConversationId = path24.basename(files[0].name, ".jsonl");
13645
+ this.currentConversationId = path26.basename(files[0].name, ".jsonl");
12790
13646
  }
12791
13647
  } catch {
12792
13648
  }
@@ -12820,13 +13676,13 @@ var HistoryService = class _HistoryService {
12820
13676
  const cutoff = this.bootTimeMs - _HistoryService.BIRTHTIME_GRACE_MS;
12821
13677
  let entries;
12822
13678
  try {
12823
- entries = fs20.readdirSync(dir, { withFileTypes: true });
13679
+ entries = fs22.readdirSync(dir, { withFileTypes: true });
12824
13680
  } catch {
12825
13681
  return null;
12826
13682
  }
12827
13683
  const files = entries.filter((e) => e.isFile() && e.name.endsWith(".jsonl")).map((e) => {
12828
13684
  try {
12829
- const stat3 = fs20.statSync(path24.join(dir, e.name));
13685
+ const stat3 = fs22.statSync(path26.join(dir, e.name));
12830
13686
  return { name: e.name, mtime: stat3.mtimeMs, birthtime: stat3.birthtimeMs };
12831
13687
  } catch {
12832
13688
  return { name: e.name, mtime: 0, birthtime: 0 };
@@ -12835,12 +13691,12 @@ var HistoryService = class _HistoryService {
12835
13691
  if (files.length === 0) return null;
12836
13692
  const targetFile = this.currentConversationId ? `${this.currentConversationId}.jsonl` : files[0].name;
12837
13693
  if (!files.some((f) => f.name === targetFile)) return null;
12838
- return this.extractUsageFromFile(path24.join(dir, targetFile));
13694
+ return this.extractUsageFromFile(path26.join(dir, targetFile));
12839
13695
  }
12840
13696
  extractUsageFromFile(filePath) {
12841
13697
  let raw;
12842
13698
  try {
12843
- raw = fs20.readFileSync(filePath, "utf8");
13699
+ raw = fs22.readFileSync(filePath, "utf8");
12844
13700
  } catch {
12845
13701
  return null;
12846
13702
  }
@@ -12885,9 +13741,9 @@ var HistoryService = class _HistoryService {
12885
13741
  let totalCost = 0;
12886
13742
  let files;
12887
13743
  try {
12888
- files = fs20.readdirSync(projectDir).filter((f) => f.endsWith(".jsonl")).filter((f) => {
13744
+ files = fs22.readdirSync(projectDir).filter((f) => f.endsWith(".jsonl")).filter((f) => {
12889
13745
  try {
12890
- return fs20.statSync(path24.join(projectDir, f)).mtimeMs >= monthStartMs;
13746
+ return fs22.statSync(path26.join(projectDir, f)).mtimeMs >= monthStartMs;
12891
13747
  } catch {
12892
13748
  return false;
12893
13749
  }
@@ -12898,7 +13754,7 @@ var HistoryService = class _HistoryService {
12898
13754
  for (const file of files) {
12899
13755
  let raw;
12900
13756
  try {
12901
- raw = fs20.readFileSync(path24.join(projectDir, file), "utf8");
13757
+ raw = fs22.readFileSync(path26.join(projectDir, file), "utf8");
12902
13758
  } catch {
12903
13759
  continue;
12904
13760
  }
@@ -12962,7 +13818,7 @@ var HistoryService = class _HistoryService {
12962
13818
  * showing an empty conversation.
12963
13819
  */
12964
13820
  async loadConversation(sessionId) {
12965
- const filePath = path24.join(this.projectDir, `${sessionId}.jsonl`);
13821
+ const filePath = path26.join(this.projectDir, `${sessionId}.jsonl`);
12966
13822
  const messages = parseJsonl(filePath);
12967
13823
  if (messages.length === 0) return;
12968
13824
  const totalBatches = Math.ceil(messages.length / CONVERSATION_BATCH_SIZE);
@@ -13016,7 +13872,7 @@ var HistoryService = class _HistoryService {
13016
13872
  if (!this.currentConversationId) return 0;
13017
13873
  }
13018
13874
  const sessionId = this.currentConversationId;
13019
- const filePath = path24.join(this.projectDir, `${sessionId}.jsonl`);
13875
+ const filePath = path26.join(this.projectDir, `${sessionId}.jsonl`);
13020
13876
  const messages = parseJsonl(filePath);
13021
13877
  if (messages.length === 0) return 0;
13022
13878
  const marker = this.lastUploadedUuid.get(sessionId);
@@ -13047,9 +13903,9 @@ var HistoryService = class _HistoryService {
13047
13903
 
13048
13904
  // src/services/file-watcher.service.ts
13049
13905
  var import_child_process8 = require("child_process");
13050
- var fs21 = __toESM(require("fs"));
13051
- var os22 = __toESM(require("os"));
13052
- var path25 = __toESM(require("path"));
13906
+ var fs23 = __toESM(require("fs"));
13907
+ var os23 = __toESM(require("os"));
13908
+ var path27 = __toESM(require("path"));
13053
13909
 
13054
13910
  // src/services/file-watcher/diff-parser.ts
13055
13911
  var HUNK_HEADER_RE = /^@@ -(\d+)(?:,\d+)? \+(\d+)(?:,\d+)? @@/;
@@ -13138,16 +13994,16 @@ function isIgnoredFilePath(filePath) {
13138
13994
  }
13139
13995
 
13140
13996
  // src/services/file-watcher/transport.ts
13141
- var http5 = __toESM(require("http"));
13142
- var https5 = __toESM(require("https"));
13143
- var _transport3 = {
13144
- post: _post2
13997
+ var http6 = __toESM(require("http"));
13998
+ var https6 = __toESM(require("https"));
13999
+ var _transport4 = {
14000
+ post: _post3
13145
14001
  };
13146
- function _post2(url, headers, payload) {
13147
- return new Promise((resolve5, reject) => {
14002
+ function _post3(url, headers, payload) {
14003
+ return new Promise((resolve6, reject) => {
13148
14004
  let settled = false;
13149
14005
  const u2 = new URL(url);
13150
- const lib = u2.protocol === "https:" ? https5 : http5;
14006
+ const lib = u2.protocol === "https:" ? https6 : http6;
13151
14007
  const req = lib.request(
13152
14008
  {
13153
14009
  hostname: u2.hostname,
@@ -13169,7 +14025,7 @@ function _post2(url, headers, payload) {
13169
14025
  res.on("end", () => {
13170
14026
  if (settled) return;
13171
14027
  settled = true;
13172
- resolve5({ statusCode: res.statusCode ?? 0, body });
14028
+ resolve6({ statusCode: res.statusCode ?? 0, body });
13173
14029
  });
13174
14030
  }
13175
14031
  );
@@ -13207,10 +14063,10 @@ var WINDOWS_LEGACY_JUNCTIONS = [
13207
14063
  /[\\/]Start Menu([\\/]|$)/i,
13208
14064
  /[\\/]Templates([\\/]|$)/i
13209
14065
  ];
13210
- function isUnsafeWindowsWatchRoot(dir, homedir19) {
14066
+ function isUnsafeWindowsWatchRoot(dir, homedir20) {
13211
14067
  const norm = (p2) => p2.replace(/\//g, "\\").replace(/\\+$/, "").toLowerCase();
13212
14068
  const cwd = norm(dir);
13213
- const home = norm(homedir19);
14069
+ const home = norm(homedir20);
13214
14070
  if (cwd === home) return true;
13215
14071
  if (/^[a-z]:$/.test(cwd)) return true;
13216
14072
  const sysRoots = [
@@ -13240,18 +14096,18 @@ var _findGitRootSeam = {
13240
14096
  resolve: _defaultFindGitRoot
13241
14097
  };
13242
14098
  function _defaultFindGitRoot(startDir) {
13243
- let dir = path25.resolve(startDir);
14099
+ let dir = path27.resolve(startDir);
13244
14100
  const seen = /* @__PURE__ */ new Set();
13245
14101
  for (let i = 0; i < 256; i++) {
13246
14102
  if (seen.has(dir)) return null;
13247
14103
  seen.add(dir);
13248
14104
  try {
13249
- const gitPath = path25.join(dir, ".git");
13250
- const stat3 = fs21.statSync(gitPath, { throwIfNoEntry: false });
14105
+ const gitPath = path27.join(dir, ".git");
14106
+ const stat3 = fs23.statSync(gitPath, { throwIfNoEntry: false });
13251
14107
  if (stat3 && (stat3.isDirectory() || stat3.isFile())) return dir;
13252
14108
  } catch {
13253
14109
  }
13254
- const parent = path25.dirname(dir);
14110
+ const parent = path27.dirname(dir);
13255
14111
  if (parent === dir) return null;
13256
14112
  dir = parent;
13257
14113
  }
@@ -13298,7 +14154,7 @@ var FileWatcherService = class {
13298
14154
  throw new Error("FileWatcherService has already been stopped \u2014 re-instantiate to restart.");
13299
14155
  }
13300
14156
  const isWin = process.platform === "win32";
13301
- if (isWin && isUnsafeWindowsWatchRoot(this.opts.workingDir, os22.homedir())) {
14157
+ if (isWin && isUnsafeWindowsWatchRoot(this.opts.workingDir, os23.homedir())) {
13302
14158
  log.warn(
13303
14159
  "fileWatcher",
13304
14160
  `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.`
@@ -13485,7 +14341,7 @@ var FileWatcherService = class {
13485
14341
  }
13486
14342
  async emitForFile(absPath, changeType) {
13487
14343
  if (this.stopped) return;
13488
- const fileDir = path25.dirname(absPath);
14344
+ const fileDir = path27.dirname(absPath);
13489
14345
  let gitRoot = this.gitRootByDir.get(fileDir);
13490
14346
  if (gitRoot === void 0) {
13491
14347
  gitRoot = findGitRoot(fileDir);
@@ -13499,10 +14355,10 @@ var FileWatcherService = class {
13499
14355
  return;
13500
14356
  }
13501
14357
  this.opts.onRepoDirty?.(gitRoot);
13502
- const relPathInRepo = path25.relative(gitRoot, absPath);
14358
+ const relPathInRepo = path27.relative(gitRoot, absPath);
13503
14359
  if (!relPathInRepo || relPathInRepo.startsWith("..")) return;
13504
- const repoPath = path25.relative(this.opts.workingDir, gitRoot);
13505
- const repoName = path25.basename(gitRoot);
14360
+ const repoPath = path27.relative(this.opts.workingDir, gitRoot);
14361
+ const repoName = path27.basename(gitRoot);
13506
14362
  let diffText = "";
13507
14363
  let fileStatus = "modified";
13508
14364
  if (changeType === "unlink") {
@@ -13646,7 +14502,7 @@ var FileWatcherService = class {
13646
14502
  for (let attempt = 0; attempt <= MAX_RETRIES; attempt += 1) {
13647
14503
  if (this.stopped) return;
13648
14504
  try {
13649
- const { statusCode, body: resBody } = await _transport3.post(url, headers, payload);
14505
+ const { statusCode, body: resBody } = await _transport4.post(url, headers, payload);
13650
14506
  if (statusCode >= 200 && statusCode < 300) {
13651
14507
  log.trace(
13652
14508
  "fileWatcher",
@@ -13765,12 +14621,12 @@ var _gitSeam = {
13765
14621
  run: _runGitImpl
13766
14622
  };
13767
14623
  async function _runGitImpl(cwd, args2, opts = {}) {
13768
- return new Promise((resolve5) => {
14624
+ return new Promise((resolve6) => {
13769
14625
  let proc;
13770
14626
  try {
13771
14627
  proc = (0, import_child_process8.spawn)("git", args2, { cwd, env: process.env });
13772
14628
  } catch {
13773
- resolve5(null);
14629
+ resolve6(null);
13774
14630
  return;
13775
14631
  }
13776
14632
  let stdout = "";
@@ -13781,13 +14637,13 @@ async function _runGitImpl(cwd, args2, opts = {}) {
13781
14637
  proc.stderr?.on("data", (c2) => {
13782
14638
  stderr += c2.toString();
13783
14639
  });
13784
- proc.on("error", () => resolve5(null));
14640
+ proc.on("error", () => resolve6(null));
13785
14641
  proc.on("close", (code) => {
13786
14642
  if (code === 0 || opts.allowNonZeroExit) {
13787
- resolve5(stdout);
14643
+ resolve6(stdout);
13788
14644
  } else {
13789
14645
  log.trace("fileWatcher", `git ${args2.join(" ")} exited ${code} stderr=${stderr.slice(0, 200)}`);
13790
- resolve5(null);
14646
+ resolve6(null);
13791
14647
  }
13792
14648
  });
13793
14649
  });
@@ -13801,7 +14657,7 @@ var import_crypto2 = require("crypto");
13801
14657
 
13802
14658
  // src/services/turn-files/git-changeset.ts
13803
14659
  var import_child_process9 = require("child_process");
13804
- var path26 = __toESM(require("path"));
14660
+ var path28 = __toESM(require("path"));
13805
14661
  async function collectRepoChangeset(opts) {
13806
14662
  const status2 = await runGit2(opts.repoRoot, ["status", "--porcelain=v1", "-z"]);
13807
14663
  if (status2 === null) return null;
@@ -13881,12 +14737,12 @@ function runGit2(cwd, args2) {
13881
14737
  return _runGitImpl2.run(cwd, args2);
13882
14738
  }
13883
14739
  function defaultRunGit(cwd, args2) {
13884
- return new Promise((resolve5) => {
14740
+ return new Promise((resolve6) => {
13885
14741
  let proc;
13886
14742
  try {
13887
14743
  proc = (0, import_child_process9.spawn)("git", args2, { cwd, env: process.env });
13888
14744
  } catch {
13889
- resolve5(null);
14745
+ resolve6(null);
13890
14746
  return;
13891
14747
  }
13892
14748
  let stdout = "";
@@ -13897,22 +14753,22 @@ function defaultRunGit(cwd, args2) {
13897
14753
  proc.stderr?.on("data", (c2) => {
13898
14754
  stderr += c2.toString();
13899
14755
  });
13900
- proc.on("error", () => resolve5(null));
14756
+ proc.on("error", () => resolve6(null));
13901
14757
  proc.on("close", (code) => {
13902
14758
  if (code === 0) {
13903
- resolve5(stdout);
14759
+ resolve6(stdout);
13904
14760
  } else {
13905
14761
  log.trace(
13906
14762
  "turnFiles",
13907
14763
  `git ${args2.join(" ")} exited ${code} stderr=${stderr.slice(0, 200)}`
13908
14764
  );
13909
- resolve5(null);
14765
+ resolve6(null);
13910
14766
  }
13911
14767
  });
13912
14768
  });
13913
14769
  }
13914
14770
  async function discoverRepos(workingDir, maxDepth = 4) {
13915
- const fs34 = await import("fs/promises");
14771
+ const fs36 = await import("fs/promises");
13916
14772
  const out2 = [];
13917
14773
  await walk(workingDir, 0);
13918
14774
  return out2;
@@ -13920,7 +14776,7 @@ async function discoverRepos(workingDir, maxDepth = 4) {
13920
14776
  if (depth > maxDepth) return;
13921
14777
  let entries = [];
13922
14778
  try {
13923
- const dirents = await fs34.readdir(dir, { withFileTypes: true });
14779
+ const dirents = await fs36.readdir(dir, { withFileTypes: true });
13924
14780
  entries = dirents.filter((d3) => !d3.name.startsWith(".") || d3.name === ".git").map((d3) => ({ name: d3.name, isDirectory: d3.isDirectory() }));
13925
14781
  } catch {
13926
14782
  return;
@@ -13931,8 +14787,8 @@ async function discoverRepos(workingDir, maxDepth = 4) {
13931
14787
  if (hasGit) {
13932
14788
  out2.push({
13933
14789
  repoRoot: dir,
13934
- repoPath: path26.relative(workingDir, dir),
13935
- repoName: path26.basename(dir)
14790
+ repoPath: path28.relative(workingDir, dir),
14791
+ repoName: path28.basename(dir)
13936
14792
  });
13937
14793
  return;
13938
14794
  }
@@ -13940,15 +14796,15 @@ async function discoverRepos(workingDir, maxDepth = 4) {
13940
14796
  if (!entry.isDirectory) continue;
13941
14797
  if (entry.name === "node_modules") continue;
13942
14798
  if (entry.name === "dist" || entry.name === "build") continue;
13943
- await walk(path26.join(dir, entry.name), depth + 1);
14799
+ await walk(path28.join(dir, entry.name), depth + 1);
13944
14800
  }
13945
14801
  }
13946
14802
  }
13947
14803
 
13948
14804
  // src/services/turn-files/files-outbox.ts
13949
- var fs22 = __toESM(require("fs/promises"));
13950
- var path27 = __toESM(require("path"));
13951
- var import_os6 = require("os");
14805
+ var fs24 = __toESM(require("fs/promises"));
14806
+ var path29 = __toESM(require("path"));
14807
+ var import_os7 = require("os");
13952
14808
  var HOME_OUTBOX_DIR = ".codeam/outbox";
13953
14809
  var MAX_AGE_MS = 24 * 60 * 60 * 1e3;
13954
14810
  var BACKOFF_STEPS_MS = [
@@ -13980,16 +14836,16 @@ var FilesOutbox = class {
13980
14836
  backoffIndex = 0;
13981
14837
  stopped = false;
13982
14838
  constructor(opts) {
13983
- const base = opts.baseDir ?? path27.join(homeDir(), HOME_OUTBOX_DIR);
13984
- this.filePath = path27.join(base, `${opts.sessionId}.jsonl`);
14839
+ const base = opts.baseDir ?? path29.join(homeDir(), HOME_OUTBOX_DIR);
14840
+ this.filePath = path29.join(base, `${opts.sessionId}.jsonl`);
13985
14841
  this.post = opts.post;
13986
14842
  this.autoSchedule = opts.autoSchedule !== false;
13987
14843
  }
13988
14844
  /** Persist the entry to disk and trigger a flush. Returns once the
13989
14845
  * line is durable on disk (not once the POST succeeds). */
13990
14846
  async enqueue(entry) {
13991
- await fs22.mkdir(path27.dirname(this.filePath), { recursive: true });
13992
- await fs22.appendFile(this.filePath, JSON.stringify(entry) + "\n", "utf8");
14847
+ await fs24.mkdir(path29.dirname(this.filePath), { recursive: true });
14848
+ await fs24.appendFile(this.filePath, JSON.stringify(entry) + "\n", "utf8");
13993
14849
  this.backoffIndex = 0;
13994
14850
  if (this.autoSchedule) this.scheduleFlush(0);
13995
14851
  }
@@ -14078,7 +14934,7 @@ var FilesOutbox = class {
14078
14934
  async readAll() {
14079
14935
  let raw = "";
14080
14936
  try {
14081
- raw = await fs22.readFile(this.filePath, "utf8");
14937
+ raw = await fs24.readFile(this.filePath, "utf8");
14082
14938
  } catch {
14083
14939
  return [];
14084
14940
  }
@@ -14102,12 +14958,12 @@ var FilesOutbox = class {
14102
14958
  async rewrite(entries) {
14103
14959
  const tmpPath = `${this.filePath}.${process.pid}.tmp`;
14104
14960
  if (entries.length === 0) {
14105
- await fs22.unlink(this.filePath).catch(() => void 0);
14961
+ await fs24.unlink(this.filePath).catch(() => void 0);
14106
14962
  return;
14107
14963
  }
14108
14964
  const payload = entries.map((e) => JSON.stringify(e)).join("\n") + "\n";
14109
- await fs22.writeFile(tmpPath, payload, "utf8");
14110
- await fs22.rename(tmpPath, this.filePath);
14965
+ await fs24.writeFile(tmpPath, payload, "utf8");
14966
+ await fs24.rename(tmpPath, this.filePath);
14111
14967
  }
14112
14968
  };
14113
14969
  function applyJitter(ms) {
@@ -14115,7 +14971,7 @@ function applyJitter(ms) {
14115
14971
  return Math.round(ms * factor);
14116
14972
  }
14117
14973
  function homeDir() {
14118
- return process.env.HOME ?? process.env.USERPROFILE ?? (0, import_os6.tmpdir)();
14974
+ return process.env.HOME ?? process.env.USERPROFILE ?? (0, import_os7.tmpdir)();
14119
14975
  }
14120
14976
 
14121
14977
  // src/services/turn-files/turn-file-aggregator.ts
@@ -14222,13 +15078,13 @@ var TurnFileAggregator = class {
14222
15078
  return;
14223
15079
  }
14224
15080
  const chunks = chunkArray(novel, MAX_BATCH_SIZE);
14225
- for (const chunk of chunks) {
15081
+ for (const chunk2 of chunks) {
14226
15082
  const entry = {
14227
15083
  turnId: (0, import_crypto2.randomUUID)(),
14228
15084
  sessionId: this.opts.sessionId,
14229
15085
  pluginId: this.opts.pluginId,
14230
15086
  enqueuedAt: Date.now(),
14231
- files: chunk
15087
+ files: chunk2
14232
15088
  };
14233
15089
  await this.outbox.enqueue(entry);
14234
15090
  }
@@ -14267,7 +15123,7 @@ var TurnFileAggregator = class {
14267
15123
  files: entry.files
14268
15124
  });
14269
15125
  try {
14270
- const res = await _transport3.post(url, headers, body);
15126
+ const res = await _transport4.post(url, headers, body);
14271
15127
  return { ok: res.statusCode >= 200 && res.statusCode < 300, statusCode: res.statusCode };
14272
15128
  } catch (err) {
14273
15129
  log.trace(
@@ -14326,101 +15182,9 @@ var RepoDirtyTracker = class {
14326
15182
 
14327
15183
  // src/services/streaming-emitter.service.ts
14328
15184
  var import_crypto3 = require("crypto");
14329
-
14330
- // src/services/streaming/transport.ts
14331
- var http6 = __toESM(require("http"));
14332
- var https6 = __toESM(require("https"));
14333
- var _transport4 = {
14334
- post: _post3,
14335
- get: _get
14336
- };
14337
- function _post3(url, headers, payload) {
14338
- return new Promise((resolve5, reject) => {
14339
- let settled = false;
14340
- const u2 = new URL(url);
14341
- const lib = u2.protocol === "https:" ? https6 : http6;
14342
- const req = lib.request(
14343
- {
14344
- hostname: u2.hostname,
14345
- port: u2.port || (u2.protocol === "https:" ? 443 : 80),
14346
- path: u2.pathname + u2.search,
14347
- method: "POST",
14348
- headers: {
14349
- ...headers,
14350
- ...vercelBypassHeader(),
14351
- "Content-Length": Buffer.byteLength(payload)
14352
- },
14353
- timeout: 8e3
14354
- },
14355
- (res) => {
14356
- let body = "";
14357
- res.on("data", (c2) => {
14358
- body += c2.toString();
14359
- });
14360
- res.on("end", () => {
14361
- if (settled) return;
14362
- settled = true;
14363
- resolve5({ statusCode: res.statusCode ?? 0, body });
14364
- });
14365
- }
14366
- );
14367
- req.on("error", (err) => {
14368
- if (settled) return;
14369
- settled = true;
14370
- reject(err);
14371
- });
14372
- req.on("timeout", () => {
14373
- req.destroy();
14374
- });
14375
- req.write(payload);
14376
- req.end();
14377
- });
14378
- }
14379
- function _get(url, headers) {
14380
- return new Promise((resolve5, reject) => {
14381
- let settled = false;
14382
- const u2 = new URL(url);
14383
- const lib = u2.protocol === "https:" ? https6 : http6;
14384
- const req = lib.request(
14385
- {
14386
- hostname: u2.hostname,
14387
- port: u2.port || (u2.protocol === "https:" ? 443 : 80),
14388
- path: u2.pathname + u2.search,
14389
- method: "GET",
14390
- headers: {
14391
- ...headers,
14392
- ...vercelBypassHeader()
14393
- },
14394
- timeout: 8e3
14395
- },
14396
- (res) => {
14397
- let body = "";
14398
- res.on("data", (c2) => {
14399
- body += c2.toString();
14400
- });
14401
- res.on("end", () => {
14402
- if (settled) return;
14403
- settled = true;
14404
- resolve5({ statusCode: res.statusCode ?? 0, body });
14405
- });
14406
- }
14407
- );
14408
- req.on("error", (err) => {
14409
- if (settled) return;
14410
- settled = true;
14411
- reject(err);
14412
- });
14413
- req.on("timeout", () => {
14414
- req.destroy();
14415
- });
14416
- req.end();
14417
- });
14418
- }
14419
-
14420
- // src/services/streaming-emitter.service.ts
14421
15185
  var API_BASE7 = resolveApiBaseUrl();
14422
15186
  var TICK_MS = 50;
14423
- var ANSWER_POLL_MS = 1500;
15187
+ var ANSWER_POLL_MS2 = 1500;
14424
15188
  var SELECTOR_STABLE_MS = 800;
14425
15189
  var MAX_RETRIES2 = 1;
14426
15190
  var RETRY_BACKOFF_MS2 = 200;
@@ -14460,7 +15224,7 @@ var StreamingEmitterService = class {
14460
15224
  this.tickTimer = setInterval(() => this.tick(), TICK_MS);
14461
15225
  this.answerPollTimer = setInterval(() => {
14462
15226
  void this.pollPendingAnswer();
14463
- }, ANSWER_POLL_MS);
15227
+ }, ANSWER_POLL_MS2);
14464
15228
  log.trace("streamingEmitter", `started session=${this.opts.sessionId.slice(0, 8)}`);
14465
15229
  }
14466
15230
  /**
@@ -14481,11 +15245,11 @@ var StreamingEmitterService = class {
14481
15245
  }
14482
15246
  if (this.activeChunk) {
14483
15247
  const finalContent = this.activeChunk.currentContent;
14484
- const chunk = this.activeChunk;
15248
+ const chunk2 = this.activeChunk;
14485
15249
  this.activeChunk = null;
14486
15250
  await this.postChunk({
14487
- chunkId: chunk.chunkId,
14488
- kind: chunk.kind,
15251
+ chunkId: chunk2.chunkId,
15252
+ kind: chunk2.kind,
14489
15253
  content: finalContent,
14490
15254
  isFinal: true
14491
15255
  });
@@ -14557,17 +15321,17 @@ var StreamingEmitterService = class {
14557
15321
  this.maybeFlushActive(false);
14558
15322
  }
14559
15323
  maybeFlushActive(force) {
14560
- const chunk = this.activeChunk;
14561
- if (!chunk) return;
15324
+ const chunk2 = this.activeChunk;
15325
+ if (!chunk2) return;
14562
15326
  const now = Date.now();
14563
- if (!force && now - chunk.lastEmitAt < TICK_MS) return;
14564
- if (chunk.currentContent === chunk.emittedContent) return;
14565
- chunk.emittedContent = chunk.currentContent;
14566
- chunk.lastEmitAt = now;
15327
+ if (!force && now - chunk2.lastEmitAt < TICK_MS) return;
15328
+ if (chunk2.currentContent === chunk2.emittedContent) return;
15329
+ chunk2.emittedContent = chunk2.currentContent;
15330
+ chunk2.lastEmitAt = now;
14567
15331
  void this.postChunk({
14568
- chunkId: chunk.chunkId,
14569
- kind: chunk.kind,
14570
- content: chunk.currentContent,
15332
+ chunkId: chunk2.chunkId,
15333
+ kind: chunk2.kind,
15334
+ content: chunk2.currentContent,
14571
15335
  isFinal: false
14572
15336
  });
14573
15337
  }
@@ -14634,7 +15398,7 @@ var StreamingEmitterService = class {
14634
15398
  if (!this.pendingAnswer) return;
14635
15399
  const questionId = this.pendingAnswer.questionId;
14636
15400
  try {
14637
- const { statusCode, body } = await _transport4.get(
15401
+ const { statusCode, body } = await _transport2.get(
14638
15402
  `${this.apiBase}/api/sessions/${encodeURIComponent(this.opts.sessionId)}/pending-answer?questionId=${encodeURIComponent(questionId)}&pluginId=${encodeURIComponent(this.opts.pluginId)}`,
14639
15403
  this.headers
14640
15404
  );
@@ -14645,7 +15409,7 @@ var StreamingEmitterService = class {
14645
15409
  log.warn("streamingEmitter", `pending-answer status=${statusCode} body=${body.slice(0, 200)}`);
14646
15410
  return;
14647
15411
  }
14648
- const parsed = parsePendingAnswerResponse(body);
15412
+ const parsed = parsePendingAnswerResponse2(body);
14649
15413
  if (!parsed || parsed.questionId !== questionId) return;
14650
15414
  const pending = this.pendingAnswer;
14651
15415
  this.pendingAnswer = null;
@@ -14695,7 +15459,7 @@ var StreamingEmitterService = class {
14695
15459
  });
14696
15460
  for (let attempt = 0; attempt <= MAX_RETRIES2; attempt += 1) {
14697
15461
  try {
14698
- const { statusCode, body: resBody } = await _transport4.post(url, this.headers, payload);
15462
+ const { statusCode, body: resBody } = await _transport2.post(url, this.headers, payload);
14699
15463
  if (statusCode >= 200 && statusCode < 300) {
14700
15464
  log.trace("streamingEmitter", `post ok url=${url} status=${statusCode}`);
14701
15465
  return;
@@ -14742,7 +15506,7 @@ function classifyLine(line, parseChrome, runtime) {
14742
15506
  if (filtered.length === 0) return null;
14743
15507
  return "text";
14744
15508
  }
14745
- function parsePendingAnswerResponse(body) {
15509
+ function parsePendingAnswerResponse2(body) {
14746
15510
  if (!body) return null;
14747
15511
  let parsed;
14748
15512
  try {
@@ -14784,7 +15548,7 @@ function buildKeepAlive(ctx) {
14784
15548
  let timer = null;
14785
15549
  async function setIdleTimeout(minutes) {
14786
15550
  if (!ctx.inCodespace || !ctx.codespaceName) return;
14787
- await new Promise((resolve5) => {
15551
+ await new Promise((resolve6) => {
14788
15552
  const proc = (0, import_child_process10.spawn)(
14789
15553
  "gh",
14790
15554
  [
@@ -14798,8 +15562,8 @@ function buildKeepAlive(ctx) {
14798
15562
  { stdio: "ignore", detached: true }
14799
15563
  );
14800
15564
  proc.unref();
14801
- proc.on("exit", () => resolve5());
14802
- proc.on("error", () => resolve5());
15565
+ proc.on("exit", () => resolve6());
15566
+ proc.on("error", () => resolve6());
14803
15567
  });
14804
15568
  }
14805
15569
  return {
@@ -14822,9 +15586,9 @@ function buildKeepAlive(ctx) {
14822
15586
  }
14823
15587
 
14824
15588
  // src/commands/start/handlers.ts
14825
- var fs30 = __toESM(require("fs"));
14826
- var os24 = __toESM(require("os"));
14827
- var path36 = __toESM(require("path"));
15589
+ var fs32 = __toESM(require("fs"));
15590
+ var os25 = __toESM(require("os"));
15591
+ var path38 = __toESM(require("path"));
14828
15592
  var import_crypto5 = require("crypto");
14829
15593
  var import_child_process15 = require("child_process");
14830
15594
 
@@ -14948,8 +15712,8 @@ function parsePayload2(schema, raw) {
14948
15712
  }
14949
15713
 
14950
15714
  // src/services/file-ops.service.ts
14951
- var fs23 = __toESM(require("fs/promises"));
14952
- var path28 = __toESM(require("path"));
15715
+ var fs25 = __toESM(require("fs/promises"));
15716
+ var path30 = __toESM(require("path"));
14953
15717
  var MAX_FILE_BYTES = 5 * 1024 * 1024;
14954
15718
  var MAX_WALK_DEPTH = 6;
14955
15719
  var MAX_VISITED_DIRS = 5e3;
@@ -14984,12 +15748,12 @@ var SUBDIR_IGNORE = /* @__PURE__ */ new Set([
14984
15748
  "__pycache__"
14985
15749
  ]);
14986
15750
  function isUnder(parent, candidate) {
14987
- const rel = path28.relative(parent, candidate);
14988
- return rel === "" || !rel.startsWith("..") && !path28.isAbsolute(rel);
15751
+ const rel = path30.relative(parent, candidate);
15752
+ return rel === "" || !rel.startsWith("..") && !path30.isAbsolute(rel);
14989
15753
  }
14990
15754
  async function isExistingFile(absPath) {
14991
15755
  try {
14992
- const stat3 = await fs23.stat(absPath);
15756
+ const stat3 = await fs25.stat(absPath);
14993
15757
  return stat3.isFile();
14994
15758
  } catch {
14995
15759
  return false;
@@ -15002,13 +15766,13 @@ async function walkForSuffix(dir, needleVariants, depth, ctx) {
15002
15766
  ctx.visited++;
15003
15767
  let entries = [];
15004
15768
  try {
15005
- entries = await fs23.readdir(dir, { withFileTypes: true });
15769
+ entries = await fs25.readdir(dir, { withFileTypes: true });
15006
15770
  } catch {
15007
15771
  return;
15008
15772
  }
15009
15773
  for (const e of entries) {
15010
15774
  if (!e.isFile()) continue;
15011
- const full = path28.join(dir, e.name);
15775
+ const full = path30.join(dir, e.name);
15012
15776
  if (needleVariants.some((needle) => full.endsWith(needle))) {
15013
15777
  ctx.matches.push(full);
15014
15778
  if (ctx.matches.length >= ctx.cap) return;
@@ -15018,21 +15782,21 @@ async function walkForSuffix(dir, needleVariants, depth, ctx) {
15018
15782
  if (!e.isDirectory()) continue;
15019
15783
  if (SUBDIR_IGNORE.has(e.name)) continue;
15020
15784
  if (e.name.startsWith(".") && SUBDIR_IGNORE.has(e.name)) continue;
15021
- await walkForSuffix(path28.join(dir, e.name), needleVariants, depth + 1, ctx);
15785
+ await walkForSuffix(path30.join(dir, e.name), needleVariants, depth + 1, ctx);
15022
15786
  if (ctx.matches.length >= ctx.cap) return;
15023
15787
  }
15024
15788
  }
15025
15789
  async function findFile(rawPath) {
15026
15790
  const cwd = process.cwd();
15027
- if (path28.isAbsolute(rawPath)) {
15028
- const abs = path28.normalize(rawPath);
15791
+ if (path30.isAbsolute(rawPath)) {
15792
+ const abs = path30.normalize(rawPath);
15029
15793
  if (isUnder(cwd, abs) && await isExistingFile(abs)) return abs;
15030
15794
  }
15031
- const direct = path28.resolve(cwd, rawPath);
15795
+ const direct = path30.resolve(cwd, rawPath);
15032
15796
  if (isUnder(cwd, direct) && await isExistingFile(direct)) return direct;
15033
- const normalized = path28.normalize(rawPath).replace(/^[./\\]+/, "");
15797
+ const normalized = path30.normalize(rawPath).replace(/^[./\\]+/, "");
15034
15798
  const needles = [
15035
- `${path28.sep}${normalized}`,
15799
+ `${path30.sep}${normalized}`,
15036
15800
  `/${normalized}`
15037
15801
  ].filter((v, i, a) => a.indexOf(v) === i);
15038
15802
  const ctx = { visited: 0, matches: [], cap: 16 };
@@ -15046,7 +15810,7 @@ async function findWriteTarget(rawPath) {
15046
15810
  const found = await findFile(rawPath);
15047
15811
  if (found) return found;
15048
15812
  const cwd = process.cwd();
15049
- const fallback = path28.isAbsolute(rawPath) ? path28.normalize(rawPath) : path28.resolve(cwd, rawPath);
15813
+ const fallback = path30.isAbsolute(rawPath) ? path30.normalize(rawPath) : path30.resolve(cwd, rawPath);
15050
15814
  if (!isUnder(cwd, fallback)) return null;
15051
15815
  return fallback;
15052
15816
  }
@@ -15063,11 +15827,11 @@ async function readProjectFile(rawPath) {
15063
15827
  if (!abs) {
15064
15828
  return { error: `File not found in the project tree: ${rawPath}` };
15065
15829
  }
15066
- const stat3 = await fs23.stat(abs);
15830
+ const stat3 = await fs25.stat(abs);
15067
15831
  if (stat3.size > MAX_FILE_BYTES) {
15068
15832
  return { error: `File too large (${(stat3.size / 1024 / 1024).toFixed(1)} MB > ${MAX_FILE_BYTES / 1024 / 1024} MB).` };
15069
15833
  }
15070
- const buf = await fs23.readFile(abs);
15834
+ const buf = await fs25.readFile(abs);
15071
15835
  if (looksBinary(buf)) {
15072
15836
  return { error: "Binary file \u2014 refusing to open in a code editor." };
15073
15837
  }
@@ -15086,8 +15850,8 @@ async function writeProjectFile(rawPath, content) {
15086
15850
  if (Buffer.byteLength(content, "utf-8") > MAX_FILE_BYTES) {
15087
15851
  return { error: "Content too large." };
15088
15852
  }
15089
- await fs23.mkdir(path28.dirname(abs), { recursive: true });
15090
- await fs23.writeFile(abs, content, "utf-8");
15853
+ await fs25.mkdir(path30.dirname(abs), { recursive: true });
15854
+ await fs25.writeFile(abs, content, "utf-8");
15091
15855
  return { ok: true };
15092
15856
  } catch (e) {
15093
15857
  const msg = e instanceof Error ? e.message : "Write failed";
@@ -15098,8 +15862,8 @@ async function writeProjectFile(rawPath, content) {
15098
15862
  // src/services/project-ops.service.ts
15099
15863
  var import_child_process11 = require("child_process");
15100
15864
  var import_util2 = require("util");
15101
- var fs24 = __toESM(require("fs/promises"));
15102
- var path29 = __toESM(require("path"));
15865
+ var fs26 = __toESM(require("fs/promises"));
15866
+ var path31 = __toESM(require("path"));
15103
15867
  var execFileP3 = (0, import_util2.promisify)(import_child_process11.execFile);
15104
15868
  var PROJECT_IGNORE = /* @__PURE__ */ new Set([
15105
15869
  "node_modules",
@@ -15147,7 +15911,7 @@ async function listProjectFiles(opts = {}) {
15147
15911
  }
15148
15912
  let entries = [];
15149
15913
  try {
15150
- entries = await fs24.readdir(dir, { withFileTypes: true });
15914
+ entries = await fs26.readdir(dir, { withFileTypes: true });
15151
15915
  } catch {
15152
15916
  return;
15153
15917
  }
@@ -15157,18 +15921,18 @@ async function listProjectFiles(opts = {}) {
15157
15921
  return;
15158
15922
  }
15159
15923
  if (PROJECT_IGNORE.has(e.name)) continue;
15160
- const full = path29.join(dir, e.name);
15924
+ const full = path31.join(dir, e.name);
15161
15925
  if (e.isDirectory()) {
15162
15926
  if (depth >= 12) continue;
15163
15927
  await walk(full, depth + 1);
15164
15928
  } else if (e.isFile()) {
15165
- const rel = path29.relative(root, full);
15929
+ const rel = path31.relative(root, full);
15166
15930
  if (q2 && !rel.toLowerCase().includes(q2) && !e.name.toLowerCase().includes(q2)) {
15167
15931
  continue;
15168
15932
  }
15169
15933
  let size = 0;
15170
15934
  try {
15171
- const st3 = await fs24.stat(full);
15935
+ const st3 = await fs26.stat(full);
15172
15936
  size = st3.size;
15173
15937
  } catch {
15174
15938
  }
@@ -15270,8 +16034,8 @@ async function gitStatus(cwd) {
15270
16034
  let hasMergeInProgress = false;
15271
16035
  try {
15272
16036
  const gitDir = (await git(["rev-parse", "--git-dir"], root)).stdout.trim();
15273
- const mergeHead = path29.isAbsolute(gitDir) ? path29.join(gitDir, "MERGE_HEAD") : path29.join(root, gitDir, "MERGE_HEAD");
15274
- await fs24.access(mergeHead);
16037
+ const mergeHead = path31.isAbsolute(gitDir) ? path31.join(gitDir, "MERGE_HEAD") : path31.join(root, gitDir, "MERGE_HEAD");
16038
+ await fs26.access(mergeHead);
15275
16039
  hasMergeInProgress = true;
15276
16040
  } catch {
15277
16041
  }
@@ -15417,7 +16181,7 @@ async function jsSearchFiles(opts, cwd, cap) {
15417
16181
  }
15418
16182
  let content = "";
15419
16183
  try {
15420
- content = await fs24.readFile(path29.join(cwd, f.path), "utf8");
16184
+ content = await fs26.readFile(path31.join(cwd, f.path), "utf8");
15421
16185
  } catch {
15422
16186
  continue;
15423
16187
  }
@@ -15698,14 +16462,14 @@ function closeAllTerminals() {
15698
16462
 
15699
16463
  // src/services/apply-file-review.service.ts
15700
16464
  var import_child_process13 = require("child_process");
15701
- var fs25 = __toESM(require("fs"));
15702
- var path31 = __toESM(require("path"));
16465
+ var fs27 = __toESM(require("fs"));
16466
+ var path33 = __toESM(require("path"));
15703
16467
  async function applyFileReview(workingDir, filePath, action) {
15704
- if (filePath.includes("..") || path31.isAbsolute(filePath)) {
16468
+ if (filePath.includes("..") || path33.isAbsolute(filePath)) {
15705
16469
  return { ok: false, action, filePath, error: "invalid file path" };
15706
16470
  }
15707
- const absFile = path31.resolve(workingDir, filePath);
15708
- const repoRoot = findGitRoot2(path31.dirname(absFile));
16471
+ const absFile = path33.resolve(workingDir, filePath);
16472
+ const repoRoot = findGitRoot2(path33.dirname(absFile));
15709
16473
  if (!repoRoot) {
15710
16474
  return {
15711
16475
  ok: false,
@@ -15714,7 +16478,7 @@ async function applyFileReview(workingDir, filePath, action) {
15714
16478
  error: `no enclosing git repo for ${filePath}`
15715
16479
  };
15716
16480
  }
15717
- const relInRepo = path31.relative(repoRoot, absFile);
16481
+ const relInRepo = path33.relative(repoRoot, absFile);
15718
16482
  if (!relInRepo || relInRepo.startsWith("..")) {
15719
16483
  return { ok: false, action, filePath, error: "path escapes repo root" };
15720
16484
  }
@@ -15736,12 +16500,12 @@ async function applyFileReview(workingDir, filePath, action) {
15736
16500
  return { ok: true, action, filePath, repoRoot };
15737
16501
  }
15738
16502
  function runGit3(cwd, args2) {
15739
- return new Promise((resolve5) => {
16503
+ return new Promise((resolve6) => {
15740
16504
  let proc;
15741
16505
  try {
15742
16506
  proc = (0, import_child_process13.spawn)("git", args2, { cwd, env: process.env });
15743
16507
  } catch (err) {
15744
- resolve5({ ok: false, code: -1, stdout: "", stderr: err.message });
16508
+ resolve6({ ok: false, code: -1, stdout: "", stderr: err.message });
15745
16509
  return;
15746
16510
  }
15747
16511
  let stdout = "";
@@ -15754,26 +16518,26 @@ function runGit3(cwd, args2) {
15754
16518
  });
15755
16519
  proc.on(
15756
16520
  "error",
15757
- (err) => resolve5({ ok: false, code: -1, stdout, stderr: stderr + err.message })
16521
+ (err) => resolve6({ ok: false, code: -1, stdout, stderr: stderr + err.message })
15758
16522
  );
15759
16523
  proc.on(
15760
16524
  "close",
15761
- (code) => resolve5({ ok: code === 0, code: code ?? -1, stdout, stderr })
16525
+ (code) => resolve6({ ok: code === 0, code: code ?? -1, stdout, stderr })
15762
16526
  );
15763
16527
  });
15764
16528
  }
15765
16529
  function findGitRoot2(startDir) {
15766
- let dir = path31.resolve(startDir);
16530
+ let dir = path33.resolve(startDir);
15767
16531
  const seen = /* @__PURE__ */ new Set();
15768
16532
  for (let i = 0; i < 256; i++) {
15769
16533
  if (seen.has(dir)) return null;
15770
16534
  seen.add(dir);
15771
16535
  try {
15772
- const stat3 = fs25.statSync(path31.join(dir, ".git"), { throwIfNoEntry: false });
16536
+ const stat3 = fs27.statSync(path33.join(dir, ".git"), { throwIfNoEntry: false });
15773
16537
  if (stat3 && (stat3.isDirectory() || stat3.isFile())) return dir;
15774
16538
  } catch {
15775
16539
  }
15776
- const parent = path31.dirname(dir);
16540
+ const parent = path33.dirname(dir);
15777
16541
  if (parent === dir) return null;
15778
16542
  dir = parent;
15779
16543
  }
@@ -15781,9 +16545,9 @@ function findGitRoot2(startDir) {
15781
16545
  }
15782
16546
 
15783
16547
  // src/commands/link.ts
15784
- var import_node_crypto5 = require("crypto");
15785
- var fs26 = __toESM(require("fs"));
15786
- var path32 = __toESM(require("path"));
16548
+ var import_node_crypto7 = require("crypto");
16549
+ var fs28 = __toESM(require("fs"));
16550
+ var path34 = __toESM(require("path"));
15787
16551
  var import_chokidar = __toESM(require("chokidar"));
15788
16552
  var import_picocolors2 = __toESM(require("picocolors"));
15789
16553
 
@@ -15843,7 +16607,7 @@ function parseLinkArgs(args2) {
15843
16607
  if (apiKeyFileArg) {
15844
16608
  const filePath = apiKeyFileArg.slice("--api-key-file=".length);
15845
16609
  try {
15846
- apiKey = fs26.readFileSync(path32.resolve(filePath), "utf8").trim();
16610
+ apiKey = fs28.readFileSync(path34.resolve(filePath), "utf8").trim();
15847
16611
  } catch (err) {
15848
16612
  throw new Error(`Could not read --api-key-file ${filePath}: ${err.message}`);
15849
16613
  }
@@ -15870,7 +16634,7 @@ async function link(args2 = []) {
15870
16634
  await linkDryRunPreflight(ctx);
15871
16635
  return;
15872
16636
  }
15873
- const pluginId = (0, import_node_crypto5.randomUUID)();
16637
+ const pluginId = (0, import_node_crypto7.randomUUID)();
15874
16638
  const spin = dist_exports.spinner();
15875
16639
  spin.start("Requesting pairing code...");
15876
16640
  const pairing = await requestCode(pluginId);
@@ -15888,7 +16652,7 @@ async function link(args2 = []) {
15888
16652
  waitSpin.start(waitMsg());
15889
16653
  const countdown = setInterval(() => waitSpin.message(waitMsg()), 1e3);
15890
16654
  countdown.unref?.();
15891
- const paired = await new Promise((resolve5, reject) => {
16655
+ const paired = await new Promise((resolve6, reject) => {
15892
16656
  let stopPoll = null;
15893
16657
  const sigint = () => {
15894
16658
  clearInterval(countdown);
@@ -15901,7 +16665,7 @@ async function link(args2 = []) {
15901
16665
  process.removeListener("SIGINT", sigint);
15902
16666
  clearInterval(countdown);
15903
16667
  waitSpin.stop("Paired");
15904
- resolve5(info);
16668
+ resolve6(info);
15905
16669
  },
15906
16670
  () => {
15907
16671
  clearInterval(countdown);
@@ -15937,7 +16701,7 @@ async function link(args2 = []) {
15937
16701
  return;
15938
16702
  }
15939
16703
  if (parsed.tokenFile) {
15940
- const credential = fs26.readFileSync(path32.resolve(parsed.tokenFile), "utf8").trim();
16704
+ const credential = fs28.readFileSync(path34.resolve(parsed.tokenFile), "utf8").trim();
15941
16705
  if (!credential) {
15942
16706
  showError(`--token-file ${parsed.tokenFile} is empty.`);
15943
16707
  process.exit(1);
@@ -16036,14 +16800,14 @@ async function captureFreshCredentials(ctx) {
16036
16800
  }
16037
16801
  };
16038
16802
  try {
16039
- const token = await new Promise((resolve5, reject) => {
16803
+ const token = await new Promise((resolve6, reject) => {
16040
16804
  let settled = false;
16041
16805
  const tryExtract = async () => {
16042
16806
  if (settled) return;
16043
16807
  const t2 = await ctx.locator.extract();
16044
16808
  if (t2 && !settled) {
16045
16809
  settled = true;
16046
- resolve5(t2);
16810
+ resolve6(t2);
16047
16811
  }
16048
16812
  };
16049
16813
  watcher.on("add", () => void tryExtract());
@@ -16156,11 +16920,11 @@ async function linkDryRunPreflight(ctx) {
16156
16920
  var import_promises = require("dns/promises");
16157
16921
  var import_fs = require("fs");
16158
16922
  var import_promises2 = __toESM(require("fs/promises"));
16159
- var import_os7 = __toESM(require("os"));
16923
+ var import_os8 = __toESM(require("os"));
16160
16924
  var import_path4 = __toESM(require("path"));
16161
16925
  var import_promises3 = require("stream/promises");
16162
16926
  var import_which = __toESM(require("which"));
16163
- var CACHED_BINARY = import_path4.default.join(import_os7.default.homedir(), ".codeam", "bin", "cloudflared");
16927
+ var CACHED_BINARY = import_path4.default.join(import_os8.default.homedir(), ".codeam", "bin", "cloudflared");
16164
16928
  async function waitForCloudflaredReady(url, timeoutMs = 6e4) {
16165
16929
  const hostname3 = new URL(url).hostname;
16166
16930
  const resolver = new import_promises.Resolver();
@@ -16447,7 +17211,7 @@ var pendingAttachmentFiles = /* @__PURE__ */ new Set();
16447
17211
  function cleanupAttachmentTempFiles() {
16448
17212
  for (const p2 of pendingAttachmentFiles) {
16449
17213
  try {
16450
- fs30.unlinkSync(p2);
17214
+ fs32.unlinkSync(p2);
16451
17215
  } catch {
16452
17216
  }
16453
17217
  }
@@ -16456,8 +17220,8 @@ function cleanupAttachmentTempFiles() {
16456
17220
  function saveFilesTemp(files) {
16457
17221
  return files.filter(({ base64 }) => base64 && base64.length > 0).map(({ filename, base64 }) => {
16458
17222
  const safeName = filename.replace(/[^a-zA-Z0-9._-]/g, "_").slice(0, 80);
16459
- const tmpPath = path36.join(os24.tmpdir(), `codeam-${(0, import_crypto5.randomUUID)()}-${safeName}`);
16460
- fs30.writeFileSync(tmpPath, Buffer.from(base64, "base64"));
17223
+ const tmpPath = path38.join(os25.tmpdir(), `codeam-${(0, import_crypto5.randomUUID)()}-${safeName}`);
17224
+ fs32.writeFileSync(tmpPath, Buffer.from(base64, "base64"));
16461
17225
  pendingAttachmentFiles.add(tmpPath);
16462
17226
  return tmpPath;
16463
17227
  });
@@ -16477,7 +17241,7 @@ var startTask = (ctx, _cmd, parsed) => {
16477
17241
  setTimeout(() => {
16478
17242
  for (const p2 of paths) {
16479
17243
  try {
16480
- fs30.unlinkSync(p2);
17244
+ fs32.unlinkSync(p2);
16481
17245
  } catch {
16482
17246
  }
16483
17247
  pendingAttachmentFiles.delete(p2);
@@ -16645,7 +17409,7 @@ var shutdownSession = async (ctx, cmd) => {
16645
17409
  ctx.relay.stop();
16646
17410
  process.exit(0);
16647
17411
  };
16648
- var readFile4 = async (ctx, cmd, parsed) => {
17412
+ var readFile5 = async (ctx, cmd, parsed) => {
16649
17413
  if (!parsed.path) {
16650
17414
  await ctx.relay.sendResult(cmd.id, "failed", { error: "Missing path" });
16651
17415
  return;
@@ -16653,7 +17417,7 @@ var readFile4 = async (ctx, cmd, parsed) => {
16653
17417
  const result = await readProjectFile(parsed.path);
16654
17418
  await ctx.relay.sendResult(cmd.id, "completed", result);
16655
17419
  };
16656
- var writeFile3 = async (ctx, cmd, parsed) => {
17420
+ var writeFile4 = async (ctx, cmd, parsed) => {
16657
17421
  if (!parsed.path || typeof parsed.content !== "string") {
16658
17422
  await ctx.relay.sendResult(cmd.id, "failed", { error: "Missing path or content" });
16659
17423
  return;
@@ -17104,8 +17868,8 @@ var previewStartH = (ctx, _cmd, parsed) => {
17104
17868
  let readyMatched = false;
17105
17869
  let expoUrl = null;
17106
17870
  const readyRe = new RegExp(detection.ready_pattern);
17107
- const onChunk = (chunk) => {
17108
- const s = chunk.toString();
17871
+ const onChunk = (chunk2) => {
17872
+ const s = chunk2.toString();
17109
17873
  if (!readyMatched && readyRe.test(s)) readyMatched = true;
17110
17874
  if (!expoUrl && detection.framework === "Expo") expoUrl = parseExpoUrl(s);
17111
17875
  };
@@ -17228,8 +17992,8 @@ var previewStartH = (ctx, _cmd, parsed) => {
17228
17992
  stdio: ["ignore", "pipe", "pipe"]
17229
17993
  });
17230
17994
  let parsedUrl = null;
17231
- const onTunnelChunk = (chunk) => {
17232
- const s = chunk.toString();
17995
+ const onTunnelChunk = (chunk2) => {
17996
+ const s = chunk2.toString();
17233
17997
  if (!parsedUrl) parsedUrl = parseCloudflaredUrl(s);
17234
17998
  const trimmed = s.replace(/\n+$/g, "");
17235
17999
  if (trimmed.length > 0) log.info("preview", `cloudflared: ${trimmed}`);
@@ -17321,22 +18085,22 @@ var previewStopH = (ctx) => {
17321
18085
  })();
17322
18086
  };
17323
18087
  function runOnce(cmd, args2, cwd, env) {
17324
- return new Promise((resolve5) => {
18088
+ return new Promise((resolve6) => {
17325
18089
  const child = (0, import_child_process15.spawn)(cmd, args2, {
17326
18090
  cwd,
17327
18091
  env: { ...process.env, ...env ?? {} },
17328
18092
  stdio: ["ignore", "pipe", "pipe"]
17329
18093
  });
17330
18094
  const tag = `setup:${cmd}`;
17331
- const onChunk = (chunk) => {
17332
- const text = chunk.toString().replace(/\n+$/g, "");
18095
+ const onChunk = (chunk2) => {
18096
+ const text = chunk2.toString().replace(/\n+$/g, "");
17333
18097
  if (text.length === 0) return;
17334
18098
  log.info("preview", `${tag}: ${text}`);
17335
18099
  };
17336
18100
  child.stdout?.on("data", onChunk);
17337
18101
  child.stderr?.on("data", onChunk);
17338
- child.once("exit", (code) => resolve5(code));
17339
- child.once("error", () => resolve5(-1));
18102
+ child.once("exit", (code) => resolve6(code));
18103
+ child.once("error", () => resolve6(-1));
17340
18104
  });
17341
18105
  }
17342
18106
  var savePreviewConfigH = (_ctx, _cmd, parsed) => {
@@ -17364,8 +18128,8 @@ var handlers = {
17364
18128
  set_keep_alive: setKeepAlive,
17365
18129
  session_terminated: sessionTerminated,
17366
18130
  shutdown_session: shutdownSession,
17367
- read_file: readFile4,
17368
- write_file: writeFile3,
18131
+ read_file: readFile5,
18132
+ write_file: writeFile4,
17369
18133
  list_files: listFiles,
17370
18134
  search_files: searchFilesH,
17371
18135
  terminal_open: terminalOpenH,
@@ -17441,6 +18205,23 @@ async function start(requestedAgent) {
17441
18205
  requestedAgent: requestedAgent ?? null
17442
18206
  });
17443
18207
  const cwd = process.cwd();
18208
+ if (process.env.CODEAM_ACP_ENABLED === "1" && session.pluginAuthToken) {
18209
+ const adapter = getAcpAdapter(session.agent);
18210
+ if (adapter) {
18211
+ await runAcpSession({
18212
+ agent: session.agent,
18213
+ sessionId: session.id,
18214
+ pluginId,
18215
+ pluginAuthToken: session.pluginAuthToken,
18216
+ adapter,
18217
+ cwd
18218
+ });
18219
+ return;
18220
+ }
18221
+ showInfo(
18222
+ `CODEAM_ACP_ENABLED is set but no ACP adapter is registered for "${session.agent}" \u2014 falling back to PTY runtime.`
18223
+ );
18224
+ }
17444
18225
  const runtime = createRuntimeStrategy(session.agent);
17445
18226
  const historySvc = new HistoryService(runtime, pluginId, cwd);
17446
18227
  const keepAliveCtx = {
@@ -17688,7 +18469,7 @@ async function pair(args2 = []) {
17688
18469
  waitSpin.message(waitMessage());
17689
18470
  }, 1e3);
17690
18471
  countdownInterval.unref?.();
17691
- await new Promise((resolve5) => {
18472
+ await new Promise((resolve6) => {
17692
18473
  let stopPolling = null;
17693
18474
  function sigintHandler() {
17694
18475
  clearInterval(countdownInterval);
@@ -17736,7 +18517,7 @@ async function pair(args2 = []) {
17736
18517
  pluginAuthToken: info.pluginAuthToken
17737
18518
  });
17738
18519
  }
17739
- resolve5();
18520
+ resolve6();
17740
18521
  },
17741
18522
  () => {
17742
18523
  clearInterval(countdownInterval);
@@ -17788,8 +18569,8 @@ async function autoLinkAfterPair(opts) {
17788
18569
  }
17789
18570
 
17790
18571
  // src/commands/pair-auto.ts
17791
- var fs31 = __toESM(require("fs"));
17792
- var os25 = __toESM(require("os"));
18572
+ var fs33 = __toESM(require("fs"));
18573
+ var os26 = __toESM(require("os"));
17793
18574
  var import_crypto7 = require("crypto");
17794
18575
 
17795
18576
  // src/commands/start-infra-only.ts
@@ -17957,12 +18738,12 @@ function readTokenFromArgs(args2) {
17957
18738
  }
17958
18739
  const fileFlag = args2.find((a) => a.startsWith("--token-file="));
17959
18740
  if (fileFlag) {
17960
- const path43 = fileFlag.slice("--token-file=".length);
18741
+ const path45 = fileFlag.slice("--token-file=".length);
17961
18742
  try {
17962
- const content = fs31.readFileSync(path43, "utf8").trim();
17963
- if (content.length === 0) fail(`--token-file ${path43} is empty`);
18743
+ const content = fs33.readFileSync(path45, "utf8").trim();
18744
+ if (content.length === 0) fail(`--token-file ${path45} is empty`);
17964
18745
  try {
17965
- fs31.unlinkSync(path43);
18746
+ fs33.unlinkSync(path45);
17966
18747
  } catch {
17967
18748
  }
17968
18749
  return content;
@@ -17988,7 +18769,7 @@ async function claimOnce(token, pluginId) {
17988
18769
  pluginId,
17989
18770
  ideName: "codeam-cli (codespace)",
17990
18771
  ideVersion: process.env.npm_package_version ?? "unknown",
17991
- hostname: os25.hostname(),
18772
+ hostname: os26.hostname(),
17992
18773
  codespaceName: process.env.CODESPACE_NAME ?? "",
17993
18774
  // Current git branch of the codespace's working directory, so the
17994
18775
  // backend can populate `PairedSession.branch` for the codespace pair.
@@ -18228,7 +19009,7 @@ var import_picocolors10 = __toESM(require("picocolors"));
18228
19009
  var import_child_process16 = require("child_process");
18229
19010
  var import_util4 = require("util");
18230
19011
  var import_picocolors8 = __toESM(require("picocolors"));
18231
- var path37 = __toESM(require("path"));
19012
+ var path39 = __toESM(require("path"));
18232
19013
  var execFileP5 = (0, import_util4.promisify)(import_child_process16.execFile);
18233
19014
  var MAX_BUFFER = 8 * 1024 * 1024;
18234
19015
  function resetStdinForChild() {
@@ -18272,12 +19053,12 @@ var GitHubCodespacesProvider = class {
18272
19053
  }
18273
19054
  if (!isAuthed) {
18274
19055
  resetStdinForChild();
18275
- await new Promise((resolve5, reject) => {
19056
+ await new Promise((resolve6, reject) => {
18276
19057
  const proc = (0, import_child_process16.spawn)("gh", ["auth", "login", "-s", "codespace,repo,read:user"], {
18277
19058
  stdio: "inherit"
18278
19059
  });
18279
19060
  proc.on("exit", (code) => {
18280
- if (code === 0) resolve5();
19061
+ if (code === 0) resolve6();
18281
19062
  else reject(new Error("gh auth login failed."));
18282
19063
  });
18283
19064
  proc.on("error", reject);
@@ -18306,13 +19087,13 @@ var GitHubCodespacesProvider = class {
18306
19087
  }
18307
19088
  wt(noteLines.join("\n"), "One more permission needed");
18308
19089
  resetStdinForChild();
18309
- const refreshCode = await new Promise((resolve5, reject) => {
19090
+ const refreshCode = await new Promise((resolve6, reject) => {
18310
19091
  const proc = (0, import_child_process16.spawn)(
18311
19092
  "gh",
18312
19093
  ["auth", "refresh", "-h", "github.com", "-s", "codespace"],
18313
19094
  { stdio: "inherit" }
18314
19095
  );
18315
- proc.on("exit", (code) => resolve5(code ?? 1));
19096
+ proc.on("exit", (code) => resolve6(code ?? 1));
18316
19097
  proc.on("error", reject);
18317
19098
  });
18318
19099
  if (refreshCode !== 0) {
@@ -18456,10 +19237,10 @@ var GitHubCodespacesProvider = class {
18456
19237
  if (q(proceed) || !proceed) return;
18457
19238
  O2.step(`Installing gh via ${installCmd.describe}\u2026`);
18458
19239
  resetStdinForChild();
18459
- const ok = await new Promise((resolve5) => {
19240
+ const ok = await new Promise((resolve6) => {
18460
19241
  const proc = (0, import_child_process16.spawn)(installCmd.exe, installCmd.args, { stdio: "inherit" });
18461
- proc.on("exit", (code) => resolve5(code === 0));
18462
- proc.on("error", () => resolve5(false));
19242
+ proc.on("exit", (code) => resolve6(code === 0));
19243
+ proc.on("error", () => resolve6(false));
18463
19244
  });
18464
19245
  if (ok) O2.success("gh installed");
18465
19246
  else O2.error("gh install failed");
@@ -18483,14 +19264,14 @@ var GitHubCodespacesProvider = class {
18483
19264
  "Expanding GitHub scopes"
18484
19265
  );
18485
19266
  resetStdinForChild();
18486
- await new Promise((resolve5, reject) => {
19267
+ await new Promise((resolve6, reject) => {
18487
19268
  const proc = (0, import_child_process16.spawn)(
18488
19269
  "gh",
18489
19270
  ["auth", "refresh", "-h", "github.com", "-s", "repo,read:org"],
18490
19271
  { stdio: "inherit" }
18491
19272
  );
18492
19273
  proc.on("exit", (code) => {
18493
- if (code === 0) resolve5();
19274
+ if (code === 0) resolve6();
18494
19275
  else reject(new Error(
18495
19276
  "gh auth refresh failed. Re-run `gh auth refresh -h github.com -s repo,read:org` manually."
18496
19277
  ));
@@ -18661,13 +19442,13 @@ var GitHubCodespacesProvider = class {
18661
19442
  }
18662
19443
  async streamCommand(workspaceId, command2) {
18663
19444
  resetStdinForChild();
18664
- return new Promise((resolve5, reject) => {
19445
+ return new Promise((resolve6, reject) => {
18665
19446
  const proc = (0, import_child_process16.spawn)(
18666
19447
  "gh",
18667
19448
  ["codespace", "ssh", "-c", workspaceId, "--", "-tt", command2],
18668
19449
  { stdio: "inherit" }
18669
19450
  );
18670
- proc.on("exit", (code) => resolve5({ code: code ?? 0 }));
19451
+ proc.on("exit", (code) => resolve6({ code: code ?? 0 }));
18671
19452
  proc.on("error", reject);
18672
19453
  });
18673
19454
  }
@@ -18688,7 +19469,7 @@ var GitHubCodespacesProvider = class {
18688
19469
  "--",
18689
19470
  `mkdir -p ${shellQuote(remoteDir)} && tar -xzf - -C ${shellQuote(remoteDir)}`
18690
19471
  ];
18691
- await new Promise((resolve5, reject) => {
19472
+ await new Promise((resolve6, reject) => {
18692
19473
  const tar = (0, import_child_process16.spawn)("tar", tarArgs, {
18693
19474
  stdio: ["ignore", "pipe", "pipe"],
18694
19475
  env: tarEnv
@@ -18708,7 +19489,7 @@ var GitHubCodespacesProvider = class {
18708
19489
  ssh.on("error", reject);
18709
19490
  ssh.on("exit", (code) => {
18710
19491
  if (code === 0) {
18711
- resolve5();
19492
+ resolve6();
18712
19493
  } else {
18713
19494
  const reason = (sshErr || tarErr || `exit ${code}`).trim().slice(0, 500);
18714
19495
  reject(new Error(`Remote tar failed: ${reason}`));
@@ -18717,7 +19498,7 @@ var GitHubCodespacesProvider = class {
18717
19498
  });
18718
19499
  }
18719
19500
  async uploadFile(workspaceId, remotePath, contents, options = {}) {
18720
- const remoteDir = path37.posix.dirname(remotePath);
19501
+ const remoteDir = path39.posix.dirname(remotePath);
18721
19502
  const parts = [
18722
19503
  `mkdir -p ${shellQuote(remoteDir)}`,
18723
19504
  `cat > ${shellQuote(remotePath)}`
@@ -18726,7 +19507,7 @@ var GitHubCodespacesProvider = class {
18726
19507
  parts.push(`chmod ${options.mode.toString(8)} ${shellQuote(remotePath)}`);
18727
19508
  }
18728
19509
  const cmd = parts.join(" && ");
18729
- await new Promise((resolve5, reject) => {
19510
+ await new Promise((resolve6, reject) => {
18730
19511
  const proc = (0, import_child_process16.spawn)(
18731
19512
  "gh",
18732
19513
  ["codespace", "ssh", "-c", workspaceId, "--", cmd],
@@ -18738,7 +19519,7 @@ var GitHubCodespacesProvider = class {
18738
19519
  });
18739
19520
  proc.on("error", reject);
18740
19521
  proc.on("exit", (code) => {
18741
- if (code === 0) resolve5();
19522
+ if (code === 0) resolve6();
18742
19523
  else reject(new Error(`Remote write failed: ${(stderr || `exit ${code}`).trim().slice(0, 500)}`));
18743
19524
  });
18744
19525
  proc.stdin?.write(contents);
@@ -18787,7 +19568,7 @@ function shellQuote(s) {
18787
19568
  // src/services/providers/gitpod.ts
18788
19569
  var import_child_process17 = require("child_process");
18789
19570
  var import_util5 = require("util");
18790
- var path38 = __toESM(require("path"));
19571
+ var path40 = __toESM(require("path"));
18791
19572
  var import_picocolors9 = __toESM(require("picocolors"));
18792
19573
  var execFileP6 = (0, import_util5.promisify)(import_child_process17.execFile);
18793
19574
  var MAX_BUFFER2 = 8 * 1024 * 1024;
@@ -18828,10 +19609,10 @@ var GitpodProvider = class {
18828
19609
  "Authenticating Gitpod"
18829
19610
  );
18830
19611
  resetStdinForChild2();
18831
- await new Promise((resolve5, reject) => {
19612
+ await new Promise((resolve6, reject) => {
18832
19613
  const proc = (0, import_child_process17.spawn)("gitpod", ["login"], { stdio: "inherit" });
18833
19614
  proc.on("exit", (code) => {
18834
- if (code === 0) resolve5();
19615
+ if (code === 0) resolve6();
18835
19616
  else reject(new Error("gitpod login failed."));
18836
19617
  });
18837
19618
  proc.on("error", reject);
@@ -18980,13 +19761,13 @@ var GitpodProvider = class {
18980
19761
  }
18981
19762
  async streamCommand(workspaceId, command2) {
18982
19763
  resetStdinForChild2();
18983
- return new Promise((resolve5, reject) => {
19764
+ return new Promise((resolve6, reject) => {
18984
19765
  const proc = (0, import_child_process17.spawn)(
18985
19766
  "gitpod",
18986
19767
  ["workspace", "ssh", workspaceId, "--", "-tt", command2],
18987
19768
  { stdio: "inherit" }
18988
19769
  );
18989
- proc.on("exit", (code) => resolve5({ code: code ?? 0 }));
19770
+ proc.on("exit", (code) => resolve6({ code: code ?? 0 }));
18990
19771
  proc.on("error", reject);
18991
19772
  });
18992
19773
  }
@@ -19000,7 +19781,7 @@ var GitpodProvider = class {
19000
19781
  tarArgs.push(".");
19001
19782
  const tarEnv = { ...process.env, COPYFILE_DISABLE: "1" };
19002
19783
  const remoteCmd = `mkdir -p ${shellQuote2(remoteDir)} && tar -xzf - -C ${shellQuote2(remoteDir)}`;
19003
- await new Promise((resolve5, reject) => {
19784
+ await new Promise((resolve6, reject) => {
19004
19785
  const tar = (0, import_child_process17.spawn)("tar", tarArgs, {
19005
19786
  stdio: ["ignore", "pipe", "pipe"],
19006
19787
  env: tarEnv
@@ -19021,13 +19802,13 @@ var GitpodProvider = class {
19021
19802
  tar.on("error", reject);
19022
19803
  ssh.on("error", reject);
19023
19804
  ssh.on("exit", (code) => {
19024
- if (code === 0) resolve5();
19805
+ if (code === 0) resolve6();
19025
19806
  else reject(new Error(`Remote tar failed: ${(sshErr || tarErr || `exit ${code}`).trim().slice(0, 500)}`));
19026
19807
  });
19027
19808
  });
19028
19809
  }
19029
19810
  async uploadFile(workspaceId, remotePath, contents, options = {}) {
19030
- const remoteDir = path38.posix.dirname(remotePath);
19811
+ const remoteDir = path40.posix.dirname(remotePath);
19031
19812
  const parts = [
19032
19813
  `mkdir -p ${shellQuote2(remoteDir)}`,
19033
19814
  `cat > ${shellQuote2(remotePath)}`
@@ -19036,7 +19817,7 @@ var GitpodProvider = class {
19036
19817
  parts.push(`chmod ${options.mode.toString(8)} ${shellQuote2(remotePath)}`);
19037
19818
  }
19038
19819
  const cmd = parts.join(" && ");
19039
- await new Promise((resolve5, reject) => {
19820
+ await new Promise((resolve6, reject) => {
19040
19821
  const proc = (0, import_child_process17.spawn)(
19041
19822
  "gitpod",
19042
19823
  ["workspace", "ssh", workspaceId, "--", cmd],
@@ -19048,7 +19829,7 @@ var GitpodProvider = class {
19048
19829
  });
19049
19830
  proc.on("error", reject);
19050
19831
  proc.on("exit", (code) => {
19051
- if (code === 0) resolve5();
19832
+ if (code === 0) resolve6();
19052
19833
  else reject(new Error(`Remote write failed: ${(stderr || `exit ${code}`).trim().slice(0, 500)}`));
19053
19834
  });
19054
19835
  proc.stdin?.write(contents);
@@ -19063,7 +19844,7 @@ function shellQuote2(s) {
19063
19844
  // src/services/providers/gitlab-workspaces.ts
19064
19845
  var import_child_process18 = require("child_process");
19065
19846
  var import_util6 = require("util");
19066
- var path39 = __toESM(require("path"));
19847
+ var path41 = __toESM(require("path"));
19067
19848
  var execFileP7 = (0, import_util6.promisify)(import_child_process18.execFile);
19068
19849
  var MAX_BUFFER3 = 8 * 1024 * 1024;
19069
19850
  var GITLAB_API_BASE = process.env.CODEAM_GITLAB_API_URL ?? "https://gitlab.com/api/v4";
@@ -19105,14 +19886,14 @@ var GitLabWorkspacesProvider = class {
19105
19886
  "Authenticating GitLab"
19106
19887
  );
19107
19888
  resetStdinForChild3();
19108
- await new Promise((resolve5, reject) => {
19889
+ await new Promise((resolve6, reject) => {
19109
19890
  const proc = (0, import_child_process18.spawn)(
19110
19891
  "glab",
19111
19892
  ["auth", "login", "--scopes", "api,read_user,read_repository"],
19112
19893
  { stdio: "inherit" }
19113
19894
  );
19114
19895
  proc.on("exit", (code) => {
19115
- if (code === 0) resolve5();
19896
+ if (code === 0) resolve6();
19116
19897
  else reject(new Error("glab auth login failed."));
19117
19898
  });
19118
19899
  proc.on("error", reject);
@@ -19277,13 +20058,13 @@ Docs: https://docs.gitlab.com/ee/user/workspace/configuration.html`
19277
20058
  async streamCommand(workspaceId, command2) {
19278
20059
  const sshHost = process.env.CODEAM_GITLAB_SSH_HOST ?? "workspaces.gitlab.com";
19279
20060
  resetStdinForChild3();
19280
- return new Promise((resolve5, reject) => {
20061
+ return new Promise((resolve6, reject) => {
19281
20062
  const proc = (0, import_child_process18.spawn)(
19282
20063
  "ssh",
19283
20064
  ["-tt", "-o", "StrictHostKeyChecking=accept-new", `${workspaceId}@${sshHost}`, command2],
19284
20065
  { stdio: "inherit" }
19285
20066
  );
19286
- proc.on("exit", (code) => resolve5({ code: code ?? 0 }));
20067
+ proc.on("exit", (code) => resolve6({ code: code ?? 0 }));
19287
20068
  proc.on("error", reject);
19288
20069
  });
19289
20070
  }
@@ -19298,7 +20079,7 @@ Docs: https://docs.gitlab.com/ee/user/workspace/configuration.html`
19298
20079
  tarArgs.push(".");
19299
20080
  const tarEnv = { ...process.env, COPYFILE_DISABLE: "1" };
19300
20081
  const remoteCmd = `mkdir -p ${shellQuote3(remoteDir)} && tar -xzf - -C ${shellQuote3(remoteDir)}`;
19301
- await new Promise((resolve5, reject) => {
20082
+ await new Promise((resolve6, reject) => {
19302
20083
  const tar = (0, import_child_process18.spawn)("tar", tarArgs, { stdio: ["ignore", "pipe", "pipe"], env: tarEnv });
19303
20084
  const ssh = (0, import_child_process18.spawn)(
19304
20085
  "ssh",
@@ -19316,20 +20097,20 @@ Docs: https://docs.gitlab.com/ee/user/workspace/configuration.html`
19316
20097
  tar.on("error", reject);
19317
20098
  ssh.on("error", reject);
19318
20099
  ssh.on("exit", (code) => {
19319
- if (code === 0) resolve5();
20100
+ if (code === 0) resolve6();
19320
20101
  else reject(new Error(`Remote tar failed: ${(sshErr || tarErr || `exit ${code}`).trim().slice(0, 500)}`));
19321
20102
  });
19322
20103
  });
19323
20104
  }
19324
20105
  async uploadFile(workspaceId, remotePath, contents, options = {}) {
19325
20106
  const sshHost = process.env.CODEAM_GITLAB_SSH_HOST ?? "workspaces.gitlab.com";
19326
- const remoteDir = path39.posix.dirname(remotePath);
20107
+ const remoteDir = path41.posix.dirname(remotePath);
19327
20108
  const parts = [`mkdir -p ${shellQuote3(remoteDir)}`, `cat > ${shellQuote3(remotePath)}`];
19328
20109
  if (options.mode != null) {
19329
20110
  parts.push(`chmod ${options.mode.toString(8)} ${shellQuote3(remotePath)}`);
19330
20111
  }
19331
20112
  const cmd = parts.join(" && ");
19332
- await new Promise((resolve5, reject) => {
20113
+ await new Promise((resolve6, reject) => {
19333
20114
  const proc = (0, import_child_process18.spawn)(
19334
20115
  "ssh",
19335
20116
  ["-o", "StrictHostKeyChecking=accept-new", `${workspaceId}@${sshHost}`, cmd],
@@ -19341,7 +20122,7 @@ Docs: https://docs.gitlab.com/ee/user/workspace/configuration.html`
19341
20122
  });
19342
20123
  proc.on("error", reject);
19343
20124
  proc.on("exit", (code) => {
19344
- if (code === 0) resolve5();
20125
+ if (code === 0) resolve6();
19345
20126
  else reject(new Error(`Remote write failed: ${(stderr || `exit ${code}`).trim().slice(0, 500)}`));
19346
20127
  });
19347
20128
  proc.stdin?.write(contents);
@@ -19391,7 +20172,7 @@ function shellQuote3(s) {
19391
20172
  // src/services/providers/railway.ts
19392
20173
  var import_child_process19 = require("child_process");
19393
20174
  var import_util7 = require("util");
19394
- var path40 = __toESM(require("path"));
20175
+ var path42 = __toESM(require("path"));
19395
20176
  var execFileP8 = (0, import_util7.promisify)(import_child_process19.execFile);
19396
20177
  var MAX_BUFFER4 = 8 * 1024 * 1024;
19397
20178
  function resetStdinForChild4() {
@@ -19432,10 +20213,10 @@ var RailwayProvider = class {
19432
20213
  "Authenticating Railway"
19433
20214
  );
19434
20215
  resetStdinForChild4();
19435
- await new Promise((resolve5, reject) => {
20216
+ await new Promise((resolve6, reject) => {
19436
20217
  const proc = (0, import_child_process19.spawn)("railway", ["login"], { stdio: "inherit" });
19437
20218
  proc.on("exit", (code) => {
19438
- if (code === 0) resolve5();
20219
+ if (code === 0) resolve6();
19439
20220
  else reject(new Error("railway login failed."));
19440
20221
  });
19441
20222
  proc.on("error", reject);
@@ -19575,13 +20356,13 @@ var RailwayProvider = class {
19575
20356
  throw new Error("Invalid Railway workspace id (expected projectId/serviceId).");
19576
20357
  }
19577
20358
  resetStdinForChild4();
19578
- return new Promise((resolve5, reject) => {
20359
+ return new Promise((resolve6, reject) => {
19579
20360
  const proc = (0, import_child_process19.spawn)(
19580
20361
  "railway",
19581
20362
  ["shell", "--project", projectId, "--service", serviceId, "--command", command2],
19582
20363
  { stdio: "inherit" }
19583
20364
  );
19584
- proc.on("exit", (code) => resolve5({ code: code ?? 0 }));
20365
+ proc.on("exit", (code) => resolve6({ code: code ?? 0 }));
19585
20366
  proc.on("error", reject);
19586
20367
  });
19587
20368
  }
@@ -19599,7 +20380,7 @@ var RailwayProvider = class {
19599
20380
  tarArgs.push(".");
19600
20381
  const tarEnv = { ...process.env, COPYFILE_DISABLE: "1" };
19601
20382
  const remoteCmd = `mkdir -p ${shellQuote4(remoteDir)} && tar -xzf - -C ${shellQuote4(remoteDir)}`;
19602
- await new Promise((resolve5, reject) => {
20383
+ await new Promise((resolve6, reject) => {
19603
20384
  const tar = (0, import_child_process19.spawn)("tar", tarArgs, { stdio: ["ignore", "pipe", "pipe"], env: tarEnv });
19604
20385
  const sh = (0, import_child_process19.spawn)(
19605
20386
  "railway",
@@ -19617,7 +20398,7 @@ var RailwayProvider = class {
19617
20398
  tar.on("error", reject);
19618
20399
  sh.on("error", reject);
19619
20400
  sh.on("exit", (code) => {
19620
- if (code === 0) resolve5();
20401
+ if (code === 0) resolve6();
19621
20402
  else reject(new Error(`Remote tar failed: ${(shErr || tarErr || `exit ${code}`).trim().slice(0, 500)}`));
19622
20403
  });
19623
20404
  });
@@ -19627,13 +20408,13 @@ var RailwayProvider = class {
19627
20408
  if (!projectId || !serviceId) {
19628
20409
  throw new Error("Invalid Railway workspace id (expected projectId/serviceId).");
19629
20410
  }
19630
- const remoteDir = path40.posix.dirname(remotePath);
20411
+ const remoteDir = path42.posix.dirname(remotePath);
19631
20412
  const parts = [`mkdir -p ${shellQuote4(remoteDir)}`, `cat > ${shellQuote4(remotePath)}`];
19632
20413
  if (options.mode != null) {
19633
20414
  parts.push(`chmod ${options.mode.toString(8)} ${shellQuote4(remotePath)}`);
19634
20415
  }
19635
20416
  const cmd = parts.join(" && ");
19636
- await new Promise((resolve5, reject) => {
20417
+ await new Promise((resolve6, reject) => {
19637
20418
  const proc = (0, import_child_process19.spawn)(
19638
20419
  "railway",
19639
20420
  ["shell", "--project", projectId, "--service", serviceId, "--command", cmd],
@@ -19645,7 +20426,7 @@ var RailwayProvider = class {
19645
20426
  });
19646
20427
  proc.on("error", reject);
19647
20428
  proc.on("exit", (code) => {
19648
- if (code === 0) resolve5();
20429
+ if (code === 0) resolve6();
19649
20430
  else reject(new Error(`Remote write failed: ${(stderr || `exit ${code}`).trim().slice(0, 500)}`));
19650
20431
  });
19651
20432
  proc.stdin?.write(contents);
@@ -20169,9 +20950,9 @@ async function stopWorkspaceFromLocal(target) {
20169
20950
  // src/commands/doctor.ts
20170
20951
  var import_node_dns = require("dns");
20171
20952
  var import_node_util4 = require("util");
20172
- var import_node_crypto6 = require("crypto");
20173
- var fs32 = __toESM(require("fs"));
20174
- var path41 = __toESM(require("path"));
20953
+ var import_node_crypto8 = require("crypto");
20954
+ var fs34 = __toESM(require("fs"));
20955
+ var path43 = __toESM(require("path"));
20175
20956
  var import_picocolors12 = __toESM(require("picocolors"));
20176
20957
  var dnsResolveP = (0, import_node_util4.promisify)(import_node_dns.resolve);
20177
20958
  async function checkDns(apiBase) {
@@ -20227,13 +21008,13 @@ async function checkHealth(apiBase) {
20227
21008
  }
20228
21009
  }
20229
21010
  function checkConfigDir() {
20230
- const dir = path41.join(require("os").homedir(), ".codeam");
21011
+ const dir = path43.join(require("os").homedir(), ".codeam");
20231
21012
  try {
20232
- fs32.mkdirSync(dir, { recursive: true, mode: 448 });
20233
- const probe = path41.join(dir, ".doctor-probe");
20234
- fs32.writeFileSync(probe, "ok", { mode: 384 });
20235
- const read = fs32.readFileSync(probe, "utf8");
20236
- fs32.unlinkSync(probe);
21013
+ fs34.mkdirSync(dir, { recursive: true, mode: 448 });
21014
+ const probe = path43.join(dir, ".doctor-probe");
21015
+ fs34.writeFileSync(probe, "ok", { mode: 384 });
21016
+ const read = fs34.readFileSync(probe, "utf8");
21017
+ fs34.unlinkSync(probe);
20237
21018
  if (read !== "ok") throw new Error("write/read round-trip mismatch");
20238
21019
  return {
20239
21020
  id: "config-dir",
@@ -20273,9 +21054,9 @@ function checkSessions() {
20273
21054
  }
20274
21055
  }
20275
21056
  function checkAgentBinaries() {
20276
- const os27 = createOsStrategy();
21057
+ const os28 = createOsStrategy();
20277
21058
  return getEnabledAgents().map((meta) => {
20278
- const found = os27.findInPath(meta.binaryName);
21059
+ const found = os28.findInPath(meta.binaryName);
20279
21060
  return {
20280
21061
  id: `agent-${meta.id}`,
20281
21062
  label: `Agent binary: ${meta.displayName} (${meta.binaryName})`,
@@ -20297,7 +21078,7 @@ function checkNodePty() {
20297
21078
  detail: "not required on this platform"
20298
21079
  };
20299
21080
  }
20300
- const vendoredPath = path41.join(__dirname, "vendor", "node-pty");
21081
+ const vendoredPath = path43.join(__dirname, "vendor", "node-pty");
20301
21082
  for (const target of [vendoredPath, "node-pty"]) {
20302
21083
  try {
20303
21084
  require(target);
@@ -20339,9 +21120,9 @@ function checkChokidar() {
20339
21120
  }
20340
21121
  async function doctor(args2 = []) {
20341
21122
  const json = args2.includes("--json");
20342
- const cliVersion = true ? "2.27.1" : "0.0.0-dev";
21123
+ const cliVersion = true ? "2.27.3" : "0.0.0-dev";
20343
21124
  const apiBase = resolveApiBaseUrl();
20344
- const diagnosticId = (0, import_node_crypto6.randomUUID)();
21125
+ const diagnosticId = (0, import_node_crypto8.randomUUID)();
20345
21126
  log.info("doctor", `run id=${diagnosticId} cli=${cliVersion}`);
20346
21127
  const [dns, health] = await Promise.all([
20347
21128
  checkDns(apiBase),
@@ -20538,7 +21319,7 @@ async function completion(args2) {
20538
21319
  // src/commands/version.ts
20539
21320
  var import_picocolors13 = __toESM(require("picocolors"));
20540
21321
  function version2() {
20541
- const v = true ? "2.27.1" : "unknown";
21322
+ const v = true ? "2.27.3" : "unknown";
20542
21323
  console.log(`${import_picocolors13.default.bold("codeam-cli")} ${import_picocolors13.default.cyan(v)}`);
20543
21324
  }
20544
21325
 
@@ -20666,9 +21447,9 @@ function tryShowSubcommandHelp(cmd, args2) {
20666
21447
  var _subcommandHelpKeys = Object.keys(HELPS);
20667
21448
 
20668
21449
  // src/lib/updateNotifier.ts
20669
- var fs33 = __toESM(require("fs"));
20670
- var os26 = __toESM(require("os"));
20671
- var path42 = __toESM(require("path"));
21450
+ var fs35 = __toESM(require("fs"));
21451
+ var os27 = __toESM(require("os"));
21452
+ var path44 = __toESM(require("path"));
20672
21453
  var https7 = __toESM(require("https"));
20673
21454
  var import_picocolors16 = __toESM(require("picocolors"));
20674
21455
  var PKG_NAME = "codeam-cli";
@@ -20676,12 +21457,12 @@ var REGISTRY_URL = `https://registry.npmjs.org/${PKG_NAME}/latest`;
20676
21457
  var TTL_MS = 24 * 60 * 60 * 1e3;
20677
21458
  var REQUEST_TIMEOUT_MS = 1500;
20678
21459
  function cachePath() {
20679
- const dir = path42.join(os26.homedir(), ".codeam");
20680
- return path42.join(dir, "update-check.json");
21460
+ const dir = path44.join(os27.homedir(), ".codeam");
21461
+ return path44.join(dir, "update-check.json");
20681
21462
  }
20682
21463
  function readCache() {
20683
21464
  try {
20684
- const raw = fs33.readFileSync(cachePath(), "utf8");
21465
+ const raw = fs35.readFileSync(cachePath(), "utf8");
20685
21466
  const parsed = JSON.parse(raw);
20686
21467
  if (typeof parsed.fetchedAt !== "number" || typeof parsed.latest !== "string") return null;
20687
21468
  return parsed;
@@ -20692,10 +21473,10 @@ function readCache() {
20692
21473
  function writeCache(cache) {
20693
21474
  try {
20694
21475
  const file = cachePath();
20695
- fs33.mkdirSync(path42.dirname(file), { recursive: true });
21476
+ fs35.mkdirSync(path44.dirname(file), { recursive: true });
20696
21477
  const tmp = `${file}.${process.pid}.tmp`;
20697
- fs33.writeFileSync(tmp, JSON.stringify(cache));
20698
- fs33.renameSync(tmp, file);
21478
+ fs35.writeFileSync(tmp, JSON.stringify(cache));
21479
+ fs35.renameSync(tmp, file);
20699
21480
  } catch {
20700
21481
  }
20701
21482
  }
@@ -20713,40 +21494,40 @@ function compareSemver(a, b) {
20713
21494
  return 0;
20714
21495
  }
20715
21496
  function fetchLatest() {
20716
- return new Promise((resolve5) => {
21497
+ return new Promise((resolve6) => {
20717
21498
  const req = https7.get(
20718
21499
  REGISTRY_URL,
20719
21500
  { headers: { Accept: "application/json" }, timeout: REQUEST_TIMEOUT_MS },
20720
21501
  (res) => {
20721
21502
  if (res.statusCode !== 200) {
20722
21503
  res.resume();
20723
- resolve5(null);
21504
+ resolve6(null);
20724
21505
  return;
20725
21506
  }
20726
21507
  let buf = "";
20727
21508
  res.setEncoding("utf8");
20728
- res.on("data", (chunk) => {
20729
- buf += chunk;
21509
+ res.on("data", (chunk2) => {
21510
+ buf += chunk2;
20730
21511
  });
20731
21512
  res.on("end", () => {
20732
21513
  try {
20733
21514
  const json = JSON.parse(buf);
20734
21515
  if (typeof json.version === "string") {
20735
- resolve5(json.version);
21516
+ resolve6(json.version);
20736
21517
  } else {
20737
- resolve5(null);
21518
+ resolve6(null);
20738
21519
  }
20739
21520
  } catch {
20740
- resolve5(null);
21521
+ resolve6(null);
20741
21522
  }
20742
21523
  });
20743
21524
  }
20744
21525
  );
20745
21526
  req.on("timeout", () => {
20746
21527
  req.destroy();
20747
- resolve5(null);
21528
+ resolve6(null);
20748
21529
  });
20749
- req.on("error", () => resolve5(null));
21530
+ req.on("error", () => resolve6(null));
20750
21531
  });
20751
21532
  }
20752
21533
  function notifyIfStale(currentVersion, latest) {
@@ -20766,7 +21547,7 @@ function checkForUpdates() {
20766
21547
  if (process.env.CODEAM_DISABLE_UPDATE_CHECK === "1") return;
20767
21548
  if (process.env.CI) return;
20768
21549
  if (!process.stdout.isTTY) return;
20769
- const current = true ? "2.27.1" : null;
21550
+ const current = true ? "2.27.3" : null;
20770
21551
  if (!current) return;
20771
21552
  const cache = readCache();
20772
21553
  const fresh = cache && Date.now() - cache.fetchedAt < TTL_MS;