openfox 2.0.70 → 2.0.72

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 (68) hide show
  1. package/dist/{chat-handler-K3DWIGHG.js → chat-handler-LK5RUAGO.js} +19 -19
  2. package/dist/{chunk-64GN6OPR.js → chunk-2DCJERYX.js} +8 -8
  3. package/dist/chunk-2M6JM7OR.js +7 -0
  4. package/dist/{chunk-XDWV5CYV.js → chunk-4LBSC4R3.js} +15 -20
  5. package/dist/{chunk-TFDK6VSG.js → chunk-6XKM25OT.js} +13 -13
  6. package/dist/{chunk-E4POUD2M.js → chunk-6XOA2WPU.js} +2 -2
  7. package/dist/{chunk-YQ3MEZJL.js → chunk-CHPRGEY5.js} +3 -3
  8. package/dist/{chunk-C3VLGVSQ.js → chunk-DJASQ2ZB.js} +11 -2
  9. package/dist/{chunk-4QBDV6LX.js → chunk-E5LLEO3C.js} +2 -2
  10. package/dist/{chunk-JHGKXQBO.js → chunk-EZK7CGJ7.js} +87 -30
  11. package/dist/{chunk-ZZ5JPVBT.js → chunk-FILRB5QX.js} +259 -300
  12. package/dist/{chunk-PVWAE43V.js → chunk-GMF36EZ3.js} +123 -106
  13. package/dist/{chunk-GMXKJGJ2.js → chunk-I4RWK2L2.js} +8 -4
  14. package/dist/{chunk-PK2RMVMB.js → chunk-I72ACQ6R.js} +19 -18
  15. package/dist/{chunk-F4PMNP7S.js → chunk-NOU2QIL5.js} +3 -2
  16. package/dist/{chunk-UTSSFPYA.js → chunk-O2BE7KVQ.js} +8 -8
  17. package/dist/{chunk-7OW6X764.js → chunk-OHGQILRF.js} +15 -4
  18. package/dist/{chunk-TFCHWMOJ.js → chunk-PYB2UEC3.js} +11 -5
  19. package/dist/{chunk-XUPKS5FL.js → chunk-QNKXZH3Z.js} +2 -2
  20. package/dist/{chunk-X5DOUA3Z.js → chunk-S7O77CVO.js} +2 -2
  21. package/dist/{chunk-46OR4W3L.js → chunk-TK4BZIXA.js} +45 -31
  22. package/dist/{chunk-KXVFHMSM.js → chunk-UV3EMWL4.js} +2 -2
  23. package/dist/chunk-X2Y3AJPP.js +216 -0
  24. package/dist/{chunk-QFSDJIUS.js → chunk-XWBNYU2L.js} +3 -3
  25. package/dist/cli/dev.js +1 -1
  26. package/dist/cli/index.js +1 -1
  27. package/dist/{client-63U5CTPE.js → client-SC6X65GY.js} +3 -3
  28. package/dist/{client-pure-XHH4RRLB.js → client-pure-NMLDKRDN.js} +6 -4
  29. package/dist/{compactor-IPMMFFHM.js → compactor-4UKYWC5F.js} +9 -9
  30. package/dist/{events-CDQ4HASP.js → events-AEQ6GQXI.js} +4 -4
  31. package/dist/{folding-V2K34EGN.js → folding-ONH3D4OK.js} +2 -2
  32. package/dist/{inspect-proxy-OVEFBJ26.js → inspect-proxy-PHVZBZUO.js} +4 -4
  33. package/dist/{manager-JXYRGR6B.js → manager-24C2AXVF.js} +6 -6
  34. package/dist/{orchestrator-PUV4WG77.js → orchestrator-RTZVMWQV.js} +18 -18
  35. package/dist/package.json +2 -2
  36. package/dist/{platform-AB3GIEGO.js → platform-XQ7BS3KQ.js} +4 -4
  37. package/dist/{processor-O2QW27BD.js → processor-3O26I7DO.js} +19 -19
  38. package/dist/{project-creator-EMGRSPYS.js → project-creator-FQRBN3RG.js} +3 -3
  39. package/dist/{projects-7HSBZY2D.js → projects-LHRNCUGW.js} +3 -3
  40. package/dist/{protocol-CTNE3ANP.js → protocol-4QFU7WAI.js} +2 -2
  41. package/dist/{protocol-DIhV-tAc.d.ts → protocol-DGQWVFcP.d.ts} +1 -1
  42. package/dist/provider/index.d.ts +4 -4
  43. package/dist/{provider-IBIWR4AI.js → provider-OJUCZ5RN.js} +4 -4
  44. package/dist/{provider-manager-7C3JVOLW.js → provider-manager-MAKXSL6L.js} +4 -4
  45. package/dist/{pwa-T6JRAYJB.js → pwa-SEWCVKZX.js} +7 -8
  46. package/dist/{serve-NOHD22CT.js → serve-6PSAQ2YH.js} +26 -24
  47. package/dist/server/index.d.ts +12 -22
  48. package/dist/server/index.js +23 -22
  49. package/dist/server-G2J4RAXL.js +38 -0
  50. package/dist/{service-6DL3MJZD.js → service-2PW2V25M.js} +6 -1
  51. package/dist/{settings-6ZPWOBWL.js → settings-PFXRYXZU.js} +3 -3
  52. package/dist/shared/index.d.ts +3 -3
  53. package/dist/{store-FXTCHBI4.js → store-SARB6V2N.js} +2 -2
  54. package/dist/{tools-CALGWIV2.js → tools-X52DFUS6.js} +17 -17
  55. package/dist/{types-BsfYpEai.d.ts → types-C4cG9CtC.d.ts} +3 -2
  56. package/dist/{types-CBC6lLqc.d.ts → types-DSZhFA8O.d.ts} +1 -1
  57. package/dist/update-I2M2RG4Q.js +40 -0
  58. package/dist/web/assets/index-C7SW16F7.js +305 -0
  59. package/dist/web/assets/{index-BVTG7SRG.css → index-CmechlaK.css} +1 -1
  60. package/dist/web/index.html +2 -2
  61. package/dist/web/sw.js +1 -1
  62. package/dist/workspace-ET3FHZYN.js +32 -0
  63. package/package.json +2 -2
  64. package/dist/chunk-VLV3XWZS.js +0 -191
  65. package/dist/cli/update.sh +0 -16
  66. package/dist/server-RLNPHLY2.js +0 -38
  67. package/dist/web/assets/index-CtWMMQ_X.js +0 -305
  68. package/dist/worktree-6U45P63P.js +0 -30
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  createLLMClient
3
- } from "./chunk-E4POUD2M.js";
3
+ } from "./chunk-6XOA2WPU.js";
4
4
  import {
5
5
  getModelProfile
6
6
  } from "./chunk-V4IE7HJY.js";
@@ -12,6 +12,9 @@ import {
12
12
  ensureVersionPrefix,
13
13
  stripVersionPrefix
14
14
  } from "./chunk-HNCM3D7Y.js";
15
+ import {
16
+ resolveAttachmentsInMessages
17
+ } from "./chunk-EZK7CGJ7.js";
15
18
  import {
16
19
  logger
17
20
  } from "./chunk-K44MW7JJ.js";
@@ -55,8 +58,16 @@ function createTransportLLMClient(provider, modelId, transport) {
55
58
  setBackend(next) {
56
59
  backend = next;
57
60
  },
58
- complete: (request) => transport.complete(request, context()),
59
- stream: (request) => transport.stream(request, context())
61
+ complete: async (request) => {
62
+ const supportsVision = request.modelSettings?.supportsVision ?? profile.supportsVision ?? false;
63
+ const resolved = { ...request, messages: await resolveAttachmentsInMessages(request.messages, supportsVision) };
64
+ return transport.complete(resolved, context());
65
+ },
66
+ stream: async function* (request) {
67
+ const supportsVision = request.modelSettings?.supportsVision ?? profile.supportsVision ?? false;
68
+ const resolved = { ...request, messages: await resolveAttachmentsInMessages(request.messages, supportsVision) };
69
+ yield* transport.stream(resolved, context());
70
+ }
60
71
  };
61
72
  }
62
73
 
@@ -796,4 +807,4 @@ export {
796
807
  parseDefaultModelSelection,
797
808
  createProviderManager
798
809
  };
799
- //# sourceMappingURL=chunk-7OW6X764.js.map
810
+ //# sourceMappingURL=chunk-OHGQILRF.js.map
@@ -8,10 +8,10 @@ import {
8
8
  removeProcess,
9
9
  startProcess,
10
10
  updateStatus
11
- } from "./chunk-PK2RMVMB.js";
11
+ } from "./chunk-I72ACQ6R.js";
12
12
  import {
13
13
  getPlatformShell
14
- } from "./chunk-X5DOUA3Z.js";
14
+ } from "./chunk-S7O77CVO.js";
15
15
 
16
16
  // src/server/utils/shell.ts
17
17
  import { spawn } from "child_process";
@@ -20,12 +20,18 @@ function checkAborted(signal) {
20
20
  }
21
21
  function spawnShell(command, options) {
22
22
  const shell = getPlatformShell();
23
- return spawn(shell.command, [...shell.args, command], {
23
+ const isCmd = shell.command === "cmd.exe";
24
+ const args = isCmd ? ["/d", "/s", "/c", `"${command}"`] : [...shell.args, command];
25
+ return spawn(shell.command, args, {
26
+ ...isCmd ? { windowsVerbatimArguments: true } : {},
24
27
  cwd: options.cwd,
25
28
  env: { ...process.env },
26
29
  stdio: options.stdio ?? ["ignore", "pipe", "pipe"],
27
30
  windowsHide: true,
28
- ...options.detached ? { detached: true } : {}
31
+ // detached is only useful for Unix process-group semantics; on win32 it
32
+ // spawns a visible console window and makes MSYS binaries (tail, sleep)
33
+ // hang on pipes. Tree-kill uses taskkill on win32, so detached is unneeded.
34
+ ...options.detached && process.platform !== "win32" ? { detached: true } : {}
29
35
  });
30
36
  }
31
37
  function spawnShellProcess(command, cwd, _signal, detached = false) {
@@ -147,4 +153,4 @@ export {
147
153
  getSessionProcesses2 as getSessionProcesses,
148
154
  getProcessLogs
149
155
  };
150
- //# sourceMappingURL=chunk-TFCHWMOJ.js.map
156
+ //# sourceMappingURL=chunk-PYB2UEC3.js.map
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  getDatabase
3
- } from "./chunk-GMXKJGJ2.js";
3
+ } from "./chunk-I4RWK2L2.js";
4
4
 
5
5
  // src/server/db/settings.ts
6
6
  var SETTINGS_KEYS = {
@@ -101,4 +101,4 @@ export {
101
101
  deleteSetting,
102
102
  getAllSettings
103
103
  };
104
- //# sourceMappingURL=chunk-XUPKS5FL.js.map
104
+ //# sourceMappingURL=chunk-QNKXZH3Z.js.map
@@ -1,7 +1,7 @@
1
1
  import {
2
2
  SETTINGS_KEYS,
3
3
  getSetting
4
- } from "./chunk-XUPKS5FL.js";
4
+ } from "./chunk-QNKXZH3Z.js";
5
5
 
6
6
  // src/server/utils/platform.ts
7
7
  import os from "os";
@@ -63,4 +63,4 @@ export {
63
63
  isWindowsPath,
64
64
  isAbsolutePath
65
65
  };
66
- //# sourceMappingURL=chunk-X5DOUA3Z.js.map
66
+ //# sourceMappingURL=chunk-S7O77CVO.js.map
@@ -3,7 +3,7 @@ import {
3
3
  computeSessionHash,
4
4
  runAgentTurn,
5
5
  runChatTurn
6
- } from "./chunk-UTSSFPYA.js";
6
+ } from "./chunk-O2BE7KVQ.js";
7
7
  import {
8
8
  deleteItemFromDir,
9
9
  devServerManager,
@@ -13,31 +13,39 @@ import {
13
13
  jsonSerializer,
14
14
  loadAllAgentsDefault,
15
15
  saveItemToDir
16
- } from "./chunk-PVWAE43V.js";
16
+ } from "./chunk-GMF36EZ3.js";
17
+ import {
18
+ gitSpawnEnv
19
+ } from "./chunk-WGMJCFCE.js";
20
+ import {
21
+ getPendingQuestionsForSession,
22
+ provideAnswer
23
+ } from "./chunk-EU3WWTFH.js";
17
24
  import {
18
25
  appendCompactionPrompt
19
- } from "./chunk-YQ3MEZJL.js";
26
+ } from "./chunk-CHPRGEY5.js";
20
27
  import {
21
28
  TurnMetrics,
22
29
  createMessageStartEvent
23
- } from "./chunk-QFSDJIUS.js";
30
+ } from "./chunk-XWBNYU2L.js";
24
31
  import {
25
32
  checkAborted,
26
33
  onProcessEvent,
27
34
  spawnShellProcess
28
- } from "./chunk-TFCHWMOJ.js";
35
+ } from "./chunk-PYB2UEC3.js";
29
36
  import {
30
- gitSpawnEnv
31
- } from "./chunk-WGMJCFCE.js";
37
+ killProcessTree,
38
+ terminateProcessTree
39
+ } from "./chunk-I72ACQ6R.js";
32
40
  import {
33
41
  getCurrentContextWindowId,
34
42
  getEventStore,
35
43
  getRuntimeConfig
36
- } from "./chunk-TFDK6VSG.js";
44
+ } from "./chunk-6XKM25OT.js";
37
45
  import {
38
46
  buildMessagesFromStoredEvents,
39
47
  foldPendingConfirmations
40
- } from "./chunk-C3VLGVSQ.js";
48
+ } from "./chunk-DJASQ2ZB.js";
41
49
  import {
42
50
  createChatErrorMessage,
43
51
  createChatMessageMessage,
@@ -52,14 +60,10 @@ import {
52
60
  parseClientMessage,
53
61
  serializeServerMessage,
54
62
  storedEventToServerMessage
55
- } from "./chunk-F4PMNP7S.js";
63
+ } from "./chunk-NOU2QIL5.js";
56
64
  import {
57
65
  getPlatformShell
58
- } from "./chunk-X5DOUA3Z.js";
59
- import {
60
- getPendingQuestionsForSession,
61
- provideAnswer
62
- } from "./chunk-EU3WWTFH.js";
66
+ } from "./chunk-S7O77CVO.js";
63
67
  import {
64
68
  logger
65
69
  } from "./chunk-K44MW7JJ.js";
@@ -84,6 +88,17 @@ import WebSocket from "ws";
84
88
  // src/server/terminal/manager.ts
85
89
  import * as pty from "node-pty";
86
90
  import os from "os";
91
+ async function killPty(ptyProcess) {
92
+ if (process.platform === "win32" && ptyProcess.pid) {
93
+ await killProcessTree(ptyProcess.pid);
94
+ try {
95
+ ptyProcess.kill();
96
+ } catch {
97
+ }
98
+ return;
99
+ }
100
+ ptyProcess.kill();
101
+ }
87
102
  var TerminalManager = class {
88
103
  sessions = /* @__PURE__ */ new Map();
89
104
  outputCallbacks = /* @__PURE__ */ new Set();
@@ -172,7 +187,7 @@ var TerminalManager = class {
172
187
  if (!session) {
173
188
  return false;
174
189
  }
175
- session.pty.kill();
190
+ void killPty(session.pty);
176
191
  this.sessions.delete(sessionId);
177
192
  logger.info("Terminal session killed", { id: sessionId });
178
193
  return true;
@@ -202,11 +217,10 @@ var TerminalManager = class {
202
217
  this.exitCallbacks.delete(callback);
203
218
  };
204
219
  }
205
- killAll() {
206
- for (const session of this.sessions.values()) {
207
- session.pty.kill();
208
- }
220
+ async killAll() {
221
+ const kills = Array.from(this.sessions.values()).map((session) => killPty(session.pty));
209
222
  this.sessions.clear();
223
+ await Promise.all(kills);
210
224
  logger.info("All terminal sessions killed");
211
225
  }
212
226
  };
@@ -548,13 +562,13 @@ function executeShellCommand(command, cwd, timeout, signal) {
548
562
  let killed = false;
549
563
  const timer = setTimeout(() => {
550
564
  killed = true;
551
- proc.kill("SIGTERM");
565
+ void terminateProcessTree(proc);
552
566
  resolve({ exitCode: 1, stdout, stderr: stderr + "\nCommand timed out", success: false });
553
567
  }, timeout);
554
568
  const onAbort = () => {
555
569
  if (!killed) {
556
570
  killed = true;
557
- proc.kill("SIGTERM");
571
+ void terminateProcessTree(proc, { immediate: true });
558
572
  clearTimeout(timer);
559
573
  reject(new Error("Aborted"));
560
574
  }
@@ -611,7 +625,7 @@ function formatCriteriaList(entries) {
611
625
  }).join("\n");
612
626
  }
613
627
  async function formatModifiedFiles(session) {
614
- const effectiveWorkdir = session.worktree ?? session.workdir;
628
+ const effectiveWorkdir = session.workspace ?? session.workdir;
615
629
  return formatGitDiffFiles(effectiveWorkdir);
616
630
  }
617
631
  function resolveTemplate(template, ctx) {
@@ -788,7 +802,7 @@ async function executeWorkflow(workflow, options, subGroup) {
788
802
  const currentWindowMessageOptions = getCurrentWindowMessageOptions(sessionId);
789
803
  const criteriaEntries = session.metadataEntries["criteria"] ?? [];
790
804
  const templateCtx = {
791
- workdir: session.worktree ?? session.workdir,
805
+ workdir: session.workspace ?? session.workdir,
792
806
  reason: buildReason(session.metadataEntries),
793
807
  verifierFindings: lastStepOutput["content"] ?? "",
794
808
  previousStepOutput: lastStepOutput["stdout"] ?? "",
@@ -960,7 +974,7 @@ async function executeWorkflow(workflow, options, subGroup) {
960
974
  })
961
975
  );
962
976
  }
963
- const result = await executeShellCommand(command, session.worktree ?? session.workdir, timeout, signal);
977
+ const result = await executeShellCommand(command, session.workspace ?? session.workdir, timeout, signal);
964
978
  const output = [result.stdout, result.stderr].filter(Boolean).join("\n").trim();
965
979
  lastStepOutput = { stdout: result.stdout ?? "", stderr: result.stderr ?? "", exitCode: String(result.exitCode) };
966
980
  const outputContent = output ? `Exit code: ${result.exitCode}
@@ -1610,11 +1624,11 @@ function createWebSocketServer(httpServer, _config, getLLMClient, getActiveProvi
1610
1624
  const messages = buildMessagesFromStoredEvents(events);
1611
1625
  const pendingConfirmations = foldPendingConfirmations(events);
1612
1626
  const pendingQuestions = getPendingQuestionsForSession(updatedSession.id);
1613
- const effectiveWorkdir = updatedSession.worktree ?? updatedSession.workdir;
1614
- let worktreeChanged = false;
1627
+ const effectiveWorkdir = updatedSession.workspace ?? updatedSession.workdir;
1628
+ let workspaceChanged = false;
1615
1629
  for (const [, client] of clients) {
1616
1630
  if (client.activeSessionId === updatedSession.id && client.activeWorkdir !== effectiveWorkdir) {
1617
- worktreeChanged = true;
1631
+ workspaceChanged = true;
1618
1632
  const prevWorkdir = client.activeWorkdir;
1619
1633
  client.activeWorkdir = effectiveWorkdir;
1620
1634
  if (prevWorkdir) moduleStopGitPolling(prevWorkdir);
@@ -1625,7 +1639,7 @@ function createWebSocketServer(httpServer, _config, getLLMClient, getActiveProvi
1625
1639
  updatedSession.id,
1626
1640
  createSessionStateMessage(updatedSession, messages, pendingConfirmations, pendingQuestions)
1627
1641
  );
1628
- if (worktreeChanged && effectiveWorkdir) {
1642
+ if (workspaceChanged && effectiveWorkdir) {
1629
1643
  ;
1630
1644
  (async () => {
1631
1645
  const branch = await moduleGitBranch(effectiveWorkdir);
@@ -1832,7 +1846,7 @@ async function handleClientMessage(ws, client, message, _getLLMClient, _getActiv
1832
1846
  return;
1833
1847
  }
1834
1848
  client.activeSessionId = session.id;
1835
- const effectiveWorkdir = session.worktree ?? session.workdir;
1849
+ const effectiveWorkdir = session.workspace ?? session.workdir;
1836
1850
  client.activeWorkdir = effectiveWorkdir;
1837
1851
  if (effectiveWorkdir) {
1838
1852
  const branch = await moduleGitBranch(effectiveWorkdir);
@@ -2158,4 +2172,4 @@ export {
2158
2172
  signalMcpReady,
2159
2173
  createWebSocketServer
2160
2174
  };
2161
- //# sourceMappingURL=chunk-46OR4W3L.js.map
2175
+ //# sourceMappingURL=chunk-TK4BZIXA.js.map
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  getDatabase
3
- } from "./chunk-GMXKJGJ2.js";
3
+ } from "./chunk-I4RWK2L2.js";
4
4
 
5
5
  // src/server/db/projects.ts
6
6
  function createProject(name, workdir) {
@@ -117,4 +117,4 @@ export {
117
117
  deleteProject,
118
118
  toggleStar
119
119
  };
120
- //# sourceMappingURL=chunk-KXVFHMSM.js.map
120
+ //# sourceMappingURL=chunk-UV3EMWL4.js.map
@@ -0,0 +1,216 @@
1
+ import {
2
+ gitSpawnEnv
3
+ } from "./chunk-WGMJCFCE.js";
4
+ import {
5
+ logger
6
+ } from "./chunk-K44MW7JJ.js";
7
+
8
+ // src/server/git/workspace.ts
9
+ import { spawn, execSync } from "child_process";
10
+ import { mkdir as mkdir2 } from "fs/promises";
11
+ import { resolve as resolve2, join as join2 } from "path";
12
+ import { homedir, platform } from "os";
13
+
14
+ // src/server/git/workspace-config.ts
15
+ import { readFile, writeFile, mkdir } from "fs/promises";
16
+ import { resolve, join } from "path";
17
+ var CONFIG_FILENAME = ".openfox/workspace.json";
18
+ function getConfigPath(workdir) {
19
+ return join(resolve(workdir), CONFIG_FILENAME);
20
+ }
21
+ async function loadWorkspaceConfig(workdir) {
22
+ try {
23
+ const configPath = getConfigPath(workdir);
24
+ const raw = await readFile(configPath, "utf-8");
25
+ const parsed = JSON.parse(raw);
26
+ if (!parsed.setup) return null;
27
+ return { setup: parsed.setup };
28
+ } catch {
29
+ return null;
30
+ }
31
+ }
32
+ async function saveWorkspaceConfig(workdir, config) {
33
+ const resolved = resolve(workdir);
34
+ const dirPath = join(resolved, ".openfox");
35
+ await mkdir(dirPath, { recursive: true });
36
+ const configPath = getConfigPath(workdir);
37
+ await writeFile(configPath, JSON.stringify(config, null, 2) + "\n", "utf-8");
38
+ }
39
+
40
+ // src/server/git/workspace.ts
41
+ function captureStdout(cwd, args) {
42
+ return new Promise((resolvePromise) => {
43
+ const proc = spawn("git", args, { cwd, env: gitSpawnEnv(), stdio: ["ignore", "pipe", "ignore"] });
44
+ let out = "";
45
+ proc.stdout.on("data", (d) => {
46
+ out += d.toString();
47
+ });
48
+ proc.on("close", (code) => resolvePromise(code === 0 ? out : null));
49
+ proc.on("error", () => resolvePromise(null));
50
+ });
51
+ }
52
+ function runGit(cwd, args) {
53
+ return new Promise((resolvePromise, reject) => {
54
+ const proc = spawn("git", args, { cwd, env: gitSpawnEnv(), stdio: ["ignore", "pipe", "pipe"] });
55
+ let stderr = "";
56
+ proc.stderr.on("data", (d) => {
57
+ stderr += d.toString();
58
+ });
59
+ proc.on("close", (code) => {
60
+ if (code === 0) resolvePromise();
61
+ else reject(new Error(stderr.trim() || `git ${args[0]} failed (code ${code})`));
62
+ });
63
+ proc.on("error", (err) => reject(err));
64
+ });
65
+ }
66
+ function getServerMode() {
67
+ return process.env["OPENFOX_DEV"] === "true" ? "development" : "production";
68
+ }
69
+ function getGlobalDataDir() {
70
+ const mode = getServerMode();
71
+ const suffix = mode === "development" ? "-dev" : "";
72
+ const home = homedir();
73
+ switch (platform()) {
74
+ case "darwin":
75
+ return join2(home, "Library", "Application Support", `openfox${suffix}`);
76
+ case "win32":
77
+ return join2(process.env["LOCALAPPDATA"] ?? join2(home, "AppData", "Local"), `openfox${suffix}`);
78
+ default:
79
+ return join2(process.env["XDG_DATA_HOME"] ?? join2(home, ".local", "share"), `openfox${suffix}`);
80
+ }
81
+ }
82
+ function getWorkspacesDir(projectName) {
83
+ return join2(getGlobalDataDir(), "workspaces", projectName);
84
+ }
85
+ function getGitBranch(cwd) {
86
+ return captureStdout(cwd, ["rev-parse", "--abbrev-ref", "HEAD"]).then((r) => r?.trim() ?? null);
87
+ }
88
+ function listBranches(cwd) {
89
+ return captureStdout(cwd, ["branch", "--format", "%(refname:short)%09%(HEAD)"]).then((out) => {
90
+ if (!out) return [];
91
+ const branches = [];
92
+ for (const line of out.trim().split("\n")) {
93
+ if (!line) continue;
94
+ const [name, head] = line.split(" ");
95
+ if (name) branches.push({ name, current: head === "*" });
96
+ }
97
+ return branches;
98
+ });
99
+ }
100
+ async function getCommitsBehind(cwd, branch) {
101
+ const out = await captureStdout(cwd, ["rev-list", "--count", "HEAD..origin/" + branch, "--"]);
102
+ if (out === null) return null;
103
+ const count = parseInt(out.trim(), 10);
104
+ return Number.isNaN(count) ? null : count;
105
+ }
106
+ function validateRef(cwd, name) {
107
+ return runGit(cwd, ["check-ref-format", `refs/heads/${name}`]);
108
+ }
109
+ function checkoutBranch(cwd, name) {
110
+ return runGit(cwd, ["checkout", name]);
111
+ }
112
+ function createBranch(cwd, name) {
113
+ return runGit(cwd, ["checkout", "-b", name]);
114
+ }
115
+ async function workspaceExists(projectName, name) {
116
+ const dir = getWorkspacesDir(projectName);
117
+ const wsPath = resolve2(dir, name);
118
+ const st = await statSafe(wsPath);
119
+ if (!st?.isDirectory()) return false;
120
+ const gitDir = join2(wsPath, ".git");
121
+ const gitSt = await statSafe(gitDir);
122
+ return gitSt?.isDirectory() ?? false;
123
+ }
124
+ async function listWorkspaces(projectName) {
125
+ const dir = getWorkspacesDir(projectName);
126
+ try {
127
+ const { readdir } = await import("fs/promises");
128
+ const entries = await readdir(dir, { withFileTypes: true });
129
+ const workspaces = [];
130
+ for (const entry of entries) {
131
+ if (entry.isDirectory()) {
132
+ const wsPath = resolve2(dir, entry.name);
133
+ const branch = await getGitBranch(wsPath);
134
+ workspaces.push({ path: wsPath, name: entry.name, branch });
135
+ }
136
+ }
137
+ return workspaces.sort((a, b) => a.name.localeCompare(b.name));
138
+ } catch {
139
+ return [];
140
+ }
141
+ }
142
+ async function ensureWorkspace(projectDir, name, projectName, branch) {
143
+ const workspacesDir = getWorkspacesDir(projectName);
144
+ const wsPath = resolve2(workspacesDir, name);
145
+ await mkdir2(workspacesDir, { recursive: true });
146
+ const existing = await statSafe(wsPath);
147
+ if (existing?.isDirectory()) {
148
+ const gitDir = join2(wsPath, ".git");
149
+ const gitStat = await statSafe(gitDir);
150
+ if (!gitStat?.isDirectory()) {
151
+ throw new Error("Existing path is not a valid workspace: " + wsPath);
152
+ }
153
+ logger.info("Workspace directory already exists, reusing", { path: wsPath });
154
+ } else {
155
+ await runGit(projectDir, ["clone", "--shared", projectDir, wsPath]);
156
+ }
157
+ const st = await statSafe(wsPath);
158
+ if (!st?.isDirectory()) {
159
+ throw new Error("Failed to clone workspace");
160
+ }
161
+ if (branch) {
162
+ await runGit(wsPath, ["checkout", branch]).catch(async () => {
163
+ const sourceBranch = await getGitBranch(projectDir) ?? "main";
164
+ await runGit(wsPath, ["checkout", "-b", branch, sourceBranch]);
165
+ });
166
+ }
167
+ const config = await loadWorkspaceConfig(projectDir);
168
+ if (config?.setup && config.setup.length > 0) {
169
+ for (const cmd of config.setup) {
170
+ logger.info("Running workspace setup command", { command: cmd, cwd: wsPath });
171
+ try {
172
+ execSync(cmd, { cwd: wsPath, stdio: "inherit", env: { ...process.env } });
173
+ } catch (err) {
174
+ logger.warn("Workspace setup command failed", { command: cmd, error: String(err) });
175
+ }
176
+ }
177
+ }
178
+ return { path: wsPath, name };
179
+ }
180
+ async function deleteWorkspace(projectName, name) {
181
+ const dir = getWorkspacesDir(projectName);
182
+ const wsPath = resolve2(dir, name);
183
+ const st = await statSafe(wsPath);
184
+ if (!st?.isDirectory()) {
185
+ throw new Error(`Workspace "${name}" does not exist`);
186
+ }
187
+ const { rm } = await import("fs/promises");
188
+ await rm(wsPath, { recursive: true, force: true });
189
+ logger.info("Deleted workspace", { projectName, name, path: wsPath });
190
+ }
191
+ async function statSafe(p) {
192
+ try {
193
+ const { stat } = await import("fs/promises");
194
+ return await stat(p);
195
+ } catch {
196
+ return null;
197
+ }
198
+ }
199
+
200
+ export {
201
+ loadWorkspaceConfig,
202
+ saveWorkspaceConfig,
203
+ runGit,
204
+ getWorkspacesDir,
205
+ getGitBranch,
206
+ listBranches,
207
+ getCommitsBehind,
208
+ validateRef,
209
+ checkoutBranch,
210
+ createBranch,
211
+ workspaceExists,
212
+ listWorkspaces,
213
+ ensureWorkspace,
214
+ deleteWorkspace
215
+ };
216
+ //# sourceMappingURL=chunk-X2Y3AJPP.js.map
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  getPlatformShell
3
- } from "./chunk-X5DOUA3Z.js";
3
+ } from "./chunk-S7O77CVO.js";
4
4
  import {
5
5
  streamWithSegments
6
6
  } from "./chunk-J2GP3J3X.js";
@@ -12,7 +12,7 @@ import {
12
12
  } from "./chunk-YHEQTVFV.js";
13
13
  import {
14
14
  buildModelParams
15
- } from "./chunk-JHGKXQBO.js";
15
+ } from "./chunk-EZK7CGJ7.js";
16
16
  import {
17
17
  logger
18
18
  } from "./chunk-K44MW7JJ.js";
@@ -678,4 +678,4 @@ export {
678
678
  createChatDoneEvent,
679
679
  consumeStreamGenerator
680
680
  };
681
- //# sourceMappingURL=chunk-QFSDJIUS.js.map
681
+ //# sourceMappingURL=chunk-XWBNYU2L.js.map
package/dist/cli/dev.js CHANGED
@@ -1,7 +1,7 @@
1
1
  #!/usr/bin/env node
2
2
  import {
3
3
  runCli
4
- } from "../chunk-XDWV5CYV.js";
4
+ } from "../chunk-4LBSC4R3.js";
5
5
  import "../chunk-XY3LESVZ.js";
6
6
  import {
7
7
  logger
package/dist/cli/index.js CHANGED
@@ -1,7 +1,7 @@
1
1
  #!/usr/bin/env node
2
2
  import {
3
3
  runCli
4
- } from "../chunk-XDWV5CYV.js";
4
+ } from "../chunk-4LBSC4R3.js";
5
5
  import "../chunk-XY3LESVZ.js";
6
6
  import {
7
7
  logger
@@ -1,14 +1,14 @@
1
1
  import {
2
2
  createLLMClient
3
- } from "./chunk-E4POUD2M.js";
3
+ } from "./chunk-6XOA2WPU.js";
4
4
  import "./chunk-V4IE7HJY.js";
5
5
  import "./chunk-YHEQTVFV.js";
6
6
  import "./chunk-HNCM3D7Y.js";
7
- import "./chunk-JHGKXQBO.js";
7
+ import "./chunk-EZK7CGJ7.js";
8
8
  import "./chunk-ZO5EXQMG.js";
9
9
  import "./chunk-K44MW7JJ.js";
10
10
  import "./chunk-5WRI5ZAA.js";
11
11
  export {
12
12
  createLLMClient
13
13
  };
14
- //# sourceMappingURL=client-63U5CTPE.js.map
14
+ //# sourceMappingURL=client-SC6X65GY.js.map
@@ -6,8 +6,9 @@ import {
6
6
  convertTools,
7
7
  getThinking,
8
8
  mapFinishReason,
9
- parseToolArguments
10
- } from "./chunk-JHGKXQBO.js";
9
+ parseToolArguments,
10
+ resolveAttachmentsInMessages
11
+ } from "./chunk-EZK7CGJ7.js";
11
12
  import "./chunk-5WRI5ZAA.js";
12
13
  export {
13
14
  buildModelParams,
@@ -17,6 +18,7 @@ export {
17
18
  convertTools,
18
19
  getThinking,
19
20
  mapFinishReason,
20
- parseToolArguments
21
+ parseToolArguments,
22
+ resolveAttachmentsInMessages
21
23
  };
22
- //# sourceMappingURL=client-pure-XHH4RRLB.js.map
24
+ //# sourceMappingURL=client-pure-NMLDKRDN.js.map
@@ -2,17 +2,17 @@ import {
2
2
  appendCompactionPrompt,
3
3
  getCompactionTarget,
4
4
  shouldCompact
5
- } from "./chunk-YQ3MEZJL.js";
6
- import "./chunk-QFSDJIUS.js";
7
- import "./chunk-TFDK6VSG.js";
8
- import "./chunk-C3VLGVSQ.js";
9
- import "./chunk-X5DOUA3Z.js";
10
- import "./chunk-XUPKS5FL.js";
11
- import "./chunk-GMXKJGJ2.js";
5
+ } from "./chunk-CHPRGEY5.js";
6
+ import "./chunk-XWBNYU2L.js";
7
+ import "./chunk-6XKM25OT.js";
8
+ import "./chunk-DJASQ2ZB.js";
9
+ import "./chunk-S7O77CVO.js";
10
+ import "./chunk-QNKXZH3Z.js";
11
+ import "./chunk-I4RWK2L2.js";
12
12
  import "./chunk-J2GP3J3X.js";
13
13
  import "./chunk-V4IE7HJY.js";
14
14
  import "./chunk-YHEQTVFV.js";
15
- import "./chunk-JHGKXQBO.js";
15
+ import "./chunk-EZK7CGJ7.js";
16
16
  import "./chunk-K44MW7JJ.js";
17
17
  import "./chunk-5WRI5ZAA.js";
18
18
  export {
@@ -20,4 +20,4 @@ export {
20
20
  getCompactionTarget,
21
21
  shouldCompact
22
22
  };
23
- //# sourceMappingURL=compactor-IPMMFFHM.js.map
23
+ //# sourceMappingURL=compactor-4UKYWC5F.js.map
@@ -38,7 +38,7 @@ import {
38
38
  isStoredEvent,
39
39
  isTurnEvent,
40
40
  truncateSessionMessages
41
- } from "./chunk-TFDK6VSG.js";
41
+ } from "./chunk-6XKM25OT.js";
42
42
  import {
43
43
  buildContextMessagesFromEventHistory,
44
44
  buildContextMessagesFromMessages,
@@ -55,8 +55,8 @@ import {
55
55
  foldTodos,
56
56
  foldTurnEventsToSnapshotMessages,
57
57
  getMessagesForWindow
58
- } from "./chunk-C3VLGVSQ.js";
59
- import "./chunk-GMXKJGJ2.js";
58
+ } from "./chunk-DJASQ2ZB.js";
59
+ import "./chunk-I4RWK2L2.js";
60
60
  import "./chunk-K44MW7JJ.js";
61
61
  import "./chunk-5WRI5ZAA.js";
62
62
  export {
@@ -115,4 +115,4 @@ export {
115
115
  isTurnEvent,
116
116
  truncateSessionMessages
117
117
  };
118
- //# sourceMappingURL=events-CDQ4HASP.js.map
118
+ //# sourceMappingURL=events-AEQ6GQXI.js.map