@fangyb/ahchat-bridge 0.1.21 → 0.1.22

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 (4) hide show
  1. package/dist/cli.cjs +521 -232
  2. package/dist/index.js +248 -117
  3. package/package.json +2 -2
  4. package/dist/cli.js +0 -51540
package/dist/cli.cjs CHANGED
@@ -3679,9 +3679,8 @@ var require_websocket_server = __commonJS({
3679
3679
 
3680
3680
  // src/cli.ts
3681
3681
  init_cjs_shims();
3682
- var import_node_os13 = __toESM(require("os"), 1);
3683
- var import_node_path22 = __toESM(require("path"), 1);
3684
- var import_node_fs10 = __toESM(require("fs"), 1);
3682
+ var import_node_path23 = __toESM(require("path"), 1);
3683
+ var import_node_fs11 = __toESM(require("fs"), 1);
3685
3684
 
3686
3685
  // ../../node_modules/.pnpm/cac@6.7.14/node_modules/cac/dist/index.mjs
3687
3686
  init_cjs_shims();
@@ -5097,11 +5096,11 @@ var RotatingFileStream = class extends import_stream.Writable {
5097
5096
  timeout;
5098
5097
  timeoutPromise;
5099
5098
  constructor(generator, options) {
5100
- const { encoding, history, maxFiles, maxSize, path: path23 } = options;
5099
+ const { encoding, history, maxFiles, maxSize, path: path24 } = options;
5101
5100
  super({ decodeStrings: true, defaultEncoding: encoding });
5102
5101
  this.createGzip = import_zlib.createGzip;
5103
5102
  this.exec = import_child_process.exec;
5104
- this.filename = path23 + generator(null);
5103
+ this.filename = path24 + generator(null);
5105
5104
  this.fsCreateReadStream = import_fs.createReadStream;
5106
5105
  this.fsCreateWriteStream = import_fs.createWriteStream;
5107
5106
  this.fsOpen = import_promises.open;
@@ -5113,7 +5112,7 @@ var RotatingFileStream = class extends import_stream.Writable {
5113
5112
  this.options = options;
5114
5113
  this.stdout = process.stdout;
5115
5114
  if (maxFiles || maxSize)
5116
- options.history = path23 + (history ? history : this.generator(null) + ".txt");
5115
+ options.history = path24 + (history ? history : this.generator(null) + ".txt");
5117
5116
  this.on("close", () => this.finished ? null : this.emit("finish"));
5118
5117
  this.on("finish", () => this.finished = this.clear());
5119
5118
  (async () => {
@@ -5241,9 +5240,9 @@ var RotatingFileStream = class extends import_stream.Writable {
5241
5240
  return this.move();
5242
5241
  }
5243
5242
  async findName() {
5244
- const { interval, path: path23, intervalBoundary } = this.options;
5243
+ const { interval, path: path24, intervalBoundary } = this.options;
5245
5244
  for (let index = 1; index < 1e3; ++index) {
5246
- const filename = path23 + this.generator(interval && intervalBoundary ? new Date(this.prev) : this.rotation, index);
5245
+ const filename = path24 + this.generator(interval && intervalBoundary ? new Date(this.prev) : this.rotation, index);
5247
5246
  if (!await exists(filename))
5248
5247
  return filename;
5249
5248
  }
@@ -5273,11 +5272,11 @@ var RotatingFileStream = class extends import_stream.Writable {
5273
5272
  return this.unlink(filename);
5274
5273
  }
5275
5274
  async classical() {
5276
- const { compress, path: path23, rotate } = this.options;
5275
+ const { compress, path: path24, rotate } = this.options;
5277
5276
  let rotatedName = "";
5278
5277
  for (let count = rotate; count > 0; --count) {
5279
- const currName = path23 + this.generator(count);
5280
- const prevName = count === 1 ? this.filename : path23 + this.generator(count - 1);
5278
+ const currName = path24 + this.generator(count);
5279
+ const prevName = count === 1 ? this.filename : path24 + this.generator(count - 1);
5281
5280
  if (!await exists(prevName))
5282
5281
  continue;
5283
5282
  if (!rotatedName)
@@ -5715,7 +5714,7 @@ function createModuleLogger(module2) {
5715
5714
 
5716
5715
  // src/start.ts
5717
5716
  init_cjs_shims();
5718
- var import_node_path20 = __toESM(require("path"), 1);
5717
+ var import_node_path21 = __toESM(require("path"), 1);
5719
5718
 
5720
5719
  // ../shared/src/index.ts
5721
5720
  init_cjs_shims();
@@ -29753,10 +29752,10 @@ function mergeDefs(...defs) {
29753
29752
  function cloneDef(schema) {
29754
29753
  return mergeDefs(schema._zod.def);
29755
29754
  }
29756
- function getElementAtPath(obj, path23) {
29757
- if (!path23)
29755
+ function getElementAtPath(obj, path24) {
29756
+ if (!path24)
29758
29757
  return obj;
29759
- return path23.reduce((acc, key) => acc?.[key], obj);
29758
+ return path24.reduce((acc, key) => acc?.[key], obj);
29760
29759
  }
29761
29760
  function promiseAllObject(promisesObj) {
29762
29761
  const keys = Object.keys(promisesObj);
@@ -30165,11 +30164,11 @@ function explicitlyAborted(x2, startIndex = 0) {
30165
30164
  }
30166
30165
  return false;
30167
30166
  }
30168
- function prefixIssues(path23, issues) {
30167
+ function prefixIssues(path24, issues) {
30169
30168
  return issues.map((iss) => {
30170
30169
  var _a3;
30171
30170
  (_a3 = iss).path ?? (_a3.path = []);
30172
- iss.path.unshift(path23);
30171
+ iss.path.unshift(path24);
30173
30172
  return iss;
30174
30173
  });
30175
30174
  }
@@ -30316,16 +30315,16 @@ function flattenError(error51, mapper = (issue2) => issue2.message) {
30316
30315
  }
30317
30316
  function formatError(error51, mapper = (issue2) => issue2.message) {
30318
30317
  const fieldErrors = { _errors: [] };
30319
- const processError = (error52, path23 = []) => {
30318
+ const processError = (error52, path24 = []) => {
30320
30319
  for (const issue2 of error52.issues) {
30321
30320
  if (issue2.code === "invalid_union" && issue2.errors.length) {
30322
- issue2.errors.map((issues) => processError({ issues }, [...path23, ...issue2.path]));
30321
+ issue2.errors.map((issues) => processError({ issues }, [...path24, ...issue2.path]));
30323
30322
  } else if (issue2.code === "invalid_key") {
30324
- processError({ issues: issue2.issues }, [...path23, ...issue2.path]);
30323
+ processError({ issues: issue2.issues }, [...path24, ...issue2.path]);
30325
30324
  } else if (issue2.code === "invalid_element") {
30326
- processError({ issues: issue2.issues }, [...path23, ...issue2.path]);
30325
+ processError({ issues: issue2.issues }, [...path24, ...issue2.path]);
30327
30326
  } else {
30328
- const fullpath = [...path23, ...issue2.path];
30327
+ const fullpath = [...path24, ...issue2.path];
30329
30328
  if (fullpath.length === 0) {
30330
30329
  fieldErrors._errors.push(mapper(issue2));
30331
30330
  } else {
@@ -30352,17 +30351,17 @@ function formatError(error51, mapper = (issue2) => issue2.message) {
30352
30351
  }
30353
30352
  function treeifyError(error51, mapper = (issue2) => issue2.message) {
30354
30353
  const result = { errors: [] };
30355
- const processError = (error52, path23 = []) => {
30354
+ const processError = (error52, path24 = []) => {
30356
30355
  var _a3, _b2;
30357
30356
  for (const issue2 of error52.issues) {
30358
30357
  if (issue2.code === "invalid_union" && issue2.errors.length) {
30359
- issue2.errors.map((issues) => processError({ issues }, [...path23, ...issue2.path]));
30358
+ issue2.errors.map((issues) => processError({ issues }, [...path24, ...issue2.path]));
30360
30359
  } else if (issue2.code === "invalid_key") {
30361
- processError({ issues: issue2.issues }, [...path23, ...issue2.path]);
30360
+ processError({ issues: issue2.issues }, [...path24, ...issue2.path]);
30362
30361
  } else if (issue2.code === "invalid_element") {
30363
- processError({ issues: issue2.issues }, [...path23, ...issue2.path]);
30362
+ processError({ issues: issue2.issues }, [...path24, ...issue2.path]);
30364
30363
  } else {
30365
- const fullpath = [...path23, ...issue2.path];
30364
+ const fullpath = [...path24, ...issue2.path];
30366
30365
  if (fullpath.length === 0) {
30367
30366
  result.errors.push(mapper(issue2));
30368
30367
  continue;
@@ -30394,8 +30393,8 @@ function treeifyError(error51, mapper = (issue2) => issue2.message) {
30394
30393
  }
30395
30394
  function toDotPath(_path) {
30396
30395
  const segs = [];
30397
- const path23 = _path.map((seg) => typeof seg === "object" ? seg.key : seg);
30398
- for (const seg of path23) {
30396
+ const path24 = _path.map((seg) => typeof seg === "object" ? seg.key : seg);
30397
+ for (const seg of path24) {
30399
30398
  if (typeof seg === "number")
30400
30399
  segs.push(`[${seg}]`);
30401
30400
  else if (typeof seg === "symbol")
@@ -43168,13 +43167,13 @@ function resolveRef(ref, ctx) {
43168
43167
  if (!ref.startsWith("#")) {
43169
43168
  throw new Error("External $ref is not supported, only local refs (#/...) are allowed");
43170
43169
  }
43171
- const path23 = ref.slice(1).split("/").filter(Boolean);
43172
- if (path23.length === 0) {
43170
+ const path24 = ref.slice(1).split("/").filter(Boolean);
43171
+ if (path24.length === 0) {
43173
43172
  return ctx.rootSchema;
43174
43173
  }
43175
43174
  const defsKey = ctx.version === "draft-2020-12" ? "$defs" : "definitions";
43176
- if (path23[0] === defsKey) {
43177
- const key = path23[1];
43175
+ if (path24[0] === defsKey) {
43176
+ const key = path24[1];
43178
43177
  if (!key || !ctx.defs[key]) {
43179
43178
  throw new Error(`Reference not found: ${ref}`);
43180
43179
  }
@@ -49771,8 +49770,8 @@ var HttpAgentRegistry = class {
49771
49770
  agents = /* @__PURE__ */ new Map();
49772
49771
  apiUrl(suffix) {
49773
49772
  const base = this.serverApiUrl.replace(/\/$/, "");
49774
- const path23 = suffix.startsWith("/") ? suffix : `/${suffix}`;
49775
- return `${base}${path23}`;
49773
+ const path24 = suffix.startsWith("/") ? suffix : `/${suffix}`;
49774
+ return `${base}${path24}`;
49776
49775
  }
49777
49776
  async refresh() {
49778
49777
  const attempt = async () => {
@@ -49864,8 +49863,8 @@ var HttpSubscriptionRegistry = class {
49864
49863
  subscriptions = /* @__PURE__ */ new Map();
49865
49864
  apiUrl(suffix) {
49866
49865
  const base = this.serverApiUrl.replace(/\/$/, "");
49867
- const path23 = suffix.startsWith("/") ? suffix : `/${suffix}`;
49868
- return `${base}${path23}`;
49866
+ const path24 = suffix.startsWith("/") ? suffix : `/${suffix}`;
49867
+ return `${base}${path24}`;
49869
49868
  }
49870
49869
  async refresh() {
49871
49870
  const attempt = async () => {
@@ -50753,19 +50752,208 @@ async function dumpAgentContext(agentId, deps) {
50753
50752
  // src/listDir.ts
50754
50753
  init_cjs_shims();
50755
50754
  var import_promises10 = __toESM(require("fs/promises"), 1);
50755
+ var import_node_path12 = __toESM(require("path"), 1);
50756
+
50757
+ // src/runtimeEnv.ts
50758
+ init_cjs_shims();
50759
+ var import_node_child_process2 = require("child_process");
50760
+ var import_node_fs4 = require("fs");
50761
+ var import_node_os8 = __toESM(require("os"), 1);
50756
50762
  var import_node_path11 = __toESM(require("path"), 1);
50763
+ var MIN_NODE_MAJOR = 20;
50764
+ function getHomeDir() {
50765
+ return process.env.USERPROFILE || import_node_os8.default.homedir();
50766
+ }
50767
+ function splitPath(value) {
50768
+ if (!value) return [];
50769
+ return value.split(import_node_path11.default.delimiter).filter((entry) => entry.length > 0);
50770
+ }
50771
+ function uniq(values) {
50772
+ return [...new Set(values.filter(Boolean))];
50773
+ }
50774
+ function parseNodeVersionMajor(version2) {
50775
+ const raw = version2.trim().replace(/^v/, "");
50776
+ const [majorRaw] = raw.split(".");
50777
+ if (!majorRaw) return null;
50778
+ const major = Number.parseInt(majorRaw, 10);
50779
+ return Number.isFinite(major) ? major : null;
50780
+ }
50781
+ function joinHomePath(home, suffix) {
50782
+ const parts = suffix.split(/[\\/]+/).filter((part) => part.length > 0);
50783
+ return import_node_path11.default.join(home, ...parts);
50784
+ }
50785
+ function sortNodeVersionDirsDesc(names) {
50786
+ return [...names].sort((a, b2) => {
50787
+ const aParts = a.replace(/^v/, "").split(".").map((p) => Number.parseInt(p, 10) || 0);
50788
+ const bParts = b2.replace(/^v/, "").split(".").map((p) => Number.parseInt(p, 10) || 0);
50789
+ for (let i = 0; i < Math.max(aParts.length, bParts.length); i += 1) {
50790
+ const delta = (bParts[i] ?? 0) - (aParts[i] ?? 0);
50791
+ if (delta !== 0) return delta;
50792
+ }
50793
+ return b2.localeCompare(a);
50794
+ });
50795
+ }
50796
+ function listNodeVersionBins(root, binSuffix) {
50797
+ if (!(0, import_node_fs4.existsSync)(root)) return [];
50798
+ try {
50799
+ return sortNodeVersionDirsDesc((0, import_node_fs4.readdirSync)(root)).map((version2) => import_node_path11.default.join(root, version2, ...binSuffix)).filter((candidate) => (0, import_node_fs4.existsSync)(candidate));
50800
+ } catch {
50801
+ return [];
50802
+ }
50803
+ }
50804
+ function getNodeRuntimeStatus(version2 = process.versions.node) {
50805
+ const major = parseNodeVersionMajor(version2);
50806
+ return {
50807
+ version: version2,
50808
+ major,
50809
+ supported: major !== null && major >= MIN_NODE_MAJOR
50810
+ };
50811
+ }
50812
+ function getNodeToolExtraPathEntries(env2 = process.env) {
50813
+ const home = getHomeDir();
50814
+ const entries = [];
50815
+ if (process.platform === "win32") {
50816
+ for (const envName of ["NVM_SYMLINK", "NVM_HOME"]) {
50817
+ const value = env2[envName];
50818
+ if (value && (0, import_node_fs4.existsSync)(value)) entries.push(value);
50819
+ }
50820
+ for (const candidate of [
50821
+ import_node_path11.default.join(home, "AppData", "Roaming", "npm"),
50822
+ import_node_path11.default.join(home, ".volta", "bin"),
50823
+ import_node_path11.default.join(home, ".asdf", "shims"),
50824
+ import_node_path11.default.join(env2.ProgramFiles ?? "C:\\Program Files", "nodejs"),
50825
+ import_node_path11.default.join(env2.LOCALAPPDATA ?? import_node_path11.default.join(home, "AppData", "Local"), "Programs", "nodejs")
50826
+ ]) {
50827
+ if ((0, import_node_fs4.existsSync)(candidate)) entries.push(candidate);
50828
+ }
50829
+ return uniq(entries);
50830
+ }
50831
+ const nvmRoot = env2.NVM_DIR ?? import_node_path11.default.join(home, ".nvm");
50832
+ entries.push(...listNodeVersionBins(import_node_path11.default.join(nvmRoot, "versions", "node"), ["bin"]));
50833
+ const fnmRoots = [
50834
+ import_node_path11.default.join(home, ".fnm", "node-versions"),
50835
+ import_node_path11.default.join(home, ".local", "share", "fnm", "node-versions")
50836
+ ];
50837
+ for (const fnmRoot of fnmRoots) {
50838
+ entries.push(...listNodeVersionBins(fnmRoot, ["installation", "bin"]));
50839
+ }
50840
+ for (const candidate of [
50841
+ import_node_path11.default.join(home, ".volta", "bin"),
50842
+ import_node_path11.default.join(home, ".asdf", "shims"),
50843
+ import_node_path11.default.join(home, ".local", "bin"),
50844
+ "/opt/homebrew/bin",
50845
+ "/usr/local/bin"
50846
+ ]) {
50847
+ if ((0, import_node_fs4.existsSync)(candidate)) entries.push(candidate);
50848
+ }
50849
+ return uniq(entries);
50850
+ }
50851
+ function buildAugmentedPath(env2 = process.env) {
50852
+ return uniq([...getNodeToolExtraPathEntries(env2), ...splitPath(env2.PATH)]).join(import_node_path11.default.delimiter);
50853
+ }
50854
+ function withAugmentedPathEnv(env2 = process.env) {
50855
+ return { ...env2, PATH: buildAugmentedPath(env2) };
50856
+ }
50857
+ function executableNames(name) {
50858
+ if (process.platform !== "win32") return [name];
50859
+ if (/\.(cmd|exe|bat)$/i.test(name)) return [name];
50860
+ return [`${name}.cmd`, `${name}.exe`, `${name}.bat`, name];
50861
+ }
50862
+ function canExecute(candidate) {
50863
+ try {
50864
+ if (process.platform === "win32") return (0, import_node_fs4.existsSync)(candidate);
50865
+ (0, import_node_fs4.accessSync)(candidate, import_node_fs4.constants.X_OK);
50866
+ return true;
50867
+ } catch {
50868
+ return false;
50869
+ }
50870
+ }
50871
+ function resolveCommand(names, env2 = process.env) {
50872
+ const pathEntries = splitPath(buildAugmentedPath(env2));
50873
+ for (const entry of pathEntries) {
50874
+ for (const name of names) {
50875
+ for (const executableName of executableNames(name)) {
50876
+ const candidate = import_node_path11.default.join(entry, executableName);
50877
+ if (canExecute(candidate)) return { name, path: candidate };
50878
+ }
50879
+ }
50880
+ }
50881
+ return void 0;
50882
+ }
50883
+ function readCommandVersion(executablePath, args = ["--version"], env2 = process.env) {
50884
+ try {
50885
+ return (0, import_node_child_process2.execFileSync)(executablePath, args, {
50886
+ env: withAugmentedPathEnv(env2),
50887
+ timeout: 1e4
50888
+ }).toString().trim();
50889
+ } catch {
50890
+ return void 0;
50891
+ }
50892
+ }
50893
+ function probeCommand(name, args = ["--version"], env2 = process.env) {
50894
+ const resolved = resolveCommand([name], env2);
50895
+ if (!resolved) {
50896
+ return {
50897
+ name,
50898
+ ok: false,
50899
+ message: `${name} was not found on PATH`
50900
+ };
50901
+ }
50902
+ const version2 = readCommandVersion(resolved.path, args, env2);
50903
+ return {
50904
+ name,
50905
+ path: resolved.path,
50906
+ version: version2,
50907
+ ok: Boolean(version2),
50908
+ message: version2 ? void 0 : `${name} was found but did not run successfully`
50909
+ };
50910
+ }
50911
+ function resolveUserPath(input) {
50912
+ const home = getHomeDir();
50913
+ let value = input.trim();
50914
+ if (value === "~") value = home;
50915
+ else if (value.startsWith("~/") || value.startsWith("~\\")) value = joinHomePath(home, value.slice(2));
50916
+ else if (value === "$HOME") value = home;
50917
+ else if (value.startsWith("$HOME/") || value.startsWith("$HOME\\")) value = joinHomePath(home, value.slice(6));
50918
+ else if (value === "${HOME}") value = home;
50919
+ else if (value.startsWith("${HOME}/") || value.startsWith("${HOME}\\")) value = joinHomePath(home, value.slice(8));
50920
+ else if (value === "$env:USERPROFILE") value = home;
50921
+ else if (value.startsWith("$env:USERPROFILE\\") || value.startsWith("$env:USERPROFILE/")) {
50922
+ value = joinHomePath(home, value.slice("$env:USERPROFILE".length + 1));
50923
+ } else if (value === "%USERPROFILE%") value = home;
50924
+ else if (value.startsWith("%USERPROFILE%\\") || value.startsWith("%USERPROFILE%/")) {
50925
+ value = joinHomePath(home, value.slice("%USERPROFILE%".length + 1));
50926
+ }
50927
+ return import_node_path11.default.isAbsolute(value) ? import_node_path11.default.normalize(value) : import_node_path11.default.resolve(value);
50928
+ }
50929
+ function normalizeBridgeServerUrls(rawUrl) {
50930
+ const url2 = new URL(rawUrl);
50931
+ if (url2.protocol === "http:" || url2.protocol === "https:") {
50932
+ url2.protocol = url2.protocol === "https:" ? "wss:" : "ws:";
50933
+ if (url2.pathname === "/" || url2.pathname === "") url2.pathname = "/ws/bridge";
50934
+ }
50935
+ if (url2.protocol !== "ws:" && url2.protocol !== "wss:") {
50936
+ throw new Error(`Unsupported bridge server URL protocol: ${url2.protocol}`);
50937
+ }
50938
+ const serverUrl = url2.toString();
50939
+ const serverApiUrl = `${url2.protocol === "wss:" ? "https" : "http"}://${url2.host}`;
50940
+ return { serverUrl, serverApiUrl };
50941
+ }
50942
+
50943
+ // src/listDir.ts
50757
50944
  var logger17 = createModuleLogger("bridge.listDir");
50758
50945
  function shouldIncludeEntry(name) {
50759
50946
  if (!name.startsWith(".")) return true;
50760
50947
  return name === ".ahchat-attachments";
50761
50948
  }
50762
50949
  async function listDirectoryEntries(dirPath) {
50763
- logger17.info("listDirectoryEntries start", { path: dirPath });
50764
- const raw = await import_promises10.default.readdir(dirPath, { withFileTypes: true });
50950
+ const resolvedDirPath = resolveUserPath(dirPath);
50951
+ logger17.info("listDirectoryEntries start", { path: dirPath, resolvedPath: resolvedDirPath });
50952
+ const raw = await import_promises10.default.readdir(resolvedDirPath, { withFileTypes: true });
50765
50953
  const entries = [];
50766
50954
  for (const entry of raw) {
50767
50955
  if (!shouldIncludeEntry(entry.name)) continue;
50768
- const fullPath = import_node_path11.default.join(dirPath, entry.name);
50956
+ const fullPath = import_node_path12.default.join(resolvedDirPath, entry.name);
50769
50957
  const isDir = entry.isDirectory();
50770
50958
  let size;
50771
50959
  let mtime;
@@ -50784,6 +50972,7 @@ async function listDirectoryEntries(dirPath) {
50784
50972
  });
50785
50973
  logger17.info("listDirectoryEntries ok", {
50786
50974
  path: dirPath,
50975
+ resolvedPath: resolvedDirPath,
50787
50976
  count: entries.length,
50788
50977
  dirCount: entries.filter((e7) => e7.type === "dir").length,
50789
50978
  fileCount: entries.filter((e7) => e7.type === "file").length
@@ -50793,9 +50982,9 @@ async function listDirectoryEntries(dirPath) {
50793
50982
 
50794
50983
  // src/logScanner.ts
50795
50984
  init_cjs_shims();
50796
- var import_node_fs4 = __toESM(require("fs"), 1);
50797
- var import_node_path12 = __toESM(require("path"), 1);
50798
- var import_node_os8 = __toESM(require("os"), 1);
50985
+ var import_node_fs5 = __toESM(require("fs"), 1);
50986
+ var import_node_path13 = __toESM(require("path"), 1);
50987
+ var import_node_os9 = __toESM(require("os"), 1);
50799
50988
  var import_node_readline = __toESM(require("readline"), 1);
50800
50989
  var logger18 = createModuleLogger("bridge.logScanner");
50801
50990
  var DEFAULT_LIMIT = 500;
@@ -50803,17 +50992,17 @@ var MAX_LIMIT = 2e3;
50803
50992
  function listLogFiles(logsDir, baseName) {
50804
50993
  let names;
50805
50994
  try {
50806
- names = import_node_fs4.default.readdirSync(logsDir);
50995
+ names = import_node_fs5.default.readdirSync(logsDir);
50807
50996
  } catch (e7) {
50808
50997
  logger18.warn("listLogFiles: readdir failed", { logsDir, error: e7 });
50809
50998
  return [];
50810
50999
  }
50811
51000
  const pattern = new RegExp(`^${baseName.replace(".", "\\.")}(\\.\\d+)?$`);
50812
- return names.filter((n2) => pattern.test(n2)).map((n2) => import_node_path12.default.join(logsDir, n2));
51001
+ return names.filter((n2) => pattern.test(n2)).map((n2) => import_node_path13.default.join(logsDir, n2));
50813
51002
  }
50814
51003
  async function scanFile(filePath, source, filter, limit, state) {
50815
- const file2 = import_node_path12.default.basename(filePath);
50816
- const stream = import_node_fs4.default.createReadStream(filePath, { encoding: "utf-8" });
51004
+ const file2 = import_node_path13.default.basename(filePath);
51005
+ const stream = import_node_fs5.default.createReadStream(filePath, { encoding: "utf-8" });
50817
51006
  const rl2 = import_node_readline.default.createInterface({ input: stream, crlfDelay: Infinity });
50818
51007
  let lineNum = 0;
50819
51008
  for await (const line of rl2) {
@@ -50851,7 +51040,7 @@ async function scanLocalLogs(logsDir, baseName, filter) {
50851
51040
  };
50852
51041
  }
50853
51042
  async function scanBridgeLogs(filter) {
50854
- const logDir = import_node_path12.default.join(import_node_os8.default.homedir(), ".ahchat", "logs");
51043
+ const logDir = import_node_path13.default.join(import_node_os9.default.homedir(), ".ahchat", "logs");
50855
51044
  logger18.info("scanBridgeLogs start", {
50856
51045
  logDir,
50857
51046
  startIso: filter.startIso,
@@ -50869,15 +51058,15 @@ async function scanBridgeLogs(filter) {
50869
51058
 
50870
51059
  // src/skillStore.ts
50871
51060
  init_cjs_shims();
50872
- var import_node_fs5 = __toESM(require("fs"), 1);
50873
- var import_node_path13 = __toESM(require("path"), 1);
51061
+ var import_node_fs6 = __toESM(require("fs"), 1);
51062
+ var import_node_path14 = __toESM(require("path"), 1);
50874
51063
  var logger19 = createModuleLogger("bridge.skillStore");
50875
51064
  var ALLOWED_NAMES = /* @__PURE__ */ new Set(["log-analysis"]);
50876
51065
  var SkillStore = class {
50877
51066
  skillsDir;
50878
51067
  constructor(dataDir) {
50879
- this.skillsDir = import_node_path13.default.join(dataDir, "skills");
50880
- import_node_fs5.default.mkdirSync(this.skillsDir, { recursive: true });
51068
+ this.skillsDir = import_node_path14.default.join(dataDir, "skills");
51069
+ import_node_fs6.default.mkdirSync(this.skillsDir, { recursive: true });
50881
51070
  logger19.info("SkillStore initialized", { skillsDir: this.skillsDir });
50882
51071
  }
50883
51072
  read(name) {
@@ -50885,9 +51074,9 @@ var SkillStore = class {
50885
51074
  logger19.warn("Skill read: unknown name", { name, allowed: [...ALLOWED_NAMES] });
50886
51075
  return "";
50887
51076
  }
50888
- const filePath = import_node_path13.default.join(this.skillsDir, `${name}.md`);
51077
+ const filePath = import_node_path14.default.join(this.skillsDir, `${name}.md`);
50889
51078
  try {
50890
- const content = import_node_fs5.default.readFileSync(filePath, "utf-8");
51079
+ const content = import_node_fs6.default.readFileSync(filePath, "utf-8");
50891
51080
  logger19.info("Skill read", { name, bytes: content.length });
50892
51081
  return content;
50893
51082
  } catch (e7) {
@@ -50900,19 +51089,19 @@ var SkillStore = class {
50900
51089
  if (!ALLOWED_NAMES.has(name)) {
50901
51090
  throw new Error(`Unknown skill name: ${name}`);
50902
51091
  }
50903
- const filePath = import_node_path13.default.join(this.skillsDir, `${name}.md`);
51092
+ const filePath = import_node_path14.default.join(this.skillsDir, `${name}.md`);
50904
51093
  const tmpPath = `${filePath}.tmp`;
50905
51094
  let existing = "";
50906
51095
  try {
50907
- existing = import_node_fs5.default.readFileSync(filePath, "utf-8");
51096
+ existing = import_node_fs6.default.readFileSync(filePath, "utf-8");
50908
51097
  } catch {
50909
51098
  }
50910
51099
  if (existing === content) {
50911
51100
  logger19.info("Skill already in sync", { name, bytes: content.length });
50912
51101
  return;
50913
51102
  }
50914
- import_node_fs5.default.writeFileSync(tmpPath, content, "utf-8");
50915
- import_node_fs5.default.renameSync(tmpPath, filePath);
51103
+ import_node_fs6.default.writeFileSync(tmpPath, content, "utf-8");
51104
+ import_node_fs6.default.renameSync(tmpPath, filePath);
50916
51105
  logger19.info("Skill seeded/re-synced", {
50917
51106
  name,
50918
51107
  bytes: content.length,
@@ -50923,8 +51112,8 @@ var SkillStore = class {
50923
51112
 
50924
51113
  // src/lockfile.ts
50925
51114
  init_cjs_shims();
50926
- var import_node_fs6 = __toESM(require("fs"), 1);
50927
- var import_node_path14 = __toESM(require("path"), 1);
51115
+ var import_node_fs7 = __toESM(require("fs"), 1);
51116
+ var import_node_path15 = __toESM(require("path"), 1);
50928
51117
  var logger20 = createModuleLogger("bridge.lockfile");
50929
51118
  var lockPath = null;
50930
51119
  function isProcessAlive(pid) {
@@ -50938,10 +51127,10 @@ function isProcessAlive(pid) {
50938
51127
  }
50939
51128
  }
50940
51129
  function acquireLock(dataDir) {
50941
- const file2 = import_node_path14.default.join(dataDir, "bridge.lock");
51130
+ const file2 = import_node_path15.default.join(dataDir, "bridge.lock");
50942
51131
  lockPath = file2;
50943
- if (import_node_fs6.default.existsSync(file2)) {
50944
- const raw = import_node_fs6.default.readFileSync(file2, "utf-8").trim();
51132
+ if (import_node_fs7.default.existsSync(file2)) {
51133
+ const raw = import_node_fs7.default.readFileSync(file2, "utf-8").trim();
50945
51134
  const pid = Number.parseInt(raw, 10);
50946
51135
  if (Number.isFinite(pid) && pid > 0) {
50947
51136
  if (isProcessAlive(pid)) {
@@ -50950,15 +51139,15 @@ function acquireLock(dataDir) {
50950
51139
  logger20.warn("Removing stale bridge.lock (process not found)", { pid, path: file2 });
50951
51140
  }
50952
51141
  }
50953
- import_node_fs6.default.mkdirSync(import_node_path14.default.dirname(file2), { recursive: true });
50954
- import_node_fs6.default.writeFileSync(file2, String(process.pid), "utf-8");
51142
+ import_node_fs7.default.mkdirSync(import_node_path15.default.dirname(file2), { recursive: true });
51143
+ import_node_fs7.default.writeFileSync(file2, String(process.pid), "utf-8");
50955
51144
  logger20.info("Acquired bridge lock", { path: file2, pid: process.pid });
50956
51145
  const release = () => {
50957
51146
  try {
50958
- if (lockPath && import_node_fs6.default.existsSync(lockPath)) {
50959
- const current = import_node_fs6.default.readFileSync(lockPath, "utf-8").trim();
51147
+ if (lockPath && import_node_fs7.default.existsSync(lockPath)) {
51148
+ const current = import_node_fs7.default.readFileSync(lockPath, "utf-8").trim();
50960
51149
  if (current === String(process.pid)) {
50961
- import_node_fs6.default.unlinkSync(lockPath);
51150
+ import_node_fs7.default.unlinkSync(lockPath);
50962
51151
  logger20.info("Released bridge lock", { path: lockPath });
50963
51152
  }
50964
51153
  }
@@ -51319,14 +51508,14 @@ async function handleGroupArchivedPush(deps, payload) {
51319
51508
 
51320
51509
  // src/sessionStore.ts
51321
51510
  init_cjs_shims();
51322
- var import_node_fs7 = __toESM(require("fs"), 1);
51323
- var import_node_path15 = __toESM(require("path"), 1);
51511
+ var import_node_fs8 = __toESM(require("fs"), 1);
51512
+ var import_node_path16 = __toESM(require("path"), 1);
51324
51513
  var logger23 = createModuleLogger("session.store");
51325
51514
  var SessionStore = class {
51326
51515
  filePath;
51327
51516
  cache;
51328
51517
  constructor(dataDir) {
51329
- this.filePath = import_node_path15.default.join(dataDir, "sessions.json");
51518
+ this.filePath = import_node_path16.default.join(dataDir, "sessions.json");
51330
51519
  this.cache = this.loadFromDisk();
51331
51520
  }
51332
51521
  cacheKey(agentId, scope) {
@@ -51361,8 +51550,8 @@ var SessionStore = class {
51361
51550
  }
51362
51551
  loadFromDisk() {
51363
51552
  try {
51364
- if (!import_node_fs7.default.existsSync(this.filePath)) return {};
51365
- const raw = import_node_fs7.default.readFileSync(this.filePath, "utf-8");
51553
+ if (!import_node_fs8.default.existsSync(this.filePath)) return {};
51554
+ const raw = import_node_fs8.default.readFileSync(this.filePath, "utf-8");
51366
51555
  const parsed = JSON.parse(raw);
51367
51556
  if (typeof parsed !== "object" || parsed === null || Array.isArray(parsed)) return {};
51368
51557
  const map2 = parsed;
@@ -51386,9 +51575,9 @@ var SessionStore = class {
51386
51575
  }
51387
51576
  saveToDisk() {
51388
51577
  try {
51389
- const dir = import_node_path15.default.dirname(this.filePath);
51390
- import_node_fs7.default.mkdirSync(dir, { recursive: true });
51391
- import_node_fs7.default.writeFileSync(this.filePath, JSON.stringify(this.cache, null, 2), "utf-8");
51578
+ const dir = import_node_path16.default.dirname(this.filePath);
51579
+ import_node_fs8.default.mkdirSync(dir, { recursive: true });
51580
+ import_node_fs8.default.writeFileSync(this.filePath, JSON.stringify(this.cache, null, 2), "utf-8");
51392
51581
  } catch (e7) {
51393
51582
  logger23.error("Failed to save sessions file", { error: e7, path: this.filePath });
51394
51583
  }
@@ -51397,9 +51586,9 @@ var SessionStore = class {
51397
51586
 
51398
51587
  // src/ensureClaudeCli.ts
51399
51588
  init_cjs_shims();
51400
- var import_node_child_process2 = require("child_process");
51401
- var import_node_fs8 = require("fs");
51402
- var import_node_path16 = require("path");
51589
+ var import_node_child_process3 = require("child_process");
51590
+ var import_node_fs9 = require("fs");
51591
+ var import_node_path17 = require("path");
51403
51592
  var logger24 = createModuleLogger("bridge.ensureCli");
51404
51593
  var DEFAULT_INSTALL_TIMEOUT_MS = 6e5;
51405
51594
  function getInstallTimeoutMs() {
@@ -51408,48 +51597,42 @@ function getInstallTimeoutMs() {
51408
51597
  const parsed = Number.parseInt(raw, 10);
51409
51598
  return Number.isFinite(parsed) && parsed > 0 ? parsed : DEFAULT_INSTALL_TIMEOUT_MS;
51410
51599
  }
51411
- function detectClaudeCli() {
51412
- try {
51413
- return (0, import_node_child_process2.execFileSync)("claude", ["--version"], { timeout: 1e4 }).toString().trim();
51414
- } catch {
51415
- return void 0;
51416
- }
51417
- }
51418
51600
  function getNpmGlobalBin() {
51601
+ const npm = resolveCommand(["npm"]);
51602
+ if (!npm) return void 0;
51419
51603
  try {
51420
- const bin = (0, import_node_child_process2.execSync)("npm bin -g", { timeout: 5e3 }).toString().trim() || void 0;
51604
+ const bin = (0, import_node_child_process3.execFileSync)(npm.path, ["bin", "-g"], {
51605
+ env: withAugmentedPathEnv(),
51606
+ timeout: 5e3
51607
+ }).toString().trim() || void 0;
51421
51608
  if (bin) return bin;
51422
51609
  } catch {
51423
51610
  }
51424
51611
  try {
51425
- const prefix = (0, import_node_child_process2.execSync)("npm prefix -g", { timeout: 5e3 }).toString().trim();
51426
- if (prefix) return (0, import_node_path16.join)(prefix, "bin");
51612
+ const prefix = (0, import_node_child_process3.execFileSync)(npm.path, ["prefix", "-g"], {
51613
+ env: withAugmentedPathEnv(),
51614
+ timeout: 5e3
51615
+ }).toString().trim();
51616
+ if (prefix) return (0, import_node_path17.join)(prefix, "bin");
51427
51617
  } catch {
51428
51618
  }
51429
51619
  return void 0;
51430
51620
  }
51431
51621
  function resolveClaudeBinary() {
51432
- const whichCmd = process.platform === "win32" ? "where" : "which";
51433
- try {
51434
- const out = (0, import_node_child_process2.execFileSync)(whichCmd, ["claude"], { timeout: 5e3 }).toString();
51435
- const first = out.split(/\r?\n/).map((l4) => l4.trim()).find(Boolean);
51436
- if (first) return first;
51437
- } catch {
51438
- }
51439
- return resolveViaNpmBin();
51622
+ return resolveCommand(["claude", "anthropic-cli"])?.path ?? resolveViaNpmBin();
51440
51623
  }
51441
51624
  function getNpmClaudeCandidates(bin) {
51442
51625
  if (process.platform === "win32") {
51443
- return ["claude.cmd", "claude.exe", "claude", "anthropic-cli.cmd", "anthropic-cli.exe", "anthropic-cli"].map((name) => (0, import_node_path16.join)(bin, name));
51626
+ return ["claude.cmd", "claude.exe", "claude", "anthropic-cli.cmd", "anthropic-cli.exe", "anthropic-cli"].map((name) => (0, import_node_path17.join)(bin, name));
51444
51627
  }
51445
- return [(0, import_node_path16.join)(bin, "claude"), (0, import_node_path16.join)(bin, "anthropic-cli")];
51628
+ return [(0, import_node_path17.join)(bin, "claude"), (0, import_node_path17.join)(bin, "anthropic-cli")];
51446
51629
  }
51447
51630
  function resolveViaNpmBin() {
51448
51631
  const bin = getNpmGlobalBin();
51449
51632
  if (!bin) return void 0;
51450
51633
  for (const candidate of getNpmClaudeCandidates(bin)) {
51451
51634
  try {
51452
- (0, import_node_fs8.accessSync)(candidate, import_node_fs8.constants.X_OK);
51635
+ (0, import_node_fs9.accessSync)(candidate, import_node_fs9.constants.X_OK);
51453
51636
  return candidate;
51454
51637
  } catch {
51455
51638
  }
@@ -51459,40 +51642,32 @@ function resolveViaNpmBin() {
51459
51642
  function detectViaNpmBin() {
51460
51643
  const binPath = resolveViaNpmBin();
51461
51644
  if (!binPath) return void 0;
51462
- try {
51463
- return (0, import_node_child_process2.execFileSync)(binPath, ["--version"], { timeout: 1e4 }).toString().trim();
51464
- } catch {
51465
- }
51466
- return void 0;
51645
+ return readCommandVersion(binPath);
51467
51646
  }
51468
51647
  function detectResolvedClaudeCli() {
51469
- const viaPath = detectClaudeCli();
51470
- if (viaPath) {
51471
- return { version: viaPath, path: resolveClaudeBinary() };
51472
- }
51473
- const npmBinPath = resolveViaNpmBin();
51474
- if (!npmBinPath) return void 0;
51475
- try {
51476
- const version2 = (0, import_node_child_process2.execFileSync)(npmBinPath, ["--version"], { timeout: 1e4 }).toString().trim();
51477
- return { version: version2, path: npmBinPath };
51478
- } catch {
51479
- }
51648
+ const resolvedPath = resolveClaudeBinary();
51649
+ if (!resolvedPath) return void 0;
51650
+ const version2 = readCommandVersion(resolvedPath);
51651
+ if (version2) return { version: version2, path: resolvedPath };
51480
51652
  return void 0;
51481
51653
  }
51482
51654
  function detectVersionFromResolvedCandidates() {
51483
51655
  const candidates = [resolveClaudeBinary(), resolveViaNpmBin()].filter((p) => Boolean(p));
51484
51656
  for (const p of candidates) {
51485
- try {
51486
- (0, import_node_fs8.accessSync)(p, import_node_fs8.constants.X_OK);
51487
- return (0, import_node_child_process2.execFileSync)(p, ["--version"], { timeout: 1e4 }).toString().trim();
51488
- } catch {
51489
- }
51657
+ const version2 = readCommandVersion(p);
51658
+ if (version2) return version2;
51490
51659
  }
51491
51660
  return void 0;
51492
51661
  }
51493
51662
  function installClaudeCli() {
51663
+ const npm = resolveCommand(["npm"]);
51664
+ if (!npm) {
51665
+ logger24.error("npm not found; cannot install Claude Code CLI");
51666
+ return void 0;
51667
+ }
51494
51668
  logger24.info("Installing Claude Code CLI via npm...");
51495
- const result = (0, import_node_child_process2.spawnSync)("npm", ["install", "-g", "@anthropic-ai/claude-code"], {
51669
+ const result = (0, import_node_child_process3.spawnSync)(npm.path, ["install", "-g", "@anthropic-ai/claude-code"], {
51670
+ env: withAugmentedPathEnv(),
51496
51671
  stdio: "inherit",
51497
51672
  timeout: getInstallTimeoutMs()
51498
51673
  });
@@ -51524,7 +51699,10 @@ function installClaudeCli() {
51524
51699
  logger24.error("claude not found after successful npm install -g", {
51525
51700
  npmGlobalBin: npmBin ?? "unknown",
51526
51701
  pathDirectories: envPath.split(process.platform === "win32" ? ";" : ":").slice(0, 10),
51527
- npmPrefix: (0, import_node_child_process2.execSync)("npm prefix -g", { timeout: 5e3 }).toString().trim() || "unknown"
51702
+ npmPrefix: (0, import_node_child_process3.execFileSync)(npm.path, ["prefix", "-g"], {
51703
+ env: withAugmentedPathEnv(),
51704
+ timeout: 5e3
51705
+ }).toString().trim() || "unknown"
51528
51706
  });
51529
51707
  return void 0;
51530
51708
  }
@@ -51554,21 +51732,21 @@ async function ensureClaudeCli() {
51554
51732
  // src/forkAgentFiles.ts
51555
51733
  init_cjs_shims();
51556
51734
  var fs11 = __toESM(require("fs/promises"), 1);
51557
- var path17 = __toESM(require("path"), 1);
51735
+ var path18 = __toESM(require("path"), 1);
51558
51736
 
51559
51737
  // src/sessionSlug.ts
51560
51738
  init_cjs_shims();
51561
- var import_node_os9 = __toESM(require("os"), 1);
51562
- var import_node_path17 = __toESM(require("path"), 1);
51563
- var CLAUDE_PROJECTS_DIR = import_node_path17.default.join(import_node_os9.default.homedir(), ".claude", "projects");
51739
+ var import_node_os10 = __toESM(require("os"), 1);
51740
+ var import_node_path18 = __toESM(require("path"), 1);
51741
+ var CLAUDE_PROJECTS_DIR = import_node_path18.default.join(import_node_os10.default.homedir(), ".claude", "projects");
51564
51742
  function cwdToSlug(cwd) {
51565
51743
  return cwd.replace(/[^a-zA-Z0-9-]/g, "-");
51566
51744
  }
51567
51745
  function sessionDirForCwd(cwd) {
51568
- return import_node_path17.default.join(CLAUDE_PROJECTS_DIR, cwdToSlug(cwd));
51746
+ return import_node_path18.default.join(CLAUDE_PROJECTS_DIR, cwdToSlug(cwd));
51569
51747
  }
51570
51748
  function sessionFilePath(cwd, sessionId) {
51571
- return import_node_path17.default.join(sessionDirForCwd(cwd), `${sessionId}.jsonl`);
51749
+ return import_node_path18.default.join(sessionDirForCwd(cwd), `${sessionId}.jsonl`);
51572
51750
  }
51573
51751
 
51574
51752
  // src/forkAgentFiles.ts
@@ -51600,9 +51778,9 @@ async function forkAgentFiles(sourceAgentId, newAgentId, sourceWorkdir, newWorkd
51600
51778
  logger25.error("Workdir copy failed", { error: e7 });
51601
51779
  throw e7;
51602
51780
  }
51603
- const srcNotebook = path17.join(dataDir, "agent-memory", sourceAgentId, "notebook.md");
51604
- const dstNotebookDir = path17.join(dataDir, "agent-memory", newAgentId);
51605
- const dstNotebook = path17.join(dstNotebookDir, "notebook.md");
51781
+ const srcNotebook = path18.join(dataDir, "agent-memory", sourceAgentId, "notebook.md");
51782
+ const dstNotebookDir = path18.join(dataDir, "agent-memory", newAgentId);
51783
+ const dstNotebook = path18.join(dstNotebookDir, "notebook.md");
51606
51784
  try {
51607
51785
  const nbStat = await fs11.stat(srcNotebook).catch(() => null);
51608
51786
  if (nbStat?.isFile()) {
@@ -51629,7 +51807,7 @@ async function forkAgentFiles(sourceAgentId, newAgentId, sourceWorkdir, newWorkd
51629
51807
  if (srcStat?.isFile()) {
51630
51808
  const dstDir = sessionDirForCwd(newWorkdir);
51631
51809
  await fs11.mkdir(dstDir, { recursive: true });
51632
- const dstPath = path17.join(dstDir, `${sourceSessionId}.jsonl`);
51810
+ const dstPath = path18.join(dstDir, `${sourceSessionId}.jsonl`);
51633
51811
  await fs11.copyFile(srcPath, dstPath);
51634
51812
  sessionStore.set(newAgentId, { kind: "single" }, sourceSessionId);
51635
51813
  sessionCopied = true;
@@ -51677,12 +51855,12 @@ async function forkAgentFiles(sourceAgentId, newAgentId, sourceWorkdir, newWorkd
51677
51855
  // src/modelQuerier.ts
51678
51856
  init_cjs_shims();
51679
51857
  var import_promises11 = __toESM(require("fs/promises"), 1);
51680
- var import_node_os10 = __toESM(require("os"), 1);
51681
- var import_node_path18 = __toESM(require("path"), 1);
51858
+ var import_node_os11 = __toESM(require("os"), 1);
51859
+ var import_node_path19 = __toESM(require("path"), 1);
51682
51860
  var logger26 = createModuleLogger("bridge.modelQuerier");
51683
51861
  async function listModels(queryFn, opts = {}) {
51684
51862
  const t0 = Date.now();
51685
- const cwd = opts.cwd ?? import_node_path18.default.join(import_node_os10.default.homedir(), ".ahchat", "workspaces", "_list_models");
51863
+ const cwd = opts.cwd ?? import_node_path19.default.join(import_node_os11.default.homedir(), ".ahchat", "workspaces", "_list_models");
51686
51864
  await import_promises11.default.mkdir(cwd, { recursive: true });
51687
51865
  const fn = queryFn ?? QA$;
51688
51866
  const ic2 = new InputController();
@@ -51745,8 +51923,8 @@ async function listModels(queryFn, opts = {}) {
51745
51923
  // src/promptOptimizer.ts
51746
51924
  init_cjs_shims();
51747
51925
  var import_promises12 = __toESM(require("fs/promises"), 1);
51748
- var import_node_os11 = __toESM(require("os"), 1);
51749
- var import_node_path19 = __toESM(require("path"), 1);
51926
+ var import_node_os12 = __toESM(require("os"), 1);
51927
+ var import_node_path20 = __toESM(require("path"), 1);
51750
51928
  var logger27 = createModuleLogger("bridge.promptOptimizer");
51751
51929
  var OPTIMIZER_SYSTEM_PROMPT = `You are an expert prompt editor for AHChat Agent creation.
51752
51930
 
@@ -51789,7 +51967,7 @@ async function optimizePrompt(queryFn, opts) {
51789
51967
  const prompt = opts.systemPrompt.trim();
51790
51968
  if (!prompt) throw new Error("systemPrompt is required");
51791
51969
  const t0 = Date.now();
51792
- const cwd = opts.cwd ?? import_node_path19.default.join(import_node_os11.default.homedir(), ".ahchat", "workspaces", "_prompt_optimizer");
51970
+ const cwd = opts.cwd ?? import_node_path20.default.join(import_node_os12.default.homedir(), ".ahchat", "workspaces", "_prompt_optimizer");
51793
51971
  await import_promises12.default.mkdir(cwd, { recursive: true });
51794
51972
  const fn = queryFn ?? QA$;
51795
51973
  const ic2 = new InputController();
@@ -51877,7 +52055,7 @@ function isRunningAsRoot2() {
51877
52055
  }
51878
52056
  }
51879
52057
  async function syncClaudeCredentialsToNodeAccessibleDir(agentConfigDir) {
51880
- const rootClaudeDir = import_node_path20.default.join(process.env.HOME ?? "/root", ".claude");
52058
+ const rootClaudeDir = import_node_path21.default.join(process.env.HOME ?? "/root", ".claude");
51881
52059
  const fs16 = await import("fs/promises");
51882
52060
  try {
51883
52061
  await fs16.access(rootClaudeDir);
@@ -51887,8 +52065,8 @@ async function syncClaudeCredentialsToNodeAccessibleDir(agentConfigDir) {
51887
52065
  }
51888
52066
  const filesToSync = [".credentials.json", "settings.json", ".credentials.backup.json"];
51889
52067
  for (const file2 of filesToSync) {
51890
- const src = import_node_path20.default.join(rootClaudeDir, file2);
51891
- const dest = import_node_path20.default.join(agentConfigDir, file2);
52068
+ const src = import_node_path21.default.join(rootClaudeDir, file2);
52069
+ const dest = import_node_path21.default.join(agentConfigDir, file2);
51892
52070
  try {
51893
52071
  await fs16.copyFile(src, dest);
51894
52072
  logger28.info("Synced credential file", { file: file2, from: src, to: dest });
@@ -51911,7 +52089,7 @@ async function chownRecursive(dirPath, uid, gid) {
51911
52089
  return;
51912
52090
  }
51913
52091
  for (const entry of entries) {
51914
- const fullPath = import_node_path20.default.join(dirPath, entry.name);
52092
+ const fullPath = import_node_path21.default.join(dirPath, entry.name);
51915
52093
  if (entry.isDirectory()) {
51916
52094
  await chownRecursive(fullPath, uid, gid);
51917
52095
  } else {
@@ -51926,7 +52104,7 @@ async function chownRecursive(dirPath, uid, gid) {
51926
52104
  async function startBridge(config2) {
51927
52105
  ensureDir(config2.dataDir);
51928
52106
  ensureDir(config2.agentConfigDir);
51929
- const workspacesDir = import_node_path20.default.join(config2.dataDir, "workspaces");
52107
+ const workspacesDir = import_node_path21.default.join(config2.dataDir, "workspaces");
51930
52108
  ensureDir(workspacesDir);
51931
52109
  process.env.CLAUDE_CONFIG_DIR = config2.agentConfigDir;
51932
52110
  installBridgeFetchAuth(config2.serverApiUrl, config2.bridgeToken);
@@ -51955,7 +52133,7 @@ Bridge token (register this machine at Settings \u2192 \u5DF2\u8FDE\u63A5\u7684\
51955
52133
  `);
51956
52134
  wsMetrics.start(5e3);
51957
52135
  const sessionStore = new SessionStore(config2.dataDir);
51958
- const memoryRoot = import_node_path20.default.join(config2.dataDir, "agent-memory");
52136
+ const memoryRoot = import_node_path21.default.join(config2.dataDir, "agent-memory");
51959
52137
  const memoryStore = new AgentMemoryStore(memoryRoot);
51960
52138
  logger28.info("Agent memory store initialized", { rootDir: memoryRoot });
51961
52139
  const smithNotebook = memoryStore.read(SMITH_AGENT_ID);
@@ -52384,21 +52562,58 @@ function compactEnv(env2) {
52384
52562
 
52385
52563
  // src/protocol.ts
52386
52564
  init_cjs_shims();
52387
- var import_node_child_process3 = require("child_process");
52388
- var import_node_fs9 = __toESM(require("fs"), 1);
52389
- var import_node_os12 = __toESM(require("os"), 1);
52390
- var import_node_path21 = __toESM(require("path"), 1);
52565
+ var import_node_child_process4 = require("child_process");
52566
+ var import_node_fs10 = __toESM(require("fs"), 1);
52567
+ var import_node_os13 = __toESM(require("os"), 1);
52568
+ var import_node_path22 = __toESM(require("path"), 1);
52391
52569
  var logger29 = createModuleLogger("bridge.protocol");
52570
+ function shellSingleQuote(value) {
52571
+ return `'${value.replace(/'/g, `'\\''`)}'`;
52572
+ }
52573
+ function psSingleQuote(value) {
52574
+ return `'${value.replace(/'/g, "''")}'`;
52575
+ }
52576
+ function desktopExecQuote(value) {
52577
+ return `"${value.replace(/\\/g, "\\\\").replace(/"/g, '\\"')}"`;
52578
+ }
52579
+ function getStableCliPath() {
52580
+ return import_node_path22.default.join(import_node_os13.default.homedir(), ".ahchat", "bridge", "cli.cjs");
52581
+ }
52392
52582
  function getStableExePath() {
52393
- const bridgeDir = import_node_path21.default.join(import_node_os12.default.homedir(), ".ahchat", "bridge");
52394
- import_node_fs9.default.mkdirSync(bridgeDir, { recursive: true });
52395
- const stablePath = import_node_path21.default.join(bridgeDir, "cli.cjs");
52396
- import_node_fs9.default.copyFileSync(__filename, stablePath);
52397
- if (process.platform !== "win32") import_node_fs9.default.chmodSync(stablePath, 493);
52583
+ const bridgeDir = import_node_path22.default.join(import_node_os13.default.homedir(), ".ahchat", "bridge");
52584
+ import_node_fs10.default.mkdirSync(bridgeDir, { recursive: true });
52585
+ const stablePath = getStableCliPath();
52586
+ import_node_fs10.default.copyFileSync(__filename, stablePath);
52587
+ if (process.platform !== "win32") import_node_fs10.default.chmodSync(stablePath, 493);
52398
52588
  return stablePath;
52399
52589
  }
52590
+ function writePosixLauncher(bridgeDir, stableExePath) {
52591
+ const launchScriptPath = import_node_path22.default.join(bridgeDir, "launch-bridge.sh");
52592
+ const augmentedPath = buildAugmentedPath();
52593
+ import_node_fs10.default.writeFileSync(
52594
+ launchScriptPath,
52595
+ [
52596
+ "#!/bin/bash",
52597
+ "set -e",
52598
+ `export PATH=${shellSingleQuote(augmentedPath)}:"\${PATH:-}"`,
52599
+ `SAVED_NODE=${shellSingleQuote(process.execPath)}`,
52600
+ `BRIDGE_CLI=${shellSingleQuote(stableExePath)}`,
52601
+ 'NODE_BIN=""',
52602
+ 'if [ -x "$SAVED_NODE" ]; then NODE_BIN="$SAVED_NODE"; fi',
52603
+ 'if [ -z "$NODE_BIN" ]; then NODE_BIN="$(command -v node || true)"; fi',
52604
+ 'if [ -z "$NODE_BIN" ]; then',
52605
+ ` echo "Node.js ${MIN_NODE_MAJOR}+ is required to launch AHChat Bridge. Install Node, then rerun: npx -y @fangyb/ahchat-bridge install" >&2`,
52606
+ " exit 1",
52607
+ "fi",
52608
+ 'exec "$NODE_BIN" "$BRIDGE_CLI" launch --url "$1"',
52609
+ ""
52610
+ ].join("\n")
52611
+ );
52612
+ import_node_fs10.default.chmodSync(launchScriptPath, 493);
52613
+ return launchScriptPath;
52614
+ }
52400
52615
  function registerProtocolHandler() {
52401
- const platform = import_node_os12.default.platform();
52616
+ const platform = import_node_os13.default.platform();
52402
52617
  if (platform === "win32") {
52403
52618
  registerWindows();
52404
52619
  } else if (platform === "darwin") {
@@ -52411,22 +52626,42 @@ function registerProtocolHandler() {
52411
52626
  function registerWindows() {
52412
52627
  const nodeExe = process.execPath;
52413
52628
  const stableExePath = getStableExePath();
52414
- const bridgeDir = import_node_path21.default.join(import_node_os12.default.homedir(), ".ahchat", "bridge");
52415
- const psLauncherPath = import_node_path21.default.join(bridgeDir, "launch-bridge.ps1");
52416
- import_node_fs9.default.writeFileSync(
52629
+ const bridgeDir = import_node_path22.default.join(import_node_os13.default.homedir(), ".ahchat", "bridge");
52630
+ const augmentedPath = buildAugmentedPath();
52631
+ const psLauncherPath = import_node_path22.default.join(bridgeDir, "launch-bridge.ps1");
52632
+ import_node_fs10.default.writeFileSync(
52417
52633
  psLauncherPath,
52418
52634
  [
52419
52635
  `param([string]$url)`,
52420
- `& '${nodeExe.replace(/'/g, "''")}' '${stableExePath.replace(/'/g, "''")}' launch --url $url`
52636
+ `$env:PATH = ${psSingleQuote(augmentedPath)} + [System.IO.Path]::PathSeparator + $env:PATH`,
52637
+ `$candidates = @(`,
52638
+ ` ${psSingleQuote(nodeExe)},`,
52639
+ ` "$env:NVM_SYMLINK\\node.exe",`,
52640
+ ` "$env:ProgramFiles\\nodejs\\node.exe",`,
52641
+ ` "$env:LOCALAPPDATA\\Programs\\nodejs\\node.exe"`,
52642
+ `)`,
52643
+ `$node = $null`,
52644
+ `foreach ($candidate in $candidates) {`,
52645
+ ` if ($candidate -and (Test-Path $candidate)) { $node = $candidate; break }`,
52646
+ `}`,
52647
+ `if (-not $node) {`,
52648
+ ` $cmd = Get-Command node -ErrorAction SilentlyContinue`,
52649
+ ` if ($cmd) { $node = $cmd.Source }`,
52650
+ `}`,
52651
+ `if (-not $node) {`,
52652
+ ` Write-Error 'Node.js ${MIN_NODE_MAJOR}+ is required to launch AHChat Bridge. Install Node, then rerun: npx -y @fangyb/ahchat-bridge install'`,
52653
+ ` exit 1`,
52654
+ `}`,
52655
+ `& $node ${psSingleQuote(stableExePath)} launch --url $url`
52421
52656
  ].join("\r\n")
52422
52657
  );
52423
52658
  const handlerValue = `powershell -ExecutionPolicy Bypass -File "${psLauncherPath}" -url "%1"`;
52424
- const psRegisterPath = import_node_path21.default.join(bridgeDir, "register-protocol.ps1");
52425
- import_node_fs9.default.writeFileSync(
52659
+ const psRegisterPath = import_node_path22.default.join(bridgeDir, "register-protocol.ps1");
52660
+ import_node_fs10.default.writeFileSync(
52426
52661
  psRegisterPath,
52427
52662
  [
52428
- `$handler = '${handlerValue.replace(/'/g, "''")}'`,
52429
- `$icon = '${nodeExe.replace(/'/g, "''")}'`,
52663
+ `$handler = ${psSingleQuote(handlerValue)}`,
52664
+ `$icon = ${psSingleQuote(nodeExe)}`,
52430
52665
  `New-Item -Path 'HKCU:\\Software\\Classes\\ahchat' -Force | Out-Null`,
52431
52666
  `Set-ItemProperty -Path 'HKCU:\\Software\\Classes\\ahchat' -Name '(Default)' -Value 'URL:ahchat' -Force`,
52432
52667
  `New-ItemProperty -Path 'HKCU:\\Software\\Classes\\ahchat' -Name 'URL Protocol' -Value '' -PropertyType String -Force | Out-Null`,
@@ -52437,7 +52672,7 @@ function registerWindows() {
52437
52672
  ].join("\r\n")
52438
52673
  );
52439
52674
  try {
52440
- (0, import_node_child_process3.execSync)(`powershell -ExecutionPolicy Bypass -File "${psRegisterPath}"`, { stdio: "pipe" });
52675
+ (0, import_node_child_process4.execSync)(`powershell -ExecutionPolicy Bypass -File "${psRegisterPath}"`, { stdio: "pipe" });
52441
52676
  } catch (e7) {
52442
52677
  logger29.error("Failed to register Windows protocol handler", { error: e7 });
52443
52678
  throw new Error("Failed to register Windows protocol handler");
@@ -52445,18 +52680,10 @@ function registerWindows() {
52445
52680
  logger29.info("Windows protocol handler registered", { psLauncherPath });
52446
52681
  }
52447
52682
  function registerMacOS() {
52448
- const appDir = import_node_path21.default.join(import_node_os12.default.homedir(), "Applications", "AHChatBridge.app");
52449
- const nodeExe = process.execPath;
52683
+ const appDir = import_node_path22.default.join(import_node_os13.default.homedir(), "Applications", "AHChatBridge.app");
52450
52684
  const stableExePath = getStableExePath();
52451
- const bridgeDir = import_node_path21.default.join(import_node_os12.default.homedir(), ".ahchat", "bridge");
52452
- const launchScriptPath = import_node_path21.default.join(bridgeDir, "launch-bridge.sh");
52453
- import_node_fs9.default.writeFileSync(
52454
- launchScriptPath,
52455
- `#!/bin/bash
52456
- exec ${JSON.stringify(nodeExe)} ${JSON.stringify(stableExePath)} launch --url "$1"
52457
- `
52458
- );
52459
- import_node_fs9.default.chmodSync(launchScriptPath, 493);
52685
+ const bridgeDir = import_node_path22.default.join(import_node_os13.default.homedir(), ".ahchat", "bridge");
52686
+ const launchScriptPath = writePosixLauncher(bridgeDir, stableExePath);
52460
52687
  const escapedScriptPath = launchScriptPath.replace(/\\/g, "\\\\").replace(/"/g, '\\"');
52461
52688
  const appleScript = [
52462
52689
  `on open location thisURL`,
@@ -52464,29 +52691,29 @@ exec ${JSON.stringify(nodeExe)} ${JSON.stringify(stableExePath)} launch --url "$
52464
52691
  ` do shell script "/bin/bash " & (quoted form of launchScript) & " " & (quoted form of thisURL) & " >/tmp/ahchat-bridge.log 2>&1 &"`,
52465
52692
  `end open location`
52466
52693
  ].join("\n");
52467
- const tmpScript = import_node_path21.default.join(import_node_os12.default.tmpdir(), "AHChatBridge.applescript");
52468
- import_node_fs9.default.writeFileSync(tmpScript, appleScript);
52694
+ const tmpScript = import_node_path22.default.join(import_node_os13.default.tmpdir(), "AHChatBridge.applescript");
52695
+ import_node_fs10.default.writeFileSync(tmpScript, appleScript);
52469
52696
  try {
52470
- import_node_fs9.default.rmSync(appDir, { recursive: true, force: true });
52697
+ import_node_fs10.default.rmSync(appDir, { recursive: true, force: true });
52471
52698
  } catch {
52472
52699
  }
52473
52700
  try {
52474
- (0, import_node_child_process3.execSync)(`osacompile -o ${JSON.stringify(appDir)} ${JSON.stringify(tmpScript)}`, { stdio: "pipe" });
52701
+ (0, import_node_child_process4.execSync)(`osacompile -o ${JSON.stringify(appDir)} ${JSON.stringify(tmpScript)}`, { stdio: "pipe" });
52475
52702
  } finally {
52476
52703
  try {
52477
- import_node_fs9.default.unlinkSync(tmpScript);
52704
+ import_node_fs10.default.unlinkSync(tmpScript);
52478
52705
  } catch {
52479
52706
  }
52480
52707
  }
52481
- const plistPath = import_node_path21.default.join(appDir, "Contents", "Info.plist");
52708
+ const plistPath = import_node_path22.default.join(appDir, "Contents", "Info.plist");
52482
52709
  const urlTypes = JSON.stringify([{ CFBundleURLName: "AHChat Bridge", CFBundleURLSchemes: ["ahchat"] }]);
52483
- (0, import_node_child_process3.execSync)(
52710
+ (0, import_node_child_process4.execSync)(
52484
52711
  `/usr/bin/plutil -insert CFBundleURLTypes -json ${JSON.stringify(urlTypes)} ${JSON.stringify(plistPath)}`,
52485
52712
  { stdio: "pipe" }
52486
52713
  );
52487
52714
  const lsregister = "/System/Library/Frameworks/CoreServices.framework/Versions/A/Frameworks/LaunchServices.framework/Versions/A/Support/lsregister";
52488
52715
  try {
52489
- (0, import_node_child_process3.execSync)(`${lsregister} -f ${JSON.stringify(appDir)}`, { stdio: "pipe" });
52716
+ (0, import_node_child_process4.execSync)(`${lsregister} -f ${JSON.stringify(appDir)}`, { stdio: "pipe" });
52490
52717
  } catch (e7) {
52491
52718
  logger29.warn("lsregister failed; URL scheme registration may be delayed", { error: e7 });
52492
52719
  }
@@ -52494,36 +52721,38 @@ exec ${JSON.stringify(nodeExe)} ${JSON.stringify(stableExePath)} launch --url "$
52494
52721
  }
52495
52722
  function registerLinux() {
52496
52723
  const stableExePath = getStableExePath();
52724
+ const bridgeDir = import_node_path22.default.join(import_node_os13.default.homedir(), ".ahchat", "bridge");
52725
+ const launchScriptPath = writePosixLauncher(bridgeDir, stableExePath);
52497
52726
  const desktopFile = [
52498
52727
  `[Desktop Entry]`,
52499
52728
  `Name=AHChat Bridge`,
52500
- `Exec=${process.execPath} ${stableExePath} launch --url %u`,
52729
+ `Exec=${desktopExecQuote(launchScriptPath)} %u`,
52501
52730
  `Type=Application`,
52502
52731
  `NoDisplay=true`,
52503
52732
  `MimeType=x-scheme-handler/ahchat;`
52504
52733
  ].join("\n");
52505
- const desktopPath = import_node_path21.default.join(import_node_os12.default.homedir(), ".local", "share", "applications", "ahchat-bridge.desktop");
52506
- import_node_fs9.default.mkdirSync(import_node_path21.default.dirname(desktopPath), { recursive: true });
52507
- import_node_fs9.default.writeFileSync(desktopPath, desktopFile);
52734
+ const desktopPath = import_node_path22.default.join(import_node_os13.default.homedir(), ".local", "share", "applications", "ahchat-bridge.desktop");
52735
+ import_node_fs10.default.mkdirSync(import_node_path22.default.dirname(desktopPath), { recursive: true });
52736
+ import_node_fs10.default.writeFileSync(desktopPath, desktopFile);
52508
52737
  try {
52509
- (0, import_node_child_process3.execSync)("update-desktop-database ~/.local/share/applications/", { stdio: "pipe" });
52738
+ (0, import_node_child_process4.execSync)("update-desktop-database ~/.local/share/applications/", { stdio: "pipe" });
52510
52739
  } catch (e7) {
52511
52740
  logger29.warn("update-desktop-database not available; run it manually if needed", { error: e7 });
52512
52741
  }
52513
52742
  logger29.info("Linux protocol handler registered", { desktopPath });
52514
52743
  }
52515
52744
  function unregisterProtocolHandler() {
52516
- const platform = import_node_os12.default.platform();
52745
+ const platform = import_node_os13.default.platform();
52517
52746
  if (platform === "win32") {
52518
52747
  try {
52519
- (0, import_node_child_process3.execSync)(
52748
+ (0, import_node_child_process4.execSync)(
52520
52749
  `powershell -ExecutionPolicy Bypass -Command "Remove-Item -Path 'HKCU:\\Software\\Classes\\ahchat' -Recurse -Force -ErrorAction SilentlyContinue"`,
52521
52750
  { stdio: "pipe" }
52522
52751
  );
52523
- const bridgeDir = import_node_path21.default.join(import_node_os12.default.homedir(), ".ahchat", "bridge");
52752
+ const bridgeDir = import_node_path22.default.join(import_node_os13.default.homedir(), ".ahchat", "bridge");
52524
52753
  for (const f7 of ["launch-bridge.ps1", "register-protocol.ps1"]) {
52525
52754
  try {
52526
- import_node_fs9.default.unlinkSync(import_node_path21.default.join(bridgeDir, f7));
52755
+ import_node_fs10.default.unlinkSync(import_node_path22.default.join(bridgeDir, f7));
52527
52756
  } catch {
52528
52757
  }
52529
52758
  }
@@ -52532,17 +52761,17 @@ function unregisterProtocolHandler() {
52532
52761
  logger29.warn("Failed to unregister Windows protocol handler", { error: e7 });
52533
52762
  }
52534
52763
  } else if (platform === "darwin") {
52535
- const appDir = import_node_path21.default.join(import_node_os12.default.homedir(), "Applications", "AHChatBridge.app");
52764
+ const appDir = import_node_path22.default.join(import_node_os13.default.homedir(), "Applications", "AHChatBridge.app");
52536
52765
  try {
52537
- import_node_fs9.default.rmSync(appDir, { recursive: true, force: true });
52766
+ import_node_fs10.default.rmSync(appDir, { recursive: true, force: true });
52538
52767
  logger29.info("macOS protocol handler unregistered");
52539
52768
  } catch (e7) {
52540
52769
  logger29.warn("Failed to unregister macOS protocol handler", { error: e7 });
52541
52770
  }
52542
52771
  } else {
52543
- const desktopPath = import_node_path21.default.join(import_node_os12.default.homedir(), ".local", "share", "applications", "ahchat-bridge.desktop");
52772
+ const desktopPath = import_node_path22.default.join(import_node_os13.default.homedir(), ".local", "share", "applications", "ahchat-bridge.desktop");
52544
52773
  try {
52545
- import_node_fs9.default.unlinkSync(desktopPath);
52774
+ import_node_fs10.default.unlinkSync(desktopPath);
52546
52775
  logger29.info("Linux protocol handler unregistered");
52547
52776
  } catch (e7) {
52548
52777
  logger29.warn("Failed to unregister Linux protocol handler", { error: e7 });
@@ -52550,20 +52779,21 @@ function unregisterProtocolHandler() {
52550
52779
  }
52551
52780
  }
52552
52781
  function isProtocolRegistered() {
52553
- const platform = import_node_os12.default.platform();
52782
+ const platform = import_node_os13.default.platform();
52783
+ const stableCliExists = import_node_fs10.default.existsSync(getStableCliPath());
52554
52784
  if (platform === "win32") {
52555
52785
  try {
52556
- (0, import_node_child_process3.execSync)('REG QUERY "HKCU\\Software\\Classes\\ahchat" /ve', { stdio: "pipe" });
52557
- return true;
52786
+ (0, import_node_child_process4.execSync)('REG QUERY "HKCU\\Software\\Classes\\ahchat" /ve', { stdio: "pipe" });
52787
+ return stableCliExists;
52558
52788
  } catch {
52559
52789
  return false;
52560
52790
  }
52561
52791
  } else if (platform === "darwin") {
52562
- const appDir = import_node_path21.default.join(import_node_os12.default.homedir(), "Applications", "AHChatBridge.app");
52563
- return import_node_fs9.default.existsSync(import_node_path21.default.join(appDir, "Contents", "Info.plist"));
52792
+ const appDir = import_node_path22.default.join(import_node_os13.default.homedir(), "Applications", "AHChatBridge.app");
52793
+ return stableCliExists && import_node_fs10.default.existsSync(import_node_path22.default.join(appDir, "Contents", "Info.plist"));
52564
52794
  } else {
52565
- const desktopPath = import_node_path21.default.join(import_node_os12.default.homedir(), ".local", "share", "applications", "ahchat-bridge.desktop");
52566
- return import_node_fs9.default.existsSync(desktopPath);
52795
+ const desktopPath = import_node_path22.default.join(import_node_os13.default.homedir(), ".local", "share", "applications", "ahchat-bridge.desktop");
52796
+ return stableCliExists && import_node_fs10.default.existsSync(desktopPath);
52567
52797
  }
52568
52798
  }
52569
52799
 
@@ -52571,14 +52801,14 @@ function isProtocolRegistered() {
52571
52801
  var logger30 = createModuleLogger("bridge");
52572
52802
  function readCliVersion() {
52573
52803
  const candidates = [
52574
- import_node_path22.default.resolve(__dirname, "../package.json"),
52575
- import_node_path22.default.resolve(__dirname, "../../package.json"),
52576
- import_node_path22.default.resolve(process.cwd(), "packages/bridge/package.json")
52804
+ import_node_path23.default.resolve(__dirname, "../package.json"),
52805
+ import_node_path23.default.resolve(__dirname, "../../package.json"),
52806
+ import_node_path23.default.resolve(process.cwd(), "packages/bridge/package.json")
52577
52807
  ];
52578
52808
  for (const candidate of candidates) {
52579
- if (!import_node_fs10.default.existsSync(candidate)) continue;
52809
+ if (!import_node_fs11.default.existsSync(candidate)) continue;
52580
52810
  try {
52581
- const parsed = JSON.parse(import_node_fs10.default.readFileSync(candidate, "utf8"));
52811
+ const parsed = JSON.parse(import_node_fs11.default.readFileSync(candidate, "utf8"));
52582
52812
  if (parsed && typeof parsed === "object" && "version" in parsed && typeof parsed.version === "string" && parsed.version.length > 0) {
52583
52813
  return parsed.version;
52584
52814
  }
@@ -52588,25 +52818,6 @@ function readCliVersion() {
52588
52818
  }
52589
52819
  return "0.1.21";
52590
52820
  }
52591
- function resolveDataDir(dataDir) {
52592
- const userHome = process.env.USERPROFILE || import_node_os13.default.homedir();
52593
- if (/^~[/\\]/.test(dataDir)) {
52594
- return import_node_path22.default.join(import_node_os13.default.homedir(), dataDir.slice(2));
52595
- }
52596
- if (dataDir === "$env:USERPROFILE") {
52597
- return userHome;
52598
- }
52599
- if (dataDir.startsWith("$env:USERPROFILE\\") || dataDir.startsWith("$env:USERPROFILE/")) {
52600
- return import_node_path22.default.join(userHome, dataDir.slice("$env:USERPROFILE".length + 1));
52601
- }
52602
- if (dataDir === "%USERPROFILE%") {
52603
- return userHome;
52604
- }
52605
- if (dataDir.startsWith("%USERPROFILE%\\") || dataDir.startsWith("%USERPROFILE%/")) {
52606
- return import_node_path22.default.join(userHome, dataDir.slice("%USERPROFILE%".length + 1));
52607
- }
52608
- return dataDir;
52609
- }
52610
52821
  function parseAhchatUrl(url2) {
52611
52822
  try {
52612
52823
  if (!url2.startsWith("ahchat://")) return null;
@@ -52620,22 +52831,97 @@ function parseAhchatUrl(url2) {
52620
52831
  const token = decodeURIComponent(rest.slice(lastTilde + 1));
52621
52832
  if (!serverUrl || !token) return null;
52622
52833
  return { serverUrl, token };
52623
- } catch {
52834
+ } catch (e7) {
52835
+ logger30.warn("Unable to parse ahchat launch URL", { error: e7 });
52624
52836
  return null;
52625
52837
  }
52626
52838
  }
52839
+ function assertSupportedNode() {
52840
+ const status = getNodeRuntimeStatus();
52841
+ if (status.supported) return;
52842
+ throw new Error(
52843
+ `Node.js ${MIN_NODE_MAJOR}+ is required to run AHChat Bridge. Current version: ${status.version}`
52844
+ );
52845
+ }
52627
52846
  async function run(args) {
52628
- const dataDir = args.dataDir ? resolveDataDir(args.dataDir) : void 0;
52847
+ assertSupportedNode();
52848
+ process.env.PATH = buildAugmentedPath();
52849
+ const dataDir = args.dataDir ? resolveUserPath(args.dataDir) : void 0;
52629
52850
  let config2 = loadBridgeConfig(dataDir ? { dataDir } : void 0);
52630
52851
  if (args.serverUrl) {
52631
- const wsUrl = new URL(args.serverUrl);
52632
- const httpBase = `${wsUrl.protocol === "wss:" ? "https" : "http"}://${wsUrl.host}`;
52633
- config2 = { ...config2, serverUrl: args.serverUrl, serverApiUrl: httpBase };
52852
+ const urls = normalizeBridgeServerUrls(args.serverUrl);
52853
+ config2 = { ...config2, serverUrl: urls.serverUrl, serverApiUrl: urls.serverApiUrl };
52634
52854
  }
52635
52855
  if (args.token) config2 = { ...config2, bridgeToken: args.token };
52636
52856
  if (args.logLevel) config2 = { ...config2, logLevel: args.logLevel };
52637
52857
  await startBridge(config2);
52638
52858
  }
52859
+ function buildDoctorReport(args) {
52860
+ process.env.PATH = buildAugmentedPath();
52861
+ const node = getNodeRuntimeStatus();
52862
+ let server = null;
52863
+ if (args.serverUrl) {
52864
+ try {
52865
+ server = { ok: true, ...normalizeBridgeServerUrls(args.serverUrl) };
52866
+ } catch (e7) {
52867
+ server = {
52868
+ ok: false,
52869
+ error: e7 instanceof Error ? e7.message : String(e7)
52870
+ };
52871
+ }
52872
+ }
52873
+ const claude = resolveCommand(["claude", "anthropic-cli"]);
52874
+ return {
52875
+ node,
52876
+ npm: probeCommand("npm"),
52877
+ npx: probeCommand("npx"),
52878
+ claude: claude ? { ok: true, name: claude.name, path: claude.path } : { ok: false, name: "claude", message: "claude was not found on PATH" },
52879
+ dataDir: args.dataDir ? resolveUserPath(args.dataDir) : resolveUserPath("~/.ahchat"),
52880
+ server,
52881
+ tokenProvided: Boolean(args.token),
52882
+ pathPreview: (process.env.PATH ?? "").split(import_node_path23.default.delimiter).slice(0, 12)
52883
+ };
52884
+ }
52885
+ function writeDoctorText(report) {
52886
+ const node = report.node;
52887
+ const npm = report.npm;
52888
+ const npx = report.npx;
52889
+ const claude = report.claude;
52890
+ const server = report.server;
52891
+ const lines = [
52892
+ "AHChat Bridge doctor",
52893
+ `- Node: ${node.supported ? "ok" : "error"} (${node.version}, requires >=${MIN_NODE_MAJOR})`,
52894
+ `- npm: ${npm.ok ? `ok (${npm.version ?? npm.path})` : `missing (${npm.message ?? "not found"})`}`,
52895
+ `- npx: ${npx.ok ? `ok (${npx.version ?? npx.path})` : `missing (${npx.message ?? "not found"})`}`,
52896
+ `- Claude CLI: ${claude.ok ? `ok (${claude.path})` : `missing (${claude.message ?? "not found"})`}`,
52897
+ `- Data dir: ${String(report.dataDir)}`,
52898
+ `- Token: ${report.tokenProvided ? "provided" : "not provided"}`
52899
+ ];
52900
+ if (server) {
52901
+ lines.push(
52902
+ server.ok ? `- Server: ok (${server.serverUrl}, api ${server.serverApiUrl})` : `- Server: error (${server.error ?? "invalid URL"})`
52903
+ );
52904
+ }
52905
+ if (!node.supported) {
52906
+ lines.push(`
52907
+ Install Node.js ${MIN_NODE_MAJOR}+ first, then rerun the bridge command.`);
52908
+ } else if (!npm.ok || !npx.ok) {
52909
+ lines.push("\nNode is installed, but npm/npx is not available on PATH. Reinstall Node.js or fix PATH.");
52910
+ } else if (!claude.ok) {
52911
+ lines.push("\nClaude Code CLI is missing. The bridge can try to install it on first run with npm.");
52912
+ }
52913
+ process.stdout.write(`${lines.join("\n")}
52914
+ `);
52915
+ }
52916
+ function doctor(args) {
52917
+ const report = buildDoctorReport(args);
52918
+ if (args.json) {
52919
+ process.stdout.write(`${JSON.stringify(report, null, 2)}
52920
+ `);
52921
+ return;
52922
+ }
52923
+ writeDoctorText(report);
52924
+ }
52639
52925
  var cli = dist_default("ahchat-bridge");
52640
52926
  cli.command("run", "Start the bridge and connect to server").option("--server-url <url>", "WebSocket URL of the AHChat server").option("--token <token>", "Auth token for server registration").option("--data-dir <dir>", "Data directory (default: ~/.ahchat)").option("--log-level <level>", "Log level (default: INFO)").action((args) => {
52641
52927
  void run(args).catch((e7) => {
@@ -52643,6 +52929,9 @@ cli.command("run", "Start the bridge and connect to server").option("--server-ur
52643
52929
  process.exit(1);
52644
52930
  });
52645
52931
  });
52932
+ cli.command("doctor", "Check Node, npm, Claude CLI, PATH, and bridge launch options").option("--server-url <url>", "WebSocket URL of the AHChat server").option("--token <token>", "Auth token for server registration").option("--data-dir <dir>", "Data directory (default: ~/.ahchat)").option("--json", "Print machine-readable JSON").action((args) => {
52933
+ doctor(args);
52934
+ });
52646
52935
  cli.command("launch", "Launch bridge from ahchat:// URL (called by OS)").option("--url <url>", "ahchat:// URL with server and token params").action((args) => {
52647
52936
  const parsed = parseAhchatUrl(args.url);
52648
52937
  if (!parsed) {