codeam-cli 2.39.23 → 2.39.25

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 +16 -0
  2. package/dist/index.js +688 -961
  3. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -498,7 +498,7 @@ var import_qrcode_terminal = __toESM(require("qrcode-terminal"));
498
498
  // package.json
499
499
  var package_default = {
500
500
  name: "codeam-cli",
501
- version: "2.39.23",
501
+ version: "2.39.25",
502
502
  description: "Workflow-continuity bridge for AI coding agents. Wrap Claude Code or Codex in a PTY and supervise, approve, and redirect the session from any device \u2014 async. The terminal companion for CodeAgent Mobile.",
503
503
  type: "commonjs",
504
504
  main: "dist/index.js",
@@ -1194,8 +1194,8 @@ function createGetModuleFromFilename(basePath = process.argv[1] ? (0, import_pat
1194
1194
  return decodedFile;
1195
1195
  };
1196
1196
  }
1197
- function normalizeWindowsPath(path55) {
1198
- return path55.replace(/^[A-Z]:/, "").replace(/\\/g, "/");
1197
+ function normalizeWindowsPath(path58) {
1198
+ return path58.replace(/^[A-Z]:/, "").replace(/\\/g, "/");
1199
1199
  }
1200
1200
 
1201
1201
  // ../../node_modules/@posthog/core/dist/featureFlagUtils.mjs
@@ -2347,10 +2347,10 @@ var PostHogCoreStateless = class {
2347
2347
  }
2348
2348
  async getRemoteConfig() {
2349
2349
  await this._initPromise;
2350
- let host = this.host;
2351
- if ("https://us.i.posthog.com" === host) host = "https://us-assets.i.posthog.com";
2352
- else if ("https://eu.i.posthog.com" === host) host = "https://eu-assets.i.posthog.com";
2353
- const url = `${host}/array/${this.apiKey}/config`;
2350
+ let host2 = this.host;
2351
+ if ("https://us.i.posthog.com" === host2) host2 = "https://us-assets.i.posthog.com";
2352
+ else if ("https://eu.i.posthog.com" === host2) host2 = "https://eu-assets.i.posthog.com";
2353
+ const url = `${host2}/array/${this.apiKey}/config`;
2354
2354
  const fetchOptions = {
2355
2355
  method: "GET",
2356
2356
  headers: {
@@ -3030,9 +3030,9 @@ var ErrorPropertiesBuilder = class {
3030
3030
 
3031
3031
  // ../../node_modules/@posthog/core/dist/error-tracking/parsers/base.mjs
3032
3032
  var UNKNOWN_FUNCTION = "?";
3033
- function createFrame(platform2, filename, func, lineno, colno) {
3033
+ function createFrame(platform3, filename, func, lineno, colno) {
3034
3034
  const frame = {
3035
- platform: platform2,
3035
+ platform: platform3,
3036
3036
  filename,
3037
3037
  function: "<anonymous>" === func ? UNKNOWN_FUNCTION : func,
3038
3038
  in_app: true
@@ -3059,11 +3059,11 @@ var extractSafariExtensionDetails = (func, filename) => {
3059
3059
  var chromeRegexNoFnName = /^\s*at (\S+?)(?::(\d+))(?::(\d+))\s*$/i;
3060
3060
  var chromeRegex = /^\s*at (?:(.+?\)(?: \[.+\])?|.*?) ?\((?:address at )?)?(?:async )?((?:<anonymous>|[-a-z]+:|.*bundle|\/)?.*?)(?::(\d+))?(?::(\d+))?\)?\s*$/i;
3061
3061
  var chromeEvalRegex = /\((\S*)(?::(\d+))(?::(\d+))\)/;
3062
- var chromeStackLineParser = (line, platform2) => {
3062
+ var chromeStackLineParser = (line, platform3) => {
3063
3063
  const noFnParts = chromeRegexNoFnName.exec(line);
3064
3064
  if (noFnParts) {
3065
3065
  const [, filename, line2, col] = noFnParts;
3066
- return createFrame(platform2, filename, UNKNOWN_FUNCTION, +line2, +col);
3066
+ return createFrame(platform3, filename, UNKNOWN_FUNCTION, +line2, +col);
3067
3067
  }
3068
3068
  const parts = chromeRegex.exec(line);
3069
3069
  if (parts) {
@@ -3077,14 +3077,14 @@ var chromeStackLineParser = (line, platform2) => {
3077
3077
  }
3078
3078
  }
3079
3079
  const [func, filename] = extractSafariExtensionDetails(parts[1] || UNKNOWN_FUNCTION, parts[2]);
3080
- return createFrame(platform2, filename, func, parts[3] ? +parts[3] : void 0, parts[4] ? +parts[4] : void 0);
3080
+ return createFrame(platform3, filename, func, parts[3] ? +parts[3] : void 0, parts[4] ? +parts[4] : void 0);
3081
3081
  }
3082
3082
  };
3083
3083
 
3084
3084
  // ../../node_modules/@posthog/core/dist/error-tracking/parsers/gecko.mjs
3085
3085
  var geckoREgex = /^\s*(.*?)(?:\((.*?)\))?(?:^|@)?((?:[-a-z]+)?:\/.*?|\[native code\]|[^@]*(?:bundle|\d+\.js)|\/[\w\-. /=]+)(?::(\d+))?(?::(\d+))?\s*$/i;
3086
3086
  var geckoEvalRegex = /(\S+) line (\d+)(?: > eval line \d+)* > eval/i;
3087
- var geckoStackLineParser = (line, platform2) => {
3087
+ var geckoStackLineParser = (line, platform3) => {
3088
3088
  const parts = geckoREgex.exec(line);
3089
3089
  if (parts) {
3090
3090
  const isEval = parts[3] && parts[3].indexOf(" > eval") > -1;
@@ -3100,33 +3100,33 @@ var geckoStackLineParser = (line, platform2) => {
3100
3100
  let filename = parts[3];
3101
3101
  let func = parts[1] || UNKNOWN_FUNCTION;
3102
3102
  [func, filename] = extractSafariExtensionDetails(func, filename);
3103
- return createFrame(platform2, filename, func, parts[4] ? +parts[4] : void 0, parts[5] ? +parts[5] : void 0);
3103
+ return createFrame(platform3, filename, func, parts[4] ? +parts[4] : void 0, parts[5] ? +parts[5] : void 0);
3104
3104
  }
3105
3105
  };
3106
3106
 
3107
3107
  // ../../node_modules/@posthog/core/dist/error-tracking/parsers/winjs.mjs
3108
3108
  var winjsRegex = /^\s*at (?:((?:\[object object\])?.+) )?\(?((?:[-a-z]+):.*?):(\d+)(?::(\d+))?\)?\s*$/i;
3109
- var winjsStackLineParser = (line, platform2) => {
3109
+ var winjsStackLineParser = (line, platform3) => {
3110
3110
  const parts = winjsRegex.exec(line);
3111
- return parts ? createFrame(platform2, parts[2], parts[1] || UNKNOWN_FUNCTION, +parts[3], parts[4] ? +parts[4] : void 0) : void 0;
3111
+ return parts ? createFrame(platform3, parts[2], parts[1] || UNKNOWN_FUNCTION, +parts[3], parts[4] ? +parts[4] : void 0) : void 0;
3112
3112
  };
3113
3113
 
3114
3114
  // ../../node_modules/@posthog/core/dist/error-tracking/parsers/opera.mjs
3115
3115
  var opera10Regex = / line (\d+).*script (?:in )?(\S+)(?:: in function (\S+))?$/i;
3116
- var opera10StackLineParser = (line, platform2) => {
3116
+ var opera10StackLineParser = (line, platform3) => {
3117
3117
  const parts = opera10Regex.exec(line);
3118
- return parts ? createFrame(platform2, parts[2], parts[3] || UNKNOWN_FUNCTION, +parts[1]) : void 0;
3118
+ return parts ? createFrame(platform3, parts[2], parts[3] || UNKNOWN_FUNCTION, +parts[1]) : void 0;
3119
3119
  };
3120
3120
  var opera11Regex = / line (\d+), column (\d+)\s*(?:in (?:<anonymous function: ([^>]+)>|([^)]+))\(.*\))? in (.*):\s*$/i;
3121
- var opera11StackLineParser = (line, platform2) => {
3121
+ var opera11StackLineParser = (line, platform3) => {
3122
3122
  const parts = opera11Regex.exec(line);
3123
- return parts ? createFrame(platform2, parts[5], parts[3] || parts[4] || UNKNOWN_FUNCTION, +parts[1], +parts[2]) : void 0;
3123
+ return parts ? createFrame(platform3, parts[5], parts[3] || parts[4] || UNKNOWN_FUNCTION, +parts[1], +parts[2]) : void 0;
3124
3124
  };
3125
3125
 
3126
3126
  // ../../node_modules/@posthog/core/dist/error-tracking/parsers/node.mjs
3127
3127
  var FILENAME_MATCH = /^\s*[-]{4,}$/;
3128
3128
  var FULL_MATCH = /at (?:async )?(?:(.+?)\s+\()?(?:(.+):(\d+):(\d+)?|([^)]+))\)?/;
3129
- var nodeStackLineParser = (line, platform2) => {
3129
+ var nodeStackLineParser = (line, platform3) => {
3130
3130
  const lineMatch = line.match(FULL_MATCH);
3131
3131
  if (lineMatch) {
3132
3132
  let object;
@@ -3172,12 +3172,12 @@ var nodeStackLineParser = (line, platform2) => {
3172
3172
  lineno: _parseIntOrUndefined(lineMatch[3]),
3173
3173
  colno: _parseIntOrUndefined(lineMatch[4]),
3174
3174
  in_app: filenameIsInApp(filename || "", isNative),
3175
- platform: platform2
3175
+ platform: platform3
3176
3176
  };
3177
3177
  }
3178
3178
  if (line.match(FILENAME_MATCH)) return {
3179
3179
  filename: line,
3180
- platform: platform2
3180
+ platform: platform3
3181
3181
  };
3182
3182
  };
3183
3183
  function filenameIsInApp(filename, isNative = false) {
@@ -3207,7 +3207,7 @@ function getLastStackFrame(arr) {
3207
3207
  function createDefaultStackParser() {
3208
3208
  return createStackParser("web:javascript", chromeStackLineParser, geckoStackLineParser);
3209
3209
  }
3210
- function createStackParser(platform2, ...parsers) {
3210
+ function createStackParser(platform3, ...parsers) {
3211
3211
  return (stack, skipFirstLines = 0) => {
3212
3212
  const frames = [];
3213
3213
  const lines = stack.split("\n");
@@ -3217,7 +3217,7 @@ function createStackParser(platform2, ...parsers) {
3217
3217
  const cleanedLine = WEBPACK_ERROR_REGEXP.test(line) ? line.replace(WEBPACK_ERROR_REGEXP, "$1") : line;
3218
3218
  if (!cleanedLine.match(/\S*Error: /)) {
3219
3219
  for (const parser of parsers) {
3220
- const frame = parser(cleanedLine, platform2);
3220
+ const frame = parser(cleanedLine, platform3);
3221
3221
  if (frame) {
3222
3222
  frames.push(frame);
3223
3223
  break;
@@ -3675,9 +3675,9 @@ async function addSourceContext(frames) {
3675
3675
  LRU_FILE_CONTENTS_CACHE.reduce();
3676
3676
  return frames;
3677
3677
  }
3678
- function getContextLinesFromFile(path55, ranges, output) {
3678
+ function getContextLinesFromFile(path58, ranges, output) {
3679
3679
  return new Promise((resolve7) => {
3680
- const stream = (0, import_node_fs.createReadStream)(path55);
3680
+ const stream = (0, import_node_fs.createReadStream)(path58);
3681
3681
  const lineReaded = (0, import_node_readline.createInterface)({
3682
3682
  input: stream
3683
3683
  });
@@ -3692,7 +3692,7 @@ function getContextLinesFromFile(path55, ranges, output) {
3692
3692
  let rangeStart = range[0];
3693
3693
  let rangeEnd = range[1];
3694
3694
  function onStreamError() {
3695
- LRU_FILE_CONTENTS_FS_READ_FAILED.set(path55, 1);
3695
+ LRU_FILE_CONTENTS_FS_READ_FAILED.set(path58, 1);
3696
3696
  lineReaded.close();
3697
3697
  lineReaded.removeAllListeners();
3698
3698
  destroyStreamAndResolve();
@@ -3753,8 +3753,8 @@ function clearLineContext(frame) {
3753
3753
  delete frame.context_line;
3754
3754
  delete frame.post_context;
3755
3755
  }
3756
- function shouldSkipContextLinesForFile(path55) {
3757
- return path55.startsWith("node:") || path55.endsWith(".min.js") || path55.endsWith(".min.cjs") || path55.endsWith(".min.mjs") || path55.startsWith("data:");
3756
+ function shouldSkipContextLinesForFile(path58) {
3757
+ return path58.startsWith("node:") || path58.endsWith(".min.js") || path58.endsWith(".min.cjs") || path58.endsWith(".min.mjs") || path58.startsWith("data:");
3758
3758
  }
3759
3759
  function shouldSkipContextLinesForFrame(frame) {
3760
3760
  if (void 0 !== frame.lineno && frame.lineno > MAX_CONTEXTLINES_LINENO) return true;
@@ -4109,7 +4109,7 @@ var RequiresServerEvaluation = class _RequiresServerEvaluation extends Error {
4109
4109
  }
4110
4110
  };
4111
4111
  var FeatureFlagsPoller = class {
4112
- constructor({ pollingInterval, personalApiKey, projectApiKey, timeout, host, customHeaders, ...options }) {
4112
+ constructor({ pollingInterval, personalApiKey, projectApiKey, timeout, host: host2, customHeaders, ...options }) {
4113
4113
  this.debugMode = false;
4114
4114
  this.shouldBeginExponentialBackoff = false;
4115
4115
  this.backOffCount = 0;
@@ -4122,7 +4122,7 @@ var FeatureFlagsPoller = class {
4122
4122
  this.loadedSuccessfullyOnce = false;
4123
4123
  this.timeout = timeout;
4124
4124
  this.projectApiKey = projectApiKey;
4125
- this.host = host;
4125
+ this.host = host2;
4126
4126
  this.poller = void 0;
4127
4127
  this.fetch = options.fetch || fetch;
4128
4128
  this.onError = options.onError;
@@ -5908,7 +5908,7 @@ function readAnonId() {
5908
5908
  }
5909
5909
  function superProperties() {
5910
5910
  return {
5911
- cliVersion: true ? "2.39.23" : "0.0.0-dev",
5911
+ cliVersion: true ? "2.39.25" : "0.0.0-dev",
5912
5912
  nodeVersion: process.version,
5913
5913
  platform: process.platform,
5914
5914
  arch: process.arch,
@@ -5942,10 +5942,10 @@ function initTelemetry() {
5942
5942
  log.trace("telemetry", "no PostHog API key baked into build \u2014 disabled");
5943
5943
  return false;
5944
5944
  }
5945
- const host = true ? "https://us.i.posthog.com" : "https://us.i.posthog.com";
5945
+ const host2 = true ? "https://us.i.posthog.com" : "https://us.i.posthog.com";
5946
5946
  distinctId = readAnonId();
5947
5947
  client = new PostHog(apiKey, {
5948
- host,
5948
+ host: host2,
5949
5949
  // 10s flush is generous for a CLI — most commands run < 30s
5950
5950
  // and we shutdown() before exit anyway. Per-batch size caps
5951
5951
  // memory growth on long-lived `codeam start` sessions.
@@ -5960,7 +5960,7 @@ function initTelemetry() {
5960
5960
  log.trace("telemetry", "posthog error (ignored)", err);
5961
5961
  });
5962
5962
  client.register(superProperties());
5963
- log.trace("telemetry", `posthog client initialised host=${host} distinctId=${distinctId}`);
5963
+ log.trace("telemetry", `posthog client initialised host=${host2} distinctId=${distinctId}`);
5964
5964
  return true;
5965
5965
  }
5966
5966
  function identifyUser(params) {
@@ -7287,8 +7287,8 @@ function createOsStrategy() {
7287
7287
  cached = buildForPlatform(process.platform);
7288
7288
  return cached;
7289
7289
  }
7290
- function buildForPlatform(platform2) {
7291
- switch (platform2) {
7290
+ function buildForPlatform(platform3) {
7291
+ switch (platform3) {
7292
7292
  case "darwin":
7293
7293
  return new DarwinOsStrategy();
7294
7294
  case "win32":
@@ -7302,10 +7302,10 @@ function buildForPlatform(platform2) {
7302
7302
  var import_node_crypto4 = require("crypto");
7303
7303
 
7304
7304
  // src/agents/claude/resolver.ts
7305
- function buildClaudeLaunch(extraArgs = [], os33 = createOsStrategy()) {
7306
- const found = os33.findInPath("claude") ?? os33.findInPath("claude-code");
7305
+ function buildClaudeLaunch(extraArgs = [], os36 = createOsStrategy()) {
7306
+ const found = os36.findInPath("claude") ?? os36.findInPath("claude-code");
7307
7307
  if (!found) return null;
7308
- return os33.buildLaunch(found, extraArgs);
7308
+ return os36.buildLaunch(found, extraArgs);
7309
7309
  }
7310
7310
 
7311
7311
  // src/agents/claude/installer.ts
@@ -9748,308 +9748,14 @@ function listResumableSessions(cwd) {
9748
9748
  return out2;
9749
9749
  }
9750
9750
 
9751
- // src/agents/claude/parsing.ts
9752
- function filterChrome(lines) {
9753
- const result = [];
9754
- let skipEchoContinuation = false;
9755
- for (const line of lines) {
9756
- const t2 = line.trim();
9757
- if (!t2) {
9758
- skipEchoContinuation = false;
9759
- continue;
9760
- }
9761
- if (/^[─━—═─\-]{3,}$/.test(t2)) {
9762
- skipEchoContinuation = false;
9763
- continue;
9764
- }
9765
- if (/^[●⏺]\s/.test(t2)) skipEchoContinuation = false;
9766
- if (/^[✳✢✶✻✽✴✷✸✹⠋⠙⠹⠸⠼⠴⠦⠧⠇⠏◐◑◒◓▁▂▃▄▅▆▇█]\s/.test(t2)) continue;
9767
- if (/esc.{0,5}to.{0,5}interrupt/i.test(t2)) continue;
9768
- if (/high\s*[·•]\s*\/effort/i.test(t2)) continue;
9769
- if (/^[❯>]\s*$/.test(t2)) continue;
9770
- if (/^\(thinking\)\s*$/.test(t2)) continue;
9771
- if (/^\?\s.*shortcut/i.test(t2)) continue;
9772
- if (/spending limit|usage limit/i.test(t2) && t2.length < 80) continue;
9773
- if (/↑\s*\/?\s*↓\s*to\s*navigate/i.test(t2)) continue;
9774
- if (t2.replace(/\s/g, "").length === 1) continue;
9775
- if ((t2.match(/─/g)?.length ?? 0) >= 6) continue;
9776
- if (/ctrl\+?o\s+to\s+expand/i.test(t2)) continue;
9777
- if (/^•\s+(?:Read(?:ing)?|Edit(?:ing)?|Writ(?:e|ing)|Bash|Runn(?:ing)?|Search(?:ing)?|Glob(?:bing)?|Grep(?:ping)?|Creat(?:e|ing)|Execut(?:e|ing)|Task|Agent|NotebookEdit)\b/i.test(
9778
- t2
9779
- ))
9780
- continue;
9781
- if (/^└\s/.test(t2)) continue;
9782
- if (/^\+\s/.test(t2) && /\d+\s*s\s*[·•]|\bthought\s+for\b|\d+\s*tokens|\(thinking\)/i.test(t2)) continue;
9783
- if (/^↓\s*\d+\s*tokens/i.test(t2)) continue;
9784
- if (/^\bthought\s+for\s+\d+/i.test(t2)) continue;
9785
- const stripped = t2.replace(/^[│╭╰╮╯┌└┐┘├┤┬┴┼]\s?/, "");
9786
- if (/^[❯>]\s+\S/.test(stripped) && !/^[❯>]\s*\d+\./.test(stripped)) {
9787
- skipEchoContinuation = true;
9788
- continue;
9789
- }
9790
- if (skipEchoContinuation) continue;
9791
- result.push(line);
9792
- }
9793
- return result;
9794
- }
9795
- var SPINNER_RE = /^(?:[✳✢✶✻✽✴✷✸✹⠋⠙⠹⠸⠼⠴⠦⠧⠇⠏◐◑◒◓▁▂▃▄▅▆▇█]|🔴|🟠|🟡|🟢|🔵|🟣|🟤|⚫|⚪|🌀|💭|✨)\s/u;
9796
- var BULLET_TOOL_RE = /^•\s+(?:Read(?:ing)?|Edit(?:ing)?|Writ(?:e|ing)|Bash|Runn(?:ing)?|Search(?:ing)?|Glob(?:bing)?|Grep(?:ping)?|Creat(?:e|ing)|Execut(?:e|ing)|Task|Agent|NotebookEdit)\b/i;
9797
- var TREE_LINE_RE = /^└\s/;
9798
- var STATUS_LINE_RE = /^(?:\+|[🔴🟠🟡🟢🔵🟣🟤⚫⚪🌀💭✨])\s/u;
9799
- function isChromeLine(line) {
9800
- const t2 = line.replace(/️/g, "").trim();
9801
- if (!t2) return false;
9802
- if (/^[─━—═─\-]{3,}$/.test(t2)) return true;
9803
- if (SPINNER_RE.test(t2)) return true;
9804
- if (BULLET_TOOL_RE.test(t2)) return true;
9805
- if (TREE_LINE_RE.test(t2)) return true;
9806
- if (STATUS_LINE_RE.test(t2) && /\d+\s*s\s*[·•]|\bthought\s+for\b|\d+\s*tokens|\(thinking\)/i.test(t2)) return true;
9807
- if (/^↓\s*\d+\s*tokens/i.test(t2)) return true;
9808
- if (/^\bthought\s+for\s+\d+/i.test(t2)) return true;
9809
- if (/esc.{0,5}to.{0,5}interrupt/i.test(t2)) return true;
9810
- if (/high\s*[·•]\s*\/effort/i.test(t2)) return true;
9811
- if (/^[❯>]\s*$/.test(t2)) return true;
9812
- if (/^\(thinking\)\s*$/.test(t2)) return true;
9813
- if (/^\?\s.*shortcut/i.test(t2)) return true;
9814
- if (/spending limit|usage limit/i.test(t2) && t2.length < 80) return true;
9815
- if (/↑\s*\/?\s*↓\s*to\s*navigate/i.test(t2)) return true;
9816
- if (t2.replace(/\s/g, "").length === 1) return true;
9817
- if ((t2.match(/─/g)?.length ?? 0) >= 6) return true;
9818
- if (/ctrl\+?o\s+to\s+expand/i.test(t2)) return true;
9819
- const hasBoxPrefix = /^[│╭╰╮╯┌└┐┘├┤┬┴┼]/.test(t2);
9820
- const stripped = t2.replace(/^[│╭╰╮╯┌└┐┘├┤┬┴┼]\s?/, "");
9821
- if (hasBoxPrefix && /^[❯>]\s+\S/.test(stripped) && !/^[❯>]\s*\d+\./.test(stripped)) return true;
9822
- return false;
9823
- }
9824
- function parseChromeLine(line) {
9825
- const t2 = line.replace(/️/g, "").trim();
9826
- if (!t2) return null;
9827
- if (/^[─━—═─\-]{3,}$/.test(t2)) return null;
9828
- if (/^[❯>]\s*$/.test(t2)) return null;
9829
- if (t2.replace(/\s/g, "").length === 1) return null;
9830
- if ((t2.match(/─/g)?.length ?? 0) >= 6) return null;
9831
- if (/esc.{0,5}to.{0,5}interrupt/i.test(t2)) return null;
9832
- if (/high\s*[·•]\s*\/effort/i.test(t2)) return null;
9833
- if (/↑\s*\/?\s*↓\s*to\s*navigate/i.test(t2)) return null;
9834
- if (/ctrl\+?o\s+to\s+expand/i.test(t2)) return null;
9835
- if (/spending limit|usage limit/i.test(t2)) return null;
9836
- if (/^\(thinking\)\s*$/.test(t2)) {
9837
- return { tool: "thinking", label: "Thinking\u2026", status: "running" };
9838
- }
9839
- if (TREE_LINE_RE.test(t2)) return null;
9840
- if (STATUS_LINE_RE.test(t2)) {
9841
- const label = t2.slice(2).replace(/….*/s, "").trim() || "Thinking\u2026";
9842
- return { tool: "thinking", label, status: "running" };
9843
- }
9844
- let text = t2;
9845
- if (SPINNER_RE.test(t2)) {
9846
- text = t2.slice(2).trim().replace(/….*/s, "").trim();
9847
- } else if (BULLET_TOOL_RE.test(t2)) {
9848
- text = t2.slice(2).trim();
9849
- text = text.replace(/\s*\(ctrl\+?o[^)]*\)/gi, "").replace(/,\s*reading\s+\d+\s+files?\s*…?/gi, "").replace(/,\s*\d+\s+files?\s*…?/gi, "").replace(/…$/, "").trim();
9850
- }
9851
- if (!text) return null;
9852
- return classifyStep(text);
9853
- }
9854
- function classifyStep(text) {
9855
- if (/^Read(?:ing)?\s+/i.test(text)) {
9856
- const label2 = text.replace(/^Read(?:ing)?\s+/i, "").replace(/\.\.\.$/, "").trim();
9857
- return { tool: "read", label: label2, status: "running" };
9858
- }
9859
- if (/^Edit(?:ing)?\s+|^Writ(?:e|ing|ing to)\s+|^Creat(?:e|ing)\s+/i.test(text)) {
9860
- const label2 = text.replace(/^(?:Edit(?:ing)?|Writ(?:e|ing(?: to)?)|Creat(?:e|ing))\s+/i, "").replace(/\.\.\.$/, "").trim();
9861
- return { tool: "edit", label: label2, status: "running" };
9862
- }
9863
- if (/^Runn(?:ing)?\s+|^Execut(?:e|ing)\s+|^Bash(?:ing)?\s*:|^\$\s+/i.test(text)) {
9864
- const label2 = text.replace(/^(?:Runn(?:ing)?|Execut(?:e|ing)|Bash(?:ing)?:|\$)\s+/i, "").replace(/\.\.\.$/, "").trim();
9865
- return { tool: "bash", label: label2, status: "running" };
9866
- }
9867
- if (/^Search(?:ing)?\s+for\s+|^Grep(?:ping)?\s*:/i.test(text)) {
9868
- const label2 = text.replace(/^(?:Search(?:ing)?\s+for|Grep(?:ping)?:)\s+/i, "").replace(/\.\.\.$/, "").trim();
9869
- return { tool: "search", label: label2, status: "running" };
9870
- }
9871
- const label = text.replace(/\.\.\.$/, "").trim();
9872
- return { tool: "other", label, status: "running" };
9873
- }
9874
- function detectSelector(lines) {
9875
- if (lines.some((l) => /\?\s+for\s+shortcuts/i.test(l.trim()))) return null;
9876
- const clean = lines.map(
9877
- (l) => l.replace(/^[│╭╰╮╯┌└┐┘├┤┬┴┼]\s?/, "").replace(/\s*[│╭╰╮╯┌└┐┘├┤┬┴┼─━═]+\s*$/, "")
9878
- );
9879
- const hasCursor = clean.some((l) => /^[❯>]\s*\d+\./.test(l.trim()));
9880
- const looksLikeTrust = clean.some(
9881
- (l) => /\b(?:trust\s+the\s+files|trust\s+this\s+folder|safety\s+check)\b/i.test(l)
9882
- );
9883
- if (!hasCursor && !looksLikeTrust) return null;
9884
- const OPTION_RE = /^(?:[❯>]\s*)?(\d+)\.(\s+|(?=\D))(.+)/;
9885
- let optionStartIdx = -1;
9886
- for (let i = 0; i < clean.length; i++) {
9887
- if (OPTION_RE.test(clean[i].trim())) {
9888
- optionStartIdx = i;
9889
- break;
9890
- }
9891
- }
9892
- if (optionStartIdx === -1) return null;
9893
- const questionParts = [];
9894
- for (let i = 0; i < optionStartIdx; i++) {
9895
- const t2 = clean[i].trim();
9896
- if (!t2) continue;
9897
- if (/^[─━—═\-]{3,}$/.test(t2)) continue;
9898
- if (/^\[.*\]$/.test(t2)) continue;
9899
- if (/^[>❯]\s/.test(t2)) continue;
9900
- if (!t2.includes(" ") && t2.length > 15) continue;
9901
- questionParts.push(t2);
9902
- }
9903
- const question = questionParts.filter((line, i, arr) => !arr.some((other, j2) => j2 !== i && other.includes(line))).join("\n").trim();
9904
- const optionLabels = /* @__PURE__ */ new Map();
9905
- const optionDescs = /* @__PURE__ */ new Map();
9906
- let currentNum = -1;
9907
- for (let i = optionStartIdx; i < clean.length; i++) {
9908
- const t2 = clean[i].trim();
9909
- if (!t2) continue;
9910
- const m = t2.match(OPTION_RE);
9911
- if (m) {
9912
- const num = parseInt(m[1], 10);
9913
- if (!optionLabels.has(num)) {
9914
- optionLabels.set(num, m[3].trim());
9915
- optionDescs.set(num, []);
9916
- }
9917
- currentNum = num;
9918
- } else if (currentNum !== -1 && !/^Enter to/i.test(t2) && !/^[─━—═\-]{3,}$/.test(t2) && !/↑.*↓.*navigate/i.test(t2) && !/Esc to/i.test(t2)) {
9919
- optionDescs.get(currentNum)?.push(t2);
9920
- }
9921
- }
9922
- const keys = [...optionLabels.keys()].sort((a, b) => a - b);
9923
- if (keys.length < 2 || keys[0] !== 1) return null;
9924
- return {
9925
- question,
9926
- options: keys.map((k2) => optionLabels.get(k2)),
9927
- optionDescriptions: keys.map((k2) => (optionDescs.get(k2) ?? []).join(" ").trim()),
9928
- currentIndex: 0
9929
- };
9930
- }
9931
- function detectInputSuggestion(lines) {
9932
- let hintIdx = -1;
9933
- for (let i = lines.length - 1; i >= 0; i--) {
9934
- if (/\?\s+for\s+shortcuts/i.test(lines[i].trim())) {
9935
- hintIdx = i;
9936
- break;
9937
- }
9938
- }
9939
- if (hintIdx === -1) return null;
9940
- if (lines.some((l) => /^[❯>]\s*\d+\./.test(l.trim()))) return null;
9941
- const windowStart = Math.max(0, hintIdx - 5);
9942
- for (let i = hintIdx - 1; i >= windowStart; i--) {
9943
- const t2 = lines[i].trim();
9944
- if (!t2) continue;
9945
- if (/^[─━═│┌┐└┘├┤┬┴┼]+$/u.test(t2)) continue;
9946
- const m = t2.match(/^[❯>]\s+(\S.*)$/);
9947
- if (!m) return null;
9948
- if (/^\d+\.\s/.test(m[1])) return null;
9949
- if (/^for\s/i.test(m[1])) return null;
9950
- const text = m[1].trim();
9951
- if (text.length === 0) return null;
9952
- return text;
9953
- }
9954
- return null;
9955
- }
9956
- function detectListSelector(lines) {
9957
- if (!lines.some((l) => /[↑↓].*navigate/i.test(l.trim()))) return null;
9958
- if (lines.some((l) => /^❯\s*\d+\./.test(l.trim()))) return null;
9959
- if (!lines.some((l) => /^\s+❯\s+\S/.test(l))) return null;
9960
- const isSelected = (line) => /^\s+❯\s+\S/.test(line);
9961
- const isUnselected = (line) => /^ \S/.test(line);
9962
- const isItem = (line) => isSelected(line) || isUnselected(line);
9963
- let optionStartIdx = -1;
9964
- for (let i = 0; i < lines.length; i++) {
9965
- if (isItem(lines[i])) {
9966
- optionStartIdx = i;
9967
- break;
9968
- }
9969
- }
9970
- if (optionStartIdx === -1) return null;
9971
- const questionParts = [];
9972
- for (let i = 0; i < optionStartIdx; i++) {
9973
- const t2 = lines[i].trim();
9974
- if (!t2) continue;
9975
- if (/^[─━—═\-]{3,}$/.test(t2)) continue;
9976
- if (/[┌└│┐┘├┤┬┴┼]/.test(t2)) {
9977
- const inner = t2.replace(/[│┌└┐┘├┤┬┴┼─]/g, "").trim();
9978
- if (inner) questionParts.push(inner);
9979
- continue;
9980
- }
9981
- if (/^[>❯]\s/.test(t2)) continue;
9982
- if (/[↑↓].*navigate/i.test(t2)) continue;
9983
- if (!t2.includes(" ") && t2.length > 15) continue;
9984
- questionParts.push(t2);
9985
- }
9986
- const question = questionParts.filter((line, i, arr) => !arr.some((other, j2) => j2 !== i && other.includes(line))).join("\n").trim();
9987
- const options = [];
9988
- let currentIndex = 0;
9989
- for (const line of lines.slice(optionStartIdx)) {
9990
- const t2 = line.trim();
9991
- if (!t2) continue;
9992
- if (/[↑↓].*navigate/i.test(t2)) break;
9993
- if (/^[─━—═\-]{3,}$/.test(t2)) continue;
9994
- if (isSelected(line)) {
9995
- currentIndex = options.length;
9996
- options.push(t2.replace(/^❯\s+/, "").trim());
9997
- } else if (isUnselected(line)) {
9998
- options.push(t2);
9999
- }
10000
- }
10001
- if (options.length < 2) return null;
10002
- return {
10003
- question,
10004
- options,
10005
- optionDescriptions: options.map(() => ""),
10006
- currentIndex
10007
- };
10008
- }
10009
- var BANNER_ART_RE = /[█▀▄▌▐▝▘▛▜▙▟▖▗▔▕▮▯▰▱▓▒░◆◇]/;
10010
- var BANNER_META_RE = /(?:Sonnet|Opus|Haiku|Claude)(?:\s|·|-|\(|$)/i;
10011
- function detectStartupBanner(lines) {
10012
- for (let i = 0; i + 2 < lines.length; i++) {
10013
- if (!/▐▛[█]+▜▌/.test(lines[i])) continue;
10014
- if (!/▝▜[█]+▛▘/.test(lines[i + 1])) continue;
10015
- if (!lines[i + 2].includes("\u2598\u2598")) continue;
10016
- const inArtTitle = lines[i].replace(/^▐▛[█]+▜▌\s*/, "").trim();
10017
- const inArtSubtitle = lines[i + 1].replace(/^▝▜[█]+▛▘\s*/, "").trim();
10018
- const inArtPath = lines[i + 2].replace(/.*▝▝\s*/, "").trim();
10019
- return {
10020
- title: inArtTitle === lines[i].trim() ? "" : inArtTitle,
10021
- subtitle: inArtSubtitle === lines[i + 1].trim() ? "" : inArtSubtitle,
10022
- path: inArtPath === lines[i + 2].trim() ? "" : inArtPath,
10023
- startIdx: i,
10024
- endIdx: i + 2
10025
- };
10026
- }
10027
- const metaIdx = lines.findIndex(
10028
- (l) => BANNER_META_RE.test(l) && /(?:Claude|API|Console)/i.test(l) && !BANNER_ART_RE.test(l)
10029
- );
10030
- if (metaIdx === -1) return null;
10031
- let artStart = metaIdx;
10032
- while (artStart > 0 && BANNER_ART_RE.test(lines[artStart - 1])) artStart--;
10033
- if (metaIdx - artStart < 2) return null;
10034
- const pathLine = (lines[metaIdx + 1] ?? "").trim();
10035
- const path55 = pathLine && !BANNER_ART_RE.test(pathLine) ? pathLine : "";
10036
- return {
10037
- title: "",
10038
- subtitle: lines[metaIdx].trim(),
10039
- path: path55,
10040
- startIdx: artStart,
10041
- endIdx: metaIdx + (path55 ? 1 : 0)
10042
- };
10043
- }
10044
-
10045
9751
  // src/agents/claude/runtime.ts
10046
9752
  var ClaudeRuntimeStrategy = class {
10047
9753
  id = "claude";
10048
9754
  meta = getAgent("claude");
10049
9755
  mode = "interactive";
10050
9756
  os;
10051
- constructor(os33) {
10052
- this.os = os33;
9757
+ constructor(os36) {
9758
+ this.os = os36;
10053
9759
  }
10054
9760
  /**
10055
9761
  * Claude Code's react-ink TUI enables bracketed-paste mode at
@@ -10152,24 +9858,17 @@ var ClaudeRuntimeStrategy = class {
10152
9858
  return { ptyInput: "/compact\r" };
10153
9859
  }
10154
9860
  // ─── TUI parser strategy methods ─────────────────────────────────
10155
- parseTuiChrome(line) {
10156
- if (!isChromeLine(line)) return null;
10157
- return parseChromeLine(line);
10158
- }
9861
+ // Claude runs ACP-only (see the `requiresAcp` dispatch in start.ts),
9862
+ // so the legacy PTY-spawn pipeline never reaches these. They remain
9863
+ // as inert stubs purely to satisfy the InteractiveAgentStrategy
9864
+ // contract; the React-Ink TUI parsers were removed with the PTY path.
9865
+ // The optional TUI hooks (parseTuiChrome / detectReadyPrompt /
9866
+ // detectStartupBanner / detectInputSuggestion) are dropped entirely.
10159
9867
  filterTuiOutput(lines) {
10160
- return filterChrome(lines);
10161
- }
10162
- detectInteractivePrompt(lines) {
10163
- return detectSelector(lines) ?? detectListSelector(lines);
10164
- }
10165
- detectReadyPrompt(lines) {
10166
- return lines.some((l) => /^\?\s.*shortcut/i.test(l.trim()));
10167
- }
10168
- detectStartupBanner(lines) {
10169
- return detectStartupBanner(lines);
9868
+ return lines;
10170
9869
  }
10171
- detectInputSuggestion(lines) {
10172
- return detectInputSuggestion(lines);
9870
+ detectInteractivePrompt(_lines) {
9871
+ return null;
10173
9872
  }
10174
9873
  credentialLocator() {
10175
9874
  return claudeCredentialLocator();
@@ -10603,410 +10302,6 @@ function getCurrentUsage2(historyDir) {
10603
10302
  };
10604
10303
  }
10605
10304
 
10606
- // src/agents/codex/parsing.ts
10607
- var BOX_DRAW_RE = /^[╭─╮│╰╯]/u;
10608
- var BULLET_CHARS = "\u2022\xB7\u2027\u2219\u22C5";
10609
- var CODEX_AGENT_REPLY_RE = new RegExp(`^[${BULLET_CHARS}]\\s`, "u");
10610
- var STRIP_BULLET_RE = new RegExp(`^(\\s*)[${BULLET_CHARS}]\\s`, "u");
10611
- var CODEX_USER_ECHO_RE = /^[›>]\s+(?!\d+\.\s)\S/u;
10612
- var TIP_RE = /^\s*Tip:\s/i;
10613
- var LEARN_MORE_RE = /^\s*Learn more:\s/i;
10614
- var CODEX_STATUS_FOOTER_RE = /\bdefault\s+[·•]\s+\S+/i;
10615
- var CODEAM_BANNER_RES = [
10616
- // Bullet-prefixed banner entries (any role / launch label).
10617
- new RegExp(`^[${BULLET_CHARS}]\\s+(Launching|Edgar|PRO|FREE|ENTERPRISE)\\b`, "i"),
10618
- /^Paired\b/,
10619
- /^codeam\b\s+v\d/,
10620
- /^✓\s+Paired/,
10621
- /^◇\s+Paired/
10622
- ];
10623
- function filterCodexChrome(lines) {
10624
- const out2 = [];
10625
- for (const line of lines) {
10626
- const t2 = line.trimEnd();
10627
- const trimmed = t2.trimStart();
10628
- if (!trimmed) continue;
10629
- if (BOX_DRAW_RE.test(trimmed)) continue;
10630
- if (/^OpenAI Codex\b/i.test(trimmed) || /^>_\s+OpenAI Codex\b/i.test(trimmed) || /^model:\s/i.test(trimmed) || /^directory:\s/i.test(trimmed)) continue;
10631
- if (TIP_RE.test(t2) || LEARN_MORE_RE.test(t2)) continue;
10632
- if (CODEX_STATUS_FOOTER_RE.test(trimmed)) continue;
10633
- if (CODEAM_BANNER_RES.some((re2) => re2.test(trimmed))) continue;
10634
- if (CODEX_USER_ECHO_RE.test(trimmed)) continue;
10635
- if (CODEX_AGENT_REPLY_RE.test(trimmed)) {
10636
- out2.push(t2.replace(STRIP_BULLET_RE, "$1"));
10637
- continue;
10638
- }
10639
- out2.push(t2);
10640
- }
10641
- const dedented = dedentCodexStructuredLines(out2);
10642
- const wrapped = wrapCodexCodeBlocks(dedented);
10643
- const hasRealInput = lines.some((l) => /\w/.test(l));
10644
- if (out2.length > 0 || hasRealInput) {
10645
- const sampleIn = lines.slice(-50).map((l, i) => ` in[${i}] ${JSON.stringify(l)}`).join("\n");
10646
- const sampleOut = dedented.map((l, i) => ` out[${i}] ${JSON.stringify(l)}`).join("\n");
10647
- log.info("codex-parse", `in=${lines.length} out=${dedented.length}
10648
- ${sampleIn}
10649
- ---
10650
- ${sampleOut}`);
10651
- } else {
10652
- log.trace("codex-parse", `filterCodexChrome in=${lines.length} out=${out2.length}`);
10653
- }
10654
- return wrapped;
10655
- }
10656
- var CODE_CHAR_RE = /[;{}]|=>|^\s*(?:import|public|private|static|class|function|interface|type|const|let|var|def|return|if|else|for|while)\b/;
10657
- var DIFF_HUNK_RE = /^@@\s+-\d+(?:,\d+)?\s+\+\d+(?:,\d+)?\s+@@/;
10658
- var DIFF_GIT_RE = /^diff\s+--git\s+/;
10659
- var DIFF_OLD_RE = /^---\s+(?:a\/)?\S/;
10660
- var DIFF_NEW_RE = /^\+\+\+\s+(?:b\/)?\S/;
10661
- var COMMIT_HEAD_RE = /^\[[\w./@-]+\s+[0-9a-f]{7,40}\]\s+/;
10662
- var COMMIT_STATS_RE = /\d+\s+files?\s+changed/;
10663
- var PUSH_TO_RE = /^To\s+(?:https?:\/\/|git@)/;
10664
- var PUSH_NEW_RE = /\[new branch\]\s+\S+\s*->\s*\S+/;
10665
- var PUSH_UPDATE_RE = /^\s*[0-9a-f]{7,40}\.\.[0-9a-f]{7,40}\s+\S+\s*->\s*\S+/;
10666
- var MERGE_UPD_RE = /^Updating\s+[0-9a-f]{7,40}\.\.[0-9a-f]{7,40}/;
10667
- var MERGE_FF_RE = /^Fast-forward\s*$/;
10668
- var PR_TITLE_RE = /^title:\s+\S/;
10669
- var PR_STATE_RE = /^state:\s+(?:OPEN|CLOSED|MERGED|DRAFT)/i;
10670
- var PR_URL_RE = /https?:\/\/github\.com\/[\w.-]+\/[\w.-]+\/pull\/\d+/;
10671
- var PR_BANNER_RE = /^\s*[✓✔]?\s*Pull request created\s*$/i;
10672
- function dedentCodexStructuredLines(lines) {
10673
- const MARKER_RE = /^( +)(?:diff --git |@@ |--- |\+\+\+ |Updating [0-9a-f]|Fast-forward|Merge made by |To (?:https?:\/\/|git@|github\.com|[\w.-]+[:/])|From (?:https?:\/\/|git@|github\.com|[\w.-]+[:/])|\[[\w./@-]+\s+[0-9a-f]{7,40}\])/;
10674
- let margin = -1;
10675
- for (const line of lines) {
10676
- const m = line.match(MARKER_RE);
10677
- if (m) {
10678
- const w3 = m[1].length;
10679
- if (margin === -1 || w3 < margin) margin = w3;
10680
- }
10681
- }
10682
- if (margin <= 0) return lines;
10683
- return lines.map((line) => {
10684
- if (line.length === 0) return line;
10685
- const lead = line.match(/^ */)?.[0].length ?? 0;
10686
- const strip = Math.min(margin, lead);
10687
- return strip > 0 ? line.slice(strip) : line;
10688
- });
10689
- }
10690
- function isStructuredBlock(block) {
10691
- if (block.some((l) => DIFF_HUNK_RE.test(l))) return true;
10692
- if (block.some((l) => DIFF_GIT_RE.test(l))) return true;
10693
- if (block.some((l) => DIFF_OLD_RE.test(l)) && block.some((l) => DIFF_NEW_RE.test(l))) return true;
10694
- if (block.some((l) => COMMIT_HEAD_RE.test(l))) return true;
10695
- if (block.some((l) => COMMIT_STATS_RE.test(l))) return true;
10696
- if (block.some((l) => PUSH_TO_RE.test(l))) return true;
10697
- if (block.some((l) => PUSH_NEW_RE.test(l))) return true;
10698
- if (block.some((l) => PUSH_UPDATE_RE.test(l))) return true;
10699
- if (block.some((l) => MERGE_UPD_RE.test(l))) return true;
10700
- if (block.some((l) => MERGE_FF_RE.test(l))) return true;
10701
- if (block.some((l) => PR_TITLE_RE.test(l)) && block.some((l) => PR_STATE_RE.test(l))) return true;
10702
- if (block.some((l) => PR_URL_RE.test(l))) return true;
10703
- if (block.some((l) => PR_BANNER_RE.test(l))) return true;
10704
- return false;
10705
- }
10706
- function inferLanguage(block) {
10707
- const head = block.slice(0, 10).join("\n");
10708
- if (/\bpublic\s+(?:static\s+)?(?:class|void|int|String)\b|System\.out\.println|\bjava\.util/.test(head)) return "java";
10709
- if (/\b(?:interface|type)\s+\w+\s*=?\s*[{<]|\bas\s+(?:string|number|boolean)\b|\b(?:string|number|boolean)\s*[;,)\]]/.test(head)) return "typescript";
10710
- if (/\bimport\s+\w+\s+from\s+['"]|=>\s*[{(]|\bconst\s+\w+\s*=\s*(?:async\s+)?\(/.test(head)) return "javascript";
10711
- if (/^\s*def\s+\w+\(|^\s*from\s+\w+\s+import|print\(/m.test(head)) return "python";
10712
- if (/^\s*package\s+\w+|^\s*func\s+\w+\(|\binterface\s*{/m.test(head)) return "go";
10713
- if (/^\s*fn\s+\w+\(|^\s*use\s+\w+::|^\s*impl\s+/m.test(head)) return "rust";
10714
- if (/#include\s*<|int\s+main\s*\(/.test(head)) return "cpp";
10715
- return "";
10716
- }
10717
- function wrapCodexCodeBlocks(lines) {
10718
- if (isStructuredBlock(lines)) {
10719
- return lines;
10720
- }
10721
- const result = [];
10722
- let i = 0;
10723
- while (i < lines.length) {
10724
- const line = lines[i];
10725
- if (!CODE_CHAR_RE.test(line)) {
10726
- result.push(line);
10727
- i++;
10728
- continue;
10729
- }
10730
- const start2 = i;
10731
- let end = i;
10732
- let j2 = i + 1;
10733
- while (j2 < lines.length) {
10734
- const l = lines[j2];
10735
- if (CODE_CHAR_RE.test(l)) {
10736
- end = j2;
10737
- j2++;
10738
- continue;
10739
- }
10740
- if (l.trim() === "") {
10741
- let k2 = j2 + 1;
10742
- while (k2 < lines.length && lines[k2].trim() === "") k2++;
10743
- if (k2 < lines.length && CODE_CHAR_RE.test(lines[k2])) {
10744
- end = k2;
10745
- j2 = k2 + 1;
10746
- continue;
10747
- }
10748
- break;
10749
- }
10750
- if (/^\s{2,}\S/.test(l)) {
10751
- end = j2;
10752
- j2++;
10753
- continue;
10754
- }
10755
- break;
10756
- }
10757
- const runLen = end - start2 + 1;
10758
- const body = lines.slice(start2, end + 1);
10759
- if (isStructuredBlock(body)) {
10760
- for (const l of body) result.push(l);
10761
- i = end + 1;
10762
- continue;
10763
- }
10764
- const codeShapedCount = body.filter((l) => CODE_CHAR_RE.test(l)).length;
10765
- if (codeShapedCount >= 3) {
10766
- const lang = inferLanguage(body);
10767
- result.push("```" + lang);
10768
- for (const l of body) result.push(l);
10769
- while (result.length > 0 && result[result.length - 1].trim() === "") {
10770
- result.pop();
10771
- }
10772
- result.push("```");
10773
- i = end + 1;
10774
- } else {
10775
- for (let k2 = start2; k2 <= end && k2 < lines.length; k2++) result.push(lines[k2]);
10776
- i = end + 1;
10777
- if (i === start2) i++;
10778
- }
10779
- void runLen;
10780
- }
10781
- return result;
10782
- }
10783
- function parseCodexChrome(_line) {
10784
- return null;
10785
- }
10786
- var CODEX_OPTION_RE = /^\s*([>›]\s+)?(\d+)\.\s+(.+)/;
10787
- var CODEX_OPTION_START_RE = /^\s*(?:[>›]\s+)?\d+\.\s/;
10788
- var CODEX_FOOTER_RE = /\bpress\s+enter\s+to\s+(?:confirm|continue|select)\b/i;
10789
- function detectCodexSelector(lines) {
10790
- let optionStartIdx = -1;
10791
- for (let i = 0; i < lines.length; i++) {
10792
- if (CODEX_OPTION_START_RE.test(lines[i])) {
10793
- optionStartIdx = i;
10794
- break;
10795
- }
10796
- }
10797
- if (optionStartIdx === -1) return null;
10798
- const optionLabels = /* @__PURE__ */ new Map();
10799
- let cursorIndex = 0;
10800
- let hasCursor = false;
10801
- let footerAfterOptions = false;
10802
- let lastOptionLineIdx = optionStartIdx;
10803
- for (let i = optionStartIdx; i < lines.length; i++) {
10804
- const raw = lines[i];
10805
- const t2 = raw.trim();
10806
- if (!t2) continue;
10807
- if (CODEX_FOOTER_RE.test(t2)) {
10808
- footerAfterOptions = true;
10809
- break;
10810
- }
10811
- const m = t2.match(CODEX_OPTION_RE);
10812
- if (!m) {
10813
- if (i - lastOptionLineIdx > 2) break;
10814
- continue;
10815
- }
10816
- const num = parseInt(m[2], 10);
10817
- if (!optionLabels.has(num)) {
10818
- optionLabels.set(num, m[3].trim());
10819
- if (m[1]) {
10820
- cursorIndex = optionLabels.size - 1;
10821
- hasCursor = true;
10822
- }
10823
- }
10824
- lastOptionLineIdx = i;
10825
- }
10826
- const keys = [...optionLabels.keys()].sort((a, b) => a - b);
10827
- if (keys.length < 2 || keys[0] !== 1) return null;
10828
- const questionParts = [];
10829
- for (let i = 0; i < optionStartIdx; i++) {
10830
- const t2 = lines[i].trim();
10831
- if (!t2) continue;
10832
- if (/^[>›]\s*$/.test(t2)) continue;
10833
- questionParts.push(t2);
10834
- }
10835
- const question = questionParts.join("\n").trim();
10836
- const questionEndsWithQuery = /\?\s*$/.test(question);
10837
- if (!hasCursor && !(questionEndsWithQuery && footerAfterOptions)) {
10838
- return null;
10839
- }
10840
- return {
10841
- question,
10842
- options: keys.map((k2) => optionLabels.get(k2)),
10843
- optionDescriptions: keys.map(() => ""),
10844
- currentIndex: hasCursor ? cursorIndex : 0
10845
- };
10846
- }
10847
-
10848
- // src/agents/codex/renderer.ts
10849
- function renderCodexBuffer(raw) {
10850
- const scrollback = [];
10851
- const screen = [""];
10852
- let row = 0;
10853
- let col = 0;
10854
- let scrollTop = null;
10855
- let scrollBottom = null;
10856
- function ensureRow() {
10857
- while (screen.length <= row) screen.push("");
10858
- }
10859
- function writeChar(ch) {
10860
- ensureRow();
10861
- if (col < screen[row].length) {
10862
- screen[row] = screen[row].slice(0, col) + ch + screen[row].slice(col + 1);
10863
- } else {
10864
- while (screen[row].length < col) screen[row] += " ";
10865
- screen[row] += ch;
10866
- }
10867
- col++;
10868
- }
10869
- function scrollRegionUp() {
10870
- if (scrollTop === null || scrollBottom === null) {
10871
- ensureRow();
10872
- return;
10873
- }
10874
- while (screen.length <= scrollBottom) screen.push("");
10875
- if (screen[scrollTop].trim() !== "") {
10876
- scrollback.push(screen[scrollTop]);
10877
- }
10878
- for (let r = scrollTop; r < scrollBottom; r++) {
10879
- screen[r] = screen[r + 1];
10880
- }
10881
- screen[scrollBottom] = "";
10882
- }
10883
- function scrollRegionDown() {
10884
- if (scrollTop === null || scrollBottom === null) {
10885
- return;
10886
- }
10887
- while (screen.length <= scrollBottom) screen.push("");
10888
- if (screen[scrollBottom].trim() !== "") {
10889
- scrollback.push(screen[scrollBottom]);
10890
- }
10891
- for (let r = scrollBottom; r > scrollTop; r--) {
10892
- screen[r] = screen[r - 1];
10893
- }
10894
- screen[scrollTop] = "";
10895
- }
10896
- let i = 0;
10897
- while (i < raw.length) {
10898
- const ch = raw[i];
10899
- if (ch === "\x1B") {
10900
- i++;
10901
- if (i >= raw.length) break;
10902
- if (raw[i] === "[") {
10903
- i++;
10904
- let param = "";
10905
- while (i < raw.length && !/[@-~]/.test(raw[i])) param += raw[i++];
10906
- const cmd = raw[i] ?? "";
10907
- const n = parseInt(param) || 1;
10908
- if (cmd === "A") {
10909
- row = Math.max(0, row - n);
10910
- } else if (cmd === "B") {
10911
- row += n;
10912
- ensureRow();
10913
- } else if (cmd === "C") {
10914
- col += n;
10915
- } else if (cmd === "D") {
10916
- col = Math.max(0, col - n);
10917
- } else if (cmd === "G") {
10918
- col = Math.max(0, n - 1);
10919
- } else if (cmd === "H" || cmd === "f") {
10920
- const p2 = param.split(";");
10921
- row = Math.max(0, (parseInt(p2[0] ?? "1") || 1) - 1);
10922
- col = Math.max(0, (parseInt(p2[1] ?? "1") || 1) - 1);
10923
- ensureRow();
10924
- } else if (cmd === "J") {
10925
- if (param === "2" || param === "3") {
10926
- for (let r = 0; r < screen.length; r++) {
10927
- if (screen[r].trim() !== "") scrollback.push(screen[r]);
10928
- }
10929
- screen.length = 1;
10930
- screen[0] = "";
10931
- row = 0;
10932
- col = 0;
10933
- } else if (param === "1") {
10934
- for (let r = 0; r < row; r++) screen[r] = "";
10935
- screen[row] = " ".repeat(col) + screen[row].slice(col);
10936
- } else {
10937
- screen[row] = screen[row].slice(0, col);
10938
- screen.splice(row + 1);
10939
- }
10940
- } else if (cmd === "K") {
10941
- ensureRow();
10942
- if (param === "" || param === "0") screen[row] = screen[row].slice(0, col);
10943
- else if (param === "1") screen[row] = " ".repeat(col) + screen[row].slice(col);
10944
- else if (param === "2") screen[row] = "";
10945
- } else if (cmd === "r") {
10946
- if (param === "" || param === ";") {
10947
- scrollTop = null;
10948
- scrollBottom = null;
10949
- } else {
10950
- const p2 = param.split(";");
10951
- const top = parseInt(p2[0] ?? "1") || 1;
10952
- const bot = parseInt(p2[1] ?? "1") || 1;
10953
- scrollTop = Math.max(0, top - 1);
10954
- scrollBottom = Math.max(scrollTop, bot - 1);
10955
- }
10956
- }
10957
- } else if (raw[i] === "]") {
10958
- i++;
10959
- while (i < raw.length) {
10960
- if (raw[i] === "\x07") break;
10961
- if (raw[i] === "\x1B" && i + 1 < raw.length && raw[i + 1] === "\\") {
10962
- i++;
10963
- break;
10964
- }
10965
- i++;
10966
- }
10967
- } else if (raw[i] === "M") {
10968
- if (scrollTop !== null && row === scrollTop) {
10969
- scrollRegionDown();
10970
- } else {
10971
- row = Math.max(0, row - 1);
10972
- }
10973
- } else if (raw[i] === "D") {
10974
- if (scrollBottom !== null && row === scrollBottom) {
10975
- scrollRegionUp();
10976
- } else {
10977
- row++;
10978
- ensureRow();
10979
- }
10980
- }
10981
- } else if (ch === "\r") {
10982
- if (i + 1 < raw.length && raw[i + 1] === "\n") {
10983
- if (scrollBottom !== null && row === scrollBottom) {
10984
- scrollRegionUp();
10985
- } else {
10986
- row++;
10987
- ensureRow();
10988
- }
10989
- col = 0;
10990
- i++;
10991
- } else {
10992
- col = 0;
10993
- }
10994
- } else if (ch === "\n") {
10995
- if (scrollBottom !== null && row === scrollBottom) {
10996
- scrollRegionUp();
10997
- } else {
10998
- row++;
10999
- ensureRow();
11000
- }
11001
- col = 0;
11002
- } else if (ch >= " " || ch === " ") {
11003
- writeChar(ch);
11004
- }
11005
- i++;
11006
- }
11007
- return [...scrollback, ...screen];
11008
- }
11009
-
11010
10305
  // src/agents/codex/link.ts
11011
10306
  var import_node_child_process3 = require("child_process");
11012
10307
 
@@ -11079,8 +10374,8 @@ function codexCredentialLocator() {
11079
10374
  function codexLoginLauncher() {
11080
10375
  return {
11081
10376
  async ensureInstalled() {
11082
- const os33 = createOsStrategy();
11083
- return os33.findInPath("codex") !== null;
10377
+ const os36 = createOsStrategy();
10378
+ return os36.findInPath("codex") !== null;
11084
10379
  },
11085
10380
  launch() {
11086
10381
  return (0, import_node_child_process3.spawn)("codex", ["login"], { stdio: "inherit" });
@@ -11103,8 +10398,8 @@ var CodexRuntimeStrategy = class {
11103
10398
  meta = getAgent("codex");
11104
10399
  mode = "interactive";
11105
10400
  os;
11106
- constructor(os33) {
11107
- this.os = os33;
10401
+ constructor(os36) {
10402
+ this.os = os36;
11108
10403
  }
11109
10404
  async prepareLaunch() {
11110
10405
  let binary = this.os.findInPath("codex");
@@ -11172,27 +10467,17 @@ var CodexRuntimeStrategy = class {
11172
10467
  return { ptyInput: "/compact\r" };
11173
10468
  }
11174
10469
  // ─── TUI parser strategy methods ─────────────────────────────────
11175
- /**
11176
- * Codex needs its own virtual terminal because the Codex CLI uses
11177
- * DECSTBM scroll regions (`\x1B[1;31r`) and Reverse Index (`\x1BM`)
11178
- * to scroll chat history within a fixed top zone — bytes the shared
11179
- * renderer drops, leaving the mobile feed with only the most recent
11180
- * frame instead of the full reply. See ./renderer.ts.
11181
- */
11182
- renderToLines(buffer) {
11183
- return renderCodexBuffer(buffer);
11184
- }
11185
- parseTuiChrome(line) {
11186
- return parseCodexChrome(line);
11187
- }
10470
+ // Codex runs ACP-only (see the `requiresAcp` dispatch in start.ts);
10471
+ // the legacy PTY-spawn pipeline never reaches these. Inert stubs to
10472
+ // satisfy the InteractiveAgentStrategy contract the Codex ratatui
10473
+ // parsers + scroll-region renderer were removed with the PTY path.
10474
+ // The optional TUI hooks (renderToLines / parseTuiChrome /
10475
+ // detectReadyPrompt) are dropped entirely.
11188
10476
  filterTuiOutput(lines) {
11189
- return filterCodexChrome(lines);
11190
- }
11191
- detectInteractivePrompt(lines) {
11192
- return detectCodexSelector(lines);
10477
+ return lines;
11193
10478
  }
11194
- detectReadyPrompt(lines) {
11195
- return lines.some((l) => /[│┃]\s*›/u.test(l));
10479
+ detectInteractivePrompt(_lines) {
10480
+ return null;
11196
10481
  }
11197
10482
  credentialLocator() {
11198
10483
  return codexCredentialLocator();
@@ -11210,12 +10495,12 @@ var CodexRuntimeStrategy = class {
11210
10495
  });
11211
10496
  }
11212
10497
  };
11213
- function resolveNpm(os33) {
11214
- return os33.id === "win32" ? "npm.cmd" : "npm";
10498
+ function resolveNpm(os36) {
10499
+ return os36.id === "win32" ? "npm.cmd" : "npm";
11215
10500
  }
11216
- async function installCodexViaNpm(os33) {
10501
+ async function installCodexViaNpm(os36) {
11217
10502
  return new Promise((resolve7, reject) => {
11218
- const proc = (0, import_node_child_process4.spawn)(resolveNpm(os33), ["install", "-g", "@openai/codex"], {
10503
+ const proc = (0, import_node_child_process4.spawn)(resolveNpm(os36), ["install", "-g", "@openai/codex"], {
11219
10504
  stdio: "inherit"
11220
10505
  });
11221
10506
  proc.on("close", (code) => {
@@ -11232,16 +10517,16 @@ async function installCodexViaNpm(os33) {
11232
10517
  });
11233
10518
  });
11234
10519
  }
11235
- function augmentNpmGlobalBin(os33) {
10520
+ function augmentNpmGlobalBin(os36) {
11236
10521
  try {
11237
- const result = (0, import_node_child_process4.spawnSync)(resolveNpm(os33), ["prefix", "-g"], {
10522
+ const result = (0, import_node_child_process4.spawnSync)(resolveNpm(os36), ["prefix", "-g"], {
11238
10523
  stdio: ["ignore", "pipe", "ignore"]
11239
10524
  });
11240
10525
  if (result.status !== 0) return;
11241
10526
  const prefix = result.stdout.toString().trim();
11242
10527
  if (!prefix) return;
11243
- const binDir = os33.id === "win32" ? prefix : path17.join(prefix, "bin");
11244
- os33.augmentPath([binDir]);
10528
+ const binDir = os36.id === "win32" ? prefix : path17.join(prefix, "bin");
10529
+ os36.augmentPath([binDir]);
11245
10530
  } catch {
11246
10531
  }
11247
10532
  }
@@ -11325,9 +10610,9 @@ var import_node_child_process7 = require("child_process");
11325
10610
  // src/agents/coderabbit/installer.ts
11326
10611
  var import_node_child_process5 = require("child_process");
11327
10612
  var INSTALL_URL = "https://cli.coderabbit.ai/install.sh";
11328
- async function ensureCoderabbitInstalled(os33) {
11329
- if (os33.findInPath("coderabbit")) return true;
11330
- if (os33.id === "win32") {
10613
+ async function ensureCoderabbitInstalled(os36) {
10614
+ if (os36.findInPath("coderabbit")) return true;
10615
+ if (os36.id === "win32") {
11331
10616
  console.error(
11332
10617
  "\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"
11333
10618
  );
@@ -11342,8 +10627,8 @@ async function ensureCoderabbitInstalled(os33) {
11342
10627
  proc.on("error", () => resolve7(false));
11343
10628
  });
11344
10629
  if (!ok) return false;
11345
- os33.augmentPath([`${os33.homeDir()}/.local/bin`, "/opt/homebrew/bin"]);
11346
- return os33.findInPath("coderabbit") !== null;
10630
+ os36.augmentPath([`${os36.homeDir()}/.local/bin`, "/opt/homebrew/bin"]);
10631
+ return os36.findInPath("coderabbit") !== null;
11347
10632
  }
11348
10633
 
11349
10634
  // src/agents/coderabbit/link.ts
@@ -11370,10 +10655,10 @@ function coderabbitCredentialLocator() {
11370
10655
  extract: extractLocalCoderabbitToken
11371
10656
  };
11372
10657
  }
11373
- function coderabbitLoginLauncher(os33) {
10658
+ function coderabbitLoginLauncher(os36) {
11374
10659
  return {
11375
10660
  async ensureInstalled() {
11376
- return ensureCoderabbitInstalled(os33);
10661
+ return ensureCoderabbitInstalled(os36);
11377
10662
  },
11378
10663
  launch() {
11379
10664
  return (0, import_node_child_process6.spawn)("coderabbit", ["login"], { stdio: "inherit" });
@@ -11396,11 +10681,11 @@ function parseReview(stdout) {
11396
10681
  for (const line of lines) {
11397
10682
  const m = line.match(HUNK_LINE_RE);
11398
10683
  if (!m) continue;
11399
- const [, path55, lineNo, sevToken, message] = m;
11400
- if (!path55 || !lineNo || !message) continue;
10684
+ const [, path58, lineNo, sevToken, message] = m;
10685
+ if (!path58 || !lineNo || !message) continue;
11401
10686
  const cleanedMessage = message.trim().replace(/^[*-]\s+/, "");
11402
10687
  hunks.push({
11403
- path: path55.trim(),
10688
+ path: path58.trim(),
11404
10689
  line: Number(lineNo),
11405
10690
  severity: sevToken ? SEVERITY_MAP[sevToken.toLowerCase()] : void 0,
11406
10691
  message: cleanedMessage
@@ -11419,8 +10704,8 @@ var CoderabbitRuntimeStrategy = class {
11419
10704
  meta = getAgent("coderabbit");
11420
10705
  mode = "batch";
11421
10706
  os;
11422
- constructor(os33) {
11423
- this.os = os33;
10707
+ constructor(os36) {
10708
+ this.os = os36;
11424
10709
  }
11425
10710
  getDefaultArgs() {
11426
10711
  return ["review"];
@@ -11535,10 +10820,10 @@ function cursorCredentialLocator() {
11535
10820
  extract: extractLocalCursorToken
11536
10821
  };
11537
10822
  }
11538
- function cursorLoginLauncher(os33) {
10823
+ function cursorLoginLauncher(os36) {
11539
10824
  return {
11540
10825
  async ensureInstalled() {
11541
- if (os33.findInPath("cursor-agent")) return true;
10826
+ if (os36.findInPath("cursor-agent")) return true;
11542
10827
  console.error(
11543
10828
  "\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"
11544
10829
  );
@@ -11600,8 +10885,8 @@ var CursorRuntimeStrategy = class {
11600
10885
  meta = getAgent("cursor");
11601
10886
  mode = "interactive";
11602
10887
  os;
11603
- constructor(os33) {
11604
- this.os = os33;
10888
+ constructor(os36) {
10889
+ this.os = os36;
11605
10890
  }
11606
10891
  async prepareLaunch() {
11607
10892
  const binary = this.os.findInPath("cursor-agent");
@@ -11721,10 +11006,10 @@ function aiderCredentialLocator() {
11721
11006
  extract: extractLocalAiderToken
11722
11007
  };
11723
11008
  }
11724
- function aiderLoginLauncher(os33) {
11009
+ function aiderLoginLauncher(os36) {
11725
11010
  return {
11726
11011
  async ensureInstalled() {
11727
- if (os33.findInPath("aider")) return true;
11012
+ if (os36.findInPath("aider")) return true;
11728
11013
  console.error(
11729
11014
  "\n \u2717 aider binary not on PATH.\n Install Aider:\n pip install aider-chat\n then re-run `codeam link aider`.\n"
11730
11015
  );
@@ -11734,7 +11019,7 @@ function aiderLoginLauncher(os33) {
11734
11019
  console.error(
11735
11020
  "\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"
11736
11021
  );
11737
- return (0, import_node_child_process9.spawn)(os33.id === "win32" ? "cmd.exe" : "sh", os33.id === "win32" ? ["/c", "exit", "0"] : ["-c", "exit 0"], {
11022
+ return (0, import_node_child_process9.spawn)(os36.id === "win32" ? "cmd.exe" : "sh", os36.id === "win32" ? ["/c", "exit", "0"] : ["-c", "exit 0"], {
11738
11023
  stdio: "ignore"
11739
11024
  });
11740
11025
  }
@@ -11806,8 +11091,8 @@ var AiderRuntimeStrategy = class {
11806
11091
  meta = getAgent("aider");
11807
11092
  mode = "interactive";
11808
11093
  os;
11809
- constructor(os33) {
11810
- this.os = os33;
11094
+ constructor(os36) {
11095
+ this.os = os36;
11811
11096
  }
11812
11097
  async prepareLaunch() {
11813
11098
  const binary = this.os.findInPath("aider");
@@ -11936,8 +11221,8 @@ function geminiCredentialLocator() {
11936
11221
  function geminiLoginLauncher() {
11937
11222
  return {
11938
11223
  async ensureInstalled() {
11939
- const os33 = createOsStrategy();
11940
- return os33.findInPath("gemini") !== null;
11224
+ const os36 = createOsStrategy();
11225
+ return os36.findInPath("gemini") !== null;
11941
11226
  },
11942
11227
  launch() {
11943
11228
  return (0, import_node_child_process10.spawn)("gemini", ["auth", "login"], { stdio: "inherit" });
@@ -11970,14 +11255,14 @@ var GeminiRuntimeStrategy = class {
11970
11255
  meta = getAgent("gemini");
11971
11256
  mode = "interactive";
11972
11257
  os;
11973
- constructor(os33) {
11974
- this.os = os33;
11258
+ constructor(os36) {
11259
+ this.os = os36;
11975
11260
  }
11976
11261
  async prepareLaunch() {
11977
11262
  const binary = this.os.findInPath("gemini");
11978
11263
  if (!binary) {
11979
11264
  throw new Error(
11980
- "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."
11265
+ "Gemini CLI is not on PATH. Install it with:\n npm install -g @google/gemini-cli\n Then run `codeam pair` again."
11981
11266
  );
11982
11267
  }
11983
11268
  return this.os.buildLaunch(binary);
@@ -12018,11 +11303,10 @@ var GeminiRuntimeStrategy = class {
12018
11303
  return { ptyInput: "/compress\r" };
12019
11304
  }
12020
11305
  /**
12021
- * Pass-through filter. Gemini's TUI chrome isn't worth
12022
- * hand-detectingusers who want clean output should use ACP
12023
- * (`CODEAM_ACP_ENABLED=1`) instead. Returning the input verbatim
12024
- * satisfies the contract's idempotency assertion and keeps mobile
12025
- * showing whatever the REPL prints.
11306
+ * Pass-through filter. Gemini runs over ACP, so this is never hit
11307
+ * in production it exists only to satisfy the contract's
11308
+ * idempotency assertion. Returning the input verbatim keeps the
11309
+ * stub trivially idempotent.
12026
11310
  */
12027
11311
  filterTuiOutput(lines) {
12028
11312
  return lines;
@@ -12071,18 +11355,18 @@ var GeminiRuntimeStrategy = class {
12071
11355
 
12072
11356
  // src/agents/registry.ts
12073
11357
  var runtimeBuilders = {
12074
- claude: (os33) => new ClaudeRuntimeStrategy(os33),
12075
- codex: (os33) => new CodexRuntimeStrategy(os33),
12076
- coderabbit: (os33) => new CoderabbitRuntimeStrategy(os33),
12077
- cursor: (os33) => new CursorRuntimeStrategy(os33),
12078
- aider: (os33) => new AiderRuntimeStrategy(os33),
12079
- gemini: (os33) => new GeminiRuntimeStrategy(os33)
11358
+ claude: (os36) => new ClaudeRuntimeStrategy(os36),
11359
+ codex: (os36) => new CodexRuntimeStrategy(os36),
11360
+ coderabbit: (os36) => new CoderabbitRuntimeStrategy(os36),
11361
+ cursor: (os36) => new CursorRuntimeStrategy(os36),
11362
+ aider: (os36) => new AiderRuntimeStrategy(os36),
11363
+ gemini: (os36) => new GeminiRuntimeStrategy(os36)
12080
11364
  };
12081
11365
  var deployBuilders = {
12082
11366
  claude: () => new ClaudeDeployStrategy(),
12083
11367
  codex: () => new CodexDeployStrategy()
12084
11368
  };
12085
- function createAgentStrategy(agent, os33 = createOsStrategy()) {
11369
+ function createAgentStrategy(agent, os36 = createOsStrategy()) {
12086
11370
  if (!AGENT_REGISTRY[agent]?.enabled) {
12087
11371
  throw new Error(
12088
11372
  `Agent "${agent}" is not supported in this codeam-cli version. Upgrade with 'npm i -g codeam-cli@latest'.`
@@ -12092,10 +11376,10 @@ function createAgentStrategy(agent, os33 = createOsStrategy()) {
12092
11376
  if (!build) {
12093
11377
  throw new Error(`No runtime strategy registered for agent "${agent}"`);
12094
11378
  }
12095
- return build(os33);
11379
+ return build(os36);
12096
11380
  }
12097
- function createInteractiveAgentStrategy(agent, os33 = createOsStrategy()) {
12098
- const s = createAgentStrategy(agent, os33);
11381
+ function createInteractiveAgentStrategy(agent, os36 = createOsStrategy()) {
11382
+ const s = createAgentStrategy(agent, os36);
12099
11383
  if (s.mode !== "interactive") {
12100
11384
  throw new Error(
12101
11385
  `Agent "${agent}" is a batch agent; use createAgentStrategy + .runOneShot for one-shot reviews.`
@@ -12172,8 +11456,8 @@ var REGISTRY = {
12172
11456
  // },
12173
11457
  //
12174
11458
  // Until then `getAcpAdapter('cursor')` returns null and the dispatch
12175
- // in start.ts falls back to the legacy PTY runtime — same behaviour
12176
- // cursor users had before ACP was added.
11459
+ // in start.ts runs cursor over the legacy PTY runtime — same
11460
+ // behaviour cursor users had before ACP was added.
12177
11461
  // Gemini speaks ACP natively via `gemini --acp` — no npm adapter
12178
11462
  // package, just the user-installed `gemini` binary on PATH. Same
12179
11463
  // {@link AdapterSpec} shape; the only difference is `command` is
@@ -12196,6 +11480,9 @@ function getAcpAdapter(agent) {
12196
11480
  const factory = REGISTRY[agent];
12197
11481
  return factory ? factory() : null;
12198
11482
  }
11483
+ function requiresAcp(agent) {
11484
+ return getAcpAdapter(agent) !== null;
11485
+ }
12199
11486
 
12200
11487
  // src/agents/acp/runner.ts
12201
11488
  var import_node_crypto7 = require("crypto");
@@ -17221,16 +16508,16 @@ async function downloadCloudflared(target) {
17221
16508
  );
17222
16509
  }
17223
16510
  function downloadUrlForPlatform() {
17224
- const platform2 = process.platform;
17225
- const arch = process.arch;
16511
+ const platform3 = process.platform;
16512
+ const arch2 = process.arch;
17226
16513
  const base = "https://github.com/cloudflare/cloudflared/releases/latest/download";
17227
- if (platform2 === "darwin" && arch === "arm64") return `${base}/cloudflared-darwin-arm64.tgz`;
17228
- if (platform2 === "darwin" && arch === "x64") return `${base}/cloudflared-darwin-amd64.tgz`;
17229
- if (platform2 === "linux" && arch === "x64") return `${base}/cloudflared-linux-amd64`;
17230
- if (platform2 === "linux" && arch === "arm64") return `${base}/cloudflared-linux-arm64`;
17231
- if (platform2 === "win32") return `${base}/cloudflared-windows-amd64.exe`;
16514
+ if (platform3 === "darwin" && arch2 === "arm64") return `${base}/cloudflared-darwin-arm64.tgz`;
16515
+ if (platform3 === "darwin" && arch2 === "x64") return `${base}/cloudflared-darwin-amd64.tgz`;
16516
+ if (platform3 === "linux" && arch2 === "x64") return `${base}/cloudflared-linux-amd64`;
16517
+ if (platform3 === "linux" && arch2 === "arm64") return `${base}/cloudflared-linux-arm64`;
16518
+ if (platform3 === "win32") return `${base}/cloudflared-windows-amd64.exe`;
17232
16519
  throw new Error(
17233
- `cloudflared auto-install not supported on ${platform2}/${arch}. Install manually from https://github.com/cloudflare/cloudflared/releases.`
16520
+ `cloudflared auto-install not supported on ${platform3}/${arch2}. Install manually from https://github.com/cloudflare/cloudflared/releases.`
17234
16521
  );
17235
16522
  }
17236
16523
 
@@ -17389,7 +16676,7 @@ function parseExpoUrl(stdout) {
17389
16676
 
17390
16677
  // src/services/preview/port-ready.ts
17391
16678
  var net = __toESM(require("net"));
17392
- function isPortListening(port, host = "127.0.0.1") {
16679
+ function isPortListening(port, host2 = "127.0.0.1") {
17393
16680
  return new Promise((resolve7) => {
17394
16681
  const socket = new net.Socket();
17395
16682
  const done = (result) => {
@@ -17401,7 +16688,7 @@ function isPortListening(port, host = "127.0.0.1") {
17401
16688
  socket.once("connect", () => done(true));
17402
16689
  socket.once("timeout", () => done(false));
17403
16690
  socket.once("error", () => done(false));
17404
- socket.connect(port, host);
16691
+ socket.connect(port, host2);
17405
16692
  });
17406
16693
  }
17407
16694
  async function waitForPortListening(port, opts) {
@@ -17934,8 +17221,8 @@ var path40 = __toESM(require("path"));
17934
17221
  var import_child_process14 = require("child_process");
17935
17222
  var INSTALL_SH_URL = "https://raw.githubusercontent.com/gastownhall/beads/main/scripts/install.sh";
17936
17223
  var INSTALL_PS1_URL = "https://raw.githubusercontent.com/gastownhall/beads/main/install.ps1";
17937
- function resolveInstallStrategy(platform2) {
17938
- if (platform2 === "win32") {
17224
+ function resolveInstallStrategy(platform3) {
17225
+ if (platform3 === "win32") {
17939
17226
  return {
17940
17227
  command: "powershell.exe",
17941
17228
  args: [
@@ -17978,8 +17265,8 @@ function _defaultInstallSpawn(strategy) {
17978
17265
  );
17979
17266
  });
17980
17267
  }
17981
- async function installBd(platform2 = process.platform) {
17982
- const strategy = resolveInstallStrategy(platform2);
17268
+ async function installBd(platform3 = process.platform) {
17269
+ const strategy = resolveInstallStrategy(platform3);
17983
17270
  log.info("beads", `installing bd via ${strategy.description}`);
17984
17271
  const result = await _installSpawnSeam.run(strategy);
17985
17272
  if (!result.ok) {
@@ -17998,8 +17285,8 @@ var os25 = __toESM(require("os"));
17998
17285
  var path38 = __toESM(require("path"));
17999
17286
  var DOLT_INSTALL_SH_URL = "https://github.com/dolthub/dolt/releases/latest/download/install.sh";
18000
17287
  var DOLT_MSI_URL = "https://github.com/dolthub/dolt/releases/latest/download/dolt-windows-amd64.msi";
18001
- function resolveDoltInstallStrategy(platform2) {
18002
- if (platform2 === "win32") {
17288
+ function resolveDoltInstallStrategy(platform3) {
17289
+ if (platform3 === "win32") {
18003
17290
  const script = [
18004
17291
  `$ErrorActionPreference='Stop';`,
18005
17292
  `$u='${DOLT_MSI_URL}';`,
@@ -18020,7 +17307,7 @@ function resolveDoltInstallStrategy(platform2) {
18020
17307
  description: "PowerShell: download official dolt MSI + silent msiexec"
18021
17308
  };
18022
17309
  }
18023
- if (platform2 === "darwin") {
17310
+ if (platform3 === "darwin") {
18024
17311
  return {
18025
17312
  command: "brew",
18026
17313
  args: ["install", "dolt"],
@@ -18034,17 +17321,17 @@ function resolveDoltInstallStrategy(platform2) {
18034
17321
  };
18035
17322
  }
18036
17323
  var DOLT_RELEASE_BASE = "https://github.com/dolthub/dolt/releases/latest/download";
18037
- function doltPlatformTuple(platform2, arch) {
18038
- const os33 = platform2 === "win32" ? "windows" : platform2 === "darwin" ? "darwin" : "linux";
18039
- const a = arch === "x64" ? "amd64" : arch === "arm64" ? "arm64" : null;
17324
+ function doltPlatformTuple(platform3, arch2) {
17325
+ const os36 = platform3 === "win32" ? "windows" : platform3 === "darwin" ? "darwin" : "linux";
17326
+ const a = arch2 === "x64" ? "amd64" : arch2 === "arm64" ? "arm64" : null;
18040
17327
  if (!a) return null;
18041
- if (os33 === "windows" && a !== "amd64") return null;
18042
- return `${os33}-${a}`;
17328
+ if (os36 === "windows" && a !== "amd64") return null;
17329
+ return `${os36}-${a}`;
18043
17330
  }
18044
- function resolveDoltTarballStrategy(targetDir, platform2, arch) {
18045
- const tuple = doltPlatformTuple(platform2, arch);
17331
+ function resolveDoltTarballStrategy(targetDir, platform3, arch2) {
17332
+ const tuple = doltPlatformTuple(platform3, arch2);
18046
17333
  if (!tuple) return null;
18047
- if (platform2 === "win32") {
17334
+ if (platform3 === "win32") {
18048
17335
  const url2 = `${DOLT_RELEASE_BASE}/dolt-${tuple}.zip`;
18049
17336
  const script = [
18050
17337
  `$ErrorActionPreference='Stop';`,
@@ -18070,11 +17357,11 @@ function resolveDoltTarballStrategy(targetDir, platform2, arch) {
18070
17357
  description: `download dolt ${tuple} tarball \u2192 ${targetDir} (no sudo)`
18071
17358
  };
18072
17359
  }
18073
- async function installDoltToDir(targetDir, platform2 = process.platform, arch = process.arch) {
18074
- const strategy = resolveDoltTarballStrategy(targetDir, platform2, arch);
17360
+ async function installDoltToDir(targetDir, platform3 = process.platform, arch2 = process.arch) {
17361
+ const strategy = resolveDoltTarballStrategy(targetDir, platform3, arch2);
18075
17362
  if (!strategy) {
18076
- log.warn("beads", `no dolt prebuilt for ${platform2}/${arch} \u2014 cannot fall back`);
18077
- return { ok: false, code: -1, stderr: `unsupported platform ${platform2}/${arch}` };
17363
+ log.warn("beads", `no dolt prebuilt for ${platform3}/${arch2} \u2014 cannot fall back`);
17364
+ return { ok: false, code: -1, stderr: `unsupported platform ${platform3}/${arch2}` };
18078
17365
  }
18079
17366
  log.info("beads", `dolt fallback: ${strategy.description}`);
18080
17367
  const result = await _doltInstallSpawnSeam.run(strategy);
@@ -18098,13 +17385,13 @@ var _doltPathSeam = {
18098
17385
  }
18099
17386
  }
18100
17387
  };
18101
- function doltBinaryNames(platform2) {
18102
- return platform2 === "win32" ? ["dolt.exe", "dolt.cmd", "dolt"] : ["dolt"];
17388
+ function doltBinaryNames(platform3) {
17389
+ return platform3 === "win32" ? ["dolt.exe", "dolt.cmd", "dolt"] : ["dolt"];
18103
17390
  }
18104
- function knownDoltDirs(platform2) {
18105
- const P3 = platform2 === "win32" ? path38.win32 : path38.posix;
17391
+ function knownDoltDirs(platform3) {
17392
+ const P3 = platform3 === "win32" ? path38.win32 : path38.posix;
18106
17393
  const home = _doltPathSeam.homedir();
18107
- if (platform2 === "win32") {
17394
+ if (platform3 === "win32") {
18108
17395
  return [
18109
17396
  "C:\\Program Files\\Dolt\\bin",
18110
17397
  home ? P3.join(home, "AppData", "Local", "Programs", "dolt", "bin") : ""
@@ -18117,17 +17404,17 @@ function knownDoltDirs(platform2) {
18117
17404
  home ? P3.join(home, "bin") : ""
18118
17405
  ].filter(Boolean);
18119
17406
  }
18120
- function ensureDoltResolvable(platform2 = process.platform) {
18121
- const P3 = platform2 === "win32" ? path38.win32 : path38.posix;
18122
- const delim = platform2 === "win32" ? ";" : ":";
18123
- const names = doltBinaryNames(platform2);
17407
+ function ensureDoltResolvable(platform3 = process.platform) {
17408
+ const P3 = platform3 === "win32" ? path38.win32 : path38.posix;
17409
+ const delim = platform3 === "win32" ? ";" : ":";
17410
+ const names = doltBinaryNames(platform3);
18124
17411
  const pathDirs = _doltPathSeam.getPath().split(delim).filter(Boolean);
18125
17412
  for (const dir of pathDirs) {
18126
17413
  for (const n of names) {
18127
17414
  if (_doltPathSeam.exists(P3.join(dir, n))) return true;
18128
17415
  }
18129
17416
  }
18130
- for (const dir of knownDoltDirs(platform2)) {
17417
+ for (const dir of knownDoltDirs(platform3)) {
18131
17418
  for (const n of names) {
18132
17419
  if (_doltPathSeam.exists(P3.join(dir, n))) {
18133
17420
  _doltPathSeam.setPath(`${dir}${delim}${_doltPathSeam.getPath()}`);
@@ -18181,8 +17468,8 @@ function _defaultDoltInstallSpawn(strategy) {
18181
17468
  proc.on("close", (code) => finish({ ok: code === 0, code: code ?? -1, stderr }));
18182
17469
  });
18183
17470
  }
18184
- async function installDolt(platform2 = process.platform) {
18185
- const strategy = resolveDoltInstallStrategy(platform2);
17471
+ async function installDolt(platform3 = process.platform) {
17472
+ const strategy = resolveDoltInstallStrategy(platform3);
18186
17473
  log.info("beads", `installing dolt via ${strategy.description}`);
18187
17474
  const result = await _doltInstallSpawnSeam.run(strategy);
18188
17475
  if (!result.ok) {
@@ -18234,11 +17521,11 @@ var path39 = __toESM(require("path"));
18234
17521
  function normalizeOrigin(raw) {
18235
17522
  const trimmed = raw.trim();
18236
17523
  if (!trimmed) return null;
18237
- let host;
17524
+ let host2;
18238
17525
  let pathPart;
18239
17526
  const scpLike = /^[^/@]+@([^:]+):(.+)$/.exec(trimmed);
18240
17527
  if (scpLike && !trimmed.includes("://")) {
18241
- host = scpLike[1];
17528
+ host2 = scpLike[1];
18242
17529
  pathPart = scpLike[2];
18243
17530
  } else {
18244
17531
  let url;
@@ -18247,13 +17534,13 @@ function normalizeOrigin(raw) {
18247
17534
  } catch {
18248
17535
  return null;
18249
17536
  }
18250
- host = url.hostname;
17537
+ host2 = url.hostname;
18251
17538
  pathPart = url.pathname;
18252
17539
  }
18253
- host = host.toLowerCase();
17540
+ host2 = host2.toLowerCase();
18254
17541
  pathPart = pathPart.replace(/^\/+/, "").replace(/\.git$/i, "").replace(/\/+$/, "");
18255
- if (!host || !pathPart) return null;
18256
- return `${host}/${pathPart}`;
17542
+ if (!host2 || !pathPart) return null;
17543
+ return `${host2}/${pathPart}`;
18257
17544
  }
18258
17545
  function findRepoRoot(cwd) {
18259
17546
  let dir = path39.resolve(cwd);
@@ -18769,11 +18056,11 @@ var BeadsWatcher = class {
18769
18056
 
18770
18057
  // src/beads/inherit-team-memories.ts
18771
18058
  async function inheritTeamMemories(opts) {
18772
- const apiBase = opts.apiBaseUrl ?? resolveApiBaseUrl();
18059
+ const apiBase2 = opts.apiBaseUrl ?? resolveApiBaseUrl();
18773
18060
  let memories = [];
18774
18061
  try {
18775
18062
  const res = await _transport3.post(
18776
- `${apiBase}/api/beads/team-memories`,
18063
+ `${apiBase2}/api/beads/team-memories`,
18777
18064
  {
18778
18065
  "Content-Type": "application/json",
18779
18066
  "X-Codeam-Protocol-Version": "2.0.0",
@@ -20153,10 +19440,10 @@ var WINDOWS_LEGACY_JUNCTIONS = [
20153
19440
  /[\\/]Start Menu([\\/]|$)/i,
20154
19441
  /[\\/]Templates([\\/]|$)/i
20155
19442
  ];
20156
- function isUnsafeWindowsWatchRoot(dir, homedir26) {
19443
+ function isUnsafeWindowsWatchRoot(dir, homedir29) {
20157
19444
  const norm = (p2) => p2.replace(/\//g, "\\").replace(/\\+$/, "").toLowerCase();
20158
19445
  const cwd = norm(dir);
20159
- const home = norm(homedir26);
19446
+ const home = norm(homedir29);
20160
19447
  if (cwd === home) return true;
20161
19448
  if (/^[a-z]:$/.test(cwd)) return true;
20162
19449
  const sysRoots = [
@@ -20950,7 +20237,7 @@ function defaultRunGit(cwd, args2) {
20950
20237
  });
20951
20238
  }
20952
20239
  async function discoverRepos(workingDir, maxDepth = 4) {
20953
- const fs43 = await import("fs/promises");
20240
+ const fs46 = await import("fs/promises");
20954
20241
  const out2 = [];
20955
20242
  await walk(workingDir, 0);
20956
20243
  return out2;
@@ -20958,7 +20245,7 @@ async function discoverRepos(workingDir, maxDepth = 4) {
20958
20245
  if (depth > maxDepth) return;
20959
20246
  let entries = [];
20960
20247
  try {
20961
- const dirents = await fs43.readdir(dir, { withFileTypes: true });
20248
+ const dirents = await fs46.readdir(dir, { withFileTypes: true });
20962
20249
  entries = dirents.filter((d3) => !d3.name.startsWith(".") || d3.name === ".git").map((d3) => ({ name: d3.name, isDirectory: d3.isDirectory() }));
20963
20250
  } catch {
20964
20251
  return;
@@ -24041,28 +23328,28 @@ async function start(requestedAgent) {
24041
23328
  `agent-spawn gate released \u2014 beads ${beads ? "ready" : "pending"}; project deps provisioned`
24042
23329
  );
24043
23330
  }
24044
- const acpDisabled = process.env.CODEAM_ACP_DISABLED === "1";
24045
- if (!acpDisabled && session.pluginAuthToken) {
23331
+ if (requiresAcp(session.agent)) {
24046
23332
  const adapter = getAcpAdapter(session.agent);
24047
- if (adapter) {
24048
- await runAcpSession({
24049
- agent: session.agent,
24050
- sessionId: session.id,
24051
- pluginId,
24052
- pluginAuthToken: session.pluginAuthToken,
24053
- adapter,
24054
- cwd,
24055
- getBeads,
24056
- // AUTO mode in a headless GitHub Codespace: no human at the phone to
24057
- // answer permission prompts, so auto-approve them rather than stall the
24058
- // turn (the agent-agnostic equivalent of --dangerously-skip-permissions).
24059
- autoApprovePermissions: process.env.CODESPACES === "true"
24060
- });
24061
- return;
23333
+ if (!adapter || !session.pluginAuthToken) {
23334
+ showError(
23335
+ `${AGENT_REGISTRY[session.agent].displayName} requires a paired session with an auth token. Re-pair with \`codeam pair\` to continue.`
23336
+ );
23337
+ process.exit(1);
24062
23338
  }
24063
- }
24064
- if (acpDisabled) {
24065
- showInfo("CODEAM_ACP_DISABLED is set \u2014 running the legacy PTY pipeline.");
23339
+ await runAcpSession({
23340
+ agent: session.agent,
23341
+ sessionId: session.id,
23342
+ pluginId,
23343
+ pluginAuthToken: session.pluginAuthToken,
23344
+ adapter,
23345
+ cwd,
23346
+ getBeads,
23347
+ // AUTO mode in a headless GitHub Codespace: no human at the phone to
23348
+ // answer permission prompts, so auto-approve them rather than stall the
23349
+ // turn (the agent-agnostic equivalent of --dangerously-skip-permissions).
23350
+ autoApprovePermissions: process.env.CODESPACES === "true"
23351
+ });
23352
+ return;
24066
23353
  }
24067
23354
  const runtime = createRuntimeStrategy(session.agent);
24068
23355
  const historySvc = new HistoryService(runtime, pluginId, cwd, {
@@ -24633,12 +23920,12 @@ function readTokenFromArgs(args2) {
24633
23920
  }
24634
23921
  const fileFlag = args2.find((a) => a.startsWith("--token-file="));
24635
23922
  if (fileFlag) {
24636
- const path55 = fileFlag.slice("--token-file=".length);
23923
+ const path58 = fileFlag.slice("--token-file=".length);
24637
23924
  try {
24638
- const content = fs40.readFileSync(path55, "utf8").trim();
24639
- if (content.length === 0) fail(`--token-file ${path55} is empty`);
23925
+ const content = fs40.readFileSync(path58, "utf8").trim();
23926
+ if (content.length === 0) fail(`--token-file ${path58} is empty`);
24640
23927
  try {
24641
- fs40.unlinkSync(path55);
23928
+ fs40.unlinkSync(path58);
24642
23929
  } catch {
24643
23930
  }
24644
23931
  return content;
@@ -25122,12 +24409,12 @@ var GitHubCodespacesProvider = class {
25122
24409
  * install error if it's still missing.
25123
24410
  */
25124
24411
  async tryInstallGh() {
25125
- const platform2 = process.platform;
24412
+ const platform3 = process.platform;
25126
24413
  wt(
25127
24414
  `GitHub CLI (${import_picocolors8.default.cyan("gh")}) is required for Codespaces deploys but isn't on your PATH.`,
25128
24415
  "Heads up"
25129
24416
  );
25130
- if (platform2 === "linux") {
24417
+ if (platform3 === "linux") {
25131
24418
  wt(
25132
24419
  [
25133
24420
  "On Linux, please install gh from the official guide:",
@@ -25139,7 +24426,7 @@ var GitHubCodespacesProvider = class {
25139
24426
  return;
25140
24427
  }
25141
24428
  let installCmd = null;
25142
- if (platform2 === "darwin") {
24429
+ if (platform3 === "darwin") {
25143
24430
  try {
25144
24431
  await execFileP5("brew", ["--version"], { maxBuffer: MAX_BUFFER });
25145
24432
  } catch {
@@ -25158,7 +24445,7 @@ var GitHubCodespacesProvider = class {
25158
24445
  args: ["install", "gh"],
25159
24446
  describe: "brew install gh"
25160
24447
  };
25161
- } else if (platform2 === "win32") {
24448
+ } else if (platform3 === "win32") {
25162
24449
  try {
25163
24450
  await execFileP5("winget", ["--version"], { maxBuffer: MAX_BUFFER });
25164
24451
  } catch {
@@ -26888,50 +26175,488 @@ async function probeCodeamPair(provider, workspace) {
26888
26175
  }
26889
26176
  async function stopWorkspaceFromLocal(target) {
26890
26177
  if (target.provider.id === "github-codespaces") {
26891
- const { execFile: execFile9 } = await import("child_process");
26892
- const { promisify: promisify10 } = await import("util");
26893
- const execFileP9 = promisify10(execFile9);
26894
- await execFileP9("gh", ["codespace", "stop", "-c", target.id], { maxBuffer: 8 * 1024 * 1024 });
26178
+ const { execFile: execFile10 } = await import("child_process");
26179
+ const { promisify: promisify11 } = await import("util");
26180
+ const execFileP10 = promisify11(execFile10);
26181
+ await execFileP10("gh", ["codespace", "stop", "-c", target.id], { maxBuffer: 8 * 1024 * 1024 });
26895
26182
  return;
26896
26183
  }
26897
26184
  }
26898
26185
 
26186
+ // src/commands/host/host-client.ts
26187
+ var fs41 = __toESM(require("fs"));
26188
+ var os32 = __toESM(require("os"));
26189
+ var path53 = __toESM(require("path"));
26190
+ function apiBase() {
26191
+ return process.env.CODEAM_API_URL ?? resolveApiBaseUrl();
26192
+ }
26193
+ function hostIdentityPath() {
26194
+ return path53.join(os32.homedir(), ".codeam", "host-agent.json");
26195
+ }
26196
+ function collectOsInfo() {
26197
+ return {
26198
+ distro: os32.platform(),
26199
+ arch: os32.arch(),
26200
+ kernel: os32.release(),
26201
+ nodeVersion: process.versions.node
26202
+ };
26203
+ }
26204
+ function loadHostIdentity() {
26205
+ try {
26206
+ const raw = fs41.readFileSync(hostIdentityPath(), "utf8");
26207
+ const parsed = JSON.parse(raw);
26208
+ if (typeof parsed === "object" && parsed !== null && typeof parsed.hostId === "string" && typeof parsed.hostToken === "string" && typeof parsed.controlPluginId === "string") {
26209
+ const p2 = parsed;
26210
+ return { hostId: p2.hostId, hostToken: p2.hostToken, controlPluginId: p2.controlPluginId };
26211
+ }
26212
+ return null;
26213
+ } catch {
26214
+ return null;
26215
+ }
26216
+ }
26217
+ function saveHostIdentity(identity) {
26218
+ const file = hostIdentityPath();
26219
+ fs41.mkdirSync(path53.dirname(file), { recursive: true, mode: 448 });
26220
+ fs41.writeFileSync(file, JSON.stringify(identity, null, 2), {
26221
+ encoding: "utf8",
26222
+ mode: 384
26223
+ });
26224
+ fs41.chmodSync(file, 384);
26225
+ }
26226
+ async function postJson(pathname, body) {
26227
+ const res = await fetch(`${apiBase()}${pathname}`, {
26228
+ method: "POST",
26229
+ headers: { "Content-Type": "application/json", ...vercelBypassHeader() },
26230
+ body: JSON.stringify(body)
26231
+ });
26232
+ const json = await res.json();
26233
+ if (!res.ok || !json.success) {
26234
+ const err = !json.success ? json.error : void 0;
26235
+ throw new Error(
26236
+ `${pathname} failed (${err?.code ?? `HTTP_${res.status}`}): ${err?.message ?? res.statusText}`
26237
+ );
26238
+ }
26239
+ return json.data;
26240
+ }
26241
+ async function redeemEnrollToken(token, label) {
26242
+ const data = await postJson("/api/self-hosted/redeem", {
26243
+ token,
26244
+ ...label ? { label } : {},
26245
+ osInfo: collectOsInfo()
26246
+ });
26247
+ return {
26248
+ hostId: data.hostId,
26249
+ hostToken: data.hostToken,
26250
+ controlPluginId: data.controlPluginId
26251
+ };
26252
+ }
26253
+ async function sendHostHeartbeat(identity) {
26254
+ await postJson("/api/self-hosted/heartbeat", {
26255
+ hostId: identity.hostId,
26256
+ hostToken: identity.hostToken
26257
+ });
26258
+ }
26259
+ function isSealedEnvelope(v) {
26260
+ if (typeof v !== "object" || v === null) return false;
26261
+ const o = v;
26262
+ return typeof o.ciphertext === "string" && typeof o.iv === "string" && typeof o.authTag === "string" && typeof o.keyVersion === "number";
26263
+ }
26264
+ function isAgentAuth(v) {
26265
+ if (typeof v !== "object" || v === null) return false;
26266
+ const o = v;
26267
+ return (o.kind === "oauth_token" || o.kind === "api_key") && typeof o.value === "string";
26268
+ }
26269
+ async function unsealAgentAuth(identity, sealedAgentAuth) {
26270
+ const envelope = JSON.parse(sealedAgentAuth);
26271
+ if (!isSealedEnvelope(envelope)) {
26272
+ throw new Error("sealedAgentAuth is not a valid vault envelope");
26273
+ }
26274
+ const data = await postJson(
26275
+ "/api/self-hosted/unseal-agent-auth",
26276
+ {
26277
+ hostId: identity.hostId,
26278
+ hostToken: identity.hostToken,
26279
+ sealedAgentAuth
26280
+ }
26281
+ );
26282
+ if (!isAgentAuth(data)) {
26283
+ throw new Error("unseal-agent-auth returned an unexpected shape");
26284
+ }
26285
+ return data;
26286
+ }
26287
+
26288
+ // src/commands/host.ts
26289
+ function readTokenFlag(args2) {
26290
+ const flag = args2.find((a) => a.startsWith("--token="));
26291
+ if (!flag) return void 0;
26292
+ const value = flag.slice("--token=".length).trim();
26293
+ return value.length > 0 ? value : void 0;
26294
+ }
26295
+ function readLabelFlag(args2) {
26296
+ const flag = args2.find((a) => a.startsWith("--label="));
26297
+ if (!flag) return void 0;
26298
+ const value = flag.slice("--label=".length).trim();
26299
+ return value.length > 0 ? value : void 0;
26300
+ }
26301
+ async function hostEnroll(args2) {
26302
+ const existing = loadHostIdentity();
26303
+ if (existing) {
26304
+ console.log(
26305
+ ` Already enrolled (host ${existing.hostId}). Run \`codeam host-agent\` to connect.`
26306
+ );
26307
+ return;
26308
+ }
26309
+ const token = readTokenFlag(args2);
26310
+ if (!token) {
26311
+ throw new Error(
26312
+ "codeam host enroll requires --token=<EPHEMERAL>. Copy the command from the app."
26313
+ );
26314
+ }
26315
+ const identity = await redeemEnrollToken(token, readLabelFlag(args2));
26316
+ saveHostIdentity(identity);
26317
+ console.log(
26318
+ ` Enrolled host ${identity.hostId}. Sealed credentials to ~/.codeam/host-agent.json (0600).`
26319
+ );
26320
+ console.log(" Run `codeam host-agent` (or let systemd start it) to connect.");
26321
+ }
26322
+ async function host(args2) {
26323
+ const sub = args2[0];
26324
+ if (sub === "enroll") {
26325
+ await hostEnroll(args2.slice(1));
26326
+ return;
26327
+ }
26328
+ throw new Error(
26329
+ `Unknown 'codeam host' subcommand: ${sub ?? "(none)"}. Try: codeam host enroll --token=<EPHEMERAL>`
26330
+ );
26331
+ }
26332
+
26333
+ // src/commands/host-agent.ts
26334
+ var import_node_child_process13 = require("child_process");
26335
+
26336
+ // src/commands/host/workspace.ts
26337
+ var fs42 = __toESM(require("fs"));
26338
+ var os33 = __toESM(require("os"));
26339
+ var path54 = __toESM(require("path"));
26340
+ var import_node_child_process12 = require("child_process");
26341
+ var import_node_util4 = require("util");
26342
+ var execFileP9 = (0, import_node_util4.promisify)(import_node_child_process12.execFile);
26343
+ function isAbsolutePathTarget(target) {
26344
+ return path54.isAbsolute(target);
26345
+ }
26346
+ function selfHostedWorkspaceRoot() {
26347
+ return path54.join(os33.homedir(), ".codeam", "self-hosted");
26348
+ }
26349
+ function repoCloneUrl(repoRef) {
26350
+ const trimmed = repoRef.trim();
26351
+ if (/^https?:\/\//.test(trimmed) || trimmed.startsWith("git@")) return trimmed;
26352
+ return `https://github.com/${trimmed.replace(/\.git$/, "")}.git`;
26353
+ }
26354
+ async function prepareWorkspace(repoOrPath, deployId) {
26355
+ if (isAbsolutePathTarget(repoOrPath)) {
26356
+ if (!fs42.existsSync(repoOrPath)) {
26357
+ throw new Error(`deploy target path does not exist: ${repoOrPath}`);
26358
+ }
26359
+ return repoOrPath;
26360
+ }
26361
+ const dest = path54.join(selfHostedWorkspaceRoot(), deployId);
26362
+ if (fs42.existsSync(path54.join(dest, ".git"))) {
26363
+ return dest;
26364
+ }
26365
+ fs42.mkdirSync(selfHostedWorkspaceRoot(), { recursive: true, mode: 448 });
26366
+ await execFileP9("git", ["clone", "--depth", "1", repoCloneUrl(repoOrPath), dest], {
26367
+ timeout: 12e4,
26368
+ maxBuffer: 16 * 1024 * 1024
26369
+ });
26370
+ return dest;
26371
+ }
26372
+
26373
+ // src/commands/host/agent-provisioning.ts
26374
+ var fs43 = __toESM(require("fs"));
26375
+ var os34 = __toESM(require("os"));
26376
+ var path55 = __toESM(require("path"));
26377
+ var PUBLIC_TO_INTERNAL_AGENT = {
26378
+ claude_code: "claude",
26379
+ claude: "claude",
26380
+ codex: "codex",
26381
+ copilot: "copilot",
26382
+ cursor: "cursor",
26383
+ aider: "aider",
26384
+ coderabbit: "coderabbit",
26385
+ gemini: "gemini"
26386
+ };
26387
+ function toInternalAgentId(publicAgentId) {
26388
+ return PUBLIC_TO_INTERNAL_AGENT[publicAgentId] ?? null;
26389
+ }
26390
+ function ensureDir(dir) {
26391
+ fs43.mkdirSync(dir, { recursive: true, mode: 448 });
26392
+ }
26393
+ function writeFile0600(filePath, contents) {
26394
+ ensureDir(path55.dirname(filePath));
26395
+ fs43.writeFileSync(filePath, contents, { encoding: "utf8", mode: 384 });
26396
+ fs43.chmodSync(filePath, 384);
26397
+ }
26398
+ var claudeProvisioner = {
26399
+ write(auth, home) {
26400
+ if (auth.kind === "api_key") {
26401
+ return { ANTHROPIC_API_KEY: auth.value };
26402
+ }
26403
+ writeFile0600(path55.join(home, ".claude", ".credentials.json"), auth.value);
26404
+ const claudeJson = path55.join(home, ".claude.json");
26405
+ if (!fs43.existsSync(claudeJson)) {
26406
+ writeFile0600(
26407
+ claudeJson,
26408
+ JSON.stringify({ hasCompletedOnboarding: true, customApiKeyResponses: { approved: [] } })
26409
+ );
26410
+ }
26411
+ return {};
26412
+ }
26413
+ };
26414
+ var codexProvisioner = {
26415
+ write(auth, home) {
26416
+ if (auth.kind === "api_key") {
26417
+ return { OPENAI_API_KEY: auth.value };
26418
+ }
26419
+ writeFile0600(path55.join(home, ".codex", "auth.json"), auth.value);
26420
+ return {};
26421
+ }
26422
+ };
26423
+ var PROVISIONERS = {
26424
+ claude: claudeProvisioner,
26425
+ codex: codexProvisioner
26426
+ };
26427
+ var UnsupportedAgentError = class extends Error {
26428
+ agentId;
26429
+ constructor(agentId) {
26430
+ super(`Self-hosted provisioning is not implemented for agent "${agentId}"`);
26431
+ this.name = "UnsupportedAgentError";
26432
+ this.agentId = agentId;
26433
+ }
26434
+ };
26435
+ function provisionAgentCredentials(publicAgentId, auth, homeDir2 = os34.homedir()) {
26436
+ const internal = toInternalAgentId(publicAgentId);
26437
+ if (!internal) throw new UnsupportedAgentError(publicAgentId);
26438
+ const provisioner = PROVISIONERS[internal];
26439
+ if (!provisioner) throw new UnsupportedAgentError(publicAgentId);
26440
+ return provisioner.write(auth, homeDir2);
26441
+ }
26442
+
26443
+ // src/commands/host-agent.ts
26444
+ var HEARTBEAT_INTERVAL_MS = 2e4;
26445
+ function isDeployPayload(p2) {
26446
+ return typeof p2.deployId === "string" && typeof p2.repoOrPath === "string" && typeof p2.agentId === "string" && typeof p2.sealedAgentAuth === "string" && typeof p2.autoPairToken === "string";
26447
+ }
26448
+ function isStopPayload(p2) {
26449
+ return typeof p2.sessionId === "string";
26450
+ }
26451
+ var CONTROL_AGENT_META = {
26452
+ id: "claude",
26453
+ displayName: "CodeAgent Host Agent",
26454
+ binaryName: "codeam",
26455
+ enabled: true,
26456
+ supportedAuthKinds: ["oauth_token"],
26457
+ preferredAuthKind: "oauth_token"
26458
+ };
26459
+ var defaultSpawner = (env, cwd) => (0, import_node_child_process13.spawn)(process.execPath, [process.argv[1], "pair-auto"], {
26460
+ cwd,
26461
+ env: { ...process.env, ...env },
26462
+ stdio: "ignore",
26463
+ detached: false
26464
+ });
26465
+ var HostAgentSupervisor = class {
26466
+ constructor(identity, deps = {}) {
26467
+ this.identity = identity;
26468
+ this.deps = deps;
26469
+ this.spawnChild = deps.spawnChild ?? defaultSpawner;
26470
+ this.resolveAgentAuth = deps.resolveAgentAuth ?? unsealAgentAuth;
26471
+ }
26472
+ identity;
26473
+ deps;
26474
+ children = /* @__PURE__ */ new Map();
26475
+ spawnChild;
26476
+ resolveAgentAuth;
26477
+ relay = null;
26478
+ heartbeatTimer = null;
26479
+ /** Open the control channel (reusing the relay) + start heartbeats. */
26480
+ start() {
26481
+ const make = this.deps.makeRelay ?? ((pluginId, onCommand, meta) => new CommandRelayService(pluginId, onCommand, meta));
26482
+ this.relay = make(
26483
+ this.identity.controlPluginId,
26484
+ (cmd) => this.handleCommand(cmd),
26485
+ CONTROL_AGENT_META
26486
+ );
26487
+ this.relay.start();
26488
+ void this.beat();
26489
+ this.heartbeatTimer = setInterval(() => void this.beat(), HEARTBEAT_INTERVAL_MS);
26490
+ this.heartbeatTimer.unref?.();
26491
+ log.info("host-agent", `supervisor up host=${this.identity.hostId.slice(0, 8)}`);
26492
+ }
26493
+ /** Stop the control channel + heartbeats + kill every child. */
26494
+ stop() {
26495
+ if (this.heartbeatTimer) {
26496
+ clearInterval(this.heartbeatTimer);
26497
+ this.heartbeatTimer = null;
26498
+ }
26499
+ this.relay?.stop();
26500
+ for (const child of this.children.values()) {
26501
+ try {
26502
+ child.proc.kill("SIGTERM");
26503
+ } catch {
26504
+ }
26505
+ }
26506
+ this.children.clear();
26507
+ }
26508
+ async beat() {
26509
+ try {
26510
+ await sendHostHeartbeat(this.identity);
26511
+ } catch (err) {
26512
+ log.trace("host-agent", "heartbeat failed", err);
26513
+ }
26514
+ }
26515
+ /** Number of live children — for tests + diagnostics. */
26516
+ childCount() {
26517
+ return this.children.size;
26518
+ }
26519
+ /**
26520
+ * Route a relay command. Only `self_hosted_deploy` / `self_hosted_stop`
26521
+ * are understood; anything else is ignored (the box accepts no
26522
+ * arbitrary command surface).
26523
+ */
26524
+ async handleCommand(cmd) {
26525
+ if (cmd.type === "self_hosted_deploy") {
26526
+ if (!isDeployPayload(cmd.payload)) {
26527
+ log.warn("host-agent", `ignoring malformed self_hosted_deploy id=${cmd.id}`);
26528
+ return;
26529
+ }
26530
+ await this.deploy(cmd.payload);
26531
+ return;
26532
+ }
26533
+ if (cmd.type === "self_hosted_stop") {
26534
+ if (!isStopPayload(cmd.payload)) {
26535
+ log.warn("host-agent", `ignoring malformed self_hosted_stop id=${cmd.id}`);
26536
+ return;
26537
+ }
26538
+ this.stopChild(cmd.payload.sessionId);
26539
+ return;
26540
+ }
26541
+ log.trace("host-agent", `ignoring unsupported command type=${cmd.type}`);
26542
+ }
26543
+ /**
26544
+ * Prepare the workspace, write the agent credential (same as codespace
26545
+ * provisioning), and spawn a supervised `codeam pair-auto` child.
26546
+ *
26547
+ * The child claims its session via `CODEAM_AUTO_TOKEN`; the backend's
26548
+ * claim response is what binds the sessionId on the server side. We key
26549
+ * the child by `deployId` immediately (and adopt the sessionId — which
26550
+ * for self-hosted equals the deployId-correlated session) so a
26551
+ * subsequent `self_hosted_stop` can find it.
26552
+ */
26553
+ async deploy(payload) {
26554
+ log.info(
26555
+ "host-agent",
26556
+ `deploy id=${payload.deployId.slice(0, 8)} agent=${payload.agentId} target=${payload.repoOrPath}`
26557
+ );
26558
+ const cwd = await prepareWorkspace(payload.repoOrPath, payload.deployId);
26559
+ const auth = await this.resolveAgentAuth(this.identity, payload.sealedAgentAuth);
26560
+ const credEnv = provisionAgentCredentials(payload.agentId, auth, void 0);
26561
+ const childEnv = {
26562
+ ...credEnv,
26563
+ CODEAM_AUTO_TOKEN: payload.autoPairToken
26564
+ };
26565
+ const proc = this.spawnChild(childEnv, cwd);
26566
+ const child = { deployId: payload.deployId, sessionId: payload.deployId, proc };
26567
+ this.children.set(payload.deployId, child);
26568
+ proc.once("exit", () => {
26569
+ if (this.children.get(payload.deployId)?.proc === proc) {
26570
+ this.children.delete(payload.deployId);
26571
+ }
26572
+ });
26573
+ }
26574
+ /** Kill the child matching `sessionId` (or its deployId). No-op if absent. */
26575
+ stopChild(sessionId) {
26576
+ const child = this.children.get(sessionId) ?? this.findBySessionId(sessionId);
26577
+ if (!child) {
26578
+ log.trace("host-agent", `stop: no child for sessionId=${sessionId}`);
26579
+ return;
26580
+ }
26581
+ log.info("host-agent", `stopping child deploy=${child.deployId.slice(0, 8)}`);
26582
+ try {
26583
+ child.proc.kill("SIGTERM");
26584
+ } catch {
26585
+ }
26586
+ this.children.delete(child.deployId);
26587
+ }
26588
+ findBySessionId(sessionId) {
26589
+ for (const child of this.children.values()) {
26590
+ if (child.sessionId === sessionId) return child;
26591
+ }
26592
+ return void 0;
26593
+ }
26594
+ };
26595
+ async function resolveHostIdentity(enrollToken) {
26596
+ const existing = loadHostIdentity();
26597
+ if (existing) return existing;
26598
+ if (!enrollToken) return null;
26599
+ const identity = await redeemEnrollToken(enrollToken);
26600
+ saveHostIdentity(identity);
26601
+ return identity;
26602
+ }
26603
+ async function hostAgent(args2 = []) {
26604
+ const tokenArg = args2.find((a) => a.startsWith("--token="));
26605
+ const enrollToken = (tokenArg ? tokenArg.slice("--token=".length).trim() : "") || process.env.CODEAM_ENROLL_TOKEN || void 0;
26606
+ const identity = await resolveHostIdentity(enrollToken);
26607
+ if (!identity) {
26608
+ throw new Error(
26609
+ "host-agent: no sealed host identity and no enroll token. Re-run the installer from the app (tokens expire after 15 min)."
26610
+ );
26611
+ }
26612
+ const supervisor = new HostAgentSupervisor(identity);
26613
+ supervisor.start();
26614
+ const shutdown = () => {
26615
+ supervisor.stop();
26616
+ process.exit(0);
26617
+ };
26618
+ process.once("SIGINT", shutdown);
26619
+ process.once("SIGTERM", shutdown);
26620
+ await new Promise(() => {
26621
+ });
26622
+ }
26623
+
26899
26624
  // src/commands/doctor.ts
26900
26625
  var import_node_dns = require("dns");
26901
- var import_node_util4 = require("util");
26626
+ var import_node_util5 = require("util");
26902
26627
  var import_node_crypto8 = require("crypto");
26903
- var fs41 = __toESM(require("fs"));
26904
- var path53 = __toESM(require("path"));
26628
+ var fs44 = __toESM(require("fs"));
26629
+ var path56 = __toESM(require("path"));
26905
26630
  var import_picocolors12 = __toESM(require("picocolors"));
26906
- var dnsResolveP = (0, import_node_util4.promisify)(import_node_dns.resolve);
26907
- async function checkDns(apiBase) {
26908
- const host = (() => {
26631
+ var dnsResolveP = (0, import_node_util5.promisify)(import_node_dns.resolve);
26632
+ async function checkDns(apiBase2) {
26633
+ const host2 = (() => {
26909
26634
  try {
26910
- return new URL(apiBase).host;
26635
+ return new URL(apiBase2).host;
26911
26636
  } catch {
26912
- return apiBase;
26637
+ return apiBase2;
26913
26638
  }
26914
26639
  })();
26915
26640
  try {
26916
- const addrs = await dnsResolveP(host);
26641
+ const addrs = await dnsResolveP(host2);
26917
26642
  return {
26918
26643
  id: "dns",
26919
- label: `DNS resolves ${host}`,
26644
+ label: `DNS resolves ${host2}`,
26920
26645
  ok: true,
26921
26646
  detail: `${addrs.length} record(s)`
26922
26647
  };
26923
26648
  } catch (err) {
26924
26649
  return {
26925
26650
  id: "dns",
26926
- label: `DNS resolves ${host}`,
26651
+ label: `DNS resolves ${host2}`,
26927
26652
  ok: false,
26928
26653
  detail: err.message,
26929
26654
  hint: "Check your network connection / DNS configuration. The backend host MUST resolve before `codeam pair` can succeed."
26930
26655
  };
26931
26656
  }
26932
26657
  }
26933
- async function checkHealth(apiBase) {
26934
- const url = `${apiBase}/health`;
26658
+ async function checkHealth(apiBase2) {
26659
+ const url = `${apiBase2}/health`;
26935
26660
  const controller = new AbortController();
26936
26661
  const timer = setTimeout(() => controller.abort(), 5e3);
26937
26662
  try {
@@ -26957,13 +26682,13 @@ async function checkHealth(apiBase) {
26957
26682
  }
26958
26683
  }
26959
26684
  function checkConfigDir() {
26960
- const dir = path53.join(require("os").homedir(), ".codeam");
26685
+ const dir = path56.join(require("os").homedir(), ".codeam");
26961
26686
  try {
26962
- fs41.mkdirSync(dir, { recursive: true, mode: 448 });
26963
- const probe = path53.join(dir, ".doctor-probe");
26964
- fs41.writeFileSync(probe, "ok", { mode: 384 });
26965
- const read = fs41.readFileSync(probe, "utf8");
26966
- fs41.unlinkSync(probe);
26687
+ fs44.mkdirSync(dir, { recursive: true, mode: 448 });
26688
+ const probe = path56.join(dir, ".doctor-probe");
26689
+ fs44.writeFileSync(probe, "ok", { mode: 384 });
26690
+ const read = fs44.readFileSync(probe, "utf8");
26691
+ fs44.unlinkSync(probe);
26967
26692
  if (read !== "ok") throw new Error("write/read round-trip mismatch");
26968
26693
  return {
26969
26694
  id: "config-dir",
@@ -27003,9 +26728,9 @@ function checkSessions() {
27003
26728
  }
27004
26729
  }
27005
26730
  function checkAgentBinaries() {
27006
- const os33 = createOsStrategy();
26731
+ const os36 = createOsStrategy();
27007
26732
  return getEnabledAgents().map((meta) => {
27008
- const found = os33.findInPath(meta.binaryName);
26733
+ const found = os36.findInPath(meta.binaryName);
27009
26734
  return {
27010
26735
  id: `agent-${meta.id}`,
27011
26736
  label: `Agent binary: ${meta.displayName} (${meta.binaryName})`,
@@ -27027,7 +26752,7 @@ function checkNodePty() {
27027
26752
  detail: "not required on this platform"
27028
26753
  };
27029
26754
  }
27030
- const vendoredPath = path53.join(__dirname, "vendor", "node-pty");
26755
+ const vendoredPath = path56.join(__dirname, "vendor", "node-pty");
27031
26756
  for (const target of [vendoredPath, "node-pty"]) {
27032
26757
  try {
27033
26758
  require(target);
@@ -27069,13 +26794,13 @@ function checkChokidar() {
27069
26794
  }
27070
26795
  async function doctor(args2 = []) {
27071
26796
  const json = args2.includes("--json");
27072
- const cliVersion = true ? "2.39.23" : "0.0.0-dev";
27073
- const apiBase = resolveApiBaseUrl();
26797
+ const cliVersion = true ? "2.39.25" : "0.0.0-dev";
26798
+ const apiBase2 = resolveApiBaseUrl();
27074
26799
  const diagnosticId = (0, import_node_crypto8.randomUUID)();
27075
26800
  log.info("doctor", `run id=${diagnosticId} cli=${cliVersion}`);
27076
26801
  const [dns2, health] = await Promise.all([
27077
- checkDns(apiBase),
27078
- checkHealth(apiBase)
26802
+ checkDns(apiBase2),
26803
+ checkHealth(apiBase2)
27079
26804
  ]);
27080
26805
  const checks = [
27081
26806
  dns2,
@@ -27092,7 +26817,7 @@ async function doctor(args2 = []) {
27092
26817
  node: process.version,
27093
26818
  platform: process.platform,
27094
26819
  arch: process.arch,
27095
- apiBase,
26820
+ apiBase: apiBase2,
27096
26821
  checks,
27097
26822
  // Optional checks (e.g. agent-binary probes) report status but
27098
26823
  // don't gate exit code — see CheckResult.optional.
@@ -27268,7 +26993,7 @@ async function completion(args2) {
27268
26993
  // src/commands/version.ts
27269
26994
  var import_picocolors13 = __toESM(require("picocolors"));
27270
26995
  function version2() {
27271
- const v = true ? "2.39.23" : "unknown";
26996
+ const v = true ? "2.39.25" : "unknown";
27272
26997
  console.log(`${import_picocolors13.default.bold("codeam-cli")} ${import_picocolors13.default.cyan(v)}`);
27273
26998
  }
27274
26999
 
@@ -27396,23 +27121,23 @@ function tryShowSubcommandHelp(cmd, args2) {
27396
27121
  var _subcommandHelpKeys = Object.keys(HELPS);
27397
27122
 
27398
27123
  // src/lib/updateNotifier.ts
27399
- var fs42 = __toESM(require("fs"));
27400
- var os32 = __toESM(require("os"));
27401
- var path54 = __toESM(require("path"));
27124
+ var fs45 = __toESM(require("fs"));
27125
+ var os35 = __toESM(require("os"));
27126
+ var path57 = __toESM(require("path"));
27402
27127
  var https8 = __toESM(require("https"));
27403
- var import_node_child_process12 = require("child_process");
27128
+ var import_node_child_process14 = require("child_process");
27404
27129
  var import_picocolors16 = __toESM(require("picocolors"));
27405
27130
  var PKG_NAME = "codeam-cli";
27406
27131
  var REGISTRY_URL = `https://registry.npmjs.org/${PKG_NAME}/latest`;
27407
27132
  var TTL_MS = 24 * 60 * 60 * 1e3;
27408
27133
  var REQUEST_TIMEOUT_MS = 1500;
27409
27134
  function cachePath() {
27410
- const dir = path54.join(os32.homedir(), ".codeam");
27411
- return path54.join(dir, "update-check.json");
27135
+ const dir = path57.join(os35.homedir(), ".codeam");
27136
+ return path57.join(dir, "update-check.json");
27412
27137
  }
27413
27138
  function readCache() {
27414
27139
  try {
27415
- const raw = fs42.readFileSync(cachePath(), "utf8");
27140
+ const raw = fs45.readFileSync(cachePath(), "utf8");
27416
27141
  const parsed = JSON.parse(raw);
27417
27142
  if (typeof parsed.fetchedAt !== "number" || typeof parsed.latest !== "string") return null;
27418
27143
  return parsed;
@@ -27423,10 +27148,10 @@ function readCache() {
27423
27148
  function writeCache(cache) {
27424
27149
  try {
27425
27150
  const file = cachePath();
27426
- fs42.mkdirSync(path54.dirname(file), { recursive: true });
27151
+ fs45.mkdirSync(path57.dirname(file), { recursive: true });
27427
27152
  const tmp = `${file}.${process.pid}.tmp`;
27428
- fs42.writeFileSync(tmp, JSON.stringify(cache));
27429
- fs42.renameSync(tmp, file);
27153
+ fs45.writeFileSync(tmp, JSON.stringify(cache));
27154
+ fs45.renameSync(tmp, file);
27430
27155
  } catch {
27431
27156
  }
27432
27157
  }
@@ -27494,14 +27219,14 @@ function notifyIfStale(currentVersion, latest) {
27494
27219
  }
27495
27220
  function isLinkedInstall() {
27496
27221
  try {
27497
- const root = (0, import_node_child_process12.execSync)("npm root -g", {
27222
+ const root = (0, import_node_child_process14.execSync)("npm root -g", {
27498
27223
  encoding: "utf8",
27499
27224
  stdio: ["ignore", "pipe", "ignore"],
27500
27225
  timeout: 2e3
27501
27226
  }).trim();
27502
27227
  if (!root) return false;
27503
- const pkgPath = path54.join(root, PKG_NAME);
27504
- return fs42.lstatSync(pkgPath).isSymbolicLink();
27228
+ const pkgPath = path57.join(root, PKG_NAME);
27229
+ return fs45.lstatSync(pkgPath).isSymbolicLink();
27505
27230
  } catch {
27506
27231
  return false;
27507
27232
  }
@@ -27522,7 +27247,7 @@ function maybeAutoUpdate(currentVersion, latest) {
27522
27247
 
27523
27248
  `
27524
27249
  );
27525
- const install = (0, import_node_child_process12.spawnSync)("npm", ["install", "-g", `${PKG_NAME}@latest`], {
27250
+ const install = (0, import_node_child_process14.spawnSync)("npm", ["install", "-g", `${PKG_NAME}@latest`], {
27526
27251
  stdio: "inherit",
27527
27252
  env: process.env
27528
27253
  });
@@ -27537,13 +27262,13 @@ function maybeAutoUpdate(currentVersion, latest) {
27537
27262
  return;
27538
27263
  }
27539
27264
  try {
27540
- fs42.unlinkSync(cachePath());
27265
+ fs45.unlinkSync(cachePath());
27541
27266
  } catch {
27542
27267
  }
27543
27268
  process.stderr.write(` ${import_picocolors16.default.green("\u2713")} Updated. Resuming session...
27544
27269
 
27545
27270
  `);
27546
- const child = (0, import_node_child_process12.spawnSync)("codeam", process.argv.slice(2), {
27271
+ const child = (0, import_node_child_process14.spawnSync)("codeam", process.argv.slice(2), {
27547
27272
  stdio: "inherit",
27548
27273
  env: process.env
27549
27274
  });
@@ -27554,7 +27279,7 @@ function checkForUpdates() {
27554
27279
  if (process.env.CODEAM_DISABLE_UPDATE_CHECK === "1") return;
27555
27280
  if (process.env.CI) return;
27556
27281
  if (!process.stdout.isTTY) return;
27557
- const current = true ? "2.39.23" : null;
27282
+ const current = true ? "2.39.25" : null;
27558
27283
  if (!current) return;
27559
27284
  const cache = readCache();
27560
27285
  const fresh = cache && Date.now() - cache.fetchedAt < TTL_MS;
@@ -27599,6 +27324,8 @@ async function main() {
27599
27324
  const commands = {
27600
27325
  "pair": () => pair(args),
27601
27326
  "pair-auto": () => pairAuto(args),
27327
+ "host-agent": () => hostAgent(args),
27328
+ "host": () => host(args),
27602
27329
  "sessions": () => sessions2(args),
27603
27330
  "status": () => status(),
27604
27331
  "logout": () => logout(),