@covro/covro 0.3.1-dev.1 → 0.3.1-dev.3
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 +16 -10
- package/README.zh-CN.md +16 -10
- package/dist/drizzle/0006_project_order.sql +8 -0
- package/dist/drizzle/meta/0006_snapshot.json +488 -0
- package/dist/drizzle/meta/_journal.json +7 -0
- package/dist/server/config-store.js +10 -2
- package/dist/server/config-store.js.map +2 -2
- package/dist/server/config.js +4 -0
- package/dist/server/config.js.map +2 -2
- package/dist/server/db/schema.js +1 -0
- package/dist/server/db/schema.js.map +2 -2
- package/dist/server/http.js +43 -5
- package/dist/server/http.js.map +2 -2
- package/dist/server/local-files.js.map +1 -1
- package/dist/server/project-clones.js +2 -2
- package/dist/server/project-clones.js.map +2 -2
- package/dist/server/project-paths.js +39 -0
- package/dist/server/project-paths.js.map +7 -0
- package/dist/server/projects.js +48 -5
- package/dist/server/projects.js.map +2 -2
- package/dist/server/service.js +15 -3
- package/dist/server/service.js.map +2 -2
- package/dist/server/text-files.js +32 -0
- package/dist/server/text-files.js.map +7 -0
- package/dist/shared/file-preview.js +22 -0
- package/dist/shared/file-preview.js.map +7 -0
- package/dist/shared/project-path.js +8 -0
- package/dist/shared/project-path.js.map +7 -0
- package/dist/shared/release-notes.js +9 -0
- package/dist/shared/release-notes.js.map +7 -0
- package/dist/shared/updates.js.map +2 -2
- package/dist/shared/validation.js +2 -0
- package/dist/shared/validation.js.map +2 -2
- package/dist/update-worker.cjs +8 -3
- package/dist/updates/manager.js +7 -4
- package/dist/updates/manager.js.map +2 -2
- package/dist/updates/registry.js +21 -9
- package/dist/updates/registry.js.map +3 -3
- package/dist/updates/release-notes.js +73 -0
- package/dist/updates/release-notes.js.map +7 -0
- package/dist/updates/worker.js +3 -3
- package/dist/updates/worker.js.map +2 -2
- package/dist/web/assets/{abnfDiagram-WEYOIA3C-DLvlob5t.js → abnfDiagram-WEYOIA3C-CgSmgOr1.js} +1 -1
- package/dist/web/assets/{arc-DgauwgFe.js → arc-DHf4CdSk.js} +1 -1
- package/dist/web/assets/{architecture-WOLXFQ4H-DvjLv0QN.js → architecture-WOLXFQ4H-DMTD1pHe.js} +1 -1
- package/dist/web/assets/{architectureDiagram-ZHVYKT53-BAyZGlzu.js → architectureDiagram-ZHVYKT53-DTR8wiWo.js} +1 -1
- package/dist/web/assets/{blockDiagram-RJAE7QA3-BVfkmjlt.js → blockDiagram-RJAE7QA3-DcNiTPKZ.js} +1 -1
- package/dist/web/assets/{c4Diagram-XDPPPE54-rTsfyMoB.js → c4Diagram-XDPPPE54-BDxJVXjQ.js} +1 -1
- package/dist/web/assets/channel-B6d61z43.js +1 -0
- package/dist/web/assets/{chunk-2E7WHVMD-CQ_N_vLS.js → chunk-2E7WHVMD-D5oKHRGT.js} +1 -1
- package/dist/web/assets/{chunk-4EMNKR2T-wBy_gyo6.js → chunk-4EMNKR2T-CtrbVCM-.js} +1 -1
- package/dist/web/assets/{chunk-55XCU76K-DYiwIhmI.js → chunk-55XCU76K-Dhl7DEL7.js} +1 -1
- package/dist/web/assets/{chunk-7S6XSTFO-CRGtfVHg.js → chunk-7S6XSTFO-C8EwegP-.js} +1 -1
- package/dist/web/assets/{chunk-AQDTSKZB-1G2Za6bm.js → chunk-AQDTSKZB-lFiNKr7B.js} +1 -1
- package/dist/web/assets/{chunk-DSNLSHJZ-B1LLoYuG.js → chunk-DSNLSHJZ-B8GftONA.js} +1 -1
- package/dist/web/assets/{chunk-HAGFOTED-78h1RpHh.js → chunk-HAGFOTED-Cnc2mh7_.js} +1 -1
- package/dist/web/assets/{chunk-HNE4Q36P-DmR5B27Q.js → chunk-HNE4Q36P-BKl6ZgI0.js} +1 -1
- package/dist/web/assets/{chunk-NQOKRVIF-D7WSEPzg.js → chunk-NQOKRVIF-qKlxSw3D.js} +1 -1
- package/dist/web/assets/{chunk-UHW7PFBP-gl7a5V-l.js → chunk-UHW7PFBP-C_zh3mNe.js} +1 -1
- package/dist/web/assets/{chunk-VQ5W62QI-KaBXxBre.js → chunk-VQ5W62QI-BXoyXK0q.js} +1 -1
- package/dist/web/assets/{chunk-WQQL4CDK-DDczFoOr.js → chunk-WQQL4CDK-BBeqcU1J.js} +1 -1
- package/dist/web/assets/{chunk-XXDRQBXY-3ug4Qhxg.js → chunk-XXDRQBXY-DzssZtz_.js} +1 -1
- package/dist/web/assets/{chunk-YZO67HFG-BpWOuCXP.js → chunk-YZO67HFG-BbNSwLjS.js} +1 -1
- package/dist/web/assets/{chunk-ZAUX2LTX-1ojPb1if.js → chunk-ZAUX2LTX-CjURwZfv.js} +1 -1
- package/dist/web/assets/{chunk-ZCHMQSOR-D2kK2cPj.js → chunk-ZCHMQSOR-u39dymjE.js} +1 -1
- package/dist/web/assets/{classDiagram-v2-4MYWR3UA-B7eMvxAO.js → classDiagram-v2-4MYWR3UA-BEXx5gKT.js} +1 -1
- package/dist/web/assets/{cose-bilkent-JH36ORCC-BtB8mbt8.js → cose-bilkent-JH36ORCC-BxDEucP_.js} +1 -1
- package/dist/web/assets/{cynefin-EF2NZ3EQ-CIctqxlE.js → cynefin-EF2NZ3EQ-BhkCsT6-.js} +1 -1
- package/dist/web/assets/{cynefinDiagram-WF24UDRP-qat2GD9P.js → cynefinDiagram-WF24UDRP-Wpu-yNke.js} +1 -1
- package/dist/web/assets/{dagre-NK4GIZVX-OJamzzID.js → dagre-NK4GIZVX-LNSSIN4l.js} +1 -1
- package/dist/web/assets/{diagram-CBK3MWTC-C7cX2tqO.js → diagram-CBK3MWTC-BVDr5Vx9.js} +1 -1
- package/dist/web/assets/{diagram-I5XIIV2D-DsMfKVN7.js → diagram-I5XIIV2D-MnLzR1Rv.js} +1 -1
- package/dist/web/assets/{diagram-ODIL2UCW-Cz66FxdJ.js → diagram-ODIL2UCW-CZ4igbo_.js} +1 -1
- package/dist/web/assets/{diagram-PFBEJUXE-CwhPuwKj.js → diagram-PFBEJUXE-4hCnyMdK.js} +1 -1
- package/dist/web/assets/{diagram-RJA3I4VV-C52IGiFJ.js → diagram-RJA3I4VV-DTo42rGA.js} +1 -1
- package/dist/web/assets/{diagram-TYUNFSNR-C2LZVLKY.js → diagram-TYUNFSNR-BC12iCUZ.js} +1 -1
- package/dist/web/assets/{dist-Bxu8YbZb.js → dist-Dg_ysSxT.js} +1 -1
- package/dist/web/assets/{ebnfDiagram-V4P3TTPT-eQKKlMli.js → ebnfDiagram-V4P3TTPT-Du8LCQ31.js} +1 -1
- package/dist/web/assets/{elk-QONTBCTQ-C5kIOZTA.js → elk-QONTBCTQ-BzTAR-T1.js} +1 -1
- package/dist/web/assets/{erDiagram-KYQIGQAA-CMTowz5P.js → erDiagram-KYQIGQAA-SXYIjNOP.js} +1 -1
- package/dist/web/assets/{eventmodeling-K75KTNOO-C5v-UF8X.js → eventmodeling-K75KTNOO-Sbno3ZxQ.js} +1 -1
- package/dist/web/assets/flowDiagram-IUGLLWBU-Ae1H3vSR.js +1 -0
- package/dist/web/assets/{ganttDiagram-R7GPNTJY-B0viiC-b.js → ganttDiagram-R7GPNTJY-DBeX1qmu.js} +1 -1
- package/dist/web/assets/{gitGraph-VSP46ZUC-tEjFmPhN.js → gitGraph-VSP46ZUC-CKWihueS.js} +1 -1
- package/dist/web/assets/{gitGraphDiagram-PDYL7XHY-CsOwt7jz.js → gitGraphDiagram-PDYL7XHY-Cj1E3jqN.js} +1 -1
- package/dist/web/assets/index-CtTuHkNv.js +147 -0
- package/dist/web/assets/index-DwnjYADO.css +1 -0
- package/dist/web/assets/{info-OHQRW6UA-DnFGu4Ew.js → info-OHQRW6UA-D0w3nR0p.js} +1 -1
- package/dist/web/assets/{infoDiagram-MVDRKAX5-B6WiNaDF.js → infoDiagram-MVDRKAX5-F0jTCG-G.js} +1 -1
- package/dist/web/assets/{ishikawaDiagram-HPEHV2GE-Dn70fI1r.js → ishikawaDiagram-HPEHV2GE-CwQGP9AT.js} +1 -1
- package/dist/web/assets/{journeyDiagram-BOSHW7O7-BUjYzV2f.js → journeyDiagram-BOSHW7O7-CPY5GqD2.js} +1 -1
- package/dist/web/assets/{kanban-definition-FNSA4HC5-BDMfWbtB.js → kanban-definition-FNSA4HC5-BCL38eXh.js} +1 -1
- package/dist/web/assets/{line-DNSRZHBK.js → line-DoUzbVJS.js} +1 -1
- package/dist/web/assets/{linear-CrS49q6J.js → linear-LoqU-SEO.js} +1 -1
- package/dist/web/assets/{mermaid-parser.core-DXuyO8Px.js → mermaid-parser.core-CvO-MTG1.js} +3 -3
- package/dist/web/assets/{mermaid.core-DUTnbQkq.js → mermaid.core-DcCEGt0V.js} +4 -4
- package/dist/web/assets/{mindmap-definition-6OY57UG3-CqZTtItJ.js → mindmap-definition-6OY57UG3-yZvSibNz.js} +1 -1
- package/dist/web/assets/{packet-JDAUHWVQ-DXEZgY0a.js → packet-JDAUHWVQ-9ZiCegsV.js} +1 -1
- package/dist/web/assets/{pegDiagram-ECIUJNBX-D6CCPNem.js → pegDiagram-ECIUJNBX-BkGwBoEk.js} +1 -1
- package/dist/web/assets/{pie-XZMESJXO-AXRFjZ4G.js → pie-XZMESJXO-kAXIwFnU.js} +1 -1
- package/dist/web/assets/{pieDiagram-GNMPY74T-B9Ss8_5s.js → pieDiagram-GNMPY74T-jiwTgtdb.js} +1 -1
- package/dist/web/assets/{quadrantDiagram-D57K4MC3-JrlylCtz.js → quadrantDiagram-D57K4MC3-u9HdbUwr.js} +1 -1
- package/dist/web/assets/{radar-ABXABTNO-ChFm4hZg.js → radar-ABXABTNO-BQ911CvQ.js} +1 -1
- package/dist/web/assets/{railroad-I3PHUGI6-DaXXzqhZ.js → railroad-I3PHUGI6-Bvm7MWf9.js} +1 -1
- package/dist/web/assets/{railroad-abnf-LNEFI6M7-DGE67q11.js → railroad-abnf-LNEFI6M7-BfFmWnzu.js} +1 -1
- package/dist/web/assets/{railroad-ebnf-DWYD2UWJ-Cq9S3q72.js → railroad-ebnf-DWYD2UWJ-B8Ekz4aD.js} +1 -1
- package/dist/web/assets/{railroad-peg-JR7BVNR7-D2Gth3eY.js → railroad-peg-JR7BVNR7-B5ANjRbC.js} +1 -1
- package/dist/web/assets/{railroadDiagram-Y72NRBL7-BpkXX59H.js → railroadDiagram-Y72NRBL7-iu8Hmuma.js} +1 -1
- package/dist/web/assets/{requirementDiagram-EQYSW5RA-oNp3DMM4.js → requirementDiagram-EQYSW5RA-DgiEaaYZ.js} +1 -1
- package/dist/web/assets/{sankeyDiagram-SY24JHO5-bHfheSKO.js → sankeyDiagram-SY24JHO5-KzDvhd8J.js} +1 -1
- package/dist/web/assets/{sequenceDiagram-BLEHNHQU-CEM2XfNF.js → sequenceDiagram-BLEHNHQU-DJMSHuO_.js} +1 -1
- package/dist/web/assets/{src-KjMctvzy.js → src-C5WkvxTS.js} +1 -1
- package/dist/web/assets/{stateDiagram-v2-A6IESKWZ-D_TaR_ix.js → stateDiagram-v2-A6IESKWZ-CyfHfUyU.js} +1 -1
- package/dist/web/assets/{swimlanes-B3ENRKFQ-BUYl2fsg.js → swimlanes-B3ENRKFQ-YPSzHf2U.js} +1 -1
- package/dist/web/assets/swimlanesDiagram-AYQRV4AK-D2_NQS-o.js +8 -0
- package/dist/web/assets/{timeline-definition-JXYKGLGB-CpKsGN6d.js → timeline-definition-JXYKGLGB-BwvPIiSj.js} +1 -1
- package/dist/web/assets/{treeView-D4JQ5SDB-Dioaagqt.js → treeView-D4JQ5SDB-DsUhEw24.js} +1 -1
- package/dist/web/assets/{treemap-SAJKECNS-B8NLQ14o.js → treemap-SAJKECNS-D5NThB4V.js} +1 -1
- package/dist/web/assets/{usecaseDiagram-WPOLBNNX-CfwdxcsG.js → usecaseDiagram-WPOLBNNX-fLWGvuS0.js} +1 -1
- package/dist/web/assets/{vennDiagram-Q3Y3IDCW-DQoKGtXo.js → vennDiagram-Q3Y3IDCW-DFzlk_u2.js} +1 -1
- package/dist/web/assets/{wardley-7MLQ67FV-DQHLHmeZ.js → wardley-7MLQ67FV-0uG-noJX.js} +1 -1
- package/dist/web/assets/{wardleyDiagram-IVAIDKWA-DV-lq7zb.js → wardleyDiagram-IVAIDKWA-DfEQPTLg.js} +1 -1
- package/dist/web/assets/{xychartDiagram-4URNHDU4-wV4ueoGC.js → xychartDiagram-4URNHDU4-BXVsWbL0.js} +1 -1
- package/dist/web/index.html +2 -2
- package/package.json +4 -4
- package/dist/web/assets/channel-GGfPocHk.js +0 -1
- package/dist/web/assets/flowDiagram-IUGLLWBU-C-jE8afx.js +0 -1
- package/dist/web/assets/index-COSho7m2.css +0 -1
- package/dist/web/assets/index-D7GZPtTj.js +0 -147
- package/dist/web/assets/swimlanesDiagram-AYQRV4AK-DDLmyR4x.js +0 -8
|
@@ -2,18 +2,23 @@ import { createHash, randomUUID } from "node:crypto";
|
|
|
2
2
|
import { constants } from "node:fs";
|
|
3
3
|
import { access, lstat, open, readFile, rename, rm } from "node:fs/promises";
|
|
4
4
|
import { dirname, join } from "node:path";
|
|
5
|
+
import { isDeepStrictEqual } from "node:util";
|
|
5
6
|
import lockfile from "proper-lockfile";
|
|
6
7
|
import { z } from "zod";
|
|
7
8
|
import { AppError } from "../shared/errors.js";
|
|
8
9
|
import { configSource, parseConfig, parseFileConfig } from "./config.js";
|
|
10
|
+
import { assertWorkDir } from "./project-paths.js";
|
|
9
11
|
const saveSchema = z.object({ revision: z.string().regex(/^[a-f0-9]{64}$/), config: z.unknown() }).strict();
|
|
10
12
|
const importSchema = z.object({ config: z.unknown() }).strict();
|
|
11
13
|
const revision = (text) => createHash("sha256").update(text).digest("hex");
|
|
12
|
-
const same =
|
|
14
|
+
const same = isDeepStrictEqual;
|
|
13
15
|
const MAX_CONFIG_BYTES = 256 * 1024;
|
|
14
16
|
class ConfigStore {
|
|
15
17
|
source;
|
|
16
18
|
running;
|
|
19
|
+
get workDir() {
|
|
20
|
+
return this.running.workDir;
|
|
21
|
+
}
|
|
17
22
|
constructor(config) {
|
|
18
23
|
this.source = configSource(config);
|
|
19
24
|
const { demo: _demo, ...running } = config;
|
|
@@ -67,7 +72,7 @@ class ConfigStore {
|
|
|
67
72
|
path: this.source.path,
|
|
68
73
|
revision: file.revision,
|
|
69
74
|
saved: file.saved,
|
|
70
|
-
running: this.running,
|
|
75
|
+
running: structuredClone(this.running),
|
|
71
76
|
restartRequired: !same(effective, this.running),
|
|
72
77
|
writable: !readOnlyReason,
|
|
73
78
|
...readOnlyReason ? { readOnlyReason } : {}
|
|
@@ -111,6 +116,7 @@ class ConfigStore {
|
|
|
111
116
|
"\u6570\u636E\u76EE\u5F55\u4E0D\u53EF\u5728\u6B64\u4FEE\u6539\uFF1B\u9879\u76EE\u8BF7\u4F7F\u7528\u9879\u76EE\u7BA1\u7406\u529F\u80FD\u3002",
|
|
112
117
|
400
|
|
113
118
|
);
|
|
119
|
+
if (config.workDir && config.workDir !== current.saved.workDir) assertWorkDir(config.workDir);
|
|
114
120
|
const contents = `${JSON.stringify(config, null, 2)}
|
|
115
121
|
`;
|
|
116
122
|
if (Buffer.byteLength(contents) > MAX_CONFIG_BYTES)
|
|
@@ -126,6 +132,8 @@ class ConfigStore {
|
|
|
126
132
|
if (latest.revision !== current.revision || !latest.writable)
|
|
127
133
|
throw new AppError("CONFIG_CONFLICT", "\u914D\u7F6E\u6587\u4EF6\u5DF2\u53D1\u751F\u53D8\u5316\uFF0C\u8BF7\u91CD\u65B0\u8BFB\u53D6\u540E\u518D\u4FDD\u5B58\u3002", 409);
|
|
128
134
|
await rename(temporary, this.source.path);
|
|
135
|
+
if (config.workDir) this.running.workDir = config.workDir;
|
|
136
|
+
else delete this.running.workDir;
|
|
129
137
|
return await this.snapshot();
|
|
130
138
|
} catch (error) {
|
|
131
139
|
if (error instanceof AppError) throw error;
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../src/server/config-store.ts"],
|
|
4
|
-
"sourcesContent": ["import { createHash, randomUUID } from 'node:crypto';\nimport { constants } from 'node:fs';\nimport { access, lstat, open, readFile, rename, rm } from 'node:fs/promises';\nimport { dirname, join } from 'node:path';\nimport lockfile from 'proper-lockfile';\nimport { z } from 'zod';\nimport { AppError } from '../shared/errors.js';\nimport type { GatewayConfig, GatewayConfigSnapshot } from '../shared/gateway-config.js';\nimport { configSource, parseConfig, parseFileConfig, type AppConfig } from './config.js';\n\nconst saveSchema = z\n .object({ revision: z.string().regex(/^[a-f0-9]{64}$/), config: z.unknown() })\n .strict();\nconst importSchema = z.object({ config: z.unknown() }).strict();\nconst revision = (text: string) => createHash('sha256').update(text).digest('hex');\nconst same =
|
|
5
|
-
"mappings": "AAAA,SAAS,YAAY,kBAAkB;AACvC,SAAS,iBAAiB;AAC1B,SAAS,QAAQ,OAAO,MAAM,UAAU,QAAQ,UAAU;AAC1D,SAAS,SAAS,YAAY;AAC9B,OAAO,cAAc;AACrB,SAAS,SAAS;AAClB,SAAS,gBAAgB;AAEzB,SAAS,cAAc,aAAa,uBAAuC;
|
|
4
|
+
"sourcesContent": ["import { createHash, randomUUID } from 'node:crypto';\nimport { constants } from 'node:fs';\nimport { access, lstat, open, readFile, rename, rm } from 'node:fs/promises';\nimport { dirname, join } from 'node:path';\nimport { isDeepStrictEqual } from 'node:util';\nimport lockfile from 'proper-lockfile';\nimport { z } from 'zod';\nimport { AppError } from '../shared/errors.js';\nimport type { GatewayConfig, GatewayConfigSnapshot } from '../shared/gateway-config.js';\nimport { configSource, parseConfig, parseFileConfig, type AppConfig } from './config.js';\nimport { assertWorkDir } from './project-paths.js';\n\nconst saveSchema = z\n .object({ revision: z.string().regex(/^[a-f0-9]{64}$/), config: z.unknown() })\n .strict();\nconst importSchema = z.object({ config: z.unknown() }).strict();\nconst revision = (text: string) => createHash('sha256').update(text).digest('hex');\nconst same = isDeepStrictEqual;\nexport const MAX_CONFIG_BYTES = 256 * 1024;\n\n/** Service settings apply on restart; workDir applies immediately to future imports. */\nexport class ConfigStore {\n private readonly source;\n private readonly running: GatewayConfig;\n get workDir() {\n return this.running.workDir;\n }\n constructor(config: AppConfig) {\n this.source = configSource(config);\n const { demo: _demo, ...running } = config;\n this.running = structuredClone(running);\n }\n\n private async read() {\n if (!this.source)\n throw new AppError('CONFIG_UNAVAILABLE', '\u6B64\u5B9E\u4F8B\u672A\u6307\u5B9A\u914D\u7F6E\u6587\u4EF6\uFF0C\u65E0\u6CD5\u7BA1\u7406\u670D\u52A1\u914D\u7F6E\u3002', 503);\n try {\n const stat = await lstat(this.source.path);\n if (stat.size > MAX_CONFIG_BYTES)\n throw new AppError('INVALID_CONFIG', '\u914D\u7F6E\u6587\u4EF6\u4E0D\u80FD\u8D85\u8FC7 256 KiB\u3002');\n const text = await readFile(this.source.path, 'utf8');\n if (Buffer.byteLength(text) > MAX_CONFIG_BYTES)\n throw new AppError('INVALID_CONFIG', '\u914D\u7F6E\u6587\u4EF6\u4E0D\u80FD\u8D85\u8FC7 256 KiB\u3002');\n let value: unknown;\n try {\n value = JSON.parse(text);\n } catch {\n throw new AppError('INVALID_CONFIG', '\u914D\u7F6E\u6587\u4EF6\u4E0D\u662F\u6709\u6548\u7684 JSON\uFF0C\u8BF7\u5148\u4FEE\u590D\u6587\u4EF6\u3002');\n }\n return { saved: parseFileConfig(value), revision: revision(text), stat };\n } catch (error) {\n if (error instanceof AppError) throw error;\n throw new AppError(\n 'CONFIG_READ_FAILED',\n '\u65E0\u6CD5\u8BFB\u53D6\u542F\u52A8\u65F6\u6307\u5B9A\u7684\u914D\u7F6E\u6587\u4EF6\uFF0C\u8BF7\u68C0\u67E5\u6587\u4EF6\u548C\u6743\u9650\u3002',\n 503,\n );\n }\n }\n\n async snapshot(): Promise<GatewayConfigSnapshot> {\n const file = await this.read();\n let readOnlyReason: string | undefined;\n if (!file.stat.isFile())\n readOnlyReason = '\u8F6F\u94FE\u63A5\u6216\u7279\u6B8A\u914D\u7F6E\u6587\u4EF6\u53EA\u652F\u6301\u67E5\u770B\u548C\u5BFC\u51FA\uFF0C\u8BF7\u76F4\u63A5\u7F16\u8F91\u6E90\u6587\u4EF6\u3002';\n else {\n try {\n await access(this.source!.path, constants.W_OK);\n await access(dirname(this.source!.path), constants.W_OK);\n } catch {\n readOnlyReason = '\u914D\u7F6E\u6587\u4EF6\u6216\u6240\u5728\u76EE\u5F55\u4E0D\u53EF\u5199\uFF0C\u8BF7\u8C03\u6574\u670D\u52A1\u8FDB\u7A0B\u7684\u6587\u4EF6\u6743\u9650\u3002';\n }\n }\n const { demo: _demo, ...effective } = parseConfig(\n file.saved,\n this.source!.path,\n this.source!.development,\n );\n return {\n path: this.source!.path,\n revision: file.revision,\n saved: file.saved,\n running: structuredClone(this.running),\n restartRequired: !same(effective, this.running),\n writable: !readOnlyReason,\n ...(readOnlyReason ? { readOnlyReason } : {}),\n };\n }\n\n async previewImport(input: unknown) {\n const imported = parseFileConfig(importSchema.parse(input).config);\n const { saved } = await this.read();\n const preserved = (['dataDir', 'projects'] as const).filter(\n (key) => !same(imported[key], saved[key]),\n );\n return { config: { ...imported, dataDir: saved.dataDir, projects: saved.projects }, preserved };\n }\n\n async export() {\n return (await this.read()).saved;\n }\n\n async save(input: unknown): Promise<GatewayConfigSnapshot> {\n const parsed = saveSchema.parse(input);\n const config = parseFileConfig(parsed.config);\n const initial = await this.snapshot();\n if (!initial.writable) throw new AppError('CONFIG_READ_ONLY', initial.readOnlyReason!, 409);\n let release: (() => Promise<void>) | undefined;\n const temporary = join(dirname(this.source!.path), `.codex-config-${randomUUID()}.tmp`);\n try {\n try {\n release = await lockfile.lock(this.source!.path, { realpath: false, retries: 0 });\n } catch {\n throw new AppError('CONFIG_BUSY', '\u914D\u7F6E\u6B63\u5728\u88AB\u5176\u4ED6\u64CD\u4F5C\u4FDD\u5B58\uFF0C\u8BF7\u7A0D\u540E\u91CD\u8BD5\u3002', 409);\n }\n const current = await this.snapshot();\n if (!current.writable) throw new AppError('CONFIG_READ_ONLY', current.readOnlyReason!, 409);\n if (current.revision !== parsed.revision)\n throw new AppError(\n 'CONFIG_CONFLICT',\n '\u914D\u7F6E\u6587\u4EF6\u5DF2\u88AB\u5176\u4ED6\u9875\u9762\u6216\u7A0B\u5E8F\u4FEE\u6539\u3002\u8BF7\u91CD\u65B0\u8BFB\u53D6\u540E\u518D\u4FDD\u5B58\u3002',\n 409,\n );\n if (\n config.dataDir !== current.saved.dataDir ||\n !same(config.projects, current.saved.projects)\n )\n throw new AppError(\n 'CONFIG_PROTECTED',\n '\u6570\u636E\u76EE\u5F55\u4E0D\u53EF\u5728\u6B64\u4FEE\u6539\uFF1B\u9879\u76EE\u8BF7\u4F7F\u7528\u9879\u76EE\u7BA1\u7406\u529F\u80FD\u3002',\n 400,\n );\n if (config.workDir && config.workDir !== current.saved.workDir) assertWorkDir(config.workDir);\n const contents = `${JSON.stringify(config, null, 2)}\\n`;\n if (Buffer.byteLength(contents) > MAX_CONFIG_BYTES)\n throw new AppError('INVALID_CONFIG', '\u914D\u7F6E\u6587\u4EF6\u4E0D\u80FD\u8D85\u8FC7 256 KiB\u3002');\n const file = await open(temporary, 'wx', 0o600);\n try {\n await file.writeFile(contents, 'utf8');\n await file.sync();\n } finally {\n await file.close();\n }\n // Catch external editors that changed the file while the temporary file was written.\n const latest = await this.snapshot();\n if (latest.revision !== current.revision || !latest.writable)\n throw new AppError('CONFIG_CONFLICT', '\u914D\u7F6E\u6587\u4EF6\u5DF2\u53D1\u751F\u53D8\u5316\uFF0C\u8BF7\u91CD\u65B0\u8BFB\u53D6\u540E\u518D\u4FDD\u5B58\u3002', 409);\n await rename(temporary, this.source!.path);\n if (config.workDir) this.running.workDir = config.workDir;\n else delete this.running.workDir;\n return await this.snapshot();\n } catch (error) {\n if (error instanceof AppError) throw error;\n throw new AppError(\n 'CONFIG_WRITE_FAILED',\n '\u4FDD\u5B58\u914D\u7F6E\u5931\u8D25\uFF0C\u8BF7\u68C0\u67E5\u6587\u4EF6\u6743\u9650\u548C\u78C1\u76D8\u7A7A\u95F4\u540E\u91CD\u8BD5\u3002',\n 500,\n );\n } finally {\n await rm(temporary, { force: true });\n await release?.();\n }\n }\n}\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
6
|
"names": []
|
|
7
7
|
}
|
package/dist/server/config.js
CHANGED
|
@@ -25,6 +25,10 @@ const configSchema = z.object({
|
|
|
25
25
|
publicOrigin: z.array(origin).min(1).default(["http://127.0.0.1:8787"]).transform((values) => [...new Set(values)]),
|
|
26
26
|
developmentOrigins: z.array(origin).default([]),
|
|
27
27
|
dataDir: z.string().min(1).default("./.local/codex-web"),
|
|
28
|
+
workDir: z.string().trim().max(4096).refine(
|
|
29
|
+
(path) => isAbsolute(path) && !path.includes("\0"),
|
|
30
|
+
"workDir \u5FC5\u987B\u662F\u670D\u52A1\u4E3B\u673A\u4E0A\u7684\u7EDD\u5BF9\u76EE\u5F55\u8DEF\u5F84"
|
|
31
|
+
).optional(),
|
|
28
32
|
codex: z.object({
|
|
29
33
|
binary: z.string().min(1).default("codex"),
|
|
30
34
|
home: z.string().refine(isAbsolute).optional(),
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../src/server/config.ts"],
|
|
4
|
-
"sourcesContent": ["import { readFile } from 'node:fs/promises';\nimport { dirname, isAbsolute, resolve } from 'node:path';\nimport { z } from 'zod';\nimport { AppError } from '../shared/errors.js';\nimport { DEFAULT_MAX_LINE_BYTES } from '../codex/transport.js';\nimport type { GatewayConfig } from '../shared/gateway-config.js';\nconst origin = z\n .string()\n .regex(/^https?:\\/\\/[^/?#\\\\\\s]+\\/?$/i, 'Expected an exact HTTP(S) origin')\n .pipe(z.string().url())\n .transform((value, context) => {\n const url = new URL(value);\n if (\n !['http:', 'https:'].includes(url.protocol) ||\n url.hostname.includes('*') ||\n value.includes('@') ||\n url.username ||\n url.password ||\n url.pathname !== '/' ||\n url.search ||\n url.hash\n ) {\n context.addIssue({\n code: 'custom',\n message:\n 'Expected an exact HTTP(S) origin without wildcard, path, credentials, query or hash',\n });\n return z.NEVER;\n }\n return url.origin;\n });\nconst project = z\n .object({\n id: z.string().regex(/^[a-zA-Z0-9_-]+$/),\n name: z.string().min(1),\n path: z.string().refine(isAbsolute, 'Project path must be absolute'),\n })\n .strict();\nexport const configSchema = z\n .object({\n host: z.string().default('127.0.0.1'),\n port: z.number().int().min(1).max(65535).default(8787),\n publicOrigin: z\n .array(origin)\n .min(1)\n .default(['http://127.0.0.1:8787'])\n .transform((values) => [...new Set(values)]),\n developmentOrigins: z.array(origin).default([]),\n dataDir: z.string().min(1).default('./.local/codex-web'),\n codex: z\n .object({\n binary: z.string().min(1).default('codex'),\n home: z.string().refine(isAbsolute).optional(),\n requestTimeoutMs: z.number().int().min(100).max(300_000).default(30_000),\n maxLineBytes: z\n .number()\n .int()\n .min(1024)\n .max(256 * 1024 * 1024)\n .default(DEFAULT_MAX_LINE_BYTES),\n })\n .strict()\n .default({ binary: 'codex', requestTimeoutMs: 30_000, maxLineBytes: DEFAULT_MAX_LINE_BYTES }),\n auth: z\n .discriminatedUnion('mode', [\n z.object({ mode: z.literal('local') }).strict(),\n z\n .object({\n mode: z.literal('cloudflare-access'),\n issuer: z.string().url(),\n audience: z.string().min(1),\n })\n .strict(),\n ])\n .default({ mode: 'local' }),\n projects: z.array(project).default([]),\n limits: z\n .object({\n eventBytes: z\n .number()\n .int()\n .min(1024)\n .max(64 * 1024 * 1024)\n .default(16 * 1024 * 1024),\n wsBytes: z\n .number()\n .int()\n .min(1024)\n .max(16 * 1024 * 1024)\n .default(4 * 1024 * 1024),\n itemBytes: z\n .number()\n .int()\n .min(1024)\n .max(8 * 1024 * 1024)\n .default(2 * 1024 * 1024),\n })\n .strict()\n .default({\n eventBytes: 16 * 1024 * 1024,\n wsBytes: 4 * 1024 * 1024,\n itemBytes: 2 * 1024 * 1024,\n }),\n })\n .strict();\nexport type AppConfig = z.infer<typeof configSchema> & { demo: boolean };\nconst sources = new WeakMap<AppConfig, { path: string; development: boolean }>();\nexport const configSource = (config: AppConfig) => sources.get(config);\nexport function isLoopback(hostname: string): boolean {\n return ['127.0.0.1', 'localhost', '::1', '[::1]'].includes(hostname);\n}\nexport function parseFileConfig(value: unknown): GatewayConfig {\n const parsed = configSchema.safeParse(value);\n if (!parsed.success)\n throw new AppError(\n 'INVALID_CONFIG',\n parsed.error.issues\n .map((issue) => `${issue.path.join('.') || '\u914D\u7F6E'}: ${issue.message}`)\n .join('\\n'),\n );\n const config = parsed.data;\n if (new Set(config.projects.map((p) => p.id)).size !== config.projects.length)\n throw new AppError('INVALID_CONFIG', 'Project IDs must be unique');\n if (config.auth.mode === 'local' && !isLoopback(config.host))\n throw new AppError('INVALID_CONFIG', 'Local mode requires a loopback host');\n if (config.developmentOrigins.some((o) => !isLoopback(new URL(o).hostname)))\n throw new AppError('INVALID_CONFIG', 'Development origins must be loopback');\n if (config.auth.mode === 'cloudflare-access') {\n const issuer = new URL(config.auth.issuer);\n if (\n issuer.protocol !== 'https:' ||\n !issuer.hostname.endsWith('.cloudflareaccess.com') ||\n issuer.pathname !== '/' ||\n issuer.search ||\n issuer.hash ||\n issuer.username ||\n issuer.password\n )\n throw new AppError(\n 'INVALID_CONFIG',\n 'Access issuer must be https://<team>.cloudflareaccess.com',\n );\n if (config.publicOrigin.some((value) => new URL(value).protocol !== 'https:'))\n throw new AppError('INVALID_CONFIG', 'All Access public origins must use HTTPS');\n config.auth.issuer = issuer.origin;\n }\n return config;\n}\nexport function parseConfig(value: unknown, filename: string, development = false): AppConfig {\n const config = parseFileConfig(value);\n const result: AppConfig = {\n ...config,\n developmentOrigins: development ? config.developmentOrigins : [],\n dataDir: resolve(dirname(filename), config.dataDir),\n demo: false,\n };\n sources.set(result, { path: resolve(filename), development });\n return result;\n}\nexport function cliOptions(args = process.argv.slice(2)): {\n configPath: string;\n development: boolean;\n} {\n const remaining = args.filter((arg) => arg !== '--');\n let configPath = 'config.local.json';\n let development = false;\n for (let i = 0; i < remaining.length; i++) {\n const arg = remaining[i];\n if (arg === '--dev') development = true;\n else if (arg === '--config' && remaining[i + 1]) configPath = remaining[++i]!;\n else throw new AppError('INVALID_ARGUMENT', `Unknown or incomplete argument: ${arg}`);\n }\n return { configPath: resolve(configPath), development };\n}\nexport async function loadConfig(options = cliOptions()): Promise<AppConfig> {\n return parseConfig(\n JSON.parse(await readFile(options.configPath, 'utf8')),\n options.configPath,\n options.development,\n );\n}\n"],
|
|
5
|
-
"mappings": "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",
|
|
4
|
+
"sourcesContent": ["import { readFile } from 'node:fs/promises';\nimport { dirname, isAbsolute, resolve } from 'node:path';\nimport { z } from 'zod';\nimport { AppError } from '../shared/errors.js';\nimport { DEFAULT_MAX_LINE_BYTES } from '../codex/transport.js';\nimport type { GatewayConfig } from '../shared/gateway-config.js';\nconst origin = z\n .string()\n .regex(/^https?:\\/\\/[^/?#\\\\\\s]+\\/?$/i, 'Expected an exact HTTP(S) origin')\n .pipe(z.string().url())\n .transform((value, context) => {\n const url = new URL(value);\n if (\n !['http:', 'https:'].includes(url.protocol) ||\n url.hostname.includes('*') ||\n value.includes('@') ||\n url.username ||\n url.password ||\n url.pathname !== '/' ||\n url.search ||\n url.hash\n ) {\n context.addIssue({\n code: 'custom',\n message:\n 'Expected an exact HTTP(S) origin without wildcard, path, credentials, query or hash',\n });\n return z.NEVER;\n }\n return url.origin;\n });\nconst project = z\n .object({\n id: z.string().regex(/^[a-zA-Z0-9_-]+$/),\n name: z.string().min(1),\n path: z.string().refine(isAbsolute, 'Project path must be absolute'),\n })\n .strict();\nexport const configSchema = z\n .object({\n host: z.string().default('127.0.0.1'),\n port: z.number().int().min(1).max(65535).default(8787),\n publicOrigin: z\n .array(origin)\n .min(1)\n .default(['http://127.0.0.1:8787'])\n .transform((values) => [...new Set(values)]),\n developmentOrigins: z.array(origin).default([]),\n dataDir: z.string().min(1).default('./.local/codex-web'),\n workDir: z\n .string()\n .trim()\n .max(4096)\n .refine(\n (path) => isAbsolute(path) && !path.includes('\\0'),\n 'workDir \u5FC5\u987B\u662F\u670D\u52A1\u4E3B\u673A\u4E0A\u7684\u7EDD\u5BF9\u76EE\u5F55\u8DEF\u5F84',\n )\n .optional(),\n codex: z\n .object({\n binary: z.string().min(1).default('codex'),\n home: z.string().refine(isAbsolute).optional(),\n requestTimeoutMs: z.number().int().min(100).max(300_000).default(30_000),\n maxLineBytes: z\n .number()\n .int()\n .min(1024)\n .max(256 * 1024 * 1024)\n .default(DEFAULT_MAX_LINE_BYTES),\n })\n .strict()\n .default({ binary: 'codex', requestTimeoutMs: 30_000, maxLineBytes: DEFAULT_MAX_LINE_BYTES }),\n auth: z\n .discriminatedUnion('mode', [\n z.object({ mode: z.literal('local') }).strict(),\n z\n .object({\n mode: z.literal('cloudflare-access'),\n issuer: z.string().url(),\n audience: z.string().min(1),\n })\n .strict(),\n ])\n .default({ mode: 'local' }),\n projects: z.array(project).default([]),\n limits: z\n .object({\n eventBytes: z\n .number()\n .int()\n .min(1024)\n .max(64 * 1024 * 1024)\n .default(16 * 1024 * 1024),\n wsBytes: z\n .number()\n .int()\n .min(1024)\n .max(16 * 1024 * 1024)\n .default(4 * 1024 * 1024),\n itemBytes: z\n .number()\n .int()\n .min(1024)\n .max(8 * 1024 * 1024)\n .default(2 * 1024 * 1024),\n })\n .strict()\n .default({\n eventBytes: 16 * 1024 * 1024,\n wsBytes: 4 * 1024 * 1024,\n itemBytes: 2 * 1024 * 1024,\n }),\n })\n .strict();\nexport type AppConfig = z.infer<typeof configSchema> & { demo: boolean };\nconst sources = new WeakMap<AppConfig, { path: string; development: boolean }>();\nexport const configSource = (config: AppConfig) => sources.get(config);\nexport function isLoopback(hostname: string): boolean {\n return ['127.0.0.1', 'localhost', '::1', '[::1]'].includes(hostname);\n}\nexport function parseFileConfig(value: unknown): GatewayConfig {\n const parsed = configSchema.safeParse(value);\n if (!parsed.success)\n throw new AppError(\n 'INVALID_CONFIG',\n parsed.error.issues\n .map((issue) => `${issue.path.join('.') || '\u914D\u7F6E'}: ${issue.message}`)\n .join('\\n'),\n );\n const config = parsed.data;\n if (new Set(config.projects.map((p) => p.id)).size !== config.projects.length)\n throw new AppError('INVALID_CONFIG', 'Project IDs must be unique');\n if (config.auth.mode === 'local' && !isLoopback(config.host))\n throw new AppError('INVALID_CONFIG', 'Local mode requires a loopback host');\n if (config.developmentOrigins.some((o) => !isLoopback(new URL(o).hostname)))\n throw new AppError('INVALID_CONFIG', 'Development origins must be loopback');\n if (config.auth.mode === 'cloudflare-access') {\n const issuer = new URL(config.auth.issuer);\n if (\n issuer.protocol !== 'https:' ||\n !issuer.hostname.endsWith('.cloudflareaccess.com') ||\n issuer.pathname !== '/' ||\n issuer.search ||\n issuer.hash ||\n issuer.username ||\n issuer.password\n )\n throw new AppError(\n 'INVALID_CONFIG',\n 'Access issuer must be https://<team>.cloudflareaccess.com',\n );\n if (config.publicOrigin.some((value) => new URL(value).protocol !== 'https:'))\n throw new AppError('INVALID_CONFIG', 'All Access public origins must use HTTPS');\n config.auth.issuer = issuer.origin;\n }\n return config;\n}\nexport function parseConfig(value: unknown, filename: string, development = false): AppConfig {\n const config = parseFileConfig(value);\n const result: AppConfig = {\n ...config,\n developmentOrigins: development ? config.developmentOrigins : [],\n dataDir: resolve(dirname(filename), config.dataDir),\n demo: false,\n };\n sources.set(result, { path: resolve(filename), development });\n return result;\n}\nexport function cliOptions(args = process.argv.slice(2)): {\n configPath: string;\n development: boolean;\n} {\n const remaining = args.filter((arg) => arg !== '--');\n let configPath = 'config.local.json';\n let development = false;\n for (let i = 0; i < remaining.length; i++) {\n const arg = remaining[i];\n if (arg === '--dev') development = true;\n else if (arg === '--config' && remaining[i + 1]) configPath = remaining[++i]!;\n else throw new AppError('INVALID_ARGUMENT', `Unknown or incomplete argument: ${arg}`);\n }\n return { configPath: resolve(configPath), development };\n}\nexport async function loadConfig(options = cliOptions()): Promise<AppConfig> {\n return parseConfig(\n JSON.parse(await readFile(options.configPath, 'utf8')),\n options.configPath,\n options.development,\n );\n}\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
6
|
"names": ["value"]
|
|
7
7
|
}
|
package/dist/server/db/schema.js
CHANGED
|
@@ -92,6 +92,7 @@ const projectRegistrations = sqliteTable("project_registrations", {
|
|
|
92
92
|
path: text("path").notNull(),
|
|
93
93
|
canonicalRoot: text("canonical_root"),
|
|
94
94
|
removedAt: integer("removed_at"),
|
|
95
|
+
sortOrder: integer("sort_order").notNull().default(0),
|
|
95
96
|
createdAt: integer("created_at").notNull(),
|
|
96
97
|
updatedAt: integer("updated_at").notNull()
|
|
97
98
|
});
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../../src/server/db/schema.ts"],
|
|
4
|
-
"sourcesContent": ["import { sql } from 'drizzle-orm';\nimport {\n check,\n index,\n integer,\n primaryKey,\n sqliteTable,\n text,\n uniqueIndex,\n} from 'drizzle-orm/sqlite-core';\nimport type { ApiError } from '../../shared/types.js';\n\nexport const operations = sqliteTable(\n 'operations',\n {\n clientOperationId: text('client_operation_id').primaryKey(),\n kind: text('kind').notNull(),\n target: text('target').notNull(),\n projectId: text('project_id'),\n requestDigest: text('request_digest').notNull(),\n state: text('state', {\n enum: ['accepted', 'dispatching', 'acknowledged', 'failed', 'unknown'],\n }).notNull(),\n result: text('result', { mode: 'json' }).$type<unknown>(),\n error: text('error', { mode: 'json' }).$type<ApiError>(),\n createdAt: integer('created_at').notNull(),\n updatedAt: integer('updated_at').notNull(),\n reconciledAt: integer('reconciled_at'),\n reconciliationDecision: text('reconciliation_decision'),\n },\n (table) => [\n check(\n 'operations_state_check',\n sql`${table.state} in ('accepted', 'dispatching', 'acknowledged', 'failed', 'unknown')`,\n ),\n index('operations_state_idx').on(table.state),\n ],\n);\n\nexport const executionMarkers = sqliteTable(\n 'execution_markers',\n {\n operationId: text('operation_id')\n .primaryKey()\n .references(() => operations.clientOperationId),\n threadId: text('thread_id').notNull(),\n turnId: text('turn_id'),\n workspaceKey: text('workspace_key').notNull(),\n codexEpoch: text('codex_epoch').notNull(),\n state: text('state', { enum: ['active', 'unknown', 'terminal', 'reconciled'] }).notNull(),\n createdAt: integer('created_at').notNull(),\n endedAt: integer('ended_at'),\n reconciledAt: integer('reconciled_at'),\n reconciliationOperationId: text('reconciliation_operation_id').references(\n () => operations.clientOperationId,\n ),\n },\n (table) => [\n check(\n 'execution_markers_state_check',\n sql`${table.state} in ('active', 'unknown', 'terminal', 'reconciled')`,\n ),\n uniqueIndex('execution_markers_active_thread')\n .on(table.threadId)\n .where(sql`${table.endedAt} is null`),\n ],\n);\n\nexport const threadLinks = sqliteTable('thread_links', {\n threadId: text('thread_id').primaryKey(),\n projectId: text('project_id'),\n cwd: text('cwd').notNull(),\n source: text('source').notNull(),\n managedEpoch: text('managed_epoch'),\n updatedAt: integer('updated_at').notNull(),\n});\n\n/** Allocations survive failed/unknown RPCs; names and archive state remain in Codex. */\nexport const sessionWorkspaces = sqliteTable('session_workspaces', {\n operationId: text('operation_id').primaryKey(),\n threadId: text('thread_id').unique(),\n rootPath: text('root_path').notNull(),\n canonicalRoot: text('canonical_root').notNull().unique(),\n createdAt: integer('created_at').notNull(),\n});\n\nexport const preferences = sqliteTable('preferences', {\n key: text('key').primaryKey(),\n value: text('value', { mode: 'json' }).$type<unknown>().notNull(),\n});\n\n/** Locally observed turn totals; upstream turn history does not include token usage. */\nexport const turnUsage = sqliteTable(\n 'turn_usage',\n {\n threadId: text('thread_id').notNull(),\n turnId: text('turn_id').notNull(),\n outputTokens: integer('output_tokens').notNull(),\n },\n (table) => [primaryKey({ columns: [table.threadId, table.turnId] })],\n);\n\n/** Removing a registration retains its identity and all existing thread links. */\nexport const projectRegistrations = sqliteTable('project_registrations', {\n id: text('id').primaryKey(),\n name: text('name').notNull(),\n path: text('path').notNull(),\n canonicalRoot: text('canonical_root'),\n removedAt: integer('removed_at'),\n createdAt: integer('created_at').notNull(),\n updatedAt: integer('updated_at').notNull(),\n});\n"],
|
|
5
|
-
"mappings": "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",
|
|
4
|
+
"sourcesContent": ["import { sql } from 'drizzle-orm';\nimport {\n check,\n index,\n integer,\n primaryKey,\n sqliteTable,\n text,\n uniqueIndex,\n} from 'drizzle-orm/sqlite-core';\nimport type { ApiError } from '../../shared/types.js';\n\nexport const operations = sqliteTable(\n 'operations',\n {\n clientOperationId: text('client_operation_id').primaryKey(),\n kind: text('kind').notNull(),\n target: text('target').notNull(),\n projectId: text('project_id'),\n requestDigest: text('request_digest').notNull(),\n state: text('state', {\n enum: ['accepted', 'dispatching', 'acknowledged', 'failed', 'unknown'],\n }).notNull(),\n result: text('result', { mode: 'json' }).$type<unknown>(),\n error: text('error', { mode: 'json' }).$type<ApiError>(),\n createdAt: integer('created_at').notNull(),\n updatedAt: integer('updated_at').notNull(),\n reconciledAt: integer('reconciled_at'),\n reconciliationDecision: text('reconciliation_decision'),\n },\n (table) => [\n check(\n 'operations_state_check',\n sql`${table.state} in ('accepted', 'dispatching', 'acknowledged', 'failed', 'unknown')`,\n ),\n index('operations_state_idx').on(table.state),\n ],\n);\n\nexport const executionMarkers = sqliteTable(\n 'execution_markers',\n {\n operationId: text('operation_id')\n .primaryKey()\n .references(() => operations.clientOperationId),\n threadId: text('thread_id').notNull(),\n turnId: text('turn_id'),\n workspaceKey: text('workspace_key').notNull(),\n codexEpoch: text('codex_epoch').notNull(),\n state: text('state', { enum: ['active', 'unknown', 'terminal', 'reconciled'] }).notNull(),\n createdAt: integer('created_at').notNull(),\n endedAt: integer('ended_at'),\n reconciledAt: integer('reconciled_at'),\n reconciliationOperationId: text('reconciliation_operation_id').references(\n () => operations.clientOperationId,\n ),\n },\n (table) => [\n check(\n 'execution_markers_state_check',\n sql`${table.state} in ('active', 'unknown', 'terminal', 'reconciled')`,\n ),\n uniqueIndex('execution_markers_active_thread')\n .on(table.threadId)\n .where(sql`${table.endedAt} is null`),\n ],\n);\n\nexport const threadLinks = sqliteTable('thread_links', {\n threadId: text('thread_id').primaryKey(),\n projectId: text('project_id'),\n cwd: text('cwd').notNull(),\n source: text('source').notNull(),\n managedEpoch: text('managed_epoch'),\n updatedAt: integer('updated_at').notNull(),\n});\n\n/** Allocations survive failed/unknown RPCs; names and archive state remain in Codex. */\nexport const sessionWorkspaces = sqliteTable('session_workspaces', {\n operationId: text('operation_id').primaryKey(),\n threadId: text('thread_id').unique(),\n rootPath: text('root_path').notNull(),\n canonicalRoot: text('canonical_root').notNull().unique(),\n createdAt: integer('created_at').notNull(),\n});\n\nexport const preferences = sqliteTable('preferences', {\n key: text('key').primaryKey(),\n value: text('value', { mode: 'json' }).$type<unknown>().notNull(),\n});\n\n/** Locally observed turn totals; upstream turn history does not include token usage. */\nexport const turnUsage = sqliteTable(\n 'turn_usage',\n {\n threadId: text('thread_id').notNull(),\n turnId: text('turn_id').notNull(),\n outputTokens: integer('output_tokens').notNull(),\n },\n (table) => [primaryKey({ columns: [table.threadId, table.turnId] })],\n);\n\n/** Removing a registration retains its identity and all existing thread links. */\nexport const projectRegistrations = sqliteTable('project_registrations', {\n id: text('id').primaryKey(),\n name: text('name').notNull(),\n path: text('path').notNull(),\n canonicalRoot: text('canonical_root'),\n removedAt: integer('removed_at'),\n sortOrder: integer('sort_order').notNull().default(0),\n createdAt: integer('created_at').notNull(),\n updatedAt: integer('updated_at').notNull(),\n});\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
6
|
"names": []
|
|
7
7
|
}
|
package/dist/server/http.js
CHANGED
|
@@ -10,11 +10,13 @@ import { AppError } from "../shared/errors.js";
|
|
|
10
10
|
import { IMAGE_REQUEST_BYTES } from "../shared/images.js";
|
|
11
11
|
import * as schemas from "../shared/validation.js";
|
|
12
12
|
import { ConfigStore } from "./config-store.js";
|
|
13
|
+
import { projectPathPreview, resolveProjectPath } from "./project-paths.js";
|
|
13
14
|
import { createAuthenticator } from "./auth.js";
|
|
14
15
|
import { socketHandlers } from "./websocket.js";
|
|
15
16
|
import { z } from "zod";
|
|
16
17
|
import { releaseChannels } from "../shared/updates.js";
|
|
17
18
|
import { settingsSchema, updateRequestSchema } from "../updates/manager.js";
|
|
19
|
+
import { createReleaseNotesLookup, releaseNotesQuery } from "../updates/release-notes.js";
|
|
18
20
|
import { startShellSchema } from "../shared/shell.js";
|
|
19
21
|
function createApp({
|
|
20
22
|
config,
|
|
@@ -65,6 +67,11 @@ function createApp({
|
|
|
65
67
|
);
|
|
66
68
|
}
|
|
67
69
|
if (updates) {
|
|
70
|
+
const releaseNotes = createReleaseNotesLookup();
|
|
71
|
+
app.get("/api/updates/release-notes", async (c) => {
|
|
72
|
+
const { version, refresh } = releaseNotesQuery.parse(c.req.query());
|
|
73
|
+
return c.json(await releaseNotes(version, refresh === "1"));
|
|
74
|
+
});
|
|
68
75
|
app.get("/api/updates", async (c) => c.json(await updates.status()));
|
|
69
76
|
app.post("/api/updates/check", async (c) => {
|
|
70
77
|
const input = z.object({ channel: z.enum(releaseChannels).optional() }).strict().parse(await c.req.json());
|
|
@@ -88,6 +95,13 @@ function createApp({
|
|
|
88
95
|
};
|
|
89
96
|
const status = (op) => op.status === "dispatching" || op.status === "accepted" ? 202 : 200;
|
|
90
97
|
const configuration = new ConfigStore(config);
|
|
98
|
+
app.get("/api/project-path", (c) => {
|
|
99
|
+
const input = z.object({
|
|
100
|
+
path: z.string().max(4096).default(""),
|
|
101
|
+
directory: z.string().max(255).refine((value) => !/[\\/\0]/.test(value) && value !== "." && value !== "..").optional()
|
|
102
|
+
}).parse({ path: c.req.query("path"), directory: c.req.query("directory") });
|
|
103
|
+
return c.json(projectPathPreview(input.path.trim(), configuration.workDir, input.directory));
|
|
104
|
+
});
|
|
91
105
|
app.use("/api/config*", async (c, next) => {
|
|
92
106
|
c.header("Cache-Control", "no-store");
|
|
93
107
|
await next();
|
|
@@ -176,7 +190,13 @@ function createApp({
|
|
|
176
190
|
});
|
|
177
191
|
app.post(
|
|
178
192
|
"/api/project-clones",
|
|
179
|
-
async (c) => c.json(
|
|
193
|
+
async (c) => c.json(
|
|
194
|
+
service.cloneProject(
|
|
195
|
+
await parse(c, schemas.cloneProjectSchema),
|
|
196
|
+
(path) => resolveProjectPath(path, configuration.workDir)
|
|
197
|
+
),
|
|
198
|
+
202
|
|
199
|
+
)
|
|
180
200
|
);
|
|
181
201
|
app.get(
|
|
182
202
|
"/api/project-clones/:cloneId",
|
|
@@ -190,6 +210,10 @@ function createApp({
|
|
|
190
210
|
await parse(c, schemas.removeProjectSchema);
|
|
191
211
|
return c.json(service.projectClones.retryRegistration(id(c.req.param("cloneId"))));
|
|
192
212
|
});
|
|
213
|
+
app.post(
|
|
214
|
+
"/api/projects/reorder",
|
|
215
|
+
async (c) => c.json(service.reorderProjects(await parse(c, schemas.reorderProjectsSchema)))
|
|
216
|
+
);
|
|
193
217
|
app.get(
|
|
194
218
|
"/api/projects/:projectId/runtime-settings",
|
|
195
219
|
async (c) => c.json(await service.readRuntimeSettings(id(c.req.param("projectId"))))
|
|
@@ -201,10 +225,12 @@ function createApp({
|
|
|
201
225
|
);
|
|
202
226
|
return c.json(op, status(op));
|
|
203
227
|
});
|
|
204
|
-
app.post(
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
228
|
+
app.post("/api/projects", async (c) => {
|
|
229
|
+
const input = await parse(c, schemas.createProjectSchema);
|
|
230
|
+
return c.json(
|
|
231
|
+
service.addProject({ ...input, path: resolveProjectPath(input.path, configuration.workDir) })
|
|
232
|
+
);
|
|
233
|
+
});
|
|
208
234
|
app.post(
|
|
209
235
|
"/api/projects/:projectId/rename",
|
|
210
236
|
async (c) => c.json(
|
|
@@ -273,6 +299,12 @@ function createApp({
|
|
|
273
299
|
c.header("Cross-Origin-Resource-Policy", "same-origin");
|
|
274
300
|
c.header("Content-Security-Policy", "default-src 'none'; sandbox");
|
|
275
301
|
const image = await service.getThreadImage(id(c.req.param("threadId")), c.req.query("path"));
|
|
302
|
+
const etag = `"${createHash("sha256").update(image.bytes).digest("hex")}"`;
|
|
303
|
+
c.header("Cache-Control", "private, no-cache");
|
|
304
|
+
c.header("ETag", etag);
|
|
305
|
+
const ifNoneMatch = c.req.header("If-None-Match");
|
|
306
|
+
if (ifNoneMatch === "*" || ifNoneMatch?.split(",").some((tag) => tag.trim().replace(/^W\//, "") === etag))
|
|
307
|
+
return c.body(null, 304);
|
|
276
308
|
return c.body(image.bytes, 200, {
|
|
277
309
|
"Content-Type": image.contentType,
|
|
278
310
|
"Content-Length": String(image.bytes.length)
|
|
@@ -286,6 +318,12 @@ function createApp({
|
|
|
286
318
|
await service.getThreadMarkdown(id(c.req.param("threadId")), c.req.query("path"))
|
|
287
319
|
);
|
|
288
320
|
});
|
|
321
|
+
app.get("/api/threads/:threadId/text", async (c) => {
|
|
322
|
+
c.header("Cache-Control", "private, no-store");
|
|
323
|
+
c.header("X-Content-Type-Options", "nosniff");
|
|
324
|
+
c.header("Cross-Origin-Resource-Policy", "same-origin");
|
|
325
|
+
return c.json(await service.getThreadText(id(c.req.param("threadId")), c.req.query("path")));
|
|
326
|
+
});
|
|
289
327
|
app.get("/api/threads/:threadId/json", async (c) => {
|
|
290
328
|
c.header("Cache-Control", "private, no-store");
|
|
291
329
|
c.header("X-Content-Type-Options", "nosniff");
|
package/dist/server/http.js.map
CHANGED
|
@@ -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 { projectPathPreview, resolveProjectPath } from './project-paths.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 { createReleaseNotesLookup, releaseNotesQuery } from '../updates/release-notes.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 const releaseNotes = createReleaseNotesLookup();\n app.get('/api/updates/release-notes', async (c) => {\n const { version, refresh } = releaseNotesQuery.parse(c.req.query());\n return c.json(await releaseNotes(version, refresh === '1'));\n });\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.get('/api/project-path', (c) => {\n const input = z\n .object({\n path: z.string().max(4096).default(''),\n directory: z\n .string()\n .max(255)\n .refine((value) => !/[\\\\/\\0]/.test(value) && value !== '.' && value !== '..')\n .optional(),\n })\n .parse({ path: c.req.query('path'), directory: c.req.query('directory') });\n return c.json(projectPathPreview(input.path.trim(), configuration.workDir, input.directory));\n });\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(\n service.cloneProject(await parse(c, schemas.cloneProjectSchema), (path) =>\n resolveProjectPath(path, configuration.workDir),\n ),\n 202,\n ),\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 const input = await parse(c, schemas.createProjectSchema);\n return c.json(\n service.addProject({ ...input, path: resolveProjectPath(input.path, configuration.workDir) }),\n );\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/text', 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.getThreadText(id(c.req.param('threadId')), c.req.query('path')));\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
|
}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../src/server/local-files.ts"],
|
|
4
|
-
"sourcesContent": ["import { constants, realpathSync, statSync } from 'node:fs';\nimport { open, realpath, stat } from 'node:fs/promises';\nimport { tmpdir } from 'node:os';\nimport { isAbsolute, parse, resolve } from 'node:path';\nimport { AppError } from '../shared/errors.js';\nimport { containsPath } from './projects.js';\n\nexport interface FileRoot {\n canonicalRoot: string;\n aliases: string[];\n}\n\nexport interface FileScope {\n cwd: string;\n roots: readonly FileRoot[];\n excludedRoots?: readonly string[];\n}\n\n/** Capture server-owned roots once; clients cannot nominate a temporary directory. */\nexport function temporaryFileRoots(): FileRoot[] {\n const roots = new Map<string, FileRoot>();\n for (const directory of [tmpdir(), ...(process.platform === 'win32' ? [] : ['/tmp'])]) {\n if (!isAbsolute(directory)) continue;\n const alias = resolve(directory);\n if (alias === parse(alias).root) continue;\n try {\n const canonicalRoot = realpathSync(alias);\n if (canonicalRoot === parse(canonicalRoot).root || !statSync(canonicalRoot).isDirectory())\n continue;\n const root = roots.get(canonicalRoot) ?? { canonicalRoot, aliases: [] };\n if (!root.aliases.includes(alias)) root.aliases.push(alias);\n roots.set(canonicalRoot, root);\n } catch {\n // Unavailable temp locations do not prevent previews in the remaining roots.\n }\n }\n return [...roots.values()];\n}\n\ninterface FilePolicy {\n code: 'IMAGE' | 'MARKDOWN' | 'JSON';\n label: string;\n maxBytes: number;\n sizeError: string;\n validatePath: (path: string) => void;\n}\n\nfunction outsideProject(policy: FilePolicy): AppError {\n return new AppError(\n `${policy.code}_OUTSIDE_PROJECT`,\n `\u53EA\u80FD\u9884\u89C8\u5F53\u524D\u4F1A\u8BDD\u5DE5\u4F5C\u533A\u6216\u5141\u8BB8\u7684\u670D\u52A1\u5668\u4E34\u65F6\u76EE\u5F55\u5185\u7684${policy.label}`,\n 403,\n );\n}\n\nfunction changedFile(policy: FilePolicy): AppError {\n return new AppError(`${policy.code}_CHANGED`, `${policy.label}\u6587\u4EF6\u5DF2\u53D8\u5316\uFF0C\u8BF7\u91CD\u65B0\u6253\u5F00\u9884\u89C8`, 409);\n}\n\n/** Shared project/temp authorization and bounded, verified file reads for previews. */\nexport async function readLocalFile(\n path: string | undefined,\n scope: FileScope,\n policy: FilePolicy,\n) {\n if (\n !path ||\n path.length > 4096 ||\n [...path].some(\n (character) => character.charCodeAt(0) < 32 || character.charCodeAt(0) === 127,\n ) ||\n /^[a-z][a-z\\d+.-]*:/i.test(path) ||\n path.startsWith('//') ||\n path.startsWith('\\\\\\\\')\n )\n throw new AppError(`INVALID_${policy.code}_PATH`, `${policy.label}\u8DEF\u5F84\u65E0\u6548`, 400);\n\n const candidate = resolve(scope.cwd, path);\n // Both spellings matter on systems where /tmp or the registered directory is an alias.\n if (\n !scope.roots.some(\n (root) =>\n containsPath(root.canonicalRoot, candidate) ||\n root.aliases.some((alias) => containsPath(alias, candidate)),\n )\n )\n throw outsideProject(policy);\n const isAllowed = (path: string) =>\n !scope.excludedRoots?.some((root) => containsPath(root, path)) &&\n scope.roots.some((root) => containsPath(root.canonicalRoot, path));\n policy.validatePath(candidate);\n\n try {\n const canonical = await realpath(candidate);\n if (!isAllowed(canonical)) throw outsideProject(policy);\n // NOFOLLOW rejects a last-component symlink swapped in after realpath. NONBLOCK\n // ensures opening a substituted FIFO cannot leave the request waiting forever.\n const file = await open(\n canonical,\n constants.O_RDONLY | constants.O_NOFOLLOW | constants.O_NONBLOCK,\n );\n try {\n const before = await file.stat();\n if (!before.isFile())\n throw new AppError(`${policy.code}_NOT_FILE`, `${policy.label}\u8DEF\u5F84\u4E0D\u662F\u666E\u901A\u6587\u4EF6`, 415);\n if (before.size > policy.maxBytes)\n throw new AppError(`${policy.code}_TOO_LARGE`, policy.sizeError, 413);\n\n // Recheck the path and opened inode to detect ordinary concurrent replacements.\n // Node's portable file API cannot make parent-directory traversal race-free.\n const verifyPath = async () => {\n const current = await realpath(candidate);\n if (!isAllowed(current)) throw outsideProject(policy);\n if (current !== canonical) throw changedFile(policy);\n const currentStat = await stat(current);\n if (currentStat.dev !== before.dev || currentStat.ino !== before.ino)\n throw changedFile(policy);\n };\n await verifyPath();\n // One extra byte detects growth without letting readFile allocate unbounded data.\n const data = Buffer.alloc(before.size + 1);\n let length = 0;\n while (length < data.length) {\n const { bytesRead } = await file.read(data, length, data.length - length, length);\n if (!bytesRead) break;\n length += bytesRead;\n }\n if (length > policy.maxBytes)\n throw new AppError(`${policy.code}_TOO_LARGE`, policy.sizeError, 413);\n const after = await file.stat();\n if (length !== before.size || after.size !== before.size || after.mtimeMs !== before.mtimeMs)\n throw changedFile(policy);\n await verifyPath();\n const bytes = data.subarray(0, length);\n return { bytes, path: canonical };\n } finally {\n await file.close();\n }\n } catch (error) {\n if (error instanceof AppError) throw error;\n const code = (error as NodeJS.ErrnoException).code;\n if (code === 'ENOENT' || code === 'ENOTDIR')\n throw new AppError(`${policy.code}_NOT_FOUND`, `${policy.label}\u6587\u4EF6\u4E0D\u5B58\u5728\u6216\u5DF2\u88AB\u5220\u9664`, 404);\n if (code === 'ELOOP') throw outsideProject(policy);\n throw new AppError(`${policy.code}_UNREADABLE`, `${policy.label}\u6587\u4EF6\u4E0D\u53EF\u8BFB\u53D6`, 403);\n }\n}\n"],
|
|
4
|
+
"sourcesContent": ["import { constants, realpathSync, statSync } from 'node:fs';\nimport { open, realpath, stat } from 'node:fs/promises';\nimport { tmpdir } from 'node:os';\nimport { isAbsolute, parse, resolve } from 'node:path';\nimport { AppError } from '../shared/errors.js';\nimport { containsPath } from './projects.js';\n\nexport interface FileRoot {\n canonicalRoot: string;\n aliases: string[];\n}\n\nexport interface FileScope {\n cwd: string;\n roots: readonly FileRoot[];\n excludedRoots?: readonly string[];\n}\n\n/** Capture server-owned roots once; clients cannot nominate a temporary directory. */\nexport function temporaryFileRoots(): FileRoot[] {\n const roots = new Map<string, FileRoot>();\n for (const directory of [tmpdir(), ...(process.platform === 'win32' ? [] : ['/tmp'])]) {\n if (!isAbsolute(directory)) continue;\n const alias = resolve(directory);\n if (alias === parse(alias).root) continue;\n try {\n const canonicalRoot = realpathSync(alias);\n if (canonicalRoot === parse(canonicalRoot).root || !statSync(canonicalRoot).isDirectory())\n continue;\n const root = roots.get(canonicalRoot) ?? { canonicalRoot, aliases: [] };\n if (!root.aliases.includes(alias)) root.aliases.push(alias);\n roots.set(canonicalRoot, root);\n } catch {\n // Unavailable temp locations do not prevent previews in the remaining roots.\n }\n }\n return [...roots.values()];\n}\n\ninterface FilePolicy {\n code: 'IMAGE' | 'MARKDOWN' | 'JSON' | 'TEXT';\n label: string;\n maxBytes: number;\n sizeError: string;\n validatePath: (path: string) => void;\n}\n\nfunction outsideProject(policy: FilePolicy): AppError {\n return new AppError(\n `${policy.code}_OUTSIDE_PROJECT`,\n `\u53EA\u80FD\u9884\u89C8\u5F53\u524D\u4F1A\u8BDD\u5DE5\u4F5C\u533A\u6216\u5141\u8BB8\u7684\u670D\u52A1\u5668\u4E34\u65F6\u76EE\u5F55\u5185\u7684${policy.label}`,\n 403,\n );\n}\n\nfunction changedFile(policy: FilePolicy): AppError {\n return new AppError(`${policy.code}_CHANGED`, `${policy.label}\u6587\u4EF6\u5DF2\u53D8\u5316\uFF0C\u8BF7\u91CD\u65B0\u6253\u5F00\u9884\u89C8`, 409);\n}\n\n/** Shared project/temp authorization and bounded, verified file reads for previews. */\nexport async function readLocalFile(\n path: string | undefined,\n scope: FileScope,\n policy: FilePolicy,\n) {\n if (\n !path ||\n path.length > 4096 ||\n [...path].some(\n (character) => character.charCodeAt(0) < 32 || character.charCodeAt(0) === 127,\n ) ||\n /^[a-z][a-z\\d+.-]*:/i.test(path) ||\n path.startsWith('//') ||\n path.startsWith('\\\\\\\\')\n )\n throw new AppError(`INVALID_${policy.code}_PATH`, `${policy.label}\u8DEF\u5F84\u65E0\u6548`, 400);\n\n const candidate = resolve(scope.cwd, path);\n // Both spellings matter on systems where /tmp or the registered directory is an alias.\n if (\n !scope.roots.some(\n (root) =>\n containsPath(root.canonicalRoot, candidate) ||\n root.aliases.some((alias) => containsPath(alias, candidate)),\n )\n )\n throw outsideProject(policy);\n const isAllowed = (path: string) =>\n !scope.excludedRoots?.some((root) => containsPath(root, path)) &&\n scope.roots.some((root) => containsPath(root.canonicalRoot, path));\n policy.validatePath(candidate);\n\n try {\n const canonical = await realpath(candidate);\n if (!isAllowed(canonical)) throw outsideProject(policy);\n // NOFOLLOW rejects a last-component symlink swapped in after realpath. NONBLOCK\n // ensures opening a substituted FIFO cannot leave the request waiting forever.\n const file = await open(\n canonical,\n constants.O_RDONLY | constants.O_NOFOLLOW | constants.O_NONBLOCK,\n );\n try {\n const before = await file.stat();\n if (!before.isFile())\n throw new AppError(`${policy.code}_NOT_FILE`, `${policy.label}\u8DEF\u5F84\u4E0D\u662F\u666E\u901A\u6587\u4EF6`, 415);\n if (before.size > policy.maxBytes)\n throw new AppError(`${policy.code}_TOO_LARGE`, policy.sizeError, 413);\n\n // Recheck the path and opened inode to detect ordinary concurrent replacements.\n // Node's portable file API cannot make parent-directory traversal race-free.\n const verifyPath = async () => {\n const current = await realpath(candidate);\n if (!isAllowed(current)) throw outsideProject(policy);\n if (current !== canonical) throw changedFile(policy);\n const currentStat = await stat(current);\n if (currentStat.dev !== before.dev || currentStat.ino !== before.ino)\n throw changedFile(policy);\n };\n await verifyPath();\n // One extra byte detects growth without letting readFile allocate unbounded data.\n const data = Buffer.alloc(before.size + 1);\n let length = 0;\n while (length < data.length) {\n const { bytesRead } = await file.read(data, length, data.length - length, length);\n if (!bytesRead) break;\n length += bytesRead;\n }\n if (length > policy.maxBytes)\n throw new AppError(`${policy.code}_TOO_LARGE`, policy.sizeError, 413);\n const after = await file.stat();\n if (length !== before.size || after.size !== before.size || after.mtimeMs !== before.mtimeMs)\n throw changedFile(policy);\n await verifyPath();\n const bytes = data.subarray(0, length);\n return { bytes, path: canonical };\n } finally {\n await file.close();\n }\n } catch (error) {\n if (error instanceof AppError) throw error;\n const code = (error as NodeJS.ErrnoException).code;\n if (code === 'ENOENT' || code === 'ENOTDIR')\n throw new AppError(`${policy.code}_NOT_FOUND`, `${policy.label}\u6587\u4EF6\u4E0D\u5B58\u5728\u6216\u5DF2\u88AB\u5220\u9664`, 404);\n if (code === 'ELOOP') throw outsideProject(policy);\n throw new AppError(`${policy.code}_UNREADABLE`, `${policy.label}\u6587\u4EF6\u4E0D\u53EF\u8BFB\u53D6`, 403);\n }\n}\n"],
|
|
5
5
|
"mappings": "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",
|
|
6
6
|
"names": ["path"]
|
|
7
7
|
}
|
|
@@ -138,7 +138,7 @@ class ProjectClones {
|
|
|
138
138
|
))
|
|
139
139
|
throw new AppError("CLONE_IN_PROGRESS", "\u8BE5\u76EE\u5F55\u6B63\u5728\u514B\u9686\uFF0C\u8BF7\u7B49\u5F85\u5B8C\u6210\u6216\u53D6\u6D88\u540E\u518D\u64CD\u4F5C", 409);
|
|
140
140
|
}
|
|
141
|
-
start(input) {
|
|
141
|
+
start(input, resolvePath = (path) => path) {
|
|
142
142
|
const parsed = cloneProjectSchema.parse(input);
|
|
143
143
|
validateRepository(parsed.repository);
|
|
144
144
|
const fingerprint = createHash("sha256").update(JSON.stringify(parsed)).digest("hex");
|
|
@@ -149,7 +149,7 @@ class ProjectClones {
|
|
|
149
149
|
return this.get(previous.view.id);
|
|
150
150
|
}
|
|
151
151
|
if (this.closing) throw new AppError("GATEWAY_STOPPING", "\u670D\u52A1\u6B63\u5728\u505C\u6B62\uFF0C\u8BF7\u7A0D\u540E\u91CD\u8BD5", 503);
|
|
152
|
-
const path = targetPath(parsed.path);
|
|
152
|
+
const path = targetPath(resolvePath(parsed.path));
|
|
153
153
|
this.assertIdlePath(path);
|
|
154
154
|
if (this.registered(path))
|
|
155
155
|
throw new AppError("PROJECT_EXISTS", "\u8BE5\u76EE\u5F55\u5DF2\u6DFB\u52A0\u4E3A\u9879\u76EE\uFF0C\u8BF7\u4F7F\u7528\u5DF2\u6709\u9879\u76EE\u6216\u9009\u62E9\u5176\u4ED6\u76EE\u5F55", 409);
|