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.
- package/dist/agent-defaults/builder.agent.md +1 -0
- package/dist/agent-defaults/planner.agent.md +1 -0
- package/dist/{chat-handler-AS44EMQE.js → chat-handler-6DRGMDXZ.js} +4 -4
- package/dist/{chunk-5MDIKIMG.js → chunk-AA4G4DGK.js} +3 -3
- package/dist/{chunk-FRU2X3IZ.js → chunk-KX4C5Q5J.js} +408 -69
- package/dist/{chunk-LQFCQ6PY.js → chunk-LHTVTDQQ.js} +519 -253
- package/dist/{chunk-P7C6KF2H.js → chunk-S5DWUGKZ.js} +2 -2
- package/dist/{chunk-CDRQJDSJ.js → chunk-SVSKQYFH.js} +7 -2
- package/dist/{chunk-EKI5SZC7.js → chunk-ZHSCXD75.js} +3 -3
- package/dist/cli/dev.js +1 -1
- package/dist/cli/index.js +1 -1
- package/dist/{orchestrator-MH7J2YVP.js → orchestrator-AHCSLBB3.js} +4 -4
- package/dist/package.json +4 -2
- package/dist/{processor-34MVJZTD.js → processor-45VWLZIV.js} +4 -4
- package/dist/{serve-NHF6B5RQ.js → serve-V44O2QE6.js} +6 -6
- package/dist/server/index.js +5 -5
- package/dist/{server-KDGQZNYQ.js → server-JDB2QVKP.js} +5 -5
- package/dist/{settings-X43DCM55.js → settings-ASLTSUE2.js} +2 -2
- package/dist/skill-defaults/{browser.skill.md → browser/SKILL.md} +5 -3
- package/dist/{tools-FQ54IAYG.js → tools-7QUMO5DJ.js} +3 -3
- package/dist/web/assets/{index-DjfRXOO7.css → index-BGkyTfuQ.css} +1 -1
- package/dist/web/assets/{index-Dqqp_j-f.js → index-CETZPQXr.js} +73 -73
- package/dist/web/index.html +2 -2
- package/dist/web/sw.js +1 -1
- package/package.json +4 -2
|
@@ -21,12 +21,13 @@ import {
|
|
|
21
21
|
tokenFromPassword,
|
|
22
22
|
verifyPassword,
|
|
23
23
|
workflowExists
|
|
24
|
-
} from "./chunk-
|
|
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
|
-
|
|
65
|
-
|
|
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
|
|
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,
|
|
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:
|
|
1434
|
-
this.servers.set(name, { config:
|
|
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(
|
|
1682
|
-
this.config =
|
|
1690
|
+
constructor(config3, workdir, commandPath) {
|
|
1691
|
+
this.config = config3;
|
|
1683
1692
|
this.workdir = workdir;
|
|
1684
|
-
this.commandPath = commandPath ??
|
|
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((
|
|
1926
|
+
return new Promise((resolve5) => {
|
|
1918
1927
|
const existing = this.diagnostics.get(path);
|
|
1919
1928
|
if (existing !== void 0) {
|
|
1920
1929
|
setTimeout(() => {
|
|
1921
|
-
|
|
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
|
-
|
|
1936
|
+
resolve5(this.diagnostics.get(path) ?? []);
|
|
1928
1937
|
}, timeoutMs);
|
|
1929
|
-
this.pendingDiagnostics.set(path, { resolve:
|
|
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(
|
|
2242
|
-
const langId =
|
|
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(
|
|
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(
|
|
2264
|
-
const langId =
|
|
2265
|
-
const commandPath = await which(
|
|
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:
|
|
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(
|
|
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
|
|
2296
|
-
if (!
|
|
2304
|
+
const config3 = detectLanguage(path);
|
|
2305
|
+
if (!config3) {
|
|
2297
2306
|
return null;
|
|
2298
2307
|
}
|
|
2299
|
-
return this.getOrCreateServer(
|
|
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
|
|
2331
|
-
if (!
|
|
2339
|
+
const config3 = detectLanguage(path);
|
|
2340
|
+
if (!config3) {
|
|
2332
2341
|
return [];
|
|
2333
2342
|
}
|
|
2334
|
-
const server = this.servers.get(
|
|
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
|
|
2346
|
-
if (!
|
|
2354
|
+
const config3 = detectLanguage(path);
|
|
2355
|
+
if (!config3) {
|
|
2347
2356
|
return false;
|
|
2348
2357
|
}
|
|
2349
|
-
if (this.unavailableServers.has(
|
|
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
|
|
2361
|
-
if (!
|
|
2369
|
+
const config3 = detectLanguage(path);
|
|
2370
|
+
if (!config3?.installHint) {
|
|
2362
2371
|
return null;
|
|
2363
2372
|
}
|
|
2364
|
-
if (this.unavailableServers.has(
|
|
2365
|
-
this.dismissedHints.add(
|
|
2366
|
-
return
|
|
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
|
|
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
|
|
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(
|
|
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
|
-
|
|
3271
|
-
|
|
3272
|
-
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 =
|
|
3353
|
+
const extra = config3.extraGetData ? await config3.extraGetData() : {};
|
|
3277
3354
|
res.json({
|
|
3278
|
-
defaults: defaults.map(
|
|
3279
|
-
userItems: userItems.map(
|
|
3280
|
-
projectItems: projectItems.map(
|
|
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
|
|
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 =
|
|
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
|
|
3301
|
-
const item =
|
|
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 =
|
|
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
|
|
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
|
|
3401
|
+
await config3.saveToProject(projectDir, body);
|
|
3325
3402
|
} else {
|
|
3326
|
-
await
|
|
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
|
|
3333
|
-
const existing =
|
|
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,
|
|
3421
|
+
const isProject = await isProjectItem(projectDir, config3.dirName, id, config3.ext);
|
|
3345
3422
|
if (isProject) {
|
|
3346
|
-
await
|
|
3423
|
+
await config3.saveToProject(projectDir, updated);
|
|
3347
3424
|
} else {
|
|
3348
|
-
await
|
|
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,
|
|
3431
|
+
const isProject = await isProjectItem(projectDir, config3.dirName, id, config3.ext);
|
|
3355
3432
|
if (isProject) {
|
|
3356
|
-
const result2 = await
|
|
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
|
|
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
|
|
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
|
|
3375
|
-
const source =
|
|
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
|
|
3464
|
+
await config3.saveToProject(projectDir, duplicated);
|
|
3388
3465
|
} else {
|
|
3389
|
-
await
|
|
3466
|
+
await config3.save(configDir, duplicated);
|
|
3390
3467
|
}
|
|
3391
3468
|
res.status(201).json(duplicated);
|
|
3392
3469
|
});
|
|
3393
|
-
|
|
3470
|
+
config3.extraRoutes?.(router);
|
|
3394
3471
|
return router;
|
|
3395
3472
|
}
|
|
3396
3473
|
|
|
3397
3474
|
// src/server/routes/skills.ts
|
|
3398
|
-
var
|
|
3399
|
-
|
|
3400
|
-
|
|
3401
|
-
|
|
3402
|
-
|
|
3403
|
-
|
|
3404
|
-
|
|
3405
|
-
|
|
3406
|
-
|
|
3407
|
-
|
|
3408
|
-
|
|
3409
|
-
|
|
3410
|
-
|
|
3411
|
-
|
|
3412
|
-
|
|
3413
|
-
|
|
3414
|
-
|
|
3415
|
-
|
|
3416
|
-
|
|
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
|
-
|
|
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
|
|
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
|
|
3428
|
-
var __bundleDir =
|
|
3429
|
-
var DEFAULTS_DIR =
|
|
3430
|
-
var DEFAULTS_DIR_ALT =
|
|
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
|
|
3649
|
+
return join5(configDir, "commands");
|
|
3434
3650
|
}
|
|
3435
3651
|
function getProjectCommandsDir(projectDir) {
|
|
3436
|
-
return
|
|
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(
|
|
3494
|
-
if (projectDir && await pathExists(
|
|
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
|
|
3717
|
+
await mkdir3(commandsDir, { recursive: true });
|
|
3502
3718
|
}
|
|
3503
|
-
const filePath =
|
|
3504
|
-
const content =
|
|
3505
|
-
await
|
|
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) =>
|
|
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 =
|
|
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
|
|
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(
|
|
3768
|
+
return createCrudRoutes(config, configDir, projectDir);
|
|
3553
3769
|
}
|
|
3554
3770
|
|
|
3555
3771
|
// src/server/routes/agents.ts
|
|
3556
|
-
var
|
|
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(
|
|
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,
|
|
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:
|
|
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
|
|
3840
|
+
import { Router as Router4 } from "express";
|
|
3625
3841
|
function createDevServerRoutes() {
|
|
3626
|
-
const router =
|
|
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
|
|
3633
|
-
if (
|
|
3634
|
-
status.config =
|
|
3635
|
-
status.url =
|
|
3636
|
-
status.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
|
|
3683
|
-
res.json({ config:
|
|
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
|
|
3694
|
-
await devServerManager.saveConfig(workdir,
|
|
3695
|
-
res.json({ config:
|
|
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
|
|
3920
|
+
import { Router as Router5 } from "express";
|
|
3705
3921
|
function createTerminalRoutes() {
|
|
3706
|
-
const router =
|
|
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
|
|
3961
|
+
import { Router as Router6 } from "express";
|
|
3746
3962
|
import { readdir } from "fs/promises";
|
|
3747
|
-
import { resolve as
|
|
3963
|
+
import { resolve as resolve3, join as join6, dirname as dirname5, basename as basename2 } from "path";
|
|
3748
3964
|
function createDirectoryRoutes() {
|
|
3749
|
-
const router =
|
|
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 =
|
|
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:
|
|
3974
|
+
path: join6(resolvedPath, entry.name)
|
|
3759
3975
|
})).sort((a, b) => a.name.localeCompare(b.name));
|
|
3760
|
-
const parent =
|
|
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
|
|
3998
|
+
import { Router as Router7 } from "express";
|
|
3783
3999
|
import fg from "fast-glob";
|
|
3784
4000
|
function createFileSearchRoutes() {
|
|
3785
|
-
const router =
|
|
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
|
|
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.
|
|
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 =
|
|
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((
|
|
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
|
-
|
|
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((
|
|
3944
|
-
child.on("close",
|
|
3945
|
-
child.on("error", () =>
|
|
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 =
|
|
3984
|
-
async function createServerHandle(
|
|
3985
|
-
setRuntimeConfig(
|
|
3986
|
-
setLogLevel(
|
|
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(
|
|
4204
|
+
const db = initDatabase(config3);
|
|
3989
4205
|
initEventStore(db);
|
|
3990
|
-
const configDir = getGlobalConfigDir(
|
|
3991
|
-
const providerManager = createProviderManager(
|
|
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 =
|
|
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(
|
|
4008
|
-
if (detected && !
|
|
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 &&
|
|
4229
|
+
} else if (detected && config3.defaultModelSelection) {
|
|
4014
4230
|
if (!useMock) {
|
|
4015
|
-
logger.debug("Skipping auto-detect, using configured model", { 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:
|
|
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 =
|
|
4041
|
-
const globalConfig = await loadGlobalConfig(mode,
|
|
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 },
|
|
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(
|
|
4056
|
-
setMcpConfigPath(
|
|
4057
|
-
const 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-
|
|
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(
|
|
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-
|
|
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-
|
|
4283
|
-
const { cancelQuestionsForSession, cancelPathConfirmationsForSession } = await import("./tools-
|
|
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(
|
|
4553
|
+
const globalConfig = await loadGlobalConfig(config3.mode ?? "production", config3.globalConfigPath);
|
|
4338
4554
|
const updatedConfig = setDefaultModelSelection(globalConfig, providerId, model);
|
|
4339
|
-
await saveGlobalConfig(
|
|
4340
|
-
|
|
4341
|
-
res.json({ success: true, 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-
|
|
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-
|
|
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-
|
|
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-
|
|
4495
|
-
if (
|
|
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-
|
|
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-
|
|
4565
|
-
const { cancelQuestionsForSession, cancelPathConfirmationsForSession } = await import("./tools-
|
|
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-
|
|
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] =
|
|
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-
|
|
4874
|
+
const { getSetting: getSetting2, SETTINGS_DEFAULTS } = await import("./settings-ASLTSUE2.js");
|
|
4659
4875
|
const key = req.params.key;
|
|
4660
|
-
const value =
|
|
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-
|
|
4880
|
+
const { getSetting: getSetting2, SETTINGS_DEFAULTS } = await import("./settings-ASLTSUE2.js");
|
|
4665
4881
|
const key = req.params.key;
|
|
4666
|
-
const value =
|
|
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-
|
|
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
|
-
|
|
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(
|
|
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 ??
|
|
4920
|
+
llmUrl: activeProvider?.url ?? config3.llm.baseUrl,
|
|
4705
4921
|
llmStatus: getLlmStatus(),
|
|
4706
4922
|
backend: llmClient.getBackend(),
|
|
4707
|
-
workdir: globalWorkdir ??
|
|
4923
|
+
workdir: globalWorkdir ?? config3.workdir,
|
|
4708
4924
|
providers: providerManager.getProviders(),
|
|
4709
4925
|
activeProviderId: providerManager.getActiveProviderId(),
|
|
4710
|
-
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 ??
|
|
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(
|
|
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(
|
|
5194
|
+
await saveGlobalConfig(config3.mode ?? "production", finalConfig, config3.globalConfigPath);
|
|
4929
5195
|
providerManager.setProviders(finalConfig.providers, finalConfig.defaultModelSelection ?? void 0);
|
|
4930
|
-
|
|
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(
|
|
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(
|
|
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(
|
|
5241
|
+
const globalConfig = await loadGlobalConfig(config3.mode ?? "production", config3.globalConfigPath);
|
|
4976
5242
|
const updatedConfig = removeProvider(globalConfig, id);
|
|
4977
|
-
await saveGlobalConfig(
|
|
5243
|
+
await saveGlobalConfig(config3.mode ?? "production", updatedConfig, config3.globalConfigPath);
|
|
4978
5244
|
providerManager.setProviders(updatedConfig.providers, updatedConfig.defaultModelSelection ?? void 0);
|
|
4979
|
-
|
|
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(
|
|
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(
|
|
5261
|
+
await saveGlobalConfig(config3.mode ?? "production", updatedConfig, config3.globalConfigPath);
|
|
4996
5262
|
providerManager.setProviders(updatedConfig.providers, updatedConfig.defaultModelSelection ?? void 0);
|
|
4997
|
-
|
|
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(
|
|
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(
|
|
5298
|
+
await saveGlobalConfig(config3.mode ?? "production", updatedConfig, config3.globalConfigPath);
|
|
5033
5299
|
providerManager.setProviders(updatedConfig.providers, updatedConfig.defaultModelSelection ?? void 0);
|
|
5034
|
-
|
|
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(
|
|
5320
|
+
const globalConfig = await loadGlobalConfig(config3.mode ?? "production", config3.globalConfigPath);
|
|
5055
5321
|
const updatedConfig = setDefaultModelSelection(globalConfig, id, llmClient.getModel());
|
|
5056
|
-
await saveGlobalConfig(
|
|
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-
|
|
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(
|
|
5409
|
+
const globalConfig = await loadGlobalConfig(config3.mode ?? "production", config3.globalConfigPath);
|
|
5144
5410
|
const updatedMcpServers = { ...globalConfig.mcpServers ?? {}, [name]: serverCfg };
|
|
5145
5411
|
await saveGlobalConfig(
|
|
5146
|
-
|
|
5412
|
+
config3.mode ?? "production",
|
|
5147
5413
|
{
|
|
5148
5414
|
...globalConfig,
|
|
5149
5415
|
mcpServers: updatedMcpServers
|
|
5150
5416
|
},
|
|
5151
|
-
|
|
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(
|
|
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
|
-
|
|
5443
|
+
config3.mode ?? "production",
|
|
5178
5444
|
{
|
|
5179
5445
|
...globalConfig,
|
|
5180
5446
|
mcpServers: updatedMcpServers
|
|
5181
5447
|
},
|
|
5182
|
-
|
|
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(
|
|
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
|
-
|
|
5476
|
+
config3.mode ?? "production",
|
|
5211
5477
|
{
|
|
5212
5478
|
...globalConfig,
|
|
5213
5479
|
mcpServers: mcpServers2
|
|
5214
5480
|
},
|
|
5215
|
-
|
|
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 =
|
|
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,
|
|
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 =
|
|
5278
|
-
const isDev =
|
|
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:
|
|
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
|
-
|
|
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(
|
|
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
|
-
|
|
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
|
-
|
|
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:
|
|
5599
|
+
logger.info("Vite middleware ready", { port: config3.server.port });
|
|
5334
5600
|
}
|
|
5335
5601
|
if (!isDev) {
|
|
5336
|
-
const distWebDir =
|
|
5602
|
+
const distWebDir = resolve4(__dirname3, "web");
|
|
5337
5603
|
app.use(
|
|
5338
5604
|
"/assets",
|
|
5339
|
-
express.static(
|
|
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(
|
|
5348
|
-
app.use("/registerSW.js", express.static(
|
|
5349
|
-
app.use("/sw.js", express.static(
|
|
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
|
-
|
|
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(
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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-
|
|
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:
|
|
5422
|
-
start: (port) => new Promise((
|
|
5423
|
-
const listenPort = port ??
|
|
5424
|
-
const 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: ${
|
|
5432
|
-
|
|
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((
|
|
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 (
|
|
5712
|
+
if (config3.globalConfigPath) {
|
|
5447
5713
|
const { unlink: unlink2 } = await import("fs/promises");
|
|
5448
|
-
unlink2(
|
|
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(() =>
|
|
5721
|
+
httpServer.close(() => resolve5());
|
|
5456
5722
|
})();
|
|
5457
5723
|
})
|
|
5458
5724
|
};
|
|
5459
5725
|
}
|
|
5460
|
-
async function createServer(
|
|
5461
|
-
const handle = await createServerHandle(
|
|
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-
|
|
5746
|
+
//# sourceMappingURL=chunk-LHTVTDQQ.js.map
|