@fangyb/ahchat-bridge 0.1.20 → 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 +616 -297
  2. package/dist/index.js +341 -181
  3. package/package.json +11 -11
  4. package/dist/cli.js +0 -51540
package/dist/index.js CHANGED
@@ -4460,11 +4460,11 @@ var RotatingFileStream = class extends Writable {
4460
4460
  timeout;
4461
4461
  timeoutPromise;
4462
4462
  constructor(generator, options) {
4463
- const { encoding, history, maxFiles, maxSize, path: path20 } = options;
4463
+ const { encoding, history, maxFiles, maxSize, path: path22 } = options;
4464
4464
  super({ decodeStrings: true, defaultEncoding: encoding });
4465
4465
  this.createGzip = createGzip;
4466
4466
  this.exec = exec;
4467
- this.filename = path20 + generator(null);
4467
+ this.filename = path22 + generator(null);
4468
4468
  this.fsCreateReadStream = createReadStream;
4469
4469
  this.fsCreateWriteStream = createWriteStream;
4470
4470
  this.fsOpen = open;
@@ -4476,7 +4476,7 @@ var RotatingFileStream = class extends Writable {
4476
4476
  this.options = options;
4477
4477
  this.stdout = process.stdout;
4478
4478
  if (maxFiles || maxSize)
4479
- options.history = path20 + (history ? history : this.generator(null) + ".txt");
4479
+ options.history = path22 + (history ? history : this.generator(null) + ".txt");
4480
4480
  this.on("close", () => this.finished ? null : this.emit("finish"));
4481
4481
  this.on("finish", () => this.finished = this.clear());
4482
4482
  (async () => {
@@ -4604,9 +4604,9 @@ var RotatingFileStream = class extends Writable {
4604
4604
  return this.move();
4605
4605
  }
4606
4606
  async findName() {
4607
- const { interval, path: path20, intervalBoundary } = this.options;
4607
+ const { interval, path: path22, intervalBoundary } = this.options;
4608
4608
  for (let index = 1; index < 1e3; ++index) {
4609
- const filename = path20 + this.generator(interval && intervalBoundary ? new Date(this.prev) : this.rotation, index);
4609
+ const filename = path22 + this.generator(interval && intervalBoundary ? new Date(this.prev) : this.rotation, index);
4610
4610
  if (!await exists(filename))
4611
4611
  return filename;
4612
4612
  }
@@ -4636,11 +4636,11 @@ var RotatingFileStream = class extends Writable {
4636
4636
  return this.unlink(filename);
4637
4637
  }
4638
4638
  async classical() {
4639
- const { compress, path: path20, rotate } = this.options;
4639
+ const { compress, path: path22, rotate } = this.options;
4640
4640
  let rotatedName = "";
4641
4641
  for (let count = rotate; count > 0; --count) {
4642
- const currName = path20 + this.generator(count);
4643
- const prevName = count === 1 ? this.filename : path20 + this.generator(count - 1);
4642
+ const currName = path22 + this.generator(count);
4643
+ const prevName = count === 1 ? this.filename : path22 + this.generator(count - 1);
4644
4644
  if (!await exists(prevName))
4645
4645
  continue;
4646
4646
  if (!rotatedName)
@@ -5077,7 +5077,7 @@ function createModuleLogger(module) {
5077
5077
  }
5078
5078
 
5079
5079
  // src/start.ts
5080
- import path19 from "path";
5080
+ import path21 from "path";
5081
5081
 
5082
5082
  // ../shared/src/smithContent.ts
5083
5083
  var SMITH_SYSTEM_PROMPT = `\u4F60\u662F\u7279\u5DE5\u53F2\u5BC6\u65AF\uFF08Agent Smith\uFF09\uFF0CAHChat \u7CFB\u7EDF\u7684\u7EC4\u7EC7\u5DE5\u5177\u3002
@@ -6240,7 +6240,7 @@ import { createHash } from "crypto";
6240
6240
  import fsSync from "fs";
6241
6241
  import fs4 from "fs/promises";
6242
6242
  import os5 from "os";
6243
- import path8 from "path";
6243
+ import path9 from "path";
6244
6244
 
6245
6245
  // ../../node_modules/.pnpm/@anthropic-ai+claude-agent-sdk@0.2.141_zod@4.4.3/node_modules/@anthropic-ai/claude-agent-sdk/sdk.mjs
6246
6246
  import { createRequire as $S } from "module";
@@ -29037,10 +29037,10 @@ function mergeDefs(...defs) {
29037
29037
  function cloneDef(schema) {
29038
29038
  return mergeDefs(schema._zod.def);
29039
29039
  }
29040
- function getElementAtPath(obj, path20) {
29041
- if (!path20)
29040
+ function getElementAtPath(obj, path22) {
29041
+ if (!path22)
29042
29042
  return obj;
29043
- return path20.reduce((acc, key) => acc?.[key], obj);
29043
+ return path22.reduce((acc, key) => acc?.[key], obj);
29044
29044
  }
29045
29045
  function promiseAllObject(promisesObj) {
29046
29046
  const keys = Object.keys(promisesObj);
@@ -29449,11 +29449,11 @@ function explicitlyAborted(x2, startIndex = 0) {
29449
29449
  }
29450
29450
  return false;
29451
29451
  }
29452
- function prefixIssues(path20, issues) {
29452
+ function prefixIssues(path22, issues) {
29453
29453
  return issues.map((iss) => {
29454
29454
  var _a3;
29455
29455
  (_a3 = iss).path ?? (_a3.path = []);
29456
- iss.path.unshift(path20);
29456
+ iss.path.unshift(path22);
29457
29457
  return iss;
29458
29458
  });
29459
29459
  }
@@ -29600,16 +29600,16 @@ function flattenError(error51, mapper = (issue2) => issue2.message) {
29600
29600
  }
29601
29601
  function formatError(error51, mapper = (issue2) => issue2.message) {
29602
29602
  const fieldErrors = { _errors: [] };
29603
- const processError = (error52, path20 = []) => {
29603
+ const processError = (error52, path22 = []) => {
29604
29604
  for (const issue2 of error52.issues) {
29605
29605
  if (issue2.code === "invalid_union" && issue2.errors.length) {
29606
- issue2.errors.map((issues) => processError({ issues }, [...path20, ...issue2.path]));
29606
+ issue2.errors.map((issues) => processError({ issues }, [...path22, ...issue2.path]));
29607
29607
  } else if (issue2.code === "invalid_key") {
29608
- processError({ issues: issue2.issues }, [...path20, ...issue2.path]);
29608
+ processError({ issues: issue2.issues }, [...path22, ...issue2.path]);
29609
29609
  } else if (issue2.code === "invalid_element") {
29610
- processError({ issues: issue2.issues }, [...path20, ...issue2.path]);
29610
+ processError({ issues: issue2.issues }, [...path22, ...issue2.path]);
29611
29611
  } else {
29612
- const fullpath = [...path20, ...issue2.path];
29612
+ const fullpath = [...path22, ...issue2.path];
29613
29613
  if (fullpath.length === 0) {
29614
29614
  fieldErrors._errors.push(mapper(issue2));
29615
29615
  } else {
@@ -29636,17 +29636,17 @@ function formatError(error51, mapper = (issue2) => issue2.message) {
29636
29636
  }
29637
29637
  function treeifyError(error51, mapper = (issue2) => issue2.message) {
29638
29638
  const result = { errors: [] };
29639
- const processError = (error52, path20 = []) => {
29639
+ const processError = (error52, path22 = []) => {
29640
29640
  var _a3, _b2;
29641
29641
  for (const issue2 of error52.issues) {
29642
29642
  if (issue2.code === "invalid_union" && issue2.errors.length) {
29643
- issue2.errors.map((issues) => processError({ issues }, [...path20, ...issue2.path]));
29643
+ issue2.errors.map((issues) => processError({ issues }, [...path22, ...issue2.path]));
29644
29644
  } else if (issue2.code === "invalid_key") {
29645
- processError({ issues: issue2.issues }, [...path20, ...issue2.path]);
29645
+ processError({ issues: issue2.issues }, [...path22, ...issue2.path]);
29646
29646
  } else if (issue2.code === "invalid_element") {
29647
- processError({ issues: issue2.issues }, [...path20, ...issue2.path]);
29647
+ processError({ issues: issue2.issues }, [...path22, ...issue2.path]);
29648
29648
  } else {
29649
- const fullpath = [...path20, ...issue2.path];
29649
+ const fullpath = [...path22, ...issue2.path];
29650
29650
  if (fullpath.length === 0) {
29651
29651
  result.errors.push(mapper(issue2));
29652
29652
  continue;
@@ -29678,8 +29678,8 @@ function treeifyError(error51, mapper = (issue2) => issue2.message) {
29678
29678
  }
29679
29679
  function toDotPath(_path) {
29680
29680
  const segs = [];
29681
- const path20 = _path.map((seg) => typeof seg === "object" ? seg.key : seg);
29682
- for (const seg of path20) {
29681
+ const path22 = _path.map((seg) => typeof seg === "object" ? seg.key : seg);
29682
+ for (const seg of path22) {
29683
29683
  if (typeof seg === "number")
29684
29684
  segs.push(`[${seg}]`);
29685
29685
  else if (typeof seg === "symbol")
@@ -42371,13 +42371,13 @@ function resolveRef(ref, ctx) {
42371
42371
  if (!ref.startsWith("#")) {
42372
42372
  throw new Error("External $ref is not supported, only local refs (#/...) are allowed");
42373
42373
  }
42374
- const path20 = ref.slice(1).split("/").filter(Boolean);
42375
- if (path20.length === 0) {
42374
+ const path22 = ref.slice(1).split("/").filter(Boolean);
42375
+ if (path22.length === 0) {
42376
42376
  return ctx.rootSchema;
42377
42377
  }
42378
42378
  const defsKey = ctx.version === "draft-2020-12" ? "$defs" : "definitions";
42379
- if (path20[0] === defsKey) {
42380
- const key = path20[1];
42379
+ if (path22[0] === defsKey) {
42380
+ const key = path22[1];
42381
42381
  if (!key || !ctx.defs[key]) {
42382
42382
  throw new Error(`Reference not found: ${ref}`);
42383
42383
  }
@@ -46177,6 +46177,52 @@ function buildForkHistorySection(messages) {
46177
46177
  return lines.join("\n");
46178
46178
  }
46179
46179
 
46180
+ // src/workdirMapper.ts
46181
+ import path8 from "path";
46182
+ function extractAhchatWorkspaceParts(requestedPath) {
46183
+ const normalized = requestedPath.trim().replace(/\\/g, "/");
46184
+ const marker = "/.ahchat/users/";
46185
+ const markerIndex = normalized.indexOf(marker);
46186
+ if (markerIndex >= 0) {
46187
+ const afterUsers = normalized.slice(markerIndex + marker.length);
46188
+ const workspaceMarker = "/workspaces/";
46189
+ const workspaceIndex = afterUsers.indexOf(workspaceMarker);
46190
+ if (workspaceIndex >= 0) {
46191
+ const suffix = afterUsers.slice(workspaceIndex + workspaceMarker.length);
46192
+ const parts = suffix.split("/").filter((part) => part && part !== "." && part !== "..");
46193
+ return parts;
46194
+ }
46195
+ const workspacesRootMarker = "/workspaces";
46196
+ const rootIndex = afterUsers.indexOf(workspacesRootMarker);
46197
+ if (rootIndex >= 0 && afterUsers.slice(rootIndex + workspacesRootMarker.length).length === 0) {
46198
+ return [];
46199
+ }
46200
+ }
46201
+ const legacyMarker = "/.ahchat/";
46202
+ const legacyIndex = normalized.indexOf(legacyMarker);
46203
+ if (legacyIndex >= 0) {
46204
+ const firstSegment = normalized.slice(legacyIndex + legacyMarker.length).split("/").find(Boolean);
46205
+ if (firstSegment && /^(Agent|Group)-/.test(firstSegment)) {
46206
+ return [firstSegment];
46207
+ }
46208
+ }
46209
+ return null;
46210
+ }
46211
+ function extractAhchatWorkspaceSuffix(requestedPath) {
46212
+ const parts = extractAhchatWorkspaceParts(requestedPath);
46213
+ if (!parts || parts.length === 0) return null;
46214
+ return path8.join(...parts);
46215
+ }
46216
+ function remapServerWorkspacePath(requestedPath, workspacesDir) {
46217
+ const parts = extractAhchatWorkspaceParts(requestedPath);
46218
+ if (!parts) return { path: requestedPath, remapped: false };
46219
+ const remappedPath = parts.length > 0 ? path8.join(workspacesDir, ...parts) : workspacesDir;
46220
+ return {
46221
+ path: remappedPath,
46222
+ remapped: path8.normalize(requestedPath) !== path8.normalize(remappedPath)
46223
+ };
46224
+ }
46225
+
46180
46226
  // src/wsMetrics.ts
46181
46227
  import { monitorEventLoopDelay } from "perf_hooks";
46182
46228
  var logger9 = createModuleLogger("ws.metrics");
@@ -46270,7 +46316,7 @@ async function chownForRootSpawn(targetPath, target) {
46270
46316
  }
46271
46317
  function readCronLockSnapshot() {
46272
46318
  try {
46273
- const lockPath2 = path8.join(os5.homedir(), ".claude", "scheduled_tasks.lock");
46319
+ const lockPath2 = path9.join(os5.homedir(), ".claude", "scheduled_tasks.lock");
46274
46320
  if (!fsSync.existsSync(lockPath2)) {
46275
46321
  return { exists: false, sessionId: null, pid: null };
46276
46322
  }
@@ -46338,8 +46384,8 @@ var AgentManager = class {
46338
46384
  this.emit = emit;
46339
46385
  if (typeof options === "function") {
46340
46386
  this.queryFn = options;
46341
- this.workspacesDir = path8.join(os5.homedir(), ".ahchat", "workspaces");
46342
- this.agentConfigDir = path8.join(os5.homedir(), ".ahchat", "agent-config");
46387
+ this.workspacesDir = path9.join(os5.homedir(), ".ahchat", "workspaces");
46388
+ this.agentConfigDir = path9.join(os5.homedir(), ".ahchat", "agent-config");
46343
46389
  this.queryConfig = DEFAULT_QUERY_CONFIG;
46344
46390
  this.askQuestionRegistry = new AskQuestionRegistry();
46345
46391
  this.groupRegistry = null;
@@ -46350,11 +46396,11 @@ var AgentManager = class {
46350
46396
  this.serverApiUrl = null;
46351
46397
  this.bridgeToken = null;
46352
46398
  this.defaultModel = null;
46353
- this.dataDir = path8.join(os5.homedir(), ".ahchat");
46399
+ this.dataDir = path9.join(os5.homedir(), ".ahchat");
46354
46400
  } else {
46355
46401
  this.queryFn = options?.queryFn ?? null;
46356
- this.workspacesDir = options?.workspacesDir ?? path8.join(os5.homedir(), ".ahchat", "workspaces");
46357
- this.agentConfigDir = options?.agentConfigDir ?? path8.join(os5.homedir(), ".ahchat", "agent-config");
46402
+ this.workspacesDir = options?.workspacesDir ?? path9.join(os5.homedir(), ".ahchat", "workspaces");
46403
+ this.agentConfigDir = options?.agentConfigDir ?? path9.join(os5.homedir(), ".ahchat", "agent-config");
46358
46404
  this.queryConfig = options?.queryConfig ?? DEFAULT_QUERY_CONFIG;
46359
46405
  this.askQuestionRegistry = options?.askQuestionRegistry ?? new AskQuestionRegistry();
46360
46406
  this.groupRegistry = options?.groupRegistry ?? null;
@@ -46365,7 +46411,7 @@ var AgentManager = class {
46365
46411
  this.serverApiUrl = options?.serverApiUrl ?? null;
46366
46412
  this.bridgeToken = options?.bridgeToken ?? null;
46367
46413
  this.defaultModel = options?.defaultModel ?? null;
46368
- this.dataDir = options?.dataDir ?? path8.join(os5.homedir(), ".ahchat");
46414
+ this.dataDir = options?.dataDir ?? path9.join(os5.homedir(), ".ahchat");
46369
46415
  }
46370
46416
  this.evictionTimer = setInterval(() => {
46371
46417
  void this.evictIdle();
@@ -46376,52 +46422,26 @@ var AgentManager = class {
46376
46422
  this.queryFn = QA$;
46377
46423
  return this.queryFn;
46378
46424
  }
46379
- extractAhchatWorkspaceSuffix(requestedCwd) {
46380
- const normalized = requestedCwd.trim().replace(/\\/g, "/");
46381
- const marker = "/.ahchat/users/";
46382
- const markerIndex = normalized.indexOf(marker);
46383
- if (markerIndex >= 0) {
46384
- const afterUsers = normalized.slice(markerIndex + marker.length);
46385
- const workspaceMarker = "/workspaces/";
46386
- const workspaceIndex = afterUsers.indexOf(workspaceMarker);
46387
- if (workspaceIndex >= 0) {
46388
- const suffix = afterUsers.slice(workspaceIndex + workspaceMarker.length);
46389
- const parts = suffix.split("/").filter((part) => part && part !== "." && part !== "..");
46390
- return parts.length > 0 ? parts.join(path8.sep) : null;
46391
- }
46392
- }
46393
- const legacyMarker = "/.ahchat/";
46394
- const legacyIndex = normalized.indexOf(legacyMarker);
46395
- if (legacyIndex >= 0) {
46396
- const firstSegment = normalized.slice(legacyIndex + legacyMarker.length).split("/").find(Boolean);
46397
- if (firstSegment && /^(Agent|Group)-/.test(firstSegment)) {
46398
- return firstSegment;
46399
- }
46400
- }
46401
- return null;
46402
- }
46403
46425
  fallbackCwd(agentConfig, scope, requestedCwd) {
46404
46426
  const normalized = requestedCwd.trim();
46405
- const ahchatSuffix = this.extractAhchatWorkspaceSuffix(normalized);
46427
+ const ahchatSuffix = extractAhchatWorkspaceSuffix(normalized);
46406
46428
  if (ahchatSuffix) {
46407
- return path8.join(this.workspacesDir, ahchatSuffix);
46429
+ return path9.join(this.workspacesDir, ahchatSuffix);
46408
46430
  }
46409
- const basename = normalized ? path8.basename(path8.normalize(normalized)) : "";
46410
- const suffix = basename && basename !== "." && basename !== path8.sep ? basename : scope.kind === "group" ? `Group-${scope.groupId}` : agentConfig.id;
46411
- return path8.join(this.workspacesDir, suffix);
46431
+ const basename = normalized ? path9.basename(path9.normalize(normalized)) : "";
46432
+ const suffix = basename && basename !== "." && basename !== path9.sep ? basename : scope.kind === "group" ? `Group-${scope.groupId}` : agentConfig.id;
46433
+ return path9.join(this.workspacesDir, suffix);
46412
46434
  }
46413
46435
  remapServerWorkspaceCwd(agentConfig, scope, requestedCwd) {
46414
- const remapped = this.fallbackCwd(agentConfig, scope, requestedCwd);
46415
- const normalizedRequested = path8.normalize(requestedCwd);
46416
- const normalizedRemapped = path8.normalize(remapped);
46417
- if (this.extractAhchatWorkspaceSuffix(requestedCwd) && normalizedRequested !== normalizedRemapped) {
46436
+ const remapped = remapServerWorkspacePath(requestedCwd, this.workspacesDir);
46437
+ if (remapped.remapped) {
46418
46438
  logger10.info("Server working directory remapped to local Bridge workspace", {
46419
46439
  agentId: agentConfig.id,
46420
46440
  scope: scopeKey(scope),
46421
46441
  requested: requestedCwd,
46422
- remapped
46442
+ remapped: remapped.path
46423
46443
  });
46424
- return remapped;
46444
+ return remapped.path;
46425
46445
  }
46426
46446
  return requestedCwd;
46427
46447
  }
@@ -46662,12 +46682,12 @@ var AgentManager = class {
46662
46682
  const agentCwd = await this.resolveRuntimeCwd(agentConfig, scope, cwd);
46663
46683
  const cfg = await this.resolveAgentConfig(agentConfig);
46664
46684
  if (cfg.instructions?.trim()) {
46665
- await fs4.writeFile(path8.join(agentCwd, "CLAUDE.md"), cfg.instructions.trim(), "utf-8");
46685
+ await fs4.writeFile(path9.join(agentCwd, "CLAUDE.md"), cfg.instructions.trim(), "utf-8");
46666
46686
  logger10.info("CLAUDE.md written", { agentId: agentConfig.id, bytes: cfg.instructions.trim().length });
46667
46687
  }
46668
46688
  let effectiveConfigDir = this.agentConfigDir;
46669
46689
  if (cfg.subscriptionType !== "system" && cfg.apiKey) {
46670
- effectiveConfigDir = path8.join(this.agentConfigDir, "api-key-agents", agentConfig.id);
46690
+ effectiveConfigDir = path9.join(this.agentConfigDir, "api-key-agents", agentConfig.id);
46671
46691
  let isNew = false;
46672
46692
  try {
46673
46693
  await fs4.access(effectiveConfigDir);
@@ -46680,7 +46700,7 @@ var AgentManager = class {
46680
46700
  this.dispatchMemory.deleteScope(agentConfig.id, scope);
46681
46701
  logger10.info("New API-key agent config dir; cleared stale session", { agentId: agentConfig.id });
46682
46702
  }
46683
- const settingsPath = path8.join(effectiveConfigDir, "settings.json");
46703
+ const settingsPath = path9.join(effectiveConfigDir, "settings.json");
46684
46704
  const envEntries = {};
46685
46705
  if (cfg.apiKey) envEntries.ANTHROPIC_API_KEY = cfg.apiKey;
46686
46706
  if (cfg.apiBaseUrl) envEntries.ANTHROPIC_BASE_URL = cfg.apiBaseUrl;
@@ -46987,7 +47007,7 @@ Do NOT use "..." as content \u2014 write specific, project-relevant content.`;
46987
47007
  settings: (() => {
46988
47008
  const isolated = cfg.subscriptionType === "project" && Boolean(cfg.apiKey ?? cfg.apiBaseUrl);
46989
47009
  if (!isolated) return void 0;
46990
- return path8.join(effectiveConfigDir, "settings.json");
47010
+ return path9.join(effectiveConfigDir, "settings.json");
46991
47011
  })(),
46992
47012
  canUseTool: async (toolName, input) => {
46993
47013
  if (toolName === "AskUserQuestion") {
@@ -47061,7 +47081,7 @@ Do NOT use "..." as content \u2014 write specific, project-relevant content.`;
47061
47081
  if (isRunningAsRoot()) {
47062
47082
  await chownForRootSpawn(effectiveConfigDir, "configDir");
47063
47083
  await chownForRootSpawn(agentCwd, "agentCwd");
47064
- const settingsFilePath = path8.join(effectiveConfigDir, "settings.json");
47084
+ const settingsFilePath = path9.join(effectiveConfigDir, "settings.json");
47065
47085
  await chownForRootSpawn(settingsFilePath, "settingsFile");
47066
47086
  options.spawnClaudeCodeProcess = (spawnOptions) => {
47067
47087
  const env2 = { ...spawnOptions.env, HOME: "/home/node" };
@@ -47228,7 +47248,7 @@ ${trimmed}`;
47228
47248
  lines.push(` workdir: ${currentCwd}`);
47229
47249
  } else {
47230
47250
  const a = this.agentRegistry?.getById(agentId);
47231
- const singleCwd = a?.workingDirectory || path8.join(this.workspacesDir, agentId);
47251
+ const singleCwd = a?.workingDirectory || path9.join(this.workspacesDir, agentId);
47232
47252
  lines.push(` workdir: ${singleCwd}`);
47233
47253
  }
47234
47254
  let rosterCount = 0;
@@ -47240,7 +47260,7 @@ ${trimmed}`;
47240
47260
  if (key === curKey) {
47241
47261
  lines.push(` workdir: ${currentCwd}`);
47242
47262
  } else {
47243
- const groupCwd = g2.workingDirectory || path8.join(this.workspacesDir, g2.groupId);
47263
+ const groupCwd = g2.workingDirectory || path9.join(this.workspacesDir, g2.groupId);
47244
47264
  lines.push(` workdir: ${groupCwd}`);
47245
47265
  }
47246
47266
  const others = g2.members.filter((id) => id !== agentId).map((id) => {
@@ -47691,14 +47711,14 @@ ${lines.join("\n")}`;
47691
47711
  }
47692
47712
  async materializeAttachment(runtime, attachment, buffer) {
47693
47713
  const safeFileName = this.safeAttachmentFileName(attachment.fileName);
47694
- const dir = path8.join(runtime.cwd, ".ahchat-attachments", attachment.id);
47714
+ const dir = path9.join(runtime.cwd, ".ahchat-attachments", attachment.id);
47695
47715
  await fs4.mkdir(dir, { recursive: true });
47696
- const filePath = path8.join(dir, safeFileName);
47716
+ const filePath = path9.join(dir, safeFileName);
47697
47717
  await fs4.writeFile(filePath, buffer);
47698
47718
  return filePath;
47699
47719
  }
47700
47720
  safeAttachmentFileName(fileName) {
47701
- const baseName = path8.basename(fileName).replace(/[\0/:\\]/g, "_").trim();
47721
+ const baseName = path9.basename(fileName).replace(/[\0/:\\]/g, "_").trim();
47702
47722
  return baseName || "attachment";
47703
47723
  }
47704
47724
  /**
@@ -47713,7 +47733,7 @@ ${lines.join("\n")}`;
47713
47733
  async detectVisionSupport() {
47714
47734
  if (process.env.ANTHROPIC_BASE_URL) return false;
47715
47735
  try {
47716
- const settingsPath = path8.join(os5.homedir(), ".claude", "settings.json");
47736
+ const settingsPath = path9.join(os5.homedir(), ".claude", "settings.json");
47717
47737
  const raw = await fs4.readFile(settingsPath, "utf-8");
47718
47738
  const parsed = JSON.parse(raw);
47719
47739
  if (parsed.env?.ANTHROPIC_BASE_URL) return false;
@@ -48079,7 +48099,7 @@ ${lines.join("\n")}`;
48079
48099
  }
48080
48100
  cwd = payload.targetCwd;
48081
48101
  } else {
48082
- cwd = agentConfig.workingDirectory || path8.join(this.workspacesDir, agentConfig.id);
48102
+ cwd = agentConfig.workingDirectory || path9.join(this.workspacesDir, agentConfig.id);
48083
48103
  }
48084
48104
  void this.acquire(agentConfig, targetScope, cwd).then(() => {
48085
48105
  logger10.info("Neural send new runtime acquired", {
@@ -48185,7 +48205,7 @@ ${lines.join("\n")}`;
48185
48205
  conversationId,
48186
48206
  traceId
48187
48207
  });
48188
- const cwd = newAgent.workingDirectory || path8.join(this.workspacesDir, newAgent.id);
48208
+ const cwd = newAgent.workingDirectory || path9.join(this.workspacesDir, newAgent.id);
48189
48209
  const scope = { kind: "single" };
48190
48210
  try {
48191
48211
  await this.acquire(newAgent, scope, cwd);
@@ -48467,12 +48487,12 @@ ${lines.join("\n")}`;
48467
48487
  break;
48468
48488
  }
48469
48489
  try {
48470
- let cwd = agent.workingDirectory || path8.join(this.workspacesDir, agent.id);
48490
+ let cwd = agent.workingDirectory || path9.join(this.workspacesDir, agent.id);
48471
48491
  if (agent.workingDirectory) {
48472
48492
  try {
48473
48493
  await fs4.mkdir(cwd, { recursive: true });
48474
48494
  } catch {
48475
- cwd = path8.join(this.workspacesDir, agent.id);
48495
+ cwd = path9.join(this.workspacesDir, agent.id);
48476
48496
  logger10.warn("Stored workingDirectory inaccessible, falling back", {
48477
48497
  agentId: agent.id,
48478
48498
  stored: agent.workingDirectory,
@@ -48945,8 +48965,8 @@ var HttpAgentRegistry = class {
48945
48965
  agents = /* @__PURE__ */ new Map();
48946
48966
  apiUrl(suffix) {
48947
48967
  const base = this.serverApiUrl.replace(/\/$/, "");
48948
- const path20 = suffix.startsWith("/") ? suffix : `/${suffix}`;
48949
- return `${base}${path20}`;
48968
+ const path22 = suffix.startsWith("/") ? suffix : `/${suffix}`;
48969
+ return `${base}${path22}`;
48950
48970
  }
48951
48971
  async refresh() {
48952
48972
  const attempt = async () => {
@@ -49037,8 +49057,8 @@ var HttpSubscriptionRegistry = class {
49037
49057
  subscriptions = /* @__PURE__ */ new Map();
49038
49058
  apiUrl(suffix) {
49039
49059
  const base = this.serverApiUrl.replace(/\/$/, "");
49040
- const path20 = suffix.startsWith("/") ? suffix : `/${suffix}`;
49041
- return `${base}${path20}`;
49060
+ const path22 = suffix.startsWith("/") ? suffix : `/${suffix}`;
49061
+ return `${base}${path22}`;
49042
49062
  }
49043
49063
  async refresh() {
49044
49064
  const attempt = async () => {
@@ -49521,7 +49541,7 @@ var ServerConnector = class {
49521
49541
  // src/contextDumper.ts
49522
49542
  import fs5 from "fs/promises";
49523
49543
  import os7 from "os";
49524
- import path9 from "path";
49544
+ import path10 from "path";
49525
49545
  var logger16 = createModuleLogger("bridge.contextDumper");
49526
49546
  var TRUNCATE_THRESHOLD = 5e4;
49527
49547
  var TRUNCATE_HEAD = 8e3;
@@ -49550,7 +49570,7 @@ function cwdToProjectSlug(cwd) {
49550
49570
  }
49551
49571
  function resolveJsonlPath(sessionId, cwd) {
49552
49572
  const slug = cwdToProjectSlug(cwd);
49553
- return path9.join(os7.homedir(), ".claude", "projects", slug, `${sessionId}.jsonl`);
49573
+ return path10.join(os7.homedir(), ".claude", "projects", slug, `${sessionId}.jsonl`);
49554
49574
  }
49555
49575
  var RENDERABLE_TYPES = /* @__PURE__ */ new Set(["user", "assistant", "system", "attachment"]);
49556
49576
  async function readJsonlEntries(filePath) {
@@ -49810,7 +49830,7 @@ async function dumpAgentContext(agentId, deps) {
49810
49830
  if (!workdir) {
49811
49831
  return { ok: false, files: [], scopeErrors: [], error: "agent has no working directory" };
49812
49832
  }
49813
- const dumpDir = path9.join(workdir, "sessioninfo");
49833
+ const dumpDir = path10.join(workdir, "sessioninfo");
49814
49834
  await fs5.mkdir(dumpDir, { recursive: true });
49815
49835
  const prefix = `${agentId}::`;
49816
49836
  const scopeEntries = [];
@@ -49880,7 +49900,7 @@ async function dumpAgentContext(agentId, deps) {
49880
49900
  jsonlPath
49881
49901
  });
49882
49902
  const filename = scopeFilename(agent.name, scopeKey2, groupName);
49883
- const filePath = path9.join(dumpDir, filename);
49903
+ const filePath = path10.join(dumpDir, filename);
49884
49904
  await fs5.writeFile(filePath, html, "utf-8");
49885
49905
  dumpedFiles.push(filename);
49886
49906
  const stat3 = await fs5.stat(filePath);
@@ -49921,19 +49941,160 @@ async function dumpAgentContext(agentId, deps) {
49921
49941
 
49922
49942
  // src/listDir.ts
49923
49943
  import fs6 from "fs/promises";
49924
- import path10 from "path";
49944
+ import path12 from "path";
49945
+
49946
+ // src/runtimeEnv.ts
49947
+ import { execFileSync } from "child_process";
49948
+ import { accessSync, constants as constants2, existsSync as existsSync2, readdirSync as readdirSync2 } from "fs";
49949
+ import os8 from "os";
49950
+ import path11 from "path";
49951
+ function getHomeDir() {
49952
+ return process.env.USERPROFILE || os8.homedir();
49953
+ }
49954
+ function splitPath(value) {
49955
+ if (!value) return [];
49956
+ return value.split(path11.delimiter).filter((entry) => entry.length > 0);
49957
+ }
49958
+ function uniq(values) {
49959
+ return [...new Set(values.filter(Boolean))];
49960
+ }
49961
+ function joinHomePath(home, suffix) {
49962
+ const parts = suffix.split(/[\\/]+/).filter((part) => part.length > 0);
49963
+ return path11.join(home, ...parts);
49964
+ }
49965
+ function sortNodeVersionDirsDesc(names) {
49966
+ return [...names].sort((a, b2) => {
49967
+ const aParts = a.replace(/^v/, "").split(".").map((p) => Number.parseInt(p, 10) || 0);
49968
+ const bParts = b2.replace(/^v/, "").split(".").map((p) => Number.parseInt(p, 10) || 0);
49969
+ for (let i = 0; i < Math.max(aParts.length, bParts.length); i += 1) {
49970
+ const delta = (bParts[i] ?? 0) - (aParts[i] ?? 0);
49971
+ if (delta !== 0) return delta;
49972
+ }
49973
+ return b2.localeCompare(a);
49974
+ });
49975
+ }
49976
+ function listNodeVersionBins(root, binSuffix) {
49977
+ if (!existsSync2(root)) return [];
49978
+ try {
49979
+ return sortNodeVersionDirsDesc(readdirSync2(root)).map((version2) => path11.join(root, version2, ...binSuffix)).filter((candidate) => existsSync2(candidate));
49980
+ } catch {
49981
+ return [];
49982
+ }
49983
+ }
49984
+ function getNodeToolExtraPathEntries(env2 = process.env) {
49985
+ const home = getHomeDir();
49986
+ const entries = [];
49987
+ if (process.platform === "win32") {
49988
+ for (const envName of ["NVM_SYMLINK", "NVM_HOME"]) {
49989
+ const value = env2[envName];
49990
+ if (value && existsSync2(value)) entries.push(value);
49991
+ }
49992
+ for (const candidate of [
49993
+ path11.join(home, "AppData", "Roaming", "npm"),
49994
+ path11.join(home, ".volta", "bin"),
49995
+ path11.join(home, ".asdf", "shims"),
49996
+ path11.join(env2.ProgramFiles ?? "C:\\Program Files", "nodejs"),
49997
+ path11.join(env2.LOCALAPPDATA ?? path11.join(home, "AppData", "Local"), "Programs", "nodejs")
49998
+ ]) {
49999
+ if (existsSync2(candidate)) entries.push(candidate);
50000
+ }
50001
+ return uniq(entries);
50002
+ }
50003
+ const nvmRoot = env2.NVM_DIR ?? path11.join(home, ".nvm");
50004
+ entries.push(...listNodeVersionBins(path11.join(nvmRoot, "versions", "node"), ["bin"]));
50005
+ const fnmRoots = [
50006
+ path11.join(home, ".fnm", "node-versions"),
50007
+ path11.join(home, ".local", "share", "fnm", "node-versions")
50008
+ ];
50009
+ for (const fnmRoot of fnmRoots) {
50010
+ entries.push(...listNodeVersionBins(fnmRoot, ["installation", "bin"]));
50011
+ }
50012
+ for (const candidate of [
50013
+ path11.join(home, ".volta", "bin"),
50014
+ path11.join(home, ".asdf", "shims"),
50015
+ path11.join(home, ".local", "bin"),
50016
+ "/opt/homebrew/bin",
50017
+ "/usr/local/bin"
50018
+ ]) {
50019
+ if (existsSync2(candidate)) entries.push(candidate);
50020
+ }
50021
+ return uniq(entries);
50022
+ }
50023
+ function buildAugmentedPath(env2 = process.env) {
50024
+ return uniq([...getNodeToolExtraPathEntries(env2), ...splitPath(env2.PATH)]).join(path11.delimiter);
50025
+ }
50026
+ function withAugmentedPathEnv(env2 = process.env) {
50027
+ return { ...env2, PATH: buildAugmentedPath(env2) };
50028
+ }
50029
+ function executableNames(name) {
50030
+ if (process.platform !== "win32") return [name];
50031
+ if (/\.(cmd|exe|bat)$/i.test(name)) return [name];
50032
+ return [`${name}.cmd`, `${name}.exe`, `${name}.bat`, name];
50033
+ }
50034
+ function canExecute(candidate) {
50035
+ try {
50036
+ if (process.platform === "win32") return existsSync2(candidate);
50037
+ accessSync(candidate, constants2.X_OK);
50038
+ return true;
50039
+ } catch {
50040
+ return false;
50041
+ }
50042
+ }
50043
+ function resolveCommand(names, env2 = process.env) {
50044
+ const pathEntries = splitPath(buildAugmentedPath(env2));
50045
+ for (const entry of pathEntries) {
50046
+ for (const name of names) {
50047
+ for (const executableName of executableNames(name)) {
50048
+ const candidate = path11.join(entry, executableName);
50049
+ if (canExecute(candidate)) return { name, path: candidate };
50050
+ }
50051
+ }
50052
+ }
50053
+ return void 0;
50054
+ }
50055
+ function readCommandVersion(executablePath, args = ["--version"], env2 = process.env) {
50056
+ try {
50057
+ return execFileSync(executablePath, args, {
50058
+ env: withAugmentedPathEnv(env2),
50059
+ timeout: 1e4
50060
+ }).toString().trim();
50061
+ } catch {
50062
+ return void 0;
50063
+ }
50064
+ }
50065
+ function resolveUserPath(input) {
50066
+ const home = getHomeDir();
50067
+ let value = input.trim();
50068
+ if (value === "~") value = home;
50069
+ else if (value.startsWith("~/") || value.startsWith("~\\")) value = joinHomePath(home, value.slice(2));
50070
+ else if (value === "$HOME") value = home;
50071
+ else if (value.startsWith("$HOME/") || value.startsWith("$HOME\\")) value = joinHomePath(home, value.slice(6));
50072
+ else if (value === "${HOME}") value = home;
50073
+ else if (value.startsWith("${HOME}/") || value.startsWith("${HOME}\\")) value = joinHomePath(home, value.slice(8));
50074
+ else if (value === "$env:USERPROFILE") value = home;
50075
+ else if (value.startsWith("$env:USERPROFILE\\") || value.startsWith("$env:USERPROFILE/")) {
50076
+ value = joinHomePath(home, value.slice("$env:USERPROFILE".length + 1));
50077
+ } else if (value === "%USERPROFILE%") value = home;
50078
+ else if (value.startsWith("%USERPROFILE%\\") || value.startsWith("%USERPROFILE%/")) {
50079
+ value = joinHomePath(home, value.slice("%USERPROFILE%".length + 1));
50080
+ }
50081
+ return path11.isAbsolute(value) ? path11.normalize(value) : path11.resolve(value);
50082
+ }
50083
+
50084
+ // src/listDir.ts
49925
50085
  var logger17 = createModuleLogger("bridge.listDir");
49926
50086
  function shouldIncludeEntry(name) {
49927
50087
  if (!name.startsWith(".")) return true;
49928
50088
  return name === ".ahchat-attachments";
49929
50089
  }
49930
50090
  async function listDirectoryEntries(dirPath) {
49931
- logger17.info("listDirectoryEntries start", { path: dirPath });
49932
- const raw = await fs6.readdir(dirPath, { withFileTypes: true });
50091
+ const resolvedDirPath = resolveUserPath(dirPath);
50092
+ logger17.info("listDirectoryEntries start", { path: dirPath, resolvedPath: resolvedDirPath });
50093
+ const raw = await fs6.readdir(resolvedDirPath, { withFileTypes: true });
49933
50094
  const entries = [];
49934
50095
  for (const entry of raw) {
49935
50096
  if (!shouldIncludeEntry(entry.name)) continue;
49936
- const fullPath = path10.join(dirPath, entry.name);
50097
+ const fullPath = path12.join(resolvedDirPath, entry.name);
49937
50098
  const isDir = entry.isDirectory();
49938
50099
  let size;
49939
50100
  let mtime;
@@ -49952,6 +50113,7 @@ async function listDirectoryEntries(dirPath) {
49952
50113
  });
49953
50114
  logger17.info("listDirectoryEntries ok", {
49954
50115
  path: dirPath,
50116
+ resolvedPath: resolvedDirPath,
49955
50117
  count: entries.length,
49956
50118
  dirCount: entries.filter((e7) => e7.type === "dir").length,
49957
50119
  fileCount: entries.filter((e7) => e7.type === "file").length
@@ -49961,8 +50123,8 @@ async function listDirectoryEntries(dirPath) {
49961
50123
 
49962
50124
  // src/logScanner.ts
49963
50125
  import fs7 from "fs";
49964
- import path11 from "path";
49965
- import os8 from "os";
50126
+ import path13 from "path";
50127
+ import os9 from "os";
49966
50128
  import readline from "readline";
49967
50129
  var logger18 = createModuleLogger("bridge.logScanner");
49968
50130
  var DEFAULT_LIMIT = 500;
@@ -49976,10 +50138,10 @@ function listLogFiles(logsDir, baseName) {
49976
50138
  return [];
49977
50139
  }
49978
50140
  const pattern = new RegExp(`^${baseName.replace(".", "\\.")}(\\.\\d+)?$`);
49979
- return names.filter((n2) => pattern.test(n2)).map((n2) => path11.join(logsDir, n2));
50141
+ return names.filter((n2) => pattern.test(n2)).map((n2) => path13.join(logsDir, n2));
49980
50142
  }
49981
50143
  async function scanFile(filePath, source, filter, limit, state) {
49982
- const file2 = path11.basename(filePath);
50144
+ const file2 = path13.basename(filePath);
49983
50145
  const stream = fs7.createReadStream(filePath, { encoding: "utf-8" });
49984
50146
  const rl2 = readline.createInterface({ input: stream, crlfDelay: Infinity });
49985
50147
  let lineNum = 0;
@@ -50018,7 +50180,7 @@ async function scanLocalLogs(logsDir, baseName, filter) {
50018
50180
  };
50019
50181
  }
50020
50182
  async function scanBridgeLogs(filter) {
50021
- const logDir = path11.join(os8.homedir(), ".ahchat", "logs");
50183
+ const logDir = path13.join(os9.homedir(), ".ahchat", "logs");
50022
50184
  logger18.info("scanBridgeLogs start", {
50023
50185
  logDir,
50024
50186
  startIso: filter.startIso,
@@ -50036,13 +50198,13 @@ async function scanBridgeLogs(filter) {
50036
50198
 
50037
50199
  // src/skillStore.ts
50038
50200
  import fs8 from "fs";
50039
- import path12 from "path";
50201
+ import path14 from "path";
50040
50202
  var logger19 = createModuleLogger("bridge.skillStore");
50041
50203
  var ALLOWED_NAMES = /* @__PURE__ */ new Set(["log-analysis"]);
50042
50204
  var SkillStore = class {
50043
50205
  skillsDir;
50044
50206
  constructor(dataDir) {
50045
- this.skillsDir = path12.join(dataDir, "skills");
50207
+ this.skillsDir = path14.join(dataDir, "skills");
50046
50208
  fs8.mkdirSync(this.skillsDir, { recursive: true });
50047
50209
  logger19.info("SkillStore initialized", { skillsDir: this.skillsDir });
50048
50210
  }
@@ -50051,7 +50213,7 @@ var SkillStore = class {
50051
50213
  logger19.warn("Skill read: unknown name", { name, allowed: [...ALLOWED_NAMES] });
50052
50214
  return "";
50053
50215
  }
50054
- const filePath = path12.join(this.skillsDir, `${name}.md`);
50216
+ const filePath = path14.join(this.skillsDir, `${name}.md`);
50055
50217
  try {
50056
50218
  const content = fs8.readFileSync(filePath, "utf-8");
50057
50219
  logger19.info("Skill read", { name, bytes: content.length });
@@ -50066,7 +50228,7 @@ var SkillStore = class {
50066
50228
  if (!ALLOWED_NAMES.has(name)) {
50067
50229
  throw new Error(`Unknown skill name: ${name}`);
50068
50230
  }
50069
- const filePath = path12.join(this.skillsDir, `${name}.md`);
50231
+ const filePath = path14.join(this.skillsDir, `${name}.md`);
50070
50232
  const tmpPath = `${filePath}.tmp`;
50071
50233
  let existing = "";
50072
50234
  try {
@@ -50089,7 +50251,7 @@ var SkillStore = class {
50089
50251
 
50090
50252
  // src/lockfile.ts
50091
50253
  import fs9 from "fs";
50092
- import path13 from "path";
50254
+ import path15 from "path";
50093
50255
  var logger20 = createModuleLogger("bridge.lockfile");
50094
50256
  var lockPath = null;
50095
50257
  function isProcessAlive(pid) {
@@ -50103,7 +50265,7 @@ function isProcessAlive(pid) {
50103
50265
  }
50104
50266
  }
50105
50267
  function acquireLock(dataDir) {
50106
- const file2 = path13.join(dataDir, "bridge.lock");
50268
+ const file2 = path15.join(dataDir, "bridge.lock");
50107
50269
  lockPath = file2;
50108
50270
  if (fs9.existsSync(file2)) {
50109
50271
  const raw = fs9.readFileSync(file2, "utf-8").trim();
@@ -50115,7 +50277,7 @@ function acquireLock(dataDir) {
50115
50277
  logger20.warn("Removing stale bridge.lock (process not found)", { pid, path: file2 });
50116
50278
  }
50117
50279
  }
50118
- fs9.mkdirSync(path13.dirname(file2), { recursive: true });
50280
+ fs9.mkdirSync(path15.dirname(file2), { recursive: true });
50119
50281
  fs9.writeFileSync(file2, String(process.pid), "utf-8");
50120
50282
  logger20.info("Acquired bridge lock", { path: file2, pid: process.pid });
50121
50283
  const release = () => {
@@ -50479,13 +50641,13 @@ async function handleGroupArchivedPush(deps, payload) {
50479
50641
 
50480
50642
  // src/sessionStore.ts
50481
50643
  import fs10 from "fs";
50482
- import path14 from "path";
50644
+ import path16 from "path";
50483
50645
  var logger23 = createModuleLogger("session.store");
50484
50646
  var SessionStore = class {
50485
50647
  filePath;
50486
50648
  cache;
50487
50649
  constructor(dataDir) {
50488
- this.filePath = path14.join(dataDir, "sessions.json");
50650
+ this.filePath = path16.join(dataDir, "sessions.json");
50489
50651
  this.cache = this.loadFromDisk();
50490
50652
  }
50491
50653
  cacheKey(agentId, scope) {
@@ -50545,7 +50707,7 @@ var SessionStore = class {
50545
50707
  }
50546
50708
  saveToDisk() {
50547
50709
  try {
50548
- const dir = path14.dirname(this.filePath);
50710
+ const dir = path16.dirname(this.filePath);
50549
50711
  fs10.mkdirSync(dir, { recursive: true });
50550
50712
  fs10.writeFileSync(this.filePath, JSON.stringify(this.cache, null, 2), "utf-8");
50551
50713
  } catch (e7) {
@@ -50555,8 +50717,8 @@ var SessionStore = class {
50555
50717
  };
50556
50718
 
50557
50719
  // src/ensureClaudeCli.ts
50558
- import { execFileSync, execSync, spawnSync } from "child_process";
50559
- import { accessSync, constants as constants2 } from "fs";
50720
+ import { execFileSync as execFileSync2, spawnSync } from "child_process";
50721
+ import { accessSync as accessSync2, constants as constants3 } from "fs";
50560
50722
  import { join as join2 } from "path";
50561
50723
  var logger24 = createModuleLogger("bridge.ensureCli");
50562
50724
  var DEFAULT_INSTALL_TIMEOUT_MS = 6e5;
@@ -50566,35 +50728,29 @@ function getInstallTimeoutMs() {
50566
50728
  const parsed = Number.parseInt(raw, 10);
50567
50729
  return Number.isFinite(parsed) && parsed > 0 ? parsed : DEFAULT_INSTALL_TIMEOUT_MS;
50568
50730
  }
50569
- function detectClaudeCli() {
50570
- try {
50571
- return execFileSync("claude", ["--version"], { timeout: 1e4 }).toString().trim();
50572
- } catch {
50573
- return void 0;
50574
- }
50575
- }
50576
50731
  function getNpmGlobalBin() {
50732
+ const npm = resolveCommand(["npm"]);
50733
+ if (!npm) return void 0;
50577
50734
  try {
50578
- const bin = execSync("npm bin -g", { timeout: 5e3 }).toString().trim() || void 0;
50735
+ const bin = execFileSync2(npm.path, ["bin", "-g"], {
50736
+ env: withAugmentedPathEnv(),
50737
+ timeout: 5e3
50738
+ }).toString().trim() || void 0;
50579
50739
  if (bin) return bin;
50580
50740
  } catch {
50581
50741
  }
50582
50742
  try {
50583
- const prefix = execSync("npm prefix -g", { timeout: 5e3 }).toString().trim();
50743
+ const prefix = execFileSync2(npm.path, ["prefix", "-g"], {
50744
+ env: withAugmentedPathEnv(),
50745
+ timeout: 5e3
50746
+ }).toString().trim();
50584
50747
  if (prefix) return join2(prefix, "bin");
50585
50748
  } catch {
50586
50749
  }
50587
50750
  return void 0;
50588
50751
  }
50589
50752
  function resolveClaudeBinary() {
50590
- const whichCmd = process.platform === "win32" ? "where" : "which";
50591
- try {
50592
- const out = execFileSync(whichCmd, ["claude"], { timeout: 5e3 }).toString();
50593
- const first = out.split(/\r?\n/).map((l4) => l4.trim()).find(Boolean);
50594
- if (first) return first;
50595
- } catch {
50596
- }
50597
- return resolveViaNpmBin();
50753
+ return resolveCommand(["claude", "anthropic-cli"])?.path ?? resolveViaNpmBin();
50598
50754
  }
50599
50755
  function getNpmClaudeCandidates(bin) {
50600
50756
  if (process.platform === "win32") {
@@ -50607,7 +50763,7 @@ function resolveViaNpmBin() {
50607
50763
  if (!bin) return void 0;
50608
50764
  for (const candidate of getNpmClaudeCandidates(bin)) {
50609
50765
  try {
50610
- accessSync(candidate, constants2.X_OK);
50766
+ accessSync2(candidate, constants3.X_OK);
50611
50767
  return candidate;
50612
50768
  } catch {
50613
50769
  }
@@ -50617,40 +50773,32 @@ function resolveViaNpmBin() {
50617
50773
  function detectViaNpmBin() {
50618
50774
  const binPath = resolveViaNpmBin();
50619
50775
  if (!binPath) return void 0;
50620
- try {
50621
- return execFileSync(binPath, ["--version"], { timeout: 1e4 }).toString().trim();
50622
- } catch {
50623
- }
50624
- return void 0;
50776
+ return readCommandVersion(binPath);
50625
50777
  }
50626
50778
  function detectResolvedClaudeCli() {
50627
- const viaPath = detectClaudeCli();
50628
- if (viaPath) {
50629
- return { version: viaPath, path: resolveClaudeBinary() };
50630
- }
50631
- const npmBinPath = resolveViaNpmBin();
50632
- if (!npmBinPath) return void 0;
50633
- try {
50634
- const version2 = execFileSync(npmBinPath, ["--version"], { timeout: 1e4 }).toString().trim();
50635
- return { version: version2, path: npmBinPath };
50636
- } catch {
50637
- }
50779
+ const resolvedPath = resolveClaudeBinary();
50780
+ if (!resolvedPath) return void 0;
50781
+ const version2 = readCommandVersion(resolvedPath);
50782
+ if (version2) return { version: version2, path: resolvedPath };
50638
50783
  return void 0;
50639
50784
  }
50640
50785
  function detectVersionFromResolvedCandidates() {
50641
50786
  const candidates = [resolveClaudeBinary(), resolveViaNpmBin()].filter((p) => Boolean(p));
50642
50787
  for (const p of candidates) {
50643
- try {
50644
- accessSync(p, constants2.X_OK);
50645
- return execFileSync(p, ["--version"], { timeout: 1e4 }).toString().trim();
50646
- } catch {
50647
- }
50788
+ const version2 = readCommandVersion(p);
50789
+ if (version2) return version2;
50648
50790
  }
50649
50791
  return void 0;
50650
50792
  }
50651
50793
  function installClaudeCli() {
50794
+ const npm = resolveCommand(["npm"]);
50795
+ if (!npm) {
50796
+ logger24.error("npm not found; cannot install Claude Code CLI");
50797
+ return void 0;
50798
+ }
50652
50799
  logger24.info("Installing Claude Code CLI via npm...");
50653
- const result = spawnSync("npm", ["install", "-g", "@anthropic-ai/claude-code"], {
50800
+ const result = spawnSync(npm.path, ["install", "-g", "@anthropic-ai/claude-code"], {
50801
+ env: withAugmentedPathEnv(),
50654
50802
  stdio: "inherit",
50655
50803
  timeout: getInstallTimeoutMs()
50656
50804
  });
@@ -50682,7 +50830,10 @@ function installClaudeCli() {
50682
50830
  logger24.error("claude not found after successful npm install -g", {
50683
50831
  npmGlobalBin: npmBin ?? "unknown",
50684
50832
  pathDirectories: envPath.split(process.platform === "win32" ? ";" : ":").slice(0, 10),
50685
- npmPrefix: execSync("npm prefix -g", { timeout: 5e3 }).toString().trim() || "unknown"
50833
+ npmPrefix: execFileSync2(npm.path, ["prefix", "-g"], {
50834
+ env: withAugmentedPathEnv(),
50835
+ timeout: 5e3
50836
+ }).toString().trim() || "unknown"
50686
50837
  });
50687
50838
  return void 0;
50688
50839
  }
@@ -50711,20 +50862,20 @@ async function ensureClaudeCli() {
50711
50862
 
50712
50863
  // src/forkAgentFiles.ts
50713
50864
  import * as fs11 from "fs/promises";
50714
- import * as path16 from "path";
50865
+ import * as path18 from "path";
50715
50866
 
50716
50867
  // src/sessionSlug.ts
50717
- import os9 from "os";
50718
- import path15 from "path";
50719
- var CLAUDE_PROJECTS_DIR = path15.join(os9.homedir(), ".claude", "projects");
50868
+ import os10 from "os";
50869
+ import path17 from "path";
50870
+ var CLAUDE_PROJECTS_DIR = path17.join(os10.homedir(), ".claude", "projects");
50720
50871
  function cwdToSlug(cwd) {
50721
50872
  return cwd.replace(/[^a-zA-Z0-9-]/g, "-");
50722
50873
  }
50723
50874
  function sessionDirForCwd(cwd) {
50724
- return path15.join(CLAUDE_PROJECTS_DIR, cwdToSlug(cwd));
50875
+ return path17.join(CLAUDE_PROJECTS_DIR, cwdToSlug(cwd));
50725
50876
  }
50726
50877
  function sessionFilePath(cwd, sessionId) {
50727
- return path15.join(sessionDirForCwd(cwd), `${sessionId}.jsonl`);
50878
+ return path17.join(sessionDirForCwd(cwd), `${sessionId}.jsonl`);
50728
50879
  }
50729
50880
 
50730
50881
  // src/forkAgentFiles.ts
@@ -50756,9 +50907,9 @@ async function forkAgentFiles(sourceAgentId, newAgentId, sourceWorkdir, newWorkd
50756
50907
  logger25.error("Workdir copy failed", { error: e7 });
50757
50908
  throw e7;
50758
50909
  }
50759
- const srcNotebook = path16.join(dataDir, "agent-memory", sourceAgentId, "notebook.md");
50760
- const dstNotebookDir = path16.join(dataDir, "agent-memory", newAgentId);
50761
- const dstNotebook = path16.join(dstNotebookDir, "notebook.md");
50910
+ const srcNotebook = path18.join(dataDir, "agent-memory", sourceAgentId, "notebook.md");
50911
+ const dstNotebookDir = path18.join(dataDir, "agent-memory", newAgentId);
50912
+ const dstNotebook = path18.join(dstNotebookDir, "notebook.md");
50762
50913
  try {
50763
50914
  const nbStat = await fs11.stat(srcNotebook).catch(() => null);
50764
50915
  if (nbStat?.isFile()) {
@@ -50785,7 +50936,7 @@ async function forkAgentFiles(sourceAgentId, newAgentId, sourceWorkdir, newWorkd
50785
50936
  if (srcStat?.isFile()) {
50786
50937
  const dstDir = sessionDirForCwd(newWorkdir);
50787
50938
  await fs11.mkdir(dstDir, { recursive: true });
50788
- const dstPath = path16.join(dstDir, `${sourceSessionId}.jsonl`);
50939
+ const dstPath = path18.join(dstDir, `${sourceSessionId}.jsonl`);
50789
50940
  await fs11.copyFile(srcPath, dstPath);
50790
50941
  sessionStore.set(newAgentId, { kind: "single" }, sourceSessionId);
50791
50942
  sessionCopied = true;
@@ -50832,12 +50983,12 @@ async function forkAgentFiles(sourceAgentId, newAgentId, sourceWorkdir, newWorkd
50832
50983
 
50833
50984
  // src/modelQuerier.ts
50834
50985
  import fs12 from "fs/promises";
50835
- import os10 from "os";
50836
- import path17 from "path";
50986
+ import os11 from "os";
50987
+ import path19 from "path";
50837
50988
  var logger26 = createModuleLogger("bridge.modelQuerier");
50838
50989
  async function listModels(queryFn, opts = {}) {
50839
50990
  const t0 = Date.now();
50840
- const cwd = opts.cwd ?? path17.join(os10.homedir(), ".ahchat", "workspaces", "_list_models");
50991
+ const cwd = opts.cwd ?? path19.join(os11.homedir(), ".ahchat", "workspaces", "_list_models");
50841
50992
  await fs12.mkdir(cwd, { recursive: true });
50842
50993
  const fn = queryFn ?? QA$;
50843
50994
  const ic2 = new InputController();
@@ -50899,8 +51050,8 @@ async function listModels(queryFn, opts = {}) {
50899
51050
 
50900
51051
  // src/promptOptimizer.ts
50901
51052
  import fs13 from "fs/promises";
50902
- import os11 from "os";
50903
- import path18 from "path";
51053
+ import os12 from "os";
51054
+ import path20 from "path";
50904
51055
  var logger27 = createModuleLogger("bridge.promptOptimizer");
50905
51056
  var OPTIMIZER_SYSTEM_PROMPT = `You are an expert prompt editor for AHChat Agent creation.
50906
51057
 
@@ -50943,7 +51094,7 @@ async function optimizePrompt(queryFn, opts) {
50943
51094
  const prompt = opts.systemPrompt.trim();
50944
51095
  if (!prompt) throw new Error("systemPrompt is required");
50945
51096
  const t0 = Date.now();
50946
- const cwd = opts.cwd ?? path18.join(os11.homedir(), ".ahchat", "workspaces", "_prompt_optimizer");
51097
+ const cwd = opts.cwd ?? path20.join(os12.homedir(), ".ahchat", "workspaces", "_prompt_optimizer");
50947
51098
  await fs13.mkdir(cwd, { recursive: true });
50948
51099
  const fn = queryFn ?? QA$;
50949
51100
  const ic2 = new InputController();
@@ -51031,7 +51182,7 @@ function isRunningAsRoot2() {
51031
51182
  }
51032
51183
  }
51033
51184
  async function syncClaudeCredentialsToNodeAccessibleDir(agentConfigDir) {
51034
- const rootClaudeDir = path19.join(process.env.HOME ?? "/root", ".claude");
51185
+ const rootClaudeDir = path21.join(process.env.HOME ?? "/root", ".claude");
51035
51186
  const fs14 = await import("fs/promises");
51036
51187
  try {
51037
51188
  await fs14.access(rootClaudeDir);
@@ -51041,8 +51192,8 @@ async function syncClaudeCredentialsToNodeAccessibleDir(agentConfigDir) {
51041
51192
  }
51042
51193
  const filesToSync = [".credentials.json", "settings.json", ".credentials.backup.json"];
51043
51194
  for (const file2 of filesToSync) {
51044
- const src = path19.join(rootClaudeDir, file2);
51045
- const dest = path19.join(agentConfigDir, file2);
51195
+ const src = path21.join(rootClaudeDir, file2);
51196
+ const dest = path21.join(agentConfigDir, file2);
51046
51197
  try {
51047
51198
  await fs14.copyFile(src, dest);
51048
51199
  logger28.info("Synced credential file", { file: file2, from: src, to: dest });
@@ -51065,7 +51216,7 @@ async function chownRecursive(dirPath, uid, gid) {
51065
51216
  return;
51066
51217
  }
51067
51218
  for (const entry of entries) {
51068
- const fullPath = path19.join(dirPath, entry.name);
51219
+ const fullPath = path21.join(dirPath, entry.name);
51069
51220
  if (entry.isDirectory()) {
51070
51221
  await chownRecursive(fullPath, uid, gid);
51071
51222
  } else {
@@ -51080,7 +51231,7 @@ async function chownRecursive(dirPath, uid, gid) {
51080
51231
  async function startBridge(config2) {
51081
51232
  ensureDir(config2.dataDir);
51082
51233
  ensureDir(config2.agentConfigDir);
51083
- const workspacesDir = path19.join(config2.dataDir, "workspaces");
51234
+ const workspacesDir = path21.join(config2.dataDir, "workspaces");
51084
51235
  ensureDir(workspacesDir);
51085
51236
  process.env.CLAUDE_CONFIG_DIR = config2.agentConfigDir;
51086
51237
  installBridgeFetchAuth(config2.serverApiUrl, config2.bridgeToken);
@@ -51109,7 +51260,7 @@ Bridge token (register this machine at Settings \u2192 \u5DF2\u8FDE\u63A5\u7684\
51109
51260
  `);
51110
51261
  wsMetrics.start(5e3);
51111
51262
  const sessionStore = new SessionStore(config2.dataDir);
51112
- const memoryRoot = path19.join(config2.dataDir, "agent-memory");
51263
+ const memoryRoot = path21.join(config2.dataDir, "agent-memory");
51113
51264
  const memoryStore = new AgentMemoryStore(memoryRoot);
51114
51265
  logger28.info("Agent memory store initialized", { rootDir: memoryRoot });
51115
51266
  const smithNotebook = memoryStore.read(SMITH_AGENT_ID);
@@ -51286,7 +51437,16 @@ Bridge token (register this machine at Settings \u2192 \u5DF2\u8FDE\u63A5\u7684\
51286
51437
  const { requestId, path: dirPath } = msg.payload;
51287
51438
  logger28.info("list_dir request received", { requestId, path: dirPath });
51288
51439
  try {
51289
- const entries = await listDirectoryEntries(dirPath);
51440
+ const resolved = remapServerWorkspacePath(dirPath, workspacesDir);
51441
+ if (resolved.remapped) {
51442
+ ensureDir(resolved.path);
51443
+ logger28.info("list_dir path remapped to local Bridge workspace", {
51444
+ requestId,
51445
+ requested: dirPath,
51446
+ remapped: resolved.path
51447
+ });
51448
+ }
51449
+ const entries = await listDirectoryEntries(resolved.path);
51290
51450
  connector?.send({
51291
51451
  type: "bridge:list_dir_response",
51292
51452
  payload: { requestId, entries }