@covro/covro 0.3.1-dev.1 → 0.3.1-dev.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (111) hide show
  1. package/README.md +10 -8
  2. package/README.zh-CN.md +10 -8
  3. package/dist/drizzle/0006_project_order.sql +8 -0
  4. package/dist/drizzle/meta/0006_snapshot.json +488 -0
  5. package/dist/drizzle/meta/_journal.json +7 -0
  6. package/dist/server/db/schema.js +1 -0
  7. package/dist/server/db/schema.js.map +2 -2
  8. package/dist/server/http.js +10 -0
  9. package/dist/server/http.js.map +2 -2
  10. package/dist/server/projects.js +48 -5
  11. package/dist/server/projects.js.map +2 -2
  12. package/dist/server/service.js +5 -0
  13. package/dist/server/service.js.map +2 -2
  14. package/dist/shared/updates.js.map +2 -2
  15. package/dist/shared/validation.js +2 -0
  16. package/dist/shared/validation.js.map +2 -2
  17. package/dist/update-worker.cjs +4 -3
  18. package/dist/updates/manager.js +7 -4
  19. package/dist/updates/manager.js.map +2 -2
  20. package/dist/updates/registry.js +21 -9
  21. package/dist/updates/registry.js.map +3 -3
  22. package/dist/updates/worker.js +3 -3
  23. package/dist/updates/worker.js.map +2 -2
  24. package/dist/web/assets/{abnfDiagram-WEYOIA3C-DLvlob5t.js → abnfDiagram-WEYOIA3C-BFmMm05x.js} +1 -1
  25. package/dist/web/assets/{arc-DgauwgFe.js → arc-lnrGH4K4.js} +1 -1
  26. package/dist/web/assets/{architecture-WOLXFQ4H-DvjLv0QN.js → architecture-WOLXFQ4H-CDbpw3av.js} +1 -1
  27. package/dist/web/assets/{architectureDiagram-ZHVYKT53-BAyZGlzu.js → architectureDiagram-ZHVYKT53-Br4uXjzV.js} +1 -1
  28. package/dist/web/assets/{blockDiagram-RJAE7QA3-BVfkmjlt.js → blockDiagram-RJAE7QA3-2cto1XrH.js} +1 -1
  29. package/dist/web/assets/{c4Diagram-XDPPPE54-rTsfyMoB.js → c4Diagram-XDPPPE54-pPOONSo8.js} +1 -1
  30. package/dist/web/assets/channel-D_YOL9S0.js +1 -0
  31. package/dist/web/assets/{chunk-2E7WHVMD-CQ_N_vLS.js → chunk-2E7WHVMD-CqOBPcBj.js} +1 -1
  32. package/dist/web/assets/{chunk-4EMNKR2T-wBy_gyo6.js → chunk-4EMNKR2T-BvMm1kcN.js} +1 -1
  33. package/dist/web/assets/{chunk-55XCU76K-DYiwIhmI.js → chunk-55XCU76K-CojzCeCA.js} +1 -1
  34. package/dist/web/assets/{chunk-7S6XSTFO-CRGtfVHg.js → chunk-7S6XSTFO-wa76hIsr.js} +1 -1
  35. package/dist/web/assets/{chunk-AQDTSKZB-1G2Za6bm.js → chunk-AQDTSKZB-tD5bFGjM.js} +1 -1
  36. package/dist/web/assets/{chunk-DSNLSHJZ-B1LLoYuG.js → chunk-DSNLSHJZ-ByVh88M0.js} +1 -1
  37. package/dist/web/assets/{chunk-HAGFOTED-78h1RpHh.js → chunk-HAGFOTED-BhOQL-iG.js} +1 -1
  38. package/dist/web/assets/{chunk-HNE4Q36P-DmR5B27Q.js → chunk-HNE4Q36P-BZiD7Dq0.js} +1 -1
  39. package/dist/web/assets/{chunk-NQOKRVIF-D7WSEPzg.js → chunk-NQOKRVIF-DhSSkcDm.js} +1 -1
  40. package/dist/web/assets/{chunk-UHW7PFBP-gl7a5V-l.js → chunk-UHW7PFBP-BONg0JLM.js} +1 -1
  41. package/dist/web/assets/{chunk-VQ5W62QI-KaBXxBre.js → chunk-VQ5W62QI-CehpDQpX.js} +1 -1
  42. package/dist/web/assets/{chunk-WQQL4CDK-DDczFoOr.js → chunk-WQQL4CDK-C4Cm0U7u.js} +1 -1
  43. package/dist/web/assets/{chunk-XXDRQBXY-3ug4Qhxg.js → chunk-XXDRQBXY-Ddcg44F2.js} +1 -1
  44. package/dist/web/assets/{chunk-YZO67HFG-BpWOuCXP.js → chunk-YZO67HFG-CJYc1t5Y.js} +1 -1
  45. package/dist/web/assets/{chunk-ZAUX2LTX-1ojPb1if.js → chunk-ZAUX2LTX-BeF7PiBe.js} +1 -1
  46. package/dist/web/assets/{chunk-ZCHMQSOR-D2kK2cPj.js → chunk-ZCHMQSOR-BzxdttZQ.js} +1 -1
  47. package/dist/web/assets/{classDiagram-v2-4MYWR3UA-B7eMvxAO.js → classDiagram-v2-4MYWR3UA-XeRozDII.js} +1 -1
  48. package/dist/web/assets/{cose-bilkent-JH36ORCC-BtB8mbt8.js → cose-bilkent-JH36ORCC-zvgm5h82.js} +1 -1
  49. package/dist/web/assets/{cynefin-EF2NZ3EQ-CIctqxlE.js → cynefin-EF2NZ3EQ-DffawypB.js} +1 -1
  50. package/dist/web/assets/{cynefinDiagram-WF24UDRP-qat2GD9P.js → cynefinDiagram-WF24UDRP-e2kk_Xg4.js} +1 -1
  51. package/dist/web/assets/{dagre-NK4GIZVX-OJamzzID.js → dagre-NK4GIZVX-DPTt7HIR.js} +1 -1
  52. package/dist/web/assets/{diagram-CBK3MWTC-C7cX2tqO.js → diagram-CBK3MWTC-CKQPukdf.js} +1 -1
  53. package/dist/web/assets/{diagram-I5XIIV2D-DsMfKVN7.js → diagram-I5XIIV2D-CBI_lUfl.js} +1 -1
  54. package/dist/web/assets/{diagram-ODIL2UCW-Cz66FxdJ.js → diagram-ODIL2UCW-DXuDuOob.js} +1 -1
  55. package/dist/web/assets/{diagram-PFBEJUXE-CwhPuwKj.js → diagram-PFBEJUXE-C9Y1lchY.js} +1 -1
  56. package/dist/web/assets/{diagram-RJA3I4VV-C52IGiFJ.js → diagram-RJA3I4VV-CbcN7pHo.js} +1 -1
  57. package/dist/web/assets/{diagram-TYUNFSNR-C2LZVLKY.js → diagram-TYUNFSNR-D_MmDJKc.js} +1 -1
  58. package/dist/web/assets/{dist-Bxu8YbZb.js → dist-C-d_G2FS.js} +1 -1
  59. package/dist/web/assets/{ebnfDiagram-V4P3TTPT-eQKKlMli.js → ebnfDiagram-V4P3TTPT-CIlDI7Ps.js} +1 -1
  60. package/dist/web/assets/{elk-QONTBCTQ-C5kIOZTA.js → elk-QONTBCTQ-BrywoKK7.js} +1 -1
  61. package/dist/web/assets/{erDiagram-KYQIGQAA-CMTowz5P.js → erDiagram-KYQIGQAA-CKg6BT7V.js} +1 -1
  62. package/dist/web/assets/{eventmodeling-K75KTNOO-C5v-UF8X.js → eventmodeling-K75KTNOO-BkwvMA16.js} +1 -1
  63. package/dist/web/assets/flowDiagram-IUGLLWBU-DTkRetsK.js +1 -0
  64. package/dist/web/assets/{ganttDiagram-R7GPNTJY-B0viiC-b.js → ganttDiagram-R7GPNTJY-DR_AlXXn.js} +1 -1
  65. package/dist/web/assets/{gitGraph-VSP46ZUC-tEjFmPhN.js → gitGraph-VSP46ZUC-Cv1cefI5.js} +1 -1
  66. package/dist/web/assets/{gitGraphDiagram-PDYL7XHY-CsOwt7jz.js → gitGraphDiagram-PDYL7XHY-D_prlvP9.js} +1 -1
  67. package/dist/web/assets/index-Br54jmas.js +147 -0
  68. package/dist/web/assets/index-Crq1cgK9.css +1 -0
  69. package/dist/web/assets/{info-OHQRW6UA-DnFGu4Ew.js → info-OHQRW6UA--i-b1e1W.js} +1 -1
  70. package/dist/web/assets/{infoDiagram-MVDRKAX5-B6WiNaDF.js → infoDiagram-MVDRKAX5-D5xGvRSb.js} +1 -1
  71. package/dist/web/assets/{ishikawaDiagram-HPEHV2GE-Dn70fI1r.js → ishikawaDiagram-HPEHV2GE-CdDzjaNo.js} +1 -1
  72. package/dist/web/assets/{journeyDiagram-BOSHW7O7-BUjYzV2f.js → journeyDiagram-BOSHW7O7-DYXkvOgG.js} +1 -1
  73. package/dist/web/assets/{kanban-definition-FNSA4HC5-BDMfWbtB.js → kanban-definition-FNSA4HC5-DMRrVeRI.js} +1 -1
  74. package/dist/web/assets/{line-DNSRZHBK.js → line-IKuSdaT6.js} +1 -1
  75. package/dist/web/assets/{linear-CrS49q6J.js → linear-DG-Bn7df.js} +1 -1
  76. package/dist/web/assets/{mermaid-parser.core-DXuyO8Px.js → mermaid-parser.core-Cr4hqI31.js} +3 -3
  77. package/dist/web/assets/{mermaid.core-DUTnbQkq.js → mermaid.core-Ch6ggfwZ.js} +4 -4
  78. package/dist/web/assets/{mindmap-definition-6OY57UG3-CqZTtItJ.js → mindmap-definition-6OY57UG3-BLIFHye9.js} +1 -1
  79. package/dist/web/assets/{packet-JDAUHWVQ-DXEZgY0a.js → packet-JDAUHWVQ-BaBw3Vck.js} +1 -1
  80. package/dist/web/assets/{pegDiagram-ECIUJNBX-D6CCPNem.js → pegDiagram-ECIUJNBX-BgFAs43s.js} +1 -1
  81. package/dist/web/assets/{pie-XZMESJXO-AXRFjZ4G.js → pie-XZMESJXO-Dr-clP68.js} +1 -1
  82. package/dist/web/assets/{pieDiagram-GNMPY74T-B9Ss8_5s.js → pieDiagram-GNMPY74T-bQ1SwzEf.js} +1 -1
  83. package/dist/web/assets/{quadrantDiagram-D57K4MC3-JrlylCtz.js → quadrantDiagram-D57K4MC3-suHq6vLw.js} +1 -1
  84. package/dist/web/assets/{radar-ABXABTNO-ChFm4hZg.js → radar-ABXABTNO-Cd4Nn-aB.js} +1 -1
  85. package/dist/web/assets/{railroad-I3PHUGI6-DaXXzqhZ.js → railroad-I3PHUGI6-BEUNjieA.js} +1 -1
  86. package/dist/web/assets/{railroad-abnf-LNEFI6M7-DGE67q11.js → railroad-abnf-LNEFI6M7-DosoD9le.js} +1 -1
  87. package/dist/web/assets/{railroad-ebnf-DWYD2UWJ-Cq9S3q72.js → railroad-ebnf-DWYD2UWJ-CNykm4Du.js} +1 -1
  88. package/dist/web/assets/{railroad-peg-JR7BVNR7-D2Gth3eY.js → railroad-peg-JR7BVNR7-C97ZuCXG.js} +1 -1
  89. package/dist/web/assets/{railroadDiagram-Y72NRBL7-BpkXX59H.js → railroadDiagram-Y72NRBL7-B1BnvTWP.js} +1 -1
  90. package/dist/web/assets/{requirementDiagram-EQYSW5RA-oNp3DMM4.js → requirementDiagram-EQYSW5RA-CrlURwLY.js} +1 -1
  91. package/dist/web/assets/{sankeyDiagram-SY24JHO5-bHfheSKO.js → sankeyDiagram-SY24JHO5-CEYWGGec.js} +1 -1
  92. package/dist/web/assets/{sequenceDiagram-BLEHNHQU-CEM2XfNF.js → sequenceDiagram-BLEHNHQU-CbUEnXPs.js} +1 -1
  93. package/dist/web/assets/{src-KjMctvzy.js → src-Dy1r7Mij.js} +1 -1
  94. package/dist/web/assets/{stateDiagram-v2-A6IESKWZ-D_TaR_ix.js → stateDiagram-v2-A6IESKWZ-DkxgX1jY.js} +1 -1
  95. package/dist/web/assets/{swimlanes-B3ENRKFQ-BUYl2fsg.js → swimlanes-B3ENRKFQ-B7RmFqNN.js} +1 -1
  96. package/dist/web/assets/swimlanesDiagram-AYQRV4AK-C0XU1o_x.js +8 -0
  97. package/dist/web/assets/{timeline-definition-JXYKGLGB-CpKsGN6d.js → timeline-definition-JXYKGLGB-DdaRshUD.js} +1 -1
  98. package/dist/web/assets/{treeView-D4JQ5SDB-Dioaagqt.js → treeView-D4JQ5SDB-UjtdDBri.js} +1 -1
  99. package/dist/web/assets/{treemap-SAJKECNS-B8NLQ14o.js → treemap-SAJKECNS-CfFPhlAU.js} +1 -1
  100. package/dist/web/assets/{usecaseDiagram-WPOLBNNX-CfwdxcsG.js → usecaseDiagram-WPOLBNNX-DpibMf1h.js} +1 -1
  101. package/dist/web/assets/{vennDiagram-Q3Y3IDCW-DQoKGtXo.js → vennDiagram-Q3Y3IDCW-DB_R8qT1.js} +1 -1
  102. package/dist/web/assets/{wardley-7MLQ67FV-DQHLHmeZ.js → wardley-7MLQ67FV-Bhatoa5s.js} +1 -1
  103. package/dist/web/assets/{wardleyDiagram-IVAIDKWA-DV-lq7zb.js → wardleyDiagram-IVAIDKWA-DBhy_JBR.js} +1 -1
  104. package/dist/web/assets/{xychartDiagram-4URNHDU4-wV4ueoGC.js → xychartDiagram-4URNHDU4-C2oV2jkE.js} +1 -1
  105. package/dist/web/index.html +2 -2
  106. package/package.json +4 -4
  107. package/dist/web/assets/channel-GGfPocHk.js +0 -1
  108. package/dist/web/assets/flowDiagram-IUGLLWBU-C-jE8afx.js +0 -1
  109. package/dist/web/assets/index-COSho7m2.css +0 -1
  110. package/dist/web/assets/index-D7GZPtTj.js +0 -147
  111. package/dist/web/assets/swimlanesDiagram-AYQRV4AK-DDLmyR4x.js +0 -8
@@ -190,6 +190,10 @@ function createApp({
190
190
  await parse(c, schemas.removeProjectSchema);
191
191
  return c.json(service.projectClones.retryRegistration(id(c.req.param("cloneId"))));
192
192
  });
193
+ app.post(
194
+ "/api/projects/reorder",
195
+ async (c) => c.json(service.reorderProjects(await parse(c, schemas.reorderProjectsSchema)))
196
+ );
193
197
  app.get(
194
198
  "/api/projects/:projectId/runtime-settings",
195
199
  async (c) => c.json(await service.readRuntimeSettings(id(c.req.param("projectId"))))
@@ -273,6 +277,12 @@ function createApp({
273
277
  c.header("Cross-Origin-Resource-Policy", "same-origin");
274
278
  c.header("Content-Security-Policy", "default-src 'none'; sandbox");
275
279
  const image = await service.getThreadImage(id(c.req.param("threadId")), c.req.query("path"));
280
+ const etag = `"${createHash("sha256").update(image.bytes).digest("hex")}"`;
281
+ c.header("Cache-Control", "private, no-cache");
282
+ c.header("ETag", etag);
283
+ const ifNoneMatch = c.req.header("If-None-Match");
284
+ if (ifNoneMatch === "*" || ifNoneMatch?.split(",").some((tag) => tag.trim().replace(/^W\//, "") === etag))
285
+ return c.body(null, 304);
276
286
  return c.body(image.bytes, 200, {
277
287
  "Content-Type": image.contentType,
278
288
  "Content-Length": String(image.bytes.length)
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../src/server/http.ts"],
4
- "sourcesContent": ["import type { BackupController } from '../shared/backups.js';\nimport { backupIdSchema } from '../shared/backups.js';\nimport { backupRequestSchema } from '../backups/manager.js';\nimport { createHash } from 'node:crypto';\nimport { Hono } from 'hono';\nimport { bodyLimit } from 'hono/body-limit';\nimport { serveStatic } from '@hono/node-server/serve-static';\nimport { upgradeWebSocket, type HttpBindings } from '@hono/node-server';\nimport type { ZodType } from 'zod';\nimport { ZodError } from 'zod';\nimport { AppError } from '../shared/errors.js';\nimport { IMAGE_REQUEST_BYTES } from '../shared/images.js';\nimport * as schemas from '../shared/validation.js';\nimport type { Operation } from '../shared/types.js';\nimport type { AppConfig } from './config.js';\nimport { ConfigStore } from './config-store.js';\nimport { createAuthenticator, type Authenticator, type Identity } from './auth.js';\nimport type { GatewayService } from './service.js';\nimport { socketHandlers } from './websocket.js';\nimport { z } from 'zod';\nimport { releaseChannels, type UpdateController } from '../shared/updates.js';\nimport { settingsSchema, updateRequestSchema } from '../updates/manager.js';\nimport { startShellSchema } from '../shared/shell.js';\nexport interface HttpDependencies {\n config: AppConfig;\n service: GatewayService;\n staticDir: string;\n authenticate?: Authenticator;\n updates?: UpdateController;\n backups?: BackupController;\n}\nexport function createApp({\n config,\n service,\n staticDir,\n authenticate = createAuthenticator(config),\n updates,\n backups,\n}: HttpDependencies) {\n const app = new Hono<{\n Bindings: HttpBindings;\n Variables: { identity: Identity; operationId?: string };\n }>();\n app.onError((error, c) => {\n const failure =\n error instanceof AppError\n ? error\n : error instanceof ZodError || error instanceof SyntaxError\n ? new AppError('VALIDATION_FAILED', 'Invalid request data', 400)\n : new AppError('INTERNAL_ERROR', 'An internal error occurred', 500);\n return c.json({ error: failure.toJSON(c.get('operationId')) }, failure.status as 400);\n });\n app.get('/api/health', (c) => c.json({ ok: true }));\n app.use('*', async (c, next) => {\n c.set('identity', await authenticate(c.req.raw, c.req.path === '/ws'));\n await next();\n });\n app.use('/api/*', (c, next) =>\n bodyLimit({\n maxSize: /^\\/api\\/threads\\/[^/]+\\/(turns|steer)$/.test(c.req.path)\n ? IMAGE_REQUEST_BYTES\n : 512 * 1024,\n onError: () => {\n throw new AppError('BODY_TOO_LARGE', 'Request body exceeds limit', 413);\n },\n })(c, next),\n );\n app.use('/api/*', async (c, next) => {\n c.header('Cache-Control', 'no-store');\n if (\n service.isInMaintenance() &&\n !['GET', 'HEAD', 'OPTIONS'].includes(c.req.method) &&\n !c.req.path.startsWith('/api/updates') &&\n !c.req.path.startsWith('/api/backups')\n )\n throw service.maintenanceError();\n await next();\n });\n if (backups) {\n app.get('/api/backups', async (c) => c.json(await backups.status()));\n app.post('/api/backups', async (c) =>\n c.json(await backups.start(backupRequestSchema.parse(await c.req.json())), 202),\n );\n app.post('/api/backups/:id/cancel', async (c) =>\n c.json(await backups.cancel(backupIdSchema.parse(c.req.param('id')))),\n );\n app.post('/api/backups/:id/verify', async (c) =>\n c.json(await backups.verify(backupIdSchema.parse(c.req.param('id')))),\n );\n }\n if (updates) {\n app.get('/api/updates', async (c) => c.json(await updates.status()));\n app.post('/api/updates/check', async (c) => {\n const input = z\n .object({ channel: z.enum(releaseChannels).optional() })\n .strict()\n .parse(await c.req.json());\n return c.json(await updates.check(input.channel));\n });\n app.post('/api/updates/settings', async (c) =>\n c.json(await updates.configure(settingsSchema.parse(await c.req.json()))),\n );\n app.post('/api/updates/run', async (c) =>\n c.json(await updates.start(updateRequestSchema.parse(await c.req.json())), 202),\n );\n }\n const id = (value: string) => schemas.idSchema.parse(value);\n const parse = async <T>(\n c: { req: { json: () => Promise<unknown> }; set: (key: 'operationId', value: string) => void },\n schema: ZodType<T>,\n ): Promise<T> => {\n const value: unknown = await c.req.json();\n if (\n value &&\n typeof value === 'object' &&\n 'clientOperationId' in value &&\n typeof value.clientOperationId === 'string'\n )\n c.set('operationId', value.clientOperationId);\n return schema.parse(value);\n };\n const status = (op: Operation) =>\n op.status === 'dispatching' || op.status === 'accepted' ? (202 as const) : (200 as const);\n const configuration = new ConfigStore(config);\n app.use('/api/config*', async (c, next) => {\n c.header('Cache-Control', 'no-store');\n await next();\n });\n app.get('/api/config', async (c) => c.json(await configuration.snapshot()));\n app.post('/api/config', async (c) => c.json(await configuration.save(await c.req.json())));\n app.post('/api/config/import', async (c) =>\n c.json(await configuration.previewImport(await c.req.json())),\n );\n app.get('/api/config/export', async (c) => {\n c.header('Content-Disposition', 'attachment; filename=\"codex-web-config.json\"');\n return c.json(await configuration.export());\n });\n app.get('/api/history-cache-context', async (c) => {\n const identity = c.get('identity');\n if (config.auth.mode !== 'local' && !identity.subject)\n throw new AppError('UNAUTHENTICATED', '\u65E0\u6CD5\u786E\u8BA4\u7F13\u5B58\u6240\u5C5E\u7528\u6237', 401);\n const projects = await service.listProjects();\n // A restart, user change or project/permission change creates a new namespace.\n // The subject is hashed on the server and is never exposed to browser storage.\n const scope = createHash('sha256')\n .update(\n JSON.stringify([\n service.events.serverEpoch,\n service.getSystem().codexEpoch,\n config.auth.mode,\n identity.subject ?? 'local',\n projects.map((project) => [project.id, project.rootPath, project.available]),\n ]),\n )\n .digest('hex');\n return c.json({ scope });\n });\n app.get('/api/system', (c) => c.json(service.getSystem()));\n app.get('/api/shell', (c) =>\n c.json(\n service.shell.snapshot(\n c.req.query('epoch'),\n z.coerce\n .number()\n .int()\n .min(0)\n .max(Number.MAX_SAFE_INTEGER)\n .default(0)\n .parse(c.req.query('after')),\n ),\n ),\n );\n app.post('/api/projects/:projectId/shell', async (c) => {\n const op = await service.startShell(\n id(c.req.param('projectId')),\n await parse(c, startShellSchema),\n );\n return c.json(op, status(op));\n });\n app.post('/api/shell/:commandId/interrupt', async (c) => {\n const op = await service.interruptShell(\n id(c.req.param('commandId')),\n await parse(c, schemas.operationSchema),\n );\n return c.json(op, status(op));\n });\n app.post('/api/system/compatibility/confirm', async (c) => {\n const op = await service.confirmCompatibility(\n await parse(c, schemas.confirmCompatibilitySchema),\n );\n return c.json(op, status(op));\n });\n app.get('/api/projects', async (c) => c.json({ data: await service.listProjects() }));\n app.get('/api/unassigned/runtime-settings', async (c) =>\n c.json(await service.readRuntimeSettings(null)),\n );\n app.post('/api/unassigned/model-defaults', async (c) => {\n const op = await service.saveModelDefaults(\n null,\n await parse(c, schemas.saveModelDefaultsSchema),\n );\n return c.json(op, status(op));\n });\n app.get('/api/unassigned/threads', async (c) =>\n c.json(\n await service.listThreadSummaries(\n null,\n c.req.query('cursor') === undefined\n ? undefined\n : schemas.cursorSchema.parse(c.req.query('cursor')),\n schemas.archivedQuerySchema.parse(c.req.query('archived')),\n ),\n ),\n );\n app.post('/api/unassigned/threads', async (c) => {\n const op = await service.createThread(null, await parse(c, schemas.createThreadSchema));\n return c.json(op, status(op));\n });\n app.post('/api/project-clones', async (c) =>\n c.json(service.cloneProject(await parse(c, schemas.cloneProjectSchema)), 202),\n );\n app.get('/api/project-clones/:cloneId', (c) =>\n c.json(service.projectClones.get(id(c.req.param('cloneId')))),\n );\n app.post('/api/project-clones/:cloneId/cancel', async (c) => {\n await parse(c, schemas.removeProjectSchema);\n return c.json(service.projectClones.cancel(id(c.req.param('cloneId'))));\n });\n app.post('/api/project-clones/:cloneId/register', async (c) => {\n await parse(c, schemas.removeProjectSchema);\n return c.json(service.projectClones.retryRegistration(id(c.req.param('cloneId'))));\n });\n app.get('/api/projects/:projectId/runtime-settings', async (c) =>\n c.json(await service.readRuntimeSettings(id(c.req.param('projectId')))),\n );\n app.post('/api/projects/:projectId/model-defaults', async (c) => {\n const op = await service.saveModelDefaults(\n id(c.req.param('projectId')),\n await parse(c, schemas.saveModelDefaultsSchema),\n );\n return c.json(op, status(op));\n });\n app.post('/api/projects', async (c) =>\n c.json(service.addProject(await parse(c, schemas.createProjectSchema))),\n );\n app.post('/api/projects/:projectId/rename', async (c) =>\n c.json(\n service.renameProject(\n id(c.req.param('projectId')),\n await parse(c, schemas.renameProjectSchema),\n ),\n ),\n );\n app.post('/api/projects/:projectId/remove', async (c) => {\n await parse(c, schemas.removeProjectSchema);\n return c.json(service.removeProject(id(c.req.param('projectId'))));\n });\n app.get('/api/projects/:projectId/threads', async (c) =>\n c.json(\n await service.listThreadSummaries(\n id(c.req.param('projectId')),\n c.req.query('cursor') === undefined\n ? undefined\n : schemas.cursorSchema.parse(c.req.query('cursor')),\n schemas.archivedQuerySchema.parse(c.req.query('archived')),\n ),\n ),\n );\n app.get('/api/threads', async (c) =>\n c.json(\n await service.listThreads(\n id(c.req.query('projectId') ?? ''),\n c.req.query('cursor') === undefined\n ? undefined\n : schemas.cursorSchema.parse(c.req.query('cursor')),\n schemas.archivedQuerySchema.parse(c.req.query('archived')),\n ),\n ),\n );\n app.post('/api/threads/:threadId/archive', async (c) => {\n return c.json(\n await service.setThreadArchived(\n id(c.req.param('threadId')),\n await parse(c, schemas.archiveThreadSchema),\n ),\n );\n });\n app.get('/api/threads/:threadId', async (c) =>\n c.json(await service.getThread(id(c.req.param('threadId')))),\n );\n app.get('/api/threads/:threadId/history', async (c) =>\n c.json(await service.getThreadHistory(id(c.req.param('threadId')), c.req.query('cursor'))),\n );\n app.get('/api/threads/:threadId/metadata', async (c) =>\n c.json(await service.getThreadMetadata(id(c.req.param('threadId')))),\n );\n app.get('/api/threads/:threadId/export', async (c) => {\n const exported = await service.exportThread(id(c.req.param('threadId')));\n c.header('Content-Type', 'text/markdown; charset=utf-8');\n c.header('Content-Disposition', `attachment; filename=\"${exported.filename}\"`);\n c.header('Cache-Control', 'no-store');\n c.header('X-Content-Type-Options', 'nosniff');\n c.header('X-Export-Completeness', exported.partial ? 'partial' : 'saved-history');\n return c.body(exported.markdown);\n });\n app.get('/api/threads/:threadId/images', async (c) => {\n c.header('Cache-Control', 'private, no-store');\n c.header('X-Content-Type-Options', 'nosniff');\n c.header('Cross-Origin-Resource-Policy', 'same-origin');\n c.header('Content-Security-Policy', \"default-src 'none'; sandbox\");\n const image = await service.getThreadImage(id(c.req.param('threadId')), c.req.query('path'));\n return c.body(image.bytes, 200, {\n 'Content-Type': image.contentType,\n 'Content-Length': String(image.bytes.length),\n });\n });\n app.get('/api/threads/:threadId/markdown', async (c) => {\n c.header('Cache-Control', 'private, no-store');\n c.header('X-Content-Type-Options', 'nosniff');\n c.header('Cross-Origin-Resource-Policy', 'same-origin');\n return c.json(\n await service.getThreadMarkdown(id(c.req.param('threadId')), c.req.query('path')),\n );\n });\n app.get('/api/threads/:threadId/json', async (c) => {\n c.header('Cache-Control', 'private, no-store');\n c.header('X-Content-Type-Options', 'nosniff');\n c.header('Cross-Origin-Resource-Policy', 'same-origin');\n return c.json(await service.getThreadJson(id(c.req.param('threadId')), c.req.query('path')));\n });\n app.get('/api/operations/:operationId', (c) =>\n c.json(service.getOperation(id(c.req.param('operationId')))),\n );\n app.post('/api/projects/:projectId/threads', async (c) => {\n const op = await service.createThread(\n id(c.req.param('projectId')),\n await parse(c, schemas.createThreadSchema),\n );\n return c.json(op, status(op));\n });\n app.post('/api/threads/:threadId/resume', async (c) => {\n const op = await service.resumeThread(\n id(c.req.param('threadId')),\n await parse(c, schemas.resumeThreadSchema),\n );\n return c.json(op, status(op));\n });\n app.post('/api/threads/:threadId/fork', async (c) => {\n const op = await service.forkThread(\n id(c.req.param('threadId')),\n await parse(c, schemas.forkThreadSchema),\n );\n return c.json(op, status(op));\n });\n app.post('/api/threads/:threadId/rename', async (c) => {\n const op = await service.renameThread(\n id(c.req.param('threadId')),\n await parse(c, schemas.renameThreadSchema),\n );\n return c.json(op, status(op));\n });\n app.post('/api/threads/:threadId/turns', async (c) => {\n const op = await service.startTurn(\n id(c.req.param('threadId')),\n await parse(c, schemas.startTurnSchema),\n );\n return c.json(op, status(op));\n });\n app.post('/api/threads/:threadId/review', async (c) => {\n const op = await service.startReview(\n id(c.req.param('threadId')),\n await parse(c, schemas.startReviewSchema),\n );\n return c.json(op, status(op));\n });\n app.post('/api/threads/:threadId/steer', async (c) => {\n const op = await service.steer(\n id(c.req.param('threadId')),\n await parse(c, schemas.steerSchema),\n );\n return c.json(op, status(op));\n });\n app.post('/api/threads/:threadId/compact', async (c) => {\n const op = await service.compactThread(\n id(c.req.param('threadId')),\n await parse(c, schemas.operationSchema),\n );\n return c.json(op, status(op));\n });\n app.post('/api/threads/:threadId/interrupt', async (c) => {\n const op = await service.interrupt(\n id(c.req.param('threadId')),\n await parse(c, schemas.interruptSchema),\n );\n return c.json(op, status(op));\n });\n app.post('/api/actions/:actionId/resolve', async (c) => {\n const op = await service.resolveAction(\n id(c.req.param('actionId')),\n await parse(c, schemas.resolveActionSchema),\n );\n return c.json(op, status(op));\n });\n app.post('/api/operations/:operationId/reconcile', async (c) => {\n const op = await service.reconcileOperation(\n id(c.req.param('operationId')),\n await parse(c, schemas.reconcileSchema),\n );\n return c.json(op, status(op));\n });\n app.get(\n '/ws',\n upgradeWebSocket((c) =>\n socketHandlers(service, config, c.get('identity'), c.req.query('overview') === '1'),\n ),\n );\n app.all('/api/*', () => {\n throw new AppError('NOT_FOUND', 'API route not found', 404);\n });\n app.all('/api', () => {\n throw new AppError('NOT_FOUND', 'API route not found', 404);\n });\n app.all('/ws/*', () => {\n throw new AppError('NOT_FOUND', 'WebSocket route not found', 404);\n });\n app.use('*', async (c, next) => {\n if (!c.req.path.startsWith('/assets/')) c.header('Cache-Control', 'no-store');\n c.header('X-Content-Type-Options', 'nosniff');\n c.header('Referrer-Policy', 'same-origin');\n c.header(\n 'Content-Security-Policy',\n // Remote Markdown images load directly in the browser, including HTTPS redirects.\n // Keep scripts, connections and all other resource policies restricted as before.\n \"default-src 'self'; script-src 'self'; style-src 'self' 'unsafe-inline'; img-src 'self' data: blob: https:; connect-src 'self'; frame-ancestors 'none'; base-uri 'none'\",\n );\n await next();\n });\n app.get('*', serveStatic({ root: staticDir }));\n app.get('*', async (c, next) => {\n if (!c.req.header('accept')?.includes('text/html') || /\\.[^/]+$/.test(c.req.path))\n return c.json({ error: { code: 'NOT_FOUND', message: 'Resource not found' } }, 404);\n return serveStatic({ path: `${staticDir}/index.html` })(c, next);\n });\n app.notFound((c) => c.json({ error: { code: 'NOT_FOUND', message: 'Resource not found' } }, 404));\n return app;\n}\n"],
5
- "mappings": "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",
4
+ "sourcesContent": ["import type { BackupController } from '../shared/backups.js';\nimport { backupIdSchema } from '../shared/backups.js';\nimport { backupRequestSchema } from '../backups/manager.js';\nimport { createHash } from 'node:crypto';\nimport { Hono } from 'hono';\nimport { bodyLimit } from 'hono/body-limit';\nimport { serveStatic } from '@hono/node-server/serve-static';\nimport { upgradeWebSocket, type HttpBindings } from '@hono/node-server';\nimport type { ZodType } from 'zod';\nimport { ZodError } from 'zod';\nimport { AppError } from '../shared/errors.js';\nimport { IMAGE_REQUEST_BYTES } from '../shared/images.js';\nimport * as schemas from '../shared/validation.js';\nimport type { Operation } from '../shared/types.js';\nimport type { AppConfig } from './config.js';\nimport { ConfigStore } from './config-store.js';\nimport { createAuthenticator, type Authenticator, type Identity } from './auth.js';\nimport type { GatewayService } from './service.js';\nimport { socketHandlers } from './websocket.js';\nimport { z } from 'zod';\nimport { releaseChannels, type UpdateController } from '../shared/updates.js';\nimport { settingsSchema, updateRequestSchema } from '../updates/manager.js';\nimport { startShellSchema } from '../shared/shell.js';\nexport interface HttpDependencies {\n config: AppConfig;\n service: GatewayService;\n staticDir: string;\n authenticate?: Authenticator;\n updates?: UpdateController;\n backups?: BackupController;\n}\nexport function createApp({\n config,\n service,\n staticDir,\n authenticate = createAuthenticator(config),\n updates,\n backups,\n}: HttpDependencies) {\n const app = new Hono<{\n Bindings: HttpBindings;\n Variables: { identity: Identity; operationId?: string };\n }>();\n app.onError((error, c) => {\n const failure =\n error instanceof AppError\n ? error\n : error instanceof ZodError || error instanceof SyntaxError\n ? new AppError('VALIDATION_FAILED', 'Invalid request data', 400)\n : new AppError('INTERNAL_ERROR', 'An internal error occurred', 500);\n return c.json({ error: failure.toJSON(c.get('operationId')) }, failure.status as 400);\n });\n app.get('/api/health', (c) => c.json({ ok: true }));\n app.use('*', async (c, next) => {\n c.set('identity', await authenticate(c.req.raw, c.req.path === '/ws'));\n await next();\n });\n app.use('/api/*', (c, next) =>\n bodyLimit({\n maxSize: /^\\/api\\/threads\\/[^/]+\\/(turns|steer)$/.test(c.req.path)\n ? IMAGE_REQUEST_BYTES\n : 512 * 1024,\n onError: () => {\n throw new AppError('BODY_TOO_LARGE', 'Request body exceeds limit', 413);\n },\n })(c, next),\n );\n app.use('/api/*', async (c, next) => {\n c.header('Cache-Control', 'no-store');\n if (\n service.isInMaintenance() &&\n !['GET', 'HEAD', 'OPTIONS'].includes(c.req.method) &&\n !c.req.path.startsWith('/api/updates') &&\n !c.req.path.startsWith('/api/backups')\n )\n throw service.maintenanceError();\n await next();\n });\n if (backups) {\n app.get('/api/backups', async (c) => c.json(await backups.status()));\n app.post('/api/backups', async (c) =>\n c.json(await backups.start(backupRequestSchema.parse(await c.req.json())), 202),\n );\n app.post('/api/backups/:id/cancel', async (c) =>\n c.json(await backups.cancel(backupIdSchema.parse(c.req.param('id')))),\n );\n app.post('/api/backups/:id/verify', async (c) =>\n c.json(await backups.verify(backupIdSchema.parse(c.req.param('id')))),\n );\n }\n if (updates) {\n app.get('/api/updates', async (c) => c.json(await updates.status()));\n app.post('/api/updates/check', async (c) => {\n const input = z\n .object({ channel: z.enum(releaseChannels).optional() })\n .strict()\n .parse(await c.req.json());\n return c.json(await updates.check(input.channel));\n });\n app.post('/api/updates/settings', async (c) =>\n c.json(await updates.configure(settingsSchema.parse(await c.req.json()))),\n );\n app.post('/api/updates/run', async (c) =>\n c.json(await updates.start(updateRequestSchema.parse(await c.req.json())), 202),\n );\n }\n const id = (value: string) => schemas.idSchema.parse(value);\n const parse = async <T>(\n c: { req: { json: () => Promise<unknown> }; set: (key: 'operationId', value: string) => void },\n schema: ZodType<T>,\n ): Promise<T> => {\n const value: unknown = await c.req.json();\n if (\n value &&\n typeof value === 'object' &&\n 'clientOperationId' in value &&\n typeof value.clientOperationId === 'string'\n )\n c.set('operationId', value.clientOperationId);\n return schema.parse(value);\n };\n const status = (op: Operation) =>\n op.status === 'dispatching' || op.status === 'accepted' ? (202 as const) : (200 as const);\n const configuration = new ConfigStore(config);\n app.use('/api/config*', async (c, next) => {\n c.header('Cache-Control', 'no-store');\n await next();\n });\n app.get('/api/config', async (c) => c.json(await configuration.snapshot()));\n app.post('/api/config', async (c) => c.json(await configuration.save(await c.req.json())));\n app.post('/api/config/import', async (c) =>\n c.json(await configuration.previewImport(await c.req.json())),\n );\n app.get('/api/config/export', async (c) => {\n c.header('Content-Disposition', 'attachment; filename=\"codex-web-config.json\"');\n return c.json(await configuration.export());\n });\n app.get('/api/history-cache-context', async (c) => {\n const identity = c.get('identity');\n if (config.auth.mode !== 'local' && !identity.subject)\n throw new AppError('UNAUTHENTICATED', '\u65E0\u6CD5\u786E\u8BA4\u7F13\u5B58\u6240\u5C5E\u7528\u6237', 401);\n const projects = await service.listProjects();\n // A restart, user change or project/permission change creates a new namespace.\n // The subject is hashed on the server and is never exposed to browser storage.\n const scope = createHash('sha256')\n .update(\n JSON.stringify([\n service.events.serverEpoch,\n service.getSystem().codexEpoch,\n config.auth.mode,\n identity.subject ?? 'local',\n projects.map((project) => [project.id, project.rootPath, project.available]),\n ]),\n )\n .digest('hex');\n return c.json({ scope });\n });\n app.get('/api/system', (c) => c.json(service.getSystem()));\n app.get('/api/shell', (c) =>\n c.json(\n service.shell.snapshot(\n c.req.query('epoch'),\n z.coerce\n .number()\n .int()\n .min(0)\n .max(Number.MAX_SAFE_INTEGER)\n .default(0)\n .parse(c.req.query('after')),\n ),\n ),\n );\n app.post('/api/projects/:projectId/shell', async (c) => {\n const op = await service.startShell(\n id(c.req.param('projectId')),\n await parse(c, startShellSchema),\n );\n return c.json(op, status(op));\n });\n app.post('/api/shell/:commandId/interrupt', async (c) => {\n const op = await service.interruptShell(\n id(c.req.param('commandId')),\n await parse(c, schemas.operationSchema),\n );\n return c.json(op, status(op));\n });\n app.post('/api/system/compatibility/confirm', async (c) => {\n const op = await service.confirmCompatibility(\n await parse(c, schemas.confirmCompatibilitySchema),\n );\n return c.json(op, status(op));\n });\n app.get('/api/projects', async (c) => c.json({ data: await service.listProjects() }));\n app.get('/api/unassigned/runtime-settings', async (c) =>\n c.json(await service.readRuntimeSettings(null)),\n );\n app.post('/api/unassigned/model-defaults', async (c) => {\n const op = await service.saveModelDefaults(\n null,\n await parse(c, schemas.saveModelDefaultsSchema),\n );\n return c.json(op, status(op));\n });\n app.get('/api/unassigned/threads', async (c) =>\n c.json(\n await service.listThreadSummaries(\n null,\n c.req.query('cursor') === undefined\n ? undefined\n : schemas.cursorSchema.parse(c.req.query('cursor')),\n schemas.archivedQuerySchema.parse(c.req.query('archived')),\n ),\n ),\n );\n app.post('/api/unassigned/threads', async (c) => {\n const op = await service.createThread(null, await parse(c, schemas.createThreadSchema));\n return c.json(op, status(op));\n });\n app.post('/api/project-clones', async (c) =>\n c.json(service.cloneProject(await parse(c, schemas.cloneProjectSchema)), 202),\n );\n app.get('/api/project-clones/:cloneId', (c) =>\n c.json(service.projectClones.get(id(c.req.param('cloneId')))),\n );\n app.post('/api/project-clones/:cloneId/cancel', async (c) => {\n await parse(c, schemas.removeProjectSchema);\n return c.json(service.projectClones.cancel(id(c.req.param('cloneId'))));\n });\n app.post('/api/project-clones/:cloneId/register', async (c) => {\n await parse(c, schemas.removeProjectSchema);\n return c.json(service.projectClones.retryRegistration(id(c.req.param('cloneId'))));\n });\n app.post('/api/projects/reorder', async (c) =>\n c.json(service.reorderProjects(await parse(c, schemas.reorderProjectsSchema))),\n );\n app.get('/api/projects/:projectId/runtime-settings', async (c) =>\n c.json(await service.readRuntimeSettings(id(c.req.param('projectId')))),\n );\n app.post('/api/projects/:projectId/model-defaults', async (c) => {\n const op = await service.saveModelDefaults(\n id(c.req.param('projectId')),\n await parse(c, schemas.saveModelDefaultsSchema),\n );\n return c.json(op, status(op));\n });\n app.post('/api/projects', async (c) =>\n c.json(service.addProject(await parse(c, schemas.createProjectSchema))),\n );\n app.post('/api/projects/:projectId/rename', async (c) =>\n c.json(\n service.renameProject(\n id(c.req.param('projectId')),\n await parse(c, schemas.renameProjectSchema),\n ),\n ),\n );\n app.post('/api/projects/:projectId/remove', async (c) => {\n await parse(c, schemas.removeProjectSchema);\n return c.json(service.removeProject(id(c.req.param('projectId'))));\n });\n app.get('/api/projects/:projectId/threads', async (c) =>\n c.json(\n await service.listThreadSummaries(\n id(c.req.param('projectId')),\n c.req.query('cursor') === undefined\n ? undefined\n : schemas.cursorSchema.parse(c.req.query('cursor')),\n schemas.archivedQuerySchema.parse(c.req.query('archived')),\n ),\n ),\n );\n app.get('/api/threads', async (c) =>\n c.json(\n await service.listThreads(\n id(c.req.query('projectId') ?? ''),\n c.req.query('cursor') === undefined\n ? undefined\n : schemas.cursorSchema.parse(c.req.query('cursor')),\n schemas.archivedQuerySchema.parse(c.req.query('archived')),\n ),\n ),\n );\n app.post('/api/threads/:threadId/archive', async (c) => {\n return c.json(\n await service.setThreadArchived(\n id(c.req.param('threadId')),\n await parse(c, schemas.archiveThreadSchema),\n ),\n );\n });\n app.get('/api/threads/:threadId', async (c) =>\n c.json(await service.getThread(id(c.req.param('threadId')))),\n );\n app.get('/api/threads/:threadId/history', async (c) =>\n c.json(await service.getThreadHistory(id(c.req.param('threadId')), c.req.query('cursor'))),\n );\n app.get('/api/threads/:threadId/metadata', async (c) =>\n c.json(await service.getThreadMetadata(id(c.req.param('threadId')))),\n );\n app.get('/api/threads/:threadId/export', async (c) => {\n const exported = await service.exportThread(id(c.req.param('threadId')));\n c.header('Content-Type', 'text/markdown; charset=utf-8');\n c.header('Content-Disposition', `attachment; filename=\"${exported.filename}\"`);\n c.header('Cache-Control', 'no-store');\n c.header('X-Content-Type-Options', 'nosniff');\n c.header('X-Export-Completeness', exported.partial ? 'partial' : 'saved-history');\n return c.body(exported.markdown);\n });\n app.get('/api/threads/:threadId/images', async (c) => {\n c.header('Cache-Control', 'private, no-store');\n c.header('X-Content-Type-Options', 'nosniff');\n c.header('Cross-Origin-Resource-Policy', 'same-origin');\n c.header('Content-Security-Policy', \"default-src 'none'; sandbox\");\n const image = await service.getThreadImage(id(c.req.param('threadId')), c.req.query('path'));\n // Hash the validated bytes so same-size overwrites with preserved mtimes also invalidate.\n const etag = `\"${createHash('sha256').update(image.bytes).digest('hex')}\"`;\n c.header('Cache-Control', 'private, no-cache');\n c.header('ETag', etag);\n const ifNoneMatch = c.req.header('If-None-Match');\n if (\n ifNoneMatch === '*' ||\n ifNoneMatch?.split(',').some((tag) => tag.trim().replace(/^W\\//, '') === etag)\n )\n return c.body(null, 304);\n return c.body(image.bytes, 200, {\n 'Content-Type': image.contentType,\n 'Content-Length': String(image.bytes.length),\n });\n });\n app.get('/api/threads/:threadId/markdown', async (c) => {\n c.header('Cache-Control', 'private, no-store');\n c.header('X-Content-Type-Options', 'nosniff');\n c.header('Cross-Origin-Resource-Policy', 'same-origin');\n return c.json(\n await service.getThreadMarkdown(id(c.req.param('threadId')), c.req.query('path')),\n );\n });\n app.get('/api/threads/:threadId/json', async (c) => {\n c.header('Cache-Control', 'private, no-store');\n c.header('X-Content-Type-Options', 'nosniff');\n c.header('Cross-Origin-Resource-Policy', 'same-origin');\n return c.json(await service.getThreadJson(id(c.req.param('threadId')), c.req.query('path')));\n });\n app.get('/api/operations/:operationId', (c) =>\n c.json(service.getOperation(id(c.req.param('operationId')))),\n );\n app.post('/api/projects/:projectId/threads', async (c) => {\n const op = await service.createThread(\n id(c.req.param('projectId')),\n await parse(c, schemas.createThreadSchema),\n );\n return c.json(op, status(op));\n });\n app.post('/api/threads/:threadId/resume', async (c) => {\n const op = await service.resumeThread(\n id(c.req.param('threadId')),\n await parse(c, schemas.resumeThreadSchema),\n );\n return c.json(op, status(op));\n });\n app.post('/api/threads/:threadId/fork', async (c) => {\n const op = await service.forkThread(\n id(c.req.param('threadId')),\n await parse(c, schemas.forkThreadSchema),\n );\n return c.json(op, status(op));\n });\n app.post('/api/threads/:threadId/rename', async (c) => {\n const op = await service.renameThread(\n id(c.req.param('threadId')),\n await parse(c, schemas.renameThreadSchema),\n );\n return c.json(op, status(op));\n });\n app.post('/api/threads/:threadId/turns', async (c) => {\n const op = await service.startTurn(\n id(c.req.param('threadId')),\n await parse(c, schemas.startTurnSchema),\n );\n return c.json(op, status(op));\n });\n app.post('/api/threads/:threadId/review', async (c) => {\n const op = await service.startReview(\n id(c.req.param('threadId')),\n await parse(c, schemas.startReviewSchema),\n );\n return c.json(op, status(op));\n });\n app.post('/api/threads/:threadId/steer', async (c) => {\n const op = await service.steer(\n id(c.req.param('threadId')),\n await parse(c, schemas.steerSchema),\n );\n return c.json(op, status(op));\n });\n app.post('/api/threads/:threadId/compact', async (c) => {\n const op = await service.compactThread(\n id(c.req.param('threadId')),\n await parse(c, schemas.operationSchema),\n );\n return c.json(op, status(op));\n });\n app.post('/api/threads/:threadId/interrupt', async (c) => {\n const op = await service.interrupt(\n id(c.req.param('threadId')),\n await parse(c, schemas.interruptSchema),\n );\n return c.json(op, status(op));\n });\n app.post('/api/actions/:actionId/resolve', async (c) => {\n const op = await service.resolveAction(\n id(c.req.param('actionId')),\n await parse(c, schemas.resolveActionSchema),\n );\n return c.json(op, status(op));\n });\n app.post('/api/operations/:operationId/reconcile', async (c) => {\n const op = await service.reconcileOperation(\n id(c.req.param('operationId')),\n await parse(c, schemas.reconcileSchema),\n );\n return c.json(op, status(op));\n });\n app.get(\n '/ws',\n upgradeWebSocket((c) =>\n socketHandlers(service, config, c.get('identity'), c.req.query('overview') === '1'),\n ),\n );\n app.all('/api/*', () => {\n throw new AppError('NOT_FOUND', 'API route not found', 404);\n });\n app.all('/api', () => {\n throw new AppError('NOT_FOUND', 'API route not found', 404);\n });\n app.all('/ws/*', () => {\n throw new AppError('NOT_FOUND', 'WebSocket route not found', 404);\n });\n app.use('*', async (c, next) => {\n if (!c.req.path.startsWith('/assets/')) c.header('Cache-Control', 'no-store');\n c.header('X-Content-Type-Options', 'nosniff');\n c.header('Referrer-Policy', 'same-origin');\n c.header(\n 'Content-Security-Policy',\n // Remote Markdown images load directly in the browser, including HTTPS redirects.\n // Keep scripts, connections and all other resource policies restricted as before.\n \"default-src 'self'; script-src 'self'; style-src 'self' 'unsafe-inline'; img-src 'self' data: blob: https:; connect-src 'self'; frame-ancestors 'none'; base-uri 'none'\",\n );\n await next();\n });\n app.get('*', serveStatic({ root: staticDir }));\n app.get('*', async (c, next) => {\n if (!c.req.header('accept')?.includes('text/html') || /\\.[^/]+$/.test(c.req.path))\n return c.json({ error: { code: 'NOT_FOUND', message: 'Resource not found' } }, 404);\n return serveStatic({ path: `${staticDir}/index.html` })(c, next);\n });\n app.notFound((c) => c.json({ error: { code: 'NOT_FOUND', message: 'Resource not found' } }, 404));\n return app;\n}\n"],
5
+ "mappings": "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",
6
6
  "names": []
7
7
  }
@@ -2,9 +2,13 @@ import { execFileSync } from "node:child_process";
2
2
  import { randomUUID } from "node:crypto";
3
3
  import { accessSync, constants, realpathSync, statSync } from "node:fs";
4
4
  import { basename, isAbsolute, relative, resolve, sep } from "node:path";
5
- import { asc } from "drizzle-orm";
5
+ import { asc, eq } from "drizzle-orm";
6
6
  import { AppError } from "../shared/errors.js";
7
- import { createProjectSchema, renameProjectSchema } from "../shared/validation.js";
7
+ import {
8
+ createProjectSchema,
9
+ renameProjectSchema,
10
+ reorderProjectsSchema
11
+ } from "../shared/validation.js";
8
12
  import { projectRegistrations } from "./db/schema.js";
9
13
  function containsPath(root, candidate) {
10
14
  const difference = relative(root, candidate);
@@ -25,7 +29,11 @@ function checkoutRoot(cwd) {
25
29
  class ProjectRegistry {
26
30
  constructor(configs, database) {
27
31
  this.database = database;
28
- for (const row of database?.select().from(projectRegistrations).orderBy(asc(projectRegistrations.createdAt), asc(projectRegistrations.id)).all() ?? [])
32
+ for (const row of database?.select().from(projectRegistrations).orderBy(
33
+ asc(projectRegistrations.sortOrder),
34
+ asc(projectRegistrations.createdAt),
35
+ asc(projectRegistrations.id)
36
+ ).all() ?? [])
29
37
  this.registered.set(row.id, row);
30
38
  const seeds = [];
31
39
  for (const config of configs) {
@@ -52,6 +60,7 @@ class ProjectRegistry {
52
60
  path,
53
61
  canonicalRoot,
54
62
  removedAt: null,
63
+ sortOrder: this.nextSortOrder(),
55
64
  createdAt: now,
56
65
  updatedAt: now
57
66
  };
@@ -68,6 +77,12 @@ class ProjectRegistry {
68
77
  Date.now()
69
78
  );
70
79
  }
80
+ nextSortOrder() {
81
+ return [...this.registered.values()].reduce(
82
+ (next, row) => Math.max(next, row.sortOrder + 1),
83
+ 0
84
+ );
85
+ }
71
86
  directory(path) {
72
87
  if (!isAbsolute(path) || path.includes("\0"))
73
88
  throw new AppError("INVALID_PROJECT_PATH", "\u8BF7\u8F93\u5165\u8FD0\u884C Codex \u7684\u7535\u8111\u4E0A\u7684\u7EDD\u5BF9\u76EE\u5F55\u8DEF\u5F84", 400);
@@ -130,7 +145,29 @@ class ProjectRegistry {
130
145
  }
131
146
  }
132
147
  list() {
133
- return [...this.registered.values()].filter((project) => project.removedAt === null).map((project) => this.inspect(project));
148
+ return [...this.registered.values()].filter((project) => project.removedAt === null).sort(
149
+ (a, b) => a.sortOrder - b.sortOrder || a.createdAt - b.createdAt || a.id.localeCompare(b.id)
150
+ ).map((project) => this.inspect(project));
151
+ }
152
+ reorder(input) {
153
+ const { projectIds } = reorderProjectsSchema.parse(input);
154
+ const active = [...this.registered.values()].filter((row) => row.removedAt === null);
155
+ if (projectIds.length !== active.length || projectIds.some(
156
+ (id) => !this.registered.has(id) || this.registered.get(id).removedAt !== null
157
+ ))
158
+ throw new AppError("PROJECT_ORDER_CONFLICT", "\u9879\u76EE\u5217\u8868\u5DF2\u53D8\u5316\uFF0C\u8BF7\u5237\u65B0\u540E\u91CD\u65B0\u8C03\u6574\u987A\u5E8F", 409);
159
+ const now = Date.now();
160
+ const rows = projectIds.map((id, sortOrder) => ({
161
+ ...this.registration(id),
162
+ sortOrder,
163
+ updatedAt: now
164
+ }));
165
+ this.database?.transaction((tx) => {
166
+ for (const row of rows)
167
+ tx.update(projectRegistrations).set({ sortOrder: row.sortOrder, updatedAt: now }).where(eq(projectRegistrations.id, row.id)).run();
168
+ });
169
+ for (const row of rows) this.registered.set(row.id, row);
170
+ return this.list();
134
171
  }
135
172
  /** Also usable when a saved directory is missing, so it can still be managed. */
136
173
  describe(id) {
@@ -153,7 +190,12 @@ class ProjectRegistry {
153
190
  const removed = matching.find((row2) => row2.removedAt !== null);
154
191
  const now = Date.now();
155
192
  if (removed) {
156
- const restored = { ...removed, removedAt: null, updatedAt: now };
193
+ const restored = {
194
+ ...removed,
195
+ removedAt: null,
196
+ sortOrder: this.nextSortOrder(),
197
+ updatedAt: now
198
+ };
157
199
  const project = this.inspect(restored);
158
200
  if (!project.available)
159
201
  throw new AppError("PROJECT_UNAVAILABLE", project.error ?? "\u539F\u9879\u76EE\u76EE\u5F55\u4E0D\u53EF\u7528", 409);
@@ -171,6 +213,7 @@ class ProjectRegistry {
171
213
  path: canonicalRoot,
172
214
  canonicalRoot,
173
215
  removedAt: null,
216
+ sortOrder: this.nextSortOrder(),
174
217
  createdAt: this.nextCreatedAt(),
175
218
  updatedAt: now
176
219
  };
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../src/server/projects.ts"],
4
- "sourcesContent": ["import { execFileSync } from 'node:child_process';\nimport { randomUUID } from 'node:crypto';\nimport { accessSync, constants, realpathSync, statSync } from 'node:fs';\nimport { basename, isAbsolute, relative, resolve, sep } from 'node:path';\nimport { asc } from 'drizzle-orm';\nimport { AppError } from '../shared/errors.js';\nimport type { CreateProjectInput, Project, RenameProjectInput } from '../shared/types.js';\nimport { createProjectSchema, renameProjectSchema } from '../shared/validation.js';\nimport type { AppDatabase } from './db/index.js';\nimport { projectRegistrations } from './db/schema.js';\n\nexport interface ProjectConfig {\n id: string;\n name: string;\n path: string;\n}\ntype Registration = typeof projectRegistrations.$inferSelect;\n\nexport function containsPath(root: string, candidate: string): boolean {\n const difference = relative(root, candidate);\n return (\n difference === '' ||\n (difference !== '..' && !difference.startsWith(`..${sep}`) && !isAbsolute(difference))\n );\n}\n\nfunction checkoutRoot(cwd: string): string | undefined {\n try {\n const output = execFileSync('git', ['-C', cwd, 'rev-parse', '--show-toplevel'], {\n encoding: 'utf8',\n timeout: 3000,\n stdio: ['ignore', 'pipe', 'ignore'],\n }).trim();\n return output ? realpathSync(output) : undefined;\n } catch {\n return undefined;\n }\n}\n\nexport class ProjectRegistry {\n private readonly registered = new Map<string, Registration>();\n\n constructor(\n configs: ProjectConfig[],\n private readonly database?: AppDatabase,\n ) {\n for (const row of database\n ?.select()\n .from(projectRegistrations)\n .orderBy(asc(projectRegistrations.createdAt), asc(projectRegistrations.id))\n .all() ?? [])\n this.registered.set(row.id, row);\n // JSON remains a source of new registrations, while persisted UI decisions win.\n // Keep legacy aliases and IDs intact; only new UI registrations reject aliases.\n const seeds: Registration[] = [];\n for (const config of configs) {\n const path = resolve(config.path);\n const existing = this.registered.get(config.id);\n if (existing) {\n if (existing.path !== path)\n throw new AppError(\n 'PROJECT_ID_CONFLICT',\n `\u9879\u76EE ${config.id} \u5DF2\u7ED1\u5B9A\u5176\u4ED6\u76EE\u5F55\uFF1B\u9879\u76EE ID \u548C\u76EE\u5F55\u4E0D\u53EF\u4FEE\u6539\uFF0C\u8BF7\u4F7F\u7528\u65B0 ID`,\n 409,\n { projectId: config.id },\n );\n continue;\n }\n let canonicalRoot: string | null = null;\n try {\n canonicalRoot = this.directory(path);\n } catch {\n // Existing configurations with temporarily missing directories still start.\n }\n const now = this.nextCreatedAt();\n const row = {\n ...config,\n path,\n canonicalRoot,\n removedAt: null,\n createdAt: now,\n updatedAt: now,\n };\n this.registered.set(config.id, row);\n seeds.push(row);\n }\n if (seeds.length) database?.insert(projectRegistrations).values(seeds).run();\n }\n\n private nextCreatedAt(): number {\n // Preserve seed order for legacy aliases even when imported within one millisecond.\n return [...this.registered.values()].reduce(\n (next, row) => Math.max(next, row.createdAt + 1),\n Date.now(),\n );\n }\n\n private directory(path: string): string {\n if (!isAbsolute(path) || path.includes('\\0'))\n throw new AppError('INVALID_PROJECT_PATH', '\u8BF7\u8F93\u5165\u8FD0\u884C Codex \u7684\u7535\u8111\u4E0A\u7684\u7EDD\u5BF9\u76EE\u5F55\u8DEF\u5F84', 400);\n try {\n const canonicalRoot = realpathSync(path);\n if (!statSync(canonicalRoot).isDirectory())\n throw new AppError('PROJECT_UNAVAILABLE', '\u9879\u76EE\u8DEF\u5F84\u4E0D\u662F\u76EE\u5F55', 409);\n accessSync(canonicalRoot, constants.R_OK | constants.X_OK);\n return canonicalRoot;\n } catch (error) {\n if (error instanceof AppError) throw error;\n const code = (error as NodeJS.ErrnoException).code;\n throw new AppError(\n 'PROJECT_UNAVAILABLE',\n code === 'ENOENT' || code === 'ENOTDIR'\n ? '\u9879\u76EE\u76EE\u5F55\u4E0D\u5B58\u5728\uFF1B\u8BF7\u8F93\u5165\u8FD0\u884C Codex \u7684\u7535\u8111\u4E0A\u7684\u76EE\u5F55'\n : code === 'EACCES' || code === 'EPERM'\n ? '\u9879\u76EE\u76EE\u5F55\u4E0D\u53EF\u8BBF\u95EE\uFF0C\u9700\u8981\u8BFB\u53D6\u548C\u8FDB\u5165\u76EE\u5F55\u7684\u6743\u9650'\n : '\u9879\u76EE\u76EE\u5F55\u4E0D\u53EF\u7528\uFF0C\u8BF7\u68C0\u67E5\u8DEF\u5F84\u548C\u8BBF\u95EE\u6743\u9650',\n 409,\n );\n }\n }\n\n private save(row: Registration): void {\n this.database\n ?.insert(projectRegistrations)\n .values(row)\n .onConflictDoUpdate({ target: projectRegistrations.id, set: row })\n .run();\n this.registered.set(row.id, row);\n }\n\n private registration(id: string): Registration {\n const row = this.registered.get(id);\n if (!row || row.removedAt !== null)\n throw new AppError('PROJECT_NOT_FOUND', '\u9879\u76EE\u672A\u6CE8\u518C\u6216\u5DF2\u79FB\u9664', 404);\n return row;\n }\n\n private inspect(config: Registration): Project {\n const rootPath = config.path;\n try {\n const canonicalRoot = this.directory(rootPath);\n if (config.canonicalRoot !== null && canonicalRoot !== config.canonicalRoot)\n throw new AppError(\n 'PROJECT_UNAVAILABLE',\n '\u9879\u76EE\u76EE\u5F55\u7684\u771F\u5B9E\u8DEF\u5F84\u5DF2\u6539\u53D8\uFF0C\u4E0D\u80FD\u6CBF\u7528\u539F\u9879\u76EE\u8EAB\u4EFD',\n 409,\n );\n if (config.canonicalRoot === null)\n this.save({ ...config, canonicalRoot, updatedAt: Date.now() });\n return {\n id: config.id,\n name: config.name,\n rootPath,\n canonicalRoot,\n workspaceKey: checkoutRoot(canonicalRoot) ?? canonicalRoot,\n available: true,\n };\n } catch (error) {\n return {\n id: config.id,\n name: config.name,\n rootPath,\n canonicalRoot: config.canonicalRoot ?? rootPath,\n workspaceKey: config.canonicalRoot ?? rootPath,\n available: false,\n error: error instanceof Error ? error.message : '\u9879\u76EE\u4E0D\u53EF\u7528',\n };\n }\n }\n\n list(): Project[] {\n return [...this.registered.values()]\n .filter((project) => project.removedAt === null)\n .map((project) => this.inspect(project));\n }\n\n /** Also usable when a saved directory is missing, so it can still be managed. */\n describe(id: string): Project {\n return this.inspect(this.registration(id));\n }\n\n add(input: CreateProjectInput): Project {\n const parsed = createProjectSchema.parse(input);\n const canonicalRoot = this.directory(parsed.path);\n // Inspect lazily imported paths before checking, including legacy unavailable seeds.\n this.list();\n for (const row of this.registered.values()) {\n if (row.canonicalRoot !== null || row.removedAt === null) continue;\n try {\n this.save({ ...row, canonicalRoot: this.directory(row.path), updatedAt: Date.now() });\n } catch {\n // A tombstone still reserves its ID while its directory remains unavailable.\n }\n }\n const matching = [...this.registered.values()].filter(\n (row) => row.canonicalRoot === canonicalRoot,\n );\n const removed = matching.find((row) => row.removedAt !== null);\n const now = Date.now();\n if (removed) {\n // Restoring a grandfathered alias reuses an existing identity; it does not\n // create a duplicate registration even when another legacy alias is active.\n // A spelling/alias supplied during restoration does not change saved identity.\n const restored = { ...removed, removedAt: null, updatedAt: now };\n const project = this.inspect(restored);\n if (!project.available)\n throw new AppError('PROJECT_UNAVAILABLE', project.error ?? '\u539F\u9879\u76EE\u76EE\u5F55\u4E0D\u53EF\u7528', 409);\n this.save({ ...restored, name: parsed.name ?? removed.name });\n return this.get(removed.id);\n }\n const existing = matching[0];\n if (existing)\n throw new AppError('PROJECT_EXISTS', '\u8BE5\u76EE\u5F55\u5DF2\u6DFB\u52A0\uFF0C\u8BF7\u9009\u62E9\u5DF2\u6709\u9879\u76EE', 409, {\n projectId: existing.id,\n });\n const row: Registration = {\n id: randomUUID(),\n name: parsed.name ?? (basename(canonicalRoot) || canonicalRoot),\n path: canonicalRoot,\n canonicalRoot,\n removedAt: null,\n createdAt: this.nextCreatedAt(),\n updatedAt: now,\n };\n this.save(row);\n return this.get(row.id);\n }\n\n rename(id: string, input: RenameProjectInput): Project {\n const { name } = renameProjectSchema.parse(input);\n this.save({ ...this.registration(id), name, updatedAt: Date.now() });\n return this.describe(id);\n }\n\n remove(id: string): void {\n const now = Date.now();\n this.save({ ...this.registration(id), removedAt: now, updatedAt: now });\n }\n\n get(id: string): Project {\n const project = this.describe(id);\n if (!project.available)\n throw new AppError('PROJECT_UNAVAILABLE', '\u9879\u76EE\u76EE\u5F55\u4E0D\u53EF\u7528', 409, { projectId: id });\n return project;\n }\n\n assertOwnership(\n projectId: string,\n cwd: string,\n ): { project: Project; cwd: string; workspaceKey: string } {\n const project = this.get(projectId);\n let canonicalCwd: string;\n try {\n canonicalCwd = realpathSync(cwd);\n if (!statSync(canonicalCwd).isDirectory()) throw new Error('not a directory');\n } catch {\n throw new AppError('PROJECT_UNAVAILABLE', '\u4F1A\u8BDD\u5DE5\u4F5C\u76EE\u5F55\u4E0D\u53EF\u7528', 409);\n }\n if (!containsPath(project.canonicalRoot, canonicalCwd))\n throw new AppError('PROJECT_OWNERSHIP', '\u4F1A\u8BDD\u4E0D\u5C5E\u4E8E\u6240\u9009\u9879\u76EE\u76EE\u5F55', 403);\n return {\n project,\n cwd: canonicalCwd,\n workspaceKey: checkoutRoot(canonicalCwd) ?? project.canonicalRoot,\n };\n }\n\n findOwner(cwd: string): { project: Project; cwd: string; workspaceKey: string } {\n let canonicalCwd: string;\n try {\n canonicalCwd = realpathSync(cwd);\n } catch {\n throw new AppError('PROJECT_UNAVAILABLE', '\u4F1A\u8BDD\u76EE\u5F55\u4E0D\u53EF\u7528', 409);\n }\n // Tombstones reserve automatic ownership as well: a parent must not adopt\n // previously undiscovered history from a removed nested project.\n // An active grandfathered alias at the same root remains eligible; only a\n // deeper tombstone reserves history against a different, shallower directory.\n const project = [...this.registered.values()]\n .map((entry) => ({ project: this.inspect(entry), removed: entry.removedAt !== null }))\n .filter((entry) => containsPath(entry.project.canonicalRoot, canonicalCwd))\n .sort(\n (a, b) =>\n b.project.canonicalRoot.length - a.project.canonicalRoot.length ||\n Number(a.removed) - Number(b.removed),\n )[0]?.project;\n if (!project) throw new AppError('PROJECT_OWNERSHIP', '\u4F1A\u8BDD\u4E0D\u5C5E\u4E8E\u5DF2\u6CE8\u518C\u9879\u76EE', 403);\n return this.assertOwnership(project.id, canonicalCwd);\n }\n}\n"],
5
- "mappings": "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",
4
+ "sourcesContent": ["import { execFileSync } from 'node:child_process';\nimport { randomUUID } from 'node:crypto';\nimport { accessSync, constants, realpathSync, statSync } from 'node:fs';\nimport { basename, isAbsolute, relative, resolve, sep } from 'node:path';\nimport { asc, eq } from 'drizzle-orm';\nimport { AppError } from '../shared/errors.js';\nimport type {\n CreateProjectInput,\n Project,\n RenameProjectInput,\n ReorderProjectsInput,\n} from '../shared/types.js';\nimport {\n createProjectSchema,\n renameProjectSchema,\n reorderProjectsSchema,\n} from '../shared/validation.js';\nimport type { AppDatabase } from './db/index.js';\nimport { projectRegistrations } from './db/schema.js';\n\nexport interface ProjectConfig {\n id: string;\n name: string;\n path: string;\n}\ntype Registration = typeof projectRegistrations.$inferSelect;\n\nexport function containsPath(root: string, candidate: string): boolean {\n const difference = relative(root, candidate);\n return (\n difference === '' ||\n (difference !== '..' && !difference.startsWith(`..${sep}`) && !isAbsolute(difference))\n );\n}\n\nfunction checkoutRoot(cwd: string): string | undefined {\n try {\n const output = execFileSync('git', ['-C', cwd, 'rev-parse', '--show-toplevel'], {\n encoding: 'utf8',\n timeout: 3000,\n stdio: ['ignore', 'pipe', 'ignore'],\n }).trim();\n return output ? realpathSync(output) : undefined;\n } catch {\n return undefined;\n }\n}\n\nexport class ProjectRegistry {\n private readonly registered = new Map<string, Registration>();\n\n constructor(\n configs: ProjectConfig[],\n private readonly database?: AppDatabase,\n ) {\n for (const row of database\n ?.select()\n .from(projectRegistrations)\n .orderBy(\n asc(projectRegistrations.sortOrder),\n asc(projectRegistrations.createdAt),\n asc(projectRegistrations.id),\n )\n .all() ?? [])\n this.registered.set(row.id, row);\n // JSON remains a source of new registrations, while persisted UI decisions win.\n // Keep legacy aliases and IDs intact; only new UI registrations reject aliases.\n const seeds: Registration[] = [];\n for (const config of configs) {\n const path = resolve(config.path);\n const existing = this.registered.get(config.id);\n if (existing) {\n if (existing.path !== path)\n throw new AppError(\n 'PROJECT_ID_CONFLICT',\n `\u9879\u76EE ${config.id} \u5DF2\u7ED1\u5B9A\u5176\u4ED6\u76EE\u5F55\uFF1B\u9879\u76EE ID \u548C\u76EE\u5F55\u4E0D\u53EF\u4FEE\u6539\uFF0C\u8BF7\u4F7F\u7528\u65B0 ID`,\n 409,\n { projectId: config.id },\n );\n continue;\n }\n let canonicalRoot: string | null = null;\n try {\n canonicalRoot = this.directory(path);\n } catch {\n // Existing configurations with temporarily missing directories still start.\n }\n const now = this.nextCreatedAt();\n const row = {\n ...config,\n path,\n canonicalRoot,\n removedAt: null,\n sortOrder: this.nextSortOrder(),\n createdAt: now,\n updatedAt: now,\n };\n this.registered.set(config.id, row);\n seeds.push(row);\n }\n if (seeds.length) database?.insert(projectRegistrations).values(seeds).run();\n }\n\n private nextCreatedAt(): number {\n // Preserve seed order for legacy aliases even when imported within one millisecond.\n return [...this.registered.values()].reduce(\n (next, row) => Math.max(next, row.createdAt + 1),\n Date.now(),\n );\n }\n\n private nextSortOrder(): number {\n return [...this.registered.values()].reduce(\n (next, row) => Math.max(next, row.sortOrder + 1),\n 0,\n );\n }\n\n private directory(path: string): string {\n if (!isAbsolute(path) || path.includes('\\0'))\n throw new AppError('INVALID_PROJECT_PATH', '\u8BF7\u8F93\u5165\u8FD0\u884C Codex \u7684\u7535\u8111\u4E0A\u7684\u7EDD\u5BF9\u76EE\u5F55\u8DEF\u5F84', 400);\n try {\n const canonicalRoot = realpathSync(path);\n if (!statSync(canonicalRoot).isDirectory())\n throw new AppError('PROJECT_UNAVAILABLE', '\u9879\u76EE\u8DEF\u5F84\u4E0D\u662F\u76EE\u5F55', 409);\n accessSync(canonicalRoot, constants.R_OK | constants.X_OK);\n return canonicalRoot;\n } catch (error) {\n if (error instanceof AppError) throw error;\n const code = (error as NodeJS.ErrnoException).code;\n throw new AppError(\n 'PROJECT_UNAVAILABLE',\n code === 'ENOENT' || code === 'ENOTDIR'\n ? '\u9879\u76EE\u76EE\u5F55\u4E0D\u5B58\u5728\uFF1B\u8BF7\u8F93\u5165\u8FD0\u884C Codex \u7684\u7535\u8111\u4E0A\u7684\u76EE\u5F55'\n : code === 'EACCES' || code === 'EPERM'\n ? '\u9879\u76EE\u76EE\u5F55\u4E0D\u53EF\u8BBF\u95EE\uFF0C\u9700\u8981\u8BFB\u53D6\u548C\u8FDB\u5165\u76EE\u5F55\u7684\u6743\u9650'\n : '\u9879\u76EE\u76EE\u5F55\u4E0D\u53EF\u7528\uFF0C\u8BF7\u68C0\u67E5\u8DEF\u5F84\u548C\u8BBF\u95EE\u6743\u9650',\n 409,\n );\n }\n }\n\n private save(row: Registration): void {\n this.database\n ?.insert(projectRegistrations)\n .values(row)\n .onConflictDoUpdate({ target: projectRegistrations.id, set: row })\n .run();\n this.registered.set(row.id, row);\n }\n\n private registration(id: string): Registration {\n const row = this.registered.get(id);\n if (!row || row.removedAt !== null)\n throw new AppError('PROJECT_NOT_FOUND', '\u9879\u76EE\u672A\u6CE8\u518C\u6216\u5DF2\u79FB\u9664', 404);\n return row;\n }\n\n private inspect(config: Registration): Project {\n const rootPath = config.path;\n try {\n const canonicalRoot = this.directory(rootPath);\n if (config.canonicalRoot !== null && canonicalRoot !== config.canonicalRoot)\n throw new AppError(\n 'PROJECT_UNAVAILABLE',\n '\u9879\u76EE\u76EE\u5F55\u7684\u771F\u5B9E\u8DEF\u5F84\u5DF2\u6539\u53D8\uFF0C\u4E0D\u80FD\u6CBF\u7528\u539F\u9879\u76EE\u8EAB\u4EFD',\n 409,\n );\n if (config.canonicalRoot === null)\n this.save({ ...config, canonicalRoot, updatedAt: Date.now() });\n return {\n id: config.id,\n name: config.name,\n rootPath,\n canonicalRoot,\n workspaceKey: checkoutRoot(canonicalRoot) ?? canonicalRoot,\n available: true,\n };\n } catch (error) {\n return {\n id: config.id,\n name: config.name,\n rootPath,\n canonicalRoot: config.canonicalRoot ?? rootPath,\n workspaceKey: config.canonicalRoot ?? rootPath,\n available: false,\n error: error instanceof Error ? error.message : '\u9879\u76EE\u4E0D\u53EF\u7528',\n };\n }\n }\n\n list(): Project[] {\n return [...this.registered.values()]\n .filter((project) => project.removedAt === null)\n .sort(\n (a, b) =>\n a.sortOrder - b.sortOrder || a.createdAt - b.createdAt || a.id.localeCompare(b.id),\n )\n .map((project) => this.inspect(project));\n }\n\n reorder(input: ReorderProjectsInput): Project[] {\n const { projectIds } = reorderProjectsSchema.parse(input);\n const active = [...this.registered.values()].filter((row) => row.removedAt === null);\n if (\n projectIds.length !== active.length ||\n projectIds.some(\n (id) => !this.registered.has(id) || this.registered.get(id)!.removedAt !== null,\n )\n )\n throw new AppError('PROJECT_ORDER_CONFLICT', '\u9879\u76EE\u5217\u8868\u5DF2\u53D8\u5316\uFF0C\u8BF7\u5237\u65B0\u540E\u91CD\u65B0\u8C03\u6574\u987A\u5E8F', 409);\n const now = Date.now();\n const rows = projectIds.map((id, sortOrder) => ({\n ...this.registration(id),\n sortOrder,\n updatedAt: now,\n }));\n // Commit the complete order atomically, then update memory only after success.\n this.database?.transaction((tx) => {\n for (const row of rows)\n tx.update(projectRegistrations)\n .set({ sortOrder: row.sortOrder, updatedAt: now })\n .where(eq(projectRegistrations.id, row.id))\n .run();\n });\n for (const row of rows) this.registered.set(row.id, row);\n return this.list();\n }\n\n /** Also usable when a saved directory is missing, so it can still be managed. */\n describe(id: string): Project {\n return this.inspect(this.registration(id));\n }\n\n add(input: CreateProjectInput): Project {\n const parsed = createProjectSchema.parse(input);\n const canonicalRoot = this.directory(parsed.path);\n // Inspect lazily imported paths before checking, including legacy unavailable seeds.\n this.list();\n for (const row of this.registered.values()) {\n if (row.canonicalRoot !== null || row.removedAt === null) continue;\n try {\n this.save({ ...row, canonicalRoot: this.directory(row.path), updatedAt: Date.now() });\n } catch {\n // A tombstone still reserves its ID while its directory remains unavailable.\n }\n }\n const matching = [...this.registered.values()].filter(\n (row) => row.canonicalRoot === canonicalRoot,\n );\n const removed = matching.find((row) => row.removedAt !== null);\n const now = Date.now();\n if (removed) {\n // Restoring a grandfathered alias reuses an existing identity; it does not\n // create a duplicate registration even when another legacy alias is active.\n // A spelling/alias supplied during restoration does not change saved identity.\n const restored = {\n ...removed,\n removedAt: null,\n sortOrder: this.nextSortOrder(),\n updatedAt: now,\n };\n const project = this.inspect(restored);\n if (!project.available)\n throw new AppError('PROJECT_UNAVAILABLE', project.error ?? '\u539F\u9879\u76EE\u76EE\u5F55\u4E0D\u53EF\u7528', 409);\n this.save({ ...restored, name: parsed.name ?? removed.name });\n return this.get(removed.id);\n }\n const existing = matching[0];\n if (existing)\n throw new AppError('PROJECT_EXISTS', '\u8BE5\u76EE\u5F55\u5DF2\u6DFB\u52A0\uFF0C\u8BF7\u9009\u62E9\u5DF2\u6709\u9879\u76EE', 409, {\n projectId: existing.id,\n });\n const row: Registration = {\n id: randomUUID(),\n name: parsed.name ?? (basename(canonicalRoot) || canonicalRoot),\n path: canonicalRoot,\n canonicalRoot,\n removedAt: null,\n sortOrder: this.nextSortOrder(),\n createdAt: this.nextCreatedAt(),\n updatedAt: now,\n };\n this.save(row);\n return this.get(row.id);\n }\n\n rename(id: string, input: RenameProjectInput): Project {\n const { name } = renameProjectSchema.parse(input);\n this.save({ ...this.registration(id), name, updatedAt: Date.now() });\n return this.describe(id);\n }\n\n remove(id: string): void {\n const now = Date.now();\n this.save({ ...this.registration(id), removedAt: now, updatedAt: now });\n }\n\n get(id: string): Project {\n const project = this.describe(id);\n if (!project.available)\n throw new AppError('PROJECT_UNAVAILABLE', '\u9879\u76EE\u76EE\u5F55\u4E0D\u53EF\u7528', 409, { projectId: id });\n return project;\n }\n\n assertOwnership(\n projectId: string,\n cwd: string,\n ): { project: Project; cwd: string; workspaceKey: string } {\n const project = this.get(projectId);\n let canonicalCwd: string;\n try {\n canonicalCwd = realpathSync(cwd);\n if (!statSync(canonicalCwd).isDirectory()) throw new Error('not a directory');\n } catch {\n throw new AppError('PROJECT_UNAVAILABLE', '\u4F1A\u8BDD\u5DE5\u4F5C\u76EE\u5F55\u4E0D\u53EF\u7528', 409);\n }\n if (!containsPath(project.canonicalRoot, canonicalCwd))\n throw new AppError('PROJECT_OWNERSHIP', '\u4F1A\u8BDD\u4E0D\u5C5E\u4E8E\u6240\u9009\u9879\u76EE\u76EE\u5F55', 403);\n return {\n project,\n cwd: canonicalCwd,\n workspaceKey: checkoutRoot(canonicalCwd) ?? project.canonicalRoot,\n };\n }\n\n findOwner(cwd: string): { project: Project; cwd: string; workspaceKey: string } {\n let canonicalCwd: string;\n try {\n canonicalCwd = realpathSync(cwd);\n } catch {\n throw new AppError('PROJECT_UNAVAILABLE', '\u4F1A\u8BDD\u76EE\u5F55\u4E0D\u53EF\u7528', 409);\n }\n // Tombstones reserve automatic ownership as well: a parent must not adopt\n // previously undiscovered history from a removed nested project.\n // An active grandfathered alias at the same root remains eligible; only a\n // deeper tombstone reserves history against a different, shallower directory.\n const project = [...this.registered.values()]\n .map((entry) => ({ project: this.inspect(entry), removed: entry.removedAt !== null }))\n .filter((entry) => containsPath(entry.project.canonicalRoot, canonicalCwd))\n .sort(\n (a, b) =>\n b.project.canonicalRoot.length - a.project.canonicalRoot.length ||\n Number(a.removed) - Number(b.removed),\n )[0]?.project;\n if (!project) throw new AppError('PROJECT_OWNERSHIP', '\u4F1A\u8BDD\u4E0D\u5C5E\u4E8E\u5DF2\u6CE8\u518C\u9879\u76EE', 403);\n return this.assertOwnership(project.id, canonicalCwd);\n }\n}\n"],
5
+ "mappings": "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",
6
6
  "names": ["row"]
7
7
  }
@@ -143,6 +143,11 @@ class GatewayService {
143
143
  this.events.publishProjects(this.projects.list());
144
144
  return project;
145
145
  }
146
+ reorderProjects(input) {
147
+ const projects = this.projects.reorder(input);
148
+ this.events.publishProjects(projects);
149
+ return { data: projects };
150
+ }
146
151
  removeProject(projectId) {
147
152
  const project = this.projects.describe(projectId);
148
153
  const pending = this.store.pendingProjectOperations(projectId)[0];