openfox 2.0.66 → 2.0.68

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 (49) hide show
  1. package/dist/{chat-handler-ZRDNSLOG.js → chat-handler-QFEHJBWN.js} +13 -12
  2. package/dist/{chunk-R7EED6ZU.js → chunk-32KLLK4N.js} +3 -3
  3. package/dist/{chunk-WNA5VPZG.js → chunk-4QBDV6LX.js} +16 -13
  4. package/dist/{chunk-TMHWQYWE.js → chunk-64GN6OPR.js} +2 -2
  5. package/dist/{chunk-3ACDQKK6.js → chunk-6LK47TNE.js} +37 -138
  6. package/dist/{chunk-5EO6SEGV.js → chunk-7OW6X764.js} +2 -2
  7. package/dist/{chunk-ZUM2ZJOX.js → chunk-COWJRBV4.js} +54 -16
  8. package/dist/{chunk-OE5B44P6.js → chunk-DWZEGLHJ.js} +409 -143
  9. package/dist/{chunk-B7WGOK7G.js → chunk-E4POUD2M.js} +2 -2
  10. package/dist/{chunk-UBIHP5Z7.js → chunk-FCLCEIY4.js} +7 -33
  11. package/dist/{chunk-BL5LOLVA.js → chunk-JHGKXQBO.js} +161 -28
  12. package/dist/{chunk-R4UTFE63.js → chunk-QFSDJIUS.js} +8 -5
  13. package/dist/{chunk-BGTEIA2S.js → chunk-TFCHWMOJ.js} +23 -30
  14. package/dist/{chunk-OBK3I4KD.js → chunk-VLV3XWZS.js} +57 -3
  15. package/dist/chunk-X5DOUA3Z.js +66 -0
  16. package/dist/{chunk-PVRSNXD4.js → chunk-XUPKS5FL.js} +5 -3
  17. package/dist/{chunk-5V5DM5E4.js → chunk-YQ3MEZJL.js} +2 -2
  18. package/dist/cli/dev.js +1 -1
  19. package/dist/cli/index.js +1 -1
  20. package/dist/{client-BUX62J2K.js → client-63U5CTPE.js} +3 -3
  21. package/dist/{client-pure-U6ZYUPJQ.js → client-pure-XHH4RRLB.js} +2 -2
  22. package/dist/{compactor-V43WJ3WX.js → compactor-IPMMFFHM.js} +6 -4
  23. package/dist/{inspect-proxy-PKMDR47R.js → inspect-proxy-OVEFBJ26.js} +2 -2
  24. package/dist/{manager-344C5JLZ.js → manager-JXYRGR6B.js} +6 -2
  25. package/dist/{orchestrator-LZQTDOWM.js → orchestrator-GNXKJPX4.js} +12 -13
  26. package/dist/package.json +4 -2
  27. package/dist/platform-AB3GIEGO.js +21 -0
  28. package/dist/{processor-7KAXESY5.js → processor-TIQEQDP7.js} +13 -12
  29. package/dist/{protocol-42f_Png6.d.ts → protocol-DIhV-tAc.d.ts} +1 -1
  30. package/dist/provider/index.d.ts +4 -4
  31. package/dist/{provider-BDH6JGZB.js → provider-IBIWR4AI.js} +4 -4
  32. package/dist/{provider-manager-D3KIOD5E.js → provider-manager-7C3JVOLW.js} +4 -4
  33. package/dist/{serve-HGHFN3D2.js → serve-QVVZ5SJE.js} +17 -16
  34. package/dist/server/index.d.ts +3 -3
  35. package/dist/server/index.js +16 -15
  36. package/dist/{server-W42ENYFX.js → server-ECKS2Y3R.js} +14 -13
  37. package/dist/{settings-ZZ6AKFPD.js → settings-6ZPWOBWL.js} +2 -2
  38. package/dist/shared/index.d.ts +3 -3
  39. package/dist/{tools-2EB7C6H2.js → tools-QAEN5KHA.js} +11 -10
  40. package/dist/{types-vezAEt_y.d.ts → types-BsfYpEai.d.ts} +2 -1
  41. package/dist/{types-apiMd7Df.d.ts → types-CBC6lLqc.d.ts} +1 -1
  42. package/dist/web/assets/{index-B0Q5uK2h.css → index-BA_65WJ5.css} +1 -1
  43. package/dist/web/assets/index-CsZQdTAd.js +303 -0
  44. package/dist/web/index.html +2 -2
  45. package/dist/web/sw.js +1 -1
  46. package/dist/{worktree-EIF75WGF.js → worktree-6U45P63P.js} +6 -2
  47. package/package.json +4 -2
  48. package/plugins-registry.json +14 -0
  49. package/dist/web/assets/index-BhQN93bI.js +0 -303
@@ -21,7 +21,7 @@ import {
21
21
  tokenFromPassword,
22
22
  verifyPassword,
23
23
  workflowExists
24
- } from "./chunk-ZUM2ZJOX.js";
24
+ } from "./chunk-COWJRBV4.js";
25
25
  import {
26
26
  agentExists,
27
27
  createToolRegistry,
@@ -66,22 +66,22 @@ import {
66
66
  setSkillEnabled,
67
67
  skillExists,
68
68
  updateOwnedSkill
69
- } from "./chunk-3ACDQKK6.js";
69
+ } from "./chunk-6LK47TNE.js";
70
70
  import {
71
71
  ALWAYS_ALLOWED,
72
72
  ALWAYS_ALLOWED_FOR_SUBAGENTS,
73
73
  TOP_LEVEL_ONLY_TOOLS
74
- } from "./chunk-R4UTFE63.js";
74
+ } from "./chunk-QFSDJIUS.js";
75
75
  import {
76
- getPathSeparator,
77
- isAbsolutePath
78
- } from "./chunk-BGTEIA2S.js";
76
+ getProject
77
+ } from "./chunk-KXVFHMSM.js";
79
78
  import {
80
79
  createMcpTools
81
80
  } from "./chunk-NWO6GRYE.js";
82
81
  import {
83
- ensureWorktree
84
- } from "./chunk-OBK3I4KD.js";
82
+ ensureWorktree,
83
+ syncIgnoredAssets
84
+ } from "./chunk-VLV3XWZS.js";
85
85
  import {
86
86
  createSession,
87
87
  deleteSession,
@@ -113,26 +113,27 @@ import {
113
113
  import {
114
114
  createContextStateMessage
115
115
  } from "./chunk-F4PMNP7S.js";
116
+ import {
117
+ getPathSeparator,
118
+ isAbsolutePath
119
+ } from "./chunk-X5DOUA3Z.js";
116
120
  import {
117
121
  deleteSetting,
118
122
  getSetting,
119
123
  setSetting
120
- } from "./chunk-PVRSNXD4.js";
121
- import {
122
- EventEmitter
123
- } from "./chunk-EU3WWTFH.js";
124
- import {
125
- getProject
126
- } from "./chunk-KXVFHMSM.js";
124
+ } from "./chunk-XUPKS5FL.js";
127
125
  import {
128
126
  initDatabase
129
127
  } from "./chunk-GMXKJGJ2.js";
128
+ import {
129
+ EventEmitter
130
+ } from "./chunk-EU3WWTFH.js";
130
131
  import {
131
132
  createProviderManager,
132
133
  detectModel,
133
134
  getLlmStatus,
134
135
  parseDefaultModelSelection
135
- } from "./chunk-5EO6SEGV.js";
136
+ } from "./chunk-7OW6X764.js";
136
137
  import {
137
138
  getModelProfile
138
139
  } from "./chunk-V4IE7HJY.js";
@@ -160,9 +161,9 @@ import {
160
161
  import express from "express";
161
162
  import cors from "cors";
162
163
  import { createServer as createHttpServer } from "http";
163
- import { fileURLToPath as fileURLToPath6 } from "url";
164
- import { dirname as dirname7, resolve as resolve5, join as join8 } from "path";
165
- import { readFile as readFile3 } from "fs/promises";
164
+ import { fileURLToPath as fileURLToPath7 } from "url";
165
+ import { dirname as dirname8, resolve as resolve7, join as join10 } from "path";
166
+ import { readFile as readFile5 } from "fs/promises";
166
167
  import { createServer as createViteServer } from "vite";
167
168
 
168
169
  // src/server/llm/mock.ts
@@ -1603,6 +1604,35 @@ var McpManager = class {
1603
1604
  }
1604
1605
  };
1605
1606
 
1607
+ // src/server/git/worktree-config.ts
1608
+ import { readFile, writeFile, mkdir } from "fs/promises";
1609
+ import { resolve, join } from "path";
1610
+ var CONFIG_FILENAME = ".openfox/worktree.json";
1611
+ function getConfigPath(workdir) {
1612
+ return join(resolve(workdir), CONFIG_FILENAME);
1613
+ }
1614
+ async function loadWorktreeConfig(workdir) {
1615
+ try {
1616
+ const configPath = getConfigPath(workdir);
1617
+ const raw = await readFile(configPath, "utf-8");
1618
+ const parsed = JSON.parse(raw);
1619
+ if (!parsed.ignoredAssets) return null;
1620
+ return {
1621
+ ignoredAssets: parsed.ignoredAssets,
1622
+ overrides: parsed.overrides ?? void 0
1623
+ };
1624
+ } catch {
1625
+ return null;
1626
+ }
1627
+ }
1628
+ async function saveWorktreeConfig(workdir, config3) {
1629
+ const resolved = resolve(workdir);
1630
+ const dirPath = join(resolved, ".openfox");
1631
+ await mkdir(dirPath, { recursive: true });
1632
+ const configPath = getConfigPath(workdir);
1633
+ await writeFile(configPath, JSON.stringify(config3, null, 2) + "\n", "utf-8");
1634
+ }
1635
+
1606
1636
  // src/server/lsp/server.ts
1607
1637
  import { spawn } from "child_process";
1608
1638
  import { extname } from "path";
@@ -1925,19 +1955,19 @@ var LspServer = class {
1925
1955
  */
1926
1956
  async getDiagnosticsWithWait(path, timeoutMs = DIAGNOSTIC_WAIT_MS) {
1927
1957
  if (this.openDocuments.has(path)) {
1928
- return new Promise((resolve6) => {
1958
+ return new Promise((resolve8) => {
1929
1959
  const existing = this.diagnostics.get(path);
1930
1960
  if (existing !== void 0) {
1931
1961
  setTimeout(() => {
1932
- resolve6(this.diagnostics.get(path) ?? []);
1962
+ resolve8(this.diagnostics.get(path) ?? []);
1933
1963
  }, 100);
1934
1964
  return;
1935
1965
  }
1936
1966
  const timeout = setTimeout(() => {
1937
1967
  this.pendingDiagnostics.delete(path);
1938
- resolve6(this.diagnostics.get(path) ?? []);
1968
+ resolve8(this.diagnostics.get(path) ?? []);
1939
1969
  }, timeoutMs);
1940
- this.pendingDiagnostics.set(path, { resolve: resolve6, timeout });
1970
+ this.pendingDiagnostics.set(path, { resolve: resolve8, timeout });
1941
1971
  });
1942
1972
  }
1943
1973
  return this.diagnostics.get(path) ?? [];
@@ -2148,14 +2178,14 @@ import { readFileSync as readFileSync2 } from "fs";
2148
2178
 
2149
2179
  // src/server/utils/which.ts
2150
2180
  import { access, constants } from "fs/promises";
2151
- import { join, dirname } from "path";
2181
+ import { join as join2, dirname } from "path";
2152
2182
  import { fileURLToPath as fileURLToPath2 } from "url";
2153
2183
  var __dirname = dirname(fileURLToPath2(import.meta.url));
2154
2184
  function getBundledBinPaths() {
2155
2185
  const paths = [];
2156
- paths.push(join(process.cwd(), "node_modules", ".bin"));
2157
- paths.push(join(__dirname, "..", "..", "..", "node_modules", ".bin"));
2158
- paths.push(join(__dirname, "..", "..", "..", "..", "node_modules", ".bin"));
2186
+ paths.push(join2(process.cwd(), "node_modules", ".bin"));
2187
+ paths.push(join2(__dirname, "..", "..", "..", "node_modules", ".bin"));
2188
+ paths.push(join2(__dirname, "..", "..", "..", "..", "node_modules", ".bin"));
2159
2189
  return paths;
2160
2190
  }
2161
2191
  async function existsExecutable(path) {
@@ -2174,13 +2204,13 @@ async function which(command, workdir) {
2174
2204
  return null;
2175
2205
  }
2176
2206
  for (const binDir of getBundledBinPaths()) {
2177
- const fullPath = join(binDir, command);
2207
+ const fullPath = join2(binDir, command);
2178
2208
  if (await existsExecutable(fullPath)) {
2179
2209
  return fullPath;
2180
2210
  }
2181
2211
  }
2182
2212
  if (workdir) {
2183
- const projectBin = join(workdir, "node_modules", ".bin", command);
2213
+ const projectBin = join2(workdir, "node_modules", ".bin", command);
2184
2214
  if (await existsExecutable(projectBin)) {
2185
2215
  return projectBin;
2186
2216
  }
@@ -2189,7 +2219,7 @@ async function which(command, workdir) {
2189
2219
  const paths = pathEnv.split(getPathSeparator());
2190
2220
  for (const dir of paths) {
2191
2221
  if (!dir) continue;
2192
- const fullPath = join(dir, command);
2222
+ const fullPath = join2(dir, command);
2193
2223
  if (await existsExecutable(fullPath)) {
2194
2224
  return fullPath;
2195
2225
  }
@@ -2201,9 +2231,9 @@ async function which(command, workdir) {
2201
2231
  import { extname as extname2, basename } from "path";
2202
2232
  import { readFileSync } from "fs";
2203
2233
  import { fileURLToPath as fileURLToPath3 } from "url";
2204
- import { dirname as dirname2, resolve } from "path";
2234
+ import { dirname as dirname2, resolve as resolve2 } from "path";
2205
2235
  var __dirname2 = dirname2(fileURLToPath3(import.meta.url));
2206
- var languagesJson = JSON.parse(readFileSync(resolve(__dirname2, "languages.json"), "utf-8"));
2236
+ var languagesJson = JSON.parse(readFileSync(resolve2(__dirname2, "languages.json"), "utf-8"));
2207
2237
  var LANGUAGES = languagesJson;
2208
2238
  var extensionToLanguage = /* @__PURE__ */ new Map();
2209
2239
  for (const lang of LANGUAGES) {
@@ -3181,6 +3211,10 @@ var SessionManager = class {
3181
3211
  if (!project) throw new Error(`Project not found: ${session.projectId}`);
3182
3212
  if (session.worktree) throw new Error("Session already has a worktree");
3183
3213
  const result = await ensureWorktree(project.workdir, name);
3214
+ const wtConfig = await loadWorktreeConfig(project.workdir);
3215
+ if (wtConfig) {
3216
+ await syncIgnoredAssets(project.workdir, result.path, wtConfig);
3217
+ }
3184
3218
  updateSessionWorkdir(sessionId, project.workdir, result.path);
3185
3219
  try {
3186
3220
  await shutdownLspManager(sessionId);
@@ -3245,6 +3279,11 @@ All file and git operations should use this directory.
3245
3279
  if (!project) throw new Error(`Project not found: ${session.projectId}`);
3246
3280
  const closedPath = session.worktree;
3247
3281
  const branchName = closedPath.includes("/worktrees/") ? closedPath.split("/worktrees/")[1] ?? closedPath : closedPath;
3282
+ try {
3283
+ await devServerManager.stop(closedPath);
3284
+ } catch (err) {
3285
+ logger.error("Error stopping dev server for worktree close", { sessionId, worktree: closedPath, error: err });
3286
+ }
3248
3287
  updateSessionWorkdir(sessionId, project.workdir, null);
3249
3288
  try {
3250
3289
  await shutdownLspManager(sessionId);
@@ -3367,15 +3406,15 @@ Operating in the original project directory at ${project.workdir}.
3367
3406
  // src/server/routes/skills.ts
3368
3407
  import { Router as Router2 } from "express";
3369
3408
  import multer from "multer";
3370
- import { access as access2, mkdir as mkdir2, realpath, stat } from "fs/promises";
3409
+ import { access as access2, mkdir as mkdir3, realpath, stat } from "fs/promises";
3371
3410
  import { constants as constants2 } from "fs";
3372
3411
  import { homedir } from "os";
3373
- import { isAbsolute as isAbsolute2, join as join4, resolve as resolve2 } from "path";
3412
+ import { isAbsolute as isAbsolute2, join as join5, resolve as resolve3 } from "path";
3374
3413
 
3375
3414
  // src/server/skills/installer.ts
3376
3415
  import { randomUUID } from "crypto";
3377
- import { mkdir, readFile, rename, rm, writeFile } from "fs/promises";
3378
- import { dirname as dirname3, isAbsolute, join as join2 } from "path";
3416
+ import { mkdir as mkdir2, readFile as readFile2, rename, rm, writeFile as writeFile2 } from "fs/promises";
3417
+ import { dirname as dirname3, isAbsolute, join as join3 } from "path";
3379
3418
  import matter from "gray-matter";
3380
3419
  var SkillInstallError = class extends Error {
3381
3420
  constructor(message, status) {
@@ -3407,17 +3446,17 @@ async function installSkillPackage(libraryDir, packageName, files) {
3407
3446
  totalBytes += file.content.byteLength;
3408
3447
  }
3409
3448
  if (totalBytes > 50 * 1024 * 1024) throw new SkillInstallError("Package exceeds 50 MiB", 413);
3410
- const destination = join2(libraryDir, packageName);
3449
+ const destination = join3(libraryDir, packageName);
3411
3450
  if (await pathExists(destination)) throw new SkillInstallError("A package with this name already exists", 409);
3412
- const staging = join2(libraryDir, `.${packageName}.openfox-upload-${randomUUID()}`);
3451
+ const staging = join3(libraryDir, `.${packageName}.openfox-upload-${randomUUID()}`);
3413
3452
  try {
3414
- await mkdir(staging, { recursive: false });
3453
+ await mkdir2(staging, { recursive: false });
3415
3454
  for (const file of files) {
3416
- const target = join2(staging, file.path);
3417
- await mkdir(dirname3(target), { recursive: true });
3418
- await writeFile(target, file.content);
3455
+ const target = join3(staging, file.path);
3456
+ await mkdir2(dirname3(target), { recursive: true });
3457
+ await writeFile2(target, file.content);
3419
3458
  }
3420
- const parsed = matter(await readFile(join2(staging, "SKILL.md"), "utf-8"));
3459
+ const parsed = matter(await readFile2(join3(staging, "SKILL.md"), "utf-8"));
3421
3460
  const name = typeof parsed.data["name"] === "string" ? parsed.data["name"].trim() : "";
3422
3461
  const description = typeof parsed.data["description"] === "string" ? parsed.data["description"].trim() : "";
3423
3462
  if (!name || !description || !parsed.content.trim()) {
@@ -3434,7 +3473,7 @@ async function installSkillPackage(libraryDir, packageName, files) {
3434
3473
 
3435
3474
  // src/server/routes/crud-helpers.ts
3436
3475
  import { Router } from "express";
3437
- import { join as join3 } from "path";
3476
+ import { join as join4 } from "path";
3438
3477
  function computeOverrideIds(defaults, userItems) {
3439
3478
  return userItems.filter((u) => defaults.some((d) => d.metadata.id === u.metadata.id)).map((u) => u.metadata.id);
3440
3479
  }
@@ -3446,7 +3485,7 @@ function validateNameIdPrompt(body) {
3446
3485
  return null;
3447
3486
  }
3448
3487
  function getProjectItemPath(projectDir, dirName, id, ext) {
3449
- return join3(projectDir, ".openfox", dirName, `${id}${ext}`);
3488
+ return join4(projectDir, ".openfox", dirName, `${id}${ext}`);
3450
3489
  }
3451
3490
  async function isProjectItem(projectDir, dirName, id, ext) {
3452
3491
  if (!projectDir) return false;
@@ -3591,7 +3630,7 @@ var upload = multer({
3591
3630
  });
3592
3631
  function expandHome(path) {
3593
3632
  if (path === "~") return homedir();
3594
- return path.startsWith("~/") ? join4(homedir(), path.slice(2)) : path;
3633
+ return path.startsWith("~/") ? join5(homedir(), path.slice(2)) : path;
3595
3634
  }
3596
3635
  function configuredDirectory() {
3597
3636
  const raw = getSetting(SKILL_DIRECTORIES_SETTING);
@@ -3604,13 +3643,13 @@ function configuredDirectory() {
3604
3643
  }
3605
3644
  }
3606
3645
  function defaultLibrary(configDir) {
3607
- const path = resolve2(configDir, "skills");
3646
+ const path = resolve3(configDir, "skills");
3608
3647
  return { configuredPath: path, resolvedPath: path, available: true, custom: false };
3609
3648
  }
3610
3649
  async function resolveLibrary(path) {
3611
3650
  const expanded = expandHome(path);
3612
3651
  if (!isAbsolute2(expanded)) throw new Error("Selected path must be absolute");
3613
- const absolute = resolve2(expanded);
3652
+ const absolute = resolve3(expanded);
3614
3653
  const info = await stat(absolute);
3615
3654
  if (!info.isDirectory()) throw new Error("Selected path is not a directory");
3616
3655
  await access2(absolute, constants2.R_OK);
@@ -3699,7 +3738,7 @@ function createSkillRoutes(configDir, projectDir) {
3699
3738
  try {
3700
3739
  const configured = configuredDirectory();
3701
3740
  const library = configured ? await resolveLibrary(configured) : defaultLibrary(configDir);
3702
- await mkdir2(library.resolvedPath, { recursive: true });
3741
+ await mkdir3(library.resolvedPath, { recursive: true });
3703
3742
  const packageName = String(req.body["packageName"] ?? "");
3704
3743
  const paths = JSON.parse(String(req.body["paths"] ?? "[]"));
3705
3744
  const files = req.files;
@@ -3749,19 +3788,19 @@ function createSkillRoutes(configDir, projectDir) {
3749
3788
  }
3750
3789
 
3751
3790
  // src/server/commands/registry.ts
3752
- import { writeFile as writeFile2, mkdir as mkdir3, unlink } from "fs/promises";
3753
- import { join as join5, dirname as dirname4 } from "path";
3791
+ import { writeFile as writeFile3, mkdir as mkdir4, unlink } from "fs/promises";
3792
+ import { join as join6, dirname as dirname4 } from "path";
3754
3793
  import { fileURLToPath as fileURLToPath4 } from "url";
3755
3794
  import matter2 from "gray-matter";
3756
3795
  var __bundleDir = dirname4(fileURLToPath4(import.meta.url));
3757
- var DEFAULTS_DIR = join5(__bundleDir, "defaults");
3758
- var DEFAULTS_DIR_ALT = join5(__bundleDir, "command-defaults");
3796
+ var DEFAULTS_DIR = join6(__bundleDir, "defaults");
3797
+ var DEFAULTS_DIR_ALT = join6(__bundleDir, "command-defaults");
3759
3798
  var COMMAND_EXTENSION = ".command.md";
3760
3799
  function getCommandsDir(configDir) {
3761
- return join5(configDir, "commands");
3800
+ return join6(configDir, "commands");
3762
3801
  }
3763
3802
  function getProjectCommandsDir(projectDir) {
3764
- return join5(projectDir, ".openfox", "commands");
3803
+ return join6(projectDir, ".openfox", "commands");
3765
3804
  }
3766
3805
  async function loadDefaultCommands() {
3767
3806
  let defaults = await loadItemsFromDir(DEFAULTS_DIR, {
@@ -3818,19 +3857,19 @@ function findCommandById(commandId, commands) {
3818
3857
  return commands.find((c) => c.metadata.id === commandId);
3819
3858
  }
3820
3859
  async function commandExists(configDir, commandId, projectDir) {
3821
- if (await pathExists(join5(getCommandsDir(configDir), `${commandId}${COMMAND_EXTENSION}`))) return true;
3822
- if (projectDir && await pathExists(join5(getProjectCommandsDir(projectDir), `${commandId}${COMMAND_EXTENSION}`)))
3860
+ if (await pathExists(join6(getCommandsDir(configDir), `${commandId}${COMMAND_EXTENSION}`))) return true;
3861
+ if (projectDir && await pathExists(join6(getProjectCommandsDir(projectDir), `${commandId}${COMMAND_EXTENSION}`)))
3823
3862
  return true;
3824
3863
  return false;
3825
3864
  }
3826
3865
  async function saveCommand(configDir, command) {
3827
3866
  const commandsDir = getCommandsDir(configDir);
3828
3867
  if (!await pathExists(commandsDir)) {
3829
- await mkdir3(commandsDir, { recursive: true });
3868
+ await mkdir4(commandsDir, { recursive: true });
3830
3869
  }
3831
- const filePath = join5(commandsDir, `${command.metadata.id}${COMMAND_EXTENSION}`);
3870
+ const filePath = join6(commandsDir, `${command.metadata.id}${COMMAND_EXTENSION}`);
3832
3871
  const content = matter2.stringify(command.prompt, command.metadata);
3833
- await writeFile2(filePath, content, "utf-8");
3872
+ await writeFile3(filePath, content, "utf-8");
3834
3873
  }
3835
3874
  async function saveCommandToProject(projectDir, command) {
3836
3875
  await saveItemToDir(
@@ -3848,7 +3887,7 @@ async function deleteCommand(configDir, commandId) {
3848
3887
  if (isDefault) {
3849
3888
  return { success: false, reason: "Cannot delete built-in defaults" };
3850
3889
  }
3851
- const filePath = join5(getCommandsDir(configDir), `${commandId}${COMMAND_EXTENSION}`);
3890
+ const filePath = join6(getCommandsDir(configDir), `${commandId}${COMMAND_EXTENSION}`);
3852
3891
  try {
3853
3892
  await unlink(filePath);
3854
3893
  return { success: true };
@@ -4028,10 +4067,38 @@ function createDevServerRoutes() {
4028
4067
  return router;
4029
4068
  }
4030
4069
 
4031
- // src/server/routes/terminals.ts
4070
+ // src/server/routes/worktree-config.ts
4032
4071
  import { Router as Router5 } from "express";
4033
- function createTerminalRoutes() {
4072
+ function createWorktreeConfigRoutes() {
4034
4073
  const router = Router5();
4074
+ router.get("/config", async (req, res) => {
4075
+ const workdir = req.query["workdir"];
4076
+ if (!workdir) return res.status(400).json({ error: "workdir required" });
4077
+ const config3 = await loadWorktreeConfig(workdir);
4078
+ res.json({ config: config3 });
4079
+ });
4080
+ router.post("/config", async (req, res) => {
4081
+ const workdir = req.query["workdir"];
4082
+ if (!workdir) return res.status(400).json({ error: "workdir required" });
4083
+ const { ignoredAssets, overrides } = req.body;
4084
+ if (!ignoredAssets || !["symlink", "copy", "skip"].includes(ignoredAssets)) {
4085
+ return res.status(400).json({ error: "ignoredAssets must be one of: symlink, copy, skip" });
4086
+ }
4087
+ try {
4088
+ const config3 = { ignoredAssets, overrides: overrides ?? void 0 };
4089
+ await saveWorktreeConfig(workdir, config3);
4090
+ res.json({ config: config3 });
4091
+ } catch (err) {
4092
+ res.status(500).json({ error: err instanceof Error ? err.message : "Failed to save config" });
4093
+ }
4094
+ });
4095
+ return router;
4096
+ }
4097
+
4098
+ // src/server/routes/terminals.ts
4099
+ import { Router as Router6 } from "express";
4100
+ function createTerminalRoutes() {
4101
+ const router = Router6();
4035
4102
  router.get("/", (req, res) => {
4036
4103
  const projectId = req.query["projectId"];
4037
4104
  const sessions = projectId ? terminalManager.getByProject(projectId) : [];
@@ -4070,20 +4137,20 @@ function createTerminalRoutes() {
4070
4137
  }
4071
4138
 
4072
4139
  // src/server/routes/directories.ts
4073
- import { Router as Router6 } from "express";
4140
+ import { Router as Router7 } from "express";
4074
4141
  import { readdir } from "fs/promises";
4075
- import { resolve as resolve3, join as join6, dirname as dirname5, basename as basename2 } from "path";
4142
+ import { resolve as resolve4, join as join7, dirname as dirname5, basename as basename2 } from "path";
4076
4143
  function createDirectoryRoutes() {
4077
- const router = Router6();
4144
+ const router = Router7();
4078
4145
  const DEFAULT_BASE_PATH = process.cwd();
4079
4146
  router.get("/", async (req, res) => {
4080
4147
  const path = req.query["path"] || DEFAULT_BASE_PATH;
4081
4148
  try {
4082
- const resolvedPath = resolve3(path);
4149
+ const resolvedPath = resolve4(path);
4083
4150
  const entries = await readdir(resolvedPath, { withFileTypes: true });
4084
4151
  const directories = entries.filter((entry) => entry.isDirectory()).map((entry) => ({
4085
4152
  name: entry.name,
4086
- path: join6(resolvedPath, entry.name)
4153
+ path: join7(resolvedPath, entry.name)
4087
4154
  })).sort((a, b) => a.name.localeCompare(b.name));
4088
4155
  const parent = dirname5(resolvedPath);
4089
4156
  const hasParent = parent !== resolvedPath;
@@ -4107,10 +4174,10 @@ function createDirectoryRoutes() {
4107
4174
  }
4108
4175
 
4109
4176
  // src/server/routes/file-search.ts
4110
- import { Router as Router7 } from "express";
4177
+ import { Router as Router8 } from "express";
4111
4178
  import fg from "fast-glob";
4112
4179
  function createFileSearchRoutes() {
4113
- const router = Router7();
4180
+ const router = Router8();
4114
4181
  router.get("/", async (req, res) => {
4115
4182
  const query = req.query["q"] || "";
4116
4183
  const workdir = req.query["workdir"] || process.cwd();
@@ -4186,11 +4253,11 @@ function fuzzyScore(filePath, query) {
4186
4253
  }
4187
4254
 
4188
4255
  // src/server/routes/auto-update.ts
4189
- import { Router as Router8 } from "express";
4256
+ import { Router as Router9 } from "express";
4190
4257
  import { spawn as spawn2 } from "child_process";
4191
4258
 
4192
4259
  // src/constants.ts
4193
- var VERSION = "2.0.66";
4260
+ var VERSION = "2.0.68";
4194
4261
 
4195
4262
  // src/server/routes/auto-update.ts
4196
4263
  var updateInProgress = false;
@@ -4208,12 +4275,12 @@ async function checkAuth(req, opts) {
4208
4275
  return true;
4209
4276
  }
4210
4277
  function createAutoUpdateRoutes(options = {}) {
4211
- const router = Router8();
4278
+ const router = Router9();
4212
4279
  router.get("/check", async (_req, res) => {
4213
4280
  const isService = isRunningAsService();
4214
4281
  const current = VERSION;
4215
4282
  try {
4216
- const latest = await new Promise((resolve6, reject) => {
4283
+ const latest = await new Promise((resolve8, reject) => {
4217
4284
  const child = spawn2("npm", ["view", "openfox", "version"], {
4218
4285
  stdio: ["ignore", "pipe", "pipe"],
4219
4286
  windowsHide: true
@@ -4224,7 +4291,7 @@ function createAutoUpdateRoutes(options = {}) {
4224
4291
  });
4225
4292
  child.on("close", (code) => {
4226
4293
  if (code === 0) {
4227
- resolve6(stdout.trim());
4294
+ resolve8(stdout.trim());
4228
4295
  } else {
4229
4296
  reject(new Error(`npm view exited with code ${code}`));
4230
4297
  }
@@ -4268,9 +4335,9 @@ function createAutoUpdateRoutes(options = {}) {
4268
4335
  const timeout = setTimeout(() => {
4269
4336
  child.kill();
4270
4337
  }, UPDATE_TIMEOUT);
4271
- const exitCode = await new Promise((resolve6) => {
4272
- child.on("close", resolve6);
4273
- child.on("error", () => resolve6(1));
4338
+ const exitCode = await new Promise((resolve8) => {
4339
+ child.on("close", resolve8);
4340
+ child.on("error", () => resolve8(1));
4274
4341
  });
4275
4342
  clearTimeout(timeout);
4276
4343
  if (exitCode === 0) {
@@ -4308,9 +4375,9 @@ function createAutoUpdateRoutes(options = {}) {
4308
4375
  }
4309
4376
 
4310
4377
  // src/server/routes/provider-auth.ts
4311
- import { Router as Router9 } from "express";
4378
+ import { Router as Router10 } from "express";
4312
4379
  function createProviderAuthRoutes(config3, providerManager, registry) {
4313
- const router = Router9();
4380
+ const router = Router10();
4314
4381
  function resolveAuthContext(providerId, res) {
4315
4382
  const provider = providerManager.getProviders().find((item) => item.id === providerId);
4316
4383
  if (!provider) {
@@ -4433,14 +4500,16 @@ var ProviderRegistry = class {
4433
4500
  }
4434
4501
  register(map, id, value, kind) {
4435
4502
  if (!id.trim()) throw new Error(`Provider ${kind} id cannot be empty`);
4436
- if (map.has(id)) throw new Error(`Provider ${kind} already registered: ${id}`);
4503
+ if (map.has(id)) {
4504
+ map.delete(id);
4505
+ }
4437
4506
  map.set(id, value);
4438
4507
  }
4439
4508
  };
4440
4509
 
4441
4510
  // src/server/providers/plugins/loader.ts
4442
- import { readdir as readdir2, readFile as readFile2, stat as stat2 } from "fs/promises";
4443
- import { dirname as dirname6, join as join7, resolve as resolve4 } from "path";
4511
+ import { readdir as readdir2, readFile as readFile3, stat as stat2 } from "fs/promises";
4512
+ import { dirname as dirname6, join as join8, resolve as resolve5 } from "path";
4444
4513
  import { fileURLToPath as fileURLToPath5, pathToFileURL } from "url";
4445
4514
  async function packageDirectories(root) {
4446
4515
  let entries;
@@ -4451,13 +4520,13 @@ async function packageDirectories(root) {
4451
4520
  }
4452
4521
  const directories = [];
4453
4522
  for (const entry of entries) {
4454
- const entryPath = join7(root, entry.name);
4523
+ const entryPath = join8(root, entry.name);
4455
4524
  const isDirectory = entry.isDirectory() || entry.isSymbolicLink() && (await stat2(entryPath).catch(() => void 0))?.isDirectory();
4456
4525
  if (!isDirectory) continue;
4457
4526
  if (entry.name.startsWith("@")) {
4458
4527
  const scoped = await readdir2(entryPath, { withFileTypes: true }).catch(() => []);
4459
4528
  for (const child of scoped) {
4460
- const childPath = join7(entryPath, child.name);
4529
+ const childPath = join8(entryPath, child.name);
4461
4530
  const childIsDirectory = child.isDirectory() || child.isSymbolicLink() && (await stat2(childPath).catch(() => void 0))?.isDirectory();
4462
4531
  if (childIsDirectory) directories.push(childPath);
4463
4532
  }
@@ -4470,19 +4539,19 @@ async function packageDirectories(root) {
4470
4539
  async function loadProviderPlugins(options) {
4471
4540
  const moduleDirectory = dirname6(fileURLToPath5(import.meta.url));
4472
4541
  const roots = [
4473
- join7(options.configDirectory, "plugins"),
4474
- join7(options.cwd ?? process.cwd(), "node_modules"),
4475
- resolve4(moduleDirectory, "node_modules"),
4476
- resolve4(moduleDirectory, "../../../../node_modules")
4542
+ join8(options.configDirectory, "plugins"),
4543
+ join8(options.cwd ?? process.cwd(), "node_modules"),
4544
+ resolve5(moduleDirectory, "node_modules"),
4545
+ resolve5(moduleDirectory, "../../../../node_modules")
4477
4546
  ];
4478
4547
  const seen = /* @__PURE__ */ new Set();
4479
4548
  const diagnostics = [];
4480
4549
  for (const root of roots) {
4481
4550
  for (const packageDir of await packageDirectories(root)) {
4482
- const manifestPath = join7(packageDir, "package.json");
4551
+ const manifestPath = join8(packageDir, "package.json");
4483
4552
  let manifest;
4484
4553
  try {
4485
- manifest = JSON.parse(await readFile2(manifestPath, "utf8"));
4554
+ manifest = JSON.parse(await readFile3(manifestPath, "utf8"));
4486
4555
  } catch {
4487
4556
  continue;
4488
4557
  }
@@ -4520,7 +4589,7 @@ async function loadProviderPlugins(options) {
4520
4589
  }
4521
4590
  };
4522
4591
  try {
4523
- const module = await import(pathToFileURL(join7(packageDir, plugin)).href);
4592
+ const module = await import(pathToFileURL(join8(packageDir, plugin)).href);
4524
4593
  if (typeof module.register !== "function") throw new Error("Plugin does not export register(registry)");
4525
4594
  await module.register(trackingRegistry);
4526
4595
  diagnostic.loaded = true;
@@ -4532,8 +4601,199 @@ async function loadProviderPlugins(options) {
4532
4601
  return diagnostics;
4533
4602
  }
4534
4603
 
4604
+ // src/server/routes/plugins.ts
4605
+ import { Router as Router11 } from "express";
4606
+ import { fileURLToPath as fileURLToPath6, pathToFileURL as pathToFileURL2 } from "url";
4607
+ import { dirname as dirname7, resolve as resolve6, join as join9 } from "path";
4608
+ import { readFile as readFile4, readdir as readdir3, rm as rm2, rename as rename2 } from "fs/promises";
4609
+ import { platform } from "os";
4610
+ import { execFile } from "child_process";
4611
+ import { promisify } from "util";
4612
+ var execFileP = promisify(execFile);
4613
+ async function openFolder(dir, res) {
4614
+ try {
4615
+ await execFileP("mkdir", ["-p", dir], { timeout: 5e3 });
4616
+ const cmd = platform() === "darwin" ? "open" : platform() === "win32" ? "explorer" : "xdg-open";
4617
+ await execFileP(cmd, [dir], { timeout: 5e3 });
4618
+ res.json({ success: true });
4619
+ } catch (err) {
4620
+ res.status(500).json({ error: err instanceof Error ? err.message : "Failed to open folder" });
4621
+ }
4622
+ }
4623
+ function createPluginRoutes(options) {
4624
+ const router = Router11();
4625
+ const { config: config3, providerAdapters, pluginDiagnostics, logger: logger2 } = options;
4626
+ let registryCache = null;
4627
+ router.get("/registry", async (_req, res) => {
4628
+ try {
4629
+ const now = Date.now();
4630
+ if (registryCache && now - registryCache.ts < 3e5) {
4631
+ return res.json({ plugins: registryCache.data });
4632
+ }
4633
+ const moduleDir = dirname7(fileURLToPath6(import.meta.url));
4634
+ const registryPath = resolve6(moduleDir, "../../../plugins-registry.json");
4635
+ const data = JSON.parse(await readFile4(registryPath, "utf8"));
4636
+ registryCache = { data, ts: now };
4637
+ res.json({ plugins: data });
4638
+ } catch (err) {
4639
+ logger2.error("Failed to load plugin registry", { error: String(err) });
4640
+ res.json({ plugins: [] });
4641
+ }
4642
+ });
4643
+ router.post("/install", async (req, res) => {
4644
+ const { githubUrl } = req.body;
4645
+ if (!githubUrl || typeof githubUrl !== "string") {
4646
+ return res.status(400).json({ error: "githubUrl is required" });
4647
+ }
4648
+ const parsed = githubUrl.match(/github\.com\/([^/]+)\/([^/]+?)(?:\/|$)/);
4649
+ if (!parsed) {
4650
+ return res.status(400).json({ error: "Invalid GitHub URL" });
4651
+ }
4652
+ const repoName = parsed[2].replace(/\.git$/, "");
4653
+ if (!/^[a-zA-Z0-9_-]+$/.test(repoName)) {
4654
+ return res.status(400).json({ error: "Invalid repository name" });
4655
+ }
4656
+ const pluginsDir = join9(getGlobalConfigDir(config3.mode ?? "production"), "plugins");
4657
+ const targetDir = join9(pluginsDir, repoName);
4658
+ const tmpDir = join9(pluginsDir, `.${repoName}-tmp-${Date.now()}`);
4659
+ try {
4660
+ await execFileP("mkdir", ["-p", pluginsDir], { timeout: 5e3 });
4661
+ } catch {
4662
+ return res.status(500).json({ error: "Failed to create plugins directory" });
4663
+ }
4664
+ let gitOk = false;
4665
+ try {
4666
+ const { stdout } = await execFileP("git", ["--version"], { timeout: 5e3 });
4667
+ gitOk = stdout.includes("git version");
4668
+ } catch {
4669
+ }
4670
+ if (!gitOk) {
4671
+ return res.status(500).json({ error: "git is not installed or not found in PATH" });
4672
+ }
4673
+ try {
4674
+ const cloneUrl = githubUrl.replace(/\/$/, "") + ".git";
4675
+ await execFileP("git", ["clone", "--depth", "1", cloneUrl, tmpDir], { timeout: 6e4 });
4676
+ await rm2(targetDir, { recursive: true, force: true });
4677
+ await rename2(tmpDir, targetDir);
4678
+ let loaded = false;
4679
+ let loadError;
4680
+ try {
4681
+ await execFileP("npm", ["install", "--no-audit", "--no-fund"], { cwd: targetDir, timeout: 12e4 });
4682
+ await execFileP("npm", ["run", "build"], { cwd: targetDir, timeout: 12e4 });
4683
+ } catch (err) {
4684
+ loadError = "Failed to install/build plugin dependencies";
4685
+ logger2.error("Plugin build failed", { repoName, error: String(err) });
4686
+ }
4687
+ if (!loadError) {
4688
+ try {
4689
+ const manifest = JSON.parse(await readFile4(join9(targetDir, "package.json"), "utf8"));
4690
+ const pluginEntry = manifest.openfox?.plugin;
4691
+ const apiVersion = manifest.openfox?.apiVersion;
4692
+ if (!pluginEntry || !manifest.name) {
4693
+ loadError = "Plugin package.json is missing openfox.plugin or name field";
4694
+ } else if (apiVersion !== 1) {
4695
+ loadError = `Unsupported plugin API version: ${String(apiVersion)}`;
4696
+ } else {
4697
+ const mod = await import(pathToFileURL2(join9(targetDir, pluginEntry)).href);
4698
+ if (typeof mod.register !== "function") {
4699
+ loadError = "Plugin does not export register(registry)";
4700
+ } else {
4701
+ const diagnostic = {
4702
+ packageName: manifest.name,
4703
+ version: manifest.version,
4704
+ source: targetDir,
4705
+ loaded: false,
4706
+ authAdapters: [],
4707
+ transportAdapters: [],
4708
+ presets: []
4709
+ };
4710
+ const trackingRegistry = {
4711
+ runtime: providerAdapters.runtime,
4712
+ registerAuth(adapter) {
4713
+ providerAdapters.registerAuth(adapter);
4714
+ diagnostic.authAdapters.push(adapter.id);
4715
+ },
4716
+ registerTransport(adapter) {
4717
+ providerAdapters.registerTransport(adapter);
4718
+ diagnostic.transportAdapters.push(adapter.id);
4719
+ },
4720
+ registerPreset(preset) {
4721
+ providerAdapters.registerPreset(preset);
4722
+ diagnostic.presets.push(preset.id);
4723
+ }
4724
+ };
4725
+ await mod.register(trackingRegistry);
4726
+ diagnostic.loaded = true;
4727
+ pluginDiagnostics.push(diagnostic);
4728
+ loaded = true;
4729
+ config3.providers = providerAdapters.resolveProviders(config3.providers ?? []);
4730
+ }
4731
+ }
4732
+ } catch (err) {
4733
+ loadError = err instanceof Error ? err.message : "Failed to load plugin";
4734
+ logger2.error("Plugin runtime load failed", { repoName, error: loadError });
4735
+ }
4736
+ }
4737
+ res.json({ success: true, loaded, loadError, path: targetDir });
4738
+ } catch (err) {
4739
+ await rm2(tmpDir, { recursive: true, force: true });
4740
+ const msg = err instanceof Error ? err.message : "Clone failed";
4741
+ logger2.error("Plugin install failed", { githubUrl, error: msg });
4742
+ res.status(500).json({ error: msg });
4743
+ }
4744
+ });
4745
+ router.get("/installed", async (_req, res) => {
4746
+ const pluginsDir = join9(getGlobalConfigDir(config3.mode ?? "production"), "plugins");
4747
+ try {
4748
+ const entries = await readdir3(pluginsDir, { withFileTypes: true });
4749
+ const installed = [];
4750
+ for (const entry of entries) {
4751
+ if (!entry.isDirectory()) continue;
4752
+ const pkgPath = join9(pluginsDir, entry.name, "package.json");
4753
+ let version = null;
4754
+ try {
4755
+ const pkg = JSON.parse(await readFile4(pkgPath, "utf8"));
4756
+ version = pkg.version ?? null;
4757
+ } catch {
4758
+ }
4759
+ installed.push({ name: entry.name, version });
4760
+ }
4761
+ res.json({ installed });
4762
+ } catch {
4763
+ res.json({ installed: [] });
4764
+ }
4765
+ });
4766
+ router.get("/open-folder", async (_req, res) => {
4767
+ const pluginsDir = join9(getGlobalConfigDir(config3.mode ?? "production"), "plugins");
4768
+ await openFolder(pluginsDir, res);
4769
+ });
4770
+ router.get("/:name/open-folder", async (req, res) => {
4771
+ const name = req.params.name;
4772
+ if (!/^[a-zA-Z0-9_-]+$/.test(name)) return res.status(400).json({ error: "Invalid plugin name" });
4773
+ const targetDir = join9(getGlobalConfigDir(config3.mode ?? "production"), "plugins", name);
4774
+ await openFolder(targetDir, res);
4775
+ });
4776
+ router.delete("/:name", async (req, res) => {
4777
+ const name = req.params.name;
4778
+ if (!/^[a-zA-Z0-9_-]+$/.test(name)) {
4779
+ return res.status(400).json({ error: "Invalid plugin name" });
4780
+ }
4781
+ const targetDir = join9(getGlobalConfigDir(config3.mode ?? "production"), "plugins", name);
4782
+ try {
4783
+ await rm2(targetDir, { recursive: true, force: true });
4784
+ for (let i = pluginDiagnostics.length - 1; i >= 0; i--) {
4785
+ if (pluginDiagnostics[i].source === targetDir) pluginDiagnostics.splice(i, 1);
4786
+ }
4787
+ res.json({ success: true });
4788
+ } catch (err) {
4789
+ res.status(500).json({ error: err instanceof Error ? err.message : "Failed to remove plugin" });
4790
+ }
4791
+ });
4792
+ return router;
4793
+ }
4794
+
4535
4795
  // src/server/index.ts
4536
- var __dirname3 = dirname7(fileURLToPath6(import.meta.url));
4796
+ var __dirname3 = dirname8(fileURLToPath7(import.meta.url));
4537
4797
  async function createServerHandle(config3) {
4538
4798
  setRuntimeConfig(config3);
4539
4799
  setLogLevel(config3.logging?.level ?? void 0, config3.mode);
@@ -4630,7 +4890,7 @@ async function createServerHandle(config3) {
4630
4890
  setMcpTools(mcpTools);
4631
4891
  logger.info("MCP tools registered", { count: mcpTools.length });
4632
4892
  }
4633
- const { signalMcpReady } = await import("./server-W42ENYFX.js");
4893
+ const { signalMcpReady } = await import("./server-ECKS2Y3R.js");
4634
4894
  signalMcpReady();
4635
4895
  });
4636
4896
  const app = express();
@@ -4791,7 +5051,7 @@ async function createServerHandle(config3) {
4791
5051
  const { getProject: getProject2 } = await import("./projects-7HSBZY2D.js");
4792
5052
  const project = getProject2(req.params.id);
4793
5053
  if (!project) return res.status(404).json({ error: "Project not found" });
4794
- const { listBranches } = await import("./worktree-EIF75WGF.js");
5054
+ const { listBranches } = await import("./worktree-6U45P63P.js");
4795
5055
  const branches = await listBranches(project.workdir);
4796
5056
  res.json({ branches });
4797
5057
  });
@@ -4801,7 +5061,7 @@ async function createServerHandle(config3) {
4801
5061
  if (!project) return res.status(404).json({ error: "Project not found" });
4802
5062
  const { branch } = req.body;
4803
5063
  if (!branch || typeof branch !== "string") return res.status(400).json({ error: "branch is required" });
4804
- const { checkoutBranch } = await import("./worktree-EIF75WGF.js");
5064
+ const { checkoutBranch } = await import("./worktree-6U45P63P.js");
4805
5065
  try {
4806
5066
  await checkoutBranch(project.workdir, branch);
4807
5067
  res.json({ branch });
@@ -4815,7 +5075,7 @@ async function createServerHandle(config3) {
4815
5075
  if (!project) return res.status(404).json({ error: "Project not found" });
4816
5076
  const { name } = req.body;
4817
5077
  if (!name || typeof name !== "string") return res.status(400).json({ error: "name is required" });
4818
- const { createBranch } = await import("./worktree-EIF75WGF.js");
5078
+ const { createBranch } = await import("./worktree-6U45P63P.js");
4819
5079
  try {
4820
5080
  await createBranch(project.workdir, name);
4821
5081
  res.json({ branch: name });
@@ -4827,7 +5087,7 @@ async function createServerHandle(config3) {
4827
5087
  const { getProject: getProject2 } = await import("./projects-7HSBZY2D.js");
4828
5088
  const project = getProject2(req.params.id);
4829
5089
  if (!project) return res.status(404).json({ error: "Project not found" });
4830
- const { listWorktrees } = await import("./worktree-EIF75WGF.js");
5090
+ const { listWorktrees } = await import("./worktree-6U45P63P.js");
4831
5091
  const all = await listWorktrees(project.workdir);
4832
5092
  const worktrees = all.filter((wt) => wt.path !== project.workdir);
4833
5093
  res.json({ worktrees });
@@ -4902,7 +5162,7 @@ async function createServerHandle(config3) {
4902
5162
  app.get("/api/sessions/:id", async (req, res) => {
4903
5163
  const { getEventStore: getEventStore2 } = await import("./events-CDQ4HASP.js");
4904
5164
  const { buildMessagesFromStoredEvents } = await import("./folding-V2K34EGN.js");
4905
- const { getPendingQuestionsForSession } = await import("./tools-2EB7C6H2.js");
5165
+ const { getPendingQuestionsForSession } = await import("./tools-QAEN5KHA.js");
4906
5166
  const session = sessionManager.getSession(req.params.id);
4907
5167
  if (!session) {
4908
5168
  return res.status(404).json({ error: "Session not found" });
@@ -4921,8 +5181,8 @@ async function createServerHandle(config3) {
4921
5181
  if (!session) {
4922
5182
  return res.status(404).json({ error: "Session not found" });
4923
5183
  }
4924
- const { stopSessionExecution } = await import("./chat-handler-ZRDNSLOG.js");
4925
- const { cancelQuestionsForSession, cancelPathConfirmationsForSession } = await import("./tools-2EB7C6H2.js");
5184
+ const { stopSessionExecution } = await import("./chat-handler-QFEHJBWN.js");
5185
+ const { cancelQuestionsForSession, cancelPathConfirmationsForSession } = await import("./tools-QAEN5KHA.js");
4926
5186
  sessionManager.clearMessageQueue(sessionId);
4927
5187
  stopSessionExecution(sessionId, sessionManager);
4928
5188
  abortSession(sessionId);
@@ -5076,7 +5336,7 @@ async function createServerHandle(config3) {
5076
5336
  if (!callId || approved === void 0) {
5077
5337
  return res.status(400).json({ error: "callId and approved are required" });
5078
5338
  }
5079
- const { providePathConfirmation } = await import("./tools-2EB7C6H2.js");
5339
+ const { providePathConfirmation } = await import("./tools-QAEN5KHA.js");
5080
5340
  const result = providePathConfirmation(callId, approved, alwaysAllow);
5081
5341
  if (!result.found) {
5082
5342
  return res.status(404).json({ error: "No pending path confirmation with that ID" });
@@ -5084,7 +5344,7 @@ async function createServerHandle(config3) {
5084
5344
  const { getEventStore: getEventStore2 } = await import("./events-CDQ4HASP.js");
5085
5345
  const { buildMessagesFromStoredEvents, foldPendingConfirmations } = await import("./folding-V2K34EGN.js");
5086
5346
  const { createSessionStateMessage } = await import("./protocol-CTNE3ANP.js");
5087
- const { getPendingQuestionsForSession } = await import("./tools-2EB7C6H2.js");
5347
+ const { getPendingQuestionsForSession } = await import("./tools-QAEN5KHA.js");
5088
5348
  const eventStore = getEventStore2();
5089
5349
  const events = eventStore.getEvents(sessionId);
5090
5350
  const messages = buildMessagesFromStoredEvents(events);
@@ -5105,7 +5365,7 @@ async function createServerHandle(config3) {
5105
5365
  if (!skip && typeof answer !== "string") {
5106
5366
  return res.status(400).json({ error: "answer is required when not skipping" });
5107
5367
  }
5108
- const { provideAnswer } = await import("./tools-2EB7C6H2.js");
5368
+ const { provideAnswer } = await import("./tools-QAEN5KHA.js");
5109
5369
  const found = provideAnswer(callId, answer ?? "", skip ?? false);
5110
5370
  if (!found) {
5111
5371
  return res.status(404).json({ error: "No pending question with that ID" });
@@ -5133,7 +5393,7 @@ async function createServerHandle(config3) {
5133
5393
  if (!session) {
5134
5394
  return res.status(404).json({ error: "Session not found" });
5135
5395
  }
5136
- const { getSetting: getSetting2, SETTINGS_KEYS } = await import("./settings-ZZ6AKFPD.js");
5396
+ const { getSetting: getSetting2, SETTINGS_KEYS } = await import("./settings-6ZPWOBWL.js");
5137
5397
  if (getSetting2(SETTINGS_KEYS.CACHE_WARMING) !== "true") {
5138
5398
  return res.json({ success: false, reason: "disabled" });
5139
5399
  }
@@ -5165,7 +5425,7 @@ async function createServerHandle(config3) {
5165
5425
  backend: activeProvider?.backend ?? llmClient.getBackend(),
5166
5426
  model: llmClient.getModel()
5167
5427
  };
5168
- const { runAgentTurn, TurnMetrics } = await import("./orchestrator-LZQTDOWM.js");
5428
+ const { runAgentTurn, TurnMetrics } = await import("./orchestrator-GNXKJPX4.js");
5169
5429
  runAgentTurn(
5170
5430
  {
5171
5431
  sessionManager,
@@ -5203,8 +5463,8 @@ async function createServerHandle(config3) {
5203
5463
  if (!session) {
5204
5464
  return res.status(404).json({ error: "Session not found" });
5205
5465
  }
5206
- const { stopSessionExecution } = await import("./chat-handler-ZRDNSLOG.js");
5207
- const { cancelQuestionsForSession, cancelPathConfirmationsForSession } = await import("./tools-2EB7C6H2.js");
5466
+ const { stopSessionExecution } = await import("./chat-handler-QFEHJBWN.js");
5467
+ const { cancelQuestionsForSession, cancelPathConfirmationsForSession } = await import("./tools-QAEN5KHA.js");
5208
5468
  const queuedMessages = sessionManager.getQueueState(sessionId);
5209
5469
  sessionManager.clearMessageQueue(sessionId);
5210
5470
  stopSessionExecution(sessionId, sessionManager);
@@ -5284,7 +5544,7 @@ async function createServerHandle(config3) {
5284
5544
  res.json({ accepted: true });
5285
5545
  });
5286
5546
  app.get("/api/settings", async (req, res) => {
5287
- const { getSetting: getSetting2, SETTINGS_DEFAULTS } = await import("./settings-ZZ6AKFPD.js");
5547
+ const { getSetting: getSetting2, SETTINGS_DEFAULTS } = await import("./settings-6ZPWOBWL.js");
5288
5548
  const keysParam = req.query["keys"];
5289
5549
  if (!keysParam) {
5290
5550
  return res.status(400).json({ error: "keys query parameter is required" });
@@ -5297,19 +5557,19 @@ async function createServerHandle(config3) {
5297
5557
  res.json(result);
5298
5558
  });
5299
5559
  app.get("/api/settings/:key", async (req, res) => {
5300
- const { getSetting: getSetting2, SETTINGS_DEFAULTS } = await import("./settings-ZZ6AKFPD.js");
5560
+ const { getSetting: getSetting2, SETTINGS_DEFAULTS } = await import("./settings-6ZPWOBWL.js");
5301
5561
  const key = req.params.key;
5302
5562
  const value = getSetting2(key) ?? SETTINGS_DEFAULTS[key] ?? null;
5303
5563
  res.json({ key, value });
5304
5564
  });
5305
5565
  app.get("/api/settings/:key", async (req, res) => {
5306
- const { getSetting: getSetting2, SETTINGS_DEFAULTS } = await import("./settings-ZZ6AKFPD.js");
5566
+ const { getSetting: getSetting2, SETTINGS_DEFAULTS } = await import("./settings-6ZPWOBWL.js");
5307
5567
  const key = req.params.key;
5308
5568
  const value = getSetting2(key) ?? SETTINGS_DEFAULTS[key] ?? null;
5309
5569
  res.json({ key, value });
5310
5570
  });
5311
5571
  app.put("/api/settings/:key", async (req, res) => {
5312
- const { setSetting: setSetting2 } = await import("./settings-ZZ6AKFPD.js");
5572
+ const { setSetting: setSetting2 } = await import("./settings-6ZPWOBWL.js");
5313
5573
  const key = req.params.key;
5314
5574
  const { value } = req.body;
5315
5575
  if (value === void 0) {
@@ -5321,20 +5581,24 @@ async function createServerHandle(config3) {
5321
5581
  app.get("/api/tools/rtk-check", async (_req, res) => {
5322
5582
  const { spawn: spawn3 } = await import("child_process");
5323
5583
  try {
5324
- const available = await new Promise((resolve6) => {
5584
+ const available = await new Promise((resolve8) => {
5325
5585
  const proc = spawn3("rtk", ["--version"], { stdio: ["ignore", "pipe", "pipe"] });
5326
5586
  let out = "";
5327
5587
  proc.stdout?.on("data", (d) => {
5328
5588
  out += d.toString();
5329
5589
  });
5330
- proc.on("error", () => resolve6(false));
5331
- proc.on("close", (code) => resolve6(code === 0 && out.startsWith("rtk ")));
5590
+ proc.on("error", () => resolve8(false));
5591
+ proc.on("close", (code) => resolve8(code === 0 && out.startsWith("rtk ")));
5332
5592
  });
5333
5593
  res.json({ available });
5334
5594
  } catch {
5335
5595
  res.json({ available: false });
5336
5596
  }
5337
5597
  });
5598
+ app.get("/api/tools/shells", async (_req, res) => {
5599
+ const { listAvailableShells } = await import("./platform-AB3GIEGO.js");
5600
+ res.json({ shells: listAvailableShells() });
5601
+ });
5338
5602
  app.get("/api/config", async (_req, res) => {
5339
5603
  const llmClient = getLLMClient();
5340
5604
  const activeProvider = providerManager.getActiveProvider();
@@ -5494,7 +5758,7 @@ async function createServerHandle(config3) {
5494
5758
  const backend = req.query["backend"];
5495
5759
  if (!url) return res.status(400).json({ error: "url is required" });
5496
5760
  try {
5497
- const { fetchModelsWithContext } = await import("./provider-manager-D3KIOD5E.js");
5761
+ const { fetchModelsWithContext } = await import("./provider-manager-7C3JVOLW.js");
5498
5762
  const { getModelProfile: getModelProfile2 } = await import("./profiles-5J7F4BML.js");
5499
5763
  const models = await fetchModelsWithContext(
5500
5764
  url,
@@ -5564,7 +5828,7 @@ async function createServerHandle(config3) {
5564
5828
  }
5565
5829
  const { getModelProfile: getModelProfile2 } = await import("./profiles-5J7F4BML.js");
5566
5830
  const { getBackendCapabilities } = await import("./backend-P3GHHLHJ.js");
5567
- const { buildNonStreamingCreateParams } = await import("./client-pure-U6ZYUPJQ.js");
5831
+ const { buildNonStreamingCreateParams } = await import("./client-pure-XHH4RRLB.js");
5568
5832
  const { OpenAIHttpClient } = await import("./http-client-PIWB7BXK.js");
5569
5833
  const { ensureVersionPrefix } = await import("./url-utils-SAJP7AEX.js");
5570
5834
  const profile = getModelProfile2(model);
@@ -5690,6 +5954,7 @@ async function createServerHandle(config3) {
5690
5954
  });
5691
5955
  }
5692
5956
  });
5957
+ app.use("/api/plugins", createPluginRoutes({ config: config3, providerAdapters, pluginDiagnostics, logger }));
5693
5958
  app.get("/api/plugins", (_req, res) => res.json({ plugins: pluginDiagnostics }));
5694
5959
  app.get("/api/provider-presets", (_req, res) => res.json({ presets: providerAdapters.getPresets() }));
5695
5960
  app.get(
@@ -5822,7 +6087,7 @@ async function createServerHandle(config3) {
5822
6087
  });
5823
6088
  async function rebuildMcpTools() {
5824
6089
  const { createMcpTools: createMcpTools2 } = await import("./tool-adapter-MOGEI6TP.js");
5825
- const { setMcpTools: setMcpTools2 } = await import("./tools-2EB7C6H2.js");
6090
+ const { setMcpTools: setMcpTools2 } = await import("./tools-QAEN5KHA.js");
5826
6091
  const mcpTools = createMcpTools2(mcpManager);
5827
6092
  setMcpTools2(mcpTools);
5828
6093
  }
@@ -5977,6 +6242,7 @@ async function createServerHandle(config3) {
5977
6242
  app.use("/api/agents", createAgentRoutes(configDir, projectDir));
5978
6243
  app.use("/api/workflows", createWorkflowRoutes(configDir, config3, projectDir));
5979
6244
  app.use("/api/dev-server", createDevServerRoutes());
6245
+ app.use("/api/worktree", createWorktreeConfigRoutes());
5980
6246
  app.use("/api/terminals", createTerminalRoutes());
5981
6247
  app.use(
5982
6248
  "/api/auto-update",
@@ -5993,13 +6259,13 @@ async function createServerHandle(config3) {
5993
6259
  })
5994
6260
  );
5995
6261
  app.get("/api/sessions/:id/background-processes", async (req, res) => {
5996
- const { getSessionProcesses } = await import("./manager-344C5JLZ.js");
6262
+ const { getSessionProcesses } = await import("./manager-JXYRGR6B.js");
5997
6263
  const sessionId = req.params.id;
5998
6264
  const processes = getSessionProcesses(sessionId);
5999
6265
  res.json({ processes });
6000
6266
  });
6001
6267
  app.post("/api/sessions/:id/background-process/:processId/stop", async (req, res) => {
6002
- const { stopProcess } = await import("./manager-344C5JLZ.js");
6268
+ const { stopProcess } = await import("./manager-JXYRGR6B.js");
6003
6269
  const sessionId = req.params.id;
6004
6270
  const processId = req.params.processId;
6005
6271
  const session = sessionManager.getSession(sessionId);
@@ -6019,14 +6285,14 @@ async function createServerHandle(config3) {
6019
6285
  });
6020
6286
  app.use("/api/directories", createDirectoryRoutes());
6021
6287
  app.use("/api/files", createFileSearchRoutes());
6022
- const webDir = resolve5(__dirname3, "../../web");
6288
+ const webDir = resolve7(__dirname3, "../../web");
6023
6289
  const isDev = config3.mode === "development";
6024
6290
  let viteServer;
6025
6291
  if (isDev) {
6026
6292
  logger.info("Dev mode: using Vite middleware");
6027
6293
  viteServer = await createViteServer({
6028
6294
  root: webDir,
6029
- configFile: resolve5(__dirname3, "../../web/vite.config.ts"),
6295
+ configFile: resolve7(__dirname3, "../../web/vite.config.ts"),
6030
6296
  server: { middlewareMode: true },
6031
6297
  appType: "spa",
6032
6298
  logLevel: "warn"
@@ -6046,7 +6312,7 @@ async function createServerHandle(config3) {
6046
6312
  }
6047
6313
  });
6048
6314
  app.get("/fox.svg", (_req, res) => {
6049
- readFile3(join8(webDir, "fox.svg")).then((content) => {
6315
+ readFile5(join10(webDir, "fox.svg")).then((content) => {
6050
6316
  res.set("Content-Type", "image/svg+xml");
6051
6317
  res.send(content);
6052
6318
  }).catch(() => {
@@ -6055,7 +6321,7 @@ async function createServerHandle(config3) {
6055
6321
  });
6056
6322
  app.use(
6057
6323
  "/sounds",
6058
- express.static(join8(webDir, "public", "sounds"), {
6324
+ express.static(join10(webDir, "public", "sounds"), {
6059
6325
  setHeaders: (res) => {
6060
6326
  res.set("Content-Type", "audio/mpeg");
6061
6327
  }
@@ -6064,13 +6330,13 @@ async function createServerHandle(config3) {
6064
6330
  app.use("/__inspect__.js", (_req, res) => {
6065
6331
  res.set("Access-Control-Allow-Origin", "*");
6066
6332
  res.set("Content-Type", "application/javascript");
6067
- readFile3(join8(webDir, "public", "__inspect__.js")).then((content) => res.send(content)).catch(() => res.status(404).send("Not found"));
6333
+ readFile5(join10(webDir, "public", "__inspect__.js")).then((content) => res.send(content)).catch(() => res.status(404).send("Not found"));
6068
6334
  });
6069
6335
  app.get("/*path", (req, res) => {
6070
6336
  if (req.path.startsWith("/api/")) {
6071
6337
  return;
6072
6338
  }
6073
- readFile3(join8(webDir, "index.html"), "utf-8").then((indexHtml) => viteServer.transformIndexHtml(req.originalUrl, indexHtml)).then((transformed) => res.send(transformed)).catch((err) => {
6339
+ readFile5(join10(webDir, "index.html"), "utf-8").then((indexHtml) => viteServer.transformIndexHtml(req.originalUrl, indexHtml)).then((transformed) => res.send(transformed)).catch((err) => {
6074
6340
  logger.error("Error serving index.html", { error: err });
6075
6341
  res.status(500).send("Server error");
6076
6342
  });
@@ -6078,10 +6344,10 @@ async function createServerHandle(config3) {
6078
6344
  logger.info("Vite middleware ready", { port: config3.server.port });
6079
6345
  }
6080
6346
  if (!isDev) {
6081
- const distWebDir = resolve5(__dirname3, "web");
6347
+ const distWebDir = resolve7(__dirname3, "web");
6082
6348
  app.use(
6083
6349
  "/assets",
6084
- express.static(join8(distWebDir, "assets"), {
6350
+ express.static(join10(distWebDir, "assets"), {
6085
6351
  setHeaders: (res, filepath) => {
6086
6352
  if (filepath.endsWith(".css")) {
6087
6353
  res.set("Content-Type", "text/css");
@@ -6089,11 +6355,11 @@ async function createServerHandle(config3) {
6089
6355
  }
6090
6356
  })
6091
6357
  );
6092
- app.use("/manifest.webmanifest", express.static(join8(distWebDir, "manifest.webmanifest")));
6093
- app.use("/registerSW.js", express.static(join8(distWebDir, "registerSW.js")));
6094
- app.use("/sw.js", express.static(join8(distWebDir, "sw.js")));
6358
+ app.use("/manifest.webmanifest", express.static(join10(distWebDir, "manifest.webmanifest")));
6359
+ app.use("/registerSW.js", express.static(join10(distWebDir, "registerSW.js")));
6360
+ app.use("/sw.js", express.static(join10(distWebDir, "sw.js")));
6095
6361
  app.get("/fox.svg", (_req, res) => {
6096
- readFile3(join8(distWebDir, "fox.svg")).then((content) => {
6362
+ readFile5(join10(distWebDir, "fox.svg")).then((content) => {
6097
6363
  res.set("Content-Type", "image/svg+xml");
6098
6364
  res.send(content);
6099
6365
  }).catch(() => {
@@ -6102,7 +6368,7 @@ async function createServerHandle(config3) {
6102
6368
  });
6103
6369
  app.use(
6104
6370
  "/sounds",
6105
- express.static(join8(distWebDir, "sounds"), {
6371
+ express.static(join10(distWebDir, "sounds"), {
6106
6372
  setHeaders: (res) => {
6107
6373
  res.set("Content-Type", "audio/mpeg");
6108
6374
  }
@@ -6111,10 +6377,10 @@ async function createServerHandle(config3) {
6111
6377
  app.use("/__inspect__.js", (_req, res) => {
6112
6378
  res.set("Access-Control-Allow-Origin", "*");
6113
6379
  res.set("Content-Type", "application/javascript");
6114
- readFile3(join8(distWebDir, "__inspect__.js")).then((content) => res.send(content)).catch(() => res.status(404).send("Not found"));
6380
+ readFile5(join10(distWebDir, "__inspect__.js")).then((content) => res.send(content)).catch(() => res.status(404).send("Not found"));
6115
6381
  });
6116
6382
  app.get("/", (_req, res) => {
6117
- readFile3(join8(distWebDir, "index.html"), "utf-8").then((content) => res.send(content)).catch(() => {
6383
+ readFile5(join10(distWebDir, "index.html"), "utf-8").then((content) => res.send(content)).catch(() => {
6118
6384
  res.status(404).send("Web UI not built. Run `npm run build:web`");
6119
6385
  });
6120
6386
  });
@@ -6122,7 +6388,7 @@ async function createServerHandle(config3) {
6122
6388
  if (req.path.startsWith("/api/") || req.path.startsWith("/assets/") || req.path.startsWith("/sounds/") || req.path.startsWith("/manifest.webmanifest") || req.path.startsWith("/registerSW.js") || req.path.startsWith("/sw.js") || req.path === "/fox.svg") {
6123
6389
  return;
6124
6390
  }
6125
- readFile3(join8(distWebDir, "index.html"), "utf-8").then((content) => res.send(content)).catch(() => {
6391
+ readFile5(join10(distWebDir, "index.html"), "utf-8").then((content) => res.send(content)).catch(() => {
6126
6392
  res.status(404).send("Web UI not built");
6127
6393
  });
6128
6394
  });
@@ -6142,7 +6408,7 @@ async function createServerHandle(config3) {
6142
6408
  const state = sessionManager.getContextState(sessionId);
6143
6409
  wssExports.broadcastForSession(sessionId, createContextStateMessage(state));
6144
6410
  });
6145
- const { QueueProcessor } = await import("./processor-7KAXESY5.js");
6411
+ const { QueueProcessor } = await import("./processor-TIQEQDP7.js");
6146
6412
  const queueProcessor = new QueueProcessor({
6147
6413
  sessionManager,
6148
6414
  providerManager,
@@ -6165,7 +6431,7 @@ async function createServerHandle(config3) {
6165
6431
  return {
6166
6432
  httpServer,
6167
6433
  ctx: { config: config3, sessionManager, llmClient: getLLMClient(), toolRegistry, providerManager },
6168
- start: (port) => new Promise((resolve6, reject) => {
6434
+ start: (port) => new Promise((resolve8, reject) => {
6169
6435
  const listenPort = port ?? config3.server.port;
6170
6436
  const host = config3.server.host;
6171
6437
  httpServer.listen(listenPort, host, () => {
@@ -6175,16 +6441,16 @@ async function createServerHandle(config3) {
6175
6441
  logger.info(`OpenFox server running at http://${host}:${actualPort}`);
6176
6442
  logger.info(`WebSocket available at ws://${host}:${actualPort}/ws`);
6177
6443
  logger.info(`LLM backend: ${client.getBackend()}, model: ${client.getModel()}, url: ${config3.llm.baseUrl}`);
6178
- resolve6({ port: actualPort });
6444
+ resolve8({ port: actualPort });
6179
6445
  });
6180
6446
  httpServer.on("error", reject);
6181
6447
  }),
6182
- close: () => new Promise((resolve6) => {
6448
+ close: () => new Promise((resolve8) => {
6183
6449
  logger.info("Shutting down...");
6184
6450
  void (async () => {
6185
6451
  await devServerManager.stopAll();
6186
6452
  await mcpManager.disconnectAll();
6187
- const { stopAllInspectProxies } = await import("./inspect-proxy-PKMDR47R.js");
6453
+ const { stopAllInspectProxies } = await import("./inspect-proxy-OVEFBJ26.js");
6188
6454
  stopAllInspectProxies();
6189
6455
  const { cleanupAllProcesses } = await import("./store-FXTCHBI4.js");
6190
6456
  cleanupAllProcesses();
@@ -6198,7 +6464,7 @@ async function createServerHandle(config3) {
6198
6464
  client.terminate();
6199
6465
  }
6200
6466
  wss.close();
6201
- httpServer.close(() => resolve6());
6467
+ httpServer.close(() => resolve8());
6202
6468
  })();
6203
6469
  })
6204
6470
  };
@@ -6223,4 +6489,4 @@ export {
6223
6489
  createServerHandle,
6224
6490
  createServer
6225
6491
  };
6226
- //# sourceMappingURL=chunk-OE5B44P6.js.map
6492
+ //# sourceMappingURL=chunk-DWZEGLHJ.js.map