@covro/covro 0.2.0-beta.1 → 0.2.1-beta.1
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 +30 -11
- package/README.zh-CN.md +30 -11
- package/dist/cli/background.js +103 -0
- package/dist/cli/background.js.map +7 -0
- package/dist/cli/daemon.js +6 -1
- package/dist/cli/daemon.js.map +2 -2
- package/dist/cli/doctor.js +92 -0
- package/dist/cli/doctor.js.map +7 -0
- package/dist/cli/launch-environment.js +85 -0
- package/dist/cli/launch-environment.js.map +7 -0
- package/dist/cli/launchd.js +172 -0
- package/dist/cli/launchd.js.map +7 -0
- package/dist/cli/main.js +7 -35
- package/dist/cli/main.js.map +2 -2
- package/dist/cli/service.js +23 -49
- package/dist/cli/service.js.map +2 -2
- package/dist/codex/adapter.js +76 -2
- package/dist/codex/adapter.js.map +2 -2
- package/dist/drizzle/0005_session_workspaces.sql +24 -0
- package/dist/drizzle/meta/0005_snapshot.json +480 -0
- package/dist/drizzle/meta/_journal.json +7 -0
- package/dist/server/auth.js +1 -1
- package/dist/server/auth.js.map +2 -2
- package/dist/server/db/operations.js +14 -1
- package/dist/server/db/operations.js.map +2 -2
- package/dist/server/db/schema.js +9 -1
- package/dist/server/db/schema.js.map +2 -2
- package/dist/server/http.js +46 -0
- package/dist/server/http.js.map +2 -2
- package/dist/server/local-files.js +2 -2
- package/dist/server/local-files.js.map +2 -2
- package/dist/server/main.js +1 -0
- package/dist/server/main.js.map +2 -2
- package/dist/server/service.js +111 -51
- package/dist/server/service.js.map +2 -2
- package/dist/server/workspaces.js +138 -0
- package/dist/server/workspaces.js.map +7 -0
- package/dist/shared/workspace-scope.js +9 -0
- package/dist/shared/workspace-scope.js.map +7 -0
- package/dist/update-worker.cjs +419 -168
- package/dist/updates/worker.js +7 -61
- package/dist/updates/worker.js.map +2 -2
- package/dist/web/assets/abnfDiagram-WEYOIA3C-B3om3w2y.js +1 -0
- package/dist/web/assets/arc-hdxlL3Om.js +1 -0
- package/dist/web/assets/architecture-WOLXFQ4H-FZOiLQD-.js +1 -0
- package/dist/web/assets/architectureDiagram-ZHVYKT53-DjatMTKS.js +36 -0
- package/dist/web/assets/array-BifhSqXX.js +1 -0
- package/dist/web/assets/blockDiagram-RJAE7QA3-Bu6LTv0p.js +140 -0
- package/dist/web/assets/c4Diagram-XDPPPE54-BGFHYP5H.js +38 -0
- package/dist/web/assets/channel-DmN_FTbE.js +1 -0
- package/dist/web/assets/chunk-2E7WHVMD-k8528aQZ.js +88 -0
- package/dist/web/assets/chunk-2Q5K7J3B-C1jixKkw.js +1 -0
- package/dist/web/assets/chunk-4EMNKR2T-CCkntZuJ.js +1 -0
- package/dist/web/assets/chunk-55XCU76K-Br-Kelsg.js +1 -0
- package/dist/web/assets/chunk-5VM5RSS4-ZNzvKenW.js +15 -0
- package/dist/web/assets/chunk-7S6XSTFO-C3W_8ji8.js +2 -0
- package/dist/web/assets/chunk-AQDTSKZB-DZQV76Iu.js +72 -0
- package/dist/web/assets/chunk-DSNLSHJZ-BRfUxqaq.js +129 -0
- package/dist/web/assets/chunk-DUW6YSOI-Dgx8z5s3.js +1 -0
- package/dist/web/assets/chunk-HAGFOTED-BD5jLT7f.js +62 -0
- package/dist/web/assets/chunk-HNE4Q36P-iJGu5Gi6.js +1 -0
- package/dist/web/assets/chunk-J5ZVWO5B-oDCd-gWO.js +1 -0
- package/dist/web/assets/chunk-JWPE2WC7-DVXcaiue.js +1 -0
- package/dist/web/assets/chunk-NGNAAXSQ-C2Wy9Xmz.js +136 -0
- package/dist/web/assets/chunk-NQOKRVIF-D-yS8zxU.js +213 -0
- package/dist/web/assets/chunk-UHW7PFBP-Dnu0oPmA.js +1 -0
- package/dist/web/assets/chunk-VQ5W62QI-CzZ8zHjp.js +1 -0
- package/dist/web/assets/chunk-WQQL4CDK-Cu4nebs6.js +10 -0
- package/dist/web/assets/chunk-XXDRQBXY-DgtjWyl3.js +1 -0
- package/dist/web/assets/chunk-Y2CYZVJY-DsF7k-Jl.js +1 -0
- package/dist/web/assets/chunk-YZO67HFG-CGApWL5X.js +1 -0
- package/dist/web/assets/chunk-ZAUX2LTX-D5DhZJoE.js +1 -0
- package/dist/web/assets/chunk-ZCHMQSOR-BC0ITpUM.js +2 -0
- package/dist/web/assets/classDiagram-v2-4MYWR3UA-Budj7a3U.js +217 -0
- package/dist/web/assets/cose-bilkent-JH36ORCC-DIHNUNHx.js +1 -0
- package/dist/web/assets/cynefin-EF2NZ3EQ-BRluapvc.js +1 -0
- package/dist/web/assets/cynefinDiagram-WF24UDRP-CATBOOnO.js +62 -0
- package/dist/web/assets/cytoscape.esm-BegBNzOV.js +321 -0
- package/dist/web/assets/dagre-NK4GIZVX-B8DaH0ao.js +4 -0
- package/dist/web/assets/defaultLocale-BFoDCU3G.js +1 -0
- package/dist/web/assets/diagram-CBK3MWTC-C6HHNKIg.js +24 -0
- package/dist/web/assets/diagram-I5XIIV2D-BftOP4e2.js +24 -0
- package/dist/web/assets/diagram-ODIL2UCW-CCO6wwvD.js +30 -0
- package/dist/web/assets/diagram-PFBEJUXE-y6w_y2UD.js +3 -0
- package/dist/web/assets/diagram-RJA3I4VV-gsgGHcdI.js +41 -0
- package/dist/web/assets/diagram-TYUNFSNR-BFuCHkk0.js +200 -0
- package/dist/web/assets/dist-DLTSHXmk.js +1 -0
- package/dist/web/assets/ebnfDiagram-V4P3TTPT-B6_sRdDs.js +1 -0
- package/dist/web/assets/elk-QONTBCTQ-BtVt8qRm.js +27 -0
- package/dist/web/assets/erDiagram-KYQIGQAA-rgfheTvQ.js +99 -0
- package/dist/web/assets/eventmodeling-K75KTNOO-r9vnCD2d.js +1 -0
- package/dist/web/assets/flowDiagram-IUGLLWBU-CiGVEt1a.js +1 -0
- package/dist/web/assets/ganttDiagram-R7GPNTJY-CJFYABWz.js +292 -0
- package/dist/web/assets/gitGraph-VSP46ZUC-ccOuonLa.js +1 -0
- package/dist/web/assets/gitGraphDiagram-PDYL7XHY-e5kzwjY2.js +106 -0
- package/dist/web/assets/graphlib-DS17s2tU.js +1 -0
- package/dist/web/assets/index-B4RJQRiD.js +142 -0
- package/dist/web/assets/index-DaMLk_sl.css +1 -0
- package/dist/web/assets/info-OHQRW6UA-C8aOXyva.js +1 -0
- package/dist/web/assets/infoDiagram-MVDRKAX5-ztgQX_ha.js +2 -0
- package/dist/web/assets/init-C-OQMol4.js +1 -0
- package/dist/web/assets/ishikawaDiagram-HPEHV2GE-BPCGAT_m.js +70 -0
- package/dist/web/assets/journeyDiagram-BOSHW7O7-BoiZL6al.js +139 -0
- package/dist/web/assets/kanban-definition-FNSA4HC5-BDcWGn8E.js +89 -0
- package/dist/web/assets/katex-B_JcIwqj.js +257 -0
- package/dist/web/assets/line-nGnhrXrD.js +1 -0
- package/dist/web/assets/linear-DiAwgAUj.js +1 -0
- package/dist/web/assets/mermaid-parser.core-DAzSIS8w.js +7 -0
- package/dist/web/assets/mermaid.core-CL_7HT-R.js +44 -0
- package/dist/web/assets/mindmap-definition-6OY57UG3-Bp7IBNG5.js +98 -0
- package/dist/web/assets/ordinal-BDEzSJ7C.js +1 -0
- package/dist/web/assets/packet-JDAUHWVQ-C-1SMzOZ.js +1 -0
- package/dist/web/assets/path-fybaL0A-.js +1 -0
- package/dist/web/assets/pegDiagram-ECIUJNBX-Be1uqoNt.js +1 -0
- package/dist/web/assets/pie-XZMESJXO-Dq5h06i3.js +1 -0
- package/dist/web/assets/pieDiagram-GNMPY74T-CN5ZGBXZ.js +39 -0
- package/dist/web/assets/quadrantDiagram-D57K4MC3-C7sLKW4U.js +7 -0
- package/dist/web/assets/radar-ABXABTNO-DVVIp0T9.js +1 -0
- package/dist/web/assets/railroad-I3PHUGI6-CxxBEQri.js +1 -0
- package/dist/web/assets/railroad-abnf-LNEFI6M7-CM7hMwVK.js +1 -0
- package/dist/web/assets/railroad-ebnf-DWYD2UWJ-KUafccMV.js +1 -0
- package/dist/web/assets/railroad-peg-JR7BVNR7-5BmypBHR.js +1 -0
- package/dist/web/assets/railroadDiagram-Y72NRBL7-DHAJk4_o.js +1 -0
- package/dist/web/assets/requirementDiagram-EQYSW5RA-y9zEf_QB.js +84 -0
- package/dist/web/assets/rough.esm-Dy-Kn_BL.js +1 -0
- package/dist/web/assets/sankeyDiagram-SY24JHO5-C6pWLy-I.js +40 -0
- package/dist/web/assets/sequenceDiagram-BLEHNHQU-Cj-Vitmg.js +169 -0
- package/dist/web/assets/sizeCapture-INFHLROL-3uHXrFqA.js +1 -0
- package/dist/web/assets/src-DbSGPN_8.js +1 -0
- package/dist/web/assets/stateDiagram-v2-A6IESKWZ-RbKYMXiF.js +291 -0
- package/dist/web/assets/swimlanes-B3ENRKFQ-7bBxQYlO.js +1 -0
- package/dist/web/assets/swimlanesDiagram-AYQRV4AK-CfAECQ56.js +8 -0
- package/dist/web/assets/timeline-definition-JXYKGLGB-5mrf5Irf.js +120 -0
- package/dist/web/assets/treeView-D4JQ5SDB-DP63r0Rt.js +1 -0
- package/dist/web/assets/treemap-SAJKECNS-ChWSB650.js +1 -0
- package/dist/web/assets/usecaseDiagram-WPOLBNNX-_OObMcZu.js +328 -0
- package/dist/web/assets/vennDiagram-Q3Y3IDCW-DO1Tmzxv.js +34 -0
- package/dist/web/assets/wardley-7MLQ67FV-DcDqvVeS.js +1 -0
- package/dist/web/assets/wardleyDiagram-IVAIDKWA-i-x65JPs.js +78 -0
- package/dist/web/assets/xychartDiagram-4URNHDU4-Dlk1JIjO.js +7 -0
- package/dist/web/index.html +2 -2
- package/package.json +6 -3
- package/dist/web/assets/index-Dr0Qkixk.css +0 -1
- package/dist/web/assets/index-z5RGzL8u.js +0 -138
package/dist/server/service.js
CHANGED
|
@@ -1,5 +1,8 @@
|
|
|
1
1
|
import { ShellService } from "./shell.js";
|
|
2
2
|
import { realpathSync } from "node:fs";
|
|
3
|
+
import { cp } from "node:fs/promises";
|
|
4
|
+
import { WorkspaceRegistry } from "./workspaces.js";
|
|
5
|
+
import { UNASSIGNED_SCOPE } from "../shared/workspace-scope.js";
|
|
3
6
|
import { AppError } from "../shared/errors.js";
|
|
4
7
|
import { threadNameError } from "../shared/thread-name.js";
|
|
5
8
|
import {
|
|
@@ -24,6 +27,11 @@ class GatewayService {
|
|
|
24
27
|
this.store = options.store;
|
|
25
28
|
this.turnUsage = new TurnUsageStore(this.store.database);
|
|
26
29
|
this.projects = options.projects;
|
|
30
|
+
this.workspaces = new WorkspaceRegistry(
|
|
31
|
+
this.store.database,
|
|
32
|
+
this.projects,
|
|
33
|
+
options.workspacesRoot
|
|
34
|
+
);
|
|
27
35
|
this.codex = options.codex;
|
|
28
36
|
this.shell = new ShellService(this.codex);
|
|
29
37
|
this.events = new EventHub(options.limits?.eventBytes);
|
|
@@ -39,6 +47,7 @@ class GatewayService {
|
|
|
39
47
|
store;
|
|
40
48
|
turnUsage;
|
|
41
49
|
projects;
|
|
50
|
+
workspaces;
|
|
42
51
|
codex;
|
|
43
52
|
removeListener;
|
|
44
53
|
fileTemporaryRoots = temporaryFileRoots();
|
|
@@ -47,6 +56,7 @@ class GatewayService {
|
|
|
47
56
|
currentEpoch;
|
|
48
57
|
confirmedCompatibility;
|
|
49
58
|
archiveChanges = /* @__PURE__ */ new Set();
|
|
59
|
+
archiveRevisions = /* @__PURE__ */ new Map();
|
|
50
60
|
turnBaselines = /* @__PURE__ */ new Map();
|
|
51
61
|
renameChanges = /* @__PURE__ */ new Set();
|
|
52
62
|
reads = /* @__PURE__ */ new Map();
|
|
@@ -99,6 +109,7 @@ class GatewayService {
|
|
|
99
109
|
return { ready: true };
|
|
100
110
|
}
|
|
101
111
|
addProject(input) {
|
|
112
|
+
this.workspaces.assertProjectDirectory(input.path);
|
|
102
113
|
const project = this.projects.add(input);
|
|
103
114
|
this.events.publishProjects(this.projects.list());
|
|
104
115
|
return project;
|
|
@@ -177,12 +188,12 @@ class GatewayService {
|
|
|
177
188
|
});
|
|
178
189
|
}
|
|
179
190
|
async readRuntimeSettings(projectId) {
|
|
180
|
-
const
|
|
181
|
-
return this.codex.readRuntimeSettings(
|
|
191
|
+
const cwd = projectId === null ? this.workspaces.defaultsDirectory() : this.projects.get(projectId).canonicalRoot;
|
|
192
|
+
return this.codex.readRuntimeSettings(cwd);
|
|
182
193
|
}
|
|
183
194
|
async saveModelDefaults(projectId, input) {
|
|
184
|
-
return this.perform("config.modelDefaults", projectId, input, async () => {
|
|
185
|
-
this.projects.get(projectId);
|
|
195
|
+
return this.perform("config.modelDefaults", projectId ?? UNASSIGNED_SCOPE, input, async () => {
|
|
196
|
+
if (projectId !== null) this.projects.get(projectId);
|
|
186
197
|
this.ensureReady();
|
|
187
198
|
this.ensureCompatibilityConfirmed();
|
|
188
199
|
this.store.associateProject(input.clientOperationId, projectId);
|
|
@@ -191,28 +202,43 @@ class GatewayService {
|
|
|
191
202
|
});
|
|
192
203
|
}
|
|
193
204
|
async listThreads(projectId, cursor, archived = false) {
|
|
194
|
-
const project = this.projects.get(projectId);
|
|
205
|
+
const project = projectId === null ? void 0 : this.projects.get(projectId);
|
|
206
|
+
const directories = project ? project.canonicalRoot : this.workspaces.allocations().map((row) => row.canonicalRoot);
|
|
195
207
|
const titleReads = new Map(
|
|
196
208
|
this.projection.values().filter((thread) => thread.projectId === projectId).map((thread) => [thread.id, this.beginTitleRead(thread.id)])
|
|
197
209
|
);
|
|
198
|
-
const
|
|
199
|
-
|
|
210
|
+
const archiveReads = new Map(
|
|
211
|
+
this.projection.values().filter((thread) => thread.projectId === projectId).map((thread) => [thread.id, this.archiveRevisions.get(thread.id) ?? 0])
|
|
212
|
+
);
|
|
213
|
+
const page = Array.isArray(directories) && !directories.length ? { data: [], nextCursor: null } : await this.codex.listThreads(directories, cursor, archived);
|
|
214
|
+
if (projectId !== null) this.projects.get(projectId);
|
|
200
215
|
const data = [];
|
|
201
216
|
for (const thread of page.data) {
|
|
202
217
|
try {
|
|
218
|
+
if (projectId === null && thread.source === "appServer" && page.data.filter((entry) => entry.cwd === thread.cwd).length === 1) {
|
|
219
|
+
const operationId = this.workspaces.recoverCreation(thread.id, thread.cwd);
|
|
220
|
+
if (operationId && !this.inFlightOperations.has(operationId)) {
|
|
221
|
+
const recovered = this.store.confirmWorkspaceCreation(operationId, thread.id);
|
|
222
|
+
this.events.publish("operation", this.operation(recovered));
|
|
223
|
+
}
|
|
224
|
+
}
|
|
203
225
|
const normalized = this.normalize(thread, projectId);
|
|
204
226
|
normalized.hasMessages ??= true;
|
|
205
227
|
const cached = this.projection.get(thread.id);
|
|
206
228
|
if (cached && normalized.hasMessages) cached.hasMessages = true;
|
|
207
229
|
const titleRead = titleReads.get(thread.id);
|
|
208
|
-
if (cached &&
|
|
209
|
-
|
|
210
|
-
|
|
230
|
+
if (cached && archiveReads.get(thread.id) === (this.archiveRevisions.get(thread.id) ?? 0))
|
|
231
|
+
this.updateArchiveState(thread.id, Boolean(normalized.archived));
|
|
232
|
+
if (cached && titleRead !== void 0)
|
|
211
233
|
this.updateThreadTitle(thread.id, normalized.title, titleRead);
|
|
212
|
-
}
|
|
213
234
|
data.push(this.threadView(cached ?? normalized));
|
|
214
235
|
} catch (error) {
|
|
215
|
-
if (!(error instanceof AppError) || ![
|
|
236
|
+
if (!(error instanceof AppError) || ![
|
|
237
|
+
"PROJECT_OWNERSHIP",
|
|
238
|
+
"PROJECT_UNAVAILABLE",
|
|
239
|
+
"PROJECT_NOT_FOUND",
|
|
240
|
+
"WORKSPACE_UNAVAILABLE"
|
|
241
|
+
].includes(error.code))
|
|
216
242
|
throw error;
|
|
217
243
|
}
|
|
218
244
|
}
|
|
@@ -239,7 +265,7 @@ class GatewayService {
|
|
|
239
265
|
(thread) => (thread.managed || this.store.activeForThread(thread.id)) && thread.projectId === projectId && this.historyVisible(thread)
|
|
240
266
|
).sort((a, b) => (b.updatedAt ?? 0) - (a.updatedAt ?? 0)).slice(0, 30);
|
|
241
267
|
for (const thread of managed) {
|
|
242
|
-
this.
|
|
268
|
+
this.workspaces.assertOwnership(projectId, thread.cwd, thread.id);
|
|
243
269
|
if (!data.some((entry) => entry.id === thread.id)) data.unshift(this.threadView(thread));
|
|
244
270
|
}
|
|
245
271
|
}
|
|
@@ -264,7 +290,7 @@ class GatewayService {
|
|
|
264
290
|
this.archiveChanges.add(threadId);
|
|
265
291
|
try {
|
|
266
292
|
const actual = await this.codex.readThreadMetadata(threadId);
|
|
267
|
-
this.
|
|
293
|
+
this.workspaces.assertOwnership(thread.projectId, actual.cwd, thread.id);
|
|
268
294
|
const current = this.projection.get(threadId);
|
|
269
295
|
if (input.archived && (this.resumes.has(threadId) || this.store.activeForThread(threadId) || current.managed && current.status === "unknown" || [actual, current].some(
|
|
270
296
|
(entry) => isActive(entry.status) || entry.turns.some((turn) => isActive(turn.status) || turn.status === "unknown") || entry.actions.some((action) => ["pending", "responding"].includes(action.status))
|
|
@@ -328,7 +354,7 @@ class GatewayService {
|
|
|
328
354
|
actual = await this.codex.readThreadMetadata(threadId);
|
|
329
355
|
if (actual.id !== threadId || this.codex.epoch !== epoch || !this.codex.ready)
|
|
330
356
|
throw new Error("\u4F1A\u8BDD\u6216 Codex \u5B9E\u4F8B\u5DF2\u53D8\u5316");
|
|
331
|
-
this.
|
|
357
|
+
this.workspaces.assertOwnership(thread.projectId, actual.cwd, thread.id);
|
|
332
358
|
} catch (cause) {
|
|
333
359
|
throw new Error("\u91CD\u547D\u540D\u5DF2\u53D1\u9001\uFF0C\u4F46\u65E0\u6CD5\u786E\u8BA4\u4F1A\u8BDD\u540D\u79F0\uFF0C\u8BF7\u6838\u5BF9\u63D0\u4EA4", { cause });
|
|
334
360
|
}
|
|
@@ -376,6 +402,7 @@ class GatewayService {
|
|
|
376
402
|
const thread = this.projection.get(threadId);
|
|
377
403
|
if (!thread || Boolean(thread.archived) === archived) return;
|
|
378
404
|
thread.archived = archived;
|
|
405
|
+
this.archiveRevisions.set(threadId, (this.archiveRevisions.get(threadId) ?? 0) + 1);
|
|
379
406
|
if (archived) this.releaseThreadOwnership(threadId);
|
|
380
407
|
this.projection.touch(threadId);
|
|
381
408
|
if (!this.archiveChanges.has(threadId)) this.publishThread(threadId);
|
|
@@ -383,9 +410,9 @@ class GatewayService {
|
|
|
383
410
|
async getThreadMetadata(threadId) {
|
|
384
411
|
const cached = this.projection.get(threadId);
|
|
385
412
|
if (cached) {
|
|
386
|
-
this.
|
|
413
|
+
this.workspaces.assertOwnership(cached.projectId, cached.cwd, cached.id);
|
|
387
414
|
if (this.codex.ready) await this.refreshThreadMetadata(threadId);
|
|
388
|
-
this.
|
|
415
|
+
this.workspaces.assertOwnership(cached.projectId, cached.cwd, cached.id);
|
|
389
416
|
return { ...this.threadView(cached), turns: [], actions: [] };
|
|
390
417
|
}
|
|
391
418
|
const metadata = this.threadView(this.normalize(await this.codex.readThreadMetadata(threadId)));
|
|
@@ -396,9 +423,21 @@ class GatewayService {
|
|
|
396
423
|
await this.loadThread(threadId);
|
|
397
424
|
if (cached && this.codex.ready) await this.refreshThreadMetadata(threadId);
|
|
398
425
|
const thread = this.projection.get(threadId);
|
|
399
|
-
this.
|
|
426
|
+
this.workspaces.assertOwnership(thread.projectId, thread.cwd, thread.id);
|
|
400
427
|
return this.threadView(thread);
|
|
401
428
|
}
|
|
429
|
+
async getThreadHistory(threadId, cursor) {
|
|
430
|
+
if (!cursor || cursor.length > 8192)
|
|
431
|
+
throw new AppError("VALIDATION_FAILED", "Missing or invalid history cursor", 400);
|
|
432
|
+
this.ensureReady();
|
|
433
|
+
const metadata = this.normalize(await this.codex.readThreadMetadata(threadId));
|
|
434
|
+
this.workspaces.assertOwnership(metadata.projectId, metadata.cwd, metadata.id);
|
|
435
|
+
if (!this.codex.readThreadPage)
|
|
436
|
+
throw new AppError("HISTORY_UNAVAILABLE", "\u5F53\u524D Codex \u4E0D\u652F\u6301\u5386\u53F2\u5206\u9875", 409);
|
|
437
|
+
const page = this.normalize(await this.codex.readThreadPage(threadId, cursor));
|
|
438
|
+
this.workspaces.assertOwnership(page.projectId, page.cwd, page.id);
|
|
439
|
+
return page;
|
|
440
|
+
}
|
|
402
441
|
async exportThread(threadId) {
|
|
403
442
|
this.ensureReady();
|
|
404
443
|
const epoch = this.codex.epoch;
|
|
@@ -415,8 +454,8 @@ class GatewayService {
|
|
|
415
454
|
409
|
|
416
455
|
);
|
|
417
456
|
const link = this.store.getThreadLink(threadId);
|
|
418
|
-
if (link) this.
|
|
419
|
-
else this.
|
|
457
|
+
if (link) this.workspaces.assertOwnership(link.projectId, thread.cwd, thread.id);
|
|
458
|
+
else this.workspaces.findOwner(thread.cwd, thread.id);
|
|
420
459
|
return renderThreadExport(thread, {
|
|
421
460
|
executionUncertain: Boolean(this.store.activeForThread(threadId)),
|
|
422
461
|
historyChanged: revision !== this.projection.revision(threadId) || thread.updatedAt !== latest.updatedAt || thread.status !== latest.status
|
|
@@ -437,33 +476,38 @@ class GatewayService {
|
|
|
437
476
|
this.normalize(await this.codex.readThreadMetadata(threadId));
|
|
438
477
|
link = this.store.getThreadLink(threadId);
|
|
439
478
|
}
|
|
440
|
-
const
|
|
479
|
+
const ownership = this.workspaces.assertOwnership(link.projectId, link.cwd, threadId);
|
|
480
|
+
const { cwd } = ownership;
|
|
441
481
|
const file = await read(path, {
|
|
442
482
|
cwd,
|
|
483
|
+
excludedRoots: this.workspaces.allocations().filter((row) => row.threadId !== threadId).map((row) => row.canonicalRoot),
|
|
443
484
|
roots: [
|
|
444
|
-
{ canonicalRoot:
|
|
445
|
-
...this.fileTemporaryRoots
|
|
485
|
+
{ canonicalRoot: ownership.canonicalRoot, aliases: [ownership.rootPath] },
|
|
486
|
+
...link.projectId === null ? [] : this.fileTemporaryRoots
|
|
446
487
|
]
|
|
447
488
|
});
|
|
448
|
-
this.
|
|
489
|
+
this.workspaces.assertOwnership(link.projectId, link.cwd, threadId);
|
|
449
490
|
return file;
|
|
450
491
|
}
|
|
451
492
|
async loadThread(threadId) {
|
|
452
493
|
const cached = this.projection.get(threadId);
|
|
453
494
|
if (cached) {
|
|
454
|
-
this.
|
|
495
|
+
this.workspaces.assertOwnership(cached.projectId, cached.cwd, cached.id);
|
|
455
496
|
const marker = this.store.activeForThread(threadId);
|
|
456
497
|
if (marker?.turnId && this.codex.ready) {
|
|
457
498
|
await this.refreshExecution(marker);
|
|
458
499
|
return this.threadView(cached);
|
|
459
500
|
}
|
|
460
|
-
if (cached.status !== "unknown" || !this.codex.ready)
|
|
501
|
+
if ((cached.managed || isActive(cached.status)) && cached.status !== "unknown" || !this.codex.ready)
|
|
502
|
+
return this.threadView(cached);
|
|
461
503
|
}
|
|
462
504
|
const existing = this.reads.get(threadId);
|
|
463
505
|
if (existing) return this.threadView(await existing);
|
|
464
506
|
const revision = this.projection.revision(threadId);
|
|
465
507
|
const loading = (async () => {
|
|
466
|
-
const thread = this.normalize(
|
|
508
|
+
const thread = this.normalize(
|
|
509
|
+
await (this.codex.readThreadPage?.(threadId) ?? this.codex.readThread(threadId))
|
|
510
|
+
);
|
|
467
511
|
const current = this.projection.get(threadId);
|
|
468
512
|
const preserveCurrent = current && (cached === void 0 || this.projection.revision(threadId) !== revision);
|
|
469
513
|
const result = preserveCurrent ? current : this.projection.thread(thread);
|
|
@@ -483,18 +527,26 @@ class GatewayService {
|
|
|
483
527
|
}
|
|
484
528
|
}
|
|
485
529
|
async createThread(projectId, input) {
|
|
486
|
-
return this.perform("thread.create", projectId, input, async () => {
|
|
487
|
-
const project = this.projects.get(projectId);
|
|
530
|
+
return this.perform("thread.create", projectId ?? UNASSIGNED_SCOPE, input, async () => {
|
|
488
531
|
this.ensureReady();
|
|
489
532
|
this.ensureCompatibilityConfirmed();
|
|
533
|
+
const cwd = projectId === null ? this.workspaces.allocate(input.clientOperationId).cwd : this.projects.get(projectId).canonicalRoot;
|
|
534
|
+
if (projectId !== null) this.workspaces.assertProjectDirectory(cwd);
|
|
490
535
|
this.store.associateProject(input.clientOperationId, projectId);
|
|
491
536
|
this.store.dispatch(input.clientOperationId);
|
|
492
|
-
this.pendingThreadCreations.set(input.clientOperationId,
|
|
537
|
+
this.pendingThreadCreations.set(input.clientOperationId, cwd);
|
|
493
538
|
const epoch = this.codex.epoch;
|
|
494
539
|
const settingsSequence = this.runtimeSettingsSequence;
|
|
495
|
-
const thread = await this.codex.createThread(
|
|
540
|
+
const thread = await this.codex.createThread(cwd, input.settings);
|
|
496
541
|
if (this.codex.epoch !== epoch || !this.codex.ready)
|
|
497
542
|
throw new Error("\u521B\u5EFA\u8BF7\u6C42\u5DF2\u53D1\u9001\uFF0C\u4F46 Codex \u5B9E\u4F8B\u5DF2\u53D8\u5316\uFF0C\u8BF7\u6838\u5BF9\u5386\u53F2");
|
|
543
|
+
if (projectId === null) {
|
|
544
|
+
try {
|
|
545
|
+
this.workspaces.bind(input.clientOperationId, thread.id, thread.cwd);
|
|
546
|
+
} catch (cause) {
|
|
547
|
+
throw new Error("\u521B\u5EFA\u8BF7\u6C42\u5DF2\u53D1\u9001\uFF0C\u4F46\u8FD4\u56DE\u7684\u5DE5\u4F5C\u76EE\u5F55\u65E0\u6CD5\u786E\u8BA4\uFF0C\u8BF7\u6838\u5BF9\u5386\u53F2", { cause });
|
|
548
|
+
}
|
|
549
|
+
}
|
|
498
550
|
const normalized = this.normalize(thread, projectId, true);
|
|
499
551
|
this.projection.thread(
|
|
500
552
|
normalized,
|
|
@@ -511,7 +563,7 @@ class GatewayService {
|
|
|
511
563
|
this.ensureReady();
|
|
512
564
|
this.ensureArchiveAvailable(thread);
|
|
513
565
|
if (thread.archived) throw new AppError("THREAD_ARCHIVED", "\u8BF7\u5148\u53D6\u6D88\u5F52\u6863\u540E\u518D\u6062\u590D\u4F1A\u8BDD", 409);
|
|
514
|
-
this.
|
|
566
|
+
this.workspaces.assertOwnership(thread.projectId, thread.cwd, thread.id);
|
|
515
567
|
this.store.associateProject(input.clientOperationId, thread.projectId);
|
|
516
568
|
const link = this.store.getThreadLink(threadId);
|
|
517
569
|
if (link?.managedEpoch === this.codex.epoch) {
|
|
@@ -525,7 +577,7 @@ class GatewayService {
|
|
|
525
577
|
operationId: marker.operationId,
|
|
526
578
|
threadId
|
|
527
579
|
});
|
|
528
|
-
const ownership = this.
|
|
580
|
+
const ownership = this.workspaces.assertOwnership(thread.projectId, thread.cwd, thread.id);
|
|
529
581
|
this.ensureCompatibilityConfirmed();
|
|
530
582
|
this.store.dispatch(input.clientOperationId);
|
|
531
583
|
let pending = this.resumes.get(threadId);
|
|
@@ -570,21 +622,29 @@ class GatewayService {
|
|
|
570
622
|
this.ensureReady();
|
|
571
623
|
const source = this.projection.get(threadId) ?? await this.codex.readThread(threadId);
|
|
572
624
|
const link = this.store.getThreadLink(threadId);
|
|
573
|
-
const ownership = link ? this.
|
|
625
|
+
const ownership = link ? this.workspaces.assertOwnership(link.projectId, source.cwd, threadId) : this.workspaces.findOwner(source.cwd, source.id);
|
|
574
626
|
this.ensureCompatibilityConfirmed();
|
|
575
627
|
const epoch = this.codex.epoch;
|
|
576
|
-
|
|
628
|
+
let forkCwd = ownership.cwd;
|
|
629
|
+
if (ownership.projectId === null) {
|
|
630
|
+
forkCwd = this.workspaces.allocate(input.clientOperationId).cwd;
|
|
631
|
+
await cp(ownership.cwd, forkCwd, { recursive: true, verbatimSymlinks: true });
|
|
632
|
+
this.workspaces.assertOwnership(null, source.cwd, source.id);
|
|
633
|
+
}
|
|
634
|
+
this.store.associateProject(input.clientOperationId, ownership.projectId);
|
|
577
635
|
this.store.dispatch(input.clientOperationId);
|
|
578
|
-
this.pendingThreadCreations.set(input.clientOperationId,
|
|
636
|
+
this.pendingThreadCreations.set(input.clientOperationId, forkCwd);
|
|
579
637
|
const settingsSequence = this.runtimeSettingsSequence;
|
|
580
|
-
const forked = await this.codex.forkThread(threadId,
|
|
638
|
+
const forked = await this.codex.forkThread(threadId, forkCwd, input.settings);
|
|
581
639
|
if (forked.id === threadId || this.codex.epoch !== epoch || !this.codex.ready)
|
|
582
640
|
throw new Error("\u5206\u53C9\u8BF7\u6C42\u5DF2\u53D1\u9001\uFF0C\u4F46\u8FD4\u56DE\u7684\u65B0\u4F1A\u8BDD\u65E0\u6CD5\u786E\u8BA4\uFF0C\u8BF7\u6838\u5BF9\u5386\u53F2");
|
|
583
641
|
let normalized;
|
|
584
642
|
try {
|
|
643
|
+
if (ownership.projectId === null)
|
|
644
|
+
this.workspaces.bind(input.clientOperationId, forked.id, forked.cwd);
|
|
585
645
|
normalized = this.normalize(
|
|
586
646
|
{ ...forked, forkedFromId: threadId },
|
|
587
|
-
ownership.
|
|
647
|
+
ownership.projectId,
|
|
588
648
|
true
|
|
589
649
|
);
|
|
590
650
|
} catch (error) {
|
|
@@ -603,7 +663,7 @@ class GatewayService {
|
|
|
603
663
|
return this.perform("turn.start", threadId, input, async () => {
|
|
604
664
|
const thread = await this.loadThread(threadId);
|
|
605
665
|
this.ensureManaged(thread);
|
|
606
|
-
const ownership = this.
|
|
666
|
+
const ownership = this.workspaces.assertOwnership(thread.projectId, thread.cwd, thread.id);
|
|
607
667
|
if (isActive(thread.status))
|
|
608
668
|
throw new AppError("THREAD_BUSY", "\u4F1A\u8BDD\u5DF2\u6709\u6D3B\u52A8\u4EFB\u52A1\uFF0C\u8BF7\u8FFD\u52A0\u8981\u6C42\u6216\u4E2D\u65AD", 409, { threadId });
|
|
609
669
|
this.ensureCompatibilityConfirmed();
|
|
@@ -636,7 +696,7 @@ class GatewayService {
|
|
|
636
696
|
return this.perform("thread.compact", threadId, input, async () => {
|
|
637
697
|
const thread = await this.loadThread(threadId);
|
|
638
698
|
this.ensureManaged(thread);
|
|
639
|
-
const ownership = this.
|
|
699
|
+
const ownership = this.workspaces.assertOwnership(thread.projectId, thread.cwd, thread.id);
|
|
640
700
|
if (isActive(thread.status) || thread.turns.some((turn) => isActive(turn.status)))
|
|
641
701
|
throw new AppError("THREAD_BUSY", "\u8BF7\u7B49\u5F85\u5F53\u524D\u4EFB\u52A1\u7ED3\u675F\u540E\u518D\u538B\u7F29\u4E0A\u4E0B\u6587", 409);
|
|
642
702
|
if (!thread.turns.length)
|
|
@@ -660,7 +720,7 @@ class GatewayService {
|
|
|
660
720
|
return this.perform("review.start", threadId, input, async () => {
|
|
661
721
|
const thread = await this.loadThread(threadId);
|
|
662
722
|
this.ensureManaged(thread);
|
|
663
|
-
const ownership = this.
|
|
723
|
+
const ownership = this.workspaces.assertOwnership(thread.projectId, thread.cwd, thread.id);
|
|
664
724
|
if (isActive(thread.status) || thread.turns.some((turn2) => isActive(turn2.status)))
|
|
665
725
|
throw new AppError("THREAD_BUSY", "\u4F1A\u8BDD\u5DF2\u6709\u6D3B\u52A8\u4EFB\u52A1\uFF0C\u7ED3\u675F\u540E\u624D\u80FD\u5F00\u59CB\u4EE3\u7801\u5BA1\u67E5", 409, {
|
|
666
726
|
threadId
|
|
@@ -759,8 +819,8 @@ class GatewayService {
|
|
|
759
819
|
try {
|
|
760
820
|
const actual = await this.codex.readThread(marker.threadId);
|
|
761
821
|
const link = this.store.getThreadLink(marker.threadId);
|
|
762
|
-
if (link) this.
|
|
763
|
-
else this.
|
|
822
|
+
if (link) this.workspaces.assertOwnership(link.projectId, actual.cwd, actual.id);
|
|
823
|
+
else this.workspaces.findOwner(actual.cwd, actual.id);
|
|
764
824
|
if (this.markerIsActive(actual, marker.turnId))
|
|
765
825
|
throw new AppError("THREAD_BUSY", "\u4E0A\u6E38\u4ECD\u62A5\u544A\u8BE5\u4EFB\u52A1\u6D3B\u52A8\uFF0C\u4E0D\u80FD\u89E3\u9664\u5360\u7528", 409, {
|
|
766
826
|
threadId: marker.threadId
|
|
@@ -855,14 +915,14 @@ class GatewayService {
|
|
|
855
915
|
}
|
|
856
916
|
normalize(thread, projectId, managed = false) {
|
|
857
917
|
const link = this.store.getThreadLink(thread.id);
|
|
858
|
-
const ownership = link ? this.
|
|
859
|
-
if (projectId && ownership.
|
|
918
|
+
const ownership = link ? this.workspaces.assertOwnership(link.projectId, thread.cwd, thread.id) : managed && projectId !== void 0 ? this.workspaces.assertOwnership(projectId, thread.cwd, thread.id) : this.workspaces.findOwner(thread.cwd, thread.id);
|
|
919
|
+
if (projectId !== void 0 && ownership.projectId !== projectId)
|
|
860
920
|
throw new AppError("PROJECT_OWNERSHIP", "\u4F1A\u8BDD\u5C5E\u4E8E\u5176\u4ED6\u9879\u76EE\uFF0C\u4E0D\u80FD\u6539\u53D8\u9879\u76EE\u5F52\u5C5E", 403);
|
|
861
921
|
const managedEpoch = thread.archived ? null : managed ? this.codex.epoch : link?.managedEpoch ?? null;
|
|
862
922
|
const result = {
|
|
863
923
|
...thread,
|
|
864
924
|
turns: this.turnUsage.restore(thread.id, thread.turns),
|
|
865
|
-
projectId: ownership.
|
|
925
|
+
projectId: ownership.projectId,
|
|
866
926
|
cwd: ownership.cwd,
|
|
867
927
|
managed: managedEpoch === this.codex.epoch,
|
|
868
928
|
resumeError: managed ? void 0 : this.projection.get(thread.id)?.resumeError,
|
|
@@ -872,7 +932,7 @@ class GatewayService {
|
|
|
872
932
|
};
|
|
873
933
|
this.store.linkThread({
|
|
874
934
|
threadId: thread.id,
|
|
875
|
-
projectId: ownership.
|
|
935
|
+
projectId: ownership.projectId,
|
|
876
936
|
cwd: ownership.cwd,
|
|
877
937
|
source: thread.source,
|
|
878
938
|
managedEpoch
|
|
@@ -907,7 +967,7 @@ class GatewayService {
|
|
|
907
967
|
if (!this.codex.ready || this.codex.epoch !== epoch || actual.id !== marker.threadId || latest?.operationId !== marker.operationId || latest.turnId !== marker.turnId || latest.codexEpoch !== marker.codexEpoch || this.projection.revision(marker.threadId) !== revision)
|
|
908
968
|
return;
|
|
909
969
|
const link = this.store.getThreadLink(marker.threadId);
|
|
910
|
-
const ownership = link ? this.
|
|
970
|
+
const ownership = link ? this.workspaces.assertOwnership(link.projectId, actual.cwd, actual.id) : this.workspaces.findOwner(actual.cwd, actual.id);
|
|
911
971
|
if (ownership.workspaceKey !== marker.workspaceKey) return;
|
|
912
972
|
const turn = actual.turns.find((entry) => entry.id === marker.turnId);
|
|
913
973
|
if (turn && isActive(turn.status) && latest.state === "active" && this.projection.get(marker.threadId)?.status === "unknown") {
|
|
@@ -1057,7 +1117,7 @@ class GatewayService {
|
|
|
1057
1117
|
const encoded = JSON.stringify(summary);
|
|
1058
1118
|
if (this.summaries.get(threadId) === encoded) return;
|
|
1059
1119
|
try {
|
|
1060
|
-
this.
|
|
1120
|
+
this.workspaces.assertOwnership(thread.projectId, thread.cwd, thread.id);
|
|
1061
1121
|
} catch {
|
|
1062
1122
|
return;
|
|
1063
1123
|
}
|
|
@@ -1105,7 +1165,7 @@ class GatewayService {
|
|
|
1105
1165
|
const thread = await this.codex.readThreadMetadata(threadId);
|
|
1106
1166
|
const current = this.projection.get(threadId);
|
|
1107
1167
|
if (!current || thread.id !== threadId) return;
|
|
1108
|
-
this.
|
|
1168
|
+
this.workspaces.assertOwnership(current.projectId, thread.cwd, thread.id);
|
|
1109
1169
|
const settings = thread.runtimeSettings ? { ...current.runtimeSettings, ...thread.runtimeSettings } : void 0;
|
|
1110
1170
|
if (this.titleReads.get(threadId) === sequence && this.projection.revision(threadId) === revision && settings && JSON.stringify(current.runtimeSettings) !== JSON.stringify(settings)) {
|
|
1111
1171
|
current.runtimeSettings = settings;
|
|
@@ -1210,7 +1270,7 @@ class GatewayService {
|
|
|
1210
1270
|
this.projection.expireTurn(thread.id, event.turn.id);
|
|
1211
1271
|
if (newCompletion) {
|
|
1212
1272
|
try {
|
|
1213
|
-
this.
|
|
1273
|
+
this.workspaces.assertOwnership(thread.projectId, thread.cwd, thread.id);
|
|
1214
1274
|
this.events.publishCompletion({
|
|
1215
1275
|
threadId: thread.id,
|
|
1216
1276
|
turnId: event.turn.id,
|