@covro/covro 0.2.1-dev.5 → 0.2.1-dev.6
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 +5 -4
- package/README.zh-CN.md +2 -1
- package/dist/codex/auto-review.js +55 -0
- package/dist/codex/auto-review.js.map +2 -2
- package/dist/server/project-clones.js +53 -17
- package/dist/server/project-clones.js.map +2 -2
- package/dist/web/assets/{abnfDiagram-WEYOIA3C-CwLX554E.js → abnfDiagram-WEYOIA3C-BkC1EVBk.js} +1 -1
- package/dist/web/assets/{arc-DAaDyyqc.js → arc-CYGT9huc.js} +1 -1
- package/dist/web/assets/{architecture-WOLXFQ4H-y6MnEqN0.js → architecture-WOLXFQ4H-DCsF3gVg.js} +1 -1
- package/dist/web/assets/{architectureDiagram-ZHVYKT53-_B_4k-Pp.js → architectureDiagram-ZHVYKT53-Cn6kovuz.js} +1 -1
- package/dist/web/assets/{blockDiagram-RJAE7QA3-C9jZ7FHy.js → blockDiagram-RJAE7QA3-CNk2zXm9.js} +1 -1
- package/dist/web/assets/{c4Diagram-XDPPPE54-DQAtMvqN.js → c4Diagram-XDPPPE54-CTuI_1XJ.js} +1 -1
- package/dist/web/assets/channel-BDxsxdm-.js +1 -0
- package/dist/web/assets/{chunk-2E7WHVMD-Bu427yab.js → chunk-2E7WHVMD-TrYs0qV3.js} +1 -1
- package/dist/web/assets/{chunk-4EMNKR2T-BrGrBG7B.js → chunk-4EMNKR2T-QL4-PZ4r.js} +1 -1
- package/dist/web/assets/{chunk-55XCU76K-l4f0OZWA.js → chunk-55XCU76K-yynn-EEr.js} +1 -1
- package/dist/web/assets/{chunk-7S6XSTFO-D7ixF313.js → chunk-7S6XSTFO-dzrEn-_W.js} +1 -1
- package/dist/web/assets/{chunk-AQDTSKZB-Cesd-BF2.js → chunk-AQDTSKZB-DaNZMEGO.js} +1 -1
- package/dist/web/assets/{chunk-DSNLSHJZ-B7dim5uT.js → chunk-DSNLSHJZ-BnMtx8Ho.js} +1 -1
- package/dist/web/assets/{chunk-HAGFOTED-B-f2I7TA.js → chunk-HAGFOTED-fg-JyiA5.js} +1 -1
- package/dist/web/assets/{chunk-HNE4Q36P-CoIvaeX6.js → chunk-HNE4Q36P-CCsfPbcn.js} +1 -1
- package/dist/web/assets/{chunk-NQOKRVIF-BybRxR7n.js → chunk-NQOKRVIF-DLlOpcWZ.js} +1 -1
- package/dist/web/assets/{chunk-UHW7PFBP-CV5WG4tq.js → chunk-UHW7PFBP-B-sPKpeS.js} +1 -1
- package/dist/web/assets/{chunk-VQ5W62QI-BO4AjXi1.js → chunk-VQ5W62QI-BVqGMzey.js} +1 -1
- package/dist/web/assets/{chunk-WQQL4CDK-CADNDGXO.js → chunk-WQQL4CDK-DcwJXNuh.js} +1 -1
- package/dist/web/assets/{chunk-XXDRQBXY-C6QZT7KW.js → chunk-XXDRQBXY-DRwLkqyU.js} +1 -1
- package/dist/web/assets/{chunk-YZO67HFG-Cm6eGReX.js → chunk-YZO67HFG-BrrgBnrQ.js} +1 -1
- package/dist/web/assets/{chunk-ZAUX2LTX-CUBoGbKg.js → chunk-ZAUX2LTX-C7qvwkRr.js} +1 -1
- package/dist/web/assets/{chunk-ZCHMQSOR-B0ImOOtq.js → chunk-ZCHMQSOR-DdBd3Cvs.js} +1 -1
- package/dist/web/assets/{classDiagram-v2-4MYWR3UA-D5NWZ9vm.js → classDiagram-v2-4MYWR3UA-Dn2RZb3v.js} +1 -1
- package/dist/web/assets/{cose-bilkent-JH36ORCC-BzckEyjA.js → cose-bilkent-JH36ORCC-BlXvtfgE.js} +1 -1
- package/dist/web/assets/{cynefin-EF2NZ3EQ-Da_J6-Jb.js → cynefin-EF2NZ3EQ-odDgTxc-.js} +1 -1
- package/dist/web/assets/{cynefinDiagram-WF24UDRP-b4g_B1A-.js → cynefinDiagram-WF24UDRP-BySEoCW8.js} +1 -1
- package/dist/web/assets/{dagre-NK4GIZVX-89H2mfs9.js → dagre-NK4GIZVX-ihxmhB4X.js} +1 -1
- package/dist/web/assets/{diagram-CBK3MWTC-CW4f_P0l.js → diagram-CBK3MWTC-Ckr5P3IM.js} +1 -1
- package/dist/web/assets/{diagram-I5XIIV2D-Cv5Qndgc.js → diagram-I5XIIV2D-D5BZIval.js} +1 -1
- package/dist/web/assets/{diagram-ODIL2UCW-LrXxeoCt.js → diagram-ODIL2UCW-BC5jjqqY.js} +1 -1
- package/dist/web/assets/{diagram-PFBEJUXE-uJV2PooB.js → diagram-PFBEJUXE-D_IdYKqC.js} +1 -1
- package/dist/web/assets/{diagram-RJA3I4VV-DlY0PGRs.js → diagram-RJA3I4VV-DlPNsPqP.js} +1 -1
- package/dist/web/assets/{diagram-TYUNFSNR-B1KoXBEE.js → diagram-TYUNFSNR-Ca2ANhGM.js} +1 -1
- package/dist/web/assets/{dist-B4eX4BCP.js → dist-BSWkYZRr.js} +1 -1
- package/dist/web/assets/{ebnfDiagram-V4P3TTPT-xCvQVbdj.js → ebnfDiagram-V4P3TTPT-BrqvmeiB.js} +1 -1
- package/dist/web/assets/{elk-QONTBCTQ-Cl8Hu-Uy.js → elk-QONTBCTQ-BfXeOE2E.js} +1 -1
- package/dist/web/assets/{erDiagram-KYQIGQAA-Xp-AvSwE.js → erDiagram-KYQIGQAA-CMuYoCVJ.js} +1 -1
- package/dist/web/assets/{eventmodeling-K75KTNOO-C0truZGU.js → eventmodeling-K75KTNOO-9s4vQqv1.js} +1 -1
- package/dist/web/assets/flowDiagram-IUGLLWBU-BHwP7Ha7.js +1 -0
- package/dist/web/assets/{ganttDiagram-R7GPNTJY-rPaYp-D7.js → ganttDiagram-R7GPNTJY-B-Xmfmm2.js} +1 -1
- package/dist/web/assets/{gitGraph-VSP46ZUC-Bgfpho41.js → gitGraph-VSP46ZUC-ZSbSQ7jf.js} +1 -1
- package/dist/web/assets/{gitGraphDiagram-PDYL7XHY-kDcmuv6k.js → gitGraphDiagram-PDYL7XHY-BhxijLaZ.js} +1 -1
- package/dist/web/assets/index-BrvtlTwN.js +147 -0
- package/dist/web/assets/{index-B7tnf28H.css → index-mkDvqMQ3.css} +1 -1
- package/dist/web/assets/{info-OHQRW6UA-CAWg5k9R.js → info-OHQRW6UA-CTSCkjt3.js} +1 -1
- package/dist/web/assets/{infoDiagram-MVDRKAX5-Cd08ebhn.js → infoDiagram-MVDRKAX5-D46MTesD.js} +1 -1
- package/dist/web/assets/{ishikawaDiagram-HPEHV2GE-tAXBLt9k.js → ishikawaDiagram-HPEHV2GE-Bafu9NOk.js} +1 -1
- package/dist/web/assets/{journeyDiagram-BOSHW7O7-ZgnWqXkg.js → journeyDiagram-BOSHW7O7-CRsIi1Ry.js} +1 -1
- package/dist/web/assets/{kanban-definition-FNSA4HC5-BUjtjd-h.js → kanban-definition-FNSA4HC5-DjIT_uLs.js} +1 -1
- package/dist/web/assets/{line-DG48HRVK.js → line-B5MDdgVs.js} +1 -1
- package/dist/web/assets/{linear-CqnXVCVw.js → linear-wm7JWxlT.js} +1 -1
- package/dist/web/assets/{mermaid-parser.core-gIvgB9WK.js → mermaid-parser.core-C7jTW4sZ.js} +3 -3
- package/dist/web/assets/{mermaid.core-C_R7Y1XB.js → mermaid.core-DcWRGjP5.js} +4 -4
- package/dist/web/assets/{mindmap-definition-6OY57UG3-CwUEEl_y.js → mindmap-definition-6OY57UG3-qY6vV0UI.js} +1 -1
- package/dist/web/assets/{packet-JDAUHWVQ-BrXmpRQ3.js → packet-JDAUHWVQ-9B7vLYK0.js} +1 -1
- package/dist/web/assets/{pegDiagram-ECIUJNBX-lLApO85Q.js → pegDiagram-ECIUJNBX-B7gLZ_Fs.js} +1 -1
- package/dist/web/assets/{pie-XZMESJXO-C_8kR5WX.js → pie-XZMESJXO-MfazZSiE.js} +1 -1
- package/dist/web/assets/{pieDiagram-GNMPY74T-CawGmJZn.js → pieDiagram-GNMPY74T-1ahagzzC.js} +1 -1
- package/dist/web/assets/{quadrantDiagram-D57K4MC3-Dg6Yq3s_.js → quadrantDiagram-D57K4MC3-C-GkwjOp.js} +1 -1
- package/dist/web/assets/{radar-ABXABTNO-Bs1xY09D.js → radar-ABXABTNO-CgutvWTi.js} +1 -1
- package/dist/web/assets/{railroad-I3PHUGI6-Bw_qS6e3.js → railroad-I3PHUGI6-Bl9LJINm.js} +1 -1
- package/dist/web/assets/{railroad-abnf-LNEFI6M7-CQr33CuI.js → railroad-abnf-LNEFI6M7-CGRpAnuU.js} +1 -1
- package/dist/web/assets/{railroad-ebnf-DWYD2UWJ-BY9nk5pg.js → railroad-ebnf-DWYD2UWJ-D6A47xZS.js} +1 -1
- package/dist/web/assets/{railroad-peg-JR7BVNR7-CcFsAEI4.js → railroad-peg-JR7BVNR7-DxGphrFO.js} +1 -1
- package/dist/web/assets/{railroadDiagram-Y72NRBL7-rkZbchmc.js → railroadDiagram-Y72NRBL7-D8P17OV4.js} +1 -1
- package/dist/web/assets/{requirementDiagram-EQYSW5RA-CSOcijj5.js → requirementDiagram-EQYSW5RA-c93_xAaQ.js} +1 -1
- package/dist/web/assets/{sankeyDiagram-SY24JHO5-E0uo0rRp.js → sankeyDiagram-SY24JHO5-FankqwcZ.js} +1 -1
- package/dist/web/assets/{sequenceDiagram-BLEHNHQU-BKWAAcRu.js → sequenceDiagram-BLEHNHQU-CWH5SeL1.js} +1 -1
- package/dist/web/assets/{src-F6L8HcEC.js → src-BXQOmVZv.js} +1 -1
- package/dist/web/assets/{stateDiagram-v2-A6IESKWZ-BBk2tpPh.js → stateDiagram-v2-A6IESKWZ-DLfcw0e7.js} +1 -1
- package/dist/web/assets/{swimlanes-B3ENRKFQ-CAUrhGwF.js → swimlanes-B3ENRKFQ-DZ-yBiap.js} +1 -1
- package/dist/web/assets/swimlanesDiagram-AYQRV4AK-BlmdUBbW.js +8 -0
- package/dist/web/assets/{timeline-definition-JXYKGLGB-D0FWj9fa.js → timeline-definition-JXYKGLGB-B7XvxDHY.js} +1 -1
- package/dist/web/assets/{treeView-D4JQ5SDB-CkJOmf93.js → treeView-D4JQ5SDB-DrB6wXJ0.js} +1 -1
- package/dist/web/assets/{treemap-SAJKECNS-BSFsardB.js → treemap-SAJKECNS-BynFjsvx.js} +1 -1
- package/dist/web/assets/{usecaseDiagram-WPOLBNNX-B2U-2o4g.js → usecaseDiagram-WPOLBNNX-BISzHOHN.js} +1 -1
- package/dist/web/assets/{vennDiagram-Q3Y3IDCW-BijTH0fT.js → vennDiagram-Q3Y3IDCW-BZwjTIST.js} +1 -1
- package/dist/web/assets/{wardley-7MLQ67FV-DDOa5RzL.js → wardley-7MLQ67FV-2bM0pMhO.js} +1 -1
- package/dist/web/assets/{wardleyDiagram-IVAIDKWA-mbb5GvW5.js → wardleyDiagram-IVAIDKWA-CflkU8us.js} +1 -1
- package/dist/web/assets/{xychartDiagram-4URNHDU4-DHd60D_Y.js → xychartDiagram-4URNHDU4-DRspOlD3.js} +1 -1
- package/dist/web/index.html +4 -4
- package/package.json +5 -3
- package/dist/web/assets/channel-ByzMCsOB.js +0 -1
- package/dist/web/assets/flowDiagram-IUGLLWBU-T67jQ-T_.js +0 -1
- package/dist/web/assets/index-3DUP8YCe.js +0 -142
- package/dist/web/assets/swimlanesDiagram-AYQRV4AK-BCnJymLL.js +0 -8
package/README.md
CHANGED
|
@@ -30,7 +30,8 @@
|
|
|
30
30
|
## ✨ What you can do
|
|
31
31
|
|
|
32
32
|
- 📱 **Step away from your desk. Stay with the task.** Follow tool activity, inspect file changes, handle permission requests, and answer Codex's questions from your computer or phone. Closing the page doesn't cancel the task.
|
|
33
|
-
- 💬 **Start without a project.** Use
|
|
33
|
+
- 💬 **Start without a project.** Use **New session** to start in an automatically assigned directory. Conversations and files stay available after refresh, restart, or archive.
|
|
34
|
+
- 🌐 **Choose your interface language.** Switch between 简体中文, English, 日本語 and 한국어 without leaving your session. Prompts, replies, tool output and paths remain unchanged. [Localization details](docs/i18n.md).
|
|
34
35
|
- 🗂️ **See every project in one place.** Run independent tasks in parallel and spot which conversations are working, waiting for approval, or ready to read.
|
|
35
36
|
- 💬 **Pick up your Codex CLI conversations.** Browse saved history, resume existing work, or fork a new direction. Rename conversations to find them later and archive them when you're done. View and copy the full Thread ID in conversation details or diagnostics.
|
|
36
37
|
- ⚡ **Open long conversations sooner.** Recent messages appear first; scrolling preloads older history. On this device, cached conversation lists and history pages appear while the server checks for updates. [Loading and cache behavior](docs/session-history.md).
|
|
@@ -51,7 +52,7 @@ Mermaid diagrams render locally after their code fence closes, with automatic li
|
|
|
51
52
|
|
|
52
53
|
## 🚀 Get started
|
|
53
54
|
|
|
54
|
-
You need **Node.js 24.19 or later in the 24.x line** and an installed, signed-in **Codex CLI**. The interface
|
|
55
|
+
You need **Node.js 24.19 or later in the 24.x line** and an installed, signed-in **Codex CLI**. The interface supports Simplified Chinese, English, Japanese and Korean. Choose **Language** in the sidebar; the first visit follows your browser preferences, and manual choices are saved on this device.
|
|
55
56
|
|
|
56
57
|
<!-- codex-compatibility:start -->
|
|
57
58
|
|
|
@@ -105,9 +106,9 @@ pnpm build
|
|
|
105
106
|
pnpm start -- --config ./config.local.json
|
|
106
107
|
```
|
|
107
108
|
|
|
108
|
-
Open **<http://127.0.0.1:8787>** and click **+** beside
|
|
109
|
+
Open **<http://127.0.0.1:8787>** and click **+** beside **Unassigned** in the sidebar to send a task without choosing a project.
|
|
109
110
|
|
|
110
|
-
To work in a project directory, click
|
|
111
|
+
To work in a project directory, click **Add project**, then choose an existing host directory or **Clone Git repository**. For cloning, enter an HTTPS/SSH repository address, an absolute destination on the host, and an optional project name. Git must be available to the service and uses the host’s existing credentials. Follow progress or cancel in the dialog; once imported, open the project and send your first task. [Clone requirements and recovery](docs/project-clone.md).
|
|
111
112
|
|
|
112
113
|
Unassigned conversations use separate directories under `<dataDir>/workspaces/`, allocated on the first send. Their files are retained until you explicitly remove them; archiving does not delete files. See [conversation workspaces](docs/workspaces.md) for directory, recovery, and backup details.
|
|
113
114
|
|
package/README.zh-CN.md
CHANGED
|
@@ -31,6 +31,7 @@
|
|
|
31
31
|
|
|
32
32
|
- 📱 **离开电脑,也能跟进任务。** 在电脑或手机上查看工具活动与文件变更、处理权限审批、回答 Codex 的问题。关闭页面,任务仍会继续。
|
|
33
33
|
- 💬 **无需项目,也能直接开始。** 点击 **新建会话**,应用自动分配独立目录;刷新、重启或归档后,会话和文件仍保留。
|
|
34
|
+
- 🌐 **选择界面语言。** 支持简体中文、English、日本語和 한국어,会话内即可切换;输入、回复、工具原文和路径保持不变。[多语言说明](docs/i18n.md)。
|
|
34
35
|
- 🗂️ **多个项目,一眼掌握进展。** 让独立任务并行执行,随时看到哪些会话正在工作、等待审批,哪些已有结果可读。
|
|
35
36
|
- 💬 **接着你的 Codex CLI 会话。** 浏览已有历史、恢复工作,或 Fork 一个新方向;为会话命名以便查找,完成后归档。在会话详情或状态诊断中查看、复制完整 Thread ID。
|
|
36
37
|
- ⚡ **更快打开长会话。** 优先显示最近消息,向上滚动时提前加载历史;本机会话列表和历史分片可先显示缓存,再由服务端核对更新。[加载与缓存说明](docs/session-history.md)。
|
|
@@ -51,7 +52,7 @@ Mermaid 图表在代码块闭合后于本地渲染,自动适配浅色/深色
|
|
|
51
52
|
|
|
52
53
|
## 🚀 开始使用
|
|
53
54
|
|
|
54
|
-
需要 **Node.js 24.x,版本不低于 24.19**,以及已经安装、登录的 **Codex CLI
|
|
55
|
+
需要 **Node.js 24.x,版本不低于 24.19**,以及已经安装、登录的 **Codex CLI**。界面支持简体中文、英语、日语和韩语。在侧栏选择「语言」;首次访问按浏览器偏好匹配,手动选择会保存在当前设备。
|
|
55
56
|
|
|
56
57
|
<!-- codex-compatibility:start -->
|
|
57
58
|
|
|
@@ -82,6 +82,60 @@ ${action.target}`
|
|
|
82
82
|
return { actionLabel: "\u64CD\u4F5C\u5BA1\u6838", actionSummary: "\u672A\u63D0\u4F9B\u53EF\u8BC6\u522B\u7684\u64CD\u4F5C\u8BE6\u60C5" };
|
|
83
83
|
}
|
|
84
84
|
}
|
|
85
|
+
function actionPresentation(action) {
|
|
86
|
+
const raw = (value) => ({ label: "raw", value });
|
|
87
|
+
switch (action.type) {
|
|
88
|
+
case "command":
|
|
89
|
+
return { kind: action.type, parts: [raw(action.command)] };
|
|
90
|
+
case "execve":
|
|
91
|
+
return {
|
|
92
|
+
kind: action.type,
|
|
93
|
+
parts: [
|
|
94
|
+
{ label: "program", value: action.program },
|
|
95
|
+
{ label: "arguments", value: action.argv.map((arg) => JSON.stringify(arg)).join(" ") }
|
|
96
|
+
]
|
|
97
|
+
};
|
|
98
|
+
case "writeStdin":
|
|
99
|
+
return {
|
|
100
|
+
kind: action.type,
|
|
101
|
+
parts: [{ label: "process", value: action.processId }, raw(action.stdin)]
|
|
102
|
+
};
|
|
103
|
+
case "applyPatch":
|
|
104
|
+
return { kind: action.type, parts: action.files.map(raw) };
|
|
105
|
+
case "networkAccess":
|
|
106
|
+
return {
|
|
107
|
+
kind: action.type,
|
|
108
|
+
parts: [raw(`${action.protocol}://${action.host}:${action.port}`), raw(action.target)]
|
|
109
|
+
};
|
|
110
|
+
case "mcpToolCall":
|
|
111
|
+
return {
|
|
112
|
+
kind: action.type,
|
|
113
|
+
parts: [
|
|
114
|
+
raw(
|
|
115
|
+
[action.connectorName, `${action.server}/${action.toolName}`, action.toolTitle].filter(Boolean).join(" \xB7 ")
|
|
116
|
+
)
|
|
117
|
+
]
|
|
118
|
+
};
|
|
119
|
+
case "requestPermissions": {
|
|
120
|
+
const parts = [];
|
|
121
|
+
if (action.reason) parts.push(raw(action.reason));
|
|
122
|
+
const network = action.permissions.network?.enabled;
|
|
123
|
+
if (network != null)
|
|
124
|
+
parts.push({ label: network ? "networkAllowed" : "networkRestricted", value: "" });
|
|
125
|
+
const files = action.permissions.fileSystem;
|
|
126
|
+
for (const path of files?.read ?? []) parts.push({ label: "read", value: path });
|
|
127
|
+
for (const path of files?.write ?? []) parts.push({ label: "write", value: path });
|
|
128
|
+
for (const entry of files?.entries ?? [])
|
|
129
|
+
parts.push({
|
|
130
|
+
label: entry.access,
|
|
131
|
+
value: entry.path.type === "path" ? entry.path.path : JSON.stringify(entry.path)
|
|
132
|
+
});
|
|
133
|
+
if (files?.globScanMaxDepth !== void 0)
|
|
134
|
+
parts.push({ label: "depth", value: String(files.globScanMaxDepth) });
|
|
135
|
+
return { kind: action.type, parts };
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
}
|
|
85
139
|
function mapAutoReview(params) {
|
|
86
140
|
const review = params.review;
|
|
87
141
|
return {
|
|
@@ -89,6 +143,7 @@ function mapAutoReview(params) {
|
|
|
89
143
|
...params.targetItemId === null ? {} : { targetItemId: params.targetItemId },
|
|
90
144
|
status: ["inProgress", "approved", "denied", "timedOut", "aborted"].includes(review.status) ? review.status : "unknown",
|
|
91
145
|
...describeAction(params.action),
|
|
146
|
+
presentation: actionPresentation(params.action),
|
|
92
147
|
...review.rationale === null ? {} : { rationale: review.rationale },
|
|
93
148
|
...review.riskLevel === null ? {} : { riskLevel: review.riskLevel },
|
|
94
149
|
...review.userAuthorization === null ? {} : { userAuthorization: review.userAuthorization },
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../src/codex/auto-review.ts"],
|
|
4
|
-
"sourcesContent": ["import type { AutoReviewView } from '../shared/types.js';\nimport type { GuardianApprovalReviewAction } from './generated/v2/GuardianApprovalReviewAction.js';\nimport type { FileSystemPath } from './generated/v2/FileSystemPath.js';\nimport type { ItemGuardianApprovalReviewStartedNotification } from './generated/v2/ItemGuardianApprovalReviewStartedNotification.js';\nimport type { ItemGuardianApprovalReviewCompletedNotification } from './generated/v2/ItemGuardianApprovalReviewCompletedNotification.js';\n\ntype Notification =\n ItemGuardianApprovalReviewStartedNotification | ItemGuardianApprovalReviewCompletedNotification;\n\nfunction pathLabel(path: FileSystemPath): string {\n if (path.type === 'path') return path.path;\n if (path.type === 'glob_pattern') return `\u5339\u914D\uFF1A${path.pattern}`;\n const special = path.value;\n const subpath =\n 'subpath' in special && special.subpath !== null ? `\uFF08\u5B50\u8DEF\u5F84\uFF1A${special.subpath}\uFF09` : '';\n switch (special.kind) {\n case 'root':\n return '\u6574\u4E2A\u6587\u4EF6\u7CFB\u7EDF\uFF08/\uFF09';\n case 'minimal':\n return '\u7CFB\u7EDF\u57FA\u7840\u8FD0\u884C\u76EE\u5F55';\n case 'project_roots':\n return `\u9879\u76EE\u6839\u76EE\u5F55${subpath}`;\n case 'tmpdir':\n return '\u7CFB\u7EDF\u4E34\u65F6\u76EE\u5F55\uFF08TMPDIR\uFF09';\n case 'slash_tmp':\n return '/tmp';\n case 'unknown':\n return `\u7279\u6B8A\u8DEF\u5F84\uFF1A${special.path}${subpath}`;\n }\n}\n\nfunction describeAction(\n action: GuardianApprovalReviewAction,\n): Pick<AutoReviewView, 'actionLabel' | 'actionSummary' | 'cwd'> {\n switch (action.type) {\n case 'command':\n return { actionLabel: '\u547D\u4EE4\u6267\u884C', actionSummary: action.command, cwd: action.cwd };\n case 'execve':\n return {\n actionLabel: '\u7A0B\u5E8F\u6267\u884C',\n actionSummary: `\u7A0B\u5E8F\uFF1A${action.program}\\n\u53C2\u6570\uFF1A${action.argv.map((arg) => JSON.stringify(arg)).join(' ')}`,\n cwd: action.cwd,\n };\n case 'writeStdin':\n return {\n actionLabel: '\u7EC8\u7AEF\u8F93\u5165',\n actionSummary: `\u8FDB\u7A0B ${action.processId}\\n${action.stdin}`,\n cwd: action.cwd,\n };\n case 'applyPatch':\n return {\n actionLabel: '\u6587\u4EF6\u53D8\u66F4',\n actionSummary: action.files.join('\\n') || '\u672A\u63D0\u4F9B\u6587\u4EF6\u8DEF\u5F84',\n cwd: action.cwd,\n };\n case 'networkAccess':\n return {\n actionLabel: '\u7F51\u7EDC\u8BBF\u95EE',\n actionSummary: `${action.protocol}://${action.host}:${action.port}\\n${action.target}`,\n };\n case 'mcpToolCall':\n return {\n actionLabel: '\u5DE5\u5177\u8C03\u7528',\n actionSummary: [\n action.connectorName,\n `${action.server}/${action.toolName}`,\n action.toolTitle,\n ]\n .filter(Boolean)\n .join(' \u00B7 '),\n };\n case 'requestPermissions': {\n const lines: string[] = [];\n if (action.reason) lines.push(action.reason);\n if (\n action.permissions.network?.enabled !== null &&\n action.permissions.network?.enabled !== undefined\n )\n lines.push(`\u7F51\u7EDC\u8BBF\u95EE\uFF1A${action.permissions.network.enabled ? '\u5141\u8BB8' : '\u4E0D\u5F00\u653E\u989D\u5916\u6743\u9650'}`);\n const files = action.permissions.fileSystem;\n if (files) {\n for (const path of files.read ?? []) lines.push(`\u8BFB\u53D6\uFF1A${path}`);\n for (const path of files.write ?? []) lines.push(`\u5199\u5165\uFF1A${path}`);\n for (const entry of files.entries ?? [])\n lines.push(\n `${{ read: '\u8BFB\u53D6', write: '\u5199\u5165', deny: '\u7981\u6B62\u8BBF\u95EE' }[entry.access]}\uFF1A${pathLabel(entry.path)}`,\n );\n if (files.globScanMaxDepth !== undefined)\n lines.push(`\u76EE\u5F55\u5339\u914D\u641C\u7D22\u6DF1\u5EA6\uFF1A${files.globScanMaxDepth}`);\n }\n return {\n actionLabel: '\u6743\u9650\u53D8\u66F4',\n actionSummary: lines.join('\\n') || '\u672A\u8BF7\u6C42\u989D\u5916\u7F51\u7EDC\u6216\u6587\u4EF6\u8BBF\u95EE\u6743\u9650',\n };\n }\n default:\n return { actionLabel: '\u64CD\u4F5C\u5BA1\u6838', actionSummary: '\u672A\u63D0\u4F9B\u53EF\u8BC6\u522B\u7684\u64CD\u4F5C\u8BE6\u60C5' };\n }\n}\n\n/** Display-only data. Notifications never become a PendingAction or a permission response. */\nexport function mapAutoReview(params: Notification): AutoReviewView {\n const review = params.review;\n return {\n reviewId: params.reviewId,\n ...(params.targetItemId === null ? {} : { targetItemId: params.targetItemId }),\n status: ['inProgress', 'approved', 'denied', 'timedOut', 'aborted'].includes(review.status)\n ? review.status\n : 'unknown',\n ...describeAction(params.action),\n ...(review.rationale === null ? {} : { rationale: review.rationale }),\n ...(review.riskLevel === null ? {} : { riskLevel: review.riskLevel }),\n ...(review.userAuthorization === null ? {} : { userAuthorization: review.userAuthorization }),\n startedAtMs: params.startedAtMs,\n ...('completedAtMs' in params ? { completedAtMs: params.completedAtMs } : {}),\n };\n}\n"],
|
|
5
|
-
"mappings": "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",
|
|
4
|
+
"sourcesContent": ["import type { AutoReviewView } from '../shared/types.js';\nimport type { GuardianApprovalReviewAction } from './generated/v2/GuardianApprovalReviewAction.js';\nimport type { FileSystemPath } from './generated/v2/FileSystemPath.js';\nimport type { ItemGuardianApprovalReviewStartedNotification } from './generated/v2/ItemGuardianApprovalReviewStartedNotification.js';\nimport type { ItemGuardianApprovalReviewCompletedNotification } from './generated/v2/ItemGuardianApprovalReviewCompletedNotification.js';\n\ntype Notification =\n ItemGuardianApprovalReviewStartedNotification | ItemGuardianApprovalReviewCompletedNotification;\n\nfunction pathLabel(path: FileSystemPath): string {\n if (path.type === 'path') return path.path;\n if (path.type === 'glob_pattern') return `\u5339\u914D\uFF1A${path.pattern}`;\n const special = path.value;\n const subpath =\n 'subpath' in special && special.subpath !== null ? `\uFF08\u5B50\u8DEF\u5F84\uFF1A${special.subpath}\uFF09` : '';\n switch (special.kind) {\n case 'root':\n return '\u6574\u4E2A\u6587\u4EF6\u7CFB\u7EDF\uFF08/\uFF09';\n case 'minimal':\n return '\u7CFB\u7EDF\u57FA\u7840\u8FD0\u884C\u76EE\u5F55';\n case 'project_roots':\n return `\u9879\u76EE\u6839\u76EE\u5F55${subpath}`;\n case 'tmpdir':\n return '\u7CFB\u7EDF\u4E34\u65F6\u76EE\u5F55\uFF08TMPDIR\uFF09';\n case 'slash_tmp':\n return '/tmp';\n case 'unknown':\n return `\u7279\u6B8A\u8DEF\u5F84\uFF1A${special.path}${subpath}`;\n }\n}\n\nfunction describeAction(\n action: GuardianApprovalReviewAction,\n): Pick<AutoReviewView, 'actionLabel' | 'actionSummary' | 'cwd'> {\n switch (action.type) {\n case 'command':\n return { actionLabel: '\u547D\u4EE4\u6267\u884C', actionSummary: action.command, cwd: action.cwd };\n case 'execve':\n return {\n actionLabel: '\u7A0B\u5E8F\u6267\u884C',\n actionSummary: `\u7A0B\u5E8F\uFF1A${action.program}\\n\u53C2\u6570\uFF1A${action.argv.map((arg) => JSON.stringify(arg)).join(' ')}`,\n cwd: action.cwd,\n };\n case 'writeStdin':\n return {\n actionLabel: '\u7EC8\u7AEF\u8F93\u5165',\n actionSummary: `\u8FDB\u7A0B ${action.processId}\\n${action.stdin}`,\n cwd: action.cwd,\n };\n case 'applyPatch':\n return {\n actionLabel: '\u6587\u4EF6\u53D8\u66F4',\n actionSummary: action.files.join('\\n') || '\u672A\u63D0\u4F9B\u6587\u4EF6\u8DEF\u5F84',\n cwd: action.cwd,\n };\n case 'networkAccess':\n return {\n actionLabel: '\u7F51\u7EDC\u8BBF\u95EE',\n actionSummary: `${action.protocol}://${action.host}:${action.port}\\n${action.target}`,\n };\n case 'mcpToolCall':\n return {\n actionLabel: '\u5DE5\u5177\u8C03\u7528',\n actionSummary: [\n action.connectorName,\n `${action.server}/${action.toolName}`,\n action.toolTitle,\n ]\n .filter(Boolean)\n .join(' \u00B7 '),\n };\n case 'requestPermissions': {\n const lines: string[] = [];\n if (action.reason) lines.push(action.reason);\n if (\n action.permissions.network?.enabled !== null &&\n action.permissions.network?.enabled !== undefined\n )\n lines.push(`\u7F51\u7EDC\u8BBF\u95EE\uFF1A${action.permissions.network.enabled ? '\u5141\u8BB8' : '\u4E0D\u5F00\u653E\u989D\u5916\u6743\u9650'}`);\n const files = action.permissions.fileSystem;\n if (files) {\n for (const path of files.read ?? []) lines.push(`\u8BFB\u53D6\uFF1A${path}`);\n for (const path of files.write ?? []) lines.push(`\u5199\u5165\uFF1A${path}`);\n for (const entry of files.entries ?? [])\n lines.push(\n `${{ read: '\u8BFB\u53D6', write: '\u5199\u5165', deny: '\u7981\u6B62\u8BBF\u95EE' }[entry.access]}\uFF1A${pathLabel(entry.path)}`,\n );\n if (files.globScanMaxDepth !== undefined)\n lines.push(`\u76EE\u5F55\u5339\u914D\u641C\u7D22\u6DF1\u5EA6\uFF1A${files.globScanMaxDepth}`);\n }\n return {\n actionLabel: '\u6743\u9650\u53D8\u66F4',\n actionSummary: lines.join('\\n') || '\u672A\u8BF7\u6C42\u989D\u5916\u7F51\u7EDC\u6216\u6587\u4EF6\u8BBF\u95EE\u6743\u9650',\n };\n }\n default:\n return { actionLabel: '\u64CD\u4F5C\u5BA1\u6838', actionSummary: '\u672A\u63D0\u4F9B\u53EF\u8BC6\u522B\u7684\u64CD\u4F5C\u8BE6\u60C5' };\n }\n}\n\nfunction actionPresentation(\n action: GuardianApprovalReviewAction,\n): NonNullable<AutoReviewView['presentation']> {\n const raw = (value: string) => ({ label: 'raw', value });\n switch (action.type) {\n case 'command':\n return { kind: action.type, parts: [raw(action.command)] };\n case 'execve':\n return {\n kind: action.type,\n parts: [\n { label: 'program', value: action.program },\n { label: 'arguments', value: action.argv.map((arg) => JSON.stringify(arg)).join(' ') },\n ],\n };\n case 'writeStdin':\n return {\n kind: action.type,\n parts: [{ label: 'process', value: action.processId }, raw(action.stdin)],\n };\n case 'applyPatch':\n return { kind: action.type, parts: action.files.map(raw) };\n case 'networkAccess':\n return {\n kind: action.type,\n parts: [raw(`${action.protocol}://${action.host}:${action.port}`), raw(action.target)],\n };\n case 'mcpToolCall':\n return {\n kind: action.type,\n parts: [\n raw(\n [action.connectorName, `${action.server}/${action.toolName}`, action.toolTitle]\n .filter(Boolean)\n .join(' \u00B7 '),\n ),\n ],\n };\n case 'requestPermissions': {\n const parts: { label: string; value: string }[] = [];\n if (action.reason) parts.push(raw(action.reason));\n const network = action.permissions.network?.enabled;\n if (network != null)\n parts.push({ label: network ? 'networkAllowed' : 'networkRestricted', value: '' });\n const files = action.permissions.fileSystem;\n for (const path of files?.read ?? []) parts.push({ label: 'read', value: path });\n for (const path of files?.write ?? []) parts.push({ label: 'write', value: path });\n for (const entry of files?.entries ?? [])\n parts.push({\n label: entry.access,\n value: entry.path.type === 'path' ? entry.path.path : JSON.stringify(entry.path),\n });\n if (files?.globScanMaxDepth !== undefined)\n parts.push({ label: 'depth', value: String(files.globScanMaxDepth) });\n return { kind: action.type, parts };\n }\n }\n}\n\n/** Display-only data. Notifications never become a PendingAction or a permission response. */\nexport function mapAutoReview(params: Notification): AutoReviewView {\n const review = params.review;\n return {\n reviewId: params.reviewId,\n ...(params.targetItemId === null ? {} : { targetItemId: params.targetItemId }),\n status: ['inProgress', 'approved', 'denied', 'timedOut', 'aborted'].includes(review.status)\n ? review.status\n : 'unknown',\n ...describeAction(params.action),\n presentation: actionPresentation(params.action),\n ...(review.rationale === null ? {} : { rationale: review.rationale }),\n ...(review.riskLevel === null ? {} : { riskLevel: review.riskLevel }),\n ...(review.userAuthorization === null ? {} : { userAuthorization: review.userAuthorization }),\n startedAtMs: params.startedAtMs,\n ...('completedAtMs' in params ? { completedAtMs: params.completedAtMs } : {}),\n };\n}\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
6
|
"names": []
|
|
7
7
|
}
|
|
@@ -64,26 +64,51 @@ function checkEmpty(path) {
|
|
|
64
64
|
}
|
|
65
65
|
}
|
|
66
66
|
function failure(output, missingGit) {
|
|
67
|
-
if (missingGit)
|
|
67
|
+
if (missingGit)
|
|
68
|
+
return {
|
|
69
|
+
code: "CLONE_GIT_MISSING",
|
|
70
|
+
message: "\u670D\u52A1\u5668\u672A\u5B89\u88C5 Git\uFF0C\u6216\u670D\u52A1\u7684 PATH \u4E2D\u627E\u4E0D\u5230 Git\u3002\u5B89\u88C5\u5E76\u914D\u7F6E\u540E\u91CD\u8BD5\u3002"
|
|
71
|
+
};
|
|
68
72
|
if (/authentication failed|could not read username|permission denied|publickey|terminal prompts disabled|credential|access denied|401|403/i.test(
|
|
69
73
|
output
|
|
70
74
|
))
|
|
71
|
-
return
|
|
75
|
+
return {
|
|
76
|
+
code: "CLONE_AUTH_FAILED",
|
|
77
|
+
message: "Git \u8BA4\u8BC1\u5931\u8D25\u6216\u6CA1\u6709\u4ED3\u5E93\u8BBF\u95EE\u6743\u9650\u3002\u8BF7\u5728\u8FD0\u884C\u670D\u52A1\u7684\u4E3B\u673A\u4E0A\u914D\u7F6E Git \u51ED\u636E\u6216 SSH \u5BC6\u94A5\u540E\u91CD\u8BD5\u3002"
|
|
78
|
+
};
|
|
72
79
|
if (/host key verification failed/i.test(output))
|
|
73
|
-
return
|
|
80
|
+
return {
|
|
81
|
+
code: "CLONE_HOST_KEY_FAILED",
|
|
82
|
+
message: "SSH \u4E3B\u673A\u9A8C\u8BC1\u5931\u8D25\u3002\u8BF7\u5728\u8FD0\u884C\u670D\u52A1\u7684\u4E3B\u673A\u4E0A\u786E\u8BA4\u5E76\u914D\u7F6E\u4ED3\u5E93\u4E3B\u673A\u7684 SSH \u4FE1\u4EFB\u540E\u91CD\u8BD5\u3002"
|
|
83
|
+
};
|
|
74
84
|
if (/repository .*not found|does not appear to be a git repository|not a repository|404/i.test(
|
|
75
85
|
output
|
|
76
86
|
))
|
|
77
|
-
return
|
|
87
|
+
return {
|
|
88
|
+
code: "CLONE_REPOSITORY_UNAVAILABLE",
|
|
89
|
+
message: "\u4ED3\u5E93\u4E0D\u5B58\u5728\u6216\u4E0D\u53EF\u8BBF\u95EE\u3002\u8BF7\u68C0\u67E5\u4ED3\u5E93\u5730\u5740\u548C\u4E3B\u673A\u4E0A\u7684 Git \u8BBF\u95EE\u6743\u9650\u3002"
|
|
90
|
+
};
|
|
78
91
|
if (/could not resolve|failed to connect|unable to access|connection|timed out|certificate/i.test(
|
|
79
92
|
output
|
|
80
93
|
))
|
|
81
|
-
return
|
|
94
|
+
return {
|
|
95
|
+
code: "CLONE_NETWORK_FAILED",
|
|
96
|
+
message: "\u65E0\u6CD5\u8FDE\u63A5 Git \u4ED3\u5E93\u3002\u8BF7\u68C0\u67E5\u670D\u52A1\u5668\u7F51\u7EDC\u3001\u4EE3\u7406\u548C\u8BC1\u4E66\u914D\u7F6E\u540E\u91CD\u8BD5\u3002"
|
|
97
|
+
};
|
|
82
98
|
if (/already exists and is not an empty directory/i.test(output))
|
|
83
|
-
return
|
|
99
|
+
return {
|
|
100
|
+
code: "CLONE_TARGET_NOT_EMPTY",
|
|
101
|
+
message: "\u76EE\u6807\u76EE\u5F55\u5DF2\u5305\u542B\u6587\u4EF6\uFF0CGit \u5DF2\u505C\u6B62\u514B\u9686\uFF1B\u4E0D\u4F1A\u8986\u76D6\u6216\u5220\u9664\u5DF2\u6709\u6587\u4EF6\u3002"
|
|
102
|
+
};
|
|
84
103
|
if (/no space left|disk quota/i.test(output))
|
|
85
|
-
return
|
|
86
|
-
|
|
104
|
+
return {
|
|
105
|
+
code: "CLONE_DISK_FULL",
|
|
106
|
+
message: "\u670D\u52A1\u5668\u78C1\u76D8\u7A7A\u95F4\u4E0D\u8DB3\uFF0C\u8BF7\u91CA\u653E\u7A7A\u95F4\u540E\u4F7F\u7528\u65B0\u7684\u7A7A\u76EE\u5F55\u91CD\u8BD5\u3002"
|
|
107
|
+
};
|
|
108
|
+
return {
|
|
109
|
+
code: "CLONE_FAILED",
|
|
110
|
+
message: "Git \u514B\u9686\u5931\u8D25\u3002\u8BF7\u68C0\u67E5\u4ED3\u5E93\u5730\u5740\u3001\u4E3B\u673A\u4E0A\u7684 Git \u8BA4\u8BC1\u3001\u76EE\u5F55\u6743\u9650\u548C\u78C1\u76D8\u7A7A\u95F4\u540E\u91CD\u8BD5\u3002"
|
|
111
|
+
};
|
|
87
112
|
}
|
|
88
113
|
class ProjectClones {
|
|
89
114
|
constructor(register, registered) {
|
|
@@ -141,7 +166,8 @@ class ProjectClones {
|
|
|
141
166
|
path,
|
|
142
167
|
name: parsed.name,
|
|
143
168
|
status: "cloning",
|
|
144
|
-
progress: "\u6B63\u5728\u8FDE\u63A5\u4ED3\u5E93\u2026"
|
|
169
|
+
progress: "\u6B63\u5728\u8FDE\u63A5\u4ED3\u5E93\u2026",
|
|
170
|
+
progressStage: "connecting"
|
|
145
171
|
},
|
|
146
172
|
fingerprint,
|
|
147
173
|
done: Promise.resolve()
|
|
@@ -166,6 +192,7 @@ class ProjectClones {
|
|
|
166
192
|
if (job && active(job) && job.view.status !== "cancelling") {
|
|
167
193
|
job.view.status = "cancelling";
|
|
168
194
|
job.view.progress = "\u6B63\u5728\u53D6\u6D88\uFF0C\u7B49\u5F85 Git \u9000\u51FA\u2026";
|
|
195
|
+
job.view.progressStage = "cancelling";
|
|
169
196
|
this.signal(job, "SIGTERM");
|
|
170
197
|
}
|
|
171
198
|
return this.get(id);
|
|
@@ -181,10 +208,14 @@ class ProjectClones {
|
|
|
181
208
|
job.view.project = this.register({ path: job.view.path, name: job.view.name });
|
|
182
209
|
job.view.status = "completed";
|
|
183
210
|
job.view.progress = "\u514B\u9686\u5B8C\u6210\uFF0C\u9879\u76EE\u5DF2\u6DFB\u52A0";
|
|
211
|
+
job.view.progressStage = "completed";
|
|
212
|
+
delete job.view.errorCode;
|
|
184
213
|
delete job.view.error;
|
|
185
214
|
} catch {
|
|
186
215
|
job.view.status = "registration-failed";
|
|
187
216
|
job.view.progress = "\u514B\u9686\u5DF2\u5B8C\u6210\uFF0C\u9879\u76EE\u5C1A\u672A\u6DFB\u52A0";
|
|
217
|
+
job.view.progressStage = "registration_failed";
|
|
218
|
+
job.view.errorCode = "CLONE_REGISTRATION_FAILED";
|
|
188
219
|
job.view.error = "\u9879\u76EE\u6CE8\u518C\u5931\u8D25\u3002\u5DF2\u514B\u9686\u7684\u6587\u4EF6\u4FDD\u7559\u5728\u76EE\u6807\u76EE\u5F55\uFF1B\u53EF\u91CD\u8BD5\u6DFB\u52A0\u9879\u76EE\uFF0C\u65E0\u9700\u518D\u6B21\u514B\u9686\uFF0C\u4E5F\u53EF\u901A\u8FC7\u201C\u5DF2\u6709\u76EE\u5F55\u201D\u6DFB\u52A0\u3002";
|
|
189
220
|
}
|
|
190
221
|
}
|
|
@@ -241,14 +272,14 @@ class ProjectClones {
|
|
|
241
272
|
);
|
|
242
273
|
if (!match || job.view.status !== "cloning") continue;
|
|
243
274
|
const labels = {
|
|
244
|
-
"Receiving objects": "\u6B63\u5728\u63A5\u6536\u5BF9\u8C61",
|
|
245
|
-
"Resolving deltas": "\u6B63\u5728\u89E3\u6790\u5DEE\u5F02",
|
|
246
|
-
"Updating files": "\u6B63\u5728\u68C0\u51FA\u6587\u4EF6",
|
|
247
|
-
"Checking out files": "\u6B63\u5728\u68C0\u51FA\u6587\u4EF6",
|
|
248
|
-
"Counting objects": "\u6B63\u5728\u7EDF\u8BA1\u5BF9\u8C61",
|
|
249
|
-
"Compressing objects": "\u6B63\u5728\u538B\u7F29\u5BF9\u8C61"
|
|
275
|
+
"Receiving objects": ["receiving", "\u6B63\u5728\u63A5\u6536\u5BF9\u8C61"],
|
|
276
|
+
"Resolving deltas": ["resolving", "\u6B63\u5728\u89E3\u6790\u5DEE\u5F02"],
|
|
277
|
+
"Updating files": ["checking_out", "\u6B63\u5728\u68C0\u51FA\u6587\u4EF6"],
|
|
278
|
+
"Checking out files": ["checking_out", "\u6B63\u5728\u68C0\u51FA\u6587\u4EF6"],
|
|
279
|
+
"Counting objects": ["counting", "\u6B63\u5728\u7EDF\u8BA1\u5BF9\u8C61"],
|
|
280
|
+
"Compressing objects": ["compressing", "\u6B63\u5728\u538B\u7F29\u5BF9\u8C61"]
|
|
250
281
|
};
|
|
251
|
-
job.view.progress = labels[match[1]];
|
|
282
|
+
[job.view.progressStage, job.view.progress] = labels[match[1]];
|
|
252
283
|
job.view.percent = Math.min(100, Number(match[2]));
|
|
253
284
|
}
|
|
254
285
|
};
|
|
@@ -276,11 +307,16 @@ class ProjectClones {
|
|
|
276
307
|
if (job.view.status === "cancelling") {
|
|
277
308
|
job.view.status = timedOut ? "failed" : "cancelled";
|
|
278
309
|
job.view.progress = timedOut ? "\u514B\u9686\u8D85\u65F6\uFF0C\u5DF2\u505C\u6B62" : "\u5DF2\u53D6\u6D88\u514B\u9686";
|
|
310
|
+
job.view.progressStage = timedOut ? "timed_out" : "cancelled";
|
|
311
|
+
job.view.errorCode = timedOut ? "CLONE_TIMED_OUT" : "CLONE_CANCELLED";
|
|
279
312
|
job.view.error = `${timedOut ? "\u514B\u9686\u8D85\u8FC7 30 \u5206\u949F\uFF0C\u5DF2\u505C\u6B62\u3002" : ""}\u672A\u6DFB\u52A0\u9879\u76EE\uFF0C\u4E5F\u672A\u5220\u9664\u4EFB\u4F55\u6587\u4EF6\u3002\u76EE\u6807\u76EE\u5F55\u53EF\u80FD\u6B8B\u7559\u6587\u4EF6\uFF0C\u8BF7\u68C0\u67E5\u540E\u4F7F\u7528\u65B0\u7684\u7A7A\u76EE\u5F55\u91CD\u8BD5\u3002`;
|
|
280
313
|
} else if (code !== 0) {
|
|
281
314
|
job.view.status = "failed";
|
|
282
315
|
job.view.progress = "\u514B\u9686\u5931\u8D25";
|
|
283
|
-
job.view.
|
|
316
|
+
job.view.progressStage = "failed";
|
|
317
|
+
const problem = failure(output, missingGit);
|
|
318
|
+
job.view.errorCode = problem.code;
|
|
319
|
+
job.view.error = `${problem.message} \u672A\u6DFB\u52A0\u9879\u76EE\uFF1B\u76EE\u6807\u76EE\u5F55\u53EF\u80FD\u6B8B\u7559\u6587\u4EF6\uFF0C\u8BF7\u68C0\u67E5\u540E\u4F7F\u7528\u65B0\u7684\u7A7A\u76EE\u5F55\u91CD\u8BD5\u3002`;
|
|
284
320
|
} else this.finishRegistration(job);
|
|
285
321
|
}
|
|
286
322
|
async close() {
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../src/server/project-clones.ts"],
|
|
4
|
-
"sourcesContent": ["import { spawn, type ChildProcess } from 'node:child_process';\nimport { createHash } from 'node:crypto';\nimport { accessSync, constants, lstatSync, readdirSync, realpathSync } from 'node:fs';\nimport { basename, dirname, isAbsolute, join, resolve } from 'node:path';\nimport { AppError } from '../shared/errors.js';\nimport type {\n CloneProjectInput,\n CreateProjectInput,\n Project,\n ProjectClone,\n} from '../shared/types.js';\nimport { cloneProjectSchema } from '../shared/validation.js';\nimport { containsPath } from './projects.js';\n\ninterface Job {\n view: ProjectClone;\n fingerprint: string;\n child?: ChildProcess;\n done: Promise<void>;\n}\nconst active = (job: Job) => ['cloning', 'cancelling'].includes(job.view.status);\nconst hasControl = (value: string) =>\n [...value].some((character) => character.charCodeAt(0) < 32 || character.charCodeAt(0) === 127);\n\n/** Only remote Git transports; never pass a local path, option, or remote helper to Git. */\nexport function validateRepository(repository: string): void {\n let valid = false;\n if (!hasControl(repository) && !/[\\s\\\\]/.test(repository)) {\n if (/^(https|ssh):\\/\\//.test(repository)) {\n try {\n const url = new URL(repository);\n valid = Boolean(\n url.hostname &&\n !url.hostname.startsWith('-') &&\n url.pathname.length > 1 &&\n !url.password &&\n !url.search &&\n !url.hash &&\n (url.protocol === 'ssh:'\n ? /^(?:[a-zA-Z0-9_][\\w.-]*)?$/.test(url.username)\n : !url.username),\n );\n } catch {\n /* Report a fixed message, never the supplied URL. */\n }\n } else {\n valid = /^(?:[a-zA-Z0-9_][\\w.-]*@)?[a-zA-Z0-9][\\w.-]*:[^/:\\s][^\\s]*$/.test(repository);\n }\n }\n if (!valid)\n throw new AppError(\n 'INVALID_REPOSITORY',\n '\u8BF7\u8F93\u5165 HTTPS \u6216 SSH Git \u4ED3\u5E93\u5730\u5740\uFF1B\u8BF7\u4F7F\u7528\u4E3B\u673A\u5DF2\u6709\u7684\u8BA4\u8BC1\uFF0C\u4E0D\u8981\u5728\u5730\u5740\u4E2D\u586B\u5199\u5BC6\u7801\u3001\u4EE4\u724C\u6216\u67E5\u8BE2\u53C2\u6570',\n 400,\n );\n}\n\nfunction targetPath(path: string): string {\n if (!isAbsolute(path) || hasControl(path) || resolve(path) === dirname(resolve(path)))\n throw new AppError('INVALID_PROJECT_PATH', '\u8BF7\u586B\u5199\u670D\u52A1\u5668\u4E0A\u7684\u7EDD\u5BF9\u76EE\u6807\u76EE\u5F55', 400);\n try {\n const target = join(realpathSync(dirname(resolve(path))), basename(resolve(path)));\n accessSync(dirname(target), constants.W_OK | constants.X_OK);\n return target;\n } catch {\n throw new AppError(\n 'CLONE_PARENT_UNAVAILABLE',\n '\u76EE\u6807\u76EE\u5F55\u7684\u7236\u76EE\u5F55\u5FC5\u987B\u5DF2\u5B58\u5728\uFF0C\u4E14\u670D\u52A1\u9700\u8981\u5199\u5165\u548C\u8FDB\u5165\u6743\u9650',\n 409,\n );\n }\n}\n\nfunction checkEmpty(path: string): void {\n try {\n const stat = lstatSync(path);\n if (!stat.isDirectory() || stat.isSymbolicLink() || readdirSync(path).length)\n throw new AppError(\n 'CLONE_TARGET_EXISTS',\n '\u76EE\u6807\u76EE\u5F55\u5DF2\u5B58\u5728\u4E14\u975E\u7A7A\uFF0C\u6216\u4E0D\u662F\u666E\u901A\u76EE\u5F55\uFF1B\u8BF7\u9009\u62E9\u65B0\u7684\u7A7A\u76EE\u5F55\uFF0C\u4E0D\u4F1A\u8986\u76D6\u5DF2\u6709\u6587\u4EF6',\n 409,\n );\n } catch (error) {\n if ((error as NodeJS.ErrnoException).code === 'ENOENT') return;\n if (error instanceof AppError) throw error;\n throw new AppError(\n 'CLONE_TARGET_UNAVAILABLE',\n '\u65E0\u6CD5\u68C0\u67E5\u76EE\u6807\u76EE\u5F55\uFF0C\u8BF7\u68C0\u67E5\u670D\u52A1\u5668\u8DEF\u5F84\u548C\u8BBF\u95EE\u6743\u9650',\n 409,\n );\n }\n}\n\nfunction failure(output: string, missingGit: boolean): string {\n if (missingGit) return '\u670D\u52A1\u5668\u672A\u5B89\u88C5 Git\uFF0C\u6216\u670D\u52A1\u7684 PATH \u4E2D\u627E\u4E0D\u5230 Git\u3002\u5B89\u88C5\u5E76\u914D\u7F6E\u540E\u91CD\u8BD5\u3002';\n if (\n /authentication failed|could not read username|permission denied|publickey|terminal prompts disabled|credential|access denied|401|403/i.test(\n output,\n )\n )\n return 'Git \u8BA4\u8BC1\u5931\u8D25\u6216\u6CA1\u6709\u4ED3\u5E93\u8BBF\u95EE\u6743\u9650\u3002\u8BF7\u5728\u8FD0\u884C\u670D\u52A1\u7684\u4E3B\u673A\u4E0A\u914D\u7F6E Git \u51ED\u636E\u6216 SSH \u5BC6\u94A5\u540E\u91CD\u8BD5\u3002';\n if (/host key verification failed/i.test(output))\n return 'SSH \u4E3B\u673A\u9A8C\u8BC1\u5931\u8D25\u3002\u8BF7\u5728\u8FD0\u884C\u670D\u52A1\u7684\u4E3B\u673A\u4E0A\u786E\u8BA4\u5E76\u914D\u7F6E\u4ED3\u5E93\u4E3B\u673A\u7684 SSH \u4FE1\u4EFB\u540E\u91CD\u8BD5\u3002';\n if (\n /repository .*not found|does not appear to be a git repository|not a repository|404/i.test(\n output,\n )\n )\n return '\u4ED3\u5E93\u4E0D\u5B58\u5728\u6216\u4E0D\u53EF\u8BBF\u95EE\u3002\u8BF7\u68C0\u67E5\u4ED3\u5E93\u5730\u5740\u548C\u4E3B\u673A\u4E0A\u7684 Git \u8BBF\u95EE\u6743\u9650\u3002';\n if (\n /could not resolve|failed to connect|unable to access|connection|timed out|certificate/i.test(\n output,\n )\n )\n return '\u65E0\u6CD5\u8FDE\u63A5 Git \u4ED3\u5E93\u3002\u8BF7\u68C0\u67E5\u670D\u52A1\u5668\u7F51\u7EDC\u3001\u4EE3\u7406\u548C\u8BC1\u4E66\u914D\u7F6E\u540E\u91CD\u8BD5\u3002';\n if (/already exists and is not an empty directory/i.test(output))\n return '\u76EE\u6807\u76EE\u5F55\u5DF2\u5305\u542B\u6587\u4EF6\uFF0CGit \u5DF2\u505C\u6B62\u514B\u9686\uFF1B\u4E0D\u4F1A\u8986\u76D6\u6216\u5220\u9664\u5DF2\u6709\u6587\u4EF6\u3002';\n if (/no space left|disk quota/i.test(output))\n return '\u670D\u52A1\u5668\u78C1\u76D8\u7A7A\u95F4\u4E0D\u8DB3\uFF0C\u8BF7\u91CA\u653E\u7A7A\u95F4\u540E\u4F7F\u7528\u65B0\u7684\u7A7A\u76EE\u5F55\u91CD\u8BD5\u3002';\n return 'Git \u514B\u9686\u5931\u8D25\u3002\u8BF7\u68C0\u67E5\u4ED3\u5E93\u5730\u5740\u3001\u4E3B\u673A\u4E0A\u7684 Git \u8BA4\u8BC1\u3001\u76EE\u5F55\u6743\u9650\u548C\u78C1\u76D8\u7A7A\u95F4\u540E\u91CD\u8BD5\u3002';\n}\n\n/** Transient jobs never persist repository URLs, credentials, or raw Git output. */\nexport class ProjectClones {\n private readonly jobs = new Map<string, Job>();\n private closing = false;\n constructor(\n private readonly register: (input: CreateProjectInput) => Project,\n private readonly registered: (path: string) => boolean,\n ) {}\n\n get hasActive(): boolean {\n return [...this.jobs.values()].some(active);\n }\n\n assertIdlePath(path: string): void {\n if (!this.hasActive) return;\n let canonical = resolve(path);\n try {\n canonical = realpathSync(path);\n } catch {\n try {\n canonical = join(realpathSync(dirname(canonical)), basename(canonical));\n } catch {\n /* Normal registration validation reports unavailable paths. */\n }\n }\n if (\n [...this.jobs.values()].some(\n (job) =>\n active(job) &&\n (containsPath(job.view.path, canonical) || containsPath(canonical, job.view.path)),\n )\n )\n 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);\n }\n\n start(input: CloneProjectInput): ProjectClone {\n const parsed = cloneProjectSchema.parse(input);\n validateRepository(parsed.repository);\n const fingerprint = createHash('sha256').update(JSON.stringify(parsed)).digest('hex');\n const previous = this.jobs.get(parsed.clientOperationId);\n if (previous) {\n if (previous.fingerprint !== fingerprint)\n throw new AppError('CLONE_ID_CONFLICT', '\u8BE5\u64CD\u4F5C ID \u5DF2\u7528\u4E8E\u5176\u4ED6\u514B\u9686\u8BF7\u6C42', 409);\n return this.get(previous.view.id);\n }\n if (this.closing) throw new AppError('GATEWAY_STOPPING', '\u670D\u52A1\u6B63\u5728\u505C\u6B62\uFF0C\u8BF7\u7A0D\u540E\u91CD\u8BD5', 503);\n const path = targetPath(parsed.path);\n this.assertIdlePath(path);\n if (this.registered(path))\n 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);\n checkEmpty(path);\n if ([...this.jobs.values()].filter(active).length >= 4)\n throw new AppError('CLONE_LIMIT', '\u5DF2\u6709\u591A\u4E2A\u4ED3\u5E93\u6B63\u5728\u514B\u9686\uFF0C\u8BF7\u7B49\u5F85\u5B8C\u6210\u540E\u91CD\u8BD5', 409);\n // Bound transient history without evicting a running process.\n if (this.jobs.size >= 100) {\n const oldest = [...this.jobs].find(([, job]) => !active(job));\n if (oldest) this.jobs.delete(oldest[0]);\n }\n const job: Job = {\n view: {\n id: parsed.clientOperationId,\n path,\n name: parsed.name,\n status: 'cloning',\n progress: '\u6B63\u5728\u8FDE\u63A5\u4ED3\u5E93\u2026',\n },\n fingerprint,\n done: Promise.resolve(),\n };\n this.jobs.set(job.view.id, job);\n job.done = this.run(job, parsed.repository);\n return this.get(job.view.id);\n }\n\n get(id: string): ProjectClone {\n const job = this.jobs.get(id);\n if (!job)\n throw new AppError(\n 'CLONE_NOT_FOUND',\n '\u672A\u627E\u5230\u514B\u9686\u8BB0\u5F55\uFF1B\u670D\u52A1\u91CD\u542F\u540E\u8BF7\u68C0\u67E5\u76EE\u6807\u76EE\u5F55\uFF0C\u518D\u6DFB\u52A0\u5DF2\u6709\u76EE\u5F55\u6216\u4F7F\u7528\u65B0\u7684\u7A7A\u76EE\u5F55\u91CD\u8BD5',\n 404,\n );\n return structuredClone(job.view);\n }\n\n cancel(id: string): ProjectClone {\n const job = this.jobs.get(id);\n this.get(id);\n if (job && active(job) && job.view.status !== 'cancelling') {\n job.view.status = 'cancelling';\n job.view.progress = '\u6B63\u5728\u53D6\u6D88\uFF0C\u7B49\u5F85 Git \u9000\u51FA\u2026';\n this.signal(job, 'SIGTERM');\n }\n return this.get(id);\n }\n\n retryRegistration(id: string): ProjectClone {\n const job = this.jobs.get(id);\n this.get(id);\n if (job?.view.status === 'registration-failed') this.finishRegistration(job);\n return this.get(id);\n }\n\n private finishRegistration(job: Job): void {\n try {\n job.view.project = this.register({ path: job.view.path, name: job.view.name });\n job.view.status = 'completed';\n job.view.progress = '\u514B\u9686\u5B8C\u6210\uFF0C\u9879\u76EE\u5DF2\u6DFB\u52A0';\n delete job.view.error;\n } catch {\n job.view.status = 'registration-failed';\n job.view.progress = '\u514B\u9686\u5DF2\u5B8C\u6210\uFF0C\u9879\u76EE\u5C1A\u672A\u6DFB\u52A0';\n job.view.error =\n '\u9879\u76EE\u6CE8\u518C\u5931\u8D25\u3002\u5DF2\u514B\u9686\u7684\u6587\u4EF6\u4FDD\u7559\u5728\u76EE\u6807\u76EE\u5F55\uFF1B\u53EF\u91CD\u8BD5\u6DFB\u52A0\u9879\u76EE\uFF0C\u65E0\u9700\u518D\u6B21\u514B\u9686\uFF0C\u4E5F\u53EF\u901A\u8FC7\u201C\u5DF2\u6709\u76EE\u5F55\u201D\u6DFB\u52A0\u3002';\n }\n }\n\n private signal(job: Job, signal: NodeJS.Signals): void {\n if (!job.child?.pid) return;\n try {\n if (process.platform === 'win32') job.child.kill(signal);\n else process.kill(-job.child.pid, signal);\n } catch {\n /* Process may have already exited. */\n }\n }\n\n private async run(job: Job, repository: string): Promise<void> {\n let output = '';\n let pending = '';\n let missingGit = false;\n let timedOut = false;\n const sshCommand =\n process.env.GIT_SSH_COMMAND ??\n (process.env.GIT_SSH ? `'${process.env.GIT_SSH.replaceAll(\"'\", \"'\\\\''\")}'` : 'ssh');\n const child = spawn(\n 'git',\n [\n '-c',\n 'protocol.allow=never',\n '-c',\n 'protocol.https.allow=always',\n '-c',\n 'protocol.ssh.allow=always',\n 'clone',\n '--progress',\n '--',\n repository,\n job.view.path,\n ],\n {\n detached: process.platform !== 'win32',\n stdio: ['ignore', 'pipe', 'pipe'],\n env: {\n ...process.env,\n GIT_TERMINAL_PROMPT: '0',\n GCM_INTERACTIVE: 'never',\n GIT_SSH_COMMAND: `${sshCommand} -oBatchMode=yes -oStrictHostKeyChecking=yes`,\n LC_ALL: 'C',\n },\n },\n );\n job.child = child;\n const read = (chunk: Buffer) => {\n output = (output + chunk.toString()).slice(-16_384);\n pending = (pending + chunk.toString()).slice(-4096);\n const lines = pending.split(/[\\r\\n]/);\n pending = lines.pop() ?? '';\n for (const line of lines) {\n const match =\n /^(?:remote: )?(Receiving objects|Resolving deltas|Updating files|Checking out files|Counting objects|Compressing objects):\\s+(\\d{1,3})%/.exec(\n line,\n );\n if (!match || job.view.status !== 'cloning') continue;\n const labels: Record<string, string> = {\n 'Receiving objects': '\u6B63\u5728\u63A5\u6536\u5BF9\u8C61',\n 'Resolving deltas': '\u6B63\u5728\u89E3\u6790\u5DEE\u5F02',\n 'Updating files': '\u6B63\u5728\u68C0\u51FA\u6587\u4EF6',\n 'Checking out files': '\u6B63\u5728\u68C0\u51FA\u6587\u4EF6',\n 'Counting objects': '\u6B63\u5728\u7EDF\u8BA1\u5BF9\u8C61',\n 'Compressing objects': '\u6B63\u5728\u538B\u7F29\u5BF9\u8C61',\n };\n job.view.progress = labels[match[1]!]!;\n job.view.percent = Math.min(100, Number(match[2]));\n }\n };\n child.stdout?.on('data', read);\n child.stderr?.on('data', read);\n const timeout = setTimeout(() => {\n timedOut = true;\n this.cancel(job.view.id);\n }, 30 * 60_000);\n timeout.unref();\n const escalation = setInterval(() => {\n if (job.view.status === 'cancelling') this.signal(job, 'SIGKILL');\n }, 3000);\n escalation.unref();\n const code = await new Promise<number | null>((done) => {\n child.once('error', (error: NodeJS.ErrnoException) => {\n missingGit = error.code === 'ENOENT';\n });\n child.once('close', done);\n });\n clearTimeout(timeout);\n clearInterval(escalation);\n job.child = undefined;\n delete job.view.percent;\n if (job.view.status === 'cancelling') {\n job.view.status = timedOut ? 'failed' : 'cancelled';\n job.view.progress = timedOut ? '\u514B\u9686\u8D85\u65F6\uFF0C\u5DF2\u505C\u6B62' : '\u5DF2\u53D6\u6D88\u514B\u9686';\n job.view.error = `${timedOut ? '\u514B\u9686\u8D85\u8FC7 30 \u5206\u949F\uFF0C\u5DF2\u505C\u6B62\u3002' : ''}\u672A\u6DFB\u52A0\u9879\u76EE\uFF0C\u4E5F\u672A\u5220\u9664\u4EFB\u4F55\u6587\u4EF6\u3002\u76EE\u6807\u76EE\u5F55\u53EF\u80FD\u6B8B\u7559\u6587\u4EF6\uFF0C\u8BF7\u68C0\u67E5\u540E\u4F7F\u7528\u65B0\u7684\u7A7A\u76EE\u5F55\u91CD\u8BD5\u3002`;\n } else if (code !== 0) {\n job.view.status = 'failed';\n job.view.progress = '\u514B\u9686\u5931\u8D25';\n job.view.error = `${failure(output, missingGit)} \u672A\u6DFB\u52A0\u9879\u76EE\uFF1B\u76EE\u6807\u76EE\u5F55\u53EF\u80FD\u6B8B\u7559\u6587\u4EF6\uFF0C\u8BF7\u68C0\u67E5\u540E\u4F7F\u7528\u65B0\u7684\u7A7A\u76EE\u5F55\u91CD\u8BD5\u3002`;\n } else this.finishRegistration(job);\n }\n\n async close(): Promise<void> {\n this.closing = true;\n for (const job of this.jobs.values()) if (active(job)) this.cancel(job.view.id);\n await Promise.all([...this.jobs.values()].map((job) => job.done));\n }\n}\n"],
|
|
5
|
-
"mappings": "AAAA,SAAS,aAAgC;AACzC,SAAS,kBAAkB;AAC3B,SAAS,YAAY,WAAW,WAAW,aAAa,oBAAoB;AAC5E,SAAS,UAAU,SAAS,YAAY,MAAM,eAAe;AAC7D,SAAS,gBAAgB;AAOzB,SAAS,0BAA0B;AACnC,SAAS,oBAAoB;AAQ7B,MAAM,SAAS,CAAC,QAAa,CAAC,WAAW,YAAY,EAAE,SAAS,IAAI,KAAK,MAAM;AAC/E,MAAM,aAAa,CAAC,UAClB,CAAC,GAAG,KAAK,EAAE,KAAK,CAAC,cAAc,UAAU,WAAW,CAAC,IAAI,MAAM,UAAU,WAAW,CAAC,MAAM,GAAG;AAGzF,SAAS,mBAAmB,YAA0B;AAC3D,MAAI,QAAQ;AACZ,MAAI,CAAC,WAAW,UAAU,KAAK,CAAC,SAAS,KAAK,UAAU,GAAG;AACzD,QAAI,oBAAoB,KAAK,UAAU,GAAG;AACxC,UAAI;AACF,cAAM,MAAM,IAAI,IAAI,UAAU;AAC9B,gBAAQ;AAAA,UACN,IAAI,YACJ,CAAC,IAAI,SAAS,WAAW,GAAG,KAC5B,IAAI,SAAS,SAAS,KACtB,CAAC,IAAI,YACL,CAAC,IAAI,UACL,CAAC,IAAI,SACJ,IAAI,aAAa,SACd,6BAA6B,KAAK,IAAI,QAAQ,IAC9C,CAAC,IAAI;AAAA,QACX;AAAA,MACF,QAAQ;AAAA,MAER;AAAA,IACF,OAAO;AACL,cAAQ,8DAA8D,KAAK,UAAU;AAAA,IACvF;AAAA,EACF;AACA,MAAI,CAAC;AACH,UAAM,IAAI;AAAA,MACR;AAAA,MACA;AAAA,MACA;AAAA,IACF;AACJ;AAEA,SAAS,WAAW,MAAsB;AACxC,MAAI,CAAC,WAAW,IAAI,KAAK,WAAW,IAAI,KAAK,QAAQ,IAAI,MAAM,QAAQ,QAAQ,IAAI,CAAC;AAClF,UAAM,IAAI,SAAS,wBAAwB,wFAAkB,GAAG;AAClE,MAAI;AACF,UAAM,SAAS,KAAK,aAAa,QAAQ,QAAQ,IAAI,CAAC,CAAC,GAAG,SAAS,QAAQ,IAAI,CAAC,CAAC;AACjF,eAAW,QAAQ,MAAM,GAAG,UAAU,OAAO,UAAU,IAAI;AAC3D,WAAO;AAAA,EACT,QAAQ;AACN,UAAM,IAAI;AAAA,MACR;AAAA,MACA;AAAA,MACA;AAAA,IACF;AAAA,EACF;AACF;AAEA,SAAS,WAAW,MAAoB;AACtC,MAAI;AACF,UAAM,OAAO,UAAU,IAAI;AAC3B,QAAI,CAAC,KAAK,YAAY,KAAK,KAAK,eAAe,KAAK,YAAY,IAAI,EAAE;AACpE,YAAM,IAAI;AAAA,QACR;AAAA,QACA;AAAA,QACA;AAAA,MACF;AAAA,EACJ,SAAS,OAAO;AACd,QAAK,MAAgC,SAAS,SAAU;AACxD,QAAI,iBAAiB,SAAU,OAAM;AACrC,UAAM,IAAI;AAAA,MACR;AAAA,MACA;AAAA,MACA;AAAA,IACF;AAAA,EACF;AACF;AAEA,SAAS,
|
|
4
|
+
"sourcesContent": ["import { spawn, type ChildProcess } from 'node:child_process';\nimport { createHash } from 'node:crypto';\nimport { accessSync, constants, lstatSync, readdirSync, realpathSync } from 'node:fs';\nimport { basename, dirname, isAbsolute, join, resolve } from 'node:path';\nimport { AppError } from '../shared/errors.js';\nimport type {\n CloneProjectInput,\n CreateProjectInput,\n Project,\n ProjectClone,\n} from '../shared/types.js';\nimport { cloneProjectSchema } from '../shared/validation.js';\nimport { containsPath } from './projects.js';\n\ninterface Job {\n view: ProjectClone;\n fingerprint: string;\n child?: ChildProcess;\n done: Promise<void>;\n}\nconst active = (job: Job) => ['cloning', 'cancelling'].includes(job.view.status);\nconst hasControl = (value: string) =>\n [...value].some((character) => character.charCodeAt(0) < 32 || character.charCodeAt(0) === 127);\n\n/** Only remote Git transports; never pass a local path, option, or remote helper to Git. */\nexport function validateRepository(repository: string): void {\n let valid = false;\n if (!hasControl(repository) && !/[\\s\\\\]/.test(repository)) {\n if (/^(https|ssh):\\/\\//.test(repository)) {\n try {\n const url = new URL(repository);\n valid = Boolean(\n url.hostname &&\n !url.hostname.startsWith('-') &&\n url.pathname.length > 1 &&\n !url.password &&\n !url.search &&\n !url.hash &&\n (url.protocol === 'ssh:'\n ? /^(?:[a-zA-Z0-9_][\\w.-]*)?$/.test(url.username)\n : !url.username),\n );\n } catch {\n /* Report a fixed message, never the supplied URL. */\n }\n } else {\n valid = /^(?:[a-zA-Z0-9_][\\w.-]*@)?[a-zA-Z0-9][\\w.-]*:[^/:\\s][^\\s]*$/.test(repository);\n }\n }\n if (!valid)\n throw new AppError(\n 'INVALID_REPOSITORY',\n '\u8BF7\u8F93\u5165 HTTPS \u6216 SSH Git \u4ED3\u5E93\u5730\u5740\uFF1B\u8BF7\u4F7F\u7528\u4E3B\u673A\u5DF2\u6709\u7684\u8BA4\u8BC1\uFF0C\u4E0D\u8981\u5728\u5730\u5740\u4E2D\u586B\u5199\u5BC6\u7801\u3001\u4EE4\u724C\u6216\u67E5\u8BE2\u53C2\u6570',\n 400,\n );\n}\n\nfunction targetPath(path: string): string {\n if (!isAbsolute(path) || hasControl(path) || resolve(path) === dirname(resolve(path)))\n throw new AppError('INVALID_PROJECT_PATH', '\u8BF7\u586B\u5199\u670D\u52A1\u5668\u4E0A\u7684\u7EDD\u5BF9\u76EE\u6807\u76EE\u5F55', 400);\n try {\n const target = join(realpathSync(dirname(resolve(path))), basename(resolve(path)));\n accessSync(dirname(target), constants.W_OK | constants.X_OK);\n return target;\n } catch {\n throw new AppError(\n 'CLONE_PARENT_UNAVAILABLE',\n '\u76EE\u6807\u76EE\u5F55\u7684\u7236\u76EE\u5F55\u5FC5\u987B\u5DF2\u5B58\u5728\uFF0C\u4E14\u670D\u52A1\u9700\u8981\u5199\u5165\u548C\u8FDB\u5165\u6743\u9650',\n 409,\n );\n }\n}\n\nfunction checkEmpty(path: string): void {\n try {\n const stat = lstatSync(path);\n if (!stat.isDirectory() || stat.isSymbolicLink() || readdirSync(path).length)\n throw new AppError(\n 'CLONE_TARGET_EXISTS',\n '\u76EE\u6807\u76EE\u5F55\u5DF2\u5B58\u5728\u4E14\u975E\u7A7A\uFF0C\u6216\u4E0D\u662F\u666E\u901A\u76EE\u5F55\uFF1B\u8BF7\u9009\u62E9\u65B0\u7684\u7A7A\u76EE\u5F55\uFF0C\u4E0D\u4F1A\u8986\u76D6\u5DF2\u6709\u6587\u4EF6',\n 409,\n );\n } catch (error) {\n if ((error as NodeJS.ErrnoException).code === 'ENOENT') return;\n if (error instanceof AppError) throw error;\n throw new AppError(\n 'CLONE_TARGET_UNAVAILABLE',\n '\u65E0\u6CD5\u68C0\u67E5\u76EE\u6807\u76EE\u5F55\uFF0C\u8BF7\u68C0\u67E5\u670D\u52A1\u5668\u8DEF\u5F84\u548C\u8BBF\u95EE\u6743\u9650',\n 409,\n );\n }\n}\n\nfunction failure(\n output: string,\n missingGit: boolean,\n): { code: NonNullable<ProjectClone['errorCode']>; message: string } {\n if (missingGit)\n return {\n code: 'CLONE_GIT_MISSING',\n message: '\u670D\u52A1\u5668\u672A\u5B89\u88C5 Git\uFF0C\u6216\u670D\u52A1\u7684 PATH \u4E2D\u627E\u4E0D\u5230 Git\u3002\u5B89\u88C5\u5E76\u914D\u7F6E\u540E\u91CD\u8BD5\u3002',\n };\n if (\n /authentication failed|could not read username|permission denied|publickey|terminal prompts disabled|credential|access denied|401|403/i.test(\n output,\n )\n )\n return {\n code: 'CLONE_AUTH_FAILED',\n message:\n 'Git \u8BA4\u8BC1\u5931\u8D25\u6216\u6CA1\u6709\u4ED3\u5E93\u8BBF\u95EE\u6743\u9650\u3002\u8BF7\u5728\u8FD0\u884C\u670D\u52A1\u7684\u4E3B\u673A\u4E0A\u914D\u7F6E Git \u51ED\u636E\u6216 SSH \u5BC6\u94A5\u540E\u91CD\u8BD5\u3002',\n };\n if (/host key verification failed/i.test(output))\n return {\n code: 'CLONE_HOST_KEY_FAILED',\n message: 'SSH \u4E3B\u673A\u9A8C\u8BC1\u5931\u8D25\u3002\u8BF7\u5728\u8FD0\u884C\u670D\u52A1\u7684\u4E3B\u673A\u4E0A\u786E\u8BA4\u5E76\u914D\u7F6E\u4ED3\u5E93\u4E3B\u673A\u7684 SSH \u4FE1\u4EFB\u540E\u91CD\u8BD5\u3002',\n };\n if (\n /repository .*not found|does not appear to be a git repository|not a repository|404/i.test(\n output,\n )\n )\n return {\n code: 'CLONE_REPOSITORY_UNAVAILABLE',\n message: '\u4ED3\u5E93\u4E0D\u5B58\u5728\u6216\u4E0D\u53EF\u8BBF\u95EE\u3002\u8BF7\u68C0\u67E5\u4ED3\u5E93\u5730\u5740\u548C\u4E3B\u673A\u4E0A\u7684 Git \u8BBF\u95EE\u6743\u9650\u3002',\n };\n if (\n /could not resolve|failed to connect|unable to access|connection|timed out|certificate/i.test(\n output,\n )\n )\n return {\n code: 'CLONE_NETWORK_FAILED',\n message: '\u65E0\u6CD5\u8FDE\u63A5 Git \u4ED3\u5E93\u3002\u8BF7\u68C0\u67E5\u670D\u52A1\u5668\u7F51\u7EDC\u3001\u4EE3\u7406\u548C\u8BC1\u4E66\u914D\u7F6E\u540E\u91CD\u8BD5\u3002',\n };\n if (/already exists and is not an empty directory/i.test(output))\n return {\n code: 'CLONE_TARGET_NOT_EMPTY',\n message: '\u76EE\u6807\u76EE\u5F55\u5DF2\u5305\u542B\u6587\u4EF6\uFF0CGit \u5DF2\u505C\u6B62\u514B\u9686\uFF1B\u4E0D\u4F1A\u8986\u76D6\u6216\u5220\u9664\u5DF2\u6709\u6587\u4EF6\u3002',\n };\n if (/no space left|disk quota/i.test(output))\n return {\n code: 'CLONE_DISK_FULL',\n message: '\u670D\u52A1\u5668\u78C1\u76D8\u7A7A\u95F4\u4E0D\u8DB3\uFF0C\u8BF7\u91CA\u653E\u7A7A\u95F4\u540E\u4F7F\u7528\u65B0\u7684\u7A7A\u76EE\u5F55\u91CD\u8BD5\u3002',\n };\n return {\n code: 'CLONE_FAILED',\n message: 'Git \u514B\u9686\u5931\u8D25\u3002\u8BF7\u68C0\u67E5\u4ED3\u5E93\u5730\u5740\u3001\u4E3B\u673A\u4E0A\u7684 Git \u8BA4\u8BC1\u3001\u76EE\u5F55\u6743\u9650\u548C\u78C1\u76D8\u7A7A\u95F4\u540E\u91CD\u8BD5\u3002',\n };\n}\n\n/** Transient jobs never persist repository URLs, credentials, or raw Git output. */\nexport class ProjectClones {\n private readonly jobs = new Map<string, Job>();\n private closing = false;\n constructor(\n private readonly register: (input: CreateProjectInput) => Project,\n private readonly registered: (path: string) => boolean,\n ) {}\n\n get hasActive(): boolean {\n return [...this.jobs.values()].some(active);\n }\n\n assertIdlePath(path: string): void {\n if (!this.hasActive) return;\n let canonical = resolve(path);\n try {\n canonical = realpathSync(path);\n } catch {\n try {\n canonical = join(realpathSync(dirname(canonical)), basename(canonical));\n } catch {\n /* Normal registration validation reports unavailable paths. */\n }\n }\n if (\n [...this.jobs.values()].some(\n (job) =>\n active(job) &&\n (containsPath(job.view.path, canonical) || containsPath(canonical, job.view.path)),\n )\n )\n 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);\n }\n\n start(input: CloneProjectInput): ProjectClone {\n const parsed = cloneProjectSchema.parse(input);\n validateRepository(parsed.repository);\n const fingerprint = createHash('sha256').update(JSON.stringify(parsed)).digest('hex');\n const previous = this.jobs.get(parsed.clientOperationId);\n if (previous) {\n if (previous.fingerprint !== fingerprint)\n throw new AppError('CLONE_ID_CONFLICT', '\u8BE5\u64CD\u4F5C ID \u5DF2\u7528\u4E8E\u5176\u4ED6\u514B\u9686\u8BF7\u6C42', 409);\n return this.get(previous.view.id);\n }\n if (this.closing) throw new AppError('GATEWAY_STOPPING', '\u670D\u52A1\u6B63\u5728\u505C\u6B62\uFF0C\u8BF7\u7A0D\u540E\u91CD\u8BD5', 503);\n const path = targetPath(parsed.path);\n this.assertIdlePath(path);\n if (this.registered(path))\n 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);\n checkEmpty(path);\n if ([...this.jobs.values()].filter(active).length >= 4)\n throw new AppError('CLONE_LIMIT', '\u5DF2\u6709\u591A\u4E2A\u4ED3\u5E93\u6B63\u5728\u514B\u9686\uFF0C\u8BF7\u7B49\u5F85\u5B8C\u6210\u540E\u91CD\u8BD5', 409);\n // Bound transient history without evicting a running process.\n if (this.jobs.size >= 100) {\n const oldest = [...this.jobs].find(([, job]) => !active(job));\n if (oldest) this.jobs.delete(oldest[0]);\n }\n const job: Job = {\n view: {\n id: parsed.clientOperationId,\n path,\n name: parsed.name,\n status: 'cloning',\n progress: '\u6B63\u5728\u8FDE\u63A5\u4ED3\u5E93\u2026',\n progressStage: 'connecting',\n },\n fingerprint,\n done: Promise.resolve(),\n };\n this.jobs.set(job.view.id, job);\n job.done = this.run(job, parsed.repository);\n return this.get(job.view.id);\n }\n\n get(id: string): ProjectClone {\n const job = this.jobs.get(id);\n if (!job)\n throw new AppError(\n 'CLONE_NOT_FOUND',\n '\u672A\u627E\u5230\u514B\u9686\u8BB0\u5F55\uFF1B\u670D\u52A1\u91CD\u542F\u540E\u8BF7\u68C0\u67E5\u76EE\u6807\u76EE\u5F55\uFF0C\u518D\u6DFB\u52A0\u5DF2\u6709\u76EE\u5F55\u6216\u4F7F\u7528\u65B0\u7684\u7A7A\u76EE\u5F55\u91CD\u8BD5',\n 404,\n );\n return structuredClone(job.view);\n }\n\n cancel(id: string): ProjectClone {\n const job = this.jobs.get(id);\n this.get(id);\n if (job && active(job) && job.view.status !== 'cancelling') {\n job.view.status = 'cancelling';\n job.view.progress = '\u6B63\u5728\u53D6\u6D88\uFF0C\u7B49\u5F85 Git \u9000\u51FA\u2026';\n job.view.progressStage = 'cancelling';\n this.signal(job, 'SIGTERM');\n }\n return this.get(id);\n }\n\n retryRegistration(id: string): ProjectClone {\n const job = this.jobs.get(id);\n this.get(id);\n if (job?.view.status === 'registration-failed') this.finishRegistration(job);\n return this.get(id);\n }\n\n private finishRegistration(job: Job): void {\n try {\n job.view.project = this.register({ path: job.view.path, name: job.view.name });\n job.view.status = 'completed';\n job.view.progress = '\u514B\u9686\u5B8C\u6210\uFF0C\u9879\u76EE\u5DF2\u6DFB\u52A0';\n job.view.progressStage = 'completed';\n delete job.view.errorCode;\n delete job.view.error;\n } catch {\n job.view.status = 'registration-failed';\n job.view.progress = '\u514B\u9686\u5DF2\u5B8C\u6210\uFF0C\u9879\u76EE\u5C1A\u672A\u6DFB\u52A0';\n job.view.progressStage = 'registration_failed';\n job.view.errorCode = 'CLONE_REGISTRATION_FAILED';\n job.view.error =\n '\u9879\u76EE\u6CE8\u518C\u5931\u8D25\u3002\u5DF2\u514B\u9686\u7684\u6587\u4EF6\u4FDD\u7559\u5728\u76EE\u6807\u76EE\u5F55\uFF1B\u53EF\u91CD\u8BD5\u6DFB\u52A0\u9879\u76EE\uFF0C\u65E0\u9700\u518D\u6B21\u514B\u9686\uFF0C\u4E5F\u53EF\u901A\u8FC7\u201C\u5DF2\u6709\u76EE\u5F55\u201D\u6DFB\u52A0\u3002';\n }\n }\n\n private signal(job: Job, signal: NodeJS.Signals): void {\n if (!job.child?.pid) return;\n try {\n if (process.platform === 'win32') job.child.kill(signal);\n else process.kill(-job.child.pid, signal);\n } catch {\n /* Process may have already exited. */\n }\n }\n\n private async run(job: Job, repository: string): Promise<void> {\n let output = '';\n let pending = '';\n let missingGit = false;\n let timedOut = false;\n const sshCommand =\n process.env.GIT_SSH_COMMAND ??\n (process.env.GIT_SSH ? `'${process.env.GIT_SSH.replaceAll(\"'\", \"'\\\\''\")}'` : 'ssh');\n const child = spawn(\n 'git',\n [\n '-c',\n 'protocol.allow=never',\n '-c',\n 'protocol.https.allow=always',\n '-c',\n 'protocol.ssh.allow=always',\n 'clone',\n '--progress',\n '--',\n repository,\n job.view.path,\n ],\n {\n detached: process.platform !== 'win32',\n stdio: ['ignore', 'pipe', 'pipe'],\n env: {\n ...process.env,\n GIT_TERMINAL_PROMPT: '0',\n GCM_INTERACTIVE: 'never',\n GIT_SSH_COMMAND: `${sshCommand} -oBatchMode=yes -oStrictHostKeyChecking=yes`,\n LC_ALL: 'C',\n },\n },\n );\n job.child = child;\n const read = (chunk: Buffer) => {\n output = (output + chunk.toString()).slice(-16_384);\n pending = (pending + chunk.toString()).slice(-4096);\n const lines = pending.split(/[\\r\\n]/);\n pending = lines.pop() ?? '';\n for (const line of lines) {\n const match =\n /^(?:remote: )?(Receiving objects|Resolving deltas|Updating files|Checking out files|Counting objects|Compressing objects):\\s+(\\d{1,3})%/.exec(\n line,\n );\n if (!match || job.view.status !== 'cloning') continue;\n const labels: Record<string, [NonNullable<ProjectClone['progressStage']>, string]> = {\n 'Receiving objects': ['receiving', '\u6B63\u5728\u63A5\u6536\u5BF9\u8C61'],\n 'Resolving deltas': ['resolving', '\u6B63\u5728\u89E3\u6790\u5DEE\u5F02'],\n 'Updating files': ['checking_out', '\u6B63\u5728\u68C0\u51FA\u6587\u4EF6'],\n 'Checking out files': ['checking_out', '\u6B63\u5728\u68C0\u51FA\u6587\u4EF6'],\n 'Counting objects': ['counting', '\u6B63\u5728\u7EDF\u8BA1\u5BF9\u8C61'],\n 'Compressing objects': ['compressing', '\u6B63\u5728\u538B\u7F29\u5BF9\u8C61'],\n };\n [job.view.progressStage, job.view.progress] = labels[match[1]!]!;\n job.view.percent = Math.min(100, Number(match[2]));\n }\n };\n child.stdout?.on('data', read);\n child.stderr?.on('data', read);\n const timeout = setTimeout(() => {\n timedOut = true;\n this.cancel(job.view.id);\n }, 30 * 60_000);\n timeout.unref();\n const escalation = setInterval(() => {\n if (job.view.status === 'cancelling') this.signal(job, 'SIGKILL');\n }, 3000);\n escalation.unref();\n const code = await new Promise<number | null>((done) => {\n child.once('error', (error: NodeJS.ErrnoException) => {\n missingGit = error.code === 'ENOENT';\n });\n child.once('close', done);\n });\n clearTimeout(timeout);\n clearInterval(escalation);\n job.child = undefined;\n delete job.view.percent;\n if (job.view.status === 'cancelling') {\n job.view.status = timedOut ? 'failed' : 'cancelled';\n job.view.progress = timedOut ? '\u514B\u9686\u8D85\u65F6\uFF0C\u5DF2\u505C\u6B62' : '\u5DF2\u53D6\u6D88\u514B\u9686';\n job.view.progressStage = timedOut ? 'timed_out' : 'cancelled';\n job.view.errorCode = timedOut ? 'CLONE_TIMED_OUT' : 'CLONE_CANCELLED';\n job.view.error = `${timedOut ? '\u514B\u9686\u8D85\u8FC7 30 \u5206\u949F\uFF0C\u5DF2\u505C\u6B62\u3002' : ''}\u672A\u6DFB\u52A0\u9879\u76EE\uFF0C\u4E5F\u672A\u5220\u9664\u4EFB\u4F55\u6587\u4EF6\u3002\u76EE\u6807\u76EE\u5F55\u53EF\u80FD\u6B8B\u7559\u6587\u4EF6\uFF0C\u8BF7\u68C0\u67E5\u540E\u4F7F\u7528\u65B0\u7684\u7A7A\u76EE\u5F55\u91CD\u8BD5\u3002`;\n } else if (code !== 0) {\n job.view.status = 'failed';\n job.view.progress = '\u514B\u9686\u5931\u8D25';\n job.view.progressStage = 'failed';\n const problem = failure(output, missingGit);\n job.view.errorCode = problem.code;\n job.view.error = `${problem.message} \u672A\u6DFB\u52A0\u9879\u76EE\uFF1B\u76EE\u6807\u76EE\u5F55\u53EF\u80FD\u6B8B\u7559\u6587\u4EF6\uFF0C\u8BF7\u68C0\u67E5\u540E\u4F7F\u7528\u65B0\u7684\u7A7A\u76EE\u5F55\u91CD\u8BD5\u3002`;\n } else this.finishRegistration(job);\n }\n\n async close(): Promise<void> {\n this.closing = true;\n for (const job of this.jobs.values()) if (active(job)) this.cancel(job.view.id);\n await Promise.all([...this.jobs.values()].map((job) => job.done));\n }\n}\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
6
|
"names": ["job"]
|
|
7
7
|
}
|
package/dist/web/assets/{abnfDiagram-WEYOIA3C-CwLX554E.js → abnfDiagram-WEYOIA3C-BkC1EVBk.js}
RENAMED
|
@@ -1 +1 @@
|
|
|
1
|
-
import{m as e,t}from"./mermaid-parser.core-
|
|
1
|
+
import{m as e,t}from"./mermaid-parser.core-C7jTW4sZ.js";import{n}from"./chunk-Y2CYZVJY-DsF7k-Jl.js";import{m as r}from"./src-BXQOmVZv.js";import"./chunk-DSNLSHJZ-BnMtx8Ho.js";import{n as i,r as a,t as o}from"./chunk-2E7WHVMD-TrYs0qV3.js";import{t as s}from"./chunk-JWPE2WC7-DVXcaiue.js";import"./mermaid.core-DcWRGjP5.js";var c=e().RailroadAbnf.parser.LangiumParser,l=n(e=>{let t=e.alternatives.map(u);return t.length===1?t[0]:{type:`choice`,alternatives:t}},`transformAlternation`),u=n(e=>{let t=e.elements.map(f);return t.length===1?t[0]:{type:`sequence`,elements:t}},`transformConcatenation`),d=n(e=>{if(e.includes(`*`)){let[t,n]=e.split(`*`);return{min:t?parseInt(t,10):0,max:n?parseInt(n,10):1/0}}let t=parseInt(e,10);return{min:t,max:t}},`parseRepeat`),f=n(e=>{let t=p(e.primary);if(!e.repeat)return t;let{min:n,max:r}=d(e.repeat);return n===0&&r===1?{type:`optional`,element:t}:{type:`repetition`,element:t,min:n,max:r}},`transformElement`),p=n(e=>{switch(e.$type){case`AbnfStringLiteral`:return{type:`terminal`,value:e.value};case`AbnfNumVal`:return{type:`terminal`,value:e.value};case`AbnfRuleName`:return{type:`nonterminal`,name:e.name};case`AbnfGroup`:return l(e.element);case`AbnfOptionalGroup`:return{type:`optional`,element:l(e.element)};default:throw Error(`Unsupported ABNF primary node: ${e.$type}`)}},`transformPrimary`),m=n(e=>({name:e.name,definition:l(e.definition)}),`transformRule`),h=n(e=>{s(e,o),e.title&&o.setTitle(e.title),e.rules.map(e=>o.addRule(m(e)))},`populateDb`),g={parser:{parse:n(e=>{o.clear(),r.debug(`[ABNF Parser] Starting Langium parse`);let n=c.parse(e);if(n.lexerErrors.length>0||n.parserErrors.length>0)throw new t(n);let i=n.value;r.debug(`[ABNF Parser] Parsed rules:`,i.rules.length),h(i),r.debug(`[ABNF Parser] Parse complete`)},`parse`),parser:{yy:o}},db:o,renderer:a,styles:i};export{g as diagram};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{n as e,t}from"./path-fybaL0A-.js";import{a as n,c as r,d as i,f as a,i as o,l as s,m as c,n as l,o as u,p as d,r as f,u as p}from"./dist-
|
|
1
|
+
import{n as e,t}from"./path-fybaL0A-.js";import{a as n,c as r,d as i,f as a,i as o,l as s,m as c,n as l,o as u,p as d,r as f,u as p}from"./dist-BSWkYZRr.js";function m(e){return e.innerRadius}function h(e){return e.outerRadius}function g(e){return e.startAngle}function _(e){return e.endAngle}function v(e){return e&&e.padAngle}function y(e,t,n,r,i,a,o,s){var c=n-e,l=r-t,u=o-i,d=s-a,f=d*c-u*l;if(!(f*f<1e-12))return f=(u*(t-a)-d*(e-i))/f,[e+f*c,t+f*l]}function b(e,t,n,r,i,a,o){var c=e-n,l=t-r,u=(o?a:-a)/d(c*c+l*l),f=u*l,p=-u*c,m=e+f,h=t+p,g=n+f,_=r+p,v=(m+g)/2,y=(h+_)/2,b=g-m,x=_-h,S=b*b+x*x,C=i-a,w=m*_-g*h,T=(x<0?-1:1)*d(s(0,C*C*S-w*w)),E=(w*x-b*T)/S,D=(-w*b-x*T)/S,O=(w*x+b*T)/S,k=(-w*b+x*T)/S,A=E-v,j=D-y,M=O-v,N=k-y;return A*A+j*j>M*M+N*N&&(E=O,D=k),{cx:E,cy:D,x01:-f,y01:-p,x11:E*(i/C-1),y11:D*(i/C-1)}}function x(){var s=m,x=h,S=e(0),C=null,w=g,T=_,E=v,D=null,O=t(k);function k(){var e,t,m=+s.apply(this,arguments),h=+x.apply(this,arguments),g=w.apply(this,arguments)-r,_=T.apply(this,arguments)-r,v=l(_-g),k=_>g;if(D||=e=O(),h<m&&(t=h,h=m,m=t),!(h>1e-12))D.moveTo(0,0);else if(v>c-1e-12)D.moveTo(h*u(g),h*a(g)),D.arc(0,0,h,g,_,!k),m>1e-12&&(D.moveTo(m*u(_),m*a(_)),D.arc(0,0,m,_,g,k));else{var A=g,j=_,M=g,N=_,P=v,F=v,I=E.apply(this,arguments)/2,L=I>1e-12&&(C?+C.apply(this,arguments):d(m*m+h*h)),R=p(l(h-m)/2,+S.apply(this,arguments)),z=R,B=R,V,H;if(L>1e-12){var U=o(L/m*a(I)),W=o(L/h*a(I));(P-=U*2)>1e-12?(U*=k?1:-1,M+=U,N-=U):(P=0,M=N=(g+_)/2),(F-=W*2)>1e-12?(W*=k?1:-1,A+=W,j-=W):(F=0,A=j=(g+_)/2)}var G=h*u(A),K=h*a(A),q=m*u(N),J=m*a(N);if(R>1e-12){var Y=h*u(j),X=h*a(j),Z=m*u(M),Q=m*a(M),$;if(v<i){if($=y(G,K,Z,Q,Y,X,q,J)){var ee=G-$[0],te=K-$[1],ne=Y-$[0],re=X-$[1],ie=1/a(f((ee*ne+te*re)/(d(ee*ee+te*te)*d(ne*ne+re*re)))/2),ae=d($[0]*$[0]+$[1]*$[1]);z=p(R,(m-ae)/(ie-1)),B=p(R,(h-ae)/(ie+1))}else z=B=0}}F>1e-12?B>1e-12?(V=b(Z,Q,G,K,h,B,k),H=b(Y,X,q,J,h,B,k),D.moveTo(V.cx+V.x01,V.cy+V.y01),B<R?D.arc(V.cx,V.cy,B,n(V.y01,V.x01),n(H.y01,H.x01),!k):(D.arc(V.cx,V.cy,B,n(V.y01,V.x01),n(V.y11,V.x11),!k),D.arc(0,0,h,n(V.cy+V.y11,V.cx+V.x11),n(H.cy+H.y11,H.cx+H.x11),!k),D.arc(H.cx,H.cy,B,n(H.y11,H.x11),n(H.y01,H.x01),!k))):(D.moveTo(G,K),D.arc(0,0,h,A,j,!k)):D.moveTo(G,K),!(m>1e-12)||!(P>1e-12)?D.lineTo(q,J):z>1e-12?(V=b(q,J,Y,X,m,-z,k),H=b(G,K,Z,Q,m,-z,k),D.lineTo(V.cx+V.x01,V.cy+V.y01),z<R?D.arc(V.cx,V.cy,z,n(V.y01,V.x01),n(H.y01,H.x01),!k):(D.arc(V.cx,V.cy,z,n(V.y01,V.x01),n(V.y11,V.x11),!k),D.arc(0,0,m,n(V.cy+V.y11,V.cx+V.x11),n(H.cy+H.y11,H.cx+H.x11),k),D.arc(H.cx,H.cy,z,n(H.y11,H.x11),n(H.y01,H.x01),!k))):D.arc(0,0,m,N,M,k)}if(D.closePath(),e)return D=null,e+``||null}return k.centroid=function(){var e=(+s.apply(this,arguments)+ +x.apply(this,arguments))/2,t=(+w.apply(this,arguments)+ +T.apply(this,arguments))/2-i/2;return[u(t)*e,a(t)*e]},k.innerRadius=function(t){return arguments.length?(s=typeof t==`function`?t:e(+t),k):s},k.outerRadius=function(t){return arguments.length?(x=typeof t==`function`?t:e(+t),k):x},k.cornerRadius=function(t){return arguments.length?(S=typeof t==`function`?t:e(+t),k):S},k.padRadius=function(t){return arguments.length?(C=t==null?null:typeof t==`function`?t:e(+t),k):C},k.startAngle=function(t){return arguments.length?(w=typeof t==`function`?t:e(+t),k):w},k.endAngle=function(t){return arguments.length?(T=typeof t==`function`?t:e(+t),k):T},k.padAngle=function(t){return arguments.length?(E=typeof t==`function`?t:e(+t),k):E},k.context=function(e){return arguments.length?(D=e??null,k):D},k}export{x as t};
|
package/dist/web/assets/{architecture-WOLXFQ4H-y6MnEqN0.js → architecture-WOLXFQ4H-DCsF3gVg.js}
RENAMED
|
@@ -1 +1 @@
|
|
|
1
|
-
import"./chunk-NGNAAXSQ-C2Wy9Xmz.js";export{M as createArchitectureServices}from"./mermaid-parser.core-
|
|
1
|
+
import"./chunk-NGNAAXSQ-C2Wy9Xmz.js";export{M as createArchitectureServices}from"./mermaid-parser.core-C7jTW4sZ.js";
|