openfox 1.6.90 → 1.6.92

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (40) hide show
  1. package/dist/{auto-compaction-6A34RHDV.js → auto-compaction-7AGIGW6F.js} +7 -7
  2. package/dist/{chat-handler-6ATTKNZT.js → chat-handler-UPAEF6HR.js} +10 -10
  3. package/dist/{chunk-ZORPFD4C.js → chunk-2BS6LMIU.js} +507 -474
  4. package/dist/{chunk-N7W42N5Y.js → chunk-2TKN2X2A.js} +3 -3
  5. package/dist/{chunk-7WHJUQ4Y.js → chunk-7KO2BQFQ.js} +9 -8
  6. package/dist/{chunk-6GGYIEBM.js → chunk-7QG6LY5Q.js} +3 -3
  7. package/dist/{chunk-UEZDAB5X.js → chunk-ARJIHQ3S.js} +240 -143
  8. package/dist/{chunk-TLOL4I7K.js → chunk-BHZL3N2F.js} +3 -3
  9. package/dist/{chunk-WSRF5L3S.js → chunk-CHICEUV6.js} +3 -3
  10. package/dist/{chunk-RCECKE5L.js → chunk-PQCFNTFJ.js} +15 -9
  11. package/dist/{chunk-YNIAFX24.js → chunk-RJIPM7CO.js} +4 -4
  12. package/dist/{chunk-B5AP3RSV.js → chunk-RW25DL5R.js} +3 -3
  13. package/dist/{chunk-JDDNBK6T.js → chunk-WBQNIWAQ.js} +5 -5
  14. package/dist/{chunk-DF5BZBP6.js → chunk-XGJ7HDQ7.js} +2 -2
  15. package/dist/{chunk-6P5WQP6Y.js → chunk-YTPPQK55.js} +2 -2
  16. package/dist/cli/dev.js +1 -1
  17. package/dist/cli/index.js +1 -1
  18. package/dist/{config-HMMPXQ46.js → config-7XJQNOKX.js} +5 -5
  19. package/dist/{events-AQKDXJFK.js → events-K6AYMXFT.js} +3 -3
  20. package/dist/{orchestrator-YUAFANFC.js → orchestrator-M4WINFF3.js} +8 -8
  21. package/dist/package.json +1 -1
  22. package/dist/{processor-4SOP4Q6S.js → processor-D4XQ5QMB.js} +5 -5
  23. package/dist/{protocol--8PKNwb_.d.ts → protocol-XRiJzlOK.d.ts} +1 -1
  24. package/dist/{provider-LDZFYLK6.js → provider-UO5PAZQQ.js} +7 -7
  25. package/dist/{runtime-config-CBJJJGBD.js → runtime-config-CYHDAC7W.js} +2 -2
  26. package/dist/{serve-YO7HQKUD.js → serve-Z5FNJUDT.js} +12 -12
  27. package/dist/server/index.d.ts +2 -1
  28. package/dist/server/index.js +10 -10
  29. package/dist/{service-TU4XSXYJ.js → service-3CLVG2HV.js} +20 -2
  30. package/dist/{settings-VTVOBDYG.js → settings-AYSWDZUI.js} +2 -2
  31. package/dist/shared/index.d.ts +2 -2
  32. package/dist/{tools-7MGUWXDP.js → tools-NFFQWH6M.js} +7 -7
  33. package/dist/{vision-fallback-WT66NURB.js → vision-fallback-GZZWU7CR.js} +2 -2
  34. package/dist/web/assets/{index-7tHnjaI4.css → index-CM0cLwa6.css} +2 -2
  35. package/dist/web/assets/index-SkaCnTiV.js +176 -0
  36. package/dist/web/index.html +2 -2
  37. package/dist/web/sw.js +1 -1
  38. package/package.json +1 -1
  39. package/dist/web/assets/index-CdF408Qk.js +0 -158
  40. /package/dist/web/{workbox-8c29f6e4.js → workbox-7fc22fbe.js} +0 -0
@@ -2,7 +2,7 @@ import {
2
2
  createVerifierNudgeConfig,
3
3
  runBuilderTurn,
4
4
  runChatTurn
5
- } from "./chunk-N7W42N5Y.js";
5
+ } from "./chunk-2TKN2X2A.js";
6
6
  import {
7
7
  TurnMetrics,
8
8
  agentExists,
@@ -10,34 +10,43 @@ import {
10
10
  createMessageStartEvent,
11
11
  createToolRegistry,
12
12
  deleteAgent,
13
+ deleteItemFromDir,
14
+ deleteProjectAgent,
15
+ deleteProjectSkill,
13
16
  deleteSkill,
14
17
  devServerManager,
15
18
  executeSubAgent,
16
19
  findAgentById,
17
20
  findSkillById,
18
- getDefaultAgentContent,
19
21
  getDefaultAgentIds,
20
22
  getDefaultIds,
21
- getDefaultSkillContent,
22
23
  getDefaultSkillIds,
23
24
  getToolRegistryForAgent,
25
+ getTopLevelAgents,
26
+ isDefaultAgent,
27
+ isDefaultSkill,
24
28
  isSkillEnabled,
29
+ jsonSerializer,
25
30
  loadAllAgents,
26
31
  loadAllAgentsDefault,
27
32
  loadAllSkills,
28
33
  loadDefaultAgents,
29
34
  loadDefaultSkills,
30
35
  loadItemsFromDir,
36
+ loadProjectAgents,
37
+ loadProjectSkills,
31
38
  loadUserAgents,
32
39
  loadUserSkills,
33
40
  pathExists,
34
41
  performManualContextCompaction,
35
42
  saveAgent,
43
+ saveAgentToProject,
44
+ saveItemToDir,
36
45
  saveSkill,
37
- setSkillEnabled,
46
+ saveSkillToProject,
38
47
  skillExists,
39
48
  spawnShellProcess
40
- } from "./chunk-UEZDAB5X.js";
49
+ } from "./chunk-ARJIHQ3S.js";
41
50
  import {
42
51
  getProject
43
52
  } from "./chunk-ZHBL5G5X.js";
@@ -50,18 +59,18 @@ import {
50
59
  import {
51
60
  createProviderManager,
52
61
  parseDefaultModelSelection
53
- } from "./chunk-6GGYIEBM.js";
62
+ } from "./chunk-7QG6LY5Q.js";
54
63
  import {
55
64
  SessionNotFoundError,
56
65
  createLLMClient,
57
66
  detectModel,
58
67
  getLlmStatus
59
- } from "./chunk-JDDNBK6T.js";
68
+ } from "./chunk-WBQNIWAQ.js";
60
69
  import {
61
70
  detectBackend,
62
71
  getBackendDisplayName,
63
72
  getModelProfile
64
- } from "./chunk-YNIAFX24.js";
73
+ } from "./chunk-RJIPM7CO.js";
65
74
  import {
66
75
  createSession,
67
76
  deleteSession,
@@ -87,7 +96,7 @@ import {
87
96
  updateSessionProvider,
88
97
  updateSessionRunning,
89
98
  updateSessionSummary
90
- } from "./chunk-6P5WQP6Y.js";
99
+ } from "./chunk-YTPPQK55.js";
91
100
  import {
92
101
  initDatabase
93
102
  } from "./chunk-5GVCNVKV.js";
@@ -126,7 +135,7 @@ import {
126
135
  import {
127
136
  getRuntimeConfig,
128
137
  setRuntimeConfig
129
- } from "./chunk-B5AP3RSV.js";
138
+ } from "./chunk-RW25DL5R.js";
130
139
  import {
131
140
  logger,
132
141
  setLogLevel
@@ -137,7 +146,7 @@ import express from "express";
137
146
  import cors from "cors";
138
147
  import { createServer as createHttpServer } from "http";
139
148
  import { fileURLToPath as fileURLToPath4 } from "url";
140
- import { dirname as dirname5, resolve, join as join5 } from "path";
149
+ import { dirname as dirname5, resolve, join as join6 } from "path";
141
150
  import { readFile as readFile3 } from "fs/promises";
142
151
  import { createServer as createViteServer } from "vite";
143
152
 
@@ -1531,6 +1540,9 @@ var WORKFLOW_EXTENSION = ".workflow.json";
1531
1540
  function getWorkflowsDir(configDir) {
1532
1541
  return join(configDir, "workflows");
1533
1542
  }
1543
+ function getProjectWorkflowsDir(projectDir) {
1544
+ return join(projectDir, ".openfox", "workflows");
1545
+ }
1534
1546
  async function pathExists2(path) {
1535
1547
  try {
1536
1548
  await access(path, constants.R_OK);
@@ -1575,7 +1587,10 @@ async function loadDefaultWorkflows() {
1575
1587
  async function loadUserWorkflows(configDir) {
1576
1588
  return loadWorkflowsFromDir(getWorkflowsDir(configDir));
1577
1589
  }
1578
- async function loadAllWorkflows(configDir) {
1590
+ async function loadProjectWorkflows(projectDir) {
1591
+ return loadWorkflowsFromDir(getProjectWorkflowsDir(projectDir));
1592
+ }
1593
+ async function loadAllWorkflows(configDir, projectDir) {
1579
1594
  const [defaultWorkflows, userWorkflows] = await Promise.all([loadDefaultWorkflows(), loadUserWorkflows(configDir)]);
1580
1595
  const workflowMap = /* @__PURE__ */ new Map();
1581
1596
  for (const workflow of defaultWorkflows) {
@@ -1584,8 +1599,20 @@ async function loadAllWorkflows(configDir) {
1584
1599
  for (const workflow of userWorkflows) {
1585
1600
  workflowMap.set(workflow.metadata.id, workflow);
1586
1601
  }
1602
+ if (projectDir) {
1603
+ const projectWorkflows = await loadProjectWorkflows(projectDir);
1604
+ for (const workflow of projectWorkflows) {
1605
+ workflowMap.set(workflow.metadata.id, workflow);
1606
+ }
1607
+ }
1587
1608
  return Array.from(workflowMap.values());
1588
1609
  }
1610
+ async function saveWorkflowToProject(projectDir, workflow) {
1611
+ await saveItemToDir(getProjectWorkflowsDir(projectDir), workflow, WORKFLOW_EXTENSION, jsonSerializer);
1612
+ }
1613
+ async function deleteProjectWorkflow(projectDir, workflowId) {
1614
+ return deleteItemFromDir(getProjectWorkflowsDir(projectDir), workflowId, WORKFLOW_EXTENSION);
1615
+ }
1589
1616
  async function getDefaultWorkflowIds() {
1590
1617
  for (const dir of [DEFAULTS_DIR, DEFAULTS_DIR_ALT]) {
1591
1618
  try {
@@ -1596,10 +1623,6 @@ async function getDefaultWorkflowIds() {
1596
1623
  }
1597
1624
  return [];
1598
1625
  }
1599
- async function getDefaultWorkflowContent(workflowId) {
1600
- const defaults = await loadDefaultWorkflows();
1601
- return defaults.find((w) => w.metadata.id === workflowId) ?? null;
1602
- }
1603
1626
  async function isDefaultWorkflow(workflowId) {
1604
1627
  const defaultIds = await getDefaultWorkflowIds();
1605
1628
  return defaultIds.includes(workflowId);
@@ -1607,9 +1630,11 @@ async function isDefaultWorkflow(workflowId) {
1607
1630
  function findWorkflowById(workflowId, workflows) {
1608
1631
  return workflows.find((p) => p.metadata.id === workflowId);
1609
1632
  }
1610
- async function workflowExists(configDir, workflowId) {
1611
- const filePath = join(getWorkflowsDir(configDir), `${workflowId}${WORKFLOW_EXTENSION}`);
1612
- return pathExists2(filePath);
1633
+ async function workflowExists(configDir, workflowId, projectDir) {
1634
+ if (await pathExists2(join(getWorkflowsDir(configDir), `${workflowId}${WORKFLOW_EXTENSION}`))) return true;
1635
+ if (projectDir && await pathExists2(join(getProjectWorkflowsDir(projectDir), `${workflowId}${WORKFLOW_EXTENSION}`)))
1636
+ return true;
1637
+ return false;
1613
1638
  }
1614
1639
  async function saveWorkflow(configDir, workflow) {
1615
1640
  const workflowsDir = getWorkflowsDir(configDir);
@@ -2480,7 +2505,7 @@ function processQueueAndRestartTurn(sessionManager, sessionId, drainFn, queueMod
2480
2505
  }
2481
2506
  var activeAgents = /* @__PURE__ */ new Map();
2482
2507
  var MAX_SEND_QUEUE_SIZE = 1e3;
2483
- function createWebSocketServer(httpServer, config, getLLMClient, getActiveProvider, sessionManager, providerManager) {
2508
+ function createWebSocketServer(httpServer, config4, getLLMClient, getActiveProvider, sessionManager, providerManager) {
2484
2509
  const wss = new WebSocketServer({ server: httpServer });
2485
2510
  const clients = /* @__PURE__ */ new Map();
2486
2511
  moduleClients = clients;
@@ -2507,9 +2532,9 @@ function createWebSocketServer(httpServer, config, getLLMClient, getActiveProvid
2507
2532
  }
2508
2533
  const baseUrl = provider.url.includes("/v1") ? provider.url : `${provider.url}/v1`;
2509
2534
  const sessionConfig = {
2510
- ...config,
2535
+ ...config4,
2511
2536
  llm: {
2512
- ...config.llm,
2537
+ ...config4.llm,
2513
2538
  baseUrl,
2514
2539
  model: session.providerModel,
2515
2540
  ...provider.apiKey && { apiKey: provider.apiKey }
@@ -3113,10 +3138,10 @@ var LspServer = class {
3113
3138
  // Pending diagnostic waiters
3114
3139
  pendingDiagnostics = /* @__PURE__ */ new Map();
3115
3140
  commandPath;
3116
- constructor(config, workdir, commandPath) {
3117
- this.config = config;
3141
+ constructor(config4, workdir, commandPath) {
3142
+ this.config = config4;
3118
3143
  this.workdir = workdir;
3119
- this.commandPath = commandPath ?? config.serverCommand;
3144
+ this.commandPath = commandPath ?? config4.serverCommand;
3120
3145
  }
3121
3146
  // ============================================================================
3122
3147
  // Lifecycle
@@ -3939,8 +3964,8 @@ var LspManager = class {
3939
3964
  // ============================================================================
3940
3965
  // Server Management
3941
3966
  // ============================================================================
3942
- async getOrCreateServer(config) {
3943
- const langId = config.id;
3967
+ async getOrCreateServer(config4) {
3968
+ const langId = config4.id;
3944
3969
  const existing = this.servers.get(langId);
3945
3970
  if (existing?.isRunning()) {
3946
3971
  return existing;
@@ -3952,7 +3977,7 @@ var LspManager = class {
3952
3977
  if (pending) {
3953
3978
  return pending;
3954
3979
  }
3955
- const promise = this.startServer(config);
3980
+ const promise = this.startServer(config4);
3956
3981
  this.serverPromises.set(langId, promise);
3957
3982
  try {
3958
3983
  const server = await promise;
@@ -3961,19 +3986,19 @@ var LspManager = class {
3961
3986
  this.serverPromises.delete(langId);
3962
3987
  }
3963
3988
  }
3964
- async startServer(config) {
3965
- const langId = config.id;
3966
- const commandPath = await which(config.serverCommand, this.workdir);
3989
+ async startServer(config4) {
3990
+ const langId = config4.id;
3991
+ const commandPath = await which(config4.serverCommand, this.workdir);
3967
3992
  if (!commandPath) {
3968
3993
  logger.warn("LSP server not installed, skipping", {
3969
3994
  language: langId,
3970
- command: config.serverCommand,
3995
+ command: config4.serverCommand,
3971
3996
  sessionId: this.sessionId
3972
3997
  });
3973
3998
  this.unavailableServers.add(langId);
3974
3999
  return null;
3975
4000
  }
3976
- const server = new LspServer(config, this.workdir, commandPath);
4001
+ const server = new LspServer(config4, this.workdir, commandPath);
3977
4002
  try {
3978
4003
  await server.start();
3979
4004
  this.servers.set(langId, server);
@@ -3993,11 +4018,11 @@ var LspManager = class {
3993
4018
  }
3994
4019
  }
3995
4020
  async getServerForFile(path) {
3996
- const config = detectLanguage(path);
3997
- if (!config) {
4021
+ const config4 = detectLanguage(path);
4022
+ if (!config4) {
3998
4023
  return null;
3999
4024
  }
4000
- return this.getOrCreateServer(config);
4025
+ return this.getOrCreateServer(config4);
4001
4026
  }
4002
4027
  // ============================================================================
4003
4028
  // Public Interface
@@ -4028,11 +4053,11 @@ var LspManager = class {
4028
4053
  * Get current diagnostics for a file without triggering a change.
4029
4054
  */
4030
4055
  getDiagnostics(path) {
4031
- const config = detectLanguage(path);
4032
- if (!config) {
4056
+ const config4 = detectLanguage(path);
4057
+ if (!config4) {
4033
4058
  return [];
4034
4059
  }
4035
- const server = this.servers.get(config.id);
4060
+ const server = this.servers.get(config4.id);
4036
4061
  if (!server?.isRunning()) {
4037
4062
  return [];
4038
4063
  }
@@ -4043,11 +4068,11 @@ var LspManager = class {
4043
4068
  * Returns true if we have or can start a server for this file type.
4044
4069
  */
4045
4070
  isAvailableFor(path) {
4046
- const config = detectLanguage(path);
4047
- if (!config) {
4071
+ const config4 = detectLanguage(path);
4072
+ if (!config4) {
4048
4073
  return false;
4049
4074
  }
4050
- if (this.unavailableServers.has(config.id)) {
4075
+ if (this.unavailableServers.has(config4.id)) {
4051
4076
  return false;
4052
4077
  }
4053
4078
  return true;
@@ -4835,130 +4860,197 @@ ${summary}`,
4835
4860
  }
4836
4861
  };
4837
4862
 
4838
- // src/server/routes/skills.ts
4839
- import { Router } from "express";
4840
-
4841
4863
  // src/server/routes/crud-helpers.ts
4864
+ import { Router } from "express";
4865
+ import { join as join4 } from "path";
4842
4866
  function computeOverrideIds(defaults, userItems) {
4843
4867
  return userItems.filter((u) => defaults.some((d) => d.metadata.id === u.metadata.id)).map((u) => u.metadata.id);
4844
4868
  }
4845
-
4846
- // src/server/routes/skills.ts
4847
- function createSkillRoutes(configDir) {
4869
+ var ID_REGEX = /^[a-z0-9-]+$/;
4870
+ function validateNameIdPrompt(body) {
4871
+ const meta = body["metadata"];
4872
+ if (!meta?.["name"] || !body["prompt"]) return "Missing required fields: metadata.name, prompt";
4873
+ if (meta["id"] && !ID_REGEX.test(String(meta["id"]))) return "ID must be lowercase alphanumeric with hyphens only";
4874
+ return null;
4875
+ }
4876
+ function getProjectItemPath(projectDir, dirName, id, ext) {
4877
+ return join4(projectDir, ".openfox", dirName, `${id}${ext}`);
4878
+ }
4879
+ async function isProjectItem(projectDir, dirName, id, ext) {
4880
+ if (!projectDir) return false;
4881
+ return pathExists(getProjectItemPath(projectDir, dirName, id, ext));
4882
+ }
4883
+ function createCrudRoutes(config4, configDir, projectDir) {
4848
4884
  const router = Router();
4849
4885
  router.get("/", async (_req, res) => {
4850
- const [defaults, userItems] = await Promise.all([loadDefaultSkills(), loadUserSkills(configDir)]);
4851
- const overrideIds = computeOverrideIds(defaults, userItems);
4886
+ const [defaults, userItems, projectItems] = await Promise.all([
4887
+ config4.loadDefaults(),
4888
+ config4.loadUser(configDir),
4889
+ projectDir ? config4.loadProject(projectDir) : []
4890
+ ]);
4891
+ const userOverrideIds = computeOverrideIds(defaults, userItems);
4892
+ const projectOverrideIds = computeOverrideIds(defaults, projectItems);
4893
+ const extra = config4.extraGetData ? await config4.extraGetData() : {};
4852
4894
  res.json({
4853
- defaults: defaults.map((s) => ({
4854
- ...s.metadata,
4855
- enabled: isSkillEnabled(s.metadata.id)
4856
- })),
4857
- userItems: userItems.map((s) => ({
4858
- ...s.metadata,
4859
- enabled: isSkillEnabled(s.metadata.id)
4860
- })),
4861
- overrideIds
4895
+ defaults: defaults.map(config4.mapToResponse),
4896
+ userItems: userItems.map(config4.mapToResponse),
4897
+ projectItems: projectItems.map(config4.mapToResponse),
4898
+ overrideIds: [...userOverrideIds, ...projectOverrideIds],
4899
+ ...extra
4862
4900
  });
4863
4901
  });
4864
4902
  router.get("/defaults/:id", async (req, res) => {
4865
4903
  const { id } = req.params;
4866
- const content = await getDefaultSkillContent(id);
4867
- if (!content) {
4868
- return res.status(404).json({ error: "Default skill not found" });
4904
+ const allDefaults = await config4.loadDefaults();
4905
+ const item = allDefaults.find((d) => d.metadata.id === id);
4906
+ if (!item) {
4907
+ return res.status(404).json({ error: "Default not found" });
4869
4908
  }
4870
- res.json(content);
4909
+ res.json(item);
4871
4910
  });
4872
4911
  router.get("/default-ids", async (_req, res) => {
4873
- const ids = await getDefaultSkillIds();
4912
+ const ids = config4.getDefaultIds ? await config4.getDefaultIds() : [];
4874
4913
  res.json({ ids });
4875
4914
  });
4876
- router.post("/:id/toggle", (req, res) => {
4877
- const { id } = req.params;
4878
- const currentlyEnabled = isSkillEnabled(id);
4879
- setSkillEnabled(id, !currentlyEnabled);
4880
- res.json({ id, enabled: !currentlyEnabled });
4881
- });
4882
4915
  router.get("/:id", async (req, res) => {
4883
4916
  const { id } = req.params;
4884
- const skills = await loadAllSkills(configDir);
4885
- const skill = findSkillById(id, skills);
4886
- if (!skill) {
4887
- return res.status(404).json({ error: "Skill not found" });
4917
+ const items = await config4.loadAll(configDir, projectDir);
4918
+ const item = config4.findById(id, items);
4919
+ if (!item) {
4920
+ return res.status(404).json({ error: "Not found" });
4888
4921
  }
4889
- res.json(skill);
4922
+ res.json(item);
4890
4923
  });
4891
4924
  router.post("/", async (req, res) => {
4892
4925
  const body = req.body;
4893
- if (!body.metadata?.id || !body.metadata?.name || !body.prompt) {
4894
- return res.status(400).json({ error: "Missing required fields: metadata.id, metadata.name, prompt" });
4926
+ const meta = body["metadata"];
4927
+ const customError = config4.validateCreate?.(body);
4928
+ if (!meta?.["id"] || customError) {
4929
+ return res.status(400).json({ error: customError ?? "Missing required fields" });
4895
4930
  }
4896
- if (!/^[a-z0-9-]+$/.test(body.metadata.id)) {
4897
- return res.status(400).json({ error: "Skill ID must be lowercase alphanumeric with hyphens only" });
4931
+ const id = String(meta["id"]);
4932
+ const destination = body["destination"] ?? "user";
4933
+ if (destination === "project" && !projectDir) {
4934
+ return res.status(400).json({ error: "No project directory configured" });
4898
4935
  }
4899
- const exists = await skillExists(configDir, body.metadata.id);
4936
+ const exists = await config4.exists(configDir, id, projectDir);
4900
4937
  if (exists) {
4901
- return res.status(409).json({ error: "A skill with this ID already exists" });
4938
+ return res.status(409).json({ error: "An item with this ID already exists" });
4939
+ }
4940
+ if (destination === "project") {
4941
+ await config4.saveToProject(projectDir, body);
4942
+ } else {
4943
+ await config4.save(configDir, body);
4902
4944
  }
4903
- await saveSkill(configDir, body);
4904
4945
  res.status(201).json(body);
4905
4946
  });
4906
4947
  router.put("/:id", async (req, res) => {
4907
4948
  const { id } = req.params;
4908
- const skills = await loadAllSkills(configDir);
4909
- const existing = findSkillById(id, skills);
4949
+ const items = await config4.loadAll(configDir, projectDir);
4950
+ const existing = config4.findById(id, items);
4910
4951
  if (!existing) {
4911
- return res.status(404).json({ error: "Skill not found" });
4952
+ return res.status(404).json({ error: "Not found" });
4912
4953
  }
4913
4954
  const body = req.body;
4955
+ const meta = body["metadata"];
4914
4956
  const updated = {
4915
- metadata: { ...existing.metadata, ...body.metadata, id },
4916
- prompt: body.prompt ?? existing.prompt
4957
+ ...existing,
4958
+ ...body,
4959
+ metadata: { ...existing.metadata, ...meta, id }
4917
4960
  };
4918
- await saveSkill(configDir, updated);
4961
+ const isProject = await isProjectItem(projectDir, config4.dirName, id, config4.ext);
4962
+ if (isProject) {
4963
+ await config4.saveToProject(projectDir, updated);
4964
+ } else {
4965
+ await config4.save(configDir, updated);
4966
+ }
4919
4967
  res.json(updated);
4920
4968
  });
4921
4969
  router.delete("/:id", async (req, res) => {
4922
4970
  const { id } = req.params;
4923
- const result = await deleteSkill(configDir, id);
4971
+ const isProject = await isProjectItem(projectDir, config4.dirName, id, config4.ext);
4972
+ if (isProject) {
4973
+ const result2 = await config4.deleteProject(projectDir, id);
4974
+ if (!result2.success) {
4975
+ return res.status(500).json({ error: "Failed to delete project item" });
4976
+ }
4977
+ return res.json({ success: true });
4978
+ }
4979
+ const isDefault = await config4.isDefault(id);
4980
+ if (isDefault) {
4981
+ return res.status(403).json({ error: "Cannot delete built-in defaults" });
4982
+ }
4983
+ const result = await config4.delete(configDir, id);
4924
4984
  if (!result.success) {
4925
- return res.status(403).json({ error: result.reason ?? "Cannot delete this skill" });
4985
+ return res.status(403).json({ error: result.reason ?? "Cannot delete this item" });
4926
4986
  }
4927
4987
  res.json({ success: true });
4928
4988
  });
4929
4989
  router.post("/:id/duplicate", async (req, res) => {
4930
4990
  const { id } = req.params;
4931
- const defaults = await loadDefaultSkills();
4932
- const userItems = await loadUserSkills(configDir);
4933
- const source = defaults.find((s) => s.metadata.id === id) ?? userItems.find((s) => s.metadata.id === id);
4991
+ const items = await config4.loadAll(configDir, projectDir);
4992
+ const source = config4.findById(id, items);
4934
4993
  if (!source) {
4935
- return res.status(404).json({ error: "Skill not found" });
4994
+ return res.status(404).json({ error: "Not found" });
4936
4995
  }
4937
4996
  const newId = `${id}-copy-${Date.now()}`;
4938
4997
  const duplicated = {
4939
- metadata: { ...source.metadata, id: newId, name: `${source.metadata.name} (copy)` },
4940
- prompt: source.prompt
4998
+ ...source,
4999
+ metadata: { ...source.metadata, id: newId, name: `${source.metadata.name} (copy)` }
4941
5000
  };
4942
- await saveSkill(configDir, duplicated);
5001
+ const destination = req.body.destination ?? "user";
5002
+ if (destination === "project") {
5003
+ if (!projectDir) return res.status(400).json({ error: "No project directory configured" });
5004
+ await config4.saveToProject(projectDir, duplicated);
5005
+ } else {
5006
+ await config4.save(configDir, duplicated);
5007
+ }
4943
5008
  res.status(201).json(duplicated);
4944
5009
  });
5010
+ config4.extraRoutes?.(router);
4945
5011
  return router;
4946
5012
  }
4947
5013
 
4948
- // src/server/routes/commands.ts
4949
- import { Router as Router2 } from "express";
5014
+ // src/server/routes/skills.ts
5015
+ var config = {
5016
+ dirName: "skills",
5017
+ ext: ".skill.md",
5018
+ loadDefaults: loadDefaultSkills,
5019
+ loadUser: loadUserSkills,
5020
+ loadProject: loadProjectSkills,
5021
+ loadAll: loadAllSkills,
5022
+ findById: findSkillById,
5023
+ save: saveSkill,
5024
+ saveToProject: saveSkillToProject,
5025
+ delete: deleteSkill,
5026
+ deleteProject: deleteProjectSkill,
5027
+ exists: skillExists,
5028
+ isDefault: isDefaultSkill,
5029
+ getDefaultIds: getDefaultSkillIds,
5030
+ validateCreate: validateNameIdPrompt,
5031
+ mapToResponse: (s) => ({
5032
+ ...s.metadata,
5033
+ enabled: isSkillEnabled(s.metadata.id)
5034
+ })
5035
+ };
5036
+ function createSkillRoutes(configDir, projectDir) {
5037
+ return createCrudRoutes(config, configDir, projectDir);
5038
+ }
4950
5039
 
4951
5040
  // src/server/commands/registry.ts
4952
5041
  import { writeFile as writeFile3, mkdir as mkdir3, unlink as unlink2 } from "fs/promises";
4953
- import { join as join4, dirname as dirname4 } from "path";
5042
+ import { join as join5, dirname as dirname4 } from "path";
4954
5043
  import { fileURLToPath as fileURLToPath3 } from "url";
4955
5044
  import matter from "gray-matter";
4956
5045
  var __bundleDir2 = dirname4(fileURLToPath3(import.meta.url));
4957
- var DEFAULTS_DIR2 = join4(__bundleDir2, "defaults");
4958
- var DEFAULTS_DIR_ALT2 = join4(__bundleDir2, "command-defaults");
5046
+ var DEFAULTS_DIR2 = join5(__bundleDir2, "defaults");
5047
+ var DEFAULTS_DIR_ALT2 = join5(__bundleDir2, "command-defaults");
4959
5048
  var COMMAND_EXTENSION = ".command.md";
4960
5049
  function getCommandsDir(configDir) {
4961
- return join4(configDir, "commands");
5050
+ return join5(configDir, "commands");
5051
+ }
5052
+ function getProjectCommandsDir(projectDir) {
5053
+ return join5(projectDir, ".openfox", "commands");
4962
5054
  }
4963
5055
  async function loadDefaultCommands() {
4964
5056
  let defaults = await loadItemsFromDir(DEFAULTS_DIR2, {
@@ -4979,7 +5071,13 @@ async function loadUserCommands(configDir) {
4979
5071
  logName: "command"
4980
5072
  });
4981
5073
  }
4982
- async function loadAllCommands(configDir) {
5074
+ async function loadProjectCommands(projectDir) {
5075
+ return loadItemsFromDir(getProjectCommandsDir(projectDir), {
5076
+ extension: COMMAND_EXTENSION,
5077
+ logName: "command"
5078
+ });
5079
+ }
5080
+ async function loadAllCommands(configDir, projectDir) {
4983
5081
  const [defaultCommands, userCommands] = await Promise.all([loadDefaultCommands(), loadUserCommands(configDir)]);
4984
5082
  const commandMap = /* @__PURE__ */ new Map();
4985
5083
  for (const cmd of defaultCommands) {
@@ -4988,6 +5086,12 @@ async function loadAllCommands(configDir) {
4988
5086
  for (const cmd of userCommands) {
4989
5087
  commandMap.set(cmd.metadata.id, cmd);
4990
5088
  }
5089
+ if (projectDir) {
5090
+ const projectCommands = await loadProjectCommands(projectDir);
5091
+ for (const cmd of projectCommands) {
5092
+ commandMap.set(cmd.metadata.id, cmd);
5093
+ }
5094
+ }
4991
5095
  return Array.from(commandMap.values());
4992
5096
  }
4993
5097
  async function getDefaultCommandIds() {
@@ -4995,10 +5099,6 @@ async function getDefaultCommandIds() {
4995
5099
  if (ids.length) return ids;
4996
5100
  return getDefaultIds(DEFAULTS_DIR_ALT2, COMMAND_EXTENSION);
4997
5101
  }
4998
- async function getDefaultCommandContent(commandId) {
4999
- const defaults = await loadDefaultCommands();
5000
- return defaults.find((c) => c.metadata.id === commandId) ?? null;
5001
- }
5002
5102
  async function isDefaultCommand(commandId) {
5003
5103
  const defaultIds = await getDefaultCommandIds();
5004
5104
  return defaultIds.includes(commandId);
@@ -5006,24 +5106,38 @@ async function isDefaultCommand(commandId) {
5006
5106
  function findCommandById(commandId, commands) {
5007
5107
  return commands.find((c) => c.metadata.id === commandId);
5008
5108
  }
5009
- async function commandExists(configDir, commandId) {
5010
- return pathExists(join4(getCommandsDir(configDir), `${commandId}${COMMAND_EXTENSION}`));
5109
+ async function commandExists(configDir, commandId, projectDir) {
5110
+ if (await pathExists(join5(getCommandsDir(configDir), `${commandId}${COMMAND_EXTENSION}`))) return true;
5111
+ if (projectDir && await pathExists(join5(getProjectCommandsDir(projectDir), `${commandId}${COMMAND_EXTENSION}`)))
5112
+ return true;
5113
+ return false;
5011
5114
  }
5012
5115
  async function saveCommand(configDir, command) {
5013
5116
  const commandsDir = getCommandsDir(configDir);
5014
5117
  if (!await pathExists(commandsDir)) {
5015
5118
  await mkdir3(commandsDir, { recursive: true });
5016
5119
  }
5017
- const filePath = join4(commandsDir, `${command.metadata.id}${COMMAND_EXTENSION}`);
5120
+ const filePath = join5(commandsDir, `${command.metadata.id}${COMMAND_EXTENSION}`);
5018
5121
  const content = matter.stringify(command.prompt, command.metadata);
5019
5122
  await writeFile3(filePath, content, "utf-8");
5020
5123
  }
5124
+ async function saveCommandToProject(projectDir, command) {
5125
+ await saveItemToDir(
5126
+ getProjectCommandsDir(projectDir),
5127
+ command,
5128
+ COMMAND_EXTENSION,
5129
+ (c) => matter.stringify(c.prompt, c.metadata)
5130
+ );
5131
+ }
5132
+ async function deleteProjectCommand(projectDir, commandId) {
5133
+ return deleteItemFromDir(getProjectCommandsDir(projectDir), commandId, COMMAND_EXTENSION);
5134
+ }
5021
5135
  async function deleteCommand(configDir, commandId) {
5022
5136
  const isDefault = await isDefaultCommand(commandId);
5023
5137
  if (isDefault) {
5024
5138
  return { success: false, reason: "Cannot delete built-in defaults" };
5025
5139
  }
5026
- const filePath = join4(getCommandsDir(configDir), `${commandId}${COMMAND_EXTENSION}`);
5140
+ const filePath = join5(getCommandsDir(configDir), `${commandId}${COMMAND_EXTENSION}`);
5027
5141
  try {
5028
5142
  await unlink2(filePath);
5029
5143
  return { success: true };
@@ -5033,292 +5147,106 @@ async function deleteCommand(configDir, commandId) {
5033
5147
  }
5034
5148
 
5035
5149
  // src/server/routes/commands.ts
5036
- function createCommandRoutes(configDir) {
5037
- const router = Router2();
5038
- router.get("/", async (_req, res) => {
5039
- const [defaults, userItems] = await Promise.all([loadDefaultCommands(), loadUserCommands(configDir)]);
5040
- const overrideIds = computeOverrideIds(defaults, userItems);
5041
- res.json({
5042
- defaults: defaults.map((c) => c.metadata),
5043
- userItems: userItems.map((c) => c.metadata),
5044
- overrideIds
5045
- });
5046
- });
5047
- router.get("/defaults/:id", async (req, res) => {
5048
- const { id } = req.params;
5049
- const content = await getDefaultCommandContent(id);
5050
- if (!content) {
5051
- return res.status(404).json({ error: "Default command not found" });
5052
- }
5053
- res.json(content);
5054
- });
5055
- router.get("/default-ids", async (_req, res) => {
5056
- const ids = await getDefaultCommandIds();
5057
- res.json({ ids });
5058
- });
5059
- router.get("/:id", async (req, res) => {
5060
- const { id } = req.params;
5061
- const commands = await loadAllCommands(configDir);
5062
- const command = findCommandById(id, commands);
5063
- if (!command) {
5064
- return res.status(404).json({ error: "Command not found" });
5065
- }
5066
- res.json(command);
5067
- });
5068
- router.post("/", async (req, res) => {
5069
- const body = req.body;
5070
- if (!body.metadata?.id || !body.metadata?.name || !body.prompt) {
5071
- return res.status(400).json({ error: "Missing required fields: metadata.id, metadata.name, prompt" });
5072
- }
5073
- if (!/^[a-z0-9-]+$/.test(body.metadata.id)) {
5074
- return res.status(400).json({ error: "Command ID must be lowercase alphanumeric with hyphens only" });
5075
- }
5076
- const exists = await commandExists(configDir, body.metadata.id);
5077
- if (exists) {
5078
- return res.status(409).json({ error: "A command with this ID already exists" });
5079
- }
5080
- await saveCommand(configDir, body);
5081
- res.status(201).json(body);
5082
- });
5083
- router.put("/:id", async (req, res) => {
5084
- const { id } = req.params;
5085
- const commands = await loadAllCommands(configDir);
5086
- const existing = findCommandById(id, commands);
5087
- if (!existing) {
5088
- return res.status(404).json({ error: "Command not found" });
5089
- }
5090
- const body = req.body;
5091
- const updated = {
5092
- metadata: { ...existing.metadata, ...body.metadata, id },
5093
- prompt: body.prompt ?? existing.prompt
5094
- };
5095
- await saveCommand(configDir, updated);
5096
- res.json(updated);
5097
- });
5098
- router.delete("/:id", async (req, res) => {
5099
- const { id } = req.params;
5100
- const result = await deleteCommand(configDir, id);
5101
- if (!result.success) {
5102
- return res.status(403).json({ error: result.reason ?? "Cannot delete this command" });
5103
- }
5104
- res.json({ success: true });
5105
- });
5106
- router.post("/:id/duplicate", async (req, res) => {
5107
- const { id } = req.params;
5108
- const defaults = await loadDefaultCommands();
5109
- const userItems = await loadUserCommands(configDir);
5110
- const source = defaults.find((c) => c.metadata.id === id) ?? userItems.find((c) => c.metadata.id === id);
5111
- if (!source) {
5112
- return res.status(404).json({ error: "Command not found" });
5113
- }
5114
- const newId = `${id}-copy-${Date.now()}`;
5115
- const duplicated = {
5116
- metadata: { ...source.metadata, id: newId, name: `${source.metadata.name} (copy)` },
5117
- prompt: source.prompt
5118
- };
5119
- await saveCommand(configDir, duplicated);
5120
- res.status(201).json(duplicated);
5121
- });
5122
- return router;
5150
+ var config2 = {
5151
+ dirName: "commands",
5152
+ ext: ".command.md",
5153
+ loadDefaults: loadDefaultCommands,
5154
+ loadUser: loadUserCommands,
5155
+ loadProject: loadProjectCommands,
5156
+ loadAll: loadAllCommands,
5157
+ findById: findCommandById,
5158
+ save: saveCommand,
5159
+ saveToProject: saveCommandToProject,
5160
+ delete: deleteCommand,
5161
+ deleteProject: deleteProjectCommand,
5162
+ exists: commandExists,
5163
+ isDefault: isDefaultCommand,
5164
+ getDefaultIds: getDefaultCommandIds,
5165
+ validateCreate: validateNameIdPrompt,
5166
+ mapToResponse: (c) => c.metadata
5167
+ };
5168
+ function createCommandRoutes(configDir, projectDir) {
5169
+ return createCrudRoutes(config2, configDir, projectDir);
5123
5170
  }
5124
5171
 
5125
5172
  // src/server/routes/agents.ts
5126
- import { Router as Router3 } from "express";
5127
- function createAgentRoutes(configDir) {
5128
- const router = Router3();
5129
- router.get("/", async (_req, res) => {
5130
- const [defaults, userItems] = await Promise.all([loadDefaultAgents(), loadUserAgents(configDir)]);
5131
- const overrideIds = computeOverrideIds(defaults, userItems);
5132
- res.json({
5133
- defaults: defaults.map((a) => a.metadata),
5134
- userItems: userItems.map((a) => a.metadata),
5135
- overrideIds
5136
- });
5137
- });
5138
- router.get("/defaults/:id", async (req, res) => {
5139
- const { id } = req.params;
5140
- const content = await getDefaultAgentContent(id);
5141
- if (!content) {
5142
- return res.status(404).json({ error: "Default agent not found" });
5143
- }
5144
- res.json(content);
5145
- });
5146
- router.get("/default-ids", async (_req, res) => {
5147
- const ids = await getDefaultAgentIds();
5148
- res.json({ ids });
5149
- });
5150
- router.get("/:id", async (req, res) => {
5151
- const { id } = req.params;
5152
- const agents = await loadAllAgents(configDir);
5153
- const agent = findAgentById(id, agents);
5154
- if (!agent) {
5155
- return res.status(404).json({ error: "Agent not found" });
5156
- }
5157
- res.json(agent);
5158
- });
5159
- router.post("/", async (req, res) => {
5160
- const body = req.body;
5161
- if (!body?.metadata?.id || !body?.prompt) {
5162
- return res.status(400).json({ error: "Missing required fields: metadata.id, prompt" });
5163
- }
5164
- const exists = await agentExists(configDir, body.metadata.id);
5165
- if (exists) {
5166
- return res.status(409).json({ error: "An agent with this ID already exists" });
5167
- }
5168
- await saveAgent(configDir, body);
5169
- res.status(201).json(body);
5170
- });
5171
- router.put("/:id", async (req, res) => {
5172
- const { id } = req.params;
5173
- if (!id) {
5174
- return res.status(400).json({ error: "Missing agent ID" });
5175
- }
5176
- const body = req.body;
5177
- const agents = await loadAllAgents(configDir);
5178
- const existing = findAgentById(id, agents);
5179
- if (!existing) {
5180
- return res.status(404).json({ error: "Agent not found" });
5181
- }
5182
- const updated = {
5183
- metadata: { ...existing.metadata, ...body.metadata, id },
5184
- prompt: body.prompt ?? existing.prompt
5185
- };
5186
- await saveAgent(configDir, updated);
5187
- res.json(updated);
5188
- });
5189
- router.delete("/:id", async (req, res) => {
5190
- const { id } = req.params;
5191
- const result = await deleteAgent(configDir, id);
5192
- if (!result.success) {
5193
- return res.status(403).json({ error: result.reason ?? "Cannot delete this agent" });
5194
- }
5195
- res.json({ success: true });
5196
- });
5197
- router.post("/:id/duplicate", async (req, res) => {
5198
- const { id } = req.params;
5199
- const defaults = await loadDefaultAgents();
5200
- const userItems = await loadUserAgents(configDir);
5201
- const source = defaults.find((a) => a.metadata.id === id) ?? userItems.find((a) => a.metadata.id === id);
5202
- if (!source) {
5203
- return res.status(404).json({ error: "Agent not found" });
5204
- }
5205
- const newId = `${id}-copy-${Date.now()}`;
5206
- const duplicated = {
5207
- metadata: { ...source.metadata, id: newId, name: `${source.metadata.name} (copy)` },
5208
- prompt: source.prompt
5209
- };
5210
- await saveAgent(configDir, duplicated);
5211
- res.status(201).json(duplicated);
5212
- });
5213
- return router;
5173
+ var config3 = {
5174
+ dirName: "agents",
5175
+ ext: ".agent.md",
5176
+ loadDefaults: loadDefaultAgents,
5177
+ loadUser: loadUserAgents,
5178
+ loadProject: loadProjectAgents,
5179
+ loadAll: loadAllAgents,
5180
+ findById: findAgentById,
5181
+ save: saveAgent,
5182
+ saveToProject: saveAgentToProject,
5183
+ delete: deleteAgent,
5184
+ deleteProject: deleteProjectAgent,
5185
+ exists: agentExists,
5186
+ isDefault: isDefaultAgent,
5187
+ getDefaultIds: getDefaultAgentIds,
5188
+ validateCreate: (body) => {
5189
+ const meta = body["metadata"];
5190
+ if (!meta?.["id"] || !body["prompt"]) return "Missing required fields: metadata.id, prompt";
5191
+ return null;
5192
+ },
5193
+ mapToResponse: (a) => a.metadata
5194
+ };
5195
+ function createAgentRoutes(configDir, projectDir) {
5196
+ return createCrudRoutes(config3, configDir, projectDir);
5214
5197
  }
5215
5198
 
5216
5199
  // src/server/routes/workflows.ts
5217
- import { Router as Router4 } from "express";
5218
- function createWorkflowRoutes(configDir, config) {
5219
- const router = Router4();
5220
- router.get("/", async (_req, res) => {
5221
- const [defaults, userItems] = await Promise.all([loadDefaultWorkflows(), loadUserWorkflows(configDir)]);
5222
- const overrideIds = computeOverrideIds(defaults, userItems);
5223
- res.json({
5224
- defaults: defaults.map((p) => ({ ...p.metadata, startCondition: p.startCondition })),
5225
- userItems: userItems.map((p) => ({ ...p.metadata, startCondition: p.startCondition })),
5226
- activeWorkflowId: config.activeWorkflowId ?? "default",
5227
- overrideIds
5228
- });
5229
- });
5230
- router.get("/template-variables", (_req, res) => {
5231
- res.json({ variables: TEMPLATE_VARIABLES });
5232
- });
5233
- router.get("/defaults/:id", async (req, res) => {
5234
- const { id } = req.params;
5235
- const content = await getDefaultWorkflowContent(id);
5236
- if (!content) {
5237
- return res.status(404).json({ error: "Default workflow not found" });
5238
- }
5239
- res.json(content);
5240
- });
5241
- router.get("/default-ids", async (_req, res) => {
5242
- const ids = await getDefaultWorkflowIds();
5243
- res.json({ ids });
5244
- });
5245
- router.get("/:id", async (req, res) => {
5246
- const { id } = req.params;
5247
- const workflows = await loadAllWorkflows(configDir);
5248
- const workflow = findWorkflowById(id, workflows);
5249
- if (!workflow) {
5250
- return res.status(404).json({ error: "Workflow not found" });
5251
- }
5252
- res.json(workflow);
5253
- });
5254
- router.post("/", async (req, res) => {
5255
- const body = req.body;
5256
- if (!body?.metadata?.id || !body?.steps?.length) {
5257
- return res.status(400).json({ error: "Missing required fields: metadata.id, steps" });
5258
- }
5259
- const exists = await workflowExists(configDir, body.metadata.id);
5260
- if (exists) {
5261
- return res.status(409).json({ error: "A workflow with this ID already exists" });
5262
- }
5263
- await saveWorkflow(configDir, body);
5264
- res.status(201).json(body);
5265
- });
5266
- router.put("/:id", async (req, res) => {
5267
- const { id } = req.params;
5268
- const workflows = await loadAllWorkflows(configDir);
5269
- const existing = findWorkflowById(id, workflows);
5270
- if (!existing) {
5271
- return res.status(404).json({ error: "Workflow not found" });
5272
- }
5273
- const body = req.body;
5274
- const updated = {
5275
- ...body,
5276
- metadata: { ...body.metadata, id }
5277
- };
5278
- await saveWorkflow(configDir, updated);
5279
- res.json(updated);
5280
- });
5281
- router.delete("/:id", async (req, res) => {
5282
- const { id } = req.params;
5283
- const result = await deleteWorkflow(configDir, id);
5284
- if (!result.success) {
5285
- return res.status(403).json({ error: result.reason ?? "Cannot delete this workflow" });
5286
- }
5287
- res.json({ success: true });
5288
- });
5289
- router.post("/:id/duplicate", async (req, res) => {
5290
- const { id } = req.params;
5291
- const defaults = await loadDefaultWorkflows();
5292
- const userItems = await loadUserWorkflows(configDir);
5293
- const source = defaults.find((w) => w.metadata.id === id) ?? userItems.find((w) => w.metadata.id === id);
5294
- if (!source) {
5295
- return res.status(404).json({ error: "Workflow not found" });
5200
+ import "express";
5201
+ function createWorkflowRoutes(configDir, config4, projectDir) {
5202
+ const crudConfig = {
5203
+ dirName: "workflows",
5204
+ ext: ".workflow.json",
5205
+ loadDefaults: loadDefaultWorkflows,
5206
+ loadUser: loadUserWorkflows,
5207
+ loadProject: loadProjectWorkflows,
5208
+ loadAll: loadAllWorkflows,
5209
+ findById: findWorkflowById,
5210
+ save: saveWorkflow,
5211
+ saveToProject: saveWorkflowToProject,
5212
+ delete: deleteWorkflow,
5213
+ deleteProject: deleteProjectWorkflow,
5214
+ exists: workflowExists,
5215
+ isDefault: isDefaultWorkflow,
5216
+ getDefaultIds: getDefaultWorkflowIds,
5217
+ validateCreate: (body) => {
5218
+ const steps = body["steps"];
5219
+ if (!Array.isArray(steps) || steps.length === 0) return "Missing required fields: metadata.id, steps";
5220
+ return null;
5221
+ },
5222
+ mapToResponse: (w) => ({
5223
+ ...w.metadata,
5224
+ startCondition: w.startCondition
5225
+ }),
5226
+ extraGetData: () => Promise.resolve({ activeWorkflowId: config4.activeWorkflowId ?? "default" }),
5227
+ extraRoutes: (router) => {
5228
+ router.get("/template-variables", (_req, res) => {
5229
+ res.json({ variables: TEMPLATE_VARIABLES });
5230
+ });
5296
5231
  }
5297
- const newId = `${id}-copy-${Date.now()}`;
5298
- const duplicated = {
5299
- ...source,
5300
- metadata: { ...source.metadata, id: newId, name: `${source.metadata.name} (copy)` }
5301
- };
5302
- await saveWorkflow(configDir, duplicated);
5303
- res.status(201).json(duplicated);
5304
- });
5305
- return router;
5232
+ };
5233
+ return createCrudRoutes(crudConfig, configDir, projectDir);
5306
5234
  }
5307
5235
 
5308
5236
  // src/server/routes/dev-server.ts
5309
- import { Router as Router5 } from "express";
5237
+ import { Router as Router3 } from "express";
5310
5238
  function createDevServerRoutes() {
5311
- const router = Router5();
5239
+ const router = Router3();
5312
5240
  router.get("/", async (req, res) => {
5313
5241
  const workdir = req.query["workdir"];
5314
5242
  if (!workdir) return res.status(400).json({ error: "workdir required" });
5315
5243
  const status = devServerManager.getStatus(workdir);
5316
5244
  if (!status.config) {
5317
- const config = await devServerManager.loadConfig(workdir);
5318
- if (config) {
5319
- status.config = config;
5320
- status.url = config.url;
5321
- status.hotReload = config.hotReload;
5245
+ const config4 = await devServerManager.loadConfig(workdir);
5246
+ if (config4) {
5247
+ status.config = config4;
5248
+ status.url = config4.url;
5249
+ status.hotReload = config4.hotReload;
5322
5250
  }
5323
5251
  }
5324
5252
  res.json(status);
@@ -5364,8 +5292,8 @@ function createDevServerRoutes() {
5364
5292
  router.get("/config", async (req, res) => {
5365
5293
  const workdir = req.query["workdir"];
5366
5294
  if (!workdir) return res.status(400).json({ error: "workdir required" });
5367
- const config = await devServerManager.loadConfig(workdir);
5368
- res.json({ config });
5295
+ const config4 = await devServerManager.loadConfig(workdir);
5296
+ res.json({ config: config4 });
5369
5297
  });
5370
5298
  router.post("/config", async (req, res) => {
5371
5299
  const workdir = req.query["workdir"];
@@ -5375,9 +5303,9 @@ function createDevServerRoutes() {
5375
5303
  return res.status(400).json({ error: "command and url are required" });
5376
5304
  }
5377
5305
  try {
5378
- const config = { command, url, hotReload: hotReload ?? false, disableInspect: disableInspect ?? false };
5379
- await devServerManager.saveConfig(workdir, config);
5380
- res.json({ config });
5306
+ const config4 = { command, url, hotReload: hotReload ?? false, disableInspect: disableInspect ?? false };
5307
+ await devServerManager.saveConfig(workdir, config4);
5308
+ res.json({ config: config4 });
5381
5309
  } catch (err) {
5382
5310
  res.status(500).json({ error: err instanceof Error ? err.message : "Failed to save config" });
5383
5311
  }
@@ -5386,9 +5314,9 @@ function createDevServerRoutes() {
5386
5314
  }
5387
5315
 
5388
5316
  // src/server/routes/terminals.ts
5389
- import { Router as Router6 } from "express";
5317
+ import { Router as Router4 } from "express";
5390
5318
  function createTerminalRoutes() {
5391
- const router = Router6();
5319
+ const router = Router4();
5392
5320
  router.get("/", (req, res) => {
5393
5321
  const projectId = req.query["projectId"];
5394
5322
  const sessions = projectId ? terminalManager.getByProject(projectId) : [];
@@ -5426,19 +5354,105 @@ function createTerminalRoutes() {
5426
5354
  return router;
5427
5355
  }
5428
5356
 
5357
+ // src/server/routes/auto-update.ts
5358
+ import { Router as Router5 } from "express";
5359
+ import { spawn as spawn4 } from "child_process";
5360
+
5429
5361
  // src/constants.ts
5430
- var VERSION = "1.6.90";
5362
+ var VERSION = "1.6.92";
5363
+
5364
+ // src/server/routes/auto-update.ts
5365
+ var updateInProgress = false;
5366
+ function createAutoUpdateRoutes(options = {}) {
5367
+ const router = Router5();
5368
+ router.get("/check", async (req, res) => {
5369
+ const isTest = req.query["test"] === "1";
5370
+ const current = VERSION;
5371
+ if (isTest) {
5372
+ res.json({ current: "1.0.0", latest: "1.1.0", isUpdateAvailable: true });
5373
+ return;
5374
+ }
5375
+ try {
5376
+ const latest = await new Promise((resolve2, reject) => {
5377
+ const child = spawn4("npm", ["view", "openfox", "version"], {
5378
+ stdio: ["ignore", "pipe", "pipe"]
5379
+ });
5380
+ let stdout = "";
5381
+ child.stdout?.on("data", (data) => {
5382
+ stdout += data.toString();
5383
+ });
5384
+ child.on("close", (code) => {
5385
+ if (code === 0) {
5386
+ resolve2(stdout.trim());
5387
+ } else {
5388
+ reject(new Error(`npm view exited with code ${code}`));
5389
+ }
5390
+ });
5391
+ child.on("error", reject);
5392
+ setTimeout(() => {
5393
+ child.kill();
5394
+ reject(new Error("npm view timed out"));
5395
+ }, 1e4);
5396
+ });
5397
+ const isUpdateAvailable = current !== latest;
5398
+ res.json({ current, latest, isUpdateAvailable });
5399
+ } catch {
5400
+ res.json({ current, latest: current, isUpdateAvailable: false });
5401
+ }
5402
+ });
5403
+ router.post("/", async (req, res) => {
5404
+ if (options.requireAuth) {
5405
+ const authorized = await options.requireAuth(req);
5406
+ if (!authorized) {
5407
+ res.status(401).json({ error: "Unauthorized" });
5408
+ return;
5409
+ }
5410
+ }
5411
+ if (updateInProgress) {
5412
+ res.status(409).json({ error: "Update already in progress" });
5413
+ return;
5414
+ }
5415
+ try {
5416
+ let stderr = "";
5417
+ const child = spawn4("bash", ["-c", "openfox update"], {
5418
+ detached: true,
5419
+ stdio: ["ignore", "ignore", "pipe"]
5420
+ });
5421
+ if (child.stderr) {
5422
+ child.stderr.on("data", (data) => {
5423
+ stderr += data.toString();
5424
+ });
5425
+ }
5426
+ child.unref();
5427
+ updateInProgress = true;
5428
+ child.on("close", () => {
5429
+ updateInProgress = false;
5430
+ });
5431
+ setTimeout(() => {
5432
+ if (stderr) {
5433
+ console.error("[auto-update] subprocess error:", stderr);
5434
+ }
5435
+ updateInProgress = false;
5436
+ }, 3e4);
5437
+ res.json({ success: true });
5438
+ } catch (err) {
5439
+ updateInProgress = false;
5440
+ res.status(500).json({ error: err instanceof Error ? err.message : "Update failed to start" });
5441
+ }
5442
+ });
5443
+ return router;
5444
+ }
5431
5445
 
5432
5446
  // src/server/index.ts
5433
5447
  var __dirname2 = dirname5(fileURLToPath4(import.meta.url));
5434
- async function createServerHandle(config) {
5435
- setRuntimeConfig(config);
5436
- setLogLevel(config.logging?.level ?? void 0, config.mode);
5448
+ async function createServerHandle(config4) {
5449
+ setRuntimeConfig(config4);
5450
+ setLogLevel(config4.logging?.level ?? void 0, config4.mode);
5437
5451
  await loadServerAuthConfig();
5438
- const db = initDatabase(config);
5452
+ const db = initDatabase(config4);
5439
5453
  initEventStore(db);
5440
- const configDir = getGlobalConfigDir(config.mode ?? "production");
5441
- const providerManager = createProviderManager(config);
5454
+ const configDir = getGlobalConfigDir(config4.mode ?? "production");
5455
+ const providerManager = createProviderManager(config4);
5442
5456
  const sessionManager = new SessionManager(providerManager);
5443
5457
  devServerManager.setSessionManager(sessionManager);
5444
5458
  const useMock = process.env["OPENFOX_MOCK_LLM"] === "true";
@@ -5451,20 +5465,20 @@ async function createServerHandle(config) {
5451
5465
  const llmClient = getLLMClient();
5452
5466
  let backend;
5453
5467
  const useMock2 = process.env["OPENFOX_MOCK_LLM"] === "true";
5454
- if (config.llm.backend === "auto") {
5455
- backend = await detectBackend(config.llm.baseUrl, void 0, useMock2);
5468
+ if (config4.llm.backend === "auto") {
5469
+ backend = await detectBackend(config4.llm.baseUrl, void 0, useMock2);
5456
5470
  llmClient.setBackend(backend);
5457
5471
  if (!useMock2) {
5458
5472
  logger.info("Auto-detected LLM backend", { backend: getBackendDisplayName(backend) });
5459
5473
  }
5460
5474
  } else {
5461
- backend = config.llm.backend;
5475
+ backend = config4.llm.backend;
5462
5476
  llmClient.setBackend(backend);
5463
5477
  if (!useMock2) {
5464
5478
  logger.info("Using configured LLM backend", { backend: getBackendDisplayName(backend) });
5465
5479
  }
5466
5480
  }
5467
- const detected = await detectModel(config.llm.baseUrl);
5481
+ const detected = await detectModel(config4.llm.baseUrl);
5468
5482
  if (detected) {
5469
5483
  llmClient.setModel(detected);
5470
5484
  if (!useMock2) {
@@ -5472,7 +5486,7 @@ async function createServerHandle(config) {
5472
5486
  }
5473
5487
  } else {
5474
5488
  if (!useMock2) {
5475
- logger.warn("Could not auto-detect model, using config", { model: config.llm.model });
5489
+ logger.warn("Could not auto-detect model, using config", { model: config4.llm.model });
5476
5490
  }
5477
5491
  }
5478
5492
  const activeProvider = providerManager.getActiveProvider();
@@ -5494,7 +5508,7 @@ async function createServerHandle(config) {
5494
5508
  app.use(express.json({ limit: "10mb" }));
5495
5509
  const authMiddleware = async (req, res, next) => {
5496
5510
  const path = req.path;
5497
- const publicPaths = ["/health", "/auth", "/auth/login"];
5511
+ const publicPaths = ["/health", "/auth", "/auth/login", "/auto-update/check"];
5498
5512
  if (publicPaths.includes(path)) {
5499
5513
  return next();
5500
5514
  }
@@ -5642,7 +5656,7 @@ async function createServerHandle(config) {
5642
5656
  res.json({ project });
5643
5657
  });
5644
5658
  app.get("/api/sessions", async (req, res) => {
5645
- const { getRecentUserPromptsForSession } = await import("./events-AQKDXJFK.js");
5659
+ const { getRecentUserPromptsForSession } = await import("./events-K6AYMXFT.js");
5646
5660
  const projectId = req.query["projectId"];
5647
5661
  const limit = Math.min(parseInt(req.query["limit"]) || 20, 100);
5648
5662
  const offset = parseInt(req.query["offset"]) || 0;
@@ -5670,12 +5684,12 @@ async function createServerHandle(config) {
5670
5684
  if (!project) {
5671
5685
  return res.status(404).json({ error: "Project not found" });
5672
5686
  }
5673
- const { providerId, model } = parseDefaultModelSelection(config.defaultModelSelection);
5687
+ const { providerId, model } = parseDefaultModelSelection(config4.defaultModelSelection);
5674
5688
  const session = sessionManager.createSession(projectId, title, providerId ?? null, model ?? null);
5675
5689
  res.status(201).json({ session });
5676
5690
  });
5677
5691
  app.get("/api/sessions/:id", async (req, res) => {
5678
- const { getEventStore: getEventStore2 } = await import("./events-AQKDXJFK.js");
5692
+ const { getEventStore: getEventStore2 } = await import("./events-K6AYMXFT.js");
5679
5693
  const { buildMessagesFromStoredEvents: buildMessagesFromStoredEvents2 } = await import("./folding-4VHGQE6Y.js");
5680
5694
  const session = sessionManager.getSession(req.params.id);
5681
5695
  if (!session) {
@@ -5706,7 +5720,7 @@ async function createServerHandle(config) {
5706
5720
  res.json({ success: true });
5707
5721
  });
5708
5722
  app.post("/api/sessions/:id/provider", async (req, res) => {
5709
- const { getEventStore: getEventStore2 } = await import("./events-AQKDXJFK.js");
5723
+ const { getEventStore: getEventStore2 } = await import("./events-K6AYMXFT.js");
5710
5724
  const { buildMessagesFromStoredEvents: buildMessagesFromStoredEvents2 } = await import("./folding-4VHGQE6Y.js");
5711
5725
  const sessionId = req.params.id;
5712
5726
  const session = sessionManager.getSession(sessionId);
@@ -5718,11 +5732,11 @@ async function createServerHandle(config) {
5718
5732
  return res.status(400).json({ error: "providerId is required" });
5719
5733
  }
5720
5734
  sessionManager.setSessionProvider(sessionId, providerId, model ?? "auto");
5721
- const { loadGlobalConfig, saveGlobalConfig, setDefaultModelSelection } = await import("./config-HMMPXQ46.js");
5722
- const globalConfig = await loadGlobalConfig(config.mode ?? "production");
5735
+ const { loadGlobalConfig, saveGlobalConfig, setDefaultModelSelection } = await import("./config-7XJQNOKX.js");
5736
+ const globalConfig = await loadGlobalConfig(config4.mode ?? "production");
5723
5737
  const updatedConfig = setDefaultModelSelection(globalConfig, providerId, model ?? "auto");
5724
- await saveGlobalConfig(config.mode ?? "production", updatedConfig);
5725
- config.defaultModelSelection = updatedConfig.defaultModelSelection;
5738
+ await saveGlobalConfig(config4.mode ?? "production", updatedConfig);
5739
+ config4.defaultModelSelection = updatedConfig.defaultModelSelection;
5726
5740
  const contextState = sessionManager.getContextState(sessionId);
5727
5741
  const eventStore = getEventStore2();
5728
5742
  const events = eventStore.getEvents(sessionId);
@@ -5744,7 +5758,7 @@ async function createServerHandle(config) {
5744
5758
  res.json({ success: true });
5745
5759
  });
5746
5760
  app.put("/api/sessions/:id/mode", async (req, res) => {
5747
- const { getEventStore: getEventStore2 } = await import("./events-AQKDXJFK.js");
5761
+ const { getEventStore: getEventStore2 } = await import("./events-K6AYMXFT.js");
5748
5762
  const { buildMessagesFromStoredEvents: buildMessagesFromStoredEvents2 } = await import("./folding-4VHGQE6Y.js");
5749
5763
  const sessionId = req.params.id;
5750
5764
  const session = sessionManager.getSession(sessionId);
@@ -5752,12 +5766,16 @@ async function createServerHandle(config) {
5752
5766
  return res.status(404).json({ error: "Session not found" });
5753
5767
  }
5754
5768
  const { mode } = req.body;
5755
- if (!mode || !["planner", "builder"].includes(mode)) {
5756
- return res.status(400).json({ error: 'mode is required and must be "planner" or "builder"' });
5769
+ if (!mode) {
5770
+ return res.status(400).json({ error: "mode is required" });
5771
+ }
5772
+ const allAgents = await loadAllAgentsDefault();
5773
+ const topLevelIds = getTopLevelAgents(allAgents).map((a) => a.metadata.id);
5774
+ if (!topLevelIds.includes(mode)) {
5775
+ return res.status(400).json({ error: `Invalid mode. Must be one of: ${topLevelIds.join(", ")}` });
5757
5776
  }
5758
5777
  sessionManager.setMode(sessionId, mode);
5759
5778
  const eventStore = getEventStore2();
5760
- eventStore.append(sessionId, { type: "mode.changed", data: { mode, auto: false } });
5761
5779
  const events = eventStore.getEvents(sessionId);
5762
5780
  const messages = buildMessagesFromStoredEvents2(events);
5763
5781
  const updatedSession = sessionManager.getSession(sessionId);
@@ -5797,12 +5815,12 @@ async function createServerHandle(config) {
5797
5815
  if (!callId || approved === void 0) {
5798
5816
  return res.status(400).json({ error: "callId and approved are required" });
5799
5817
  }
5800
- const { providePathConfirmation } = await import("./tools-7MGUWXDP.js");
5818
+ const { providePathConfirmation } = await import("./tools-NFFQWH6M.js");
5801
5819
  const result = providePathConfirmation(callId, approved, alwaysAllow);
5802
5820
  if (!result.found) {
5803
5821
  return res.status(404).json({ error: "No pending path confirmation with that ID" });
5804
5822
  }
5805
- const { getEventStore: getEventStore2 } = await import("./events-AQKDXJFK.js");
5823
+ const { getEventStore: getEventStore2 } = await import("./events-K6AYMXFT.js");
5806
5824
  const { buildMessagesFromStoredEvents: buildMessagesFromStoredEvents2, foldPendingConfirmations: foldPendingConfirmations2 } = await import("./folding-4VHGQE6Y.js");
5807
5825
  const { createSessionStateMessage: createSessionStateMessage2 } = await import("./protocol-QDSVJTRN.js");
5808
5826
  const eventStore = getEventStore2();
@@ -5821,7 +5839,7 @@ async function createServerHandle(config) {
5821
5839
  if (!callId || !answer) {
5822
5840
  return res.status(400).json({ error: "callId and answer are required" });
5823
5841
  }
5824
- const { provideAnswer: provideAnswer2 } = await import("./tools-7MGUWXDP.js");
5842
+ const { provideAnswer: provideAnswer2 } = await import("./tools-NFFQWH6M.js");
5825
5843
  const found = provideAnswer2(callId, answer);
5826
5844
  if (!found) {
5827
5845
  return res.status(404).json({ error: "No pending question with that ID" });
@@ -5857,14 +5875,14 @@ async function createServerHandle(config) {
5857
5875
  if (!session) {
5858
5876
  return res.status(404).json({ error: "Session not found" });
5859
5877
  }
5860
- const { stopSessionExecution } = await import("./chat-handler-6ATTKNZT.js");
5861
- const { cancelQuestionsForSession, cancelPathConfirmationsForSession } = await import("./tools-7MGUWXDP.js");
5878
+ const { stopSessionExecution } = await import("./chat-handler-UPAEF6HR.js");
5879
+ const { cancelQuestionsForSession, cancelPathConfirmationsForSession } = await import("./tools-NFFQWH6M.js");
5862
5880
  stopSessionExecution(sessionId, sessionManager);
5863
5881
  abortSession(sessionId);
5864
5882
  cancelQuestionsForSession(sessionId, "Session stopped by user");
5865
5883
  cancelPathConfirmationsForSession(sessionId, "Session stopped by user");
5866
5884
  sessionManager.clearMessageQueue(sessionId);
5867
- const eventStore = (await import("./events-AQKDXJFK.js")).getEventStore();
5885
+ const eventStore = (await import("./events-K6AYMXFT.js")).getEventStore();
5868
5886
  eventStore.append(sessionId, { type: "running.changed", data: { isRunning: false } });
5869
5887
  res.json({ success: true });
5870
5888
  });
@@ -5878,7 +5896,7 @@ async function createServerHandle(config) {
5878
5896
  if (typeof messageIndex !== "number" || messageIndex < 0) {
5879
5897
  return res.status(400).json({ error: "messageIndex must be a non-negative number" });
5880
5898
  }
5881
- const { truncateSessionMessages } = await import("./events-AQKDXJFK.js");
5899
+ const { truncateSessionMessages } = await import("./events-K6AYMXFT.js");
5882
5900
  truncateSessionMessages(sessionId, messageIndex);
5883
5901
  res.json({ success: true });
5884
5902
  });
@@ -5909,13 +5927,13 @@ async function createServerHandle(config) {
5909
5927
  res.json({ accepted: true });
5910
5928
  });
5911
5929
  app.get("/api/settings/:key", async (req, res) => {
5912
- const { getSetting, SETTINGS_DEFAULTS } = await import("./settings-VTVOBDYG.js");
5930
+ const { getSetting, SETTINGS_DEFAULTS } = await import("./settings-AYSWDZUI.js");
5913
5931
  const key = req.params.key;
5914
5932
  const value = getSetting(key) ?? SETTINGS_DEFAULTS[key] ?? null;
5915
5933
  res.json({ key, value });
5916
5934
  });
5917
5935
  app.put("/api/settings/:key", async (req, res) => {
5918
- const { setSetting } = await import("./settings-VTVOBDYG.js");
5936
+ const { setSetting } = await import("./settings-AYSWDZUI.js");
5919
5937
  const key = req.params.key;
5920
5938
  const { value } = req.body;
5921
5939
  if (value === void 0) {
@@ -5930,8 +5948,8 @@ async function createServerHandle(config) {
5930
5948
  let visionFallback;
5931
5949
  let globalWorkdir;
5932
5950
  try {
5933
- const { loadGlobalConfig, getVisionFallback } = await import("./config-HMMPXQ46.js");
5934
- const globalConfig = await loadGlobalConfig(config.mode ?? "production");
5951
+ const { loadGlobalConfig, getVisionFallback } = await import("./config-7XJQNOKX.js");
5952
+ const globalConfig = await loadGlobalConfig(config4.mode ?? "production");
5935
5953
  const fallback = getVisionFallback(globalConfig);
5936
5954
  if (fallback) {
5937
5955
  visionFallback = {
@@ -5948,13 +5966,13 @@ async function createServerHandle(config) {
5948
5966
  version: VERSION,
5949
5967
  model: llmClient.getModel(),
5950
5968
  maxContext: providerManager.getCurrentModelContext(),
5951
- llmUrl: activeProvider?.url ?? config.llm.baseUrl,
5969
+ llmUrl: activeProvider?.url ?? config4.llm.baseUrl,
5952
5970
  llmStatus: getLlmStatus(),
5953
5971
  backend: llmClient.getBackend(),
5954
- workdir: globalWorkdir ?? config.workdir,
5972
+ workdir: globalWorkdir ?? config4.workdir,
5955
5973
  providers: providerManager.getProviders(),
5956
5974
  activeProviderId: providerManager.getActiveProviderId(),
5957
- defaultModelSelection: config.defaultModelSelection,
5975
+ defaultModelSelection: config4.defaultModelSelection,
5958
5976
  visionFallback
5959
5977
  });
5960
5978
  });
@@ -5963,7 +5981,7 @@ async function createServerHandle(config) {
5963
5981
  const currentModel = providerManager.getCurrentModel();
5964
5982
  if (currentModel === "auto") {
5965
5983
  const activeProvider = providerManager.getActiveProvider();
5966
- const baseUrl = activeProvider?.url ?? config.llm.baseUrl;
5984
+ const baseUrl = activeProvider?.url ?? config4.llm.baseUrl;
5967
5985
  const detected = await detectModel(baseUrl);
5968
5986
  if (detected) {
5969
5987
  llmClient.setModel(detected);
@@ -6009,8 +6027,8 @@ async function createServerHandle(config) {
6009
6027
  return res.status(400).json({ error: "name, url, and backend are required" });
6010
6028
  }
6011
6029
  try {
6012
- const { loadGlobalConfig, saveGlobalConfig, addProvider, setDefaultModelSelection } = await import("./config-HMMPXQ46.js");
6013
- const globalConfig = await loadGlobalConfig(config.mode ?? "production");
6030
+ const { loadGlobalConfig, saveGlobalConfig, addProvider, setDefaultModelSelection } = await import("./config-7XJQNOKX.js");
6031
+ const globalConfig = await loadGlobalConfig(config4.mode ?? "production");
6014
6032
  const providerBackend = backend;
6015
6033
  const configWithProvider = addProvider(globalConfig, {
6016
6034
  name,
@@ -6025,7 +6043,7 @@ async function createServerHandle(config) {
6025
6043
  configWithProvider.providers[configWithProvider.providers.length - 1].id,
6026
6044
  model ?? "auto"
6027
6045
  );
6028
- await saveGlobalConfig(config.mode ?? "production", finalConfig);
6046
+ await saveGlobalConfig(config4.mode ?? "production", finalConfig);
6029
6047
  providerManager.setProviders(finalConfig.providers, finalConfig.defaultModelSelection ?? void 0);
6030
6048
  const newProvider = finalConfig.providers[finalConfig.providers.length - 1];
6031
6049
  res.status(201).json({
@@ -6042,14 +6060,14 @@ async function createServerHandle(config) {
6042
6060
  app.post("/api/init/config", async (req, res) => {
6043
6061
  const { workdir, visionFallback } = req.body;
6044
6062
  try {
6045
- const { loadGlobalConfig, saveGlobalConfig } = await import("./config-HMMPXQ46.js");
6046
- const globalConfig = await loadGlobalConfig(config.mode ?? "production");
6063
+ const { loadGlobalConfig, saveGlobalConfig } = await import("./config-7XJQNOKX.js");
6064
+ const globalConfig = await loadGlobalConfig(config4.mode ?? "production");
6047
6065
  const updatedConfig = {
6048
6066
  ...globalConfig,
6049
6067
  workspace: workdir ? { workdir } : globalConfig.workspace,
6050
6068
  visionFallback: visionFallback ?? globalConfig.visionFallback
6051
6069
  };
6052
- await saveGlobalConfig(config.mode ?? "production", updatedConfig);
6070
+ await saveGlobalConfig(config4.mode ?? "production", updatedConfig);
6053
6071
  res.json({ success: true });
6054
6072
  } catch (error) {
6055
6073
  res.status(500).json({
@@ -6070,10 +6088,10 @@ async function createServerHandle(config) {
6070
6088
  });
6071
6089
  app.delete("/api/providers/:id", async (req, res) => {
6072
6090
  const { id } = req.params;
6073
- const { loadGlobalConfig, saveGlobalConfig, removeProvider } = await import("./config-HMMPXQ46.js");
6074
- const globalConfig = await loadGlobalConfig(config.mode ?? "production");
6091
+ const { loadGlobalConfig, saveGlobalConfig, removeProvider } = await import("./config-7XJQNOKX.js");
6092
+ const globalConfig = await loadGlobalConfig(config4.mode ?? "production");
6075
6093
  const updatedConfig = removeProvider(globalConfig, id);
6076
- await saveGlobalConfig(config.mode ?? "production", updatedConfig);
6094
+ await saveGlobalConfig(config4.mode ?? "production", updatedConfig);
6077
6095
  providerManager.setProviders(updatedConfig.providers, updatedConfig.defaultModelSelection ?? void 0);
6078
6096
  res.json({ success: true });
6079
6097
  });
@@ -6090,10 +6108,10 @@ async function createServerHandle(config) {
6090
6108
  return res.status(400).json({ error: result.error });
6091
6109
  }
6092
6110
  const llmClient = getLLMClient();
6093
- const { loadGlobalConfig, saveGlobalConfig, setDefaultModelSelection } = await import("./config-HMMPXQ46.js");
6094
- const globalConfig = await loadGlobalConfig(config.mode ?? "production");
6111
+ const { loadGlobalConfig, saveGlobalConfig, setDefaultModelSelection } = await import("./config-7XJQNOKX.js");
6112
+ const globalConfig = await loadGlobalConfig(config4.mode ?? "production");
6095
6113
  const updatedConfig = setDefaultModelSelection(globalConfig, id, llmClient.getModel());
6096
- await saveGlobalConfig(config.mode ?? "production", updatedConfig);
6114
+ await saveGlobalConfig(config4.mode ?? "production", updatedConfig);
6097
6115
  res.json({
6098
6116
  success: true,
6099
6117
  activeProviderId: id,
@@ -6121,8 +6139,8 @@ async function createServerHandle(config) {
6121
6139
  if (!result.success) {
6122
6140
  return res.status(400).json({ error: result.error });
6123
6141
  }
6124
- const { loadGlobalConfig, saveGlobalConfig } = await import("./config-HMMPXQ46.js");
6125
- const globalConfig = await loadGlobalConfig(config.mode ?? "production");
6142
+ const { loadGlobalConfig, saveGlobalConfig } = await import("./config-7XJQNOKX.js");
6143
+ const globalConfig = await loadGlobalConfig(config4.mode ?? "production");
6126
6144
  const updatedProviders = providerManager.getProviders();
6127
6145
  const updatedConfig = {
6128
6146
  ...globalConfig,
@@ -6130,7 +6148,7 @@ async function createServerHandle(config) {
6130
6148
  activeProviderId: providerManager.getActiveProviderId(),
6131
6149
  defaultModelSelection: providerManager.getActiveProviderId() ? `${providerManager.getActiveProviderId()}/${providerManager.getCurrentModel()}` : void 0
6132
6150
  };
6133
- await saveGlobalConfig(config.mode ?? "production", updatedConfig);
6151
+ await saveGlobalConfig(config4.mode ?? "production", updatedConfig);
6134
6152
  let contextState = null;
6135
6153
  const sessions = sessionManager.listSessions();
6136
6154
  if (sessions.length > 0) {
@@ -6165,12 +6183,27 @@ async function createServerHandle(config) {
6165
6183
  status: updatedProvider?.status ?? "unknown"
6166
6184
  });
6167
6185
  });
6168
- app.use("/api/skills", createSkillRoutes(configDir));
6169
- app.use("/api/commands", createCommandRoutes(configDir));
6170
- app.use("/api/agents", createAgentRoutes(configDir));
6171
- app.use("/api/workflows", createWorkflowRoutes(configDir, config));
6186
+ const projectDir = config4.workdir;
6187
+ app.use("/api/skills", createSkillRoutes(configDir, projectDir));
6188
+ app.use("/api/commands", createCommandRoutes(configDir, projectDir));
6189
+ app.use("/api/agents", createAgentRoutes(configDir, projectDir));
6190
+ app.use("/api/workflows", createWorkflowRoutes(configDir, config4, projectDir));
6172
6191
  app.use("/api/dev-server", createDevServerRoutes());
6173
6192
  app.use("/api/terminals", createTerminalRoutes());
6193
+ app.use(
6194
+ "/api/auto-update",
6195
+ createAutoUpdateRoutes({
6196
+ requireAuth: async (req) => {
6197
+ const authConfig = getAuthConfig();
6198
+ if (authConfig?.strategy !== "network" || !authConfig?.encryptedPassword) {
6199
+ return true;
6200
+ }
6201
+ const token = req.headers["x-session-token"];
6202
+ if (!token) return false;
6203
+ return Boolean(await isValidToken(token));
6204
+ }
6205
+ })
6206
+ );
6174
6207
  app.get("/api/sessions/:id/background-processes", async (req, res) => {
6175
6208
  const { getSessionProcesses } = await import("./manager-W7N3XQ4Q.js");
6176
6209
  const sessionId = req.params.id;
@@ -6204,7 +6237,7 @@ async function createServerHandle(config) {
6204
6237
  const entries = await import("fs/promises").then((m) => m.readdir(resolvedPath, { withFileTypes: true }));
6205
6238
  const directories = entries.filter((entry) => entry.isDirectory() && !entry.name.startsWith(".")).map((entry) => ({
6206
6239
  name: entry.name,
6207
- path: join5(resolvedPath, entry.name)
6240
+ path: join6(resolvedPath, entry.name)
6208
6241
  })).sort((a, b) => a.name.localeCompare(b.name));
6209
6242
  const parent = dirname5(resolvedPath);
6210
6243
  const hasParent = parent !== resolvedPath;
@@ -6225,7 +6258,7 @@ async function createServerHandle(config) {
6225
6258
  }
6226
6259
  });
6227
6260
  const webDir = resolve(__dirname2, "../../web");
6228
- const isDev = config.mode === "development";
6261
+ const isDev = config4.mode === "development";
6229
6262
  let viteServer;
6230
6263
  if (isDev) {
6231
6264
  logger.info("Dev mode: using Vite middleware");
@@ -6251,7 +6284,7 @@ async function createServerHandle(config) {
6251
6284
  }
6252
6285
  });
6253
6286
  app.get("/fox.svg", (_req, res) => {
6254
- readFile3(join5(webDir, "fox.svg")).then((content) => {
6287
+ readFile3(join6(webDir, "fox.svg")).then((content) => {
6255
6288
  res.set("Content-Type", "image/svg+xml");
6256
6289
  res.send(content);
6257
6290
  }).catch(() => {
@@ -6260,7 +6293,7 @@ async function createServerHandle(config) {
6260
6293
  });
6261
6294
  app.use(
6262
6295
  "/sounds",
6263
- express.static(join5(webDir, "public", "sounds"), {
6296
+ express.static(join6(webDir, "public", "sounds"), {
6264
6297
  setHeaders: (res) => {
6265
6298
  res.set("Content-Type", "audio/mpeg");
6266
6299
  }
@@ -6269,24 +6302,24 @@ async function createServerHandle(config) {
6269
6302
  app.use("/__inspect__.js", (_req, res) => {
6270
6303
  res.set("Access-Control-Allow-Origin", "*");
6271
6304
  res.set("Content-Type", "application/javascript");
6272
- readFile3(join5(webDir, "public", "__inspect__.js")).then((content) => res.send(content)).catch(() => res.status(404).send("Not found"));
6305
+ readFile3(join6(webDir, "public", "__inspect__.js")).then((content) => res.send(content)).catch(() => res.status(404).send("Not found"));
6273
6306
  });
6274
6307
  app.get("/*path", (req, res) => {
6275
6308
  if (req.path.startsWith("/api/")) {
6276
6309
  return;
6277
6310
  }
6278
- readFile3(join5(webDir, "index.html"), "utf-8").then((indexHtml) => viteServer.transformIndexHtml(req.originalUrl, indexHtml)).then((transformed) => res.send(transformed)).catch((err) => {
6311
+ readFile3(join6(webDir, "index.html"), "utf-8").then((indexHtml) => viteServer.transformIndexHtml(req.originalUrl, indexHtml)).then((transformed) => res.send(transformed)).catch((err) => {
6279
6312
  logger.error("Error serving index.html", { error: err });
6280
6313
  res.status(500).send("Server error");
6281
6314
  });
6282
6315
  });
6283
- logger.info("Vite middleware ready", { port: config.server.port });
6316
+ logger.info("Vite middleware ready", { port: config4.server.port });
6284
6317
  }
6285
6318
  if (!isDev) {
6286
6319
  const distWebDir = resolve(__dirname2, "web");
6287
6320
  app.use(
6288
6321
  "/assets",
6289
- express.static(join5(distWebDir, "assets"), {
6322
+ express.static(join6(distWebDir, "assets"), {
6290
6323
  setHeaders: (res, filepath) => {
6291
6324
  if (filepath.endsWith(".css")) {
6292
6325
  res.set("Content-Type", "text/css");
@@ -6294,11 +6327,11 @@ async function createServerHandle(config) {
6294
6327
  }
6295
6328
  })
6296
6329
  );
6297
- app.use("/manifest.webmanifest", express.static(join5(distWebDir, "manifest.webmanifest")));
6298
- app.use("/registerSW.js", express.static(join5(distWebDir, "registerSW.js")));
6299
- app.use("/sw.js", express.static(join5(distWebDir, "sw.js")));
6330
+ app.use("/manifest.webmanifest", express.static(join6(distWebDir, "manifest.webmanifest")));
6331
+ app.use("/registerSW.js", express.static(join6(distWebDir, "registerSW.js")));
6332
+ app.use("/sw.js", express.static(join6(distWebDir, "sw.js")));
6300
6333
  app.get("/fox.svg", (_req, res) => {
6301
- readFile3(join5(distWebDir, "fox.svg")).then((content) => {
6334
+ readFile3(join6(distWebDir, "fox.svg")).then((content) => {
6302
6335
  res.set("Content-Type", "image/svg+xml");
6303
6336
  res.send(content);
6304
6337
  }).catch(() => {
@@ -6307,7 +6340,7 @@ async function createServerHandle(config) {
6307
6340
  });
6308
6341
  app.use(
6309
6342
  "/sounds",
6310
- express.static(join5(distWebDir, "sounds"), {
6343
+ express.static(join6(distWebDir, "sounds"), {
6311
6344
  setHeaders: (res) => {
6312
6345
  res.set("Content-Type", "audio/mpeg");
6313
6346
  }
@@ -6316,15 +6349,15 @@ async function createServerHandle(config) {
6316
6349
  app.use("/__inspect__.js", (_req, res) => {
6317
6350
  res.set("Access-Control-Allow-Origin", "*");
6318
6351
  res.set("Content-Type", "application/javascript");
6319
- readFile3(join5(distWebDir, "__inspect__.js")).then((content) => res.send(content)).catch(() => res.status(404).send("Not found"));
6352
+ readFile3(join6(distWebDir, "__inspect__.js")).then((content) => res.send(content)).catch(() => res.status(404).send("Not found"));
6320
6353
  });
6321
6354
  app.use("/__inspect__.js", (_req, res) => {
6322
6355
  res.set("Access-Control-Allow-Origin", "*");
6323
6356
  res.set("Content-Type", "application/javascript");
6324
- readFile3(join5(distWebDir, "__inspect__.js")).then((content) => res.send(content)).catch(() => res.status(404).send("Not found"));
6357
+ readFile3(join6(distWebDir, "__inspect__.js")).then((content) => res.send(content)).catch(() => res.status(404).send("Not found"));
6325
6358
  });
6326
6359
  app.get("/", (_req, res) => {
6327
- readFile3(join5(distWebDir, "index.html"), "utf-8").then((content) => res.send(content)).catch(() => {
6360
+ readFile3(join6(distWebDir, "index.html"), "utf-8").then((content) => res.send(content)).catch(() => {
6328
6361
  res.status(404).send("Web UI not built. Run `npm run build:web`");
6329
6362
  });
6330
6363
  });
@@ -6332,7 +6365,7 @@ async function createServerHandle(config) {
6332
6365
  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") {
6333
6366
  return;
6334
6367
  }
6335
- readFile3(join5(distWebDir, "index.html"), "utf-8").then((content) => res.send(content)).catch(() => {
6368
+ readFile3(join6(distWebDir, "index.html"), "utf-8").then((content) => res.send(content)).catch(() => {
6336
6369
  res.status(404).send("Web UI not built");
6337
6370
  });
6338
6371
  });
@@ -6340,14 +6373,14 @@ async function createServerHandle(config) {
6340
6373
  const httpServer = createHttpServer(app);
6341
6374
  const wssExports = createWebSocketServer(
6342
6375
  httpServer,
6343
- config,
6376
+ config4,
6344
6377
  getLLMClient,
6345
6378
  () => providerManager.getActiveProvider(),
6346
6379
  sessionManager,
6347
6380
  providerManager
6348
6381
  );
6349
6382
  const wss = wssExports.wss;
6350
- const { QueueProcessor } = await import("./processor-4SOP4Q6S.js");
6383
+ const { QueueProcessor } = await import("./processor-D4XQ5QMB.js");
6351
6384
  const queueProcessor = new QueueProcessor({
6352
6385
  sessionManager,
6353
6386
  providerManager,
@@ -6366,17 +6399,17 @@ async function createServerHandle(config) {
6366
6399
  };
6367
6400
  return {
6368
6401
  httpServer,
6369
- ctx: { config, sessionManager, llmClient: getLLMClient(), toolRegistry, providerManager },
6402
+ ctx: { config: config4, sessionManager, llmClient: getLLMClient(), toolRegistry, providerManager },
6370
6403
  start: (port) => new Promise((resolve2, reject) => {
6371
- const listenPort = port ?? config.server.port;
6372
- const host = config.server.host;
6404
+ const listenPort = port ?? config4.server.port;
6405
+ const host = config4.server.host;
6373
6406
  httpServer.listen(listenPort, host, () => {
6374
6407
  const addr = httpServer.address();
6375
6408
  const actualPort = typeof addr === "object" && addr ? addr.port : listenPort;
6376
6409
  const client = getLLMClient();
6377
6410
  logger.info(`OpenFox server running at http://${host}:${actualPort}`);
6378
6411
  logger.info(`WebSocket available at ws://${host}:${actualPort}/ws`);
6379
- logger.info(`LLM backend: ${client.getBackend()}, model: ${client.getModel()}, url: ${config.llm.baseUrl}`);
6412
+ logger.info(`LLM backend: ${client.getBackend()}, model: ${client.getModel()}, url: ${config4.llm.baseUrl}`);
6380
6413
  resolve2({ port: actualPort });
6381
6414
  });
6382
6415
  httpServer.on("error", reject);
@@ -6399,8 +6432,8 @@ async function createServerHandle(config) {
6399
6432
  })
6400
6433
  };
6401
6434
  }
6402
- async function createServer(config) {
6403
- const handle = await createServerHandle(config);
6435
+ async function createServer(config4) {
6436
+ const handle = await createServerHandle(config4);
6404
6437
  await handle.start();
6405
6438
  const shutdown = () => {
6406
6439
  const forceExitTimer = setTimeout(() => {
@@ -6422,4 +6455,4 @@ export {
6422
6455
  createServerHandle,
6423
6456
  createServer
6424
6457
  };
6425
- //# sourceMappingURL=chunk-ZORPFD4C.js.map
6458
+ //# sourceMappingURL=chunk-2BS6LMIU.js.map