openfox 2.0.59 → 2.0.61

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.
@@ -21,12 +21,13 @@ import {
21
21
  tokenFromPassword,
22
22
  verifyPassword,
23
23
  workflowExists
24
- } from "./chunk-5MDIKIMG.js";
24
+ } from "./chunk-AA4G4DGK.js";
25
25
  import {
26
26
  agentExists,
27
27
  createToolRegistry,
28
28
  deleteAgent,
29
29
  deleteItemFromDir,
30
+ deleteOwnedSkill,
30
31
  deleteProjectAgent,
31
32
  deleteProjectSkill,
32
33
  deleteSkill,
@@ -43,6 +44,7 @@ import {
43
44
  loadAllAgents,
44
45
  loadAllAgentsDefault,
45
46
  loadAllSkills,
47
+ loadAllSkillsWithDiagnostics,
46
48
  loadDefaultAgents,
47
49
  loadDefaultSkills,
48
50
  loadItemsFromDir,
@@ -61,8 +63,10 @@ import {
61
63
  setMcpManagerForTools,
62
64
  setMcpTools,
63
65
  setNotifyMcpServersChanged,
64
- skillExists
65
- } from "./chunk-FRU2X3IZ.js";
66
+ setSkillEnabled,
67
+ skillExists,
68
+ updateOwnedSkill
69
+ } from "./chunk-KX4C5Q5J.js";
66
70
  import {
67
71
  getPathSeparator,
68
72
  isAbsolutePath
@@ -76,6 +80,11 @@ import {
76
80
  import {
77
81
  EventEmitter
78
82
  } from "./chunk-EU3WWTFH.js";
83
+ import {
84
+ deleteSetting,
85
+ getSetting,
86
+ setSetting
87
+ } from "./chunk-SVSKQYFH.js";
79
88
  import {
80
89
  getProject
81
90
  } from "./chunk-YQ3SOPBI.js";
@@ -150,8 +159,8 @@ import express from "express";
150
159
  import cors from "cors";
151
160
  import { createServer as createHttpServer } from "http";
152
161
  import { fileURLToPath as fileURLToPath5 } from "url";
153
- import { dirname as dirname5, resolve as resolve3, join as join5 } from "path";
154
- import { readFile } from "fs/promises";
162
+ import { dirname as dirname6, resolve as resolve4, join as join7 } from "path";
163
+ import { readFile as readFile2 } from "fs/promises";
155
164
  import { createServer as createViteServer } from "vite";
156
165
 
157
166
  // src/server/llm/mock.ts
@@ -1426,12 +1435,12 @@ var McpManager = class {
1426
1435
  this.onServersChanged = options?.onServersChanged;
1427
1436
  this.onToolsDiscovered = options?.onToolsDiscovered;
1428
1437
  }
1429
- async addServer(name, config4) {
1438
+ async addServer(name, config3) {
1430
1439
  if (this.servers.has(name)) {
1431
1440
  throw new Error(`MCP server '${name}' already exists`);
1432
1441
  }
1433
- const state = { name, config: config4, status: "disconnected", tools: [], estimatedTokens: 0 };
1434
- this.servers.set(name, { config: config4, client: void 0, transport: null, state });
1442
+ const state = { name, config: config3, status: "disconnected", tools: [], estimatedTokens: 0 };
1443
+ this.servers.set(name, { config: config3, client: void 0, transport: null, state });
1435
1444
  await this.connectServer(name);
1436
1445
  }
1437
1446
  removeServer(name) {
@@ -1678,10 +1687,10 @@ var LspServer = class {
1678
1687
  // Pending diagnostic waiters
1679
1688
  pendingDiagnostics = /* @__PURE__ */ new Map();
1680
1689
  commandPath;
1681
- constructor(config4, workdir, commandPath) {
1682
- this.config = config4;
1690
+ constructor(config3, workdir, commandPath) {
1691
+ this.config = config3;
1683
1692
  this.workdir = workdir;
1684
- this.commandPath = commandPath ?? config4.serverCommand;
1693
+ this.commandPath = commandPath ?? config3.serverCommand;
1685
1694
  }
1686
1695
  // ============================================================================
1687
1696
  // Lifecycle
@@ -1914,19 +1923,19 @@ var LspServer = class {
1914
1923
  */
1915
1924
  async getDiagnosticsWithWait(path, timeoutMs = DIAGNOSTIC_WAIT_MS) {
1916
1925
  if (this.openDocuments.has(path)) {
1917
- return new Promise((resolve4) => {
1926
+ return new Promise((resolve5) => {
1918
1927
  const existing = this.diagnostics.get(path);
1919
1928
  if (existing !== void 0) {
1920
1929
  setTimeout(() => {
1921
- resolve4(this.diagnostics.get(path) ?? []);
1930
+ resolve5(this.diagnostics.get(path) ?? []);
1922
1931
  }, 100);
1923
1932
  return;
1924
1933
  }
1925
1934
  const timeout = setTimeout(() => {
1926
1935
  this.pendingDiagnostics.delete(path);
1927
- resolve4(this.diagnostics.get(path) ?? []);
1936
+ resolve5(this.diagnostics.get(path) ?? []);
1928
1937
  }, timeoutMs);
1929
- this.pendingDiagnostics.set(path, { resolve: resolve4, timeout });
1938
+ this.pendingDiagnostics.set(path, { resolve: resolve5, timeout });
1930
1939
  });
1931
1940
  }
1932
1941
  return this.diagnostics.get(path) ?? [];
@@ -2238,8 +2247,8 @@ var LspManager = class {
2238
2247
  // ============================================================================
2239
2248
  // Server Management
2240
2249
  // ============================================================================
2241
- async getOrCreateServer(config4) {
2242
- const langId = config4.id;
2250
+ async getOrCreateServer(config3) {
2251
+ const langId = config3.id;
2243
2252
  const existing = this.servers.get(langId);
2244
2253
  if (existing?.isRunning()) {
2245
2254
  return existing;
@@ -2251,7 +2260,7 @@ var LspManager = class {
2251
2260
  if (pending) {
2252
2261
  return pending;
2253
2262
  }
2254
- const promise = this.startServer(config4);
2263
+ const promise = this.startServer(config3);
2255
2264
  this.serverPromises.set(langId, promise);
2256
2265
  try {
2257
2266
  const server = await promise;
@@ -2260,19 +2269,19 @@ var LspManager = class {
2260
2269
  this.serverPromises.delete(langId);
2261
2270
  }
2262
2271
  }
2263
- async startServer(config4) {
2264
- const langId = config4.id;
2265
- const commandPath = await which(config4.serverCommand, this.workdir);
2272
+ async startServer(config3) {
2273
+ const langId = config3.id;
2274
+ const commandPath = await which(config3.serverCommand, this.workdir);
2266
2275
  if (!commandPath) {
2267
2276
  logger.warn("LSP server not installed, skipping", {
2268
2277
  language: langId,
2269
- command: config4.serverCommand,
2278
+ command: config3.serverCommand,
2270
2279
  sessionId: this.sessionId
2271
2280
  });
2272
2281
  this.unavailableServers.add(langId);
2273
2282
  return null;
2274
2283
  }
2275
- const server = new LspServer(config4, this.workdir, commandPath);
2284
+ const server = new LspServer(config3, this.workdir, commandPath);
2276
2285
  try {
2277
2286
  await server.start();
2278
2287
  this.servers.set(langId, server);
@@ -2292,11 +2301,11 @@ var LspManager = class {
2292
2301
  }
2293
2302
  }
2294
2303
  async getServerForFile(path) {
2295
- const config4 = detectLanguage(path);
2296
- if (!config4) {
2304
+ const config3 = detectLanguage(path);
2305
+ if (!config3) {
2297
2306
  return null;
2298
2307
  }
2299
- return this.getOrCreateServer(config4);
2308
+ return this.getOrCreateServer(config3);
2300
2309
  }
2301
2310
  // ============================================================================
2302
2311
  // Public Interface
@@ -2327,11 +2336,11 @@ var LspManager = class {
2327
2336
  * Get current diagnostics for a file without triggering a change.
2328
2337
  */
2329
2338
  getDiagnostics(path) {
2330
- const config4 = detectLanguage(path);
2331
- if (!config4) {
2339
+ const config3 = detectLanguage(path);
2340
+ if (!config3) {
2332
2341
  return [];
2333
2342
  }
2334
- const server = this.servers.get(config4.id);
2343
+ const server = this.servers.get(config3.id);
2335
2344
  if (!server?.isRunning()) {
2336
2345
  return [];
2337
2346
  }
@@ -2342,11 +2351,11 @@ var LspManager = class {
2342
2351
  * Returns true if we have or can start a server for this file type.
2343
2352
  */
2344
2353
  isAvailableFor(path) {
2345
- const config4 = detectLanguage(path);
2346
- if (!config4) {
2354
+ const config3 = detectLanguage(path);
2355
+ if (!config3) {
2347
2356
  return false;
2348
2357
  }
2349
- if (this.unavailableServers.has(config4.id)) {
2358
+ if (this.unavailableServers.has(config3.id)) {
2350
2359
  return false;
2351
2360
  }
2352
2361
  return true;
@@ -2357,13 +2366,13 @@ var LspManager = class {
2357
2366
  * to avoid spamming the user on every edit.
2358
2367
  */
2359
2368
  getInstallHint(path) {
2360
- const config4 = detectLanguage(path);
2361
- if (!config4?.installHint) {
2369
+ const config3 = detectLanguage(path);
2370
+ if (!config3?.installHint) {
2362
2371
  return null;
2363
2372
  }
2364
- if (this.unavailableServers.has(config4.id) && !this.dismissedHints.has(config4.id)) {
2365
- this.dismissedHints.add(config4.id);
2366
- return config4.installHint;
2373
+ if (this.unavailableServers.has(config3.id) && !this.dismissedHints.has(config3.id)) {
2374
+ this.dismissedHints.add(config3.id);
2375
+ return config3.installHint;
2367
2376
  }
2368
2377
  return null;
2369
2378
  }
@@ -3243,9 +3252,77 @@ var SessionManager = class {
3243
3252
  }
3244
3253
  };
3245
3254
 
3255
+ // src/server/routes/skills.ts
3256
+ import { Router as Router2 } from "express";
3257
+ import multer from "multer";
3258
+ import { access as access2, mkdir as mkdir2, realpath, stat } from "fs/promises";
3259
+ import { constants as constants2 } from "fs";
3260
+ import { homedir } from "os";
3261
+ import { isAbsolute as isAbsolute2, join as join4, resolve as resolve2 } from "path";
3262
+
3263
+ // src/server/skills/installer.ts
3264
+ import { randomUUID } from "crypto";
3265
+ import { mkdir, readFile, rename, rm, writeFile } from "fs/promises";
3266
+ import { dirname as dirname3, isAbsolute, join as join2 } from "path";
3267
+ import matter from "gray-matter";
3268
+ var SkillInstallError = class extends Error {
3269
+ constructor(message, status) {
3270
+ super(message);
3271
+ this.status = status;
3272
+ }
3273
+ };
3274
+ function validateRelativePath(path) {
3275
+ if (!path || isAbsolute(path) || path.includes("\\") || path.includes("\0") || path.split("/").some((part) => !part || part === "." || part === "..")) {
3276
+ throw new SkillInstallError(`Invalid package path: ${path}`, 400);
3277
+ }
3278
+ }
3279
+ function validatePackageName(name) {
3280
+ if (!name || name.includes("/") || name.includes("\\") || name === "." || name === ".." || name.includes("\0")) {
3281
+ throw new SkillInstallError("Invalid package name", 400);
3282
+ }
3283
+ }
3284
+ async function installSkillPackage(libraryDir, packageName, files) {
3285
+ validatePackageName(packageName);
3286
+ if (!files.length || files.length > 1e3) throw new SkillInstallError("Package must contain 1-1000 files", 400);
3287
+ if (!files.some((file) => file.path === "SKILL.md")) throw new SkillInstallError("Package must contain SKILL.md", 400);
3288
+ let totalBytes = 0;
3289
+ const paths = /* @__PURE__ */ new Set();
3290
+ for (const file of files) {
3291
+ validateRelativePath(file.path);
3292
+ if (paths.has(file.path)) throw new SkillInstallError(`Duplicate package path: ${file.path}`, 400);
3293
+ paths.add(file.path);
3294
+ if (file.content.byteLength > 25 * 1024 * 1024) throw new SkillInstallError("Package file exceeds 25 MiB", 413);
3295
+ totalBytes += file.content.byteLength;
3296
+ }
3297
+ if (totalBytes > 50 * 1024 * 1024) throw new SkillInstallError("Package exceeds 50 MiB", 413);
3298
+ const destination = join2(libraryDir, packageName);
3299
+ if (await pathExists(destination)) throw new SkillInstallError("A package with this name already exists", 409);
3300
+ const staging = join2(libraryDir, `.${packageName}.openfox-upload-${randomUUID()}`);
3301
+ try {
3302
+ await mkdir(staging, { recursive: false });
3303
+ for (const file of files) {
3304
+ const target = join2(staging, file.path);
3305
+ await mkdir(dirname3(target), { recursive: true });
3306
+ await writeFile(target, file.content);
3307
+ }
3308
+ const parsed = matter(await readFile(join2(staging, "SKILL.md"), "utf-8"));
3309
+ const name = typeof parsed.data["name"] === "string" ? parsed.data["name"].trim() : "";
3310
+ const description = typeof parsed.data["description"] === "string" ? parsed.data["description"].trim() : "";
3311
+ if (!name || !description || !parsed.content.trim()) {
3312
+ throw new SkillInstallError("SKILL.md requires name, description, and instructions", 400);
3313
+ }
3314
+ if (await pathExists(destination)) throw new SkillInstallError("A package with this name already exists", 409);
3315
+ await rename(staging, destination);
3316
+ return { id: name, path: destination };
3317
+ } catch (error) {
3318
+ await rm(staging, { recursive: true, force: true });
3319
+ throw error;
3320
+ }
3321
+ }
3322
+
3246
3323
  // src/server/routes/crud-helpers.ts
3247
3324
  import { Router } from "express";
3248
- import { join as join2 } from "path";
3325
+ import { join as join3 } from "path";
3249
3326
  function computeOverrideIds(defaults, userItems) {
3250
3327
  return userItems.filter((u) => defaults.some((d) => d.metadata.id === u.metadata.id)).map((u) => u.metadata.id);
3251
3328
  }
@@ -3257,34 +3334,34 @@ function validateNameIdPrompt(body) {
3257
3334
  return null;
3258
3335
  }
3259
3336
  function getProjectItemPath(projectDir, dirName, id, ext) {
3260
- return join2(projectDir, ".openfox", dirName, `${id}${ext}`);
3337
+ return join3(projectDir, ".openfox", dirName, `${id}${ext}`);
3261
3338
  }
3262
3339
  async function isProjectItem(projectDir, dirName, id, ext) {
3263
3340
  if (!projectDir) return false;
3264
3341
  return pathExists(getProjectItemPath(projectDir, dirName, id, ext));
3265
3342
  }
3266
- function createCrudRoutes(config4, configDir, projectDir) {
3343
+ function createCrudRoutes(config3, configDir, projectDir) {
3267
3344
  const router = Router();
3268
3345
  router.get("/", async (_req, res) => {
3269
3346
  const [defaults, userItems, projectItems] = await Promise.all([
3270
- config4.loadDefaults(),
3271
- config4.loadUser(configDir),
3272
- projectDir ? config4.loadProject(projectDir) : []
3347
+ config3.loadDefaults(),
3348
+ config3.loadUser(configDir),
3349
+ projectDir ? config3.loadProject(projectDir) : []
3273
3350
  ]);
3274
3351
  const userOverrideIds = computeOverrideIds(defaults, userItems);
3275
3352
  const projectOverrideIds = computeOverrideIds(defaults, projectItems);
3276
- const extra = config4.extraGetData ? await config4.extraGetData() : {};
3353
+ const extra = config3.extraGetData ? await config3.extraGetData() : {};
3277
3354
  res.json({
3278
- defaults: defaults.map(config4.mapToResponse),
3279
- userItems: userItems.map(config4.mapToResponse),
3280
- projectItems: projectItems.map(config4.mapToResponse),
3355
+ defaults: defaults.map(config3.mapToResponse),
3356
+ userItems: userItems.map(config3.mapToResponse),
3357
+ projectItems: projectItems.map(config3.mapToResponse),
3281
3358
  overrideIds: [...userOverrideIds, ...projectOverrideIds],
3282
3359
  ...extra
3283
3360
  });
3284
3361
  });
3285
3362
  router.get("/defaults/:id", async (req, res) => {
3286
3363
  const { id } = req.params;
3287
- const allDefaults = await config4.loadDefaults();
3364
+ const allDefaults = await config3.loadDefaults();
3288
3365
  const item = allDefaults.find((d) => d.metadata.id === id);
3289
3366
  if (!item) {
3290
3367
  return res.status(404).json({ error: "Default not found" });
@@ -3292,13 +3369,13 @@ function createCrudRoutes(config4, configDir, projectDir) {
3292
3369
  res.json(item);
3293
3370
  });
3294
3371
  router.get("/default-ids", async (_req, res) => {
3295
- const ids = config4.getDefaultIds ? await config4.getDefaultIds() : [];
3372
+ const ids = config3.getDefaultIds ? await config3.getDefaultIds() : [];
3296
3373
  res.json({ ids });
3297
3374
  });
3298
3375
  router.get("/:id", async (req, res) => {
3299
3376
  const { id } = req.params;
3300
- const items = await config4.loadAll(configDir, projectDir);
3301
- const item = config4.findById(id, items);
3377
+ const items = await config3.loadAll(configDir, projectDir);
3378
+ const item = config3.findById(id, items);
3302
3379
  if (!item) {
3303
3380
  return res.status(404).json({ error: "Not found" });
3304
3381
  }
@@ -3307,7 +3384,7 @@ function createCrudRoutes(config4, configDir, projectDir) {
3307
3384
  router.post("/", async (req, res) => {
3308
3385
  const body = req.body;
3309
3386
  const meta = body["metadata"];
3310
- const customError = config4.validateCreate?.(body);
3387
+ const customError = config3.validateCreate?.(body);
3311
3388
  if (!meta?.["id"] || customError) {
3312
3389
  return res.status(400).json({ error: customError ?? "Missing required fields" });
3313
3390
  }
@@ -3316,21 +3393,21 @@ function createCrudRoutes(config4, configDir, projectDir) {
3316
3393
  if (destination === "project" && !projectDir) {
3317
3394
  return res.status(400).json({ error: "No project directory configured" });
3318
3395
  }
3319
- const exists = await config4.exists(configDir, id, projectDir);
3396
+ const exists = await config3.exists(configDir, id, projectDir);
3320
3397
  if (exists) {
3321
3398
  return res.status(409).json({ error: "An item with this ID already exists" });
3322
3399
  }
3323
3400
  if (destination === "project") {
3324
- await config4.saveToProject(projectDir, body);
3401
+ await config3.saveToProject(projectDir, body);
3325
3402
  } else {
3326
- await config4.save(configDir, body);
3403
+ await config3.save(configDir, body);
3327
3404
  }
3328
3405
  res.status(201).json(body);
3329
3406
  });
3330
3407
  router.put("/:id", async (req, res) => {
3331
3408
  const { id } = req.params;
3332
- const items = await config4.loadAll(configDir, projectDir);
3333
- const existing = config4.findById(id, items);
3409
+ const items = await config3.loadAll(configDir, projectDir);
3410
+ const existing = config3.findById(id, items);
3334
3411
  if (!existing) {
3335
3412
  return res.status(404).json({ error: "Not found" });
3336
3413
  }
@@ -3341,29 +3418,29 @@ function createCrudRoutes(config4, configDir, projectDir) {
3341
3418
  ...body,
3342
3419
  metadata: { ...existing.metadata, ...meta, id }
3343
3420
  };
3344
- const isProject = await isProjectItem(projectDir, config4.dirName, id, config4.ext);
3421
+ const isProject = await isProjectItem(projectDir, config3.dirName, id, config3.ext);
3345
3422
  if (isProject) {
3346
- await config4.saveToProject(projectDir, updated);
3423
+ await config3.saveToProject(projectDir, updated);
3347
3424
  } else {
3348
- await config4.save(configDir, updated);
3425
+ await config3.save(configDir, updated);
3349
3426
  }
3350
3427
  res.json(updated);
3351
3428
  });
3352
3429
  router.delete("/:id", async (req, res) => {
3353
3430
  const { id } = req.params;
3354
- const isProject = await isProjectItem(projectDir, config4.dirName, id, config4.ext);
3431
+ const isProject = await isProjectItem(projectDir, config3.dirName, id, config3.ext);
3355
3432
  if (isProject) {
3356
- const result2 = await config4.deleteProject(projectDir, id);
3433
+ const result2 = await config3.deleteProject(projectDir, id);
3357
3434
  if (!result2.success) {
3358
3435
  return res.status(500).json({ error: "Failed to delete project item" });
3359
3436
  }
3360
3437
  return res.json({ success: true });
3361
3438
  }
3362
- const isDefault = await config4.isDefault(id);
3439
+ const isDefault = await config3.isDefault(id);
3363
3440
  if (isDefault) {
3364
3441
  return res.status(403).json({ error: "Cannot delete built-in defaults" });
3365
3442
  }
3366
- const result = await config4.delete(configDir, id);
3443
+ const result = await config3.delete(configDir, id);
3367
3444
  if (!result.success) {
3368
3445
  return res.status(403).json({ error: result.reason ?? "Cannot delete this item" });
3369
3446
  }
@@ -3371,8 +3448,8 @@ function createCrudRoutes(config4, configDir, projectDir) {
3371
3448
  });
3372
3449
  router.post("/:id/duplicate", async (req, res) => {
3373
3450
  const { id } = req.params;
3374
- const items = await config4.loadAll(configDir, projectDir);
3375
- const source = config4.findById(id, items);
3451
+ const items = await config3.loadAll(configDir, projectDir);
3452
+ const source = config3.findById(id, items);
3376
3453
  if (!source) {
3377
3454
  return res.status(404).json({ error: "Not found" });
3378
3455
  }
@@ -3384,56 +3461,195 @@ function createCrudRoutes(config4, configDir, projectDir) {
3384
3461
  const destination = req.body.destination ?? "user";
3385
3462
  if (destination === "project") {
3386
3463
  if (!projectDir) return res.status(400).json({ error: "No project directory configured" });
3387
- await config4.saveToProject(projectDir, duplicated);
3464
+ await config3.saveToProject(projectDir, duplicated);
3388
3465
  } else {
3389
- await config4.save(configDir, duplicated);
3466
+ await config3.save(configDir, duplicated);
3390
3467
  }
3391
3468
  res.status(201).json(duplicated);
3392
3469
  });
3393
- config4.extraRoutes?.(router);
3470
+ config3.extraRoutes?.(router);
3394
3471
  return router;
3395
3472
  }
3396
3473
 
3397
3474
  // src/server/routes/skills.ts
3398
- var config = {
3399
- dirName: "skills",
3400
- ext: ".skill.md",
3401
- loadDefaults: loadDefaultSkills,
3402
- loadUser: loadUserSkills,
3403
- loadProject: loadProjectSkills,
3404
- loadAll: loadAllSkills,
3405
- findById: findSkillById,
3406
- save: saveSkill,
3407
- saveToProject: saveSkillToProject,
3408
- delete: deleteSkill,
3409
- deleteProject: deleteProjectSkill,
3410
- exists: skillExists,
3411
- isDefault: isDefaultSkill,
3412
- getDefaultIds: getDefaultSkillIds,
3413
- validateCreate: validateNameIdPrompt,
3414
- mapToResponse: (s) => ({
3415
- ...s.metadata,
3416
- enabled: isSkillEnabled(s.metadata.id)
3417
- })
3418
- };
3475
+ var SKILL_DIRECTORIES_SETTING = "skills.directories";
3476
+ var upload = multer({
3477
+ storage: multer.memoryStorage(),
3478
+ limits: { files: 1e3, fileSize: 25 * 1024 * 1024, fields: 4 }
3479
+ });
3480
+ function expandHome(path) {
3481
+ if (path === "~") return homedir();
3482
+ return path.startsWith("~/") ? join4(homedir(), path.slice(2)) : path;
3483
+ }
3484
+ function configuredDirectory() {
3485
+ const raw = getSetting(SKILL_DIRECTORIES_SETTING);
3486
+ if (!raw) return null;
3487
+ try {
3488
+ const parsed = JSON.parse(raw);
3489
+ return Array.isArray(parsed) && typeof parsed[0] === "string" ? parsed[0] : null;
3490
+ } catch {
3491
+ return null;
3492
+ }
3493
+ }
3494
+ function defaultLibrary(configDir) {
3495
+ const path = resolve2(configDir, "skills");
3496
+ return { configuredPath: path, resolvedPath: path, available: true, custom: false };
3497
+ }
3498
+ async function resolveLibrary(path) {
3499
+ const expanded = expandHome(path);
3500
+ if (!isAbsolute2(expanded)) throw new Error("Selected path must be absolute");
3501
+ const absolute = resolve2(expanded);
3502
+ const info = await stat(absolute);
3503
+ if (!info.isDirectory()) throw new Error("Selected path is not a directory");
3504
+ await access2(absolute, constants2.R_OK);
3505
+ return { configuredPath: path, resolvedPath: await realpath(absolute) };
3506
+ }
3507
+ function mapToResponse(skill) {
3508
+ const source = skill.source ?? "global-openfox";
3509
+ return {
3510
+ ...skill.metadata,
3511
+ enabled: isSkillEnabled(skill.metadata.id),
3512
+ source,
3513
+ path: skill.entrypoint ?? null,
3514
+ legacy: skill.legacy ?? true,
3515
+ readOnly: source === "bundled" || (source === "global-shared" || source === "selected" || source === "project-shared") && (skill.legacy ?? true),
3516
+ warnings: skill.warnings ?? []
3517
+ };
3518
+ }
3519
+ function validateSkillCreate(body) {
3520
+ const baseError = validateNameIdPrompt(body);
3521
+ if (baseError) return baseError;
3522
+ const metadata = body["metadata"];
3523
+ if (typeof metadata["description"] !== "string" || !metadata["description"].trim()) {
3524
+ return "Missing required field: metadata.description";
3525
+ }
3526
+ return null;
3527
+ }
3528
+ function createConfig(configDir, projectDir) {
3529
+ return {
3530
+ dirName: "skills",
3531
+ ext: ".skill.md",
3532
+ loadDefaults: loadDefaultSkills,
3533
+ loadUser: loadUserSkills,
3534
+ loadProject: loadProjectSkills,
3535
+ loadAll: loadAllSkills,
3536
+ findById: findSkillById,
3537
+ save: saveSkill,
3538
+ saveToProject: saveSkillToProject,
3539
+ delete: deleteSkill,
3540
+ deleteProject: deleteProjectSkill,
3541
+ exists: skillExists,
3542
+ isDefault: isDefaultSkill,
3543
+ getDefaultIds: getDefaultSkillIds,
3544
+ validateCreate: validateSkillCreate,
3545
+ mapToResponse,
3546
+ extraGetData: async () => {
3547
+ const discovery = await loadAllSkillsWithDiagnostics(configDir, projectDir);
3548
+ const items = discovery.skills;
3549
+ const configured = configuredDirectory();
3550
+ let selectedDirectory = configured ? { configuredPath: configured, resolvedPath: null, available: false, custom: true } : defaultLibrary(configDir);
3551
+ const diagnostics = [];
3552
+ if (configured) {
3553
+ try {
3554
+ selectedDirectory = { ...await resolveLibrary(configured), available: true, custom: true };
3555
+ } catch {
3556
+ diagnostics.push(`Selected skills directory is unavailable: ${configured}`);
3557
+ }
3558
+ }
3559
+ return {
3560
+ items: items.map(mapToResponse),
3561
+ selectedDirectory,
3562
+ diagnostics: [...diagnostics, ...discovery.diagnostics, ...items.flatMap((skill) => skill.warnings ?? [])]
3563
+ };
3564
+ }
3565
+ };
3566
+ }
3419
3567
  function createSkillRoutes(configDir, projectDir) {
3420
- return createCrudRoutes(config, configDir, projectDir);
3568
+ const router = Router2();
3569
+ router.put("/library", async (req, res) => {
3570
+ const path = req.body.path;
3571
+ if (typeof path !== "string" || !path.trim()) return res.status(400).json({ error: "path is required" });
3572
+ try {
3573
+ const selected = await resolveLibrary(path);
3574
+ setSetting(SKILL_DIRECTORIES_SETTING, JSON.stringify([path]));
3575
+ res.json(selected);
3576
+ } catch (error) {
3577
+ res.status(400).json({ error: error instanceof Error ? error.message : "Cannot use directory" });
3578
+ }
3579
+ });
3580
+ router.delete("/library", (_req, res) => {
3581
+ deleteSetting(SKILL_DIRECTORIES_SETTING);
3582
+ res.json({ success: true });
3583
+ });
3584
+ router.post("/install", (req, res) => {
3585
+ upload.array("files", 1e3)(req, res, async (uploadError) => {
3586
+ if (uploadError) return res.status(413).json({ error: uploadError.message });
3587
+ try {
3588
+ const configured = configuredDirectory();
3589
+ const library = configured ? await resolveLibrary(configured) : defaultLibrary(configDir);
3590
+ await mkdir2(library.resolvedPath, { recursive: true });
3591
+ const packageName = String(req.body["packageName"] ?? "");
3592
+ const paths = JSON.parse(String(req.body["paths"] ?? "[]"));
3593
+ const files = req.files;
3594
+ if (!Array.isArray(paths) || paths.length !== files.length || paths.some((path) => typeof path !== "string")) {
3595
+ return res.status(400).json({ error: "File path manifest does not match upload" });
3596
+ }
3597
+ const totalBytes = files.reduce((total, file) => total + file.size, 0);
3598
+ if (totalBytes > 50 * 1024 * 1024) return res.status(413).json({ error: "Package exceeds 50 MiB" });
3599
+ const installed = await installSkillPackage(
3600
+ library.resolvedPath,
3601
+ packageName,
3602
+ files.map((file, index) => ({ path: paths[index], content: file.buffer }))
3603
+ );
3604
+ res.status(201).json(installed);
3605
+ } catch (error) {
3606
+ const status = error instanceof SkillInstallError ? error.status : 400;
3607
+ res.status(status).json({ error: error instanceof Error ? error.message : "Install failed" });
3608
+ }
3609
+ });
3610
+ });
3611
+ router.post("/:id/toggle", async (req, res) => {
3612
+ const skills = await loadAllSkills(configDir, projectDir);
3613
+ const existing = findSkillById(req.params["id"], skills);
3614
+ if (!existing) return res.status(404).json({ error: "Not found" });
3615
+ const enabled = !isSkillEnabled(existing.metadata.id);
3616
+ setSkillEnabled(existing.metadata.id, enabled);
3617
+ res.json({ enabled });
3618
+ });
3619
+ router.put("/:id", async (req, res) => {
3620
+ const skills = await loadAllSkills(configDir, projectDir);
3621
+ const existing = findSkillById(req.params["id"], skills);
3622
+ if (!existing) return res.status(404).json({ error: "Not found" });
3623
+ const updated = await updateOwnedSkill(existing, req.body);
3624
+ if (!updated) return res.status(403).json({ error: "This skill is read-only" });
3625
+ res.json(updated);
3626
+ });
3627
+ router.delete("/:id", async (req, res) => {
3628
+ const skills = await loadAllSkills(configDir, projectDir);
3629
+ const existing = findSkillById(req.params["id"], skills);
3630
+ if (!existing) return res.status(404).json({ error: "Not found" });
3631
+ const result = await deleteOwnedSkill(existing);
3632
+ if (!result.success) return res.status(403).json({ error: result.reason ?? "Cannot delete this skill" });
3633
+ res.json({ success: true });
3634
+ });
3635
+ router.use(createCrudRoutes(createConfig(configDir, projectDir), configDir, projectDir));
3636
+ return router;
3421
3637
  }
3422
3638
 
3423
3639
  // src/server/commands/registry.ts
3424
- import { writeFile, mkdir, unlink } from "fs/promises";
3425
- import { join as join3, dirname as dirname3 } from "path";
3640
+ import { writeFile as writeFile2, mkdir as mkdir3, unlink } from "fs/promises";
3641
+ import { join as join5, dirname as dirname4 } from "path";
3426
3642
  import { fileURLToPath as fileURLToPath4 } from "url";
3427
- import matter from "gray-matter";
3428
- var __bundleDir = dirname3(fileURLToPath4(import.meta.url));
3429
- var DEFAULTS_DIR = join3(__bundleDir, "defaults");
3430
- var DEFAULTS_DIR_ALT = join3(__bundleDir, "command-defaults");
3643
+ import matter2 from "gray-matter";
3644
+ var __bundleDir = dirname4(fileURLToPath4(import.meta.url));
3645
+ var DEFAULTS_DIR = join5(__bundleDir, "defaults");
3646
+ var DEFAULTS_DIR_ALT = join5(__bundleDir, "command-defaults");
3431
3647
  var COMMAND_EXTENSION = ".command.md";
3432
3648
  function getCommandsDir(configDir) {
3433
- return join3(configDir, "commands");
3649
+ return join5(configDir, "commands");
3434
3650
  }
3435
3651
  function getProjectCommandsDir(projectDir) {
3436
- return join3(projectDir, ".openfox", "commands");
3652
+ return join5(projectDir, ".openfox", "commands");
3437
3653
  }
3438
3654
  async function loadDefaultCommands() {
3439
3655
  let defaults = await loadItemsFromDir(DEFAULTS_DIR, {
@@ -3490,26 +3706,26 @@ function findCommandById(commandId, commands) {
3490
3706
  return commands.find((c) => c.metadata.id === commandId);
3491
3707
  }
3492
3708
  async function commandExists(configDir, commandId, projectDir) {
3493
- if (await pathExists(join3(getCommandsDir(configDir), `${commandId}${COMMAND_EXTENSION}`))) return true;
3494
- if (projectDir && await pathExists(join3(getProjectCommandsDir(projectDir), `${commandId}${COMMAND_EXTENSION}`)))
3709
+ if (await pathExists(join5(getCommandsDir(configDir), `${commandId}${COMMAND_EXTENSION}`))) return true;
3710
+ if (projectDir && await pathExists(join5(getProjectCommandsDir(projectDir), `${commandId}${COMMAND_EXTENSION}`)))
3495
3711
  return true;
3496
3712
  return false;
3497
3713
  }
3498
3714
  async function saveCommand(configDir, command) {
3499
3715
  const commandsDir = getCommandsDir(configDir);
3500
3716
  if (!await pathExists(commandsDir)) {
3501
- await mkdir(commandsDir, { recursive: true });
3717
+ await mkdir3(commandsDir, { recursive: true });
3502
3718
  }
3503
- const filePath = join3(commandsDir, `${command.metadata.id}${COMMAND_EXTENSION}`);
3504
- const content = matter.stringify(command.prompt, command.metadata);
3505
- await writeFile(filePath, content, "utf-8");
3719
+ const filePath = join5(commandsDir, `${command.metadata.id}${COMMAND_EXTENSION}`);
3720
+ const content = matter2.stringify(command.prompt, command.metadata);
3721
+ await writeFile2(filePath, content, "utf-8");
3506
3722
  }
3507
3723
  async function saveCommandToProject(projectDir, command) {
3508
3724
  await saveItemToDir(
3509
3725
  getProjectCommandsDir(projectDir),
3510
3726
  command,
3511
3727
  COMMAND_EXTENSION,
3512
- (c) => matter.stringify(c.prompt, c.metadata)
3728
+ (c) => matter2.stringify(c.prompt, c.metadata)
3513
3729
  );
3514
3730
  }
3515
3731
  async function deleteProjectCommand(projectDir, commandId) {
@@ -3520,7 +3736,7 @@ async function deleteCommand(configDir, commandId) {
3520
3736
  if (isDefault) {
3521
3737
  return { success: false, reason: "Cannot delete built-in defaults" };
3522
3738
  }
3523
- const filePath = join3(getCommandsDir(configDir), `${commandId}${COMMAND_EXTENSION}`);
3739
+ const filePath = join5(getCommandsDir(configDir), `${commandId}${COMMAND_EXTENSION}`);
3524
3740
  try {
3525
3741
  await unlink(filePath);
3526
3742
  return { success: true };
@@ -3530,7 +3746,7 @@ async function deleteCommand(configDir, commandId) {
3530
3746
  }
3531
3747
 
3532
3748
  // src/server/routes/commands.ts
3533
- var config2 = {
3749
+ var config = {
3534
3750
  dirName: "commands",
3535
3751
  ext: ".command.md",
3536
3752
  loadDefaults: loadDefaultCommands,
@@ -3549,11 +3765,11 @@ var config2 = {
3549
3765
  mapToResponse: (c) => c.metadata
3550
3766
  };
3551
3767
  function createCommandRoutes(configDir, projectDir) {
3552
- return createCrudRoutes(config2, configDir, projectDir);
3768
+ return createCrudRoutes(config, configDir, projectDir);
3553
3769
  }
3554
3770
 
3555
3771
  // src/server/routes/agents.ts
3556
- var config3 = {
3772
+ var config2 = {
3557
3773
  dirName: "agents",
3558
3774
  ext: ".agent.md",
3559
3775
  loadDefaults: loadDefaultAgents,
@@ -3576,12 +3792,12 @@ var config3 = {
3576
3792
  mapToResponse: (a) => a.metadata
3577
3793
  };
3578
3794
  function createAgentRoutes(configDir, projectDir) {
3579
- return createCrudRoutes(config3, configDir, projectDir);
3795
+ return createCrudRoutes(config2, configDir, projectDir);
3580
3796
  }
3581
3797
 
3582
3798
  // src/server/routes/workflows.ts
3583
3799
  import "express";
3584
- function createWorkflowRoutes(configDir, config4, projectDir) {
3800
+ function createWorkflowRoutes(configDir, config3, projectDir) {
3585
3801
  const crudConfig = {
3586
3802
  dirName: "workflows",
3587
3803
  ext: ".workflow.json",
@@ -3610,7 +3826,7 @@ function createWorkflowRoutes(configDir, config4, projectDir) {
3610
3826
  subGroups: subGroups.length > 0 ? subGroups : void 0
3611
3827
  };
3612
3828
  },
3613
- extraGetData: () => Promise.resolve({ activeWorkflowId: config4.activeWorkflowId ?? "default" }),
3829
+ extraGetData: () => Promise.resolve({ activeWorkflowId: config3.activeWorkflowId ?? "default" }),
3614
3830
  extraRoutes: (router) => {
3615
3831
  router.get("/template-variables", (_req, res) => {
3616
3832
  res.json({ variables: TEMPLATE_VARIABLES });
@@ -3621,19 +3837,19 @@ function createWorkflowRoutes(configDir, config4, projectDir) {
3621
3837
  }
3622
3838
 
3623
3839
  // src/server/routes/dev-server.ts
3624
- import { Router as Router3 } from "express";
3840
+ import { Router as Router4 } from "express";
3625
3841
  function createDevServerRoutes() {
3626
- const router = Router3();
3842
+ const router = Router4();
3627
3843
  router.get("/", async (req, res) => {
3628
3844
  const workdir = req.query["workdir"];
3629
3845
  if (!workdir) return res.status(400).json({ error: "workdir required" });
3630
3846
  const status = devServerManager.getStatus(workdir);
3631
3847
  if (!status.config) {
3632
- const config4 = await devServerManager.loadConfig(workdir);
3633
- if (config4) {
3634
- status.config = config4;
3635
- status.url = config4.url;
3636
- status.hotReload = config4.hotReload;
3848
+ const config3 = await devServerManager.loadConfig(workdir);
3849
+ if (config3) {
3850
+ status.config = config3;
3851
+ status.url = config3.url;
3852
+ status.hotReload = config3.hotReload;
3637
3853
  }
3638
3854
  }
3639
3855
  res.json(status);
@@ -3679,8 +3895,8 @@ function createDevServerRoutes() {
3679
3895
  router.get("/config", async (req, res) => {
3680
3896
  const workdir = req.query["workdir"];
3681
3897
  if (!workdir) return res.status(400).json({ error: "workdir required" });
3682
- const config4 = await devServerManager.loadConfig(workdir);
3683
- res.json({ config: config4 });
3898
+ const config3 = await devServerManager.loadConfig(workdir);
3899
+ res.json({ config: config3 });
3684
3900
  });
3685
3901
  router.post("/config", async (req, res) => {
3686
3902
  const workdir = req.query["workdir"];
@@ -3690,9 +3906,9 @@ function createDevServerRoutes() {
3690
3906
  return res.status(400).json({ error: "command and url are required" });
3691
3907
  }
3692
3908
  try {
3693
- const config4 = { command, url, hotReload: hotReload ?? false, disableInspect: disableInspect ?? false };
3694
- await devServerManager.saveConfig(workdir, config4);
3695
- res.json({ config: config4 });
3909
+ const config3 = { command, url, hotReload: hotReload ?? false, disableInspect: disableInspect ?? false };
3910
+ await devServerManager.saveConfig(workdir, config3);
3911
+ res.json({ config: config3 });
3696
3912
  } catch (err) {
3697
3913
  res.status(500).json({ error: err instanceof Error ? err.message : "Failed to save config" });
3698
3914
  }
@@ -3701,9 +3917,9 @@ function createDevServerRoutes() {
3701
3917
  }
3702
3918
 
3703
3919
  // src/server/routes/terminals.ts
3704
- import { Router as Router4 } from "express";
3920
+ import { Router as Router5 } from "express";
3705
3921
  function createTerminalRoutes() {
3706
- const router = Router4();
3922
+ const router = Router5();
3707
3923
  router.get("/", (req, res) => {
3708
3924
  const projectId = req.query["projectId"];
3709
3925
  const sessions = projectId ? terminalManager.getByProject(projectId) : [];
@@ -3742,22 +3958,22 @@ function createTerminalRoutes() {
3742
3958
  }
3743
3959
 
3744
3960
  // src/server/routes/directories.ts
3745
- import { Router as Router5 } from "express";
3961
+ import { Router as Router6 } from "express";
3746
3962
  import { readdir } from "fs/promises";
3747
- import { resolve as resolve2, join as join4, dirname as dirname4, basename as basename2 } from "path";
3963
+ import { resolve as resolve3, join as join6, dirname as dirname5, basename as basename2 } from "path";
3748
3964
  function createDirectoryRoutes() {
3749
- const router = Router5();
3965
+ const router = Router6();
3750
3966
  const DEFAULT_BASE_PATH = process.cwd();
3751
3967
  router.get("/", async (req, res) => {
3752
3968
  const path = req.query["path"] || DEFAULT_BASE_PATH;
3753
3969
  try {
3754
- const resolvedPath = resolve2(path);
3970
+ const resolvedPath = resolve3(path);
3755
3971
  const entries = await readdir(resolvedPath, { withFileTypes: true });
3756
3972
  const directories = entries.filter((entry) => entry.isDirectory()).map((entry) => ({
3757
3973
  name: entry.name,
3758
- path: join4(resolvedPath, entry.name)
3974
+ path: join6(resolvedPath, entry.name)
3759
3975
  })).sort((a, b) => a.name.localeCompare(b.name));
3760
- const parent = dirname4(resolvedPath);
3976
+ const parent = dirname5(resolvedPath);
3761
3977
  const hasParent = parent !== resolvedPath;
3762
3978
  res.json({
3763
3979
  current: resolvedPath,
@@ -3779,10 +3995,10 @@ function createDirectoryRoutes() {
3779
3995
  }
3780
3996
 
3781
3997
  // src/server/routes/file-search.ts
3782
- import { Router as Router6 } from "express";
3998
+ import { Router as Router7 } from "express";
3783
3999
  import fg from "fast-glob";
3784
4000
  function createFileSearchRoutes() {
3785
- const router = Router6();
4001
+ const router = Router7();
3786
4002
  router.get("/", async (req, res) => {
3787
4003
  const query = req.query["q"] || "";
3788
4004
  const workdir = req.query["workdir"] || process.cwd();
@@ -3858,11 +4074,11 @@ function fuzzyScore(filePath, query) {
3858
4074
  }
3859
4075
 
3860
4076
  // src/server/routes/auto-update.ts
3861
- import { Router as Router7 } from "express";
4077
+ import { Router as Router8 } from "express";
3862
4078
  import { spawn as spawn2 } from "child_process";
3863
4079
 
3864
4080
  // src/constants.ts
3865
- var VERSION = "2.0.59";
4081
+ var VERSION = "2.0.61";
3866
4082
 
3867
4083
  // src/server/routes/auto-update.ts
3868
4084
  var updateInProgress = false;
@@ -3880,12 +4096,12 @@ async function checkAuth(req, opts) {
3880
4096
  return true;
3881
4097
  }
3882
4098
  function createAutoUpdateRoutes(options = {}) {
3883
- const router = Router7();
4099
+ const router = Router8();
3884
4100
  router.get("/check", async (_req, res) => {
3885
4101
  const isService = isRunningAsService();
3886
4102
  const current = VERSION;
3887
4103
  try {
3888
- const latest = await new Promise((resolve4, reject) => {
4104
+ const latest = await new Promise((resolve5, reject) => {
3889
4105
  const child = spawn2("npm", ["view", "openfox", "version"], {
3890
4106
  stdio: ["ignore", "pipe", "pipe"],
3891
4107
  windowsHide: true
@@ -3896,7 +4112,7 @@ function createAutoUpdateRoutes(options = {}) {
3896
4112
  });
3897
4113
  child.on("close", (code) => {
3898
4114
  if (code === 0) {
3899
- resolve4(stdout.trim());
4115
+ resolve5(stdout.trim());
3900
4116
  } else {
3901
4117
  reject(new Error(`npm view exited with code ${code}`));
3902
4118
  }
@@ -3940,9 +4156,9 @@ function createAutoUpdateRoutes(options = {}) {
3940
4156
  const timeout = setTimeout(() => {
3941
4157
  child.kill();
3942
4158
  }, UPDATE_TIMEOUT);
3943
- const exitCode = await new Promise((resolve4) => {
3944
- child.on("close", resolve4);
3945
- child.on("error", () => resolve4(1));
4159
+ const exitCode = await new Promise((resolve5) => {
4160
+ child.on("close", resolve5);
4161
+ child.on("error", () => resolve5(1));
3946
4162
  });
3947
4163
  clearTimeout(timeout);
3948
4164
  if (exitCode === 0) {
@@ -3980,15 +4196,15 @@ function createAutoUpdateRoutes(options = {}) {
3980
4196
  }
3981
4197
 
3982
4198
  // src/server/index.ts
3983
- var __dirname3 = dirname5(fileURLToPath5(import.meta.url));
3984
- async function createServerHandle(config4) {
3985
- setRuntimeConfig(config4);
3986
- setLogLevel(config4.logging?.level ?? void 0, config4.mode);
4199
+ var __dirname3 = dirname6(fileURLToPath5(import.meta.url));
4200
+ async function createServerHandle(config3) {
4201
+ setRuntimeConfig(config3);
4202
+ setLogLevel(config3.logging?.level ?? void 0, config3.mode);
3987
4203
  await loadServerAuthConfig();
3988
- const db = initDatabase(config4);
4204
+ const db = initDatabase(config3);
3989
4205
  initEventStore(db);
3990
- const configDir = getGlobalConfigDir(config4.mode ?? "production");
3991
- const providerManager = createProviderManager(config4);
4206
+ const configDir = getGlobalConfigDir(config3.mode ?? "production");
4207
+ const providerManager = createProviderManager(config3);
3992
4208
  const sessionManager = new SessionManager(providerManager);
3993
4209
  devServerManager.setSessionManager(sessionManager);
3994
4210
  const useMock = process.env["OPENFOX_MOCK_LLM"] === "true";
@@ -3999,24 +4215,24 @@ async function createServerHandle(config4) {
3999
4215
  }
4000
4216
  async function initLLM() {
4001
4217
  const llmClient = getLLMClient();
4002
- const backend = config4.llm.backend;
4218
+ const backend = config3.llm.backend;
4003
4219
  llmClient.setBackend(backend);
4004
4220
  if (!useMock) {
4005
4221
  logger.info("Using configured LLM backend", { backend: getBackendDisplayName(backend) });
4006
4222
  }
4007
- const detected = await detectModel(config4.llm.baseUrl);
4008
- if (detected && !config4.defaultModelSelection) {
4223
+ const detected = await detectModel(config3.llm.baseUrl);
4224
+ if (detected && !config3.defaultModelSelection) {
4009
4225
  llmClient.setModel(detected);
4010
4226
  if (!useMock) {
4011
4227
  logger.info("Auto-detected LLM model", { model: detected, backend: getBackendDisplayName(backend) });
4012
4228
  }
4013
- } else if (detected && config4.defaultModelSelection) {
4229
+ } else if (detected && config3.defaultModelSelection) {
4014
4230
  if (!useMock) {
4015
- logger.debug("Skipping auto-detect, using configured model", { model: config4.llm.model });
4231
+ logger.debug("Skipping auto-detect, using configured model", { model: config3.llm.model });
4016
4232
  }
4017
4233
  } else {
4018
4234
  if (!useMock) {
4019
- logger.warn("Could not auto-detect model, using config", { model: config4.llm.model });
4235
+ logger.warn("Could not auto-detect model, using config", { model: config3.llm.model });
4020
4236
  }
4021
4237
  }
4022
4238
  const activeProvider = providerManager.getActiveProvider();
@@ -4037,14 +4253,14 @@ async function createServerHandle(config4) {
4037
4253
  onToolsDiscovered: async (name, tools) => {
4038
4254
  try {
4039
4255
  const { loadGlobalConfig, saveGlobalConfig } = await import("./config-GMQUPNBY.js");
4040
- const mode = config4.mode ?? "production";
4041
- const globalConfig = await loadGlobalConfig(mode, config4.globalConfigPath);
4256
+ const mode = config3.mode ?? "production";
4257
+ const globalConfig = await loadGlobalConfig(mode, config3.globalConfigPath);
4042
4258
  const mcpServers2 = {
4043
4259
  ...globalConfig.mcpServers ?? {}
4044
4260
  };
4045
4261
  if (mcpServers2[name]) {
4046
4262
  mcpServers2[name] = { ...mcpServers2[name], cachedTools: tools };
4047
- await saveGlobalConfig(mode, { ...globalConfig, mcpServers: mcpServers2 }, config4.globalConfigPath);
4263
+ await saveGlobalConfig(mode, { ...globalConfig, mcpServers: mcpServers2 }, config3.globalConfigPath);
4048
4264
  }
4049
4265
  } catch (err) {
4050
4266
  logger.warn("Failed to persist MCP tool cache", { name, error: String(err) });
@@ -4052,9 +4268,9 @@ async function createServerHandle(config4) {
4052
4268
  }
4053
4269
  });
4054
4270
  setMcpManagerForTools(mcpManager);
4055
- setMcpConfigMode(config4.mode ?? "production");
4056
- setMcpConfigPath(config4.globalConfigPath);
4057
- const mcpServers = config4.mcpServers ?? {};
4271
+ setMcpConfigMode(config3.mode ?? "production");
4272
+ setMcpConfigPath(config3.globalConfigPath);
4273
+ const mcpServers = config3.mcpServers ?? {};
4058
4274
  Promise.all(
4059
4275
  Object.entries(mcpServers).map(
4060
4276
  ([name, serverConfig]) => mcpManager.addServer(name, serverConfig).catch((err) => {
@@ -4067,7 +4283,7 @@ async function createServerHandle(config4) {
4067
4283
  setMcpTools(mcpTools);
4068
4284
  logger.info("MCP tools registered", { count: mcpTools.length });
4069
4285
  }
4070
- const { signalMcpReady } = await import("./server-KDGQZNYQ.js");
4286
+ const { signalMcpReady } = await import("./server-JDB2QVKP.js");
4071
4287
  signalMcpReady();
4072
4288
  });
4073
4289
  const app = express();
@@ -4253,14 +4469,14 @@ async function createServerHandle(config4) {
4253
4469
  if (!project) {
4254
4470
  return res.status(404).json({ error: "Project not found" });
4255
4471
  }
4256
- const { providerId, model } = parseDefaultModelSelection(config4.defaultModelSelection);
4472
+ const { providerId, model } = parseDefaultModelSelection(config3.defaultModelSelection);
4257
4473
  const session = sessionManager.createSession(projectId, title, providerId ?? null, model ?? null);
4258
4474
  res.status(201).json({ session });
4259
4475
  });
4260
4476
  app.get("/api/sessions/:id", async (req, res) => {
4261
4477
  const { getEventStore: getEventStore2 } = await import("./events-52XADT6Y.js");
4262
4478
  const { buildMessagesFromStoredEvents } = await import("./folding-2BGFLV5J.js");
4263
- const { getPendingQuestionsForSession } = await import("./tools-FQ54IAYG.js");
4479
+ const { getPendingQuestionsForSession } = await import("./tools-7QUMO5DJ.js");
4264
4480
  const session = sessionManager.getSession(req.params.id);
4265
4481
  if (!session) {
4266
4482
  return res.status(404).json({ error: "Session not found" });
@@ -4279,8 +4495,8 @@ async function createServerHandle(config4) {
4279
4495
  if (!session) {
4280
4496
  return res.status(404).json({ error: "Session not found" });
4281
4497
  }
4282
- const { stopSessionExecution } = await import("./chat-handler-AS44EMQE.js");
4283
- const { cancelQuestionsForSession, cancelPathConfirmationsForSession } = await import("./tools-FQ54IAYG.js");
4498
+ const { stopSessionExecution } = await import("./chat-handler-6DRGMDXZ.js");
4499
+ const { cancelQuestionsForSession, cancelPathConfirmationsForSession } = await import("./tools-7QUMO5DJ.js");
4284
4500
  sessionManager.clearMessageQueue(sessionId);
4285
4501
  stopSessionExecution(sessionId, sessionManager);
4286
4502
  abortSession(sessionId);
@@ -4334,11 +4550,11 @@ async function createServerHandle(config4) {
4334
4550
  return res.status(500).json({ error: result.error ?? "Failed to set default model" });
4335
4551
  }
4336
4552
  const { loadGlobalConfig, saveGlobalConfig, setDefaultModelSelection } = await import("./config-GMQUPNBY.js");
4337
- const globalConfig = await loadGlobalConfig(config4.mode ?? "production", config4.globalConfigPath);
4553
+ const globalConfig = await loadGlobalConfig(config3.mode ?? "production", config3.globalConfigPath);
4338
4554
  const updatedConfig = setDefaultModelSelection(globalConfig, providerId, model);
4339
- await saveGlobalConfig(config4.mode ?? "production", updatedConfig, config4.globalConfigPath);
4340
- config4.defaultModelSelection = updatedConfig.defaultModelSelection;
4341
- res.json({ success: true, defaultModelSelection: config4.defaultModelSelection });
4555
+ await saveGlobalConfig(config3.mode ?? "production", updatedConfig, config3.globalConfigPath);
4556
+ config3.defaultModelSelection = updatedConfig.defaultModelSelection;
4557
+ res.json({ success: true, defaultModelSelection: config3.defaultModelSelection });
4342
4558
  });
4343
4559
  app.put("/api/sessions/:id/criteria", async (req, res) => {
4344
4560
  const sessionId = req.params.id;
@@ -4434,7 +4650,7 @@ async function createServerHandle(config4) {
4434
4650
  if (!callId || approved === void 0) {
4435
4651
  return res.status(400).json({ error: "callId and approved are required" });
4436
4652
  }
4437
- const { providePathConfirmation } = await import("./tools-FQ54IAYG.js");
4653
+ const { providePathConfirmation } = await import("./tools-7QUMO5DJ.js");
4438
4654
  const result = providePathConfirmation(callId, approved, alwaysAllow);
4439
4655
  if (!result.found) {
4440
4656
  return res.status(404).json({ error: "No pending path confirmation with that ID" });
@@ -4442,7 +4658,7 @@ async function createServerHandle(config4) {
4442
4658
  const { getEventStore: getEventStore2 } = await import("./events-52XADT6Y.js");
4443
4659
  const { buildMessagesFromStoredEvents, foldPendingConfirmations } = await import("./folding-2BGFLV5J.js");
4444
4660
  const { createSessionStateMessage } = await import("./protocol-CTNE3ANP.js");
4445
- const { getPendingQuestionsForSession } = await import("./tools-FQ54IAYG.js");
4661
+ const { getPendingQuestionsForSession } = await import("./tools-7QUMO5DJ.js");
4446
4662
  const eventStore = getEventStore2();
4447
4663
  const events = eventStore.getEvents(sessionId);
4448
4664
  const messages = buildMessagesFromStoredEvents(events);
@@ -4463,7 +4679,7 @@ async function createServerHandle(config4) {
4463
4679
  if (!skip && typeof answer !== "string") {
4464
4680
  return res.status(400).json({ error: "answer is required when not skipping" });
4465
4681
  }
4466
- const { provideAnswer } = await import("./tools-FQ54IAYG.js");
4682
+ const { provideAnswer } = await import("./tools-7QUMO5DJ.js");
4467
4683
  const found = provideAnswer(callId, answer ?? "", skip ?? false);
4468
4684
  if (!found) {
4469
4685
  return res.status(404).json({ error: "No pending question with that ID" });
@@ -4491,8 +4707,8 @@ async function createServerHandle(config4) {
4491
4707
  if (!session) {
4492
4708
  return res.status(404).json({ error: "Session not found" });
4493
4709
  }
4494
- const { getSetting, SETTINGS_KEYS } = await import("./settings-X43DCM55.js");
4495
- if (getSetting(SETTINGS_KEYS.CACHE_WARMING) !== "true") {
4710
+ const { getSetting: getSetting2, SETTINGS_KEYS } = await import("./settings-ASLTSUE2.js");
4711
+ if (getSetting2(SETTINGS_KEYS.CACHE_WARMING) !== "true") {
4496
4712
  return res.json({ success: false, reason: "disabled" });
4497
4713
  }
4498
4714
  if (session.messages.length > 0) {
@@ -4523,7 +4739,7 @@ async function createServerHandle(config4) {
4523
4739
  backend: activeProvider?.backend ?? llmClient.getBackend(),
4524
4740
  model: llmClient.getModel()
4525
4741
  };
4526
- const { runAgentTurn, TurnMetrics } = await import("./orchestrator-MH7J2YVP.js");
4742
+ const { runAgentTurn, TurnMetrics } = await import("./orchestrator-AHCSLBB3.js");
4527
4743
  runAgentTurn(
4528
4744
  {
4529
4745
  sessionManager,
@@ -4561,8 +4777,8 @@ async function createServerHandle(config4) {
4561
4777
  if (!session) {
4562
4778
  return res.status(404).json({ error: "Session not found" });
4563
4779
  }
4564
- const { stopSessionExecution } = await import("./chat-handler-AS44EMQE.js");
4565
- const { cancelQuestionsForSession, cancelPathConfirmationsForSession } = await import("./tools-FQ54IAYG.js");
4780
+ const { stopSessionExecution } = await import("./chat-handler-6DRGMDXZ.js");
4781
+ const { cancelQuestionsForSession, cancelPathConfirmationsForSession } = await import("./tools-7QUMO5DJ.js");
4566
4782
  const queuedMessages = sessionManager.getQueueState(sessionId);
4567
4783
  sessionManager.clearMessageQueue(sessionId);
4568
4784
  stopSessionExecution(sessionId, sessionManager);
@@ -4642,7 +4858,7 @@ async function createServerHandle(config4) {
4642
4858
  res.json({ accepted: true });
4643
4859
  });
4644
4860
  app.get("/api/settings", async (req, res) => {
4645
- const { getSetting, SETTINGS_DEFAULTS } = await import("./settings-X43DCM55.js");
4861
+ const { getSetting: getSetting2, SETTINGS_DEFAULTS } = await import("./settings-ASLTSUE2.js");
4646
4862
  const keysParam = req.query["keys"];
4647
4863
  if (!keysParam) {
4648
4864
  return res.status(400).json({ error: "keys query parameter is required" });
@@ -4650,30 +4866,30 @@ async function createServerHandle(config4) {
4650
4866
  const keys = keysParam.split(",").map((k) => k.trim());
4651
4867
  const result = {};
4652
4868
  for (const key of keys) {
4653
- result[key] = getSetting(key) ?? SETTINGS_DEFAULTS[key] ?? "";
4869
+ result[key] = getSetting2(key) ?? SETTINGS_DEFAULTS[key] ?? "";
4654
4870
  }
4655
4871
  res.json(result);
4656
4872
  });
4657
4873
  app.get("/api/settings/:key", async (req, res) => {
4658
- const { getSetting, SETTINGS_DEFAULTS } = await import("./settings-X43DCM55.js");
4874
+ const { getSetting: getSetting2, SETTINGS_DEFAULTS } = await import("./settings-ASLTSUE2.js");
4659
4875
  const key = req.params.key;
4660
- const value = getSetting(key) ?? SETTINGS_DEFAULTS[key] ?? null;
4876
+ const value = getSetting2(key) ?? SETTINGS_DEFAULTS[key] ?? null;
4661
4877
  res.json({ key, value });
4662
4878
  });
4663
4879
  app.get("/api/settings/:key", async (req, res) => {
4664
- const { getSetting, SETTINGS_DEFAULTS } = await import("./settings-X43DCM55.js");
4880
+ const { getSetting: getSetting2, SETTINGS_DEFAULTS } = await import("./settings-ASLTSUE2.js");
4665
4881
  const key = req.params.key;
4666
- const value = getSetting(key) ?? SETTINGS_DEFAULTS[key] ?? null;
4882
+ const value = getSetting2(key) ?? SETTINGS_DEFAULTS[key] ?? null;
4667
4883
  res.json({ key, value });
4668
4884
  });
4669
4885
  app.put("/api/settings/:key", async (req, res) => {
4670
- const { setSetting } = await import("./settings-X43DCM55.js");
4886
+ const { setSetting: setSetting2 } = await import("./settings-ASLTSUE2.js");
4671
4887
  const key = req.params.key;
4672
4888
  const { value } = req.body;
4673
4889
  if (value === void 0) {
4674
4890
  return res.status(400).json({ error: "value is required" });
4675
4891
  }
4676
- setSetting(key, value);
4892
+ setSetting2(key, value);
4677
4893
  res.json({ key, value });
4678
4894
  });
4679
4895
  app.get("/api/config", async (_req, res) => {
@@ -4683,7 +4899,7 @@ async function createServerHandle(config4) {
4683
4899
  let globalWorkdir;
4684
4900
  try {
4685
4901
  const { loadGlobalConfig, getVisionFallback } = await import("./config-GMQUPNBY.js");
4686
- const globalConfig = await loadGlobalConfig(config4.mode ?? "production", config4.globalConfigPath);
4902
+ const globalConfig = await loadGlobalConfig(config3.mode ?? "production", config3.globalConfigPath);
4687
4903
  const fallback = getVisionFallback(globalConfig);
4688
4904
  if (fallback) {
4689
4905
  visionFallback = {
@@ -4701,13 +4917,13 @@ async function createServerHandle(config4) {
4701
4917
  version: VERSION,
4702
4918
  model: llmClient.getModel(),
4703
4919
  maxContext: providerManager.getCurrentModelContext(),
4704
- llmUrl: activeProvider?.url ?? config4.llm.baseUrl,
4920
+ llmUrl: activeProvider?.url ?? config3.llm.baseUrl,
4705
4921
  llmStatus: getLlmStatus(),
4706
4922
  backend: llmClient.getBackend(),
4707
- workdir: globalWorkdir ?? config4.workdir,
4923
+ workdir: globalWorkdir ?? config3.workdir,
4708
4924
  providers: providerManager.getProviders(),
4709
4925
  activeProviderId: providerManager.getActiveProviderId(),
4710
- defaultModelSelection: config4.defaultModelSelection,
4926
+ defaultModelSelection: config3.defaultModelSelection,
4711
4927
  visionFallback
4712
4928
  });
4713
4929
  });
@@ -4716,7 +4932,7 @@ async function createServerHandle(config4) {
4716
4932
  const currentModel = providerManager.getCurrentModel();
4717
4933
  if (currentModel === "auto") {
4718
4934
  const activeProvider = providerManager.getActiveProvider();
4719
- const baseUrl = activeProvider?.url ?? config4.llm.baseUrl;
4935
+ const baseUrl = activeProvider?.url ?? config3.llm.baseUrl;
4720
4936
  const detected = await detectModel(baseUrl);
4721
4937
  if (detected) {
4722
4938
  llmClient.setModel(detected);
@@ -4775,6 +4991,56 @@ async function createServerHandle(config4) {
4775
4991
  });
4776
4992
  }
4777
4993
  });
4994
+ app.post("/api/search/test", async (req, res) => {
4995
+ const { engine, tavilyApiKey, searxngUrl, searxngApiKey } = req.body;
4996
+ try {
4997
+ if (engine === "tavily") {
4998
+ const key = tavilyApiKey || process.env["TAVILY_API_KEY"];
4999
+ if (!key) {
5000
+ return res.status(400).json({ success: false, error: "Tavily API key is required" });
5001
+ }
5002
+ const response = await fetch("https://api.tavily.com/search", {
5003
+ method: "POST",
5004
+ headers: { "Content-Type": "application/json" },
5005
+ body: JSON.stringify({ api_key: key, query: "test", max_results: 1 }),
5006
+ signal: AbortSignal.timeout(1e4)
5007
+ });
5008
+ if (!response.ok) {
5009
+ const body = await response.text().catch(() => "");
5010
+ return res.status(400).json({ success: false, error: `Tavily error (${response.status}): ${body}` });
5011
+ }
5012
+ return res.json({ success: true, message: "Tavily connection OK" });
5013
+ }
5014
+ if (engine === "searxng") {
5015
+ const url = searxngUrl || process.env["SEARXNG_URL"];
5016
+ if (!url) {
5017
+ return res.status(400).json({ success: false, error: "SearXNG URL is required" });
5018
+ }
5019
+ const searchUrl = new URL(`${url.replace(/\/+$/, "")}/search`);
5020
+ searchUrl.searchParams.set("format", "json");
5021
+ searchUrl.searchParams.set("q", "test");
5022
+ const apiKey = searxngApiKey || process.env["SEARXNG_API_KEY"] || void 0;
5023
+ const headers = {};
5024
+ if (apiKey) headers["Authorization"] = `Bearer ${apiKey}`;
5025
+ const response = await fetch(searchUrl.toString(), {
5026
+ headers,
5027
+ signal: AbortSignal.timeout(1e4)
5028
+ });
5029
+ if (!response.ok) {
5030
+ const body = await response.text().catch(() => "");
5031
+ return res.status(400).json({ success: false, error: `SearXNG error (${response.status}): ${body}` });
5032
+ }
5033
+ return res.json({ success: true, message: "SearXNG connection OK" });
5034
+ }
5035
+ return res.status(400).json({ success: false, error: "Invalid engine" });
5036
+ } catch (error) {
5037
+ const message = error instanceof Error ? error.message : "Connection failed";
5038
+ if (error instanceof DOMException && error.name === "AbortError") {
5039
+ return res.status(400).json({ success: false, error: "Connection timed out" });
5040
+ }
5041
+ return res.status(400).json({ success: false, error: message });
5042
+ }
5043
+ });
4778
5044
  app.get("/api/providers/models", async (req, res) => {
4779
5045
  const url = req.query["url"];
4780
5046
  const apiKey = req.query["apiKey"];
@@ -4906,7 +5172,7 @@ async function createServerHandle(config4) {
4906
5172
  }
4907
5173
  try {
4908
5174
  const { loadGlobalConfig, saveGlobalConfig, addProvider, setDefaultModelSelection } = await import("./config-GMQUPNBY.js");
4909
- const globalConfig = await loadGlobalConfig(config4.mode ?? "production", config4.globalConfigPath);
5175
+ const globalConfig = await loadGlobalConfig(config3.mode ?? "production", config3.globalConfigPath);
4910
5176
  const providerBackend = backend;
4911
5177
  const providerModels = modelConfigs?.length ? buildModelConfigs(modelConfigs) : model ? [{ id: model, contextWindow: 2e5, source: "user" }] : [];
4912
5178
  const configWithProvider = addProvider(globalConfig, {
@@ -4925,9 +5191,9 @@ async function createServerHandle(config4) {
4925
5191
  configWithProvider.providers[configWithProvider.providers.length - 1].id,
4926
5192
  firstModelId
4927
5193
  );
4928
- await saveGlobalConfig(config4.mode ?? "production", finalConfig, config4.globalConfigPath);
5194
+ await saveGlobalConfig(config3.mode ?? "production", finalConfig, config3.globalConfigPath);
4929
5195
  providerManager.setProviders(finalConfig.providers, finalConfig.defaultModelSelection ?? void 0);
4930
- config4.defaultModelSelection = finalConfig.defaultModelSelection;
5196
+ config3.defaultModelSelection = finalConfig.defaultModelSelection;
4931
5197
  const newProvider = finalConfig.providers[finalConfig.providers.length - 1];
4932
5198
  res.status(201).json({
4933
5199
  success: true,
@@ -4944,13 +5210,13 @@ async function createServerHandle(config4) {
4944
5210
  const { workdir, visionFallback } = req.body;
4945
5211
  try {
4946
5212
  const { loadGlobalConfig, saveGlobalConfig } = await import("./config-GMQUPNBY.js");
4947
- const globalConfig = await loadGlobalConfig(config4.mode ?? "production", config4.globalConfigPath);
5213
+ const globalConfig = await loadGlobalConfig(config3.mode ?? "production", config3.globalConfigPath);
4948
5214
  const updatedConfig = {
4949
5215
  ...globalConfig,
4950
5216
  workspace: workdir ? { workdir } : globalConfig.workspace,
4951
5217
  visionFallback: visionFallback ?? globalConfig.visionFallback
4952
5218
  };
4953
- await saveGlobalConfig(config4.mode ?? "production", updatedConfig, config4.globalConfigPath);
5219
+ await saveGlobalConfig(config3.mode ?? "production", updatedConfig, config3.globalConfigPath);
4954
5220
  res.json({ success: true });
4955
5221
  } catch (error) {
4956
5222
  res.status(500).json({
@@ -4972,11 +5238,11 @@ async function createServerHandle(config4) {
4972
5238
  app.delete("/api/providers/:id", async (req, res) => {
4973
5239
  const { id } = req.params;
4974
5240
  const { loadGlobalConfig, saveGlobalConfig, removeProvider } = await import("./config-GMQUPNBY.js");
4975
- const globalConfig = await loadGlobalConfig(config4.mode ?? "production", config4.globalConfigPath);
5241
+ const globalConfig = await loadGlobalConfig(config3.mode ?? "production", config3.globalConfigPath);
4976
5242
  const updatedConfig = removeProvider(globalConfig, id);
4977
- await saveGlobalConfig(config4.mode ?? "production", updatedConfig, config4.globalConfigPath);
5243
+ await saveGlobalConfig(config3.mode ?? "production", updatedConfig, config3.globalConfigPath);
4978
5244
  providerManager.setProviders(updatedConfig.providers, updatedConfig.defaultModelSelection ?? void 0);
4979
- config4.defaultModelSelection = updatedConfig.defaultModelSelection;
5245
+ config3.defaultModelSelection = updatedConfig.defaultModelSelection;
4980
5246
  res.json({ success: true });
4981
5247
  });
4982
5248
  app.patch("/api/providers/:id", async (req, res) => {
@@ -4984,7 +5250,7 @@ async function createServerHandle(config4) {
4984
5250
  const { isLocal } = req.body;
4985
5251
  try {
4986
5252
  const { loadGlobalConfig, saveGlobalConfig, updateProvider } = await import("./config-GMQUPNBY.js");
4987
- const globalConfig = await loadGlobalConfig(config4.mode ?? "production", config4.globalConfigPath);
5253
+ const globalConfig = await loadGlobalConfig(config3.mode ?? "production", config3.globalConfigPath);
4988
5254
  const provider = globalConfig.providers.find((p) => p.id === id);
4989
5255
  if (!provider) {
4990
5256
  return res.status(404).json({ error: "Provider not found" });
@@ -4992,9 +5258,9 @@ async function createServerHandle(config4) {
4992
5258
  const updates = {};
4993
5259
  if (isLocal !== void 0) updates["isLocal"] = isLocal;
4994
5260
  const updatedConfig = updateProvider(globalConfig, id, updates);
4995
- await saveGlobalConfig(config4.mode ?? "production", updatedConfig, config4.globalConfigPath);
5261
+ await saveGlobalConfig(config3.mode ?? "production", updatedConfig, config3.globalConfigPath);
4996
5262
  providerManager.setProviders(updatedConfig.providers, updatedConfig.defaultModelSelection ?? void 0);
4997
- config4.defaultModelSelection = updatedConfig.defaultModelSelection;
5263
+ config3.defaultModelSelection = updatedConfig.defaultModelSelection;
4998
5264
  res.json({ success: true, provider: updatedConfig.providers.find((p) => p.id === id) });
4999
5265
  } catch (error) {
5000
5266
  res.status(500).json({ error: error instanceof Error ? error.message : "Failed to update provider" });
@@ -5013,7 +5279,7 @@ async function createServerHandle(config4) {
5013
5279
  } = req.body;
5014
5280
  try {
5015
5281
  const { loadGlobalConfig, saveGlobalConfig, updateProvider } = await import("./config-GMQUPNBY.js");
5016
- const globalConfig = await loadGlobalConfig(config4.mode ?? "production", config4.globalConfigPath);
5282
+ const globalConfig = await loadGlobalConfig(config3.mode ?? "production", config3.globalConfigPath);
5017
5283
  const provider = globalConfig.providers.find((p) => p.id === id);
5018
5284
  if (!provider) {
5019
5285
  return res.status(404).json({ error: "Provider not found" });
@@ -5029,9 +5295,9 @@ async function createServerHandle(config4) {
5029
5295
  updates["models"] = buildModelConfigs(modelConfigs);
5030
5296
  }
5031
5297
  const updatedConfig = updateProvider(globalConfig, id, updates);
5032
- await saveGlobalConfig(config4.mode ?? "production", updatedConfig, config4.globalConfigPath);
5298
+ await saveGlobalConfig(config3.mode ?? "production", updatedConfig, config3.globalConfigPath);
5033
5299
  providerManager.setProviders(updatedConfig.providers, updatedConfig.defaultModelSelection ?? void 0);
5034
- config4.defaultModelSelection = updatedConfig.defaultModelSelection;
5300
+ config3.defaultModelSelection = updatedConfig.defaultModelSelection;
5035
5301
  res.json({ success: true, provider: updatedConfig.providers.find((p) => p.id === id) });
5036
5302
  } catch (error) {
5037
5303
  res.status(500).json({ error: error instanceof Error ? error.message : "Failed to update provider" });
@@ -5051,9 +5317,9 @@ async function createServerHandle(config4) {
5051
5317
  }
5052
5318
  const llmClient = getLLMClient();
5053
5319
  const { loadGlobalConfig, saveGlobalConfig, setDefaultModelSelection } = await import("./config-GMQUPNBY.js");
5054
- const globalConfig = await loadGlobalConfig(config4.mode ?? "production", config4.globalConfigPath);
5320
+ const globalConfig = await loadGlobalConfig(config3.mode ?? "production", config3.globalConfigPath);
5055
5321
  const updatedConfig = setDefaultModelSelection(globalConfig, id, llmClient.getModel());
5056
- await saveGlobalConfig(config4.mode ?? "production", updatedConfig, config4.globalConfigPath);
5322
+ await saveGlobalConfig(config3.mode ?? "production", updatedConfig, config3.globalConfigPath);
5057
5323
  res.json({
5058
5324
  success: true,
5059
5325
  activeProviderId: id,
@@ -5077,7 +5343,7 @@ async function createServerHandle(config4) {
5077
5343
  });
5078
5344
  async function rebuildMcpTools() {
5079
5345
  const { createMcpTools: createMcpTools2 } = await import("./tool-adapter-MOGEI6TP.js");
5080
- const { setMcpTools: setMcpTools2 } = await import("./tools-FQ54IAYG.js");
5346
+ const { setMcpTools: setMcpTools2 } = await import("./tools-7QUMO5DJ.js");
5081
5347
  const mcpTools = createMcpTools2(mcpManager);
5082
5348
  setMcpTools2(mcpTools);
5083
5349
  }
@@ -5140,15 +5406,15 @@ async function createServerHandle(config4) {
5140
5406
  await mcpManager.addServer(name, serverCfg);
5141
5407
  const server = mcpManager.getServer(name);
5142
5408
  const { loadGlobalConfig, saveGlobalConfig } = await import("./config-GMQUPNBY.js");
5143
- const globalConfig = await loadGlobalConfig(config4.mode ?? "production", config4.globalConfigPath);
5409
+ const globalConfig = await loadGlobalConfig(config3.mode ?? "production", config3.globalConfigPath);
5144
5410
  const updatedMcpServers = { ...globalConfig.mcpServers ?? {}, [name]: serverCfg };
5145
5411
  await saveGlobalConfig(
5146
- config4.mode ?? "production",
5412
+ config3.mode ?? "production",
5147
5413
  {
5148
5414
  ...globalConfig,
5149
5415
  mcpServers: updatedMcpServers
5150
5416
  },
5151
- config4.globalConfigPath
5417
+ config3.globalConfigPath
5152
5418
  );
5153
5419
  await rebuildMcpTools();
5154
5420
  if (server) {
@@ -5170,16 +5436,16 @@ async function createServerHandle(config4) {
5170
5436
  }
5171
5437
  mcpManager.removeServer(name);
5172
5438
  const { loadGlobalConfig, saveGlobalConfig } = await import("./config-GMQUPNBY.js");
5173
- const globalConfig = await loadGlobalConfig(config4.mode ?? "production", config4.globalConfigPath);
5439
+ const globalConfig = await loadGlobalConfig(config3.mode ?? "production", config3.globalConfigPath);
5174
5440
  const updatedMcpServers = { ...globalConfig.mcpServers ?? {} };
5175
5441
  delete updatedMcpServers[name];
5176
5442
  await saveGlobalConfig(
5177
- config4.mode ?? "production",
5443
+ config3.mode ?? "production",
5178
5444
  {
5179
5445
  ...globalConfig,
5180
5446
  mcpServers: updatedMcpServers
5181
5447
  },
5182
- config4.globalConfigPath
5448
+ config3.globalConfigPath
5183
5449
  );
5184
5450
  await rebuildMcpTools();
5185
5451
  const sessions = sessionManager.listSessions();
@@ -5199,7 +5465,7 @@ async function createServerHandle(config4) {
5199
5465
  const server = mcpManager.getServer(name);
5200
5466
  if (server) {
5201
5467
  const { loadGlobalConfig, saveGlobalConfig } = await import("./config-GMQUPNBY.js");
5202
- const globalConfig = await loadGlobalConfig(config4.mode ?? "production", config4.globalConfigPath);
5468
+ const globalConfig = await loadGlobalConfig(config3.mode ?? "production", config3.globalConfigPath);
5203
5469
  const mcpServers2 = { ...globalConfig.mcpServers ?? {} };
5204
5470
  const serverCfg = mcpServers2[name];
5205
5471
  if (serverCfg) {
@@ -5207,12 +5473,12 @@ async function createServerHandle(config4) {
5207
5473
  const cfg = { ...serverCfg, ...disabledTools.length > 0 ? { disabledTools } : { disabledTools: void 0 } };
5208
5474
  mcpServers2[name] = cfg;
5209
5475
  await saveGlobalConfig(
5210
- config4.mode ?? "production",
5476
+ config3.mode ?? "production",
5211
5477
  {
5212
5478
  ...globalConfig,
5213
5479
  mcpServers: mcpServers2
5214
5480
  },
5215
- config4.globalConfigPath
5481
+ config3.globalConfigPath
5216
5482
  );
5217
5483
  }
5218
5484
  }
@@ -5226,11 +5492,11 @@ async function createServerHandle(config4) {
5226
5492
  res.status(400).json({ error: error instanceof Error ? error.message : String(error) });
5227
5493
  }
5228
5494
  });
5229
- const projectDir = config4.workdir;
5495
+ const projectDir = config3.workdir;
5230
5496
  app.use("/api/skills", createSkillRoutes(configDir, projectDir));
5231
5497
  app.use("/api/commands", createCommandRoutes(configDir, projectDir));
5232
5498
  app.use("/api/agents", createAgentRoutes(configDir, projectDir));
5233
- app.use("/api/workflows", createWorkflowRoutes(configDir, config4, projectDir));
5499
+ app.use("/api/workflows", createWorkflowRoutes(configDir, config3, projectDir));
5234
5500
  app.use("/api/dev-server", createDevServerRoutes());
5235
5501
  app.use("/api/terminals", createTerminalRoutes());
5236
5502
  app.use(
@@ -5274,14 +5540,14 @@ async function createServerHandle(config4) {
5274
5540
  });
5275
5541
  app.use("/api/directories", createDirectoryRoutes());
5276
5542
  app.use("/api/files", createFileSearchRoutes());
5277
- const webDir = resolve3(__dirname3, "../../web");
5278
- const isDev = config4.mode === "development";
5543
+ const webDir = resolve4(__dirname3, "../../web");
5544
+ const isDev = config3.mode === "development";
5279
5545
  let viteServer;
5280
5546
  if (isDev) {
5281
5547
  logger.info("Dev mode: using Vite middleware");
5282
5548
  viteServer = await createViteServer({
5283
5549
  root: webDir,
5284
- configFile: resolve3(__dirname3, "../../web/vite.config.ts"),
5550
+ configFile: resolve4(__dirname3, "../../web/vite.config.ts"),
5285
5551
  server: { middlewareMode: true },
5286
5552
  appType: "spa",
5287
5553
  logLevel: "warn"
@@ -5301,7 +5567,7 @@ async function createServerHandle(config4) {
5301
5567
  }
5302
5568
  });
5303
5569
  app.get("/fox.svg", (_req, res) => {
5304
- readFile(join5(webDir, "fox.svg")).then((content) => {
5570
+ readFile2(join7(webDir, "fox.svg")).then((content) => {
5305
5571
  res.set("Content-Type", "image/svg+xml");
5306
5572
  res.send(content);
5307
5573
  }).catch(() => {
@@ -5310,7 +5576,7 @@ async function createServerHandle(config4) {
5310
5576
  });
5311
5577
  app.use(
5312
5578
  "/sounds",
5313
- express.static(join5(webDir, "public", "sounds"), {
5579
+ express.static(join7(webDir, "public", "sounds"), {
5314
5580
  setHeaders: (res) => {
5315
5581
  res.set("Content-Type", "audio/mpeg");
5316
5582
  }
@@ -5319,24 +5585,24 @@ async function createServerHandle(config4) {
5319
5585
  app.use("/__inspect__.js", (_req, res) => {
5320
5586
  res.set("Access-Control-Allow-Origin", "*");
5321
5587
  res.set("Content-Type", "application/javascript");
5322
- readFile(join5(webDir, "public", "__inspect__.js")).then((content) => res.send(content)).catch(() => res.status(404).send("Not found"));
5588
+ readFile2(join7(webDir, "public", "__inspect__.js")).then((content) => res.send(content)).catch(() => res.status(404).send("Not found"));
5323
5589
  });
5324
5590
  app.get("/*path", (req, res) => {
5325
5591
  if (req.path.startsWith("/api/")) {
5326
5592
  return;
5327
5593
  }
5328
- readFile(join5(webDir, "index.html"), "utf-8").then((indexHtml) => viteServer.transformIndexHtml(req.originalUrl, indexHtml)).then((transformed) => res.send(transformed)).catch((err) => {
5594
+ readFile2(join7(webDir, "index.html"), "utf-8").then((indexHtml) => viteServer.transformIndexHtml(req.originalUrl, indexHtml)).then((transformed) => res.send(transformed)).catch((err) => {
5329
5595
  logger.error("Error serving index.html", { error: err });
5330
5596
  res.status(500).send("Server error");
5331
5597
  });
5332
5598
  });
5333
- logger.info("Vite middleware ready", { port: config4.server.port });
5599
+ logger.info("Vite middleware ready", { port: config3.server.port });
5334
5600
  }
5335
5601
  if (!isDev) {
5336
- const distWebDir = resolve3(__dirname3, "web");
5602
+ const distWebDir = resolve4(__dirname3, "web");
5337
5603
  app.use(
5338
5604
  "/assets",
5339
- express.static(join5(distWebDir, "assets"), {
5605
+ express.static(join7(distWebDir, "assets"), {
5340
5606
  setHeaders: (res, filepath) => {
5341
5607
  if (filepath.endsWith(".css")) {
5342
5608
  res.set("Content-Type", "text/css");
@@ -5344,11 +5610,11 @@ async function createServerHandle(config4) {
5344
5610
  }
5345
5611
  })
5346
5612
  );
5347
- app.use("/manifest.webmanifest", express.static(join5(distWebDir, "manifest.webmanifest")));
5348
- app.use("/registerSW.js", express.static(join5(distWebDir, "registerSW.js")));
5349
- app.use("/sw.js", express.static(join5(distWebDir, "sw.js")));
5613
+ app.use("/manifest.webmanifest", express.static(join7(distWebDir, "manifest.webmanifest")));
5614
+ app.use("/registerSW.js", express.static(join7(distWebDir, "registerSW.js")));
5615
+ app.use("/sw.js", express.static(join7(distWebDir, "sw.js")));
5350
5616
  app.get("/fox.svg", (_req, res) => {
5351
- readFile(join5(distWebDir, "fox.svg")).then((content) => {
5617
+ readFile2(join7(distWebDir, "fox.svg")).then((content) => {
5352
5618
  res.set("Content-Type", "image/svg+xml");
5353
5619
  res.send(content);
5354
5620
  }).catch(() => {
@@ -5357,7 +5623,7 @@ async function createServerHandle(config4) {
5357
5623
  });
5358
5624
  app.use(
5359
5625
  "/sounds",
5360
- express.static(join5(distWebDir, "sounds"), {
5626
+ express.static(join7(distWebDir, "sounds"), {
5361
5627
  setHeaders: (res) => {
5362
5628
  res.set("Content-Type", "audio/mpeg");
5363
5629
  }
@@ -5366,10 +5632,10 @@ async function createServerHandle(config4) {
5366
5632
  app.use("/__inspect__.js", (_req, res) => {
5367
5633
  res.set("Access-Control-Allow-Origin", "*");
5368
5634
  res.set("Content-Type", "application/javascript");
5369
- readFile(join5(distWebDir, "__inspect__.js")).then((content) => res.send(content)).catch(() => res.status(404).send("Not found"));
5635
+ readFile2(join7(distWebDir, "__inspect__.js")).then((content) => res.send(content)).catch(() => res.status(404).send("Not found"));
5370
5636
  });
5371
5637
  app.get("/", (_req, res) => {
5372
- readFile(join5(distWebDir, "index.html"), "utf-8").then((content) => res.send(content)).catch(() => {
5638
+ readFile2(join7(distWebDir, "index.html"), "utf-8").then((content) => res.send(content)).catch(() => {
5373
5639
  res.status(404).send("Web UI not built. Run `npm run build:web`");
5374
5640
  });
5375
5641
  });
@@ -5377,7 +5643,7 @@ async function createServerHandle(config4) {
5377
5643
  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") {
5378
5644
  return;
5379
5645
  }
5380
- readFile(join5(distWebDir, "index.html"), "utf-8").then((content) => res.send(content)).catch(() => {
5646
+ readFile2(join7(distWebDir, "index.html"), "utf-8").then((content) => res.send(content)).catch(() => {
5381
5647
  res.status(404).send("Web UI not built");
5382
5648
  });
5383
5649
  });
@@ -5385,7 +5651,7 @@ async function createServerHandle(config4) {
5385
5651
  const httpServer = createHttpServer(app);
5386
5652
  const wssExports = createWebSocketServer(
5387
5653
  httpServer,
5388
- config4,
5654
+ config3,
5389
5655
  getLLMClient,
5390
5656
  () => providerManager.getActiveProvider(),
5391
5657
  sessionManager,
@@ -5397,7 +5663,7 @@ async function createServerHandle(config4) {
5397
5663
  const state = sessionManager.getContextState(sessionId);
5398
5664
  wssExports.broadcastForSession(sessionId, createContextStateMessage(state));
5399
5665
  });
5400
- const { QueueProcessor } = await import("./processor-34MVJZTD.js");
5666
+ const { QueueProcessor } = await import("./processor-45VWLZIV.js");
5401
5667
  const queueProcessor = new QueueProcessor({
5402
5668
  sessionManager,
5403
5669
  providerManager,
@@ -5418,22 +5684,22 @@ async function createServerHandle(config4) {
5418
5684
  };
5419
5685
  return {
5420
5686
  httpServer,
5421
- ctx: { config: config4, sessionManager, llmClient: getLLMClient(), toolRegistry, providerManager },
5422
- start: (port) => new Promise((resolve4, reject) => {
5423
- const listenPort = port ?? config4.server.port;
5424
- const host = config4.server.host;
5687
+ ctx: { config: config3, sessionManager, llmClient: getLLMClient(), toolRegistry, providerManager },
5688
+ start: (port) => new Promise((resolve5, reject) => {
5689
+ const listenPort = port ?? config3.server.port;
5690
+ const host = config3.server.host;
5425
5691
  httpServer.listen(listenPort, host, () => {
5426
5692
  const addr = httpServer.address();
5427
5693
  const actualPort = typeof addr === "object" && addr ? addr.port : listenPort;
5428
5694
  const client = getLLMClient();
5429
5695
  logger.info(`OpenFox server running at http://${host}:${actualPort}`);
5430
5696
  logger.info(`WebSocket available at ws://${host}:${actualPort}/ws`);
5431
- logger.info(`LLM backend: ${client.getBackend()}, model: ${client.getModel()}, url: ${config4.llm.baseUrl}`);
5432
- resolve4({ port: actualPort });
5697
+ logger.info(`LLM backend: ${client.getBackend()}, model: ${client.getModel()}, url: ${config3.llm.baseUrl}`);
5698
+ resolve5({ port: actualPort });
5433
5699
  });
5434
5700
  httpServer.on("error", reject);
5435
5701
  }),
5436
- close: () => new Promise((resolve4) => {
5702
+ close: () => new Promise((resolve5) => {
5437
5703
  logger.info("Shutting down...");
5438
5704
  void (async () => {
5439
5705
  await devServerManager.stopAll();
@@ -5443,22 +5709,22 @@ async function createServerHandle(config4) {
5443
5709
  const { cleanupAllProcesses } = await import("./store-FXTCHBI4.js");
5444
5710
  cleanupAllProcesses();
5445
5711
  viteServer?.close();
5446
- if (config4.globalConfigPath) {
5712
+ if (config3.globalConfigPath) {
5447
5713
  const { unlink: unlink2 } = await import("fs/promises");
5448
- unlink2(config4.globalConfigPath).catch(() => {
5714
+ unlink2(config3.globalConfigPath).catch(() => {
5449
5715
  });
5450
5716
  }
5451
5717
  for (const client of wss.clients) {
5452
5718
  client.terminate();
5453
5719
  }
5454
5720
  wss.close();
5455
- httpServer.close(() => resolve4());
5721
+ httpServer.close(() => resolve5());
5456
5722
  })();
5457
5723
  })
5458
5724
  };
5459
5725
  }
5460
- async function createServer(config4) {
5461
- const handle = await createServerHandle(config4);
5726
+ async function createServer(config3) {
5727
+ const handle = await createServerHandle(config3);
5462
5728
  await handle.start();
5463
5729
  const shutdown = () => {
5464
5730
  const forceExitTimer = setTimeout(() => {
@@ -5477,4 +5743,4 @@ export {
5477
5743
  createServerHandle,
5478
5744
  createServer
5479
5745
  };
5480
- //# sourceMappingURL=chunk-LQFCQ6PY.js.map
5746
+ //# sourceMappingURL=chunk-LHTVTDQQ.js.map