@fangyb/ahchat-bridge 0.1.21 → 0.1.23

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 +535 -234
  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,220 @@ 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 assertBridgeUserSlug(user) {
50930
+ const value = user.trim();
50931
+ if (!/^[A-Za-z0-9_-]+$/.test(value)) {
50932
+ throw new Error('Bridge --user may only contain letters, numbers, "_" and "-"');
50933
+ }
50934
+ return value;
50935
+ }
50936
+ function resolveBridgeDataDir(opts) {
50937
+ if (opts.dataDir) return resolveUserPath(opts.dataDir);
50938
+ if (opts.user) return resolveUserPath(`~/.ahchat/users/${assertBridgeUserSlug(opts.user)}`);
50939
+ return void 0;
50940
+ }
50941
+ function normalizeBridgeServerUrls(rawUrl) {
50942
+ const url2 = new URL(rawUrl);
50943
+ if (url2.protocol === "http:" || url2.protocol === "https:") {
50944
+ url2.protocol = url2.protocol === "https:" ? "wss:" : "ws:";
50945
+ if (url2.pathname === "/" || url2.pathname === "") url2.pathname = "/ws/bridge";
50946
+ }
50947
+ if (url2.protocol !== "ws:" && url2.protocol !== "wss:") {
50948
+ throw new Error(`Unsupported bridge server URL protocol: ${url2.protocol}`);
50949
+ }
50950
+ const serverUrl = url2.toString();
50951
+ const serverApiUrl = `${url2.protocol === "wss:" ? "https" : "http"}://${url2.host}`;
50952
+ return { serverUrl, serverApiUrl };
50953
+ }
50954
+
50955
+ // src/listDir.ts
50757
50956
  var logger17 = createModuleLogger("bridge.listDir");
50758
50957
  function shouldIncludeEntry(name) {
50759
50958
  if (!name.startsWith(".")) return true;
50760
50959
  return name === ".ahchat-attachments";
50761
50960
  }
50762
50961
  async function listDirectoryEntries(dirPath) {
50763
- logger17.info("listDirectoryEntries start", { path: dirPath });
50764
- const raw = await import_promises10.default.readdir(dirPath, { withFileTypes: true });
50962
+ const resolvedDirPath = resolveUserPath(dirPath);
50963
+ logger17.info("listDirectoryEntries start", { path: dirPath, resolvedPath: resolvedDirPath });
50964
+ const raw = await import_promises10.default.readdir(resolvedDirPath, { withFileTypes: true });
50765
50965
  const entries = [];
50766
50966
  for (const entry of raw) {
50767
50967
  if (!shouldIncludeEntry(entry.name)) continue;
50768
- const fullPath = import_node_path11.default.join(dirPath, entry.name);
50968
+ const fullPath = import_node_path12.default.join(resolvedDirPath, entry.name);
50769
50969
  const isDir = entry.isDirectory();
50770
50970
  let size;
50771
50971
  let mtime;
@@ -50784,6 +50984,7 @@ async function listDirectoryEntries(dirPath) {
50784
50984
  });
50785
50985
  logger17.info("listDirectoryEntries ok", {
50786
50986
  path: dirPath,
50987
+ resolvedPath: resolvedDirPath,
50787
50988
  count: entries.length,
50788
50989
  dirCount: entries.filter((e7) => e7.type === "dir").length,
50789
50990
  fileCount: entries.filter((e7) => e7.type === "file").length
@@ -50793,9 +50994,9 @@ async function listDirectoryEntries(dirPath) {
50793
50994
 
50794
50995
  // src/logScanner.ts
50795
50996
  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);
50997
+ var import_node_fs5 = __toESM(require("fs"), 1);
50998
+ var import_node_path13 = __toESM(require("path"), 1);
50999
+ var import_node_os9 = __toESM(require("os"), 1);
50799
51000
  var import_node_readline = __toESM(require("readline"), 1);
50800
51001
  var logger18 = createModuleLogger("bridge.logScanner");
50801
51002
  var DEFAULT_LIMIT = 500;
@@ -50803,17 +51004,17 @@ var MAX_LIMIT = 2e3;
50803
51004
  function listLogFiles(logsDir, baseName) {
50804
51005
  let names;
50805
51006
  try {
50806
- names = import_node_fs4.default.readdirSync(logsDir);
51007
+ names = import_node_fs5.default.readdirSync(logsDir);
50807
51008
  } catch (e7) {
50808
51009
  logger18.warn("listLogFiles: readdir failed", { logsDir, error: e7 });
50809
51010
  return [];
50810
51011
  }
50811
51012
  const pattern = new RegExp(`^${baseName.replace(".", "\\.")}(\\.\\d+)?$`);
50812
- return names.filter((n2) => pattern.test(n2)).map((n2) => import_node_path12.default.join(logsDir, n2));
51013
+ return names.filter((n2) => pattern.test(n2)).map((n2) => import_node_path13.default.join(logsDir, n2));
50813
51014
  }
50814
51015
  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" });
51016
+ const file2 = import_node_path13.default.basename(filePath);
51017
+ const stream = import_node_fs5.default.createReadStream(filePath, { encoding: "utf-8" });
50817
51018
  const rl2 = import_node_readline.default.createInterface({ input: stream, crlfDelay: Infinity });
50818
51019
  let lineNum = 0;
50819
51020
  for await (const line of rl2) {
@@ -50851,7 +51052,7 @@ async function scanLocalLogs(logsDir, baseName, filter) {
50851
51052
  };
50852
51053
  }
50853
51054
  async function scanBridgeLogs(filter) {
50854
- const logDir = import_node_path12.default.join(import_node_os8.default.homedir(), ".ahchat", "logs");
51055
+ const logDir = import_node_path13.default.join(import_node_os9.default.homedir(), ".ahchat", "logs");
50855
51056
  logger18.info("scanBridgeLogs start", {
50856
51057
  logDir,
50857
51058
  startIso: filter.startIso,
@@ -50869,15 +51070,15 @@ async function scanBridgeLogs(filter) {
50869
51070
 
50870
51071
  // src/skillStore.ts
50871
51072
  init_cjs_shims();
50872
- var import_node_fs5 = __toESM(require("fs"), 1);
50873
- var import_node_path13 = __toESM(require("path"), 1);
51073
+ var import_node_fs6 = __toESM(require("fs"), 1);
51074
+ var import_node_path14 = __toESM(require("path"), 1);
50874
51075
  var logger19 = createModuleLogger("bridge.skillStore");
50875
51076
  var ALLOWED_NAMES = /* @__PURE__ */ new Set(["log-analysis"]);
50876
51077
  var SkillStore = class {
50877
51078
  skillsDir;
50878
51079
  constructor(dataDir) {
50879
- this.skillsDir = import_node_path13.default.join(dataDir, "skills");
50880
- import_node_fs5.default.mkdirSync(this.skillsDir, { recursive: true });
51080
+ this.skillsDir = import_node_path14.default.join(dataDir, "skills");
51081
+ import_node_fs6.default.mkdirSync(this.skillsDir, { recursive: true });
50881
51082
  logger19.info("SkillStore initialized", { skillsDir: this.skillsDir });
50882
51083
  }
50883
51084
  read(name) {
@@ -50885,9 +51086,9 @@ var SkillStore = class {
50885
51086
  logger19.warn("Skill read: unknown name", { name, allowed: [...ALLOWED_NAMES] });
50886
51087
  return "";
50887
51088
  }
50888
- const filePath = import_node_path13.default.join(this.skillsDir, `${name}.md`);
51089
+ const filePath = import_node_path14.default.join(this.skillsDir, `${name}.md`);
50889
51090
  try {
50890
- const content = import_node_fs5.default.readFileSync(filePath, "utf-8");
51091
+ const content = import_node_fs6.default.readFileSync(filePath, "utf-8");
50891
51092
  logger19.info("Skill read", { name, bytes: content.length });
50892
51093
  return content;
50893
51094
  } catch (e7) {
@@ -50900,19 +51101,19 @@ var SkillStore = class {
50900
51101
  if (!ALLOWED_NAMES.has(name)) {
50901
51102
  throw new Error(`Unknown skill name: ${name}`);
50902
51103
  }
50903
- const filePath = import_node_path13.default.join(this.skillsDir, `${name}.md`);
51104
+ const filePath = import_node_path14.default.join(this.skillsDir, `${name}.md`);
50904
51105
  const tmpPath = `${filePath}.tmp`;
50905
51106
  let existing = "";
50906
51107
  try {
50907
- existing = import_node_fs5.default.readFileSync(filePath, "utf-8");
51108
+ existing = import_node_fs6.default.readFileSync(filePath, "utf-8");
50908
51109
  } catch {
50909
51110
  }
50910
51111
  if (existing === content) {
50911
51112
  logger19.info("Skill already in sync", { name, bytes: content.length });
50912
51113
  return;
50913
51114
  }
50914
- import_node_fs5.default.writeFileSync(tmpPath, content, "utf-8");
50915
- import_node_fs5.default.renameSync(tmpPath, filePath);
51115
+ import_node_fs6.default.writeFileSync(tmpPath, content, "utf-8");
51116
+ import_node_fs6.default.renameSync(tmpPath, filePath);
50916
51117
  logger19.info("Skill seeded/re-synced", {
50917
51118
  name,
50918
51119
  bytes: content.length,
@@ -50923,8 +51124,8 @@ var SkillStore = class {
50923
51124
 
50924
51125
  // src/lockfile.ts
50925
51126
  init_cjs_shims();
50926
- var import_node_fs6 = __toESM(require("fs"), 1);
50927
- var import_node_path14 = __toESM(require("path"), 1);
51127
+ var import_node_fs7 = __toESM(require("fs"), 1);
51128
+ var import_node_path15 = __toESM(require("path"), 1);
50928
51129
  var logger20 = createModuleLogger("bridge.lockfile");
50929
51130
  var lockPath = null;
50930
51131
  function isProcessAlive(pid) {
@@ -50938,10 +51139,10 @@ function isProcessAlive(pid) {
50938
51139
  }
50939
51140
  }
50940
51141
  function acquireLock(dataDir) {
50941
- const file2 = import_node_path14.default.join(dataDir, "bridge.lock");
51142
+ const file2 = import_node_path15.default.join(dataDir, "bridge.lock");
50942
51143
  lockPath = file2;
50943
- if (import_node_fs6.default.existsSync(file2)) {
50944
- const raw = import_node_fs6.default.readFileSync(file2, "utf-8").trim();
51144
+ if (import_node_fs7.default.existsSync(file2)) {
51145
+ const raw = import_node_fs7.default.readFileSync(file2, "utf-8").trim();
50945
51146
  const pid = Number.parseInt(raw, 10);
50946
51147
  if (Number.isFinite(pid) && pid > 0) {
50947
51148
  if (isProcessAlive(pid)) {
@@ -50950,15 +51151,15 @@ function acquireLock(dataDir) {
50950
51151
  logger20.warn("Removing stale bridge.lock (process not found)", { pid, path: file2 });
50951
51152
  }
50952
51153
  }
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");
51154
+ import_node_fs7.default.mkdirSync(import_node_path15.default.dirname(file2), { recursive: true });
51155
+ import_node_fs7.default.writeFileSync(file2, String(process.pid), "utf-8");
50955
51156
  logger20.info("Acquired bridge lock", { path: file2, pid: process.pid });
50956
51157
  const release = () => {
50957
51158
  try {
50958
- if (lockPath && import_node_fs6.default.existsSync(lockPath)) {
50959
- const current = import_node_fs6.default.readFileSync(lockPath, "utf-8").trim();
51159
+ if (lockPath && import_node_fs7.default.existsSync(lockPath)) {
51160
+ const current = import_node_fs7.default.readFileSync(lockPath, "utf-8").trim();
50960
51161
  if (current === String(process.pid)) {
50961
- import_node_fs6.default.unlinkSync(lockPath);
51162
+ import_node_fs7.default.unlinkSync(lockPath);
50962
51163
  logger20.info("Released bridge lock", { path: lockPath });
50963
51164
  }
50964
51165
  }
@@ -51319,14 +51520,14 @@ async function handleGroupArchivedPush(deps, payload) {
51319
51520
 
51320
51521
  // src/sessionStore.ts
51321
51522
  init_cjs_shims();
51322
- var import_node_fs7 = __toESM(require("fs"), 1);
51323
- var import_node_path15 = __toESM(require("path"), 1);
51523
+ var import_node_fs8 = __toESM(require("fs"), 1);
51524
+ var import_node_path16 = __toESM(require("path"), 1);
51324
51525
  var logger23 = createModuleLogger("session.store");
51325
51526
  var SessionStore = class {
51326
51527
  filePath;
51327
51528
  cache;
51328
51529
  constructor(dataDir) {
51329
- this.filePath = import_node_path15.default.join(dataDir, "sessions.json");
51530
+ this.filePath = import_node_path16.default.join(dataDir, "sessions.json");
51330
51531
  this.cache = this.loadFromDisk();
51331
51532
  }
51332
51533
  cacheKey(agentId, scope) {
@@ -51361,8 +51562,8 @@ var SessionStore = class {
51361
51562
  }
51362
51563
  loadFromDisk() {
51363
51564
  try {
51364
- if (!import_node_fs7.default.existsSync(this.filePath)) return {};
51365
- const raw = import_node_fs7.default.readFileSync(this.filePath, "utf-8");
51565
+ if (!import_node_fs8.default.existsSync(this.filePath)) return {};
51566
+ const raw = import_node_fs8.default.readFileSync(this.filePath, "utf-8");
51366
51567
  const parsed = JSON.parse(raw);
51367
51568
  if (typeof parsed !== "object" || parsed === null || Array.isArray(parsed)) return {};
51368
51569
  const map2 = parsed;
@@ -51386,9 +51587,9 @@ var SessionStore = class {
51386
51587
  }
51387
51588
  saveToDisk() {
51388
51589
  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");
51590
+ const dir = import_node_path16.default.dirname(this.filePath);
51591
+ import_node_fs8.default.mkdirSync(dir, { recursive: true });
51592
+ import_node_fs8.default.writeFileSync(this.filePath, JSON.stringify(this.cache, null, 2), "utf-8");
51392
51593
  } catch (e7) {
51393
51594
  logger23.error("Failed to save sessions file", { error: e7, path: this.filePath });
51394
51595
  }
@@ -51397,9 +51598,9 @@ var SessionStore = class {
51397
51598
 
51398
51599
  // src/ensureClaudeCli.ts
51399
51600
  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");
51601
+ var import_node_child_process3 = require("child_process");
51602
+ var import_node_fs9 = require("fs");
51603
+ var import_node_path17 = require("path");
51403
51604
  var logger24 = createModuleLogger("bridge.ensureCli");
51404
51605
  var DEFAULT_INSTALL_TIMEOUT_MS = 6e5;
51405
51606
  function getInstallTimeoutMs() {
@@ -51408,48 +51609,42 @@ function getInstallTimeoutMs() {
51408
51609
  const parsed = Number.parseInt(raw, 10);
51409
51610
  return Number.isFinite(parsed) && parsed > 0 ? parsed : DEFAULT_INSTALL_TIMEOUT_MS;
51410
51611
  }
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
51612
  function getNpmGlobalBin() {
51613
+ const npm = resolveCommand(["npm"]);
51614
+ if (!npm) return void 0;
51419
51615
  try {
51420
- const bin = (0, import_node_child_process2.execSync)("npm bin -g", { timeout: 5e3 }).toString().trim() || void 0;
51616
+ const bin = (0, import_node_child_process3.execFileSync)(npm.path, ["bin", "-g"], {
51617
+ env: withAugmentedPathEnv(),
51618
+ timeout: 5e3
51619
+ }).toString().trim() || void 0;
51421
51620
  if (bin) return bin;
51422
51621
  } catch {
51423
51622
  }
51424
51623
  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");
51624
+ const prefix = (0, import_node_child_process3.execFileSync)(npm.path, ["prefix", "-g"], {
51625
+ env: withAugmentedPathEnv(),
51626
+ timeout: 5e3
51627
+ }).toString().trim();
51628
+ if (prefix) return (0, import_node_path17.join)(prefix, "bin");
51427
51629
  } catch {
51428
51630
  }
51429
51631
  return void 0;
51430
51632
  }
51431
51633
  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();
51634
+ return resolveCommand(["claude", "anthropic-cli"])?.path ?? resolveViaNpmBin();
51440
51635
  }
51441
51636
  function getNpmClaudeCandidates(bin) {
51442
51637
  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));
51638
+ return ["claude.cmd", "claude.exe", "claude", "anthropic-cli.cmd", "anthropic-cli.exe", "anthropic-cli"].map((name) => (0, import_node_path17.join)(bin, name));
51444
51639
  }
51445
- return [(0, import_node_path16.join)(bin, "claude"), (0, import_node_path16.join)(bin, "anthropic-cli")];
51640
+ return [(0, import_node_path17.join)(bin, "claude"), (0, import_node_path17.join)(bin, "anthropic-cli")];
51446
51641
  }
51447
51642
  function resolveViaNpmBin() {
51448
51643
  const bin = getNpmGlobalBin();
51449
51644
  if (!bin) return void 0;
51450
51645
  for (const candidate of getNpmClaudeCandidates(bin)) {
51451
51646
  try {
51452
- (0, import_node_fs8.accessSync)(candidate, import_node_fs8.constants.X_OK);
51647
+ (0, import_node_fs9.accessSync)(candidate, import_node_fs9.constants.X_OK);
51453
51648
  return candidate;
51454
51649
  } catch {
51455
51650
  }
@@ -51459,40 +51654,32 @@ function resolveViaNpmBin() {
51459
51654
  function detectViaNpmBin() {
51460
51655
  const binPath = resolveViaNpmBin();
51461
51656
  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;
51657
+ return readCommandVersion(binPath);
51467
51658
  }
51468
51659
  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
- }
51660
+ const resolvedPath = resolveClaudeBinary();
51661
+ if (!resolvedPath) return void 0;
51662
+ const version2 = readCommandVersion(resolvedPath);
51663
+ if (version2) return { version: version2, path: resolvedPath };
51480
51664
  return void 0;
51481
51665
  }
51482
51666
  function detectVersionFromResolvedCandidates() {
51483
51667
  const candidates = [resolveClaudeBinary(), resolveViaNpmBin()].filter((p) => Boolean(p));
51484
51668
  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
- }
51669
+ const version2 = readCommandVersion(p);
51670
+ if (version2) return version2;
51490
51671
  }
51491
51672
  return void 0;
51492
51673
  }
51493
51674
  function installClaudeCli() {
51675
+ const npm = resolveCommand(["npm"]);
51676
+ if (!npm) {
51677
+ logger24.error("npm not found; cannot install Claude Code CLI");
51678
+ return void 0;
51679
+ }
51494
51680
  logger24.info("Installing Claude Code CLI via npm...");
51495
- const result = (0, import_node_child_process2.spawnSync)("npm", ["install", "-g", "@anthropic-ai/claude-code"], {
51681
+ const result = (0, import_node_child_process3.spawnSync)(npm.path, ["install", "-g", "@anthropic-ai/claude-code"], {
51682
+ env: withAugmentedPathEnv(),
51496
51683
  stdio: "inherit",
51497
51684
  timeout: getInstallTimeoutMs()
51498
51685
  });
@@ -51524,7 +51711,10 @@ function installClaudeCli() {
51524
51711
  logger24.error("claude not found after successful npm install -g", {
51525
51712
  npmGlobalBin: npmBin ?? "unknown",
51526
51713
  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"
51714
+ npmPrefix: (0, import_node_child_process3.execFileSync)(npm.path, ["prefix", "-g"], {
51715
+ env: withAugmentedPathEnv(),
51716
+ timeout: 5e3
51717
+ }).toString().trim() || "unknown"
51528
51718
  });
51529
51719
  return void 0;
51530
51720
  }
@@ -51554,21 +51744,21 @@ async function ensureClaudeCli() {
51554
51744
  // src/forkAgentFiles.ts
51555
51745
  init_cjs_shims();
51556
51746
  var fs11 = __toESM(require("fs/promises"), 1);
51557
- var path17 = __toESM(require("path"), 1);
51747
+ var path18 = __toESM(require("path"), 1);
51558
51748
 
51559
51749
  // src/sessionSlug.ts
51560
51750
  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");
51751
+ var import_node_os10 = __toESM(require("os"), 1);
51752
+ var import_node_path18 = __toESM(require("path"), 1);
51753
+ var CLAUDE_PROJECTS_DIR = import_node_path18.default.join(import_node_os10.default.homedir(), ".claude", "projects");
51564
51754
  function cwdToSlug(cwd) {
51565
51755
  return cwd.replace(/[^a-zA-Z0-9-]/g, "-");
51566
51756
  }
51567
51757
  function sessionDirForCwd(cwd) {
51568
- return import_node_path17.default.join(CLAUDE_PROJECTS_DIR, cwdToSlug(cwd));
51758
+ return import_node_path18.default.join(CLAUDE_PROJECTS_DIR, cwdToSlug(cwd));
51569
51759
  }
51570
51760
  function sessionFilePath(cwd, sessionId) {
51571
- return import_node_path17.default.join(sessionDirForCwd(cwd), `${sessionId}.jsonl`);
51761
+ return import_node_path18.default.join(sessionDirForCwd(cwd), `${sessionId}.jsonl`);
51572
51762
  }
51573
51763
 
51574
51764
  // src/forkAgentFiles.ts
@@ -51600,9 +51790,9 @@ async function forkAgentFiles(sourceAgentId, newAgentId, sourceWorkdir, newWorkd
51600
51790
  logger25.error("Workdir copy failed", { error: e7 });
51601
51791
  throw e7;
51602
51792
  }
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");
51793
+ const srcNotebook = path18.join(dataDir, "agent-memory", sourceAgentId, "notebook.md");
51794
+ const dstNotebookDir = path18.join(dataDir, "agent-memory", newAgentId);
51795
+ const dstNotebook = path18.join(dstNotebookDir, "notebook.md");
51606
51796
  try {
51607
51797
  const nbStat = await fs11.stat(srcNotebook).catch(() => null);
51608
51798
  if (nbStat?.isFile()) {
@@ -51629,7 +51819,7 @@ async function forkAgentFiles(sourceAgentId, newAgentId, sourceWorkdir, newWorkd
51629
51819
  if (srcStat?.isFile()) {
51630
51820
  const dstDir = sessionDirForCwd(newWorkdir);
51631
51821
  await fs11.mkdir(dstDir, { recursive: true });
51632
- const dstPath = path17.join(dstDir, `${sourceSessionId}.jsonl`);
51822
+ const dstPath = path18.join(dstDir, `${sourceSessionId}.jsonl`);
51633
51823
  await fs11.copyFile(srcPath, dstPath);
51634
51824
  sessionStore.set(newAgentId, { kind: "single" }, sourceSessionId);
51635
51825
  sessionCopied = true;
@@ -51677,12 +51867,12 @@ async function forkAgentFiles(sourceAgentId, newAgentId, sourceWorkdir, newWorkd
51677
51867
  // src/modelQuerier.ts
51678
51868
  init_cjs_shims();
51679
51869
  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);
51870
+ var import_node_os11 = __toESM(require("os"), 1);
51871
+ var import_node_path19 = __toESM(require("path"), 1);
51682
51872
  var logger26 = createModuleLogger("bridge.modelQuerier");
51683
51873
  async function listModels(queryFn, opts = {}) {
51684
51874
  const t0 = Date.now();
51685
- const cwd = opts.cwd ?? import_node_path18.default.join(import_node_os10.default.homedir(), ".ahchat", "workspaces", "_list_models");
51875
+ const cwd = opts.cwd ?? import_node_path19.default.join(import_node_os11.default.homedir(), ".ahchat", "workspaces", "_list_models");
51686
51876
  await import_promises11.default.mkdir(cwd, { recursive: true });
51687
51877
  const fn = queryFn ?? QA$;
51688
51878
  const ic2 = new InputController();
@@ -51745,8 +51935,8 @@ async function listModels(queryFn, opts = {}) {
51745
51935
  // src/promptOptimizer.ts
51746
51936
  init_cjs_shims();
51747
51937
  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);
51938
+ var import_node_os12 = __toESM(require("os"), 1);
51939
+ var import_node_path20 = __toESM(require("path"), 1);
51750
51940
  var logger27 = createModuleLogger("bridge.promptOptimizer");
51751
51941
  var OPTIMIZER_SYSTEM_PROMPT = `You are an expert prompt editor for AHChat Agent creation.
51752
51942
 
@@ -51789,7 +51979,7 @@ async function optimizePrompt(queryFn, opts) {
51789
51979
  const prompt = opts.systemPrompt.trim();
51790
51980
  if (!prompt) throw new Error("systemPrompt is required");
51791
51981
  const t0 = Date.now();
51792
- const cwd = opts.cwd ?? import_node_path19.default.join(import_node_os11.default.homedir(), ".ahchat", "workspaces", "_prompt_optimizer");
51982
+ const cwd = opts.cwd ?? import_node_path20.default.join(import_node_os12.default.homedir(), ".ahchat", "workspaces", "_prompt_optimizer");
51793
51983
  await import_promises12.default.mkdir(cwd, { recursive: true });
51794
51984
  const fn = queryFn ?? QA$;
51795
51985
  const ic2 = new InputController();
@@ -51877,7 +52067,7 @@ function isRunningAsRoot2() {
51877
52067
  }
51878
52068
  }
51879
52069
  async function syncClaudeCredentialsToNodeAccessibleDir(agentConfigDir) {
51880
- const rootClaudeDir = import_node_path20.default.join(process.env.HOME ?? "/root", ".claude");
52070
+ const rootClaudeDir = import_node_path21.default.join(process.env.HOME ?? "/root", ".claude");
51881
52071
  const fs16 = await import("fs/promises");
51882
52072
  try {
51883
52073
  await fs16.access(rootClaudeDir);
@@ -51887,8 +52077,8 @@ async function syncClaudeCredentialsToNodeAccessibleDir(agentConfigDir) {
51887
52077
  }
51888
52078
  const filesToSync = [".credentials.json", "settings.json", ".credentials.backup.json"];
51889
52079
  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);
52080
+ const src = import_node_path21.default.join(rootClaudeDir, file2);
52081
+ const dest = import_node_path21.default.join(agentConfigDir, file2);
51892
52082
  try {
51893
52083
  await fs16.copyFile(src, dest);
51894
52084
  logger28.info("Synced credential file", { file: file2, from: src, to: dest });
@@ -51911,7 +52101,7 @@ async function chownRecursive(dirPath, uid, gid) {
51911
52101
  return;
51912
52102
  }
51913
52103
  for (const entry of entries) {
51914
- const fullPath = import_node_path20.default.join(dirPath, entry.name);
52104
+ const fullPath = import_node_path21.default.join(dirPath, entry.name);
51915
52105
  if (entry.isDirectory()) {
51916
52106
  await chownRecursive(fullPath, uid, gid);
51917
52107
  } else {
@@ -51926,7 +52116,7 @@ async function chownRecursive(dirPath, uid, gid) {
51926
52116
  async function startBridge(config2) {
51927
52117
  ensureDir(config2.dataDir);
51928
52118
  ensureDir(config2.agentConfigDir);
51929
- const workspacesDir = import_node_path20.default.join(config2.dataDir, "workspaces");
52119
+ const workspacesDir = import_node_path21.default.join(config2.dataDir, "workspaces");
51930
52120
  ensureDir(workspacesDir);
51931
52121
  process.env.CLAUDE_CONFIG_DIR = config2.agentConfigDir;
51932
52122
  installBridgeFetchAuth(config2.serverApiUrl, config2.bridgeToken);
@@ -51955,7 +52145,7 @@ Bridge token (register this machine at Settings \u2192 \u5DF2\u8FDE\u63A5\u7684\
51955
52145
  `);
51956
52146
  wsMetrics.start(5e3);
51957
52147
  const sessionStore = new SessionStore(config2.dataDir);
51958
- const memoryRoot = import_node_path20.default.join(config2.dataDir, "agent-memory");
52148
+ const memoryRoot = import_node_path21.default.join(config2.dataDir, "agent-memory");
51959
52149
  const memoryStore = new AgentMemoryStore(memoryRoot);
51960
52150
  logger28.info("Agent memory store initialized", { rootDir: memoryRoot });
51961
52151
  const smithNotebook = memoryStore.read(SMITH_AGENT_ID);
@@ -52384,21 +52574,58 @@ function compactEnv(env2) {
52384
52574
 
52385
52575
  // src/protocol.ts
52386
52576
  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);
52577
+ var import_node_child_process4 = require("child_process");
52578
+ var import_node_fs10 = __toESM(require("fs"), 1);
52579
+ var import_node_os13 = __toESM(require("os"), 1);
52580
+ var import_node_path22 = __toESM(require("path"), 1);
52391
52581
  var logger29 = createModuleLogger("bridge.protocol");
52582
+ function shellSingleQuote(value) {
52583
+ return `'${value.replace(/'/g, `'\\''`)}'`;
52584
+ }
52585
+ function psSingleQuote(value) {
52586
+ return `'${value.replace(/'/g, "''")}'`;
52587
+ }
52588
+ function desktopExecQuote(value) {
52589
+ return `"${value.replace(/\\/g, "\\\\").replace(/"/g, '\\"')}"`;
52590
+ }
52591
+ function getStableCliPath() {
52592
+ return import_node_path22.default.join(import_node_os13.default.homedir(), ".ahchat", "bridge", "cli.cjs");
52593
+ }
52392
52594
  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);
52595
+ const bridgeDir = import_node_path22.default.join(import_node_os13.default.homedir(), ".ahchat", "bridge");
52596
+ import_node_fs10.default.mkdirSync(bridgeDir, { recursive: true });
52597
+ const stablePath = getStableCliPath();
52598
+ import_node_fs10.default.copyFileSync(__filename, stablePath);
52599
+ if (process.platform !== "win32") import_node_fs10.default.chmodSync(stablePath, 493);
52398
52600
  return stablePath;
52399
52601
  }
52602
+ function writePosixLauncher(bridgeDir, stableExePath) {
52603
+ const launchScriptPath = import_node_path22.default.join(bridgeDir, "launch-bridge.sh");
52604
+ const augmentedPath = buildAugmentedPath();
52605
+ import_node_fs10.default.writeFileSync(
52606
+ launchScriptPath,
52607
+ [
52608
+ "#!/bin/bash",
52609
+ "set -e",
52610
+ `export PATH=${shellSingleQuote(augmentedPath)}:"\${PATH:-}"`,
52611
+ `SAVED_NODE=${shellSingleQuote(process.execPath)}`,
52612
+ `BRIDGE_CLI=${shellSingleQuote(stableExePath)}`,
52613
+ 'NODE_BIN=""',
52614
+ 'if [ -x "$SAVED_NODE" ]; then NODE_BIN="$SAVED_NODE"; fi',
52615
+ 'if [ -z "$NODE_BIN" ]; then NODE_BIN="$(command -v node || true)"; fi',
52616
+ 'if [ -z "$NODE_BIN" ]; then',
52617
+ ` echo "Node.js ${MIN_NODE_MAJOR}+ is required to launch AHChat Bridge. Install Node, then rerun: npx -y @fangyb/ahchat-bridge install" >&2`,
52618
+ " exit 1",
52619
+ "fi",
52620
+ 'exec "$NODE_BIN" "$BRIDGE_CLI" launch --url "$1"',
52621
+ ""
52622
+ ].join("\n")
52623
+ );
52624
+ import_node_fs10.default.chmodSync(launchScriptPath, 493);
52625
+ return launchScriptPath;
52626
+ }
52400
52627
  function registerProtocolHandler() {
52401
- const platform = import_node_os12.default.platform();
52628
+ const platform = import_node_os13.default.platform();
52402
52629
  if (platform === "win32") {
52403
52630
  registerWindows();
52404
52631
  } else if (platform === "darwin") {
@@ -52411,22 +52638,42 @@ function registerProtocolHandler() {
52411
52638
  function registerWindows() {
52412
52639
  const nodeExe = process.execPath;
52413
52640
  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(
52641
+ const bridgeDir = import_node_path22.default.join(import_node_os13.default.homedir(), ".ahchat", "bridge");
52642
+ const augmentedPath = buildAugmentedPath();
52643
+ const psLauncherPath = import_node_path22.default.join(bridgeDir, "launch-bridge.ps1");
52644
+ import_node_fs10.default.writeFileSync(
52417
52645
  psLauncherPath,
52418
52646
  [
52419
52647
  `param([string]$url)`,
52420
- `& '${nodeExe.replace(/'/g, "''")}' '${stableExePath.replace(/'/g, "''")}' launch --url $url`
52648
+ `$env:PATH = ${psSingleQuote(augmentedPath)} + [System.IO.Path]::PathSeparator + $env:PATH`,
52649
+ `$candidates = @(`,
52650
+ ` ${psSingleQuote(nodeExe)},`,
52651
+ ` "$env:NVM_SYMLINK\\node.exe",`,
52652
+ ` "$env:ProgramFiles\\nodejs\\node.exe",`,
52653
+ ` "$env:LOCALAPPDATA\\Programs\\nodejs\\node.exe"`,
52654
+ `)`,
52655
+ `$node = $null`,
52656
+ `foreach ($candidate in $candidates) {`,
52657
+ ` if ($candidate -and (Test-Path $candidate)) { $node = $candidate; break }`,
52658
+ `}`,
52659
+ `if (-not $node) {`,
52660
+ ` $cmd = Get-Command node -ErrorAction SilentlyContinue`,
52661
+ ` if ($cmd) { $node = $cmd.Source }`,
52662
+ `}`,
52663
+ `if (-not $node) {`,
52664
+ ` Write-Error 'Node.js ${MIN_NODE_MAJOR}+ is required to launch AHChat Bridge. Install Node, then rerun: npx -y @fangyb/ahchat-bridge install'`,
52665
+ ` exit 1`,
52666
+ `}`,
52667
+ `& $node ${psSingleQuote(stableExePath)} launch --url $url`
52421
52668
  ].join("\r\n")
52422
52669
  );
52423
52670
  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(
52671
+ const psRegisterPath = import_node_path22.default.join(bridgeDir, "register-protocol.ps1");
52672
+ import_node_fs10.default.writeFileSync(
52426
52673
  psRegisterPath,
52427
52674
  [
52428
- `$handler = '${handlerValue.replace(/'/g, "''")}'`,
52429
- `$icon = '${nodeExe.replace(/'/g, "''")}'`,
52675
+ `$handler = ${psSingleQuote(handlerValue)}`,
52676
+ `$icon = ${psSingleQuote(nodeExe)}`,
52430
52677
  `New-Item -Path 'HKCU:\\Software\\Classes\\ahchat' -Force | Out-Null`,
52431
52678
  `Set-ItemProperty -Path 'HKCU:\\Software\\Classes\\ahchat' -Name '(Default)' -Value 'URL:ahchat' -Force`,
52432
52679
  `New-ItemProperty -Path 'HKCU:\\Software\\Classes\\ahchat' -Name 'URL Protocol' -Value '' -PropertyType String -Force | Out-Null`,
@@ -52437,7 +52684,7 @@ function registerWindows() {
52437
52684
  ].join("\r\n")
52438
52685
  );
52439
52686
  try {
52440
- (0, import_node_child_process3.execSync)(`powershell -ExecutionPolicy Bypass -File "${psRegisterPath}"`, { stdio: "pipe" });
52687
+ (0, import_node_child_process4.execSync)(`powershell -ExecutionPolicy Bypass -File "${psRegisterPath}"`, { stdio: "pipe" });
52441
52688
  } catch (e7) {
52442
52689
  logger29.error("Failed to register Windows protocol handler", { error: e7 });
52443
52690
  throw new Error("Failed to register Windows protocol handler");
@@ -52445,18 +52692,10 @@ function registerWindows() {
52445
52692
  logger29.info("Windows protocol handler registered", { psLauncherPath });
52446
52693
  }
52447
52694
  function registerMacOS() {
52448
- const appDir = import_node_path21.default.join(import_node_os12.default.homedir(), "Applications", "AHChatBridge.app");
52449
- const nodeExe = process.execPath;
52695
+ const appDir = import_node_path22.default.join(import_node_os13.default.homedir(), "Applications", "AHChatBridge.app");
52450
52696
  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);
52697
+ const bridgeDir = import_node_path22.default.join(import_node_os13.default.homedir(), ".ahchat", "bridge");
52698
+ const launchScriptPath = writePosixLauncher(bridgeDir, stableExePath);
52460
52699
  const escapedScriptPath = launchScriptPath.replace(/\\/g, "\\\\").replace(/"/g, '\\"');
52461
52700
  const appleScript = [
52462
52701
  `on open location thisURL`,
@@ -52464,29 +52703,29 @@ exec ${JSON.stringify(nodeExe)} ${JSON.stringify(stableExePath)} launch --url "$
52464
52703
  ` do shell script "/bin/bash " & (quoted form of launchScript) & " " & (quoted form of thisURL) & " >/tmp/ahchat-bridge.log 2>&1 &"`,
52465
52704
  `end open location`
52466
52705
  ].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);
52706
+ const tmpScript = import_node_path22.default.join(import_node_os13.default.tmpdir(), "AHChatBridge.applescript");
52707
+ import_node_fs10.default.writeFileSync(tmpScript, appleScript);
52469
52708
  try {
52470
- import_node_fs9.default.rmSync(appDir, { recursive: true, force: true });
52709
+ import_node_fs10.default.rmSync(appDir, { recursive: true, force: true });
52471
52710
  } catch {
52472
52711
  }
52473
52712
  try {
52474
- (0, import_node_child_process3.execSync)(`osacompile -o ${JSON.stringify(appDir)} ${JSON.stringify(tmpScript)}`, { stdio: "pipe" });
52713
+ (0, import_node_child_process4.execSync)(`osacompile -o ${JSON.stringify(appDir)} ${JSON.stringify(tmpScript)}`, { stdio: "pipe" });
52475
52714
  } finally {
52476
52715
  try {
52477
- import_node_fs9.default.unlinkSync(tmpScript);
52716
+ import_node_fs10.default.unlinkSync(tmpScript);
52478
52717
  } catch {
52479
52718
  }
52480
52719
  }
52481
- const plistPath = import_node_path21.default.join(appDir, "Contents", "Info.plist");
52720
+ const plistPath = import_node_path22.default.join(appDir, "Contents", "Info.plist");
52482
52721
  const urlTypes = JSON.stringify([{ CFBundleURLName: "AHChat Bridge", CFBundleURLSchemes: ["ahchat"] }]);
52483
- (0, import_node_child_process3.execSync)(
52722
+ (0, import_node_child_process4.execSync)(
52484
52723
  `/usr/bin/plutil -insert CFBundleURLTypes -json ${JSON.stringify(urlTypes)} ${JSON.stringify(plistPath)}`,
52485
52724
  { stdio: "pipe" }
52486
52725
  );
52487
52726
  const lsregister = "/System/Library/Frameworks/CoreServices.framework/Versions/A/Frameworks/LaunchServices.framework/Versions/A/Support/lsregister";
52488
52727
  try {
52489
- (0, import_node_child_process3.execSync)(`${lsregister} -f ${JSON.stringify(appDir)}`, { stdio: "pipe" });
52728
+ (0, import_node_child_process4.execSync)(`${lsregister} -f ${JSON.stringify(appDir)}`, { stdio: "pipe" });
52490
52729
  } catch (e7) {
52491
52730
  logger29.warn("lsregister failed; URL scheme registration may be delayed", { error: e7 });
52492
52731
  }
@@ -52494,36 +52733,38 @@ exec ${JSON.stringify(nodeExe)} ${JSON.stringify(stableExePath)} launch --url "$
52494
52733
  }
52495
52734
  function registerLinux() {
52496
52735
  const stableExePath = getStableExePath();
52736
+ const bridgeDir = import_node_path22.default.join(import_node_os13.default.homedir(), ".ahchat", "bridge");
52737
+ const launchScriptPath = writePosixLauncher(bridgeDir, stableExePath);
52497
52738
  const desktopFile = [
52498
52739
  `[Desktop Entry]`,
52499
52740
  `Name=AHChat Bridge`,
52500
- `Exec=${process.execPath} ${stableExePath} launch --url %u`,
52741
+ `Exec=${desktopExecQuote(launchScriptPath)} %u`,
52501
52742
  `Type=Application`,
52502
52743
  `NoDisplay=true`,
52503
52744
  `MimeType=x-scheme-handler/ahchat;`
52504
52745
  ].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);
52746
+ const desktopPath = import_node_path22.default.join(import_node_os13.default.homedir(), ".local", "share", "applications", "ahchat-bridge.desktop");
52747
+ import_node_fs10.default.mkdirSync(import_node_path22.default.dirname(desktopPath), { recursive: true });
52748
+ import_node_fs10.default.writeFileSync(desktopPath, desktopFile);
52508
52749
  try {
52509
- (0, import_node_child_process3.execSync)("update-desktop-database ~/.local/share/applications/", { stdio: "pipe" });
52750
+ (0, import_node_child_process4.execSync)("update-desktop-database ~/.local/share/applications/", { stdio: "pipe" });
52510
52751
  } catch (e7) {
52511
52752
  logger29.warn("update-desktop-database not available; run it manually if needed", { error: e7 });
52512
52753
  }
52513
52754
  logger29.info("Linux protocol handler registered", { desktopPath });
52514
52755
  }
52515
52756
  function unregisterProtocolHandler() {
52516
- const platform = import_node_os12.default.platform();
52757
+ const platform = import_node_os13.default.platform();
52517
52758
  if (platform === "win32") {
52518
52759
  try {
52519
- (0, import_node_child_process3.execSync)(
52760
+ (0, import_node_child_process4.execSync)(
52520
52761
  `powershell -ExecutionPolicy Bypass -Command "Remove-Item -Path 'HKCU:\\Software\\Classes\\ahchat' -Recurse -Force -ErrorAction SilentlyContinue"`,
52521
52762
  { stdio: "pipe" }
52522
52763
  );
52523
- const bridgeDir = import_node_path21.default.join(import_node_os12.default.homedir(), ".ahchat", "bridge");
52764
+ const bridgeDir = import_node_path22.default.join(import_node_os13.default.homedir(), ".ahchat", "bridge");
52524
52765
  for (const f7 of ["launch-bridge.ps1", "register-protocol.ps1"]) {
52525
52766
  try {
52526
- import_node_fs9.default.unlinkSync(import_node_path21.default.join(bridgeDir, f7));
52767
+ import_node_fs10.default.unlinkSync(import_node_path22.default.join(bridgeDir, f7));
52527
52768
  } catch {
52528
52769
  }
52529
52770
  }
@@ -52532,17 +52773,17 @@ function unregisterProtocolHandler() {
52532
52773
  logger29.warn("Failed to unregister Windows protocol handler", { error: e7 });
52533
52774
  }
52534
52775
  } else if (platform === "darwin") {
52535
- const appDir = import_node_path21.default.join(import_node_os12.default.homedir(), "Applications", "AHChatBridge.app");
52776
+ const appDir = import_node_path22.default.join(import_node_os13.default.homedir(), "Applications", "AHChatBridge.app");
52536
52777
  try {
52537
- import_node_fs9.default.rmSync(appDir, { recursive: true, force: true });
52778
+ import_node_fs10.default.rmSync(appDir, { recursive: true, force: true });
52538
52779
  logger29.info("macOS protocol handler unregistered");
52539
52780
  } catch (e7) {
52540
52781
  logger29.warn("Failed to unregister macOS protocol handler", { error: e7 });
52541
52782
  }
52542
52783
  } else {
52543
- const desktopPath = import_node_path21.default.join(import_node_os12.default.homedir(), ".local", "share", "applications", "ahchat-bridge.desktop");
52784
+ const desktopPath = import_node_path22.default.join(import_node_os13.default.homedir(), ".local", "share", "applications", "ahchat-bridge.desktop");
52544
52785
  try {
52545
- import_node_fs9.default.unlinkSync(desktopPath);
52786
+ import_node_fs10.default.unlinkSync(desktopPath);
52546
52787
  logger29.info("Linux protocol handler unregistered");
52547
52788
  } catch (e7) {
52548
52789
  logger29.warn("Failed to unregister Linux protocol handler", { error: e7 });
@@ -52550,20 +52791,21 @@ function unregisterProtocolHandler() {
52550
52791
  }
52551
52792
  }
52552
52793
  function isProtocolRegistered() {
52553
- const platform = import_node_os12.default.platform();
52794
+ const platform = import_node_os13.default.platform();
52795
+ const stableCliExists = import_node_fs10.default.existsSync(getStableCliPath());
52554
52796
  if (platform === "win32") {
52555
52797
  try {
52556
- (0, import_node_child_process3.execSync)('REG QUERY "HKCU\\Software\\Classes\\ahchat" /ve', { stdio: "pipe" });
52557
- return true;
52798
+ (0, import_node_child_process4.execSync)('REG QUERY "HKCU\\Software\\Classes\\ahchat" /ve', { stdio: "pipe" });
52799
+ return stableCliExists;
52558
52800
  } catch {
52559
52801
  return false;
52560
52802
  }
52561
52803
  } 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"));
52804
+ const appDir = import_node_path22.default.join(import_node_os13.default.homedir(), "Applications", "AHChatBridge.app");
52805
+ return stableCliExists && import_node_fs10.default.existsSync(import_node_path22.default.join(appDir, "Contents", "Info.plist"));
52564
52806
  } 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);
52807
+ const desktopPath = import_node_path22.default.join(import_node_os13.default.homedir(), ".local", "share", "applications", "ahchat-bridge.desktop");
52808
+ return stableCliExists && import_node_fs10.default.existsSync(desktopPath);
52567
52809
  }
52568
52810
  }
52569
52811
 
@@ -52571,14 +52813,14 @@ function isProtocolRegistered() {
52571
52813
  var logger30 = createModuleLogger("bridge");
52572
52814
  function readCliVersion() {
52573
52815
  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")
52816
+ import_node_path23.default.resolve(__dirname, "../package.json"),
52817
+ import_node_path23.default.resolve(__dirname, "../../package.json"),
52818
+ import_node_path23.default.resolve(process.cwd(), "packages/bridge/package.json")
52577
52819
  ];
52578
52820
  for (const candidate of candidates) {
52579
- if (!import_node_fs10.default.existsSync(candidate)) continue;
52821
+ if (!import_node_fs11.default.existsSync(candidate)) continue;
52580
52822
  try {
52581
- const parsed = JSON.parse(import_node_fs10.default.readFileSync(candidate, "utf8"));
52823
+ const parsed = JSON.parse(import_node_fs11.default.readFileSync(candidate, "utf8"));
52582
52824
  if (parsed && typeof parsed === "object" && "version" in parsed && typeof parsed.version === "string" && parsed.version.length > 0) {
52583
52825
  return parsed.version;
52584
52826
  }
@@ -52586,26 +52828,7 @@ function readCliVersion() {
52586
52828
  logger30.warn("Unable to read CLI package version candidate", { error: e7, candidate });
52587
52829
  }
52588
52830
  }
52589
- return "0.1.21";
52590
- }
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;
52831
+ return "0.1.23";
52609
52832
  }
52610
52833
  function parseAhchatUrl(url2) {
52611
52834
  try {
@@ -52620,29 +52843,107 @@ function parseAhchatUrl(url2) {
52620
52843
  const token = decodeURIComponent(rest.slice(lastTilde + 1));
52621
52844
  if (!serverUrl || !token) return null;
52622
52845
  return { serverUrl, token };
52623
- } catch {
52846
+ } catch (e7) {
52847
+ logger30.warn("Unable to parse ahchat launch URL", { error: e7 });
52624
52848
  return null;
52625
52849
  }
52626
52850
  }
52851
+ function assertSupportedNode() {
52852
+ const status = getNodeRuntimeStatus();
52853
+ if (status.supported) return;
52854
+ throw new Error(
52855
+ `Node.js ${MIN_NODE_MAJOR}+ is required to run AHChat Bridge. Current version: ${status.version}`
52856
+ );
52857
+ }
52627
52858
  async function run(args) {
52628
- const dataDir = args.dataDir ? resolveDataDir(args.dataDir) : void 0;
52859
+ assertSupportedNode();
52860
+ process.env.PATH = buildAugmentedPath();
52861
+ const dataDir = resolveBridgeDataDir(args);
52629
52862
  let config2 = loadBridgeConfig(dataDir ? { dataDir } : void 0);
52630
52863
  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 };
52864
+ const urls = normalizeBridgeServerUrls(args.serverUrl);
52865
+ config2 = { ...config2, serverUrl: urls.serverUrl, serverApiUrl: urls.serverApiUrl };
52634
52866
  }
52635
52867
  if (args.token) config2 = { ...config2, bridgeToken: args.token };
52636
52868
  if (args.logLevel) config2 = { ...config2, logLevel: args.logLevel };
52637
52869
  await startBridge(config2);
52638
52870
  }
52871
+ function buildDoctorReport(args) {
52872
+ process.env.PATH = buildAugmentedPath();
52873
+ const node = getNodeRuntimeStatus();
52874
+ let server = null;
52875
+ if (args.serverUrl) {
52876
+ try {
52877
+ server = { ok: true, ...normalizeBridgeServerUrls(args.serverUrl) };
52878
+ } catch (e7) {
52879
+ server = {
52880
+ ok: false,
52881
+ error: e7 instanceof Error ? e7.message : String(e7)
52882
+ };
52883
+ }
52884
+ }
52885
+ const claude = resolveCommand(["claude", "anthropic-cli"]);
52886
+ return {
52887
+ node,
52888
+ npm: probeCommand("npm"),
52889
+ npx: probeCommand("npx"),
52890
+ claude: claude ? { ok: true, name: claude.name, path: claude.path } : { ok: false, name: "claude", message: "claude was not found on PATH" },
52891
+ dataDir: resolveBridgeDataDir(args) ?? resolveUserPath("~/.ahchat"),
52892
+ server,
52893
+ tokenProvided: Boolean(args.token),
52894
+ pathPreview: (process.env.PATH ?? "").split(import_node_path23.default.delimiter).slice(0, 12)
52895
+ };
52896
+ }
52897
+ function writeDoctorText(report) {
52898
+ const node = report.node;
52899
+ const npm = report.npm;
52900
+ const npx = report.npx;
52901
+ const claude = report.claude;
52902
+ const server = report.server;
52903
+ const lines = [
52904
+ "AHChat Bridge doctor",
52905
+ `- Node: ${node.supported ? "ok" : "error"} (${node.version}, requires >=${MIN_NODE_MAJOR})`,
52906
+ `- npm: ${npm.ok ? `ok (${npm.version ?? npm.path})` : `missing (${npm.message ?? "not found"})`}`,
52907
+ `- npx: ${npx.ok ? `ok (${npx.version ?? npx.path})` : `missing (${npx.message ?? "not found"})`}`,
52908
+ `- Claude CLI: ${claude.ok ? `ok (${claude.path})` : `missing (${claude.message ?? "not found"})`}`,
52909
+ `- Data dir: ${String(report.dataDir)}`,
52910
+ `- Token: ${report.tokenProvided ? "provided" : "not provided"}`
52911
+ ];
52912
+ if (server) {
52913
+ lines.push(
52914
+ server.ok ? `- Server: ok (${server.serverUrl}, api ${server.serverApiUrl})` : `- Server: error (${server.error ?? "invalid URL"})`
52915
+ );
52916
+ }
52917
+ if (!node.supported) {
52918
+ lines.push(`
52919
+ Install Node.js ${MIN_NODE_MAJOR}+ first, then rerun the bridge command.`);
52920
+ } else if (!npm.ok || !npx.ok) {
52921
+ lines.push("\nNode is installed, but npm/npx is not available on PATH. Reinstall Node.js or fix PATH.");
52922
+ } else if (!claude.ok) {
52923
+ lines.push("\nClaude Code CLI is missing. The bridge can try to install it on first run with npm.");
52924
+ }
52925
+ process.stdout.write(`${lines.join("\n")}
52926
+ `);
52927
+ }
52928
+ function doctor(args) {
52929
+ const report = buildDoctorReport(args);
52930
+ if (args.json) {
52931
+ process.stdout.write(`${JSON.stringify(report, null, 2)}
52932
+ `);
52933
+ return;
52934
+ }
52935
+ writeDoctorText(report);
52936
+ }
52639
52937
  var cli = dist_default("ahchat-bridge");
52640
- 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) => {
52938
+ 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("--user <slug>", "User data-dir slug; derives ~/.ahchat/users/<slug>").option("--data-dir <dir>", "Data directory (default: ~/.ahchat)").option("--log-level <level>", "Log level (default: INFO)").action((args) => {
52641
52939
  void run(args).catch((e7) => {
52642
52940
  logger30.error("Bridge failed to start", { error: e7 });
52643
52941
  process.exit(1);
52644
52942
  });
52645
52943
  });
52944
+ 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("--user <slug>", "User data-dir slug; derives ~/.ahchat/users/<slug>").option("--data-dir <dir>", "Data directory (default: ~/.ahchat)").option("--json", "Print machine-readable JSON").action((args) => {
52945
+ doctor(args);
52946
+ });
52646
52947
  cli.command("launch", "Launch bridge from ahchat:// URL (called by OS)").option("--url <url>", "ahchat:// URL with server and token params").action((args) => {
52647
52948
  const parsed = parseAhchatUrl(args.url);
52648
52949
  if (!parsed) {