openfox 2.0.77 → 2.0.78
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/{chat-handler-FNRKORKP.js → chat-handler-5ZHPYU24.js} +18 -17
- package/dist/{chunk-MF3UW36X.js → chunk-37WF2Z3T.js} +84 -17
- package/dist/{chunk-OBQPXUXI.js → chunk-5CQRB7AD.js} +2 -2
- package/dist/chunk-64TKWWNF.js +7 -0
- package/dist/{chunk-LXFLRM7M.js → chunk-6AE3JPDJ.js} +2 -2
- package/dist/chunk-AANVMTYU.js +327 -0
- package/dist/{chunk-YMFCFMMB.js → chunk-AFJLY43Z.js} +8 -3
- package/dist/{chunk-CI5BWHW7.js → chunk-DI4SIPFT.js} +46 -27
- package/dist/{chunk-XNCHOD2X.js → chunk-EHVDAVYG.js} +3 -2
- package/dist/{chunk-DH3SSBZN.js → chunk-EYIWUONA.js} +2 -2
- package/dist/{chunk-NGJ5XJ7D.js → chunk-M7EFXRDS.js} +57 -290
- package/dist/{chunk-4QHDQP2N.js → chunk-MTUYDBSL.js} +2 -2
- package/dist/{chunk-YPHLCRFP.js → chunk-NUR2B4RU.js} +3 -3
- package/dist/{chunk-46RORSKU.js → chunk-NZIYELSQ.js} +9 -7
- package/dist/{chunk-7QXRQ5ZL.js → chunk-RGUUZD5B.js} +5 -1
- package/dist/{chunk-YDV7RIQ6.js → chunk-SGBXBP3S.js} +143 -43
- package/dist/{chunk-O3NUNU4H.js → chunk-SJTG56CH.js} +5 -4
- package/dist/{chunk-DJASQ2ZB.js → chunk-TSVFRWSG.js} +24 -5
- package/dist/{chunk-3TQN25OM.js → chunk-UIU7OGHP.js} +25 -9
- package/dist/{chunk-BX3VJP2D.js → chunk-UMX7MMNW.js} +573 -221
- package/dist/{chunk-VMYDUZWI.js → chunk-VGWJFXPR.js} +23 -11
- package/dist/{chunk-CPTC2MD5.js → chunk-WUSWNLTV.js} +3 -3
- package/dist/{chunk-5GOOSZX7.js → chunk-X6BXZFO7.js} +1 -1
- package/dist/cli/dev.js +1 -1
- package/dist/cli/index.js +1 -1
- package/dist/{compactor-BJXFE5PL.js → compactor-G7B3V24F.js} +9 -8
- package/dist/{events-FLWRLM6V.js → events-KNOZX7YG.js} +7 -4
- package/dist/{folding-ONH3D4OK.js → folding-OPP2BVUB.js} +2 -2
- package/dist/{inspect-proxy-JHZV34QR.js → inspect-proxy-DRYIIH4O.js} +4 -4
- package/dist/{manager-OPMCSYN2.js → manager-ATWZUL3O.js} +5 -5
- package/dist/{orchestrator-67BZJEOO.js → orchestrator-L6LX5BV7.js} +17 -16
- package/dist/package.json +4 -1
- package/dist/{path-security-ZROWNL22.js → path-security-HUXQHEQ5.js} +10 -7
- package/dist/{platform-QGXO3CNR.js → platform-VBNNMTR5.js} +4 -4
- package/dist/{processor-BCOSEMZK.js → processor-UQX5KL56.js} +18 -17
- package/dist/{project-creator-C22J43BC.js → project-creator-NNUITNUI.js} +3 -3
- package/dist/{projects-WSVINK2C.js → projects-ZGJGICGT.js} +3 -3
- package/dist/{protocol-Ceapo3jt.d.ts → protocol-BXxJmsOW.d.ts} +2 -1
- package/dist/{protocol-EWEVW65B.js → protocol-Q7X4NEME.js} +3 -3
- package/dist/provider/index.d.ts +4 -4
- package/dist/{serve-BD44GN7N.js → serve-QWC3U5MB.js} +21 -20
- package/dist/server/index.d.ts +27 -4
- package/dist/server/index.js +20 -19
- package/dist/server-3XUD2NB7.js +40 -0
- package/dist/sessions-SFG42M4W.js +42 -0
- package/dist/{settings-7XDMSPIC.js → settings-6M55N4Q2.js} +5 -3
- package/dist/shared/index.d.ts +3 -3
- package/dist/shared/index.js +1 -1
- package/dist/{tools-T7S45DOQ.js → tools-HGKUD327.js} +23 -20
- package/dist/{types-DABVUbUu.d.ts → types-C308SU4B.d.ts} +2 -0
- package/dist/{types-pssMT-oY.d.ts → types-o07LJcLI.d.ts} +1 -1
- package/dist/{update-56VZOLQJ.js → update-NWZALB5M.js} +2 -2
- package/dist/web/assets/{index-BuUd6IgK.js → index-7_xKqeJc.js} +75 -75
- package/dist/web/assets/index-DRkTjPo3.css +32 -0
- package/dist/web/index.html +4 -4
- package/dist/web/manifest.webmanifest +1 -1
- package/dist/web/registerSW.js +1 -1
- package/dist/web/sw.js +1 -1
- package/dist/{workspace-6QTTUMMW.js → workspace-J74ZMSOJ.js} +14 -2
- package/package.json +4 -1
- package/dist/chunk-NUXS5OQB.js +0 -7
- package/dist/server-OLRW27DH.js +0 -39
- package/dist/web/assets/index-BNVBxKnB.css +0 -32
|
@@ -21,7 +21,7 @@ import {
|
|
|
21
21
|
tokenFromPassword,
|
|
22
22
|
verifyPassword,
|
|
23
23
|
workflowExists
|
|
24
|
-
} from "./chunk-
|
|
24
|
+
} from "./chunk-DI4SIPFT.js";
|
|
25
25
|
import {
|
|
26
26
|
agentExists,
|
|
27
27
|
createToolRegistry,
|
|
@@ -66,7 +66,7 @@ import {
|
|
|
66
66
|
setSkillEnabled,
|
|
67
67
|
skillExists,
|
|
68
68
|
updateOwnedSkill
|
|
69
|
-
} from "./chunk-
|
|
69
|
+
} from "./chunk-SGBXBP3S.js";
|
|
70
70
|
import {
|
|
71
71
|
createMcpTools
|
|
72
72
|
} from "./chunk-NWO6GRYE.js";
|
|
@@ -74,14 +74,18 @@ import {
|
|
|
74
74
|
deleteWorkspace,
|
|
75
75
|
ensureWorkspace,
|
|
76
76
|
getCommitsBehind,
|
|
77
|
+
getDefaultBranch,
|
|
77
78
|
getGitBranch,
|
|
79
|
+
getGlobalDataDir,
|
|
78
80
|
getWorkspacesDir,
|
|
79
81
|
loadWorkspaceConfig,
|
|
82
|
+
resolveAndValidateSourceBranch,
|
|
80
83
|
runGit,
|
|
81
84
|
saveWorkspaceConfig,
|
|
85
|
+
validateRef,
|
|
82
86
|
validateWorkspaceName,
|
|
83
87
|
workspaceExists
|
|
84
|
-
} from "./chunk-
|
|
88
|
+
} from "./chunk-37WF2Z3T.js";
|
|
85
89
|
import {
|
|
86
90
|
EventEmitter
|
|
87
91
|
} from "./chunk-EU3WWTFH.js";
|
|
@@ -89,25 +93,23 @@ import {
|
|
|
89
93
|
ALWAYS_ALLOWED,
|
|
90
94
|
ALWAYS_ALLOWED_FOR_SUBAGENTS,
|
|
91
95
|
TOP_LEVEL_ONLY_TOOLS
|
|
92
|
-
} from "./chunk-
|
|
93
|
-
import {
|
|
94
|
-
getProject
|
|
95
|
-
} from "./chunk-LXFLRM7M.js";
|
|
96
|
+
} from "./chunk-EHVDAVYG.js";
|
|
96
97
|
import {
|
|
97
98
|
getPathSeparator,
|
|
98
99
|
isAbsolutePath
|
|
99
|
-
} from "./chunk-
|
|
100
|
+
} from "./chunk-MTUYDBSL.js";
|
|
100
101
|
import {
|
|
101
102
|
deleteSetting,
|
|
102
103
|
getSetting,
|
|
103
104
|
setSetting
|
|
104
|
-
} from "./chunk-
|
|
105
|
+
} from "./chunk-AFJLY43Z.js";
|
|
106
|
+
import {
|
|
107
|
+
getProject
|
|
108
|
+
} from "./chunk-6AE3JPDJ.js";
|
|
105
109
|
import {
|
|
106
110
|
VERSION
|
|
107
|
-
} from "./chunk-
|
|
111
|
+
} from "./chunk-64TKWWNF.js";
|
|
108
112
|
import {
|
|
109
|
-
createSession,
|
|
110
|
-
deleteSession,
|
|
111
113
|
emitAssistantMessageStart,
|
|
112
114
|
emitContextState,
|
|
113
115
|
emitCriteriaSet,
|
|
@@ -119,26 +121,31 @@ import {
|
|
|
119
121
|
emitSessionInitialized,
|
|
120
122
|
emitUserMessage,
|
|
121
123
|
getEventStore,
|
|
122
|
-
getSession,
|
|
123
|
-
getSessionCachedPrompt,
|
|
124
124
|
getSessionState,
|
|
125
125
|
initEventStore,
|
|
126
|
+
setRuntimeConfig
|
|
127
|
+
} from "./chunk-M7EFXRDS.js";
|
|
128
|
+
import {
|
|
129
|
+
createSession,
|
|
130
|
+
deleteSession,
|
|
131
|
+
getSession,
|
|
132
|
+
getSessionCachedPrompt,
|
|
126
133
|
listSessions,
|
|
127
134
|
listSessionsByProject,
|
|
128
|
-
|
|
135
|
+
updateSessionBranch,
|
|
129
136
|
updateSessionCachedPrompt,
|
|
130
137
|
updateSessionDangerLevel,
|
|
131
138
|
updateSessionMetadata,
|
|
132
139
|
updateSessionProvider,
|
|
133
140
|
updateSessionRunning,
|
|
134
141
|
updateSessionWorkdir
|
|
135
|
-
} from "./chunk-
|
|
142
|
+
} from "./chunk-AANVMTYU.js";
|
|
136
143
|
import {
|
|
137
144
|
initDatabase
|
|
138
|
-
} from "./chunk-
|
|
145
|
+
} from "./chunk-RGUUZD5B.js";
|
|
139
146
|
import {
|
|
140
147
|
createContextStateMessage
|
|
141
|
-
} from "./chunk-
|
|
148
|
+
} from "./chunk-SJTG56CH.js";
|
|
142
149
|
import {
|
|
143
150
|
createProviderManager,
|
|
144
151
|
detectModel,
|
|
@@ -163,7 +170,7 @@ import {
|
|
|
163
170
|
} from "./chunk-K44MW7JJ.js";
|
|
164
171
|
import {
|
|
165
172
|
createServerMessage
|
|
166
|
-
} from "./chunk-
|
|
173
|
+
} from "./chunk-X6BXZFO7.js";
|
|
167
174
|
import {
|
|
168
175
|
getGlobalConfigDir
|
|
169
176
|
} from "./chunk-CQGTEGKL.js";
|
|
@@ -173,7 +180,7 @@ import express from "express";
|
|
|
173
180
|
import cors from "cors";
|
|
174
181
|
import { createServer as createHttpServer } from "http";
|
|
175
182
|
import { fileURLToPath as fileURLToPath7 } from "url";
|
|
176
|
-
import { dirname as dirname8, resolve as
|
|
183
|
+
import { dirname as dirname8, resolve as resolve8, join as join10 } from "path";
|
|
177
184
|
import { readFile as readFile4 } from "fs/promises";
|
|
178
185
|
import { createServer as createViteServer } from "vite";
|
|
179
186
|
|
|
@@ -1771,8 +1778,8 @@ var LspServer = class {
|
|
|
1771
1778
|
windowsHide: true,
|
|
1772
1779
|
shell: needsShell
|
|
1773
1780
|
});
|
|
1774
|
-
await new Promise((
|
|
1775
|
-
this.process.once("spawn",
|
|
1781
|
+
await new Promise((resolve9, reject) => {
|
|
1782
|
+
this.process.once("spawn", resolve9);
|
|
1776
1783
|
this.process.once("error", reject);
|
|
1777
1784
|
});
|
|
1778
1785
|
if (!this.process.stdin || !this.process.stdout) {
|
|
@@ -1992,19 +1999,19 @@ var LspServer = class {
|
|
|
1992
1999
|
*/
|
|
1993
2000
|
async getDiagnosticsWithWait(path, timeoutMs = DIAGNOSTIC_WAIT_MS) {
|
|
1994
2001
|
if (this.openDocuments.has(path)) {
|
|
1995
|
-
return new Promise((
|
|
2002
|
+
return new Promise((resolve9) => {
|
|
1996
2003
|
const existing = this.diagnostics.get(path);
|
|
1997
2004
|
if (existing !== void 0) {
|
|
1998
2005
|
setTimeout(() => {
|
|
1999
|
-
|
|
2006
|
+
resolve9(this.diagnostics.get(path) ?? []);
|
|
2000
2007
|
}, 100);
|
|
2001
2008
|
return;
|
|
2002
2009
|
}
|
|
2003
2010
|
const timeout = setTimeout(() => {
|
|
2004
2011
|
this.pendingDiagnostics.delete(path);
|
|
2005
|
-
|
|
2012
|
+
resolve9(this.diagnostics.get(path) ?? []);
|
|
2006
2013
|
}, timeoutMs);
|
|
2007
|
-
this.pendingDiagnostics.set(path, { resolve:
|
|
2014
|
+
this.pendingDiagnostics.set(path, { resolve: resolve9, timeout });
|
|
2008
2015
|
});
|
|
2009
2016
|
}
|
|
2010
2017
|
return this.diagnostics.get(path) ?? [];
|
|
@@ -2623,6 +2630,35 @@ var SessionManager = class {
|
|
|
2623
2630
|
getCurrentModelContext() {
|
|
2624
2631
|
return this.providerManager.getCurrentModelContext();
|
|
2625
2632
|
}
|
|
2633
|
+
/**
|
|
2634
|
+
* Get the effective working directory for a session.
|
|
2635
|
+
* Uses workspace path when active, otherwise the project workdir.
|
|
2636
|
+
*/
|
|
2637
|
+
getEffectiveWorkdir(sessionId) {
|
|
2638
|
+
const session = this.requireSession(sessionId);
|
|
2639
|
+
return session.workspace ?? session.workdir;
|
|
2640
|
+
}
|
|
2641
|
+
/**
|
|
2642
|
+
* Return (effectiveWorkdir, actualBranch) for a session.
|
|
2643
|
+
*/
|
|
2644
|
+
async getActualBranchPair(sessionId) {
|
|
2645
|
+
const effectiveWorkdir = this.getEffectiveWorkdir(sessionId);
|
|
2646
|
+
const branch = await getGitBranch(effectiveWorkdir);
|
|
2647
|
+
return { workdir: effectiveWorkdir, branch };
|
|
2648
|
+
}
|
|
2649
|
+
/**
|
|
2650
|
+
* Check that the session's persisted branch matches the actual branch on disk.
|
|
2651
|
+
* If they differ (and persisted branch exists), returns a warning message.
|
|
2652
|
+
*/
|
|
2653
|
+
async checkBranchConsistency(sessionId) {
|
|
2654
|
+
const session = this.getSession(sessionId);
|
|
2655
|
+
if (!session?.branch) return null;
|
|
2656
|
+
const actualBranch = await getGitBranch(session.workspace ?? session.workdir);
|
|
2657
|
+
if (actualBranch && actualBranch !== session.branch) {
|
|
2658
|
+
return `Branch mismatch: session expects "${session.branch}" but workspace is on "${actualBranch}". The workspace branch was changed externally.`;
|
|
2659
|
+
}
|
|
2660
|
+
return null;
|
|
2661
|
+
}
|
|
2626
2662
|
// ============================================================================
|
|
2627
2663
|
// Session Lifecycle
|
|
2628
2664
|
// ============================================================================
|
|
@@ -2646,6 +2682,20 @@ var SessionManager = class {
|
|
|
2646
2682
|
const contextWindowId = crypto.randomUUID();
|
|
2647
2683
|
emitSessionInitialized(dbSession.id, projectId, effectiveWorkdir, contextWindowId, sessionTitle);
|
|
2648
2684
|
const session = this.buildSessionFromDb(dbSession);
|
|
2685
|
+
getGitBranch(effectiveWorkdir).then((branch) => {
|
|
2686
|
+
if (branch) {
|
|
2687
|
+
updateSessionBranch(session.id, branch);
|
|
2688
|
+
const updatedDb = getSession(session.id);
|
|
2689
|
+
if (updatedDb) {
|
|
2690
|
+
this.emit({ type: "session_updated", session: this.buildSessionFromDb(updatedDb) });
|
|
2691
|
+
}
|
|
2692
|
+
}
|
|
2693
|
+
}).catch((err) => {
|
|
2694
|
+
logger.error("Failed to persist initial branch for session", {
|
|
2695
|
+
sessionId: session.id,
|
|
2696
|
+
error: err instanceof Error ? err.message : String(err)
|
|
2697
|
+
});
|
|
2698
|
+
});
|
|
2649
2699
|
this.emit({ type: "session_created", session });
|
|
2650
2700
|
return session;
|
|
2651
2701
|
}
|
|
@@ -2739,6 +2789,16 @@ var SessionManager = class {
|
|
|
2739
2789
|
this.emit({ type: "session_updated", session: updatedSession });
|
|
2740
2790
|
return updatedSession;
|
|
2741
2791
|
}
|
|
2792
|
+
/**
|
|
2793
|
+
* Emit session_updated for the given session.
|
|
2794
|
+
* Used by REST handlers after updating sibling session branches.
|
|
2795
|
+
*/
|
|
2796
|
+
emitBranchChange(sessionId) {
|
|
2797
|
+
const session = this.getSession(sessionId);
|
|
2798
|
+
if (session) {
|
|
2799
|
+
this.emit({ type: "session_updated", session });
|
|
2800
|
+
}
|
|
2801
|
+
}
|
|
2742
2802
|
/**
|
|
2743
2803
|
* Change session phase. Emits phase.changed event.
|
|
2744
2804
|
*/
|
|
@@ -3249,6 +3309,24 @@ var SessionManager = class {
|
|
|
3249
3309
|
const effectiveWorkdir = session.workspace ?? session.workdir;
|
|
3250
3310
|
return getLspManager(sessionId, effectiveWorkdir);
|
|
3251
3311
|
}
|
|
3312
|
+
async applyBranchIfNeeded(projectDir, projectName, workspaceName, branch, sourceBranch) {
|
|
3313
|
+
const wsPath = resolve2(await getWorkspacesDir(projectName, projectDir), workspaceName);
|
|
3314
|
+
const currentBranch = await getGitBranch(wsPath);
|
|
3315
|
+
if (currentBranch !== branch) {
|
|
3316
|
+
try {
|
|
3317
|
+
await validateRef(wsPath, branch);
|
|
3318
|
+
await runGit(wsPath, ["checkout", branch]).catch(async () => {
|
|
3319
|
+
const sb = sourceBranch ?? await getDefaultBranch(projectDir);
|
|
3320
|
+
const validated = sourceBranch ? await resolveAndValidateSourceBranch(wsPath, sourceBranch, projectDir) : sb;
|
|
3321
|
+
await runGit(wsPath, ["checkout", "-b", branch, validated]);
|
|
3322
|
+
});
|
|
3323
|
+
} catch (err) {
|
|
3324
|
+
throw new Error(
|
|
3325
|
+
`Failed to apply branch "${branch}" to workspace "${workspaceName}": ${err instanceof Error ? err.message : String(err)}`
|
|
3326
|
+
);
|
|
3327
|
+
}
|
|
3328
|
+
}
|
|
3329
|
+
}
|
|
3252
3330
|
// ============================================================================
|
|
3253
3331
|
// Workspace Lifecycle
|
|
3254
3332
|
// ============================================================================
|
|
@@ -3258,44 +3336,55 @@ var SessionManager = class {
|
|
|
3258
3336
|
* Emits a single type of event — switching is always "opening" a workspace.
|
|
3259
3337
|
* Switches are serialized per-session to prevent race conditions.
|
|
3260
3338
|
*/
|
|
3261
|
-
async switchWorkspace(sessionId, target, branch) {
|
|
3262
|
-
if (target !== "original")
|
|
3263
|
-
validateWorkspaceName(target);
|
|
3264
|
-
}
|
|
3339
|
+
async switchWorkspace(sessionId, target, branch, sourceBranch) {
|
|
3340
|
+
if (target !== "original") validateWorkspaceName(target);
|
|
3265
3341
|
const existingLock = this.switchLocks.get(sessionId);
|
|
3266
|
-
if (existingLock)
|
|
3267
|
-
await existingLock;
|
|
3268
|
-
}
|
|
3342
|
+
if (existingLock) await existingLock;
|
|
3269
3343
|
const lockPromise = (async () => {
|
|
3270
3344
|
const session = this.requireSession(sessionId);
|
|
3271
3345
|
const project = getProject(session.projectId);
|
|
3272
3346
|
if (!project) throw new Error(`Project not found: ${session.projectId}`);
|
|
3273
|
-
|
|
3274
|
-
if (target
|
|
3347
|
+
const isBranchChangeOnly = target !== "original" && session.workspace?.split("/").pop() === target && branch !== void 0;
|
|
3348
|
+
if (target === "original" && !session.workspace && !branch) return session;
|
|
3349
|
+
if (target !== "original" && session.workspace?.split("/").pop() === target && !branch) return session;
|
|
3350
|
+
if (isBranchChangeOnly) {
|
|
3351
|
+
const currentBranch = await getGitBranch(session.workspace ?? session.workdir);
|
|
3352
|
+
if (currentBranch === branch) return session;
|
|
3353
|
+
}
|
|
3275
3354
|
const previousPath = session.workspace;
|
|
3276
|
-
if (previousPath) {
|
|
3277
|
-
|
|
3278
|
-
|
|
3279
|
-
|
|
3280
|
-
|
|
3281
|
-
|
|
3282
|
-
|
|
3283
|
-
|
|
3355
|
+
if (previousPath && !isBranchChangeOnly) {
|
|
3356
|
+
const otherSessionsUsingPath = this.listSessions().filter(
|
|
3357
|
+
(s) => s.id !== sessionId && s.workspace === previousPath
|
|
3358
|
+
);
|
|
3359
|
+
if (otherSessionsUsingPath.length === 0) {
|
|
3360
|
+
try {
|
|
3361
|
+
await devServerManager.stop(previousPath);
|
|
3362
|
+
} catch (err) {
|
|
3363
|
+
logger.error("Error stopping dev server for workspace switch", {
|
|
3364
|
+
sessionId,
|
|
3365
|
+
workspace: previousPath,
|
|
3366
|
+
error: err
|
|
3367
|
+
});
|
|
3368
|
+
}
|
|
3369
|
+
} else {
|
|
3370
|
+
logger.info("Skipping dev server stop \u2014 other sessions still use path", {
|
|
3371
|
+
path: previousPath,
|
|
3372
|
+
otherSessions: otherSessionsUsingPath.map((s) => s.id)
|
|
3284
3373
|
});
|
|
3285
3374
|
}
|
|
3286
3375
|
}
|
|
3287
3376
|
if (target === "original") {
|
|
3288
3377
|
updateSessionWorkdir(sessionId, project.workdir, null);
|
|
3289
|
-
} else {
|
|
3378
|
+
} else if (!isBranchChangeOnly) {
|
|
3290
3379
|
const createLockKey = `${project.name}:${target}`;
|
|
3291
3380
|
const existingCreateLock = this.workspaceCreationLocks.get(createLockKey);
|
|
3292
|
-
if (existingCreateLock)
|
|
3293
|
-
await existingCreateLock;
|
|
3294
|
-
}
|
|
3381
|
+
if (existingCreateLock) await existingCreateLock;
|
|
3295
3382
|
const createLockPromise = (async () => {
|
|
3296
|
-
const exists = await workspaceExists(project.name, target);
|
|
3383
|
+
const exists = await workspaceExists(project.name, target, project.workdir);
|
|
3297
3384
|
if (!exists) {
|
|
3298
|
-
await ensureWorkspace(project.workdir, target, project.name, branch);
|
|
3385
|
+
await ensureWorkspace(project.workdir, target, project.name, branch, sourceBranch);
|
|
3386
|
+
} else if (branch) {
|
|
3387
|
+
await this.applyBranchIfNeeded(project.workdir, project.name, target, branch, sourceBranch);
|
|
3299
3388
|
}
|
|
3300
3389
|
})();
|
|
3301
3390
|
this.workspaceCreationLocks.set(createLockKey, createLockPromise);
|
|
@@ -3304,16 +3393,31 @@ var SessionManager = class {
|
|
|
3304
3393
|
} finally {
|
|
3305
3394
|
this.workspaceCreationLocks.delete(createLockKey);
|
|
3306
3395
|
}
|
|
3307
|
-
const
|
|
3396
|
+
const wsDir = await getWorkspacesDir(project.name, project.workdir);
|
|
3397
|
+
const wsPath = resolve2(wsDir, target);
|
|
3308
3398
|
updateSessionWorkdir(sessionId, project.workdir, wsPath);
|
|
3399
|
+
} else {
|
|
3400
|
+
await this.applyBranchIfNeeded(project.workdir, project.name, target, branch, sourceBranch);
|
|
3309
3401
|
}
|
|
3310
3402
|
try {
|
|
3311
3403
|
await shutdownLspManager(sessionId);
|
|
3312
3404
|
} catch (err) {
|
|
3313
3405
|
logger.error("Error shutting down LSP for workspace switch", { sessionId, error: err });
|
|
3314
3406
|
}
|
|
3315
|
-
const
|
|
3407
|
+
const wsDirForBranch = await getWorkspacesDir(project.name, project.workdir);
|
|
3408
|
+
const effectiveWorkdir = target === "original" ? project.workdir : resolve2(wsDirForBranch, target);
|
|
3316
3409
|
const actualBranch = await getGitBranch(effectiveWorkdir);
|
|
3410
|
+
if (actualBranch) {
|
|
3411
|
+
updateSessionBranch(sessionId, actualBranch);
|
|
3412
|
+
const otherSessionsOnWorkspace = this.listSessions().filter(
|
|
3413
|
+
(s) => s.id !== sessionId && s.workspace === effectiveWorkdir
|
|
3414
|
+
);
|
|
3415
|
+
for (const other of otherSessionsOnWorkspace) {
|
|
3416
|
+
updateSessionBranch(other.id, actualBranch);
|
|
3417
|
+
const updated2 = this.getSession(other.id);
|
|
3418
|
+
if (updated2) this.emit({ type: "session_updated", session: updated2 });
|
|
3419
|
+
}
|
|
3420
|
+
}
|
|
3317
3421
|
let stalenessHint = "";
|
|
3318
3422
|
if (target !== "original" && actualBranch) {
|
|
3319
3423
|
await runGit(effectiveWorkdir, ["fetch", "origin", "--no-tags", "--quiet"]).catch(() => {
|
|
@@ -3350,9 +3454,7 @@ All file and git operations should use this directory.
|
|
|
3350
3454
|
})();
|
|
3351
3455
|
this.switchLocks.set(sessionId, lockPromise);
|
|
3352
3456
|
lockPromise.finally(() => {
|
|
3353
|
-
if (this.switchLocks.get(sessionId) === lockPromise)
|
|
3354
|
-
this.switchLocks.delete(sessionId);
|
|
3355
|
-
}
|
|
3457
|
+
if (this.switchLocks.get(sessionId) === lockPromise) this.switchLocks.delete(sessionId);
|
|
3356
3458
|
});
|
|
3357
3459
|
return lockPromise;
|
|
3358
3460
|
}
|
|
@@ -3380,12 +3482,13 @@ All file and git operations should use this directory.
|
|
|
3380
3482
|
if (currentWsName === target) {
|
|
3381
3483
|
await this.switchWorkspace(sessionId, "original");
|
|
3382
3484
|
}
|
|
3383
|
-
const
|
|
3485
|
+
const wsDir = await getWorkspacesDir(project.name, project.workdir);
|
|
3486
|
+
const effectivePath = resolve2(wsDir, target);
|
|
3384
3487
|
try {
|
|
3385
3488
|
await devServerManager.stop(effectivePath);
|
|
3386
3489
|
} catch {
|
|
3387
3490
|
}
|
|
3388
|
-
await deleteWorkspace(project.name, target);
|
|
3491
|
+
await deleteWorkspace(project.name, target, project.workdir);
|
|
3389
3492
|
const updated = this.requireSession(sessionId);
|
|
3390
3493
|
this.emit({ type: "session_updated", session: updated });
|
|
3391
3494
|
return updated;
|
|
@@ -4154,6 +4257,85 @@ function createDevServerRoutes() {
|
|
|
4154
4257
|
|
|
4155
4258
|
// src/server/routes/workspace-config.ts
|
|
4156
4259
|
import { Router as Router5 } from "express";
|
|
4260
|
+
import { stat as stat2, mkdir as mkdir4, readdir, access as access3 } from "fs/promises";
|
|
4261
|
+
import { constants as constants3 } from "fs";
|
|
4262
|
+
import { resolve as resolve4, isAbsolute as isAbsolute3, join as join6 } from "path";
|
|
4263
|
+
|
|
4264
|
+
// src/shared/workspace.ts
|
|
4265
|
+
var BLOCKED_EXACT_PATHS = [
|
|
4266
|
+
"/",
|
|
4267
|
+
"/etc",
|
|
4268
|
+
"/bin",
|
|
4269
|
+
"/sbin",
|
|
4270
|
+
"/lib",
|
|
4271
|
+
"/lib64",
|
|
4272
|
+
"/usr",
|
|
4273
|
+
"/var",
|
|
4274
|
+
"/opt",
|
|
4275
|
+
"/root",
|
|
4276
|
+
"/run",
|
|
4277
|
+
"/tmp",
|
|
4278
|
+
"/home",
|
|
4279
|
+
"/mnt",
|
|
4280
|
+
"/media"
|
|
4281
|
+
];
|
|
4282
|
+
var BLOCKED_VIRTUAL_FS_PREFIXES = ["/proc/", "/sys/", "/dev/", "/boot/", "/etc/", "/lost+found/"];
|
|
4283
|
+
function getRootDirBlockReason(path, exactPaths = BLOCKED_EXACT_PATHS, virtualFsPrefixes = BLOCKED_VIRTUAL_FS_PREFIXES) {
|
|
4284
|
+
if (!path) return null;
|
|
4285
|
+
const normalized = path.replace(/\/+$/, "") || "/";
|
|
4286
|
+
if (exactPaths.includes(normalized)) return "exact";
|
|
4287
|
+
for (const prefix of virtualFsPrefixes) {
|
|
4288
|
+
if (normalized.startsWith(prefix)) return "virtual_fs";
|
|
4289
|
+
}
|
|
4290
|
+
return null;
|
|
4291
|
+
}
|
|
4292
|
+
|
|
4293
|
+
// src/server/routes/workspace-config.ts
|
|
4294
|
+
async function isWritable(path) {
|
|
4295
|
+
try {
|
|
4296
|
+
await access3(path, constants3.W_OK);
|
|
4297
|
+
return true;
|
|
4298
|
+
} catch {
|
|
4299
|
+
return false;
|
|
4300
|
+
}
|
|
4301
|
+
}
|
|
4302
|
+
function resolveRootDir(rootDir, workdir) {
|
|
4303
|
+
return isAbsolute3(rootDir) ? rootDir : resolve4(workdir, rootDir);
|
|
4304
|
+
}
|
|
4305
|
+
async function checkDirExists(path) {
|
|
4306
|
+
try {
|
|
4307
|
+
const st = await stat2(path);
|
|
4308
|
+
return st.isDirectory();
|
|
4309
|
+
} catch {
|
|
4310
|
+
return false;
|
|
4311
|
+
}
|
|
4312
|
+
}
|
|
4313
|
+
async function validatePathWritable(path) {
|
|
4314
|
+
if (await isWritable(path)) return null;
|
|
4315
|
+
return "Workspace root directory exists but is not writable";
|
|
4316
|
+
}
|
|
4317
|
+
async function findOrphanedWorkspaces(dir) {
|
|
4318
|
+
const results = [];
|
|
4319
|
+
try {
|
|
4320
|
+
const entries = await readdir(dir, { withFileTypes: true });
|
|
4321
|
+
for (const entry of entries) {
|
|
4322
|
+
if (entry.isDirectory()) {
|
|
4323
|
+
try {
|
|
4324
|
+
const gitStat = await stat2(join6(dir, entry.name, ".git"));
|
|
4325
|
+
if (gitStat.isDirectory()) {
|
|
4326
|
+
results.push({ name: entry.name });
|
|
4327
|
+
}
|
|
4328
|
+
} catch {
|
|
4329
|
+
}
|
|
4330
|
+
}
|
|
4331
|
+
}
|
|
4332
|
+
} catch (err) {
|
|
4333
|
+
if (err?.code !== "ENOENT") {
|
|
4334
|
+
logger.error("Error scanning for orphaned workspaces", { dir, error: String(err) });
|
|
4335
|
+
}
|
|
4336
|
+
}
|
|
4337
|
+
return results;
|
|
4338
|
+
}
|
|
4157
4339
|
function createWorkspaceConfigRoutes() {
|
|
4158
4340
|
const router = Router5();
|
|
4159
4341
|
router.get("/config", async (req, res) => {
|
|
@@ -4165,18 +4347,93 @@ function createWorkspaceConfigRoutes() {
|
|
|
4165
4347
|
router.post("/config", async (req, res) => {
|
|
4166
4348
|
const workdir = req.query["workdir"];
|
|
4167
4349
|
if (!workdir) return res.status(400).json({ error: "workdir required" });
|
|
4168
|
-
const { setup } = req.body;
|
|
4169
|
-
if (!Array.isArray(setup)) {
|
|
4350
|
+
const { setup, rootDir } = req.body;
|
|
4351
|
+
if (!Array.isArray(setup) && typeof rootDir !== "string") {
|
|
4352
|
+
return res.status(400).json({ error: "At least one of setup or rootDir must be provided" });
|
|
4353
|
+
}
|
|
4354
|
+
if (setup !== void 0 && !Array.isArray(setup)) {
|
|
4170
4355
|
return res.status(400).json({ error: "setup must be an array of strings" });
|
|
4171
4356
|
}
|
|
4357
|
+
const config3 = {};
|
|
4358
|
+
if (Array.isArray(setup)) {
|
|
4359
|
+
config3.setup = setup;
|
|
4360
|
+
}
|
|
4361
|
+
if (typeof rootDir === "string") {
|
|
4362
|
+
const trimmed = rootDir.trim();
|
|
4363
|
+
if (trimmed) {
|
|
4364
|
+
const resolvedPath = resolveRootDir(trimmed, workdir);
|
|
4365
|
+
const displayPath = resolvedPath.replace(/\/+$/, "") || "/";
|
|
4366
|
+
const blockReason = getRootDirBlockReason(resolvedPath);
|
|
4367
|
+
if (blockReason === "exact") {
|
|
4368
|
+
return res.status(400).json({ error: `Cannot use "${displayPath}" directly as workspace root. Use a subdirectory instead.` });
|
|
4369
|
+
}
|
|
4370
|
+
if (blockReason === "virtual_fs") {
|
|
4371
|
+
return res.status(400).json({ error: `Cannot use paths under "${displayPath}" for workspaces.` });
|
|
4372
|
+
}
|
|
4373
|
+
const dirExists = await checkDirExists(resolvedPath);
|
|
4374
|
+
if (dirExists) {
|
|
4375
|
+
const writableErr = await validatePathWritable(resolvedPath);
|
|
4376
|
+
if (writableErr) return res.status(400).json({ error: writableErr });
|
|
4377
|
+
}
|
|
4378
|
+
config3.rootDir = trimmed;
|
|
4379
|
+
}
|
|
4380
|
+
}
|
|
4172
4381
|
try {
|
|
4173
|
-
const config3 = { setup };
|
|
4174
4382
|
await saveWorkspaceConfig(workdir, config3);
|
|
4175
4383
|
res.json({ config: config3 });
|
|
4176
4384
|
} catch (err) {
|
|
4177
4385
|
res.status(500).json({ error: err instanceof Error ? err.message : "Failed to save config" });
|
|
4178
4386
|
}
|
|
4179
4387
|
});
|
|
4388
|
+
router.post("/config/validate", async (req, res) => {
|
|
4389
|
+
const { rootDir, workdir, projectName, createIfMissing } = req.body;
|
|
4390
|
+
if (!rootDir || typeof rootDir !== "string") {
|
|
4391
|
+
return res.status(400).json({ error: "rootDir is required" });
|
|
4392
|
+
}
|
|
4393
|
+
if (!workdir || typeof workdir !== "string") {
|
|
4394
|
+
return res.status(400).json({ error: "workdir is required" });
|
|
4395
|
+
}
|
|
4396
|
+
const resolvedPath = resolveRootDir(rootDir, workdir);
|
|
4397
|
+
const displayPath = resolvedPath.replace(/\/+$/, "") || "/";
|
|
4398
|
+
const blockReason = getRootDirBlockReason(resolvedPath);
|
|
4399
|
+
if (blockReason === "exact") {
|
|
4400
|
+
const suggestion = typeof projectName === "string" ? `${displayPath}/${projectName}` : void 0;
|
|
4401
|
+
return res.status(400).json({
|
|
4402
|
+
error: suggestion ? `Cannot use "${displayPath}" directly as workspace root. Use a subdirectory like "${suggestion}" instead.` : `Cannot use "${displayPath}" directly as workspace root. Use a subdirectory instead.`
|
|
4403
|
+
});
|
|
4404
|
+
}
|
|
4405
|
+
if (blockReason === "virtual_fs") {
|
|
4406
|
+
return res.status(400).json({ error: `Cannot use paths under "${displayPath}" for workspaces.` });
|
|
4407
|
+
}
|
|
4408
|
+
let dirExists = await checkDirExists(resolvedPath);
|
|
4409
|
+
if (dirExists) {
|
|
4410
|
+
const writableErr = await validatePathWritable(resolvedPath);
|
|
4411
|
+
if (writableErr) return res.status(400).json({ error: writableErr });
|
|
4412
|
+
}
|
|
4413
|
+
let created = false;
|
|
4414
|
+
if (!dirExists && createIfMissing) {
|
|
4415
|
+
await mkdir4(resolvedPath, { recursive: true });
|
|
4416
|
+
dirExists = true;
|
|
4417
|
+
created = true;
|
|
4418
|
+
}
|
|
4419
|
+
const workspaces = [];
|
|
4420
|
+
try {
|
|
4421
|
+
const currentConfig = await loadWorkspaceConfig(workdir);
|
|
4422
|
+
const previousRootDir = currentConfig?.rootDir ? resolveRootDir(currentConfig.rootDir, workdir) : null;
|
|
4423
|
+
if (previousRootDir && previousRootDir !== resolvedPath) {
|
|
4424
|
+
const orphans = await findOrphanedWorkspaces(previousRootDir);
|
|
4425
|
+
workspaces.push(...orphans);
|
|
4426
|
+
} else if (!previousRootDir && projectName && typeof projectName === "string") {
|
|
4427
|
+
const defaultDir = join6(getGlobalDataDir(), "workspaces", projectName);
|
|
4428
|
+
if (defaultDir !== resolvedPath) {
|
|
4429
|
+
const orphans = await findOrphanedWorkspaces(defaultDir);
|
|
4430
|
+
workspaces.push(...orphans);
|
|
4431
|
+
}
|
|
4432
|
+
}
|
|
4433
|
+
} catch {
|
|
4434
|
+
}
|
|
4435
|
+
res.json({ exists: dirExists, resolvedPath, created, workspaces });
|
|
4436
|
+
});
|
|
4180
4437
|
return router;
|
|
4181
4438
|
}
|
|
4182
4439
|
|
|
@@ -4223,19 +4480,19 @@ function createTerminalRoutes() {
|
|
|
4223
4480
|
|
|
4224
4481
|
// src/server/routes/directories.ts
|
|
4225
4482
|
import { Router as Router7 } from "express";
|
|
4226
|
-
import { readdir } from "fs/promises";
|
|
4227
|
-
import { resolve as
|
|
4483
|
+
import { readdir as readdir2 } from "fs/promises";
|
|
4484
|
+
import { resolve as resolve5, join as join7, dirname as dirname5, basename as basename2 } from "path";
|
|
4228
4485
|
function createDirectoryRoutes() {
|
|
4229
4486
|
const router = Router7();
|
|
4230
4487
|
const DEFAULT_BASE_PATH = process.cwd();
|
|
4231
4488
|
router.get("/", async (req, res) => {
|
|
4232
4489
|
const path = req.query["path"] || DEFAULT_BASE_PATH;
|
|
4233
4490
|
try {
|
|
4234
|
-
const resolvedPath =
|
|
4235
|
-
const entries = await
|
|
4491
|
+
const resolvedPath = resolve5(path);
|
|
4492
|
+
const entries = await readdir2(resolvedPath, { withFileTypes: true });
|
|
4236
4493
|
const directories = entries.filter((entry) => entry.isDirectory()).map((entry) => ({
|
|
4237
4494
|
name: entry.name,
|
|
4238
|
-
path:
|
|
4495
|
+
path: join7(resolvedPath, entry.name)
|
|
4239
4496
|
})).sort((a, b) => a.name.localeCompare(b.name));
|
|
4240
4497
|
const parent = dirname5(resolvedPath);
|
|
4241
4498
|
const hasParent = parent !== resolvedPath;
|
|
@@ -4389,7 +4646,7 @@ function isDevMode() {
|
|
|
4389
4646
|
return process.env["NODE_ENV"] === "development";
|
|
4390
4647
|
}
|
|
4391
4648
|
async function getRemote() {
|
|
4392
|
-
return new Promise((
|
|
4649
|
+
return new Promise((resolve9) => {
|
|
4393
4650
|
const child = spawn2("git", ["remote"], {
|
|
4394
4651
|
stdio: ["ignore", "pipe", "pipe"],
|
|
4395
4652
|
windowsHide: true
|
|
@@ -4400,23 +4657,23 @@ async function getRemote() {
|
|
|
4400
4657
|
});
|
|
4401
4658
|
child.on("close", () => {
|
|
4402
4659
|
const remotes = output.trim().split("\n").map((r) => r.trim()).filter(Boolean);
|
|
4403
|
-
if (remotes.includes("upstream"))
|
|
4404
|
-
else if (remotes.includes("origin"))
|
|
4405
|
-
else
|
|
4660
|
+
if (remotes.includes("upstream")) resolve9("upstream");
|
|
4661
|
+
else if (remotes.includes("origin")) resolve9("origin");
|
|
4662
|
+
else resolve9("origin");
|
|
4406
4663
|
});
|
|
4407
|
-
child.on("error", () =>
|
|
4664
|
+
child.on("error", () => resolve9("origin"));
|
|
4408
4665
|
});
|
|
4409
4666
|
}
|
|
4410
4667
|
async function getLatestDevelopVersion() {
|
|
4411
4668
|
const remote = await getRemote();
|
|
4412
|
-
return new Promise((
|
|
4669
|
+
return new Promise((resolve9) => {
|
|
4413
4670
|
const fetchChild = spawn2("git", ["fetch", remote, "develop", "--no-tags"], {
|
|
4414
4671
|
stdio: ["ignore", "pipe", "pipe"],
|
|
4415
4672
|
windowsHide: true
|
|
4416
4673
|
});
|
|
4417
4674
|
fetchChild.on("error", (err) => {
|
|
4418
4675
|
logger.error("[auto-update] Git fetch error", { error: err.message });
|
|
4419
|
-
|
|
4676
|
+
resolve9("unknown");
|
|
4420
4677
|
});
|
|
4421
4678
|
fetchChild.on("close", (code) => {
|
|
4422
4679
|
if (code !== 0) {
|
|
@@ -4432,7 +4689,7 @@ async function getLatestDevelopVersion() {
|
|
|
4432
4689
|
});
|
|
4433
4690
|
tagChild.on("close", (tagCode) => {
|
|
4434
4691
|
if (tagCode === 0 && tagOutput.trim()) {
|
|
4435
|
-
|
|
4692
|
+
resolve9(tagOutput.trim());
|
|
4436
4693
|
} else {
|
|
4437
4694
|
const fallbackChild = spawn2("git", ["describe", "--tags", "--abbrev=0", "develop"], {
|
|
4438
4695
|
stdio: ["ignore", "pipe", "pipe"],
|
|
@@ -4444,18 +4701,18 @@ async function getLatestDevelopVersion() {
|
|
|
4444
4701
|
});
|
|
4445
4702
|
fallbackChild.on("close", (fallbackCode) => {
|
|
4446
4703
|
if (fallbackCode === 0 && fallbackOutput.trim()) {
|
|
4447
|
-
|
|
4704
|
+
resolve9(fallbackOutput.trim());
|
|
4448
4705
|
} else {
|
|
4449
4706
|
logger.warn("[auto-update] No tags found on develop branch");
|
|
4450
|
-
|
|
4707
|
+
resolve9("unknown");
|
|
4451
4708
|
}
|
|
4452
4709
|
});
|
|
4453
|
-
fallbackChild.on("error", () =>
|
|
4710
|
+
fallbackChild.on("error", () => resolve9("unknown"));
|
|
4454
4711
|
}
|
|
4455
4712
|
});
|
|
4456
4713
|
tagChild.on("error", (err) => {
|
|
4457
4714
|
logger.error("[auto-update] Git tag error", { error: err.message });
|
|
4458
|
-
|
|
4715
|
+
resolve9("unknown");
|
|
4459
4716
|
});
|
|
4460
4717
|
});
|
|
4461
4718
|
});
|
|
@@ -4472,7 +4729,7 @@ function createAutoUpdateRoutes(options = {}) {
|
|
|
4472
4729
|
return;
|
|
4473
4730
|
}
|
|
4474
4731
|
try {
|
|
4475
|
-
const latest = isDev ? await getLatestDevelopVersion() : await new Promise((
|
|
4732
|
+
const latest = isDev ? await getLatestDevelopVersion() : await new Promise((resolve9, reject) => {
|
|
4476
4733
|
const win = process.platform === "win32";
|
|
4477
4734
|
const child = spawn2(win ? "npm view openfox version" : "npm", win ? [] : ["view", "openfox", "version"], {
|
|
4478
4735
|
stdio: ["ignore", "pipe", "pipe"],
|
|
@@ -4485,7 +4742,7 @@ function createAutoUpdateRoutes(options = {}) {
|
|
|
4485
4742
|
});
|
|
4486
4743
|
child.on("close", (code) => {
|
|
4487
4744
|
if (code === 0) {
|
|
4488
|
-
|
|
4745
|
+
resolve9(stdout.trim());
|
|
4489
4746
|
} else {
|
|
4490
4747
|
reject(new Error(`npm view exited with code ${code}`));
|
|
4491
4748
|
}
|
|
@@ -4500,12 +4757,13 @@ function createAutoUpdateRoutes(options = {}) {
|
|
|
4500
4757
|
const latestVersion = latest.replace(/^v/, "");
|
|
4501
4758
|
const isUpdateAvailable = currentVersion !== latestVersion;
|
|
4502
4759
|
const now = Date.now();
|
|
4503
|
-
versionCache.data = { current, latest, isUpdateAvailable, isService };
|
|
4760
|
+
versionCache.data = { current, latest: latestVersion, isUpdateAvailable, isService };
|
|
4504
4761
|
versionCache.timestamp = now;
|
|
4505
|
-
res.json({ current, latest, isUpdateAvailable, isService });
|
|
4762
|
+
res.json({ current, latest: latestVersion, isUpdateAvailable, isService });
|
|
4506
4763
|
} catch (err) {
|
|
4507
4764
|
logger.error("[auto-update] Error in version check", { error: err instanceof Error ? err.message : String(err) });
|
|
4508
|
-
|
|
4765
|
+
const currentVersion = isDev ? current.replace(/-dev$/, "") : current;
|
|
4766
|
+
res.json({ current, latest: currentVersion, isUpdateAvailable: false, isService });
|
|
4509
4767
|
}
|
|
4510
4768
|
});
|
|
4511
4769
|
router.post("/", async (req, res) => {
|
|
@@ -4536,9 +4794,9 @@ function createAutoUpdateRoutes(options = {}) {
|
|
|
4536
4794
|
const timeout = setTimeout(() => {
|
|
4537
4795
|
child.kill();
|
|
4538
4796
|
}, UPDATE_TIMEOUT);
|
|
4539
|
-
const exitCode = await new Promise((
|
|
4540
|
-
child.on("close",
|
|
4541
|
-
child.on("error", () =>
|
|
4797
|
+
const exitCode = await new Promise((resolve9) => {
|
|
4798
|
+
child.on("close", resolve9);
|
|
4799
|
+
child.on("error", () => resolve9(1));
|
|
4542
4800
|
});
|
|
4543
4801
|
clearTimeout(timeout);
|
|
4544
4802
|
if (exitCode === 0) {
|
|
@@ -4710,26 +4968,26 @@ var ProviderRegistry = class {
|
|
|
4710
4968
|
};
|
|
4711
4969
|
|
|
4712
4970
|
// src/server/providers/plugins/loader.ts
|
|
4713
|
-
import { readdir as
|
|
4714
|
-
import { dirname as dirname6, join as
|
|
4971
|
+
import { readdir as readdir3, readFile as readFile2, stat as stat3 } from "fs/promises";
|
|
4972
|
+
import { dirname as dirname6, join as join8, resolve as resolve6 } from "path";
|
|
4715
4973
|
import { fileURLToPath as fileURLToPath5, pathToFileURL } from "url";
|
|
4716
4974
|
async function packageDirectories(root) {
|
|
4717
4975
|
let entries;
|
|
4718
4976
|
try {
|
|
4719
|
-
entries = await
|
|
4977
|
+
entries = await readdir3(root, { withFileTypes: true });
|
|
4720
4978
|
} catch {
|
|
4721
4979
|
return [];
|
|
4722
4980
|
}
|
|
4723
4981
|
const directories = [];
|
|
4724
4982
|
for (const entry of entries) {
|
|
4725
|
-
const entryPath =
|
|
4726
|
-
const isDirectory = entry.isDirectory() || entry.isSymbolicLink() && (await
|
|
4983
|
+
const entryPath = join8(root, entry.name);
|
|
4984
|
+
const isDirectory = entry.isDirectory() || entry.isSymbolicLink() && (await stat3(entryPath).catch(() => void 0))?.isDirectory();
|
|
4727
4985
|
if (!isDirectory) continue;
|
|
4728
4986
|
if (entry.name.startsWith("@")) {
|
|
4729
|
-
const scoped = await
|
|
4987
|
+
const scoped = await readdir3(entryPath, { withFileTypes: true }).catch(() => []);
|
|
4730
4988
|
for (const child of scoped) {
|
|
4731
|
-
const childPath =
|
|
4732
|
-
const childIsDirectory = child.isDirectory() || child.isSymbolicLink() && (await
|
|
4989
|
+
const childPath = join8(entryPath, child.name);
|
|
4990
|
+
const childIsDirectory = child.isDirectory() || child.isSymbolicLink() && (await stat3(childPath).catch(() => void 0))?.isDirectory();
|
|
4733
4991
|
if (childIsDirectory) directories.push(childPath);
|
|
4734
4992
|
}
|
|
4735
4993
|
} else {
|
|
@@ -4741,16 +4999,16 @@ async function packageDirectories(root) {
|
|
|
4741
4999
|
async function loadProviderPlugins(options) {
|
|
4742
5000
|
const moduleDirectory = dirname6(fileURLToPath5(import.meta.url));
|
|
4743
5001
|
const roots = [
|
|
4744
|
-
|
|
4745
|
-
|
|
4746
|
-
|
|
4747
|
-
|
|
5002
|
+
join8(options.configDirectory, "plugins"),
|
|
5003
|
+
join8(options.cwd ?? process.cwd(), "node_modules"),
|
|
5004
|
+
resolve6(moduleDirectory, "node_modules"),
|
|
5005
|
+
resolve6(moduleDirectory, "../../../../node_modules")
|
|
4748
5006
|
];
|
|
4749
5007
|
const seen = /* @__PURE__ */ new Set();
|
|
4750
5008
|
const diagnostics = [];
|
|
4751
5009
|
for (const root of roots) {
|
|
4752
5010
|
for (const packageDir of await packageDirectories(root)) {
|
|
4753
|
-
const manifestPath =
|
|
5011
|
+
const manifestPath = join8(packageDir, "package.json");
|
|
4754
5012
|
let manifest;
|
|
4755
5013
|
try {
|
|
4756
5014
|
manifest = JSON.parse(await readFile2(manifestPath, "utf8"));
|
|
@@ -4791,7 +5049,7 @@ async function loadProviderPlugins(options) {
|
|
|
4791
5049
|
}
|
|
4792
5050
|
};
|
|
4793
5051
|
try {
|
|
4794
|
-
const module = await import(pathToFileURL(
|
|
5052
|
+
const module = await import(pathToFileURL(join8(packageDir, plugin)).href);
|
|
4795
5053
|
if (typeof module.register !== "function") throw new Error("Plugin does not export register(registry)");
|
|
4796
5054
|
await module.register(trackingRegistry);
|
|
4797
5055
|
diagnostic.loaded = true;
|
|
@@ -4806,9 +5064,9 @@ async function loadProviderPlugins(options) {
|
|
|
4806
5064
|
// src/server/routes/plugins.ts
|
|
4807
5065
|
import { Router as Router11 } from "express";
|
|
4808
5066
|
import { fileURLToPath as fileURLToPath6, pathToFileURL as pathToFileURL2 } from "url";
|
|
4809
|
-
import { dirname as dirname7, resolve as
|
|
5067
|
+
import { dirname as dirname7, resolve as resolve7, join as join9 } from "path";
|
|
4810
5068
|
import { existsSync } from "fs";
|
|
4811
|
-
import { readFile as readFile3, readdir as
|
|
5069
|
+
import { readFile as readFile3, readdir as readdir4, rm as rm2, rename as rename2 } from "fs/promises";
|
|
4812
5070
|
import { platform } from "os";
|
|
4813
5071
|
import { execFile } from "child_process";
|
|
4814
5072
|
import { promisify } from "util";
|
|
@@ -4834,9 +5092,9 @@ function createPluginRoutes(options) {
|
|
|
4834
5092
|
return res.json({ plugins: registryCache.data });
|
|
4835
5093
|
}
|
|
4836
5094
|
const moduleDir = dirname7(fileURLToPath6(import.meta.url));
|
|
4837
|
-
let registryPath =
|
|
5095
|
+
let registryPath = resolve7(moduleDir, "../plugins-registry.json");
|
|
4838
5096
|
if (!existsSync(registryPath)) {
|
|
4839
|
-
registryPath =
|
|
5097
|
+
registryPath = resolve7(moduleDir, "../../../plugins-registry.json");
|
|
4840
5098
|
}
|
|
4841
5099
|
const data = JSON.parse(await readFile3(registryPath, "utf8"));
|
|
4842
5100
|
registryCache = { data, ts: now };
|
|
@@ -4859,9 +5117,9 @@ function createPluginRoutes(options) {
|
|
|
4859
5117
|
if (!/^[a-zA-Z0-9_-]+$/.test(repoName)) {
|
|
4860
5118
|
return res.status(400).json({ error: "Invalid repository name" });
|
|
4861
5119
|
}
|
|
4862
|
-
const pluginsDir =
|
|
4863
|
-
const targetDir =
|
|
4864
|
-
const tmpDir =
|
|
5120
|
+
const pluginsDir = join9(getGlobalConfigDir(config3.mode ?? "production"), "plugins");
|
|
5121
|
+
const targetDir = join9(pluginsDir, repoName);
|
|
5122
|
+
const tmpDir = join9(pluginsDir, `.${repoName}-tmp-${Date.now()}`);
|
|
4865
5123
|
try {
|
|
4866
5124
|
await execFileP("mkdir", ["-p", pluginsDir], { timeout: 5e3 });
|
|
4867
5125
|
} catch {
|
|
@@ -4892,7 +5150,7 @@ function createPluginRoutes(options) {
|
|
|
4892
5150
|
}
|
|
4893
5151
|
if (!loadError) {
|
|
4894
5152
|
try {
|
|
4895
|
-
const manifest = JSON.parse(await readFile3(
|
|
5153
|
+
const manifest = JSON.parse(await readFile3(join9(targetDir, "package.json"), "utf8"));
|
|
4896
5154
|
const pluginEntry = manifest.openfox?.plugin;
|
|
4897
5155
|
const apiVersion = manifest.openfox?.apiVersion;
|
|
4898
5156
|
if (!pluginEntry || !manifest.name) {
|
|
@@ -4900,7 +5158,7 @@ function createPluginRoutes(options) {
|
|
|
4900
5158
|
} else if (apiVersion !== 1) {
|
|
4901
5159
|
loadError = `Unsupported plugin API version: ${String(apiVersion)}`;
|
|
4902
5160
|
} else {
|
|
4903
|
-
const mod = await import(pathToFileURL2(
|
|
5161
|
+
const mod = await import(pathToFileURL2(join9(targetDir, pluginEntry)).href);
|
|
4904
5162
|
if (typeof mod.register !== "function") {
|
|
4905
5163
|
loadError = "Plugin does not export register(registry)";
|
|
4906
5164
|
} else {
|
|
@@ -4949,13 +5207,13 @@ function createPluginRoutes(options) {
|
|
|
4949
5207
|
}
|
|
4950
5208
|
});
|
|
4951
5209
|
router.get("/installed", async (_req, res) => {
|
|
4952
|
-
const pluginsDir =
|
|
5210
|
+
const pluginsDir = join9(getGlobalConfigDir(config3.mode ?? "production"), "plugins");
|
|
4953
5211
|
try {
|
|
4954
|
-
const entries = await
|
|
5212
|
+
const entries = await readdir4(pluginsDir, { withFileTypes: true });
|
|
4955
5213
|
const installed = [];
|
|
4956
5214
|
for (const entry of entries) {
|
|
4957
5215
|
if (!entry.isDirectory()) continue;
|
|
4958
|
-
const pkgPath =
|
|
5216
|
+
const pkgPath = join9(pluginsDir, entry.name, "package.json");
|
|
4959
5217
|
let version = null;
|
|
4960
5218
|
try {
|
|
4961
5219
|
const pkg = JSON.parse(await readFile3(pkgPath, "utf8"));
|
|
@@ -4970,13 +5228,13 @@ function createPluginRoutes(options) {
|
|
|
4970
5228
|
}
|
|
4971
5229
|
});
|
|
4972
5230
|
router.get("/open-folder", async (_req, res) => {
|
|
4973
|
-
const pluginsDir =
|
|
5231
|
+
const pluginsDir = join9(getGlobalConfigDir(config3.mode ?? "production"), "plugins");
|
|
4974
5232
|
await openFolder(pluginsDir, res);
|
|
4975
5233
|
});
|
|
4976
5234
|
router.get("/:name/open-folder", async (req, res) => {
|
|
4977
5235
|
const name = req.params.name;
|
|
4978
5236
|
if (!/^[a-zA-Z0-9_-]+$/.test(name)) return res.status(400).json({ error: "Invalid plugin name" });
|
|
4979
|
-
const targetDir =
|
|
5237
|
+
const targetDir = join9(getGlobalConfigDir(config3.mode ?? "production"), "plugins", name);
|
|
4980
5238
|
await openFolder(targetDir, res);
|
|
4981
5239
|
});
|
|
4982
5240
|
router.delete("/:name", async (req, res) => {
|
|
@@ -4984,7 +5242,7 @@ function createPluginRoutes(options) {
|
|
|
4984
5242
|
if (!/^[a-zA-Z0-9_-]+$/.test(name)) {
|
|
4985
5243
|
return res.status(400).json({ error: "Invalid plugin name" });
|
|
4986
5244
|
}
|
|
4987
|
-
const targetDir =
|
|
5245
|
+
const targetDir = join9(getGlobalConfigDir(config3.mode ?? "production"), "plugins", name);
|
|
4988
5246
|
try {
|
|
4989
5247
|
await rm2(targetDir, { recursive: true, force: true });
|
|
4990
5248
|
for (let i = pluginDiagnostics.length - 1; i >= 0; i--) {
|
|
@@ -5144,7 +5402,7 @@ async function createServerHandle(config3) {
|
|
|
5144
5402
|
setMcpTools(mcpTools);
|
|
5145
5403
|
logger.info("MCP tools registered", { count: mcpTools.length });
|
|
5146
5404
|
}
|
|
5147
|
-
const { signalMcpReady } = await import("./server-
|
|
5405
|
+
const { signalMcpReady } = await import("./server-3XUD2NB7.js");
|
|
5148
5406
|
signalMcpReady();
|
|
5149
5407
|
});
|
|
5150
5408
|
const app = express();
|
|
@@ -5206,7 +5464,7 @@ async function createServerHandle(config3) {
|
|
|
5206
5464
|
res.json({ tools });
|
|
5207
5465
|
});
|
|
5208
5466
|
app.get("/api/projects", async (_req, res) => {
|
|
5209
|
-
const { listProjects } = await import("./projects-
|
|
5467
|
+
const { listProjects } = await import("./projects-ZGJGICGT.js");
|
|
5210
5468
|
const projects = listProjects();
|
|
5211
5469
|
res.json({ projects });
|
|
5212
5470
|
});
|
|
@@ -5215,7 +5473,7 @@ async function createServerHandle(config3) {
|
|
|
5215
5473
|
if (!name || !workdir) {
|
|
5216
5474
|
return res.status(400).json({ error: "name and workdir are required" });
|
|
5217
5475
|
}
|
|
5218
|
-
const { createDirectoryWithGit } = await import("./project-creator-
|
|
5476
|
+
const { createDirectoryWithGit } = await import("./project-creator-NNUITNUI.js");
|
|
5219
5477
|
try {
|
|
5220
5478
|
const project = await createDirectoryWithGit(name, workdir);
|
|
5221
5479
|
res.status(201).json({ project });
|
|
@@ -5260,7 +5518,7 @@ async function createServerHandle(config3) {
|
|
|
5260
5518
|
}
|
|
5261
5519
|
});
|
|
5262
5520
|
app.get("/api/projects/:id", async (req, res) => {
|
|
5263
|
-
const { getProject: getProject2 } = await import("./projects-
|
|
5521
|
+
const { getProject: getProject2 } = await import("./projects-ZGJGICGT.js");
|
|
5264
5522
|
const project = getProject2(req.params.id);
|
|
5265
5523
|
if (!project) {
|
|
5266
5524
|
return res.status(404).json({ error: "Project not found" });
|
|
@@ -5268,7 +5526,7 @@ async function createServerHandle(config3) {
|
|
|
5268
5526
|
res.json({ project });
|
|
5269
5527
|
});
|
|
5270
5528
|
app.put("/api/projects/:id", async (req, res) => {
|
|
5271
|
-
const { updateProject } = await import("./projects-
|
|
5529
|
+
const { updateProject } = await import("./projects-ZGJGICGT.js");
|
|
5272
5530
|
const { name, customInstructions, dangerLevel } = req.body;
|
|
5273
5531
|
const updates = {};
|
|
5274
5532
|
if (name !== void 0) updates.name = name;
|
|
@@ -5281,7 +5539,7 @@ async function createServerHandle(config3) {
|
|
|
5281
5539
|
res.json({ project: updated });
|
|
5282
5540
|
});
|
|
5283
5541
|
app.delete("/api/projects/:id", async (req, res) => {
|
|
5284
|
-
const { getProject: getProject2, deleteProject } = await import("./projects-
|
|
5542
|
+
const { getProject: getProject2, deleteProject } = await import("./projects-ZGJGICGT.js");
|
|
5285
5543
|
const project = getProject2(req.params.id);
|
|
5286
5544
|
if (!project) {
|
|
5287
5545
|
return res.status(404).json({ error: "Project not found" });
|
|
@@ -5290,7 +5548,7 @@ async function createServerHandle(config3) {
|
|
|
5290
5548
|
res.json({ success: true });
|
|
5291
5549
|
});
|
|
5292
5550
|
app.put("/api/projects/:id/star", async (req, res) => {
|
|
5293
|
-
const { toggleStar } = await import("./projects-
|
|
5551
|
+
const { toggleStar } = await import("./projects-ZGJGICGT.js");
|
|
5294
5552
|
const { isStarred } = req.body;
|
|
5295
5553
|
if (typeof isStarred !== "boolean") {
|
|
5296
5554
|
return res.status(400).json({ error: "isStarred is required and must be a boolean" });
|
|
@@ -5302,37 +5560,60 @@ async function createServerHandle(config3) {
|
|
|
5302
5560
|
res.json({ project });
|
|
5303
5561
|
});
|
|
5304
5562
|
app.get("/api/projects/:id/branches", async (req, res) => {
|
|
5305
|
-
const { getProject: getProject2 } = await import("./projects-
|
|
5563
|
+
const { getProject: getProject2 } = await import("./projects-ZGJGICGT.js");
|
|
5306
5564
|
const project = getProject2(req.params.id);
|
|
5307
5565
|
if (!project) return res.status(404).json({ error: "Project not found" });
|
|
5308
|
-
const { listBranches } = await import("./workspace-
|
|
5566
|
+
const { listBranches } = await import("./workspace-J74ZMSOJ.js");
|
|
5309
5567
|
const branches = await listBranches(project.workdir);
|
|
5310
5568
|
res.json({ branches });
|
|
5311
5569
|
});
|
|
5312
5570
|
app.post("/api/projects/:id/checkout", async (req, res) => {
|
|
5313
|
-
const { getProject: getProject2 } = await import("./projects-
|
|
5571
|
+
const { getProject: getProject2 } = await import("./projects-ZGJGICGT.js");
|
|
5314
5572
|
const project = getProject2(req.params.id);
|
|
5315
5573
|
if (!project) return res.status(404).json({ error: "Project not found" });
|
|
5316
5574
|
const { branch } = req.body;
|
|
5317
5575
|
if (!branch || typeof branch !== "string") return res.status(400).json({ error: "branch is required" });
|
|
5318
|
-
const { checkoutBranch } = await import("./workspace-
|
|
5576
|
+
const { checkoutBranch } = await import("./workspace-J74ZMSOJ.js");
|
|
5319
5577
|
try {
|
|
5320
5578
|
await checkoutBranch(project.workdir, branch);
|
|
5579
|
+
const { updateSessionBranch: updateSessionBranch2 } = await import("./sessions-SFG42M4W.js");
|
|
5580
|
+
const allSessions = sessionManager.listSessions();
|
|
5581
|
+
for (const s of allSessions) {
|
|
5582
|
+
const effectiveWorkdir = s.workspace ?? s.workdir;
|
|
5583
|
+
if (effectiveWorkdir === project.workdir) {
|
|
5584
|
+
updateSessionBranch2(s.id, branch);
|
|
5585
|
+
sessionManager.emitBranchChange(s.id);
|
|
5586
|
+
}
|
|
5587
|
+
}
|
|
5321
5588
|
res.json({ branch });
|
|
5322
5589
|
} catch (err) {
|
|
5323
5590
|
res.status(400).json({ error: err instanceof Error ? err.message : "Failed to checkout branch" });
|
|
5324
5591
|
}
|
|
5325
5592
|
});
|
|
5326
5593
|
app.post("/api/projects/:id/checkout-new", async (req, res) => {
|
|
5327
|
-
const { getProject: getProject2 } = await import("./projects-
|
|
5594
|
+
const { getProject: getProject2 } = await import("./projects-ZGJGICGT.js");
|
|
5328
5595
|
const project = getProject2(req.params.id);
|
|
5329
5596
|
if (!project) return res.status(404).json({ error: "Project not found" });
|
|
5330
|
-
const { name } = req.body;
|
|
5597
|
+
const { name, sourceBranch } = req.body;
|
|
5331
5598
|
if (!name || typeof name !== "string") return res.status(400).json({ error: "name is required" });
|
|
5332
|
-
const { createBranch } = await import("./workspace-
|
|
5599
|
+
const { createBranch, resolveAndValidateSourceBranch: resolveAndValidateSourceBranch2, validateRef: validateRef2 } = await import("./workspace-J74ZMSOJ.js");
|
|
5333
5600
|
try {
|
|
5334
|
-
await
|
|
5335
|
-
|
|
5601
|
+
await validateRef2(project.workdir, name);
|
|
5602
|
+
let sb;
|
|
5603
|
+
if (sourceBranch) {
|
|
5604
|
+
sb = await resolveAndValidateSourceBranch2(project.workdir, sourceBranch, project.workdir);
|
|
5605
|
+
}
|
|
5606
|
+
await createBranch(project.workdir, name, sb);
|
|
5607
|
+
const { updateSessionBranch: updateSessionBranch2 } = await import("./sessions-SFG42M4W.js");
|
|
5608
|
+
const allSessions = sessionManager.listSessions();
|
|
5609
|
+
for (const s of allSessions) {
|
|
5610
|
+
const effectiveWorkdir = s.workspace ?? s.workdir;
|
|
5611
|
+
if (effectiveWorkdir === project.workdir) {
|
|
5612
|
+
updateSessionBranch2(s.id, name);
|
|
5613
|
+
sessionManager.emitBranchChange(s.id);
|
|
5614
|
+
}
|
|
5615
|
+
}
|
|
5616
|
+
res.json({ branch: name, sourceBranch: sourceBranch ?? null });
|
|
5336
5617
|
} catch (err) {
|
|
5337
5618
|
res.status(400).json({ error: err instanceof Error ? err.message : "Failed to create branch" });
|
|
5338
5619
|
}
|
|
@@ -5341,9 +5622,13 @@ async function createServerHandle(config3) {
|
|
|
5341
5622
|
const session = sessionManager.getSession(req.params.id);
|
|
5342
5623
|
if (!session) return res.status(404).json({ error: "Session not found" });
|
|
5343
5624
|
const effectiveWorkdir = session.workspace ?? session.workdir;
|
|
5344
|
-
const { listBranches } = await import("./workspace-
|
|
5345
|
-
const branches = await
|
|
5346
|
-
|
|
5625
|
+
const { listBranches, getDefaultBranch: getDefaultBranch2 } = await import("./workspace-J74ZMSOJ.js");
|
|
5626
|
+
const [branches, defaultBranch] = await Promise.all([
|
|
5627
|
+
listBranches(effectiveWorkdir),
|
|
5628
|
+
getDefaultBranch2(session.workdir)
|
|
5629
|
+
// project root, not workspace, for real upstream origin/HEAD
|
|
5630
|
+
]);
|
|
5631
|
+
res.json({ branches, defaultBranch });
|
|
5347
5632
|
});
|
|
5348
5633
|
app.post("/api/sessions/:id/checkout", async (req, res) => {
|
|
5349
5634
|
const session = sessionManager.getSession(req.params.id);
|
|
@@ -5351,10 +5636,21 @@ async function createServerHandle(config3) {
|
|
|
5351
5636
|
const { branch } = req.body;
|
|
5352
5637
|
if (!branch || typeof branch !== "string") return res.status(400).json({ error: "branch is required" });
|
|
5353
5638
|
const effectiveWorkdir = session.workspace ?? session.workdir;
|
|
5354
|
-
const { checkoutBranch, validateRef } = await import("./workspace-
|
|
5639
|
+
const { checkoutBranch, validateRef: validateRef2 } = await import("./workspace-J74ZMSOJ.js");
|
|
5640
|
+
const { updateSessionBranch: updateSessionBranch2 } = await import("./sessions-SFG42M4W.js");
|
|
5355
5641
|
try {
|
|
5356
|
-
await
|
|
5642
|
+
await validateRef2(effectiveWorkdir, branch);
|
|
5357
5643
|
await checkoutBranch(effectiveWorkdir, branch);
|
|
5644
|
+
updateSessionBranch2(req.params.id, branch);
|
|
5645
|
+
if (session.workspace) {
|
|
5646
|
+
const all = sessionManager.listSessions();
|
|
5647
|
+
for (const s of all) {
|
|
5648
|
+
if (s.id !== req.params.id && s.workspace === session.workspace) {
|
|
5649
|
+
updateSessionBranch2(s.id, branch);
|
|
5650
|
+
sessionManager.emitBranchChange(s.id);
|
|
5651
|
+
}
|
|
5652
|
+
}
|
|
5653
|
+
}
|
|
5358
5654
|
res.json({ branch });
|
|
5359
5655
|
} catch (err) {
|
|
5360
5656
|
res.status(400).json({ error: err instanceof Error ? err.message : "Failed to checkout branch" });
|
|
@@ -5363,30 +5659,48 @@ async function createServerHandle(config3) {
|
|
|
5363
5659
|
app.post("/api/sessions/:id/checkout-new", async (req, res) => {
|
|
5364
5660
|
const session = sessionManager.getSession(req.params.id);
|
|
5365
5661
|
if (!session) return res.status(404).json({ error: "Session not found" });
|
|
5366
|
-
const { name } = req.body;
|
|
5662
|
+
const { name, sourceBranch } = req.body;
|
|
5367
5663
|
if (!name || typeof name !== "string") return res.status(400).json({ error: "name is required" });
|
|
5368
5664
|
const effectiveWorkdir = session.workspace ?? session.workdir;
|
|
5369
|
-
const {
|
|
5665
|
+
const { validateRef: validateRef2 } = await import("./workspace-J74ZMSOJ.js");
|
|
5666
|
+
const { updateSessionBranch: updateSessionBranch2 } = await import("./sessions-SFG42M4W.js");
|
|
5370
5667
|
try {
|
|
5371
|
-
await
|
|
5372
|
-
|
|
5373
|
-
|
|
5668
|
+
await validateRef2(effectiveWorkdir, name);
|
|
5669
|
+
if (sourceBranch) {
|
|
5670
|
+
const { resolveAndValidateSourceBranch: resolveAndValidateSourceBranch2, createBranch } = await import("./workspace-J74ZMSOJ.js");
|
|
5671
|
+
const sb = await resolveAndValidateSourceBranch2(effectiveWorkdir, sourceBranch, session.workdir);
|
|
5672
|
+
await createBranch(effectiveWorkdir, name, sb);
|
|
5673
|
+
} else {
|
|
5674
|
+
const { createBranch } = await import("./workspace-J74ZMSOJ.js");
|
|
5675
|
+
await createBranch(effectiveWorkdir, name);
|
|
5676
|
+
}
|
|
5677
|
+
updateSessionBranch2(req.params.id, name);
|
|
5678
|
+
if (session.workspace) {
|
|
5679
|
+
const all = sessionManager.listSessions();
|
|
5680
|
+
for (const s of all) {
|
|
5681
|
+
if (s.id !== req.params.id && s.workspace === session.workspace) {
|
|
5682
|
+
updateSessionBranch2(s.id, name);
|
|
5683
|
+
sessionManager.emitBranchChange(s.id);
|
|
5684
|
+
}
|
|
5685
|
+
}
|
|
5686
|
+
}
|
|
5687
|
+
res.json({ branch: name, sourceBranch: sourceBranch ?? null });
|
|
5374
5688
|
} catch (err) {
|
|
5375
5689
|
res.status(400).json({ error: err instanceof Error ? err.message : "Failed to create branch" });
|
|
5376
5690
|
}
|
|
5377
5691
|
});
|
|
5378
5692
|
app.get("/api/projects/:id/workspaces", async (req, res) => {
|
|
5379
|
-
const { getProject: getProject2 } = await import("./projects-
|
|
5693
|
+
const { getProject: getProject2 } = await import("./projects-ZGJGICGT.js");
|
|
5380
5694
|
const project = getProject2(req.params.id);
|
|
5381
5695
|
if (!project) return res.status(404).json({ error: "Project not found" });
|
|
5382
|
-
const { listWorkspaces } = await import("./workspace-
|
|
5383
|
-
const all = await listWorkspaces(project.name);
|
|
5696
|
+
const { listWorkspaces } = await import("./workspace-J74ZMSOJ.js");
|
|
5697
|
+
const all = await listWorkspaces(project.name, project.workdir);
|
|
5384
5698
|
const workspacesList = all.filter((ws) => ws.path !== project.workdir);
|
|
5385
5699
|
res.json({ workspaces: workspacesList });
|
|
5386
5700
|
});
|
|
5387
5701
|
app.get("/api/sessions", async (req, res) => {
|
|
5388
|
-
const { getRecentUserPromptsForSession } = await import("./events-
|
|
5389
|
-
const { getPendingConfirmationsBySession } = await import("./path-security-
|
|
5702
|
+
const { getRecentUserPromptsForSession } = await import("./events-KNOZX7YG.js");
|
|
5703
|
+
const { getPendingConfirmationsBySession } = await import("./path-security-HUXQHEQ5.js");
|
|
5390
5704
|
const projectId = req.query["projectId"];
|
|
5391
5705
|
const limit = Math.min(parseInt(req.query["limit"]) || 20, 100);
|
|
5392
5706
|
const offset = parseInt(req.query["offset"]) || 0;
|
|
@@ -5424,7 +5738,7 @@ async function createServerHandle(config3) {
|
|
|
5424
5738
|
}
|
|
5425
5739
|
const { providerId, model } = parseDefaultModelSelection(config3.defaultModelSelection);
|
|
5426
5740
|
const session = sessionManager.createSession(projectId, title, providerId ?? null, model ?? null);
|
|
5427
|
-
wssExports.
|
|
5741
|
+
wssExports.broadcastForProject(projectId, session.id, {
|
|
5428
5742
|
type: "session.created",
|
|
5429
5743
|
sessionId: session.id,
|
|
5430
5744
|
payload: {
|
|
@@ -5452,10 +5766,10 @@ async function createServerHandle(config3) {
|
|
|
5452
5766
|
app.post("/api/sessions/:id/switch-workspace", async (req, res) => {
|
|
5453
5767
|
const session = sessionManager.getSession(req.params.id);
|
|
5454
5768
|
if (!session) return res.status(404).json({ error: "Session not found" });
|
|
5455
|
-
const { target, branch } = req.body;
|
|
5769
|
+
const { target, branch, sourceBranch } = req.body;
|
|
5456
5770
|
if (!target || typeof target !== "string") return res.status(400).json({ error: "target is required" });
|
|
5457
5771
|
try {
|
|
5458
|
-
const updated = await sessionManager.switchWorkspace(req.params.id, target, branch);
|
|
5772
|
+
const updated = await sessionManager.switchWorkspace(req.params.id, target, branch, sourceBranch);
|
|
5459
5773
|
res.json({ session: updated });
|
|
5460
5774
|
} catch (err) {
|
|
5461
5775
|
res.status(400).json({ error: err instanceof Error ? err.message : "Failed to switch workspace" });
|
|
@@ -5474,21 +5788,32 @@ async function createServerHandle(config3) {
|
|
|
5474
5788
|
}
|
|
5475
5789
|
});
|
|
5476
5790
|
app.get("/api/sessions/:id", async (req, res) => {
|
|
5477
|
-
const { getEventStore: getEventStore2 } = await import("./events-
|
|
5478
|
-
const { buildMessagesFromStoredEvents, foldPendingConfirmations } = await import("./folding-
|
|
5479
|
-
const { getPendingQuestionsForSession } = await import("./tools-
|
|
5791
|
+
const { getEventStore: getEventStore2, combineEventsWithSnapshot } = await import("./events-KNOZX7YG.js");
|
|
5792
|
+
const { buildMessagesFromStoredEvents, foldPendingConfirmations } = await import("./folding-OPP2BVUB.js");
|
|
5793
|
+
const { getPendingQuestionsForSession } = await import("./tools-HGKUD327.js");
|
|
5794
|
+
const { getMaxVisibleItems } = await import("./settings-6M55N4Q2.js");
|
|
5480
5795
|
const session = sessionManager.getSession(req.params.id);
|
|
5481
5796
|
if (!session) {
|
|
5482
5797
|
return res.status(404).json({ error: "Session not found" });
|
|
5483
5798
|
}
|
|
5484
5799
|
const eventStore = getEventStore2();
|
|
5485
|
-
const events = eventStore.
|
|
5486
|
-
const
|
|
5800
|
+
const { snapshot, events: eventsSinceSnapshot } = eventStore.getEventsSinceSnapshot(req.params.id);
|
|
5801
|
+
const events = combineEventsWithSnapshot(req.params.id, snapshot, eventsSinceSnapshot);
|
|
5802
|
+
const maxVisibleItems = req.query["full"] === "true" ? void 0 : getMaxVisibleItems() || void 0;
|
|
5803
|
+
const { messages, hiddenCount } = buildMessagesFromStoredEvents(events, maxVisibleItems);
|
|
5487
5804
|
const contextState = sessionManager.getContextState(req.params.id);
|
|
5488
5805
|
const queueState = sessionManager.getQueueState(req.params.id);
|
|
5489
5806
|
const pendingQuestions = getPendingQuestionsForSession(req.params.id);
|
|
5490
5807
|
const pendingConfirmations = foldPendingConfirmations(events);
|
|
5491
|
-
res.json({
|
|
5808
|
+
res.json({
|
|
5809
|
+
session,
|
|
5810
|
+
messages,
|
|
5811
|
+
hiddenCount,
|
|
5812
|
+
contextState,
|
|
5813
|
+
queueState,
|
|
5814
|
+
pendingQuestions,
|
|
5815
|
+
pendingConfirmations
|
|
5816
|
+
});
|
|
5492
5817
|
});
|
|
5493
5818
|
app.delete("/api/sessions/:id", async (req, res) => {
|
|
5494
5819
|
const sessionId = req.params["id"];
|
|
@@ -5496,8 +5821,8 @@ async function createServerHandle(config3) {
|
|
|
5496
5821
|
if (!session) {
|
|
5497
5822
|
return res.status(404).json({ error: "Session not found" });
|
|
5498
5823
|
}
|
|
5499
|
-
const { stopSessionExecution } = await import("./chat-handler-
|
|
5500
|
-
const { cancelQuestionsForSession, cancelPathConfirmationsForSession } = await import("./tools-
|
|
5824
|
+
const { stopSessionExecution } = await import("./chat-handler-5ZHPYU24.js");
|
|
5825
|
+
const { cancelQuestionsForSession, cancelPathConfirmationsForSession } = await import("./tools-HGKUD327.js");
|
|
5501
5826
|
sessionManager.clearMessageQueue(sessionId);
|
|
5502
5827
|
stopSessionExecution(sessionId, sessionManager);
|
|
5503
5828
|
abortSession(sessionId);
|
|
@@ -5526,8 +5851,9 @@ async function createServerHandle(config3) {
|
|
|
5526
5851
|
res.json({ success: true });
|
|
5527
5852
|
});
|
|
5528
5853
|
app.post("/api/sessions/:id/provider", async (req, res) => {
|
|
5529
|
-
const { getEventStore: getEventStore2 } = await import("./events-
|
|
5530
|
-
const { buildMessagesFromStoredEvents } = await import("./folding-
|
|
5854
|
+
const { getEventStore: getEventStore2, combineEventsWithSnapshot: combineEv } = await import("./events-KNOZX7YG.js");
|
|
5855
|
+
const { buildMessagesFromStoredEvents } = await import("./folding-OPP2BVUB.js");
|
|
5856
|
+
const { getMaxVisibleItems } = await import("./settings-6M55N4Q2.js");
|
|
5531
5857
|
const sessionId = req.params.id;
|
|
5532
5858
|
const session = sessionManager.getSession(sessionId);
|
|
5533
5859
|
if (!session) {
|
|
@@ -5542,10 +5868,12 @@ async function createServerHandle(config3) {
|
|
|
5542
5868
|
sessionManager.setSessionProvider(sessionId, providerId, resolvedModel);
|
|
5543
5869
|
const contextState = sessionManager.getContextState(sessionId);
|
|
5544
5870
|
const eventStore = getEventStore2();
|
|
5545
|
-
const events = eventStore.
|
|
5546
|
-
const
|
|
5871
|
+
const { snapshot, events: eventsSinceSnapshot } = eventStore.getEventsSinceSnapshot(sessionId);
|
|
5872
|
+
const events = combineEv(sessionId, snapshot, eventsSinceSnapshot);
|
|
5873
|
+
const maxVisibleItems = getMaxVisibleItems();
|
|
5874
|
+
const { messages, hiddenCount } = buildMessagesFromStoredEvents(events, maxVisibleItems || void 0);
|
|
5547
5875
|
const updatedSession = sessionManager.getSession(sessionId);
|
|
5548
|
-
res.json({ session: updatedSession, messages, contextState });
|
|
5876
|
+
res.json({ session: updatedSession, messages, hiddenCount, contextState });
|
|
5549
5877
|
});
|
|
5550
5878
|
app.post("/api/default-model", async (req, res) => {
|
|
5551
5879
|
const { providerId, model } = req.body;
|
|
@@ -5638,8 +5966,9 @@ async function createServerHandle(config3) {
|
|
|
5638
5966
|
res.json({ success: true });
|
|
5639
5967
|
});
|
|
5640
5968
|
app.put("/api/sessions/:id/mode", async (req, res) => {
|
|
5641
|
-
const { getEventStore: getEventStore2 } = await import("./events-
|
|
5642
|
-
const { buildMessagesFromStoredEvents } = await import("./folding-
|
|
5969
|
+
const { getEventStore: getEventStore2, combineEventsWithSnapshot: combineEv } = await import("./events-KNOZX7YG.js");
|
|
5970
|
+
const { buildMessagesFromStoredEvents } = await import("./folding-OPP2BVUB.js");
|
|
5971
|
+
const { getMaxVisibleItems } = await import("./settings-6M55N4Q2.js");
|
|
5643
5972
|
const sessionId = req.params.id;
|
|
5644
5973
|
const session = sessionManager.getSession(sessionId);
|
|
5645
5974
|
if (!session) {
|
|
@@ -5656,10 +5985,12 @@ async function createServerHandle(config3) {
|
|
|
5656
5985
|
}
|
|
5657
5986
|
sessionManager.setMode(sessionId, mode);
|
|
5658
5987
|
const eventStore = getEventStore2();
|
|
5659
|
-
const events = eventStore.
|
|
5660
|
-
const
|
|
5988
|
+
const { snapshot, events: eventsSinceSnapshot } = eventStore.getEventsSinceSnapshot(sessionId);
|
|
5989
|
+
const events = combineEv(sessionId, snapshot, eventsSinceSnapshot);
|
|
5990
|
+
const maxVisibleItems = getMaxVisibleItems();
|
|
5991
|
+
const { messages, hiddenCount } = buildMessagesFromStoredEvents(events, maxVisibleItems || void 0);
|
|
5661
5992
|
const updatedSession = sessionManager.getSession(sessionId);
|
|
5662
|
-
res.json({ session: updatedSession, messages });
|
|
5993
|
+
res.json({ session: updatedSession, messages, hiddenCount });
|
|
5663
5994
|
});
|
|
5664
5995
|
app.put("/api/sessions/:id/danger-level", async (req, res) => {
|
|
5665
5996
|
const sessionId = req.params.id;
|
|
@@ -5692,29 +6023,50 @@ async function createServerHandle(config3) {
|
|
|
5692
6023
|
app.post("/api/sessions/:id/confirm-path", async (req, res) => {
|
|
5693
6024
|
const sessionId = req.params.id;
|
|
5694
6025
|
const { callId, approved, alwaysAllow } = req.body;
|
|
5695
|
-
if (!callId || approved
|
|
5696
|
-
return res.status(400).json({ error: "callId and approved are required" });
|
|
6026
|
+
if (!callId || typeof approved !== "boolean") {
|
|
6027
|
+
return res.status(400).json({ error: "callId and approved (boolean) are required" });
|
|
6028
|
+
}
|
|
6029
|
+
if (alwaysAllow !== void 0 && typeof alwaysAllow !== "boolean") {
|
|
6030
|
+
return res.status(400).json({ error: "alwaysAllow must be a boolean if provided" });
|
|
6031
|
+
}
|
|
6032
|
+
const { providePathConfirmation, getConfirmationSessionId } = await import("./tools-HGKUD327.js");
|
|
6033
|
+
const pendingSessionId = getConfirmationSessionId(callId);
|
|
6034
|
+
if (!pendingSessionId) {
|
|
6035
|
+
return res.status(404).json({ error: "No pending path confirmation with that ID" });
|
|
6036
|
+
}
|
|
6037
|
+
if (pendingSessionId !== sessionId) {
|
|
6038
|
+
return res.status(403).json({ error: "Confirmation does not belong to this session" });
|
|
5697
6039
|
}
|
|
5698
|
-
const { providePathConfirmation } = await import("./tools-T7S45DOQ.js");
|
|
5699
6040
|
const result = providePathConfirmation(callId, approved, alwaysAllow);
|
|
5700
6041
|
if (!result.found) {
|
|
5701
6042
|
return res.status(404).json({ error: "No pending path confirmation with that ID" });
|
|
5702
6043
|
}
|
|
5703
|
-
const { getEventStore: getEventStore2 } = await import("./events-
|
|
5704
|
-
const { buildMessagesFromStoredEvents, foldPendingConfirmations } = await import("./folding-
|
|
5705
|
-
const { createSessionStateMessage } = await import("./protocol-
|
|
5706
|
-
const { getPendingQuestionsForSession } = await import("./tools-
|
|
6044
|
+
const { getEventStore: getEventStore2, combineEventsWithSnapshot: combineEvents } = await import("./events-KNOZX7YG.js");
|
|
6045
|
+
const { buildMessagesFromStoredEvents, foldPendingConfirmations } = await import("./folding-OPP2BVUB.js");
|
|
6046
|
+
const { createSessionStateMessage } = await import("./protocol-Q7X4NEME.js");
|
|
6047
|
+
const { getPendingQuestionsForSession } = await import("./tools-HGKUD327.js");
|
|
6048
|
+
const { getMaxVisibleItems } = await import("./settings-6M55N4Q2.js");
|
|
5707
6049
|
const eventStore = getEventStore2();
|
|
5708
|
-
const events = eventStore.
|
|
5709
|
-
const
|
|
6050
|
+
const { snapshot, events: eventsSinceSnapshot } = eventStore.getEventsSinceSnapshot(sessionId);
|
|
6051
|
+
const events = combineEvents(sessionId, snapshot, eventsSinceSnapshot);
|
|
6052
|
+
const maxVisibleItems = getMaxVisibleItems();
|
|
6053
|
+
const { messages, hiddenCount } = buildMessagesFromStoredEvents(events, maxVisibleItems || void 0);
|
|
5710
6054
|
const pendingConfirmations = foldPendingConfirmations(events);
|
|
5711
6055
|
const pendingQuestions = getPendingQuestionsForSession(sessionId);
|
|
5712
6056
|
const session = sessionManager.getSession(sessionId);
|
|
5713
6057
|
if (session) {
|
|
5714
|
-
const stateMsg = createSessionStateMessage(
|
|
6058
|
+
const stateMsg = createSessionStateMessage(
|
|
6059
|
+
session,
|
|
6060
|
+
messages,
|
|
6061
|
+
pendingConfirmations,
|
|
6062
|
+
pendingQuestions,
|
|
6063
|
+
void 0,
|
|
6064
|
+
void 0,
|
|
6065
|
+
hiddenCount
|
|
6066
|
+
);
|
|
5715
6067
|
wssExports.broadcastForSession(sessionId, { ...stateMsg, sessionId });
|
|
5716
6068
|
}
|
|
5717
|
-
wssExports.
|
|
6069
|
+
wssExports.broadcastForSession(sessionId, {
|
|
5718
6070
|
type: "session.confirmation_resolved",
|
|
5719
6071
|
sessionId,
|
|
5720
6072
|
payload: { sessionId, callId }
|
|
@@ -5729,7 +6081,7 @@ async function createServerHandle(config3) {
|
|
|
5729
6081
|
if (!skip && typeof answer !== "string") {
|
|
5730
6082
|
return res.status(400).json({ error: "answer is required when not skipping" });
|
|
5731
6083
|
}
|
|
5732
|
-
const { provideAnswer } = await import("./tools-
|
|
6084
|
+
const { provideAnswer } = await import("./tools-HGKUD327.js");
|
|
5733
6085
|
const found = provideAnswer(callId, answer ?? "", skip ?? false);
|
|
5734
6086
|
if (!found) {
|
|
5735
6087
|
return res.status(404).json({ error: "No pending question with that ID" });
|
|
@@ -5757,7 +6109,7 @@ async function createServerHandle(config3) {
|
|
|
5757
6109
|
if (!session) {
|
|
5758
6110
|
return res.status(404).json({ error: "Session not found" });
|
|
5759
6111
|
}
|
|
5760
|
-
const { getSetting: getSetting2, SETTINGS_KEYS } = await import("./settings-
|
|
6112
|
+
const { getSetting: getSetting2, SETTINGS_KEYS } = await import("./settings-6M55N4Q2.js");
|
|
5761
6113
|
if (getSetting2(SETTINGS_KEYS.CACHE_WARMING) !== "true") {
|
|
5762
6114
|
return res.json({ success: false, reason: "disabled" });
|
|
5763
6115
|
}
|
|
@@ -5789,7 +6141,7 @@ async function createServerHandle(config3) {
|
|
|
5789
6141
|
backend: activeProvider?.backend ?? llmClient.getBackend(),
|
|
5790
6142
|
model: llmClient.getModel()
|
|
5791
6143
|
};
|
|
5792
|
-
const { runAgentTurn, TurnMetrics } = await import("./orchestrator-
|
|
6144
|
+
const { runAgentTurn, TurnMetrics } = await import("./orchestrator-L6LX5BV7.js");
|
|
5793
6145
|
runAgentTurn(
|
|
5794
6146
|
{
|
|
5795
6147
|
sessionManager,
|
|
@@ -5827,15 +6179,15 @@ async function createServerHandle(config3) {
|
|
|
5827
6179
|
if (!session) {
|
|
5828
6180
|
return res.status(404).json({ error: "Session not found" });
|
|
5829
6181
|
}
|
|
5830
|
-
const { stopSessionExecution } = await import("./chat-handler-
|
|
5831
|
-
const { cancelQuestionsForSession, cancelPathConfirmationsForSession } = await import("./tools-
|
|
6182
|
+
const { stopSessionExecution } = await import("./chat-handler-5ZHPYU24.js");
|
|
6183
|
+
const { cancelQuestionsForSession, cancelPathConfirmationsForSession } = await import("./tools-HGKUD327.js");
|
|
5832
6184
|
const queuedMessages = sessionManager.getQueueState(sessionId);
|
|
5833
6185
|
sessionManager.clearMessageQueue(sessionId);
|
|
5834
6186
|
stopSessionExecution(sessionId, sessionManager);
|
|
5835
6187
|
abortSession(sessionId);
|
|
5836
6188
|
cancelQuestionsForSession(sessionId, "Session stopped by user");
|
|
5837
6189
|
cancelPathConfirmationsForSession(sessionId, "Session stopped by user");
|
|
5838
|
-
const eventStore = (await import("./events-
|
|
6190
|
+
const eventStore = (await import("./events-KNOZX7YG.js")).getEventStore();
|
|
5839
6191
|
eventStore.append(sessionId, { type: "running.changed", data: { isRunning: false } });
|
|
5840
6192
|
res.json({ success: true, queuedMessages });
|
|
5841
6193
|
});
|
|
@@ -5849,7 +6201,7 @@ async function createServerHandle(config3) {
|
|
|
5849
6201
|
if (typeof messageIndex !== "number" || messageIndex < 0) {
|
|
5850
6202
|
return res.status(400).json({ error: "messageIndex must be a non-negative number" });
|
|
5851
6203
|
}
|
|
5852
|
-
const { truncateSessionMessages } = await import("./events-
|
|
6204
|
+
const { truncateSessionMessages } = await import("./events-KNOZX7YG.js");
|
|
5853
6205
|
truncateSessionMessages(sessionId, messageIndex);
|
|
5854
6206
|
res.json({ success: true });
|
|
5855
6207
|
});
|
|
@@ -5866,11 +6218,11 @@ async function createServerHandle(config3) {
|
|
|
5866
6218
|
if (content !== void 0 && (typeof content !== "string" || !content.trim())) {
|
|
5867
6219
|
return res.status(400).json({ error: "content must be a non-empty string if provided" });
|
|
5868
6220
|
}
|
|
5869
|
-
const { getEventStore: getEventStore2 } = await import("./events-
|
|
5870
|
-
const { buildMessagesFromStoredEvents } = await import("./folding-
|
|
6221
|
+
const { getEventStore: getEventStore2 } = await import("./events-KNOZX7YG.js");
|
|
6222
|
+
const { buildMessagesFromStoredEvents } = await import("./folding-OPP2BVUB.js");
|
|
5871
6223
|
const eventStore = getEventStore2();
|
|
5872
6224
|
const events = eventStore.getEvents(sessionId);
|
|
5873
|
-
const messages = buildMessagesFromStoredEvents(events);
|
|
6225
|
+
const { messages } = buildMessagesFromStoredEvents(events);
|
|
5874
6226
|
const msgIndex = messages.findIndex((m) => m.id === messageId);
|
|
5875
6227
|
if (msgIndex === -1) {
|
|
5876
6228
|
return res.status(400).json({ error: "Message not found" });
|
|
@@ -5879,7 +6231,7 @@ async function createServerHandle(config3) {
|
|
|
5879
6231
|
if (msg.role !== "user" || msg.isSystemGenerated) {
|
|
5880
6232
|
return res.status(400).json({ error: "Can only replay user messages" });
|
|
5881
6233
|
}
|
|
5882
|
-
const { truncateSessionMessages } = await import("./events-
|
|
6234
|
+
const { truncateSessionMessages } = await import("./events-KNOZX7YG.js");
|
|
5883
6235
|
truncateSessionMessages(sessionId, msgIndex - 1);
|
|
5884
6236
|
sessionManager.queueMessage(sessionId, "asap", content ?? msg.content, msg.attachments, msg.messageKind);
|
|
5885
6237
|
res.json({ success: true });
|
|
@@ -5911,7 +6263,7 @@ async function createServerHandle(config3) {
|
|
|
5911
6263
|
res.json({ accepted: true });
|
|
5912
6264
|
});
|
|
5913
6265
|
app.get("/api/settings", async (req, res) => {
|
|
5914
|
-
const { getSetting: getSetting2, SETTINGS_DEFAULTS } = await import("./settings-
|
|
6266
|
+
const { getSetting: getSetting2, SETTINGS_DEFAULTS } = await import("./settings-6M55N4Q2.js");
|
|
5915
6267
|
const keysParam = req.query["keys"];
|
|
5916
6268
|
if (!keysParam) {
|
|
5917
6269
|
return res.status(400).json({ error: "keys query parameter is required" });
|
|
@@ -5924,19 +6276,19 @@ async function createServerHandle(config3) {
|
|
|
5924
6276
|
res.json(result);
|
|
5925
6277
|
});
|
|
5926
6278
|
app.get("/api/settings/:key", async (req, res) => {
|
|
5927
|
-
const { getSetting: getSetting2, SETTINGS_DEFAULTS } = await import("./settings-
|
|
6279
|
+
const { getSetting: getSetting2, SETTINGS_DEFAULTS } = await import("./settings-6M55N4Q2.js");
|
|
5928
6280
|
const key = req.params.key;
|
|
5929
6281
|
const value = getSetting2(key) ?? SETTINGS_DEFAULTS[key] ?? null;
|
|
5930
6282
|
res.json({ key, value });
|
|
5931
6283
|
});
|
|
5932
6284
|
app.get("/api/settings/:key", async (req, res) => {
|
|
5933
|
-
const { getSetting: getSetting2, SETTINGS_DEFAULTS } = await import("./settings-
|
|
6285
|
+
const { getSetting: getSetting2, SETTINGS_DEFAULTS } = await import("./settings-6M55N4Q2.js");
|
|
5934
6286
|
const key = req.params.key;
|
|
5935
6287
|
const value = getSetting2(key) ?? SETTINGS_DEFAULTS[key] ?? null;
|
|
5936
6288
|
res.json({ key, value });
|
|
5937
6289
|
});
|
|
5938
6290
|
app.put("/api/settings/:key", async (req, res) => {
|
|
5939
|
-
const { setSetting: setSetting2 } = await import("./settings-
|
|
6291
|
+
const { setSetting: setSetting2 } = await import("./settings-6M55N4Q2.js");
|
|
5940
6292
|
const key = req.params.key;
|
|
5941
6293
|
const { value } = req.body;
|
|
5942
6294
|
if (value === void 0) {
|
|
@@ -5948,14 +6300,14 @@ async function createServerHandle(config3) {
|
|
|
5948
6300
|
app.get("/api/tools/rtk-check", async (_req, res) => {
|
|
5949
6301
|
const { spawn: spawn3 } = await import("child_process");
|
|
5950
6302
|
try {
|
|
5951
|
-
const available = await new Promise((
|
|
6303
|
+
const available = await new Promise((resolve9) => {
|
|
5952
6304
|
const proc = spawn3("rtk", ["--version"], { stdio: ["ignore", "pipe", "pipe"] });
|
|
5953
6305
|
let out = "";
|
|
5954
6306
|
proc.stdout?.on("data", (d) => {
|
|
5955
6307
|
out += d.toString();
|
|
5956
6308
|
});
|
|
5957
|
-
proc.on("error", () =>
|
|
5958
|
-
proc.on("close", (code) =>
|
|
6309
|
+
proc.on("error", () => resolve9(false));
|
|
6310
|
+
proc.on("close", (code) => resolve9(code === 0 && out.startsWith("rtk ")));
|
|
5959
6311
|
});
|
|
5960
6312
|
res.json({ available });
|
|
5961
6313
|
} catch {
|
|
@@ -5963,7 +6315,7 @@ async function createServerHandle(config3) {
|
|
|
5963
6315
|
}
|
|
5964
6316
|
});
|
|
5965
6317
|
app.get("/api/tools/shells", async (_req, res) => {
|
|
5966
|
-
const { listAvailableShells } = await import("./platform-
|
|
6318
|
+
const { listAvailableShells } = await import("./platform-VBNNMTR5.js");
|
|
5967
6319
|
res.json({ shells: listAvailableShells() });
|
|
5968
6320
|
});
|
|
5969
6321
|
app.get("/api/config", async (_req, res) => {
|
|
@@ -6455,7 +6807,7 @@ async function createServerHandle(config3) {
|
|
|
6455
6807
|
});
|
|
6456
6808
|
async function rebuildMcpTools() {
|
|
6457
6809
|
const { createMcpTools: createMcpTools2 } = await import("./tool-adapter-MOGEI6TP.js");
|
|
6458
|
-
const { setMcpTools: setMcpTools2 } = await import("./tools-
|
|
6810
|
+
const { setMcpTools: setMcpTools2 } = await import("./tools-HGKUD327.js");
|
|
6459
6811
|
const mcpTools = createMcpTools2(mcpManager);
|
|
6460
6812
|
setMcpTools2(mcpTools);
|
|
6461
6813
|
}
|
|
@@ -6702,13 +7054,13 @@ async function createServerHandle(config3) {
|
|
|
6702
7054
|
})
|
|
6703
7055
|
);
|
|
6704
7056
|
app.get("/api/sessions/:id/background-processes", async (req, res) => {
|
|
6705
|
-
const { getSessionProcesses } = await import("./manager-
|
|
7057
|
+
const { getSessionProcesses } = await import("./manager-ATWZUL3O.js");
|
|
6706
7058
|
const sessionId = req.params.id;
|
|
6707
7059
|
const processes = getSessionProcesses(sessionId);
|
|
6708
7060
|
res.json({ processes });
|
|
6709
7061
|
});
|
|
6710
7062
|
app.post("/api/sessions/:id/background-process/:processId/stop", async (req, res) => {
|
|
6711
|
-
const { stopProcess } = await import("./manager-
|
|
7063
|
+
const { stopProcess } = await import("./manager-ATWZUL3O.js");
|
|
6712
7064
|
const sessionId = req.params.id;
|
|
6713
7065
|
const processId = req.params.processId;
|
|
6714
7066
|
const session = sessionManager.getSession(sessionId);
|
|
@@ -6728,14 +7080,14 @@ async function createServerHandle(config3) {
|
|
|
6728
7080
|
});
|
|
6729
7081
|
app.use("/api/directories", createDirectoryRoutes());
|
|
6730
7082
|
app.use("/api/files", createFileSearchRoutes());
|
|
6731
|
-
const webDir =
|
|
7083
|
+
const webDir = resolve8(__dirname3, "../../web");
|
|
6732
7084
|
const isDev = config3.mode === "development";
|
|
6733
7085
|
let viteServer;
|
|
6734
7086
|
if (isDev) {
|
|
6735
7087
|
logger.info("Dev mode: using Vite middleware");
|
|
6736
7088
|
viteServer = await createViteServer({
|
|
6737
7089
|
root: webDir,
|
|
6738
|
-
configFile:
|
|
7090
|
+
configFile: resolve8(__dirname3, "../../web/vite.config.ts"),
|
|
6739
7091
|
server: { middlewareMode: true },
|
|
6740
7092
|
appType: "spa",
|
|
6741
7093
|
logLevel: "warn"
|
|
@@ -6755,7 +7107,7 @@ async function createServerHandle(config3) {
|
|
|
6755
7107
|
}
|
|
6756
7108
|
});
|
|
6757
7109
|
app.get("/fox.svg", (_req, res) => {
|
|
6758
|
-
readFile4(
|
|
7110
|
+
readFile4(join10(webDir, "fox.svg")).then((content) => {
|
|
6759
7111
|
res.set("Content-Type", "image/svg+xml");
|
|
6760
7112
|
res.send(content);
|
|
6761
7113
|
}).catch(() => {
|
|
@@ -6764,7 +7116,7 @@ async function createServerHandle(config3) {
|
|
|
6764
7116
|
});
|
|
6765
7117
|
app.use(
|
|
6766
7118
|
"/sounds",
|
|
6767
|
-
express.static(
|
|
7119
|
+
express.static(join10(webDir, "public", "sounds"), {
|
|
6768
7120
|
setHeaders: (res) => {
|
|
6769
7121
|
res.set("Content-Type", "audio/mpeg");
|
|
6770
7122
|
}
|
|
@@ -6773,13 +7125,13 @@ async function createServerHandle(config3) {
|
|
|
6773
7125
|
app.use("/__inspect__.js", (_req, res) => {
|
|
6774
7126
|
res.set("Access-Control-Allow-Origin", "*");
|
|
6775
7127
|
res.set("Content-Type", "application/javascript");
|
|
6776
|
-
readFile4(
|
|
7128
|
+
readFile4(join10(webDir, "public", "__inspect__.js")).then((content) => res.send(content)).catch(() => res.status(404).send("Not found"));
|
|
6777
7129
|
});
|
|
6778
7130
|
app.get("/*path", (req, res) => {
|
|
6779
7131
|
if (req.path.startsWith("/api/")) {
|
|
6780
7132
|
return;
|
|
6781
7133
|
}
|
|
6782
|
-
readFile4(
|
|
7134
|
+
readFile4(join10(webDir, "index.html"), "utf-8").then((indexHtml) => viteServer.transformIndexHtml(req.originalUrl, indexHtml)).then((transformed) => res.send(transformed)).catch((err) => {
|
|
6783
7135
|
logger.error("Error serving index.html", { error: err });
|
|
6784
7136
|
res.status(500).send("Server error");
|
|
6785
7137
|
});
|
|
@@ -6787,10 +7139,10 @@ async function createServerHandle(config3) {
|
|
|
6787
7139
|
logger.info("Vite middleware ready", { port: config3.server.port });
|
|
6788
7140
|
}
|
|
6789
7141
|
if (!isDev) {
|
|
6790
|
-
const distWebDir =
|
|
7142
|
+
const distWebDir = resolve8(__dirname3, "web");
|
|
6791
7143
|
app.use(
|
|
6792
7144
|
"/assets",
|
|
6793
|
-
express.static(
|
|
7145
|
+
express.static(join10(distWebDir, "assets"), {
|
|
6794
7146
|
setHeaders: (res, filepath) => {
|
|
6795
7147
|
if (filepath.endsWith(".css")) {
|
|
6796
7148
|
res.set("Content-Type", "text/css");
|
|
@@ -6798,11 +7150,11 @@ async function createServerHandle(config3) {
|
|
|
6798
7150
|
}
|
|
6799
7151
|
})
|
|
6800
7152
|
);
|
|
6801
|
-
app.use("/manifest.webmanifest", express.static(
|
|
6802
|
-
app.use("/registerSW.js", express.static(
|
|
6803
|
-
app.use("/sw.js", express.static(
|
|
7153
|
+
app.use("/manifest.webmanifest", express.static(join10(distWebDir, "manifest.webmanifest")));
|
|
7154
|
+
app.use("/registerSW.js", express.static(join10(distWebDir, "registerSW.js")));
|
|
7155
|
+
app.use("/sw.js", express.static(join10(distWebDir, "sw.js")));
|
|
6804
7156
|
app.get("/fox.svg", (_req, res) => {
|
|
6805
|
-
readFile4(
|
|
7157
|
+
readFile4(join10(distWebDir, "fox.svg")).then((content) => {
|
|
6806
7158
|
res.set("Content-Type", "image/svg+xml");
|
|
6807
7159
|
res.send(content);
|
|
6808
7160
|
}).catch(() => {
|
|
@@ -6811,7 +7163,7 @@ async function createServerHandle(config3) {
|
|
|
6811
7163
|
});
|
|
6812
7164
|
app.use(
|
|
6813
7165
|
"/sounds",
|
|
6814
|
-
express.static(
|
|
7166
|
+
express.static(join10(distWebDir, "sounds"), {
|
|
6815
7167
|
setHeaders: (res) => {
|
|
6816
7168
|
res.set("Content-Type", "audio/mpeg");
|
|
6817
7169
|
}
|
|
@@ -6820,10 +7172,10 @@ async function createServerHandle(config3) {
|
|
|
6820
7172
|
app.use("/__inspect__.js", (_req, res) => {
|
|
6821
7173
|
res.set("Access-Control-Allow-Origin", "*");
|
|
6822
7174
|
res.set("Content-Type", "application/javascript");
|
|
6823
|
-
readFile4(
|
|
7175
|
+
readFile4(join10(distWebDir, "__inspect__.js")).then((content) => res.send(content)).catch(() => res.status(404).send("Not found"));
|
|
6824
7176
|
});
|
|
6825
7177
|
app.get("/", (_req, res) => {
|
|
6826
|
-
readFile4(
|
|
7178
|
+
readFile4(join10(distWebDir, "index.html"), "utf-8").then((content) => res.send(content)).catch(() => {
|
|
6827
7179
|
res.status(404).send("Web UI not built. Run `npm run build:web`");
|
|
6828
7180
|
});
|
|
6829
7181
|
});
|
|
@@ -6831,7 +7183,7 @@ async function createServerHandle(config3) {
|
|
|
6831
7183
|
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") {
|
|
6832
7184
|
return;
|
|
6833
7185
|
}
|
|
6834
|
-
readFile4(
|
|
7186
|
+
readFile4(join10(distWebDir, "index.html"), "utf-8").then((content) => res.send(content)).catch(() => {
|
|
6835
7187
|
res.status(404).send("Web UI not built");
|
|
6836
7188
|
});
|
|
6837
7189
|
});
|
|
@@ -6851,7 +7203,7 @@ async function createServerHandle(config3) {
|
|
|
6851
7203
|
const state = sessionManager.getContextState(sessionId);
|
|
6852
7204
|
wssExports.broadcastForSession(sessionId, createContextStateMessage(state));
|
|
6853
7205
|
});
|
|
6854
|
-
const { QueueProcessor } = await import("./processor-
|
|
7206
|
+
const { QueueProcessor } = await import("./processor-UQX5KL56.js");
|
|
6855
7207
|
const queueProcessor = new QueueProcessor({
|
|
6856
7208
|
sessionManager,
|
|
6857
7209
|
providerManager,
|
|
@@ -6874,7 +7226,7 @@ async function createServerHandle(config3) {
|
|
|
6874
7226
|
return {
|
|
6875
7227
|
httpServer,
|
|
6876
7228
|
ctx: { config: config3, sessionManager, llmClient: getLLMClient(), toolRegistry, providerManager },
|
|
6877
|
-
start: (port) => new Promise((
|
|
7229
|
+
start: (port) => new Promise((resolve9, reject) => {
|
|
6878
7230
|
const listenPort = port ?? config3.server.port;
|
|
6879
7231
|
const host = config3.server.host;
|
|
6880
7232
|
httpServer.listen(listenPort, host, () => {
|
|
@@ -6884,16 +7236,16 @@ async function createServerHandle(config3) {
|
|
|
6884
7236
|
logger.info(`OpenFox server running at http://${host}:${actualPort}`);
|
|
6885
7237
|
logger.info(`WebSocket available at ws://${host}:${actualPort}/ws`);
|
|
6886
7238
|
logger.info(`LLM backend: ${client.getBackend()}, model: ${client.getModel()}, url: ${config3.llm.baseUrl}`);
|
|
6887
|
-
|
|
7239
|
+
resolve9({ port: actualPort });
|
|
6888
7240
|
});
|
|
6889
7241
|
httpServer.on("error", reject);
|
|
6890
7242
|
}),
|
|
6891
|
-
close: () => new Promise((
|
|
7243
|
+
close: () => new Promise((resolve9) => {
|
|
6892
7244
|
logger.info("Shutting down...");
|
|
6893
7245
|
void (async () => {
|
|
6894
7246
|
await devServerManager.stopAll();
|
|
6895
7247
|
await mcpManager.disconnectAll();
|
|
6896
|
-
const { stopAllInspectProxies } = await import("./inspect-proxy-
|
|
7248
|
+
const { stopAllInspectProxies } = await import("./inspect-proxy-DRYIIH4O.js");
|
|
6897
7249
|
stopAllInspectProxies();
|
|
6898
7250
|
const { cleanupAllProcesses } = await import("./store-SARB6V2N.js");
|
|
6899
7251
|
cleanupAllProcesses();
|
|
@@ -6907,7 +7259,7 @@ async function createServerHandle(config3) {
|
|
|
6907
7259
|
client.terminate();
|
|
6908
7260
|
}
|
|
6909
7261
|
wss.close();
|
|
6910
|
-
httpServer.close(() =>
|
|
7262
|
+
httpServer.close(() => resolve9());
|
|
6911
7263
|
})();
|
|
6912
7264
|
})
|
|
6913
7265
|
};
|
|
@@ -6931,4 +7283,4 @@ export {
|
|
|
6931
7283
|
createServerHandle,
|
|
6932
7284
|
createServer
|
|
6933
7285
|
};
|
|
6934
|
-
//# sourceMappingURL=chunk-
|
|
7286
|
+
//# sourceMappingURL=chunk-UMX7MMNW.js.map
|