farming-code 2.2.8 → 2.2.11
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/README.md +36 -23
- package/README.zh_cn.md +36 -23
- package/THIRD_PARTY_NOTICES.md +11 -2
- package/backend/acp-checkpoint-store.js +198 -0
- package/backend/acp-runtime.js +306 -83
- package/backend/acp-session-state.js +202 -6
- package/backend/acp-transcript.js +112 -0
- package/backend/agent-activity.js +6 -157
- package/backend/agent-manager.js +1482 -730
- package/backend/agent-provider-session.js +17 -242
- package/backend/agent-runtime-binding.js +219 -0
- package/backend/agent-session-history.js +66 -1
- package/backend/auth.js +79 -6
- package/backend/codex-models.js +81 -84
- package/backend/codex-session-archive.js +45 -0
- package/backend/codex-terminal-profile.js +500 -0
- package/backend/codex-transcript-sanitizer.js +12 -0
- package/backend/codex-transcript.js +230 -8
- package/backend/config-manager.js +30 -0
- package/backend/control-api.js +192 -17
- package/backend/farming-net-pass.js +285 -0
- package/backend/farming-net-registry.js +112 -0
- package/backend/farming-net-server.js +298 -0
- package/backend/farming-session-store.js +5 -13
- package/backend/git-worktree-info.js +181 -0
- package/backend/local-session-engine.js +411 -186
- package/backend/main-page-session.js +5 -2
- package/backend/native-pty-controller-generation.js +106 -0
- package/backend/native-pty-host-client.js +275 -7
- package/backend/native-pty-host-identity.js +86 -0
- package/backend/native-pty-host.js +813 -114
- package/backend/native-session-engine.js +100 -28
- package/backend/packaged-node-pty.js +22 -2
- package/backend/provider-adapters.js +253 -0
- package/backend/provider-session-service.js +241 -0
- package/backend/runtime-observation.js +81 -0
- package/backend/server.js +294 -86
- package/backend/session-engine-bridge.js +21 -2
- package/backend/session-engine-router.js +1 -1
- package/backend/session-engine.js +1 -1
- package/backend/session-stream-protocol.js +185 -0
- package/backend/storage-layout.js +55 -0
- package/backend/terminal-attach-checkpoint.js +74 -0
- package/backend/terminal-exit-quiescence.js +39 -0
- package/backend/terminal-reducer-flow-control.js +97 -0
- package/backend/terminal-screen-state.js +11 -2
- package/backend/terminal-screen-worker-pool.js +59 -6
- package/backend/terminal-screen-worker-thread.js +97 -57
- package/backend/terminal-screen-worker.js +133 -51
- package/backend/terminal-state-serialization.js +127 -0
- package/backend/terminal-status.js +23 -4
- package/backend/usage-monitor.js +81 -4
- package/backend/workspace-directory.js +152 -0
- package/backend/workspace-file-router.js +182 -76
- package/backend/workspace-file-service.js +319 -4
- package/backend/workspace-root-registry.js +164 -0
- package/dist/assets/App-CHYpgECZ.js +208 -0
- package/dist/assets/{FileEditorMarkdownPreview-elKWc8Im.js → FileEditorMarkdownPreview--Cri80nQ.js} +92 -92
- package/dist/assets/FileEditorPane-DGJlEKdf.js +2 -0
- package/dist/assets/IconGlyphs-Cc14sepw.js +1 -0
- package/dist/assets/ProjectFilesSection-Cz1wG4NT.js +12 -0
- package/dist/assets/{ReviewPage-BaXu1ZdX.js → ReviewPage-LonRc6Fz.js} +1 -1
- package/dist/assets/code-dark-B1pAtO_P.css +1 -0
- package/dist/assets/file-icons-Bw2qd5iT.js +1 -0
- package/dist/assets/{index-BrbljRqn.js → index-miO2r9IZ.js} +3 -3
- package/dist/assets/main-DNe0jPw-.css +1 -0
- package/dist/assets/workspace-editor-model-BQol4qbA.js +1 -0
- package/dist/assets/workspace-editor-monaco-5gFbeDPF.js +1 -0
- package/dist/assets/workspace-editor-monaco-xPv8JzHH.js +4 -0
- package/dist/assets/workspace-view-state-DvYG_9PH.js +7 -0
- package/dist/assets/workspace-working-copy-D8-s_Sgh.js +1 -0
- package/dist/index.html +1 -1
- package/frontend/farming-net/app.css +625 -0
- package/frontend/farming-net/app.js +268 -0
- package/frontend/farming-net/index.html +86 -0
- package/frontend/reading-anchor.js +198 -0
- package/frontend/session-bridge.js +12 -3
- package/frontend/session-modal-bridge.js +5 -12
- package/frontend/skins/crt/app.js +1976 -793
- package/frontend/skins/crt/index.html +313 -23
- package/frontend/skins/crt/styles/billing.css +294 -223
- package/frontend/skins/crt/styles/monochrome-green.css +7 -2
- package/frontend/terminal-replay.js +372 -0
- package/package.json +10 -3
- package/shared/browser-protocol.d.ts +5 -0
- package/shared/browser-protocol.js +130 -0
- package/dist/assets/App-8dYAM6ql.js +0 -124
- package/dist/assets/FileEditorPane-RWiFD2cq.js +0 -5
- package/dist/assets/IconGlyphs-DfL0EBnj.js +0 -1
- package/dist/assets/ProjectFilesSection-Q4PDsWmM.js +0 -12
- package/dist/assets/code-dark-CDkOQAtK.css +0 -1
- package/dist/assets/file-icons-EFUGSSwf.js +0 -1
- package/dist/assets/main-D073SnW4.css +0 -1
- package/dist/assets/qoder-C9LmmOSf.svg +0 -1
- package/dist/assets/qoder-Cf9gl0Y5.svg +0 -1
- package/dist/assets/qoder-gHCinseV.svg +0 -1
- package/dist/assets/workspace-view-state-CTyDzk2D.js +0 -1
- package/dist/assets/zsh-CLpveKlF.svg +0 -1
- package/dist/assets/zsh-FxSpMPbz.svg +0 -1
- /package/dist/assets/{api-D1lyBYIQ.js → api-D8nyOEbz.js} +0 -0
- /package/dist/assets/{core-ZlAPicox.js → core-D0LFJkDt.js} +0 -0
- /package/dist/assets/{useWorkspaceMenuKeyboard-CneKAZUJ.js → useWorkspaceMenuKeyboard-Brws6Ar9.js} +0 -0
|
@@ -2,13 +2,40 @@ const express = require('express');
|
|
|
2
2
|
const fs = require('fs');
|
|
3
3
|
const os = require('os');
|
|
4
4
|
const path = require('path');
|
|
5
|
+
const { inspectGitWorktree } = require('./git-worktree-info');
|
|
5
6
|
const { WorkspaceFileError } = require('./workspace-file-service');
|
|
7
|
+
const {
|
|
8
|
+
GLOBAL_WORKSPACE_FILES_AGENT_ID,
|
|
9
|
+
GLOBAL_WORKSPACE_FILES_ROOT,
|
|
10
|
+
GLOBAL_WORKSPACE_ROOT_ID,
|
|
11
|
+
PROJECT_FILES_WORKSPACE_PREFIX,
|
|
12
|
+
WorkspaceRootRegistry,
|
|
13
|
+
projectWorkspaceFromLegacyRef,
|
|
14
|
+
} = require('./workspace-root-registry');
|
|
6
15
|
|
|
7
|
-
const
|
|
8
|
-
const GLOBAL_WORKSPACE_FILES_ROOT = '/';
|
|
16
|
+
const ROOT_REGISTRIES = new WeakMap();
|
|
9
17
|
|
|
10
18
|
function isGlobalWorkspaceFilesAgentId(agentId) {
|
|
11
|
-
return agentId === GLOBAL_WORKSPACE_FILES_AGENT_ID;
|
|
19
|
+
return agentId === GLOBAL_WORKSPACE_FILES_AGENT_ID || agentId === GLOBAL_WORKSPACE_ROOT_ID;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
function projectWorkspaceFromFilesId(filesId) {
|
|
23
|
+
return projectWorkspaceFromLegacyRef(filesId);
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
function workspaceRootRegistryFor(agentManager) {
|
|
27
|
+
let registry = ROOT_REGISTRIES.get(agentManager);
|
|
28
|
+
if (!registry) {
|
|
29
|
+
registry = new WorkspaceRootRegistry(agentManager);
|
|
30
|
+
ROOT_REGISTRIES.set(agentManager, registry);
|
|
31
|
+
}
|
|
32
|
+
return registry;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
function workspaceRef(source) {
|
|
36
|
+
return source && typeof source.rootId === 'string' && source.rootId.trim()
|
|
37
|
+
? source.rootId
|
|
38
|
+
: source?.agentId;
|
|
12
39
|
}
|
|
13
40
|
|
|
14
41
|
function normalizeAbsolutePath(value) {
|
|
@@ -51,6 +78,7 @@ function collectCandidateAllowedRoots(agentManager) {
|
|
|
51
78
|
for (const agent of state.agents || []) {
|
|
52
79
|
roots.push(agent && agent.projectWorkspace);
|
|
53
80
|
roots.push(agent && agent.cwd);
|
|
81
|
+
roots.push(agent && agent.gitWorktree && agent.gitWorktree.workspace);
|
|
54
82
|
}
|
|
55
83
|
for (const entry of state.taskHistory || []) {
|
|
56
84
|
roots.push(entry && entry.projectWorkspace);
|
|
@@ -63,6 +91,7 @@ function collectCandidateAllowedRoots(agentManager) {
|
|
|
63
91
|
: null;
|
|
64
92
|
if (settings) {
|
|
65
93
|
roots.push(...(Array.isArray(settings.workspaceHistory) ? settings.workspaceHistory : []));
|
|
94
|
+
roots.push(...(Array.isArray(settings.projectWorkspaces) ? settings.projectWorkspaces : []));
|
|
66
95
|
}
|
|
67
96
|
return roots;
|
|
68
97
|
}
|
|
@@ -157,29 +186,8 @@ function sendWorkspaceFileError(res, error) {
|
|
|
157
186
|
res.status(500).json({ error: 'workspace file operation failed' });
|
|
158
187
|
}
|
|
159
188
|
|
|
160
|
-
function resolveWorkspaceRoot(agentManager,
|
|
161
|
-
|
|
162
|
-
throw new WorkspaceFileError('agentId is required', 400);
|
|
163
|
-
}
|
|
164
|
-
|
|
165
|
-
if (isGlobalWorkspaceFilesAgentId(agentId)) {
|
|
166
|
-
return GLOBAL_WORKSPACE_FILES_ROOT;
|
|
167
|
-
}
|
|
168
|
-
|
|
169
|
-
if (agentManager && typeof agentManager.getAgentWorkspaceRoot === 'function') {
|
|
170
|
-
const root = agentManager.getAgentWorkspaceRoot(agentId);
|
|
171
|
-
if (root) return root;
|
|
172
|
-
}
|
|
173
|
-
|
|
174
|
-
const state = agentManager && typeof agentManager.getState === 'function'
|
|
175
|
-
? agentManager.getState()
|
|
176
|
-
: { agents: [] };
|
|
177
|
-
const agent = (state.agents || []).find(candidate => candidate.id === agentId);
|
|
178
|
-
if (!agent) {
|
|
179
|
-
throw new WorkspaceFileError('agent not found', 404);
|
|
180
|
-
}
|
|
181
|
-
|
|
182
|
-
return agent.projectWorkspace || agent.cwd;
|
|
189
|
+
function resolveWorkspaceRoot(agentManager, rootRef) {
|
|
190
|
+
return workspaceRootRegistryFor(agentManager).resolve(rootRef).canonicalPath;
|
|
183
191
|
}
|
|
184
192
|
|
|
185
193
|
function assertWritableWorkspaceAgent(agentId) {
|
|
@@ -194,18 +202,29 @@ function readOptionsForAgent(agentManager, agentId) {
|
|
|
194
202
|
: { allowedExternalRoots: globalWorkspaceAllowedRoots(agentManager) };
|
|
195
203
|
}
|
|
196
204
|
|
|
197
|
-
function createWorkspaceFileRouter(agentManager, fileService) {
|
|
205
|
+
function createWorkspaceFileRouter(agentManager, fileService, options = {}) {
|
|
198
206
|
const router = express.Router();
|
|
207
|
+
const rootRegistry = options.rootRegistry || workspaceRootRegistryFor(agentManager);
|
|
199
208
|
|
|
200
209
|
router.use(express.json({ limit: '3mb' }));
|
|
201
210
|
|
|
211
|
+
router.get('/roots', (_req, res) => {
|
|
212
|
+
res.json({ roots: rootRegistry.list() });
|
|
213
|
+
});
|
|
214
|
+
|
|
215
|
+
const resolveRequestRoot = source => {
|
|
216
|
+
const workspaceRoot = rootRegistry.resolve(workspaceRef(source));
|
|
217
|
+
return { root: workspaceRoot.canonicalPath, rootId: workspaceRoot.rootId };
|
|
218
|
+
};
|
|
219
|
+
|
|
202
220
|
router.get('/tree', async (req, res) => {
|
|
203
221
|
try {
|
|
204
|
-
const
|
|
205
|
-
const
|
|
222
|
+
const rootRef = workspaceRef(req.query);
|
|
223
|
+
const { root, rootId } = resolveRequestRoot(req.query);
|
|
224
|
+
const tree = isGlobalWorkspaceFilesAgentId(rootRef)
|
|
206
225
|
? await listGlobalWorkspaceTree(agentManager, fileService, req.query.path || '')
|
|
207
|
-
: await fileService.listTree(root, req.query.path || '', readOptionsForAgent(agentManager,
|
|
208
|
-
res.json({ root, tree });
|
|
226
|
+
: await fileService.listTree(root, req.query.path || '', readOptionsForAgent(agentManager, rootRef));
|
|
227
|
+
res.json({ rootId, root, tree });
|
|
209
228
|
} catch (error) {
|
|
210
229
|
sendWorkspaceFileError(res, error);
|
|
211
230
|
}
|
|
@@ -213,12 +232,13 @@ function createWorkspaceFileRouter(agentManager, fileService) {
|
|
|
213
232
|
|
|
214
233
|
router.get('/file', async (req, res) => {
|
|
215
234
|
try {
|
|
216
|
-
|
|
235
|
+
const rootRef = workspaceRef(req.query);
|
|
236
|
+
if (isGlobalWorkspaceFilesAgentId(rootRef)) {
|
|
217
237
|
assertGlobalWorkspacePathAllowed(agentManager, req.query.path || '');
|
|
218
238
|
}
|
|
219
|
-
const root =
|
|
220
|
-
const file = await fileService.readFile(root, req.query.path || '', readOptionsForAgent(agentManager,
|
|
221
|
-
res.json({ root, file });
|
|
239
|
+
const { root, rootId } = resolveRequestRoot(req.query);
|
|
240
|
+
const file = await fileService.readFile(root, req.query.path || '', readOptionsForAgent(agentManager, rootRef));
|
|
241
|
+
res.json({ rootId, root, file });
|
|
222
242
|
} catch (error) {
|
|
223
243
|
sendWorkspaceFileError(res, error);
|
|
224
244
|
}
|
|
@@ -226,11 +246,12 @@ function createWorkspaceFileRouter(agentManager, fileService) {
|
|
|
226
246
|
|
|
227
247
|
router.get('/raw', async (req, res) => {
|
|
228
248
|
try {
|
|
229
|
-
|
|
249
|
+
const rootRef = workspaceRef(req.query);
|
|
250
|
+
if (isGlobalWorkspaceFilesAgentId(rootRef)) {
|
|
230
251
|
assertGlobalWorkspacePathAllowed(agentManager, req.query.path || '');
|
|
231
252
|
}
|
|
232
|
-
const root =
|
|
233
|
-
const file = await fileService.readPreviewFile(root, req.query.path || '', readOptionsForAgent(agentManager,
|
|
253
|
+
const { root } = resolveRequestRoot(req.query);
|
|
254
|
+
const file = await fileService.readPreviewFile(root, req.query.path || '', readOptionsForAgent(agentManager, rootRef));
|
|
234
255
|
res
|
|
235
256
|
.status(200)
|
|
236
257
|
.type(file.preview.mediaType)
|
|
@@ -246,13 +267,14 @@ function createWorkspaceFileRouter(agentManager, fileService) {
|
|
|
246
267
|
router.put('/file', async (req, res) => {
|
|
247
268
|
try {
|
|
248
269
|
const body = req.body || {};
|
|
249
|
-
|
|
250
|
-
|
|
270
|
+
const rootRef = workspaceRef(body);
|
|
271
|
+
assertWritableWorkspaceAgent(rootRef);
|
|
272
|
+
const { root, rootId } = resolveRequestRoot(body);
|
|
251
273
|
const file = await fileService.writeFile(root, body.path || '', body.content, {
|
|
252
274
|
baseSha1: body.baseSha1,
|
|
253
275
|
overwrite: body.overwrite === true,
|
|
254
276
|
});
|
|
255
|
-
res.json({ root, file });
|
|
277
|
+
res.json({ rootId, root, file });
|
|
256
278
|
} catch (error) {
|
|
257
279
|
sendWorkspaceFileError(res, error);
|
|
258
280
|
}
|
|
@@ -261,10 +283,11 @@ function createWorkspaceFileRouter(agentManager, fileService) {
|
|
|
261
283
|
router.post('/move', async (req, res) => {
|
|
262
284
|
try {
|
|
263
285
|
const body = req.body || {};
|
|
264
|
-
|
|
265
|
-
|
|
286
|
+
const rootRef = workspaceRef(body);
|
|
287
|
+
assertWritableWorkspaceAgent(rootRef);
|
|
288
|
+
const { root, rootId } = resolveRequestRoot(body);
|
|
266
289
|
const move = await fileService.moveEntry(root, body.sourcePath || '', body.targetDirectory || '');
|
|
267
|
-
res.json({ root, move });
|
|
290
|
+
res.json({ rootId, root, move });
|
|
268
291
|
} catch (error) {
|
|
269
292
|
sendWorkspaceFileError(res, error);
|
|
270
293
|
}
|
|
@@ -273,10 +296,11 @@ function createWorkspaceFileRouter(agentManager, fileService) {
|
|
|
273
296
|
router.post('/entry', async (req, res) => {
|
|
274
297
|
try {
|
|
275
298
|
const body = req.body || {};
|
|
276
|
-
|
|
277
|
-
|
|
299
|
+
const rootRef = workspaceRef(body);
|
|
300
|
+
assertWritableWorkspaceAgent(rootRef);
|
|
301
|
+
const { root, rootId } = resolveRequestRoot(body);
|
|
278
302
|
const created = await fileService.createEntry(root, body.parentPath || '', body.name || '', body.entryType || 'file', body.content || '');
|
|
279
|
-
res.status(201).json({ root, ...created });
|
|
303
|
+
res.status(201).json({ rootId, root, ...created });
|
|
280
304
|
} catch (error) {
|
|
281
305
|
sendWorkspaceFileError(res, error);
|
|
282
306
|
}
|
|
@@ -285,10 +309,11 @@ function createWorkspaceFileRouter(agentManager, fileService) {
|
|
|
285
309
|
router.patch('/entry', async (req, res) => {
|
|
286
310
|
try {
|
|
287
311
|
const body = req.body || {};
|
|
288
|
-
|
|
289
|
-
|
|
312
|
+
const rootRef = workspaceRef(body);
|
|
313
|
+
assertWritableWorkspaceAgent(rootRef);
|
|
314
|
+
const { root, rootId } = resolveRequestRoot(body);
|
|
290
315
|
const move = await fileService.renameEntry(root, body.path || '', body.name || '');
|
|
291
|
-
res.json({ root, move });
|
|
316
|
+
res.json({ rootId, root, move });
|
|
292
317
|
} catch (error) {
|
|
293
318
|
sendWorkspaceFileError(res, error);
|
|
294
319
|
}
|
|
@@ -297,12 +322,13 @@ function createWorkspaceFileRouter(agentManager, fileService) {
|
|
|
297
322
|
router.delete('/entry', async (req, res) => {
|
|
298
323
|
try {
|
|
299
324
|
const body = req.body || {};
|
|
300
|
-
const
|
|
325
|
+
const rootRef = workspaceRef(body) || workspaceRef(req.query);
|
|
301
326
|
const targetPath = body.path || req.query.path || '';
|
|
302
|
-
assertWritableWorkspaceAgent(
|
|
303
|
-
const
|
|
327
|
+
assertWritableWorkspaceAgent(rootRef);
|
|
328
|
+
const workspaceRoot = rootRegistry.resolve(rootRef);
|
|
329
|
+
const { canonicalPath: root, rootId } = workspaceRoot;
|
|
304
330
|
const deleted = await fileService.deleteEntry(root, targetPath);
|
|
305
|
-
res.json({ root, deleted });
|
|
331
|
+
res.json({ rootId, root, deleted });
|
|
306
332
|
} catch (error) {
|
|
307
333
|
sendWorkspaceFileError(res, error);
|
|
308
334
|
}
|
|
@@ -310,10 +336,11 @@ function createWorkspaceFileRouter(agentManager, fileService) {
|
|
|
310
336
|
|
|
311
337
|
router.get('/search', async (req, res) => {
|
|
312
338
|
try {
|
|
313
|
-
|
|
339
|
+
const rootRef = workspaceRef(req.query);
|
|
340
|
+
if (isGlobalWorkspaceFilesAgentId(rootRef)) {
|
|
314
341
|
assertGlobalWorkspacePathAllowed(agentManager, req.query.path || '');
|
|
315
342
|
}
|
|
316
|
-
const root =
|
|
343
|
+
const { root, rootId } = resolveRequestRoot(req.query);
|
|
317
344
|
const settings = agentManager?.configManager?.getSettings?.() || {};
|
|
318
345
|
const results = await fileService.search(root, req.query.q || '', {
|
|
319
346
|
includeIgnored: req.query.includeIgnored === 'true',
|
|
@@ -321,7 +348,7 @@ function createWorkspaceFileRouter(agentManager, fileService) {
|
|
|
321
348
|
limit: req.query.limit,
|
|
322
349
|
timeoutMs: settings.searchTimeoutMs,
|
|
323
350
|
});
|
|
324
|
-
res.json({ root, results });
|
|
351
|
+
res.json({ rootId, root, results });
|
|
325
352
|
} catch (error) {
|
|
326
353
|
sendWorkspaceFileError(res, error);
|
|
327
354
|
}
|
|
@@ -329,12 +356,13 @@ function createWorkspaceFileRouter(agentManager, fileService) {
|
|
|
329
356
|
|
|
330
357
|
router.get('/diff', async (req, res) => {
|
|
331
358
|
try {
|
|
332
|
-
|
|
359
|
+
const rootRef = workspaceRef(req.query);
|
|
360
|
+
if (isGlobalWorkspaceFilesAgentId(rootRef)) {
|
|
333
361
|
assertGlobalWorkspacePathAllowed(agentManager, req.query.path || '', { allowMissing: true });
|
|
334
362
|
}
|
|
335
|
-
const root =
|
|
363
|
+
const { root, rootId } = resolveRequestRoot(req.query);
|
|
336
364
|
const diff = await fileService.diff(root, req.query.path || '');
|
|
337
|
-
res.json({ root, diff });
|
|
365
|
+
res.json({ rootId, root, diff });
|
|
338
366
|
} catch (error) {
|
|
339
367
|
sendWorkspaceFileError(res, error);
|
|
340
368
|
}
|
|
@@ -342,14 +370,15 @@ function createWorkspaceFileRouter(agentManager, fileService) {
|
|
|
342
370
|
|
|
343
371
|
router.get('/changes', async (req, res) => {
|
|
344
372
|
try {
|
|
345
|
-
|
|
373
|
+
const rootRef = workspaceRef(req.query);
|
|
374
|
+
if (isGlobalWorkspaceFilesAgentId(rootRef)) {
|
|
346
375
|
throw new WorkspaceFileError('global files do not support workspace changes', 403);
|
|
347
376
|
}
|
|
348
|
-
const root =
|
|
377
|
+
const { root, rootId } = resolveRequestRoot(req.query);
|
|
349
378
|
const changes = await fileService.changes(root, {
|
|
350
379
|
limit: req.query.limit,
|
|
351
380
|
});
|
|
352
|
-
res.json({ root, changes });
|
|
381
|
+
res.json({ rootId, root, changes });
|
|
353
382
|
} catch (error) {
|
|
354
383
|
sendWorkspaceFileError(res, error);
|
|
355
384
|
}
|
|
@@ -357,12 +386,82 @@ function createWorkspaceFileRouter(agentManager, fileService) {
|
|
|
357
386
|
|
|
358
387
|
router.get('/branch', async (req, res) => {
|
|
359
388
|
try {
|
|
360
|
-
|
|
389
|
+
const rootRef = workspaceRef(req.query);
|
|
390
|
+
if (isGlobalWorkspaceFilesAgentId(rootRef)) {
|
|
361
391
|
throw new WorkspaceFileError('global files do not support git branches', 403);
|
|
362
392
|
}
|
|
363
|
-
const root =
|
|
393
|
+
const { root, rootId } = resolveRequestRoot(req.query);
|
|
364
394
|
const branch = await fileService.gitBranch(root);
|
|
365
|
-
res.json({ root, branch });
|
|
395
|
+
res.json({ rootId, root, branch });
|
|
396
|
+
} catch (error) {
|
|
397
|
+
sendWorkspaceFileError(res, error);
|
|
398
|
+
}
|
|
399
|
+
});
|
|
400
|
+
|
|
401
|
+
router.get('/worktrees', async (req, res) => {
|
|
402
|
+
try {
|
|
403
|
+
const rootRef = workspaceRef(req.query);
|
|
404
|
+
if (isGlobalWorkspaceFilesAgentId(rootRef)) {
|
|
405
|
+
throw new WorkspaceFileError('global files do not support git worktrees', 403);
|
|
406
|
+
}
|
|
407
|
+
const { root, rootId } = resolveRequestRoot(req.query);
|
|
408
|
+
const info = await inspectGitWorktree(root, { cacheMs: 0 });
|
|
409
|
+
res.json({
|
|
410
|
+
rootId,
|
|
411
|
+
root,
|
|
412
|
+
worktrees: info
|
|
413
|
+
? {
|
|
414
|
+
isGitRepo: true,
|
|
415
|
+
commonDir: info.commonDir,
|
|
416
|
+
currentWorkspace: info.workspace,
|
|
417
|
+
mainWorkspace: info.mainWorkspace,
|
|
418
|
+
items: info.worktrees,
|
|
419
|
+
}
|
|
420
|
+
: {
|
|
421
|
+
isGitRepo: false,
|
|
422
|
+
commonDir: '',
|
|
423
|
+
currentWorkspace: root,
|
|
424
|
+
mainWorkspace: '',
|
|
425
|
+
items: [],
|
|
426
|
+
},
|
|
427
|
+
});
|
|
428
|
+
} catch (error) {
|
|
429
|
+
sendWorkspaceFileError(res, error);
|
|
430
|
+
}
|
|
431
|
+
});
|
|
432
|
+
|
|
433
|
+
router.get('/history', async (req, res) => {
|
|
434
|
+
try {
|
|
435
|
+
const rootRef = workspaceRef(req.query);
|
|
436
|
+
if (isGlobalWorkspaceFilesAgentId(rootRef)) {
|
|
437
|
+
throw new WorkspaceFileError('global files do not support git history', 403);
|
|
438
|
+
}
|
|
439
|
+
const { root, rootId } = resolveRequestRoot(req.query);
|
|
440
|
+
const history = await fileService.gitHistory(root, {
|
|
441
|
+
limit: req.query.limit,
|
|
442
|
+
skip: req.query.skip,
|
|
443
|
+
scope: req.query.scope,
|
|
444
|
+
});
|
|
445
|
+
res.json({ rootId, root, history });
|
|
446
|
+
} catch (error) {
|
|
447
|
+
sendWorkspaceFileError(res, error);
|
|
448
|
+
}
|
|
449
|
+
});
|
|
450
|
+
|
|
451
|
+
router.get('/history/changes', async (req, res) => {
|
|
452
|
+
try {
|
|
453
|
+
const rootRef = workspaceRef(req.query);
|
|
454
|
+
if (isGlobalWorkspaceFilesAgentId(rootRef)) {
|
|
455
|
+
throw new WorkspaceFileError('global files do not support git history', 403);
|
|
456
|
+
}
|
|
457
|
+
const { root, rootId } = resolveRequestRoot(req.query);
|
|
458
|
+
const changes = await fileService.gitHistoryChanges(
|
|
459
|
+
root,
|
|
460
|
+
req.query.commit,
|
|
461
|
+
req.query.parent,
|
|
462
|
+
{ limit: req.query.limit }
|
|
463
|
+
);
|
|
464
|
+
res.json({ rootId, root, changes });
|
|
366
465
|
} catch (error) {
|
|
367
466
|
sendWorkspaceFileError(res, error);
|
|
368
467
|
}
|
|
@@ -370,17 +469,18 @@ function createWorkspaceFileRouter(agentManager, fileService) {
|
|
|
370
469
|
|
|
371
470
|
router.get('/line-changes', async (req, res) => {
|
|
372
471
|
try {
|
|
373
|
-
|
|
472
|
+
const rootRef = workspaceRef(req.query);
|
|
473
|
+
if (isGlobalWorkspaceFilesAgentId(rootRef)) {
|
|
374
474
|
assertGlobalWorkspacePathAllowed(agentManager, req.query.path || '');
|
|
375
475
|
}
|
|
376
|
-
const root =
|
|
476
|
+
const { root, rootId } = resolveRequestRoot(req.query);
|
|
377
477
|
const changes = await fileService.lineChanges(
|
|
378
478
|
root,
|
|
379
479
|
req.query.path || '',
|
|
380
480
|
req.query.lineNumber,
|
|
381
481
|
req.query.mode || 'working'
|
|
382
482
|
);
|
|
383
|
-
res.json({ root, changes });
|
|
483
|
+
res.json({ rootId, root, changes });
|
|
384
484
|
} catch (error) {
|
|
385
485
|
sendWorkspaceFileError(res, error);
|
|
386
486
|
}
|
|
@@ -388,12 +488,13 @@ function createWorkspaceFileRouter(agentManager, fileService) {
|
|
|
388
488
|
|
|
389
489
|
router.get('/blame', async (req, res) => {
|
|
390
490
|
try {
|
|
391
|
-
|
|
491
|
+
const rootRef = workspaceRef(req.query);
|
|
492
|
+
if (isGlobalWorkspaceFilesAgentId(rootRef)) {
|
|
392
493
|
assertGlobalWorkspacePathAllowed(agentManager, req.query.path || '');
|
|
393
494
|
}
|
|
394
|
-
const root =
|
|
495
|
+
const { root, rootId } = resolveRequestRoot(req.query);
|
|
395
496
|
const blame = await fileService.blame(root, req.query.path || '');
|
|
396
|
-
res.json({ root, blame });
|
|
497
|
+
res.json({ rootId, root, blame });
|
|
397
498
|
} catch (error) {
|
|
398
499
|
sendWorkspaceFileError(res, error);
|
|
399
500
|
}
|
|
@@ -401,16 +502,17 @@ function createWorkspaceFileRouter(agentManager, fileService) {
|
|
|
401
502
|
|
|
402
503
|
router.get('/blame-capability', async (req, res) => {
|
|
403
504
|
try {
|
|
404
|
-
|
|
505
|
+
const rootRef = workspaceRef(req.query);
|
|
506
|
+
if (isGlobalWorkspaceFilesAgentId(rootRef)) {
|
|
405
507
|
assertGlobalWorkspacePathAllowed(agentManager, req.query.path || '');
|
|
406
508
|
}
|
|
407
|
-
const root =
|
|
509
|
+
const { root, rootId } = resolveRequestRoot(req.query);
|
|
408
510
|
const capability = await fileService.blameCapability(
|
|
409
511
|
root,
|
|
410
512
|
req.query.path || '',
|
|
411
|
-
readOptionsForAgent(agentManager,
|
|
513
|
+
readOptionsForAgent(agentManager, rootRef)
|
|
412
514
|
);
|
|
413
|
-
res.json({ root, capability });
|
|
515
|
+
res.json({ rootId, root, capability });
|
|
414
516
|
} catch (error) {
|
|
415
517
|
sendWorkspaceFileError(res, error);
|
|
416
518
|
}
|
|
@@ -422,10 +524,14 @@ function createWorkspaceFileRouter(agentManager, fileService) {
|
|
|
422
524
|
module.exports = {
|
|
423
525
|
GLOBAL_WORKSPACE_FILES_AGENT_ID,
|
|
424
526
|
GLOBAL_WORKSPACE_FILES_ROOT,
|
|
527
|
+
PROJECT_FILES_WORKSPACE_PREFIX,
|
|
528
|
+
PROJECT_FILES_AGENT_PREFIX: PROJECT_FILES_WORKSPACE_PREFIX,
|
|
425
529
|
assertGlobalWorkspacePathAllowed,
|
|
426
530
|
createWorkspaceFileRouter,
|
|
427
531
|
globalWorkspaceAllowedRoots,
|
|
428
532
|
isGlobalWorkspaceFilesAgentId,
|
|
533
|
+
projectWorkspaceFromFilesId,
|
|
534
|
+
projectWorkspaceFromFilesAgentId: projectWorkspaceFromFilesId,
|
|
429
535
|
resolveWorkspaceRoot,
|
|
430
536
|
sendWorkspaceFileError,
|
|
431
537
|
};
|