openfox 2.0.67 → 2.0.69

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 (40) hide show
  1. package/dist/{chat-handler-72DFHK5K.js → chat-handler-K3DWIGHG.js} +7 -6
  2. package/dist/{chunk-B6JUWVTK.js → chunk-46OR4W3L.js} +7 -7
  3. package/dist/{chunk-TMHWQYWE.js → chunk-64GN6OPR.js} +2 -2
  4. package/dist/{chunk-5EO6SEGV.js → chunk-7OW6X764.js} +2 -2
  5. package/dist/{chunk-B7WGOK7G.js → chunk-E4POUD2M.js} +2 -2
  6. package/dist/{chunk-BL5LOLVA.js → chunk-JHGKXQBO.js} +161 -28
  7. package/dist/{chunk-YU6DZAVA.js → chunk-PVWAE43V.js} +55 -100
  8. package/dist/{chunk-TJO4QFDQ.js → chunk-QFSDJIUS.js} +2 -2
  9. package/dist/chunk-UN26GIQG.js +54 -0
  10. package/dist/{chunk-PYRH4G2J.js → chunk-UTSSFPYA.js} +5 -5
  11. package/dist/{chunk-MQHMT5WF.js → chunk-VFYNKALW.js} +3 -3
  12. package/dist/{chunk-2GRUQN2B.js → chunk-WHVCGVQX.js} +384 -68
  13. package/dist/{chunk-ZLON3JTI.js → chunk-YQ3MEZJL.js} +2 -2
  14. package/dist/cli/dev.js +1 -1
  15. package/dist/cli/index.js +1 -1
  16. package/dist/{client-BUX62J2K.js → client-63U5CTPE.js} +3 -3
  17. package/dist/{client-pure-U6ZYUPJQ.js → client-pure-XHH4RRLB.js} +2 -2
  18. package/dist/{compactor-74CZVUWI.js → compactor-IPMMFFHM.js} +4 -4
  19. package/dist/{orchestrator-FEMPJOZD.js → orchestrator-PUV4WG77.js} +6 -5
  20. package/dist/package.json +3 -2
  21. package/dist/{processor-QB3NMRMO.js → processor-O2QW27BD.js} +7 -6
  22. package/dist/{protocol-42f_Png6.d.ts → protocol-DIhV-tAc.d.ts} +1 -1
  23. package/dist/provider/index.d.ts +4 -4
  24. package/dist/{provider-BDH6JGZB.js → provider-IBIWR4AI.js} +4 -4
  25. package/dist/{provider-manager-D3KIOD5E.js → provider-manager-7C3JVOLW.js} +4 -4
  26. package/dist/{serve-32FAA4FX.js → serve-Q34XBLR5.js} +11 -10
  27. package/dist/server/index.d.ts +3 -3
  28. package/dist/server/index.js +10 -9
  29. package/dist/{server-OYE7GERR.js → server-RLNPHLY2.js} +8 -7
  30. package/dist/shared/index.d.ts +3 -3
  31. package/dist/{tools-INWP6FGV.js → tools-CALGWIV2.js} +5 -4
  32. package/dist/{types-vezAEt_y.d.ts → types-BsfYpEai.d.ts} +2 -1
  33. package/dist/{types-apiMd7Df.d.ts → types-CBC6lLqc.d.ts} +1 -1
  34. package/dist/update-server-MYYQUNZX.js +10 -0
  35. package/dist/web/assets/{index-BELeAs12.css → index-BVTG7SRG.css} +1 -1
  36. package/dist/web/assets/{index-C3n65CNu.js → index-oKJVe9Cz.js} +75 -73
  37. package/dist/web/index.html +2 -2
  38. package/dist/web/sw.js +1 -1
  39. package/package.json +3 -2
  40. package/plugins-registry.json +14 -0
@@ -21,7 +21,7 @@ import {
21
21
  tokenFromPassword,
22
22
  verifyPassword,
23
23
  workflowExists
24
- } from "./chunk-B6JUWVTK.js";
24
+ } from "./chunk-46OR4W3L.js";
25
25
  import {
26
26
  agentExists,
27
27
  createToolRegistry,
@@ -66,12 +66,12 @@ import {
66
66
  setSkillEnabled,
67
67
  skillExists,
68
68
  updateOwnedSkill
69
- } from "./chunk-YU6DZAVA.js";
69
+ } from "./chunk-PVWAE43V.js";
70
70
  import {
71
71
  ALWAYS_ALLOWED,
72
72
  ALWAYS_ALLOWED_FOR_SUBAGENTS,
73
73
  TOP_LEVEL_ONLY_TOOLS
74
- } from "./chunk-TJO4QFDQ.js";
74
+ } from "./chunk-QFSDJIUS.js";
75
75
  import {
76
76
  getProject
77
77
  } from "./chunk-KXVFHMSM.js";
@@ -133,7 +133,7 @@ import {
133
133
  detectModel,
134
134
  getLlmStatus,
135
135
  parseDefaultModelSelection
136
- } from "./chunk-5EO6SEGV.js";
136
+ } from "./chunk-7OW6X764.js";
137
137
  import {
138
138
  getModelProfile
139
139
  } from "./chunk-V4IE7HJY.js";
@@ -161,9 +161,9 @@ import {
161
161
  import express from "express";
162
162
  import cors from "cors";
163
163
  import { createServer as createHttpServer } from "http";
164
- import { fileURLToPath as fileURLToPath6 } from "url";
165
- import { dirname as dirname7, resolve as resolve6, join as join9 } from "path";
166
- import { readFile as readFile4 } 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";
167
167
  import { createServer as createViteServer } from "vite";
168
168
 
169
169
  // src/server/llm/mock.ts
@@ -1733,7 +1733,9 @@ var LspServer = class {
1733
1733
  }
1734
1734
  this.state = "starting";
1735
1735
  try {
1736
- this.process = spawn(this.commandPath, this.config.serverArgs, {
1736
+ const needsShell = process.platform === "win32" && /\.(cmd|bat)$/i.test(this.commandPath);
1737
+ const command = needsShell ? [`"${this.commandPath}"`, ...this.config.serverArgs].join(" ") : this.commandPath;
1738
+ this.process = spawn(command, needsShell ? [] : this.config.serverArgs, {
1737
1739
  cwd: this.workdir,
1738
1740
  stdio: ["pipe", "pipe", "pipe"],
1739
1741
  env: {
@@ -1741,11 +1743,21 @@ var LspServer = class {
1741
1743
  HOME: process.env["HOME"],
1742
1744
  PATH: process.env["PATH"]
1743
1745
  },
1744
- windowsHide: true
1746
+ windowsHide: true,
1747
+ shell: needsShell
1748
+ });
1749
+ await new Promise((resolve8, reject) => {
1750
+ this.process.once("spawn", resolve8);
1751
+ this.process.once("error", reject);
1745
1752
  });
1746
1753
  if (!this.process.stdin || !this.process.stdout) {
1747
1754
  throw new Error("Failed to get stdio streams from language server process");
1748
1755
  }
1756
+ const onStreamError = (err) => {
1757
+ logger.debug("LSP stream error", { language: this.config.id, error: err.message });
1758
+ };
1759
+ this.process.stdin.on("error", onStreamError);
1760
+ this.process.stdout.on("error", onStreamError);
1749
1761
  this.connection = createMessageConnection(
1750
1762
  new StreamMessageReader(this.process.stdout),
1751
1763
  new StreamMessageWriter(this.process.stdin)
@@ -1955,19 +1967,19 @@ var LspServer = class {
1955
1967
  */
1956
1968
  async getDiagnosticsWithWait(path, timeoutMs = DIAGNOSTIC_WAIT_MS) {
1957
1969
  if (this.openDocuments.has(path)) {
1958
- return new Promise((resolve7) => {
1970
+ return new Promise((resolve8) => {
1959
1971
  const existing = this.diagnostics.get(path);
1960
1972
  if (existing !== void 0) {
1961
1973
  setTimeout(() => {
1962
- resolve7(this.diagnostics.get(path) ?? []);
1974
+ resolve8(this.diagnostics.get(path) ?? []);
1963
1975
  }, 100);
1964
1976
  return;
1965
1977
  }
1966
1978
  const timeout = setTimeout(() => {
1967
1979
  this.pendingDiagnostics.delete(path);
1968
- resolve7(this.diagnostics.get(path) ?? []);
1980
+ resolve8(this.diagnostics.get(path) ?? []);
1969
1981
  }, timeoutMs);
1970
- this.pendingDiagnostics.set(path, { resolve: resolve7, timeout });
1982
+ this.pendingDiagnostics.set(path, { resolve: resolve8, timeout });
1971
1983
  });
1972
1984
  }
1973
1985
  return this.diagnostics.get(path) ?? [];
@@ -2196,6 +2208,21 @@ async function existsExecutable(path) {
2196
2208
  return false;
2197
2209
  }
2198
2210
  }
2211
+ function candidateNames(command) {
2212
+ if (process.platform !== "win32") {
2213
+ return [command];
2214
+ }
2215
+ return [".cmd", ".exe", ".bat", ""].map((ext) => command + ext);
2216
+ }
2217
+ async function findInDir(dir, command) {
2218
+ for (const name of candidateNames(command)) {
2219
+ const fullPath = join2(dir, name);
2220
+ if (await existsExecutable(fullPath)) {
2221
+ return fullPath;
2222
+ }
2223
+ }
2224
+ return null;
2225
+ }
2199
2226
  async function which(command, workdir) {
2200
2227
  if (isAbsolutePath(command)) {
2201
2228
  if (await existsExecutable(command)) {
@@ -2204,24 +2231,24 @@ async function which(command, workdir) {
2204
2231
  return null;
2205
2232
  }
2206
2233
  for (const binDir of getBundledBinPaths()) {
2207
- const fullPath = join2(binDir, command);
2208
- if (await existsExecutable(fullPath)) {
2209
- return fullPath;
2234
+ const found = await findInDir(binDir, command);
2235
+ if (found) {
2236
+ return found;
2210
2237
  }
2211
2238
  }
2212
2239
  if (workdir) {
2213
- const projectBin = join2(workdir, "node_modules", ".bin", command);
2214
- if (await existsExecutable(projectBin)) {
2215
- return projectBin;
2240
+ const found = await findInDir(join2(workdir, "node_modules", ".bin"), command);
2241
+ if (found) {
2242
+ return found;
2216
2243
  }
2217
2244
  }
2218
2245
  const pathEnv = process.env["PATH"] ?? "";
2219
2246
  const paths = pathEnv.split(getPathSeparator());
2220
2247
  for (const dir of paths) {
2221
2248
  if (!dir) continue;
2222
- const fullPath = join2(dir, command);
2223
- if (await existsExecutable(fullPath)) {
2224
- return fullPath;
2249
+ const found = await findInDir(dir, command);
2250
+ if (found) {
2251
+ return found;
2225
2252
  }
2226
2253
  }
2227
2254
  return null;
@@ -4257,7 +4284,7 @@ import { Router as Router9 } from "express";
4257
4284
  import { spawn as spawn2 } from "child_process";
4258
4285
 
4259
4286
  // src/constants.ts
4260
- var VERSION = "2.0.67";
4287
+ var VERSION = "2.0.69";
4261
4288
 
4262
4289
  // src/server/routes/auto-update.ts
4263
4290
  var updateInProgress = false;
@@ -4280,7 +4307,7 @@ function createAutoUpdateRoutes(options = {}) {
4280
4307
  const isService = isRunningAsService();
4281
4308
  const current = VERSION;
4282
4309
  try {
4283
- const latest = await new Promise((resolve7, reject) => {
4310
+ const latest = await new Promise((resolve8, reject) => {
4284
4311
  const child = spawn2("npm", ["view", "openfox", "version"], {
4285
4312
  stdio: ["ignore", "pipe", "pipe"],
4286
4313
  windowsHide: true
@@ -4291,7 +4318,7 @@ function createAutoUpdateRoutes(options = {}) {
4291
4318
  });
4292
4319
  child.on("close", (code) => {
4293
4320
  if (code === 0) {
4294
- resolve7(stdout.trim());
4321
+ resolve8(stdout.trim());
4295
4322
  } else {
4296
4323
  reject(new Error(`npm view exited with code ${code}`));
4297
4324
  }
@@ -4335,9 +4362,9 @@ function createAutoUpdateRoutes(options = {}) {
4335
4362
  const timeout = setTimeout(() => {
4336
4363
  child.kill();
4337
4364
  }, UPDATE_TIMEOUT);
4338
- const exitCode = await new Promise((resolve7) => {
4339
- child.on("close", resolve7);
4340
- child.on("error", () => resolve7(1));
4365
+ const exitCode = await new Promise((resolve8) => {
4366
+ child.on("close", resolve8);
4367
+ child.on("error", () => resolve8(1));
4341
4368
  });
4342
4369
  clearTimeout(timeout);
4343
4370
  if (exitCode === 0) {
@@ -4500,7 +4527,9 @@ var ProviderRegistry = class {
4500
4527
  }
4501
4528
  register(map, id, value, kind) {
4502
4529
  if (!id.trim()) throw new Error(`Provider ${kind} id cannot be empty`);
4503
- if (map.has(id)) throw new Error(`Provider ${kind} already registered: ${id}`);
4530
+ if (map.has(id)) {
4531
+ map.delete(id);
4532
+ }
4504
4533
  map.set(id, value);
4505
4534
  }
4506
4535
  };
@@ -4599,8 +4628,199 @@ async function loadProviderPlugins(options) {
4599
4628
  return diagnostics;
4600
4629
  }
4601
4630
 
4631
+ // src/server/routes/plugins.ts
4632
+ import { Router as Router11 } from "express";
4633
+ import { fileURLToPath as fileURLToPath6, pathToFileURL as pathToFileURL2 } from "url";
4634
+ import { dirname as dirname7, resolve as resolve6, join as join9 } from "path";
4635
+ import { readFile as readFile4, readdir as readdir3, rm as rm2, rename as rename2 } from "fs/promises";
4636
+ import { platform } from "os";
4637
+ import { execFile } from "child_process";
4638
+ import { promisify } from "util";
4639
+ var execFileP = promisify(execFile);
4640
+ async function openFolder(dir, res) {
4641
+ try {
4642
+ await execFileP("mkdir", ["-p", dir], { timeout: 5e3 });
4643
+ const cmd = platform() === "darwin" ? "open" : platform() === "win32" ? "explorer" : "xdg-open";
4644
+ await execFileP(cmd, [dir], { timeout: 5e3 });
4645
+ res.json({ success: true });
4646
+ } catch (err) {
4647
+ res.status(500).json({ error: err instanceof Error ? err.message : "Failed to open folder" });
4648
+ }
4649
+ }
4650
+ function createPluginRoutes(options) {
4651
+ const router = Router11();
4652
+ const { config: config3, providerAdapters, pluginDiagnostics, logger: logger2 } = options;
4653
+ let registryCache = null;
4654
+ router.get("/registry", async (_req, res) => {
4655
+ try {
4656
+ const now = Date.now();
4657
+ if (registryCache && now - registryCache.ts < 3e5) {
4658
+ return res.json({ plugins: registryCache.data });
4659
+ }
4660
+ const moduleDir = dirname7(fileURLToPath6(import.meta.url));
4661
+ const registryPath = resolve6(moduleDir, "../../../plugins-registry.json");
4662
+ const data = JSON.parse(await readFile4(registryPath, "utf8"));
4663
+ registryCache = { data, ts: now };
4664
+ res.json({ plugins: data });
4665
+ } catch (err) {
4666
+ logger2.error("Failed to load plugin registry", { error: String(err) });
4667
+ res.json({ plugins: [] });
4668
+ }
4669
+ });
4670
+ router.post("/install", async (req, res) => {
4671
+ const { githubUrl } = req.body;
4672
+ if (!githubUrl || typeof githubUrl !== "string") {
4673
+ return res.status(400).json({ error: "githubUrl is required" });
4674
+ }
4675
+ const parsed = githubUrl.match(/github\.com\/([^/]+)\/([^/]+?)(?:\/|$)/);
4676
+ if (!parsed) {
4677
+ return res.status(400).json({ error: "Invalid GitHub URL" });
4678
+ }
4679
+ const repoName = parsed[2].replace(/\.git$/, "");
4680
+ if (!/^[a-zA-Z0-9_-]+$/.test(repoName)) {
4681
+ return res.status(400).json({ error: "Invalid repository name" });
4682
+ }
4683
+ const pluginsDir = join9(getGlobalConfigDir(config3.mode ?? "production"), "plugins");
4684
+ const targetDir = join9(pluginsDir, repoName);
4685
+ const tmpDir = join9(pluginsDir, `.${repoName}-tmp-${Date.now()}`);
4686
+ try {
4687
+ await execFileP("mkdir", ["-p", pluginsDir], { timeout: 5e3 });
4688
+ } catch {
4689
+ return res.status(500).json({ error: "Failed to create plugins directory" });
4690
+ }
4691
+ let gitOk = false;
4692
+ try {
4693
+ const { stdout } = await execFileP("git", ["--version"], { timeout: 5e3 });
4694
+ gitOk = stdout.includes("git version");
4695
+ } catch {
4696
+ }
4697
+ if (!gitOk) {
4698
+ return res.status(500).json({ error: "git is not installed or not found in PATH" });
4699
+ }
4700
+ try {
4701
+ const cloneUrl = githubUrl.replace(/\/$/, "") + ".git";
4702
+ await execFileP("git", ["clone", "--depth", "1", cloneUrl, tmpDir], { timeout: 6e4 });
4703
+ await rm2(targetDir, { recursive: true, force: true });
4704
+ await rename2(tmpDir, targetDir);
4705
+ let loaded = false;
4706
+ let loadError;
4707
+ try {
4708
+ await execFileP("npm", ["install", "--no-audit", "--no-fund"], { cwd: targetDir, timeout: 12e4 });
4709
+ await execFileP("npm", ["run", "build"], { cwd: targetDir, timeout: 12e4 });
4710
+ } catch (err) {
4711
+ loadError = "Failed to install/build plugin dependencies";
4712
+ logger2.error("Plugin build failed", { repoName, error: String(err) });
4713
+ }
4714
+ if (!loadError) {
4715
+ try {
4716
+ const manifest = JSON.parse(await readFile4(join9(targetDir, "package.json"), "utf8"));
4717
+ const pluginEntry = manifest.openfox?.plugin;
4718
+ const apiVersion = manifest.openfox?.apiVersion;
4719
+ if (!pluginEntry || !manifest.name) {
4720
+ loadError = "Plugin package.json is missing openfox.plugin or name field";
4721
+ } else if (apiVersion !== 1) {
4722
+ loadError = `Unsupported plugin API version: ${String(apiVersion)}`;
4723
+ } else {
4724
+ const mod = await import(pathToFileURL2(join9(targetDir, pluginEntry)).href);
4725
+ if (typeof mod.register !== "function") {
4726
+ loadError = "Plugin does not export register(registry)";
4727
+ } else {
4728
+ const diagnostic = {
4729
+ packageName: manifest.name,
4730
+ version: manifest.version,
4731
+ source: targetDir,
4732
+ loaded: false,
4733
+ authAdapters: [],
4734
+ transportAdapters: [],
4735
+ presets: []
4736
+ };
4737
+ const trackingRegistry = {
4738
+ runtime: providerAdapters.runtime,
4739
+ registerAuth(adapter) {
4740
+ providerAdapters.registerAuth(adapter);
4741
+ diagnostic.authAdapters.push(adapter.id);
4742
+ },
4743
+ registerTransport(adapter) {
4744
+ providerAdapters.registerTransport(adapter);
4745
+ diagnostic.transportAdapters.push(adapter.id);
4746
+ },
4747
+ registerPreset(preset) {
4748
+ providerAdapters.registerPreset(preset);
4749
+ diagnostic.presets.push(preset.id);
4750
+ }
4751
+ };
4752
+ await mod.register(trackingRegistry);
4753
+ diagnostic.loaded = true;
4754
+ pluginDiagnostics.push(diagnostic);
4755
+ loaded = true;
4756
+ config3.providers = providerAdapters.resolveProviders(config3.providers ?? []);
4757
+ }
4758
+ }
4759
+ } catch (err) {
4760
+ loadError = err instanceof Error ? err.message : "Failed to load plugin";
4761
+ logger2.error("Plugin runtime load failed", { repoName, error: loadError });
4762
+ }
4763
+ }
4764
+ res.json({ success: true, loaded, loadError, path: targetDir });
4765
+ } catch (err) {
4766
+ await rm2(tmpDir, { recursive: true, force: true });
4767
+ const msg = err instanceof Error ? err.message : "Clone failed";
4768
+ logger2.error("Plugin install failed", { githubUrl, error: msg });
4769
+ res.status(500).json({ error: msg });
4770
+ }
4771
+ });
4772
+ router.get("/installed", async (_req, res) => {
4773
+ const pluginsDir = join9(getGlobalConfigDir(config3.mode ?? "production"), "plugins");
4774
+ try {
4775
+ const entries = await readdir3(pluginsDir, { withFileTypes: true });
4776
+ const installed = [];
4777
+ for (const entry of entries) {
4778
+ if (!entry.isDirectory()) continue;
4779
+ const pkgPath = join9(pluginsDir, entry.name, "package.json");
4780
+ let version = null;
4781
+ try {
4782
+ const pkg = JSON.parse(await readFile4(pkgPath, "utf8"));
4783
+ version = pkg.version ?? null;
4784
+ } catch {
4785
+ }
4786
+ installed.push({ name: entry.name, version });
4787
+ }
4788
+ res.json({ installed });
4789
+ } catch {
4790
+ res.json({ installed: [] });
4791
+ }
4792
+ });
4793
+ router.get("/open-folder", async (_req, res) => {
4794
+ const pluginsDir = join9(getGlobalConfigDir(config3.mode ?? "production"), "plugins");
4795
+ await openFolder(pluginsDir, res);
4796
+ });
4797
+ router.get("/:name/open-folder", async (req, res) => {
4798
+ const name = req.params.name;
4799
+ if (!/^[a-zA-Z0-9_-]+$/.test(name)) return res.status(400).json({ error: "Invalid plugin name" });
4800
+ const targetDir = join9(getGlobalConfigDir(config3.mode ?? "production"), "plugins", name);
4801
+ await openFolder(targetDir, res);
4802
+ });
4803
+ router.delete("/:name", async (req, res) => {
4804
+ const name = req.params.name;
4805
+ if (!/^[a-zA-Z0-9_-]+$/.test(name)) {
4806
+ return res.status(400).json({ error: "Invalid plugin name" });
4807
+ }
4808
+ const targetDir = join9(getGlobalConfigDir(config3.mode ?? "production"), "plugins", name);
4809
+ try {
4810
+ await rm2(targetDir, { recursive: true, force: true });
4811
+ for (let i = pluginDiagnostics.length - 1; i >= 0; i--) {
4812
+ if (pluginDiagnostics[i].source === targetDir) pluginDiagnostics.splice(i, 1);
4813
+ }
4814
+ res.json({ success: true });
4815
+ } catch (err) {
4816
+ res.status(500).json({ error: err instanceof Error ? err.message : "Failed to remove plugin" });
4817
+ }
4818
+ });
4819
+ return router;
4820
+ }
4821
+
4602
4822
  // src/server/index.ts
4603
- var __dirname3 = dirname7(fileURLToPath6(import.meta.url));
4823
+ var __dirname3 = dirname8(fileURLToPath7(import.meta.url));
4604
4824
  async function createServerHandle(config3) {
4605
4825
  setRuntimeConfig(config3);
4606
4826
  setLogLevel(config3.logging?.level ?? void 0, config3.mode);
@@ -4697,7 +4917,7 @@ async function createServerHandle(config3) {
4697
4917
  setMcpTools(mcpTools);
4698
4918
  logger.info("MCP tools registered", { count: mcpTools.length });
4699
4919
  }
4700
- const { signalMcpReady } = await import("./server-OYE7GERR.js");
4920
+ const { signalMcpReady } = await import("./server-RLNPHLY2.js");
4701
4921
  signalMcpReady();
4702
4922
  });
4703
4923
  const app = express();
@@ -4969,7 +5189,7 @@ async function createServerHandle(config3) {
4969
5189
  app.get("/api/sessions/:id", async (req, res) => {
4970
5190
  const { getEventStore: getEventStore2 } = await import("./events-CDQ4HASP.js");
4971
5191
  const { buildMessagesFromStoredEvents } = await import("./folding-V2K34EGN.js");
4972
- const { getPendingQuestionsForSession } = await import("./tools-INWP6FGV.js");
5192
+ const { getPendingQuestionsForSession } = await import("./tools-CALGWIV2.js");
4973
5193
  const session = sessionManager.getSession(req.params.id);
4974
5194
  if (!session) {
4975
5195
  return res.status(404).json({ error: "Session not found" });
@@ -4988,8 +5208,8 @@ async function createServerHandle(config3) {
4988
5208
  if (!session) {
4989
5209
  return res.status(404).json({ error: "Session not found" });
4990
5210
  }
4991
- const { stopSessionExecution } = await import("./chat-handler-72DFHK5K.js");
4992
- const { cancelQuestionsForSession, cancelPathConfirmationsForSession } = await import("./tools-INWP6FGV.js");
5211
+ const { stopSessionExecution } = await import("./chat-handler-K3DWIGHG.js");
5212
+ const { cancelQuestionsForSession, cancelPathConfirmationsForSession } = await import("./tools-CALGWIV2.js");
4993
5213
  sessionManager.clearMessageQueue(sessionId);
4994
5214
  stopSessionExecution(sessionId, sessionManager);
4995
5215
  abortSession(sessionId);
@@ -5085,6 +5305,38 @@ async function createServerHandle(config3) {
5085
5305
  sessionManager.setMetadataEntries(sessionId, "review_findings", entries);
5086
5306
  res.json({ success: true });
5087
5307
  });
5308
+ app.put("/api/sessions/:id/metadata/:key", async (req, res) => {
5309
+ const sessionId = req.params.id;
5310
+ const key = req.params.key;
5311
+ const session = sessionManager.getSession(sessionId);
5312
+ if (!session) {
5313
+ return res.status(404).json({ error: "Session not found" });
5314
+ }
5315
+ const { entries } = req.body;
5316
+ if (!Array.isArray(entries)) {
5317
+ return res.status(400).json({ error: "entries is required and must be an array" });
5318
+ }
5319
+ for (let i = 0; i < entries.length; i++) {
5320
+ const c = entries[i];
5321
+ if (c === null || typeof c !== "object" || Array.isArray(c)) {
5322
+ return res.status(400).json({ error: `entries[${i}] must be an object` });
5323
+ }
5324
+ if (c.description !== void 0 && typeof c.description !== "string") {
5325
+ return res.status(400).json({ error: `entries[${i}].description must be a string` });
5326
+ }
5327
+ if (c.status !== void 0 && typeof c.status !== "string") {
5328
+ return res.status(400).json({ error: `entries[${i}].status must be a string` });
5329
+ }
5330
+ }
5331
+ const mapped = entries.map((c, i) => ({
5332
+ ...c,
5333
+ id: c.id != null ? String(c.id) : String(i),
5334
+ description: c.description ?? "",
5335
+ status: c.status ?? "open"
5336
+ }));
5337
+ sessionManager.setMetadataEntries(sessionId, key, mapped);
5338
+ res.json({ success: true });
5339
+ });
5088
5340
  app.put("/api/sessions/:id/mode", async (req, res) => {
5089
5341
  const { getEventStore: getEventStore2 } = await import("./events-CDQ4HASP.js");
5090
5342
  const { buildMessagesFromStoredEvents } = await import("./folding-V2K34EGN.js");
@@ -5143,7 +5395,7 @@ async function createServerHandle(config3) {
5143
5395
  if (!callId || approved === void 0) {
5144
5396
  return res.status(400).json({ error: "callId and approved are required" });
5145
5397
  }
5146
- const { providePathConfirmation } = await import("./tools-INWP6FGV.js");
5398
+ const { providePathConfirmation } = await import("./tools-CALGWIV2.js");
5147
5399
  const result = providePathConfirmation(callId, approved, alwaysAllow);
5148
5400
  if (!result.found) {
5149
5401
  return res.status(404).json({ error: "No pending path confirmation with that ID" });
@@ -5151,7 +5403,7 @@ async function createServerHandle(config3) {
5151
5403
  const { getEventStore: getEventStore2 } = await import("./events-CDQ4HASP.js");
5152
5404
  const { buildMessagesFromStoredEvents, foldPendingConfirmations } = await import("./folding-V2K34EGN.js");
5153
5405
  const { createSessionStateMessage } = await import("./protocol-CTNE3ANP.js");
5154
- const { getPendingQuestionsForSession } = await import("./tools-INWP6FGV.js");
5406
+ const { getPendingQuestionsForSession } = await import("./tools-CALGWIV2.js");
5155
5407
  const eventStore = getEventStore2();
5156
5408
  const events = eventStore.getEvents(sessionId);
5157
5409
  const messages = buildMessagesFromStoredEvents(events);
@@ -5172,7 +5424,7 @@ async function createServerHandle(config3) {
5172
5424
  if (!skip && typeof answer !== "string") {
5173
5425
  return res.status(400).json({ error: "answer is required when not skipping" });
5174
5426
  }
5175
- const { provideAnswer } = await import("./tools-INWP6FGV.js");
5427
+ const { provideAnswer } = await import("./tools-CALGWIV2.js");
5176
5428
  const found = provideAnswer(callId, answer ?? "", skip ?? false);
5177
5429
  if (!found) {
5178
5430
  return res.status(404).json({ error: "No pending question with that ID" });
@@ -5232,7 +5484,7 @@ async function createServerHandle(config3) {
5232
5484
  backend: activeProvider?.backend ?? llmClient.getBackend(),
5233
5485
  model: llmClient.getModel()
5234
5486
  };
5235
- const { runAgentTurn, TurnMetrics } = await import("./orchestrator-FEMPJOZD.js");
5487
+ const { runAgentTurn, TurnMetrics } = await import("./orchestrator-PUV4WG77.js");
5236
5488
  runAgentTurn(
5237
5489
  {
5238
5490
  sessionManager,
@@ -5270,8 +5522,8 @@ async function createServerHandle(config3) {
5270
5522
  if (!session) {
5271
5523
  return res.status(404).json({ error: "Session not found" });
5272
5524
  }
5273
- const { stopSessionExecution } = await import("./chat-handler-72DFHK5K.js");
5274
- const { cancelQuestionsForSession, cancelPathConfirmationsForSession } = await import("./tools-INWP6FGV.js");
5525
+ const { stopSessionExecution } = await import("./chat-handler-K3DWIGHG.js");
5526
+ const { cancelQuestionsForSession, cancelPathConfirmationsForSession } = await import("./tools-CALGWIV2.js");
5275
5527
  const queuedMessages = sessionManager.getQueueState(sessionId);
5276
5528
  sessionManager.clearMessageQueue(sessionId);
5277
5529
  stopSessionExecution(sessionId, sessionManager);
@@ -5388,14 +5640,14 @@ async function createServerHandle(config3) {
5388
5640
  app.get("/api/tools/rtk-check", async (_req, res) => {
5389
5641
  const { spawn: spawn3 } = await import("child_process");
5390
5642
  try {
5391
- const available = await new Promise((resolve7) => {
5643
+ const available = await new Promise((resolve8) => {
5392
5644
  const proc = spawn3("rtk", ["--version"], { stdio: ["ignore", "pipe", "pipe"] });
5393
5645
  let out = "";
5394
5646
  proc.stdout?.on("data", (d) => {
5395
5647
  out += d.toString();
5396
5648
  });
5397
- proc.on("error", () => resolve7(false));
5398
- proc.on("close", (code) => resolve7(code === 0 && out.startsWith("rtk ")));
5649
+ proc.on("error", () => resolve8(false));
5650
+ proc.on("close", (code) => resolve8(code === 0 && out.startsWith("rtk ")));
5399
5651
  });
5400
5652
  res.json({ available });
5401
5653
  } catch {
@@ -5565,7 +5817,7 @@ async function createServerHandle(config3) {
5565
5817
  const backend = req.query["backend"];
5566
5818
  if (!url) return res.status(400).json({ error: "url is required" });
5567
5819
  try {
5568
- const { fetchModelsWithContext } = await import("./provider-manager-D3KIOD5E.js");
5820
+ const { fetchModelsWithContext } = await import("./provider-manager-7C3JVOLW.js");
5569
5821
  const { getModelProfile: getModelProfile2 } = await import("./profiles-5J7F4BML.js");
5570
5822
  const models = await fetchModelsWithContext(
5571
5823
  url,
@@ -5635,7 +5887,7 @@ async function createServerHandle(config3) {
5635
5887
  }
5636
5888
  const { getModelProfile: getModelProfile2 } = await import("./profiles-5J7F4BML.js");
5637
5889
  const { getBackendCapabilities } = await import("./backend-P3GHHLHJ.js");
5638
- const { buildNonStreamingCreateParams } = await import("./client-pure-U6ZYUPJQ.js");
5890
+ const { buildNonStreamingCreateParams } = await import("./client-pure-XHH4RRLB.js");
5639
5891
  const { OpenAIHttpClient } = await import("./http-client-PIWB7BXK.js");
5640
5892
  const { ensureVersionPrefix } = await import("./url-utils-SAJP7AEX.js");
5641
5893
  const profile = getModelProfile2(model);
@@ -5761,6 +6013,7 @@ async function createServerHandle(config3) {
5761
6013
  });
5762
6014
  }
5763
6015
  });
6016
+ app.use("/api/plugins", createPluginRoutes({ config: config3, providerAdapters, pluginDiagnostics, logger }));
5764
6017
  app.get("/api/plugins", (_req, res) => res.json({ plugins: pluginDiagnostics }));
5765
6018
  app.get("/api/provider-presets", (_req, res) => res.json({ presets: providerAdapters.getPresets() }));
5766
6019
  app.get(
@@ -5893,7 +6146,7 @@ async function createServerHandle(config3) {
5893
6146
  });
5894
6147
  async function rebuildMcpTools() {
5895
6148
  const { createMcpTools: createMcpTools2 } = await import("./tool-adapter-MOGEI6TP.js");
5896
- const { setMcpTools: setMcpTools2 } = await import("./tools-INWP6FGV.js");
6149
+ const { setMcpTools: setMcpTools2 } = await import("./tools-CALGWIV2.js");
5897
6150
  const mcpTools = createMcpTools2(mcpManager);
5898
6151
  setMcpTools2(mcpTools);
5899
6152
  }
@@ -5978,6 +6231,69 @@ async function createServerHandle(config3) {
5978
6231
  res.status(400).json({ error: error instanceof Error ? error.message : String(error) });
5979
6232
  }
5980
6233
  });
6234
+ app.put("/api/mcp/servers/:name", async (req, res) => {
6235
+ const { name } = req.params;
6236
+ const existing = mcpManager.getServer(name);
6237
+ if (!existing) {
6238
+ return res.status(404).json({ error: `MCP server '${name}' not found` });
6239
+ }
6240
+ const body = req.body;
6241
+ const { transport: rawTransport, command, args, env, url, headers } = body;
6242
+ if (rawTransport !== void 0 && rawTransport !== "stdio" && rawTransport !== "http") {
6243
+ return res.status(400).json({ error: `Invalid transport '${String(rawTransport)}'. Must be 'stdio' or 'http'.` });
6244
+ }
6245
+ if (command !== void 0 && typeof command !== "string") {
6246
+ return res.status(400).json({ error: "command must be a string" });
6247
+ }
6248
+ if (args !== void 0 && (!Array.isArray(args) || args.some((a) => typeof a !== "string"))) {
6249
+ return res.status(400).json({ error: "args must be an array of strings" });
6250
+ }
6251
+ if (env !== void 0 && (typeof env !== "object" || env === null || Array.isArray(env) || Object.values(env).some((v) => typeof v !== "string"))) {
6252
+ return res.status(400).json({ error: "env must be a string/string object" });
6253
+ }
6254
+ if (url !== void 0 && typeof url !== "string") {
6255
+ return res.status(400).json({ error: "url must be a string" });
6256
+ }
6257
+ if (headers !== void 0 && (typeof headers !== "object" || headers === null || Array.isArray(headers) || Object.values(headers).some((v) => typeof v !== "string"))) {
6258
+ return res.status(400).json({ error: "headers must be a string/string object" });
6259
+ }
6260
+ try {
6261
+ const { loadGlobalConfig, saveGlobalConfig } = await import("./config-EUMVEFHU.js");
6262
+ const { applyMcpServerUpdate } = await import("./update-server-MYYQUNZX.js");
6263
+ const globalConfig = await loadGlobalConfig(config3.mode ?? "production", config3.globalConfigPath);
6264
+ const mcpServers2 = { ...globalConfig.mcpServers ?? {} };
6265
+ const patch = {
6266
+ ...rawTransport !== void 0 ? { transport: rawTransport } : {},
6267
+ ...command !== void 0 ? { command } : {},
6268
+ ...args !== void 0 ? { args } : {},
6269
+ ...env !== void 0 ? { env } : {},
6270
+ ...url !== void 0 ? { url } : {},
6271
+ ...headers !== void 0 ? { headers } : {}
6272
+ };
6273
+ const { error: updateError } = await applyMcpServerUpdate({
6274
+ name,
6275
+ patch,
6276
+ existing,
6277
+ persistedCfg: mcpServers2[name],
6278
+ mcpManager,
6279
+ save: async (cfg) => {
6280
+ mcpServers2[name] = cfg;
6281
+ await saveGlobalConfig(config3.mode ?? "production", { ...globalConfig, mcpServers: mcpServers2 }, config3.globalConfigPath);
6282
+ }
6283
+ });
6284
+ if (updateError) {
6285
+ return res.status(400).json({ error: updateError });
6286
+ }
6287
+ await rebuildMcpTools();
6288
+ const sessions = sessionManager.listSessions();
6289
+ for (const s of sessions) {
6290
+ sessionManager.setDynamicContextChanged(s.id, true);
6291
+ }
6292
+ res.json({ server: mcpManager.getServer(name) });
6293
+ } catch (error) {
6294
+ res.status(400).json({ error: error instanceof Error ? error.message : String(error) });
6295
+ }
6296
+ });
5981
6297
  app.delete("/api/mcp/servers/:name", async (req, res) => {
5982
6298
  const { name } = req.params;
5983
6299
  const server = mcpManager.getServer(name);
@@ -6091,14 +6407,14 @@ async function createServerHandle(config3) {
6091
6407
  });
6092
6408
  app.use("/api/directories", createDirectoryRoutes());
6093
6409
  app.use("/api/files", createFileSearchRoutes());
6094
- const webDir = resolve6(__dirname3, "../../web");
6410
+ const webDir = resolve7(__dirname3, "../../web");
6095
6411
  const isDev = config3.mode === "development";
6096
6412
  let viteServer;
6097
6413
  if (isDev) {
6098
6414
  logger.info("Dev mode: using Vite middleware");
6099
6415
  viteServer = await createViteServer({
6100
6416
  root: webDir,
6101
- configFile: resolve6(__dirname3, "../../web/vite.config.ts"),
6417
+ configFile: resolve7(__dirname3, "../../web/vite.config.ts"),
6102
6418
  server: { middlewareMode: true },
6103
6419
  appType: "spa",
6104
6420
  logLevel: "warn"
@@ -6118,7 +6434,7 @@ async function createServerHandle(config3) {
6118
6434
  }
6119
6435
  });
6120
6436
  app.get("/fox.svg", (_req, res) => {
6121
- readFile4(join9(webDir, "fox.svg")).then((content) => {
6437
+ readFile5(join10(webDir, "fox.svg")).then((content) => {
6122
6438
  res.set("Content-Type", "image/svg+xml");
6123
6439
  res.send(content);
6124
6440
  }).catch(() => {
@@ -6127,7 +6443,7 @@ async function createServerHandle(config3) {
6127
6443
  });
6128
6444
  app.use(
6129
6445
  "/sounds",
6130
- express.static(join9(webDir, "public", "sounds"), {
6446
+ express.static(join10(webDir, "public", "sounds"), {
6131
6447
  setHeaders: (res) => {
6132
6448
  res.set("Content-Type", "audio/mpeg");
6133
6449
  }
@@ -6136,13 +6452,13 @@ async function createServerHandle(config3) {
6136
6452
  app.use("/__inspect__.js", (_req, res) => {
6137
6453
  res.set("Access-Control-Allow-Origin", "*");
6138
6454
  res.set("Content-Type", "application/javascript");
6139
- readFile4(join9(webDir, "public", "__inspect__.js")).then((content) => res.send(content)).catch(() => res.status(404).send("Not found"));
6455
+ readFile5(join10(webDir, "public", "__inspect__.js")).then((content) => res.send(content)).catch(() => res.status(404).send("Not found"));
6140
6456
  });
6141
6457
  app.get("/*path", (req, res) => {
6142
6458
  if (req.path.startsWith("/api/")) {
6143
6459
  return;
6144
6460
  }
6145
- readFile4(join9(webDir, "index.html"), "utf-8").then((indexHtml) => viteServer.transformIndexHtml(req.originalUrl, indexHtml)).then((transformed) => res.send(transformed)).catch((err) => {
6461
+ readFile5(join10(webDir, "index.html"), "utf-8").then((indexHtml) => viteServer.transformIndexHtml(req.originalUrl, indexHtml)).then((transformed) => res.send(transformed)).catch((err) => {
6146
6462
  logger.error("Error serving index.html", { error: err });
6147
6463
  res.status(500).send("Server error");
6148
6464
  });
@@ -6150,10 +6466,10 @@ async function createServerHandle(config3) {
6150
6466
  logger.info("Vite middleware ready", { port: config3.server.port });
6151
6467
  }
6152
6468
  if (!isDev) {
6153
- const distWebDir = resolve6(__dirname3, "web");
6469
+ const distWebDir = resolve7(__dirname3, "web");
6154
6470
  app.use(
6155
6471
  "/assets",
6156
- express.static(join9(distWebDir, "assets"), {
6472
+ express.static(join10(distWebDir, "assets"), {
6157
6473
  setHeaders: (res, filepath) => {
6158
6474
  if (filepath.endsWith(".css")) {
6159
6475
  res.set("Content-Type", "text/css");
@@ -6161,11 +6477,11 @@ async function createServerHandle(config3) {
6161
6477
  }
6162
6478
  })
6163
6479
  );
6164
- app.use("/manifest.webmanifest", express.static(join9(distWebDir, "manifest.webmanifest")));
6165
- app.use("/registerSW.js", express.static(join9(distWebDir, "registerSW.js")));
6166
- app.use("/sw.js", express.static(join9(distWebDir, "sw.js")));
6480
+ app.use("/manifest.webmanifest", express.static(join10(distWebDir, "manifest.webmanifest")));
6481
+ app.use("/registerSW.js", express.static(join10(distWebDir, "registerSW.js")));
6482
+ app.use("/sw.js", express.static(join10(distWebDir, "sw.js")));
6167
6483
  app.get("/fox.svg", (_req, res) => {
6168
- readFile4(join9(distWebDir, "fox.svg")).then((content) => {
6484
+ readFile5(join10(distWebDir, "fox.svg")).then((content) => {
6169
6485
  res.set("Content-Type", "image/svg+xml");
6170
6486
  res.send(content);
6171
6487
  }).catch(() => {
@@ -6174,7 +6490,7 @@ async function createServerHandle(config3) {
6174
6490
  });
6175
6491
  app.use(
6176
6492
  "/sounds",
6177
- express.static(join9(distWebDir, "sounds"), {
6493
+ express.static(join10(distWebDir, "sounds"), {
6178
6494
  setHeaders: (res) => {
6179
6495
  res.set("Content-Type", "audio/mpeg");
6180
6496
  }
@@ -6183,10 +6499,10 @@ async function createServerHandle(config3) {
6183
6499
  app.use("/__inspect__.js", (_req, res) => {
6184
6500
  res.set("Access-Control-Allow-Origin", "*");
6185
6501
  res.set("Content-Type", "application/javascript");
6186
- readFile4(join9(distWebDir, "__inspect__.js")).then((content) => res.send(content)).catch(() => res.status(404).send("Not found"));
6502
+ readFile5(join10(distWebDir, "__inspect__.js")).then((content) => res.send(content)).catch(() => res.status(404).send("Not found"));
6187
6503
  });
6188
6504
  app.get("/", (_req, res) => {
6189
- readFile4(join9(distWebDir, "index.html"), "utf-8").then((content) => res.send(content)).catch(() => {
6505
+ readFile5(join10(distWebDir, "index.html"), "utf-8").then((content) => res.send(content)).catch(() => {
6190
6506
  res.status(404).send("Web UI not built. Run `npm run build:web`");
6191
6507
  });
6192
6508
  });
@@ -6194,7 +6510,7 @@ async function createServerHandle(config3) {
6194
6510
  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") {
6195
6511
  return;
6196
6512
  }
6197
- readFile4(join9(distWebDir, "index.html"), "utf-8").then((content) => res.send(content)).catch(() => {
6513
+ readFile5(join10(distWebDir, "index.html"), "utf-8").then((content) => res.send(content)).catch(() => {
6198
6514
  res.status(404).send("Web UI not built");
6199
6515
  });
6200
6516
  });
@@ -6214,7 +6530,7 @@ async function createServerHandle(config3) {
6214
6530
  const state = sessionManager.getContextState(sessionId);
6215
6531
  wssExports.broadcastForSession(sessionId, createContextStateMessage(state));
6216
6532
  });
6217
- const { QueueProcessor } = await import("./processor-QB3NMRMO.js");
6533
+ const { QueueProcessor } = await import("./processor-O2QW27BD.js");
6218
6534
  const queueProcessor = new QueueProcessor({
6219
6535
  sessionManager,
6220
6536
  providerManager,
@@ -6237,7 +6553,7 @@ async function createServerHandle(config3) {
6237
6553
  return {
6238
6554
  httpServer,
6239
6555
  ctx: { config: config3, sessionManager, llmClient: getLLMClient(), toolRegistry, providerManager },
6240
- start: (port) => new Promise((resolve7, reject) => {
6556
+ start: (port) => new Promise((resolve8, reject) => {
6241
6557
  const listenPort = port ?? config3.server.port;
6242
6558
  const host = config3.server.host;
6243
6559
  httpServer.listen(listenPort, host, () => {
@@ -6247,11 +6563,11 @@ async function createServerHandle(config3) {
6247
6563
  logger.info(`OpenFox server running at http://${host}:${actualPort}`);
6248
6564
  logger.info(`WebSocket available at ws://${host}:${actualPort}/ws`);
6249
6565
  logger.info(`LLM backend: ${client.getBackend()}, model: ${client.getModel()}, url: ${config3.llm.baseUrl}`);
6250
- resolve7({ port: actualPort });
6566
+ resolve8({ port: actualPort });
6251
6567
  });
6252
6568
  httpServer.on("error", reject);
6253
6569
  }),
6254
- close: () => new Promise((resolve7) => {
6570
+ close: () => new Promise((resolve8) => {
6255
6571
  logger.info("Shutting down...");
6256
6572
  void (async () => {
6257
6573
  await devServerManager.stopAll();
@@ -6270,7 +6586,7 @@ async function createServerHandle(config3) {
6270
6586
  client.terminate();
6271
6587
  }
6272
6588
  wss.close();
6273
- httpServer.close(() => resolve7());
6589
+ httpServer.close(() => resolve8());
6274
6590
  })();
6275
6591
  })
6276
6592
  };
@@ -6295,4 +6611,4 @@ export {
6295
6611
  createServerHandle,
6296
6612
  createServer
6297
6613
  };
6298
- //# sourceMappingURL=chunk-2GRUQN2B.js.map
6614
+ //# sourceMappingURL=chunk-WHVCGVQX.js.map