cli-claw-kit 0.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +245 -0
- package/config/default-groups.json +1 -0
- package/config/global-agents-md.template.md +37 -0
- package/config/mount-allowlist.json +11 -0
- package/container/Dockerfile +160 -0
- package/container/agent-runner/dist/.tsbuildinfo +1 -0
- package/container/agent-runner/dist/agent-definitions.js +22 -0
- package/container/agent-runner/dist/channel-prefixes.js +16 -0
- package/container/agent-runner/dist/codex-config.js +29 -0
- package/container/agent-runner/dist/image-detector.js +96 -0
- package/container/agent-runner/dist/index.js +2587 -0
- package/container/agent-runner/dist/mcp-tools.js +1076 -0
- package/container/agent-runner/dist/stream-event.types.js +5 -0
- package/container/agent-runner/dist/stream-processor.js +867 -0
- package/container/agent-runner/dist/types.js +6 -0
- package/container/agent-runner/dist/utils.js +115 -0
- package/container/agent-runner/package.json +36 -0
- package/container/agent-runner/prompts/security-rules.md +31 -0
- package/container/agent-runner/src/agent-definitions.ts +27 -0
- package/container/agent-runner/src/channel-prefixes.ts +16 -0
- package/container/agent-runner/src/codex-config.ts +40 -0
- package/container/agent-runner/src/image-detector.ts +116 -0
- package/container/agent-runner/src/index.ts +3107 -0
- package/container/agent-runner/src/mcp-tools.ts +1295 -0
- package/container/agent-runner/src/stream-event.types.ts +10 -0
- package/container/agent-runner/src/stream-processor.ts +932 -0
- package/container/agent-runner/src/types.ts +75 -0
- package/container/agent-runner/src/utils.ts +114 -0
- package/container/agent-runner/tsconfig.json +17 -0
- package/container/build.sh +28 -0
- package/container/entrypoint.sh +64 -0
- package/container/skills/agent-browser/SKILL.md +159 -0
- package/container/skills/install-skill/SKILL.md +64 -0
- package/container/skills/post-test-cleanup/SKILL.md +121 -0
- package/dist/.tsbuildinfo +1 -0
- package/dist/agent-output-parser.js +459 -0
- package/dist/app-root.js +52 -0
- package/dist/assistant-meta-footer.js +1 -0
- package/dist/auth.js +91 -0
- package/dist/billing.js +694 -0
- package/dist/channel-prefixes.js +16 -0
- package/dist/cli.js +86 -0
- package/dist/commands.js +79 -0
- package/dist/config.js +120 -0
- package/dist/container-runner.js +981 -0
- package/dist/daily-summary.js +210 -0
- package/dist/db.js +3683 -0
- package/dist/dingtalk.js +1347 -0
- package/dist/feishu-markdown-style.js +97 -0
- package/dist/feishu-streaming-card.js +1875 -0
- package/dist/feishu.js +1628 -0
- package/dist/file-manager.js +270 -0
- package/dist/group-queue.js +1070 -0
- package/dist/group-runtime.js +35 -0
- package/dist/host-workspace-cwd.js +85 -0
- package/dist/im-channel.js +384 -0
- package/dist/im-command-utils.js +142 -0
- package/dist/im-downloader.js +45 -0
- package/dist/im-manager.js +527 -0
- package/dist/im-utils.js +53 -0
- package/dist/image-detector.js +96 -0
- package/dist/index.js +5828 -0
- package/dist/logger.js +22 -0
- package/dist/mcp-utils.js +66 -0
- package/dist/message-attachments.js +69 -0
- package/dist/message-notifier.js +36 -0
- package/dist/middleware/auth.js +85 -0
- package/dist/mount-security.js +315 -0
- package/dist/permissions.js +67 -0
- package/dist/project-memory.js +6 -0
- package/dist/provider-pool.js +189 -0
- package/dist/qq.js +826 -0
- package/dist/reset-admin.js +42 -0
- package/dist/routes/admin.js +543 -0
- package/dist/routes/agent-definitions.js +241 -0
- package/dist/routes/agents.js +533 -0
- package/dist/routes/auth.js +675 -0
- package/dist/routes/billing.js +490 -0
- package/dist/routes/browse.js +210 -0
- package/dist/routes/bug-report.js +387 -0
- package/dist/routes/config.js +1868 -0
- package/dist/routes/files.js +671 -0
- package/dist/routes/groups.js +1367 -0
- package/dist/routes/mcp-servers.js +320 -0
- package/dist/routes/memory.js +523 -0
- package/dist/routes/monitor.js +307 -0
- package/dist/routes/skills.js +777 -0
- package/dist/routes/tasks.js +509 -0
- package/dist/routes/usage.js +64 -0
- package/dist/routes/workspace-config.js +458 -0
- package/dist/runtime-build.js +112 -0
- package/dist/runtime-command-handler.js +189 -0
- package/dist/runtime-command-registry.js +1 -0
- package/dist/runtime-config.js +1777 -0
- package/dist/runtime-identity.js +52 -0
- package/dist/schemas.js +590 -0
- package/dist/script-runner.js +64 -0
- package/dist/sdk-query.js +82 -0
- package/dist/skill-utils.js +145 -0
- package/dist/sqlite-compat.js +19 -0
- package/dist/stream-event.types.js +5 -0
- package/dist/streaming-runtime-meta.js +29 -0
- package/dist/task-scheduler.js +695 -0
- package/dist/task-utils.js +13 -0
- package/dist/telegram-pairing.js +59 -0
- package/dist/telegram.js +897 -0
- package/dist/terminal-manager.js +307 -0
- package/dist/tool-step-display.js +1 -0
- package/dist/types.js +1 -0
- package/dist/utils.js +85 -0
- package/dist/web-context.js +161 -0
- package/dist/web.js +1377 -0
- package/dist/wechat-crypto.js +182 -0
- package/dist/wechat.js +589 -0
- package/dist/workspace-runtime-reset.js +35 -0
- package/package.json +107 -0
- package/shared/assistant-meta-footer.ts +127 -0
- package/shared/channel-prefixes.ts +16 -0
- package/shared/dist/assistant-meta-footer.d.ts +29 -0
- package/shared/dist/assistant-meta-footer.js +85 -0
- package/shared/dist/channel-prefixes.d.ts +4 -0
- package/shared/dist/channel-prefixes.js +16 -0
- package/shared/dist/image-detector.d.ts +20 -0
- package/shared/dist/image-detector.js +96 -0
- package/shared/dist/runtime-command-registry.d.ts +38 -0
- package/shared/dist/runtime-command-registry.js +185 -0
- package/shared/dist/stream-event.d.ts +65 -0
- package/shared/dist/stream-event.js +8 -0
- package/shared/dist/tool-step-display.d.ts +4 -0
- package/shared/dist/tool-step-display.js +11 -0
- package/shared/image-detector.ts +116 -0
- package/shared/runtime-command-registry.ts +252 -0
- package/shared/stream-event.ts +67 -0
- package/shared/tool-step-display.ts +21 -0
- package/shared/tsconfig.json +24 -0
- package/web/dist/assets/BillingPage-B1wBR_o-.js +52 -0
- package/web/dist/assets/ChatPage-6GBZ9nXN.css +32 -0
- package/web/dist/assets/ChatPage-BOJcXtaj.js +161 -0
- package/web/dist/assets/KaTeX_AMS-Regular-BQhdFMY1.woff2 +0 -0
- package/web/dist/assets/KaTeX_AMS-Regular-DMm9YOAa.woff +0 -0
- package/web/dist/assets/KaTeX_AMS-Regular-DRggAlZN.ttf +0 -0
- package/web/dist/assets/KaTeX_Caligraphic-Bold-ATXxdsX0.ttf +0 -0
- package/web/dist/assets/KaTeX_Caligraphic-Bold-BEiXGLvX.woff +0 -0
- package/web/dist/assets/KaTeX_Caligraphic-Bold-Dq_IR9rO.woff2 +0 -0
- package/web/dist/assets/KaTeX_Caligraphic-Regular-CTRA-rTL.woff +0 -0
- package/web/dist/assets/KaTeX_Caligraphic-Regular-Di6jR-x-.woff2 +0 -0
- package/web/dist/assets/KaTeX_Caligraphic-Regular-wX97UBjC.ttf +0 -0
- package/web/dist/assets/KaTeX_Fraktur-Bold-BdnERNNW.ttf +0 -0
- package/web/dist/assets/KaTeX_Fraktur-Bold-BsDP51OF.woff +0 -0
- package/web/dist/assets/KaTeX_Fraktur-Bold-CL6g_b3V.woff2 +0 -0
- package/web/dist/assets/KaTeX_Fraktur-Regular-CB_wures.ttf +0 -0
- package/web/dist/assets/KaTeX_Fraktur-Regular-CTYiF6lA.woff2 +0 -0
- package/web/dist/assets/KaTeX_Fraktur-Regular-Dxdc4cR9.woff +0 -0
- package/web/dist/assets/KaTeX_Main-Bold-Cx986IdX.woff2 +0 -0
- package/web/dist/assets/KaTeX_Main-Bold-Jm3AIy58.woff +0 -0
- package/web/dist/assets/KaTeX_Main-Bold-waoOVXN0.ttf +0 -0
- package/web/dist/assets/KaTeX_Main-BoldItalic-DxDJ3AOS.woff2 +0 -0
- package/web/dist/assets/KaTeX_Main-BoldItalic-DzxPMmG6.ttf +0 -0
- package/web/dist/assets/KaTeX_Main-BoldItalic-SpSLRI95.woff +0 -0
- package/web/dist/assets/KaTeX_Main-Italic-3WenGoN9.ttf +0 -0
- package/web/dist/assets/KaTeX_Main-Italic-BMLOBm91.woff +0 -0
- package/web/dist/assets/KaTeX_Main-Italic-NWA7e6Wa.woff2 +0 -0
- package/web/dist/assets/KaTeX_Main-Regular-B22Nviop.woff2 +0 -0
- package/web/dist/assets/KaTeX_Main-Regular-Dr94JaBh.woff +0 -0
- package/web/dist/assets/KaTeX_Main-Regular-ypZvNtVU.ttf +0 -0
- package/web/dist/assets/KaTeX_Math-BoldItalic-B3XSjfu4.ttf +0 -0
- package/web/dist/assets/KaTeX_Math-BoldItalic-CZnvNsCZ.woff2 +0 -0
- package/web/dist/assets/KaTeX_Math-BoldItalic-iY-2wyZ7.woff +0 -0
- package/web/dist/assets/KaTeX_Math-Italic-DA0__PXp.woff +0 -0
- package/web/dist/assets/KaTeX_Math-Italic-flOr_0UB.ttf +0 -0
- package/web/dist/assets/KaTeX_Math-Italic-t53AETM-.woff2 +0 -0
- package/web/dist/assets/KaTeX_SansSerif-Bold-CFMepnvq.ttf +0 -0
- package/web/dist/assets/KaTeX_SansSerif-Bold-D1sUS0GD.woff2 +0 -0
- package/web/dist/assets/KaTeX_SansSerif-Bold-DbIhKOiC.woff +0 -0
- package/web/dist/assets/KaTeX_SansSerif-Italic-C3H0VqGB.woff2 +0 -0
- package/web/dist/assets/KaTeX_SansSerif-Italic-DN2j7dab.woff +0 -0
- package/web/dist/assets/KaTeX_SansSerif-Italic-YYjJ1zSn.ttf +0 -0
- package/web/dist/assets/KaTeX_SansSerif-Regular-BNo7hRIc.ttf +0 -0
- package/web/dist/assets/KaTeX_SansSerif-Regular-CS6fqUqJ.woff +0 -0
- package/web/dist/assets/KaTeX_SansSerif-Regular-DDBCnlJ7.woff2 +0 -0
- package/web/dist/assets/KaTeX_Script-Regular-C5JkGWo-.ttf +0 -0
- package/web/dist/assets/KaTeX_Script-Regular-D3wIWfF6.woff2 +0 -0
- package/web/dist/assets/KaTeX_Script-Regular-D5yQViql.woff +0 -0
- package/web/dist/assets/KaTeX_Size1-Regular-C195tn64.woff +0 -0
- package/web/dist/assets/KaTeX_Size1-Regular-Dbsnue_I.ttf +0 -0
- package/web/dist/assets/KaTeX_Size1-Regular-mCD8mA8B.woff2 +0 -0
- package/web/dist/assets/KaTeX_Size2-Regular-B7gKUWhC.ttf +0 -0
- package/web/dist/assets/KaTeX_Size2-Regular-Dy4dx90m.woff2 +0 -0
- package/web/dist/assets/KaTeX_Size2-Regular-oD1tc_U0.woff +0 -0
- package/web/dist/assets/KaTeX_Size3-Regular-CTq5MqoE.woff +0 -0
- package/web/dist/assets/KaTeX_Size3-Regular-DgpXs0kz.ttf +0 -0
- package/web/dist/assets/KaTeX_Size4-Regular-BF-4gkZK.woff +0 -0
- package/web/dist/assets/KaTeX_Size4-Regular-DWFBv043.ttf +0 -0
- package/web/dist/assets/KaTeX_Size4-Regular-Dl5lxZxV.woff2 +0 -0
- package/web/dist/assets/KaTeX_Typewriter-Regular-C0xS9mPB.woff +0 -0
- package/web/dist/assets/KaTeX_Typewriter-Regular-CO6r4hn1.woff2 +0 -0
- package/web/dist/assets/KaTeX_Typewriter-Regular-D3Ib7_Hf.ttf +0 -0
- package/web/dist/assets/SettingsPage-DoY7FoZ_.js +153 -0
- package/web/dist/assets/ShareImageDialog-C1ga8b7l.js +22 -0
- package/web/dist/assets/TasksPage-CRivnNsx.js +14 -0
- package/web/dist/assets/_basePickBy-Bf-bSoS9.js +1 -0
- package/web/dist/assets/_baseUniq-zAOaCuKw.js +1 -0
- package/web/dist/assets/arc-Dm9mVQ9U.js +1 -0
- package/web/dist/assets/architectureDiagram-2XIMDMQ5-BLmzX1wr.js +36 -0
- package/web/dist/assets/band-CquvqAHh.js +1 -0
- package/web/dist/assets/blockDiagram-WCTKOSBZ-B9pcqm3j.js +132 -0
- package/web/dist/assets/c4Diagram-IC4MRINW-Cytx1q3b.js +10 -0
- package/web/dist/assets/channel-BOVj73LR.js +1 -0
- package/web/dist/assets/channel-meta-CQD0Pei-.js +41 -0
- package/web/dist/assets/chunk-4BX2VUAB-0ToDr6RE.js +1 -0
- package/web/dist/assets/chunk-55IACEB6-DQDjnXfS.js +1 -0
- package/web/dist/assets/chunk-FMBD7UC4-Di8ABm6c.js +15 -0
- package/web/dist/assets/chunk-JSJVCQXG-BZQN6rnX.js +1 -0
- package/web/dist/assets/chunk-KX2RTZJC-zBbcpaN_.js +1 -0
- package/web/dist/assets/chunk-NQ4KR5QH-BCrLoU88.js +220 -0
- package/web/dist/assets/chunk-QZHKN3VN-Bqk8juan.js +1 -0
- package/web/dist/assets/chunk-WL4C6EOR-D2YX-MHY.js +189 -0
- package/web/dist/assets/classDiagram-VBA2DB6C-DUUoMyaK.js +1 -0
- package/web/dist/assets/classDiagram-v2-RAHNMMFH-DUUoMyaK.js +1 -0
- package/web/dist/assets/clone-BmaCesfa.js +1 -0
- package/web/dist/assets/cose-bilkent-S5V4N54A-CTsv6qQA.js +1 -0
- package/web/dist/assets/cytoscape.esm-BQaXIfA_.js +331 -0
- package/web/dist/assets/dagre-KLK3FWXG-Ci4Jh9nu.js +4 -0
- package/web/dist/assets/defaultLocale-DX6XiGOO.js +1 -0
- package/web/dist/assets/diagram-E7M64L7V-BFRnfTI2.js +24 -0
- package/web/dist/assets/diagram-IFDJBPK2-B7Zhnp0b.js +43 -0
- package/web/dist/assets/diagram-P4PSJMXO-BVyP7nwq.js +24 -0
- package/web/dist/assets/erDiagram-INFDFZHY-NorKdTOF.js +70 -0
- package/web/dist/assets/error-CGD5mp5f.js +1 -0
- package/web/dist/assets/flowDiagram-PKNHOUZH-Ch97nABF.js +162 -0
- package/web/dist/assets/ganttDiagram-A5KZAMGK-BQ2pLWsy.js +292 -0
- package/web/dist/assets/gitGraphDiagram-K3NZZRJ6-bcvnBsD2.js +65 -0
- package/web/dist/assets/graph-CeAEckur.js +1 -0
- package/web/dist/assets/index-CPnL1_qC.js +768 -0
- package/web/dist/assets/index-DVevCbcO.css +10 -0
- package/web/dist/assets/infoDiagram-LFFYTUFH-CcsrFdj-.js +2 -0
- package/web/dist/assets/init-Dmth1JHB.js +1 -0
- package/web/dist/assets/ishikawaDiagram-PHBUUO56-1upyMfHN.js +70 -0
- package/web/dist/assets/journeyDiagram-4ABVD52K-CKUi-V0c.js +139 -0
- package/web/dist/assets/kanban-definition-K7BYSVSG-DOnQwXfL.js +89 -0
- package/web/dist/assets/layout-BmMMqTnJ.js +1 -0
- package/web/dist/assets/linear-DiaJloY5.js +1 -0
- package/web/dist/assets/mermaid.core-BWLV1B2v.js +254 -0
- package/web/dist/assets/mindmap-definition-YRQLILUH-BeAKHVWP.js +68 -0
- package/web/dist/assets/ordinal-DILIJJjt.js +1 -0
- package/web/dist/assets/pieDiagram-SKSYHLDU-DfiMSfWo.js +30 -0
- package/web/dist/assets/quadrantDiagram-337W2JSQ-wZxZOJxd.js +7 -0
- package/web/dist/assets/requirementDiagram-Z7DCOOCP-BK4HHm17.js +73 -0
- package/web/dist/assets/sankeyDiagram-WA2Y5GQK-BX6t2avX.js +10 -0
- package/web/dist/assets/sequenceDiagram-2WXFIKYE-BPQlkbAa.js +145 -0
- package/web/dist/assets/sheet-rI0FfB1g.js +6 -0
- package/web/dist/assets/sliders-horizontal-CuijWFNK.js +6 -0
- package/web/dist/assets/sparkles-BsMYXJoT.js +11 -0
- package/web/dist/assets/square-0CqMX1Q3.js +11 -0
- package/web/dist/assets/stateDiagram-RAJIS63D-DxkV0Vwd.js +1 -0
- package/web/dist/assets/stateDiagram-v2-FVOUBMTO-qLYoiOPe.js +1 -0
- package/web/dist/assets/step-D51IIHGA.js +1 -0
- package/web/dist/assets/tasks-D8JjBTwx.js +1 -0
- package/web/dist/assets/time-O8zIGux3.js +1 -0
- package/web/dist/assets/timeline-definition-YZTLITO2-kNp1DyFc.js +61 -0
- package/web/dist/assets/treemap-KZPCXAKY-CkrClVhk.js +162 -0
- package/web/dist/assets/utils-KGAn0XTg.js +11 -0
- package/web/dist/assets/vennDiagram-LZ73GAT5-CgdzEZz4.js +34 -0
- package/web/dist/assets/xychartDiagram-JWTSCODW-DfYGPfNB.js +7 -0
- package/web/dist/assets/zap-_hKJYy7J.js +6 -0
- package/web/dist/favicon.svg +332 -0
- package/web/dist/fonts/AlibabaPuHuiTi-3-55-Regular.woff2 +0 -0
- package/web/dist/fonts/AlibabaPuHuiTi-3-65-Medium.woff2 +0 -0
- package/web/dist/fonts/AlibabaPuHuiTi-3-75-SemiBold.woff2 +0 -0
- package/web/dist/fonts/DMSans-latin-ext.woff2 +0 -0
- package/web/dist/fonts/DMSans-latin.woff2 +0 -0
- package/web/dist/icons/README.md +20 -0
- package/web/dist/icons/apple-touch-icon-180.png +0 -0
- package/web/dist/icons/icon-128.png +0 -0
- package/web/dist/icons/icon-144.png +0 -0
- package/web/dist/icons/icon-152.png +0 -0
- package/web/dist/icons/icon-192.png +0 -0
- package/web/dist/icons/icon-192.svg +332 -0
- package/web/dist/icons/icon-384.png +0 -0
- package/web/dist/icons/icon-48.png +0 -0
- package/web/dist/icons/icon-512-maskable.png +0 -0
- package/web/dist/icons/icon-512.png +0 -0
- package/web/dist/icons/icon-512.svg +332 -0
- package/web/dist/icons/icon-72.png +0 -0
- package/web/dist/icons/icon-96.png +0 -0
- package/web/dist/icons/loading-logo.svg +332 -0
- package/web/dist/icons/logo-1024.png +0 -0
- package/web/dist/icons/logo-icon.svg +332 -0
- package/web/dist/icons/logo-text.svg +332 -0
- package/web/dist/index.html +30 -0
- package/web/dist/manifest.webmanifest +1 -0
- package/web/dist/registerSW.js +1 -0
- package/web/dist/sw.js +1 -0
- package/web/dist/workbox-08d6266a.js +1 -0
|
@@ -0,0 +1,270 @@
|
|
|
1
|
+
import path from 'path';
|
|
2
|
+
import fs from 'fs';
|
|
3
|
+
import { DATA_DIR, GROUPS_DIR } from './config.js';
|
|
4
|
+
import { deleteContainerEnvConfig } from './runtime-config.js';
|
|
5
|
+
import { logger } from './logger.js';
|
|
6
|
+
// --- Storage usage cache (5 minute TTL) ---
|
|
7
|
+
const _storageCache = new Map();
|
|
8
|
+
const STORAGE_CACHE_TTL = 5 * 60 * 1000;
|
|
9
|
+
function getStorageCacheKey(folder, rootOverride) {
|
|
10
|
+
return getFileRoot(folder, rootOverride);
|
|
11
|
+
}
|
|
12
|
+
// 常量
|
|
13
|
+
export const MAX_FILE_SIZE = 50 * 1024 * 1024; // 50MB
|
|
14
|
+
const SYSTEM_PATHS = ['logs', 'AGENTS.md', '.claude', 'conversations'];
|
|
15
|
+
/**
|
|
16
|
+
* 获取会话流的文件根目录
|
|
17
|
+
* @param folder 会话流文件夹名(如 main)
|
|
18
|
+
* @param rootOverride 可选的自定义根目录(绝对路径),用于宿主机模式 customCwd
|
|
19
|
+
* @returns 绝对路径
|
|
20
|
+
*/
|
|
21
|
+
export function getFileRoot(folder, rootOverride) {
|
|
22
|
+
if (rootOverride && path.isAbsolute(rootOverride)) {
|
|
23
|
+
return rootOverride;
|
|
24
|
+
}
|
|
25
|
+
return path.join(GROUPS_DIR, folder);
|
|
26
|
+
}
|
|
27
|
+
/**
|
|
28
|
+
* 安全路径解析:防止路径遍历攻击
|
|
29
|
+
* @param folder 会话流文件夹名
|
|
30
|
+
* @param relativePath 用户提供的相对路径
|
|
31
|
+
* @param rootOverride 可选的自定义根目录(绝对路径)
|
|
32
|
+
* @returns 验证后的绝对路径
|
|
33
|
+
* @throws 路径越界时抛出异常
|
|
34
|
+
*/
|
|
35
|
+
export function validateAndResolvePath(folder, relativePath, rootOverride) {
|
|
36
|
+
const root = getFileRoot(folder, rootOverride);
|
|
37
|
+
const normalized = path.normalize(relativePath);
|
|
38
|
+
const resolved = path.resolve(root, normalized);
|
|
39
|
+
// 使用 path.relative 检查是否在根目录内
|
|
40
|
+
const relative = path.relative(root, resolved);
|
|
41
|
+
if (relative.startsWith('..')) {
|
|
42
|
+
throw new Error('Path traversal detected');
|
|
43
|
+
}
|
|
44
|
+
// 解析符号链接:沿路径向上找到最近的已存在祖先,确保其 realpath 仍在根目录内。
|
|
45
|
+
// 这防止了"父级是 symlink、末级还不存在"的绕过场景。
|
|
46
|
+
const realRoot = fs.existsSync(root) ? fs.realpathSync(root) : root;
|
|
47
|
+
let checkPath = resolved;
|
|
48
|
+
while (checkPath !== root && checkPath !== path.dirname(checkPath)) {
|
|
49
|
+
if (fs.existsSync(checkPath)) {
|
|
50
|
+
const realPath = fs.realpathSync(checkPath);
|
|
51
|
+
if (realPath !== realRoot && !realPath.startsWith(realRoot + path.sep)) {
|
|
52
|
+
throw new Error('Symlink traversal detected');
|
|
53
|
+
}
|
|
54
|
+
break;
|
|
55
|
+
}
|
|
56
|
+
checkPath = path.dirname(checkPath);
|
|
57
|
+
}
|
|
58
|
+
return resolved;
|
|
59
|
+
}
|
|
60
|
+
/**
|
|
61
|
+
* 判断路径是否为系统路径(禁止删除)
|
|
62
|
+
* @param relativePath 相对路径
|
|
63
|
+
* @returns 是否为系统路径
|
|
64
|
+
*/
|
|
65
|
+
export function isSystemPath(relativePath) {
|
|
66
|
+
const normalized = path.normalize(relativePath);
|
|
67
|
+
const segments = normalized.split(path.sep).filter(Boolean);
|
|
68
|
+
if (segments.length === 0)
|
|
69
|
+
return false;
|
|
70
|
+
// '.' alone is not a system path (root guard lives in deleteFile)
|
|
71
|
+
if (segments.length === 1 && segments[0] === '.')
|
|
72
|
+
return false;
|
|
73
|
+
// 检查第一段或完全匹配
|
|
74
|
+
const firstSegment = segments[0];
|
|
75
|
+
return SYSTEM_PATHS.some((sysPath) => firstSegment === sysPath || normalized === sysPath);
|
|
76
|
+
}
|
|
77
|
+
/**
|
|
78
|
+
* 列出目录内容
|
|
79
|
+
* @param folder 会话流文件夹名
|
|
80
|
+
* @param subPath 可选的子路径
|
|
81
|
+
* @param rootOverride 可选的自定义根目录(绝对路径)
|
|
82
|
+
* @returns 文件列表和当前路径
|
|
83
|
+
*/
|
|
84
|
+
export function listFiles(folder, subPath, rootOverride) {
|
|
85
|
+
const relativePath = subPath || '';
|
|
86
|
+
const absolutePath = validateAndResolvePath(folder, relativePath, rootOverride);
|
|
87
|
+
// 目录不存在时返回空列表,不自动创建(避免 GET 请求产生写副作用)
|
|
88
|
+
if (!fs.existsSync(absolutePath)) {
|
|
89
|
+
return { files: [], currentPath: relativePath };
|
|
90
|
+
}
|
|
91
|
+
const stat = fs.statSync(absolutePath);
|
|
92
|
+
if (!stat.isDirectory()) {
|
|
93
|
+
throw new Error('Path is not a directory');
|
|
94
|
+
}
|
|
95
|
+
const entries = fs.readdirSync(absolutePath);
|
|
96
|
+
const files = [];
|
|
97
|
+
for (const name of entries) {
|
|
98
|
+
const entryPath = path.join(absolutePath, name);
|
|
99
|
+
const stats = fs.statSync(entryPath);
|
|
100
|
+
const entryRelativePath = path.join(relativePath, name);
|
|
101
|
+
files.push({
|
|
102
|
+
name,
|
|
103
|
+
path: entryRelativePath,
|
|
104
|
+
type: stats.isDirectory() ? 'directory' : 'file',
|
|
105
|
+
size: stats.size,
|
|
106
|
+
modifiedAt: stats.mtime.toISOString(),
|
|
107
|
+
isSystem: isSystemPath(entryRelativePath),
|
|
108
|
+
});
|
|
109
|
+
}
|
|
110
|
+
// 文件夹在前,文件在后,按名称排序
|
|
111
|
+
files.sort((a, b) => {
|
|
112
|
+
if (a.type !== b.type) {
|
|
113
|
+
return a.type === 'directory' ? -1 : 1;
|
|
114
|
+
}
|
|
115
|
+
return a.name.localeCompare(b.name);
|
|
116
|
+
});
|
|
117
|
+
return {
|
|
118
|
+
files,
|
|
119
|
+
currentPath: relativePath,
|
|
120
|
+
};
|
|
121
|
+
}
|
|
122
|
+
/**
|
|
123
|
+
* 删除文件或目录
|
|
124
|
+
* @param folder 会话流文件夹名
|
|
125
|
+
* @param relativePath 相对路径
|
|
126
|
+
* @param rootOverride 可选的自定义根目录(绝对路径)
|
|
127
|
+
* @throws 系统路径或路径不存在时抛出异常
|
|
128
|
+
*/
|
|
129
|
+
export function deleteFile(folder, relativePath, rootOverride) {
|
|
130
|
+
// Reject empty / root-equivalent paths explicitly
|
|
131
|
+
if (!relativePath || relativePath === '.' || relativePath === '/') {
|
|
132
|
+
throw new Error('Cannot delete root directory');
|
|
133
|
+
}
|
|
134
|
+
// 检查是否为系统路径
|
|
135
|
+
if (isSystemPath(relativePath)) {
|
|
136
|
+
throw new Error('Cannot delete system path');
|
|
137
|
+
}
|
|
138
|
+
const absolutePath = validateAndResolvePath(folder, relativePath, rootOverride);
|
|
139
|
+
const root = getFileRoot(folder, rootOverride);
|
|
140
|
+
// Double-check: never delete the group root itself
|
|
141
|
+
if (path.resolve(absolutePath) === path.resolve(root)) {
|
|
142
|
+
throw new Error('Cannot delete root directory');
|
|
143
|
+
}
|
|
144
|
+
if (!fs.existsSync(absolutePath)) {
|
|
145
|
+
throw new Error('File or directory not found');
|
|
146
|
+
}
|
|
147
|
+
// Re-verify realpath right before destructive operation (TOCTOU defense-in-depth)
|
|
148
|
+
const realRoot = fs.realpathSync(root);
|
|
149
|
+
const realPath = fs.realpathSync(absolutePath);
|
|
150
|
+
if (realPath !== realRoot && !realPath.startsWith(realRoot + path.sep)) {
|
|
151
|
+
throw new Error('Symlink traversal detected');
|
|
152
|
+
}
|
|
153
|
+
if (realPath === realRoot) {
|
|
154
|
+
throw new Error('Cannot delete root directory');
|
|
155
|
+
}
|
|
156
|
+
const stats = fs.statSync(absolutePath);
|
|
157
|
+
if (stats.isDirectory()) {
|
|
158
|
+
fs.rmSync(absolutePath, { recursive: true, force: true });
|
|
159
|
+
}
|
|
160
|
+
else {
|
|
161
|
+
fs.unlinkSync(absolutePath);
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
/**
|
|
165
|
+
* 创建目录
|
|
166
|
+
* @param folder 会话流文件夹名
|
|
167
|
+
* @param parentPath 父目录相对路径
|
|
168
|
+
* @param name 新目录名称
|
|
169
|
+
* @param rootOverride 可选的自定义根目录(绝对路径)
|
|
170
|
+
* @throws 目录已存在时抛出异常
|
|
171
|
+
*/
|
|
172
|
+
export function createDirectory(folder, parentPath, name, rootOverride) {
|
|
173
|
+
const targetPath = path.join(parentPath, name);
|
|
174
|
+
// 禁止在系统路径下创建目录
|
|
175
|
+
if (isSystemPath(targetPath)) {
|
|
176
|
+
throw new Error('Cannot create directory in system path');
|
|
177
|
+
}
|
|
178
|
+
const absolutePath = validateAndResolvePath(folder, targetPath, rootOverride);
|
|
179
|
+
if (fs.existsSync(absolutePath)) {
|
|
180
|
+
throw new Error('Directory already exists');
|
|
181
|
+
}
|
|
182
|
+
fs.mkdirSync(absolutePath, { recursive: true });
|
|
183
|
+
// chmod 0o777 确保容器(node/1000)与宿主机用户均可读写
|
|
184
|
+
// 与 container-runner.ts 的 mkdirForContainer() 行为一致
|
|
185
|
+
try {
|
|
186
|
+
fs.chmodSync(absolutePath, 0o777);
|
|
187
|
+
}
|
|
188
|
+
catch {
|
|
189
|
+
/* 忽略只读文件系统 */
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
/**
|
|
193
|
+
* 递归计算目录总大小(字节),带 5 分钟缓存
|
|
194
|
+
*/
|
|
195
|
+
export function getGroupStorageUsage(folder, rootOverride) {
|
|
196
|
+
const cacheKey = getStorageCacheKey(folder, rootOverride);
|
|
197
|
+
const cached = _storageCache.get(cacheKey);
|
|
198
|
+
if (cached && cached.expires > Date.now()) {
|
|
199
|
+
return cached.bytes;
|
|
200
|
+
}
|
|
201
|
+
const root = getFileRoot(folder, rootOverride);
|
|
202
|
+
if (!fs.existsSync(root))
|
|
203
|
+
return 0;
|
|
204
|
+
let totalBytes = 0;
|
|
205
|
+
try {
|
|
206
|
+
totalBytes = calculateDirSize(root);
|
|
207
|
+
}
|
|
208
|
+
catch (err) {
|
|
209
|
+
logger.warn({ err, folder }, 'Failed to calculate storage usage');
|
|
210
|
+
}
|
|
211
|
+
_storageCache.set(cacheKey, {
|
|
212
|
+
bytes: totalBytes,
|
|
213
|
+
expires: Date.now() + STORAGE_CACHE_TTL,
|
|
214
|
+
});
|
|
215
|
+
return totalBytes;
|
|
216
|
+
}
|
|
217
|
+
export function invalidateGroupStorageUsage(folder, rootOverride) {
|
|
218
|
+
_storageCache.delete(getStorageCacheKey(folder, rootOverride));
|
|
219
|
+
}
|
|
220
|
+
const MAX_DIR_DEPTH = 20;
|
|
221
|
+
function calculateDirSize(dirPath, depth = 0) {
|
|
222
|
+
if (depth > MAX_DIR_DEPTH)
|
|
223
|
+
return 0;
|
|
224
|
+
let total = 0;
|
|
225
|
+
let entries;
|
|
226
|
+
try {
|
|
227
|
+
entries = fs.readdirSync(dirPath, { withFileTypes: true });
|
|
228
|
+
}
|
|
229
|
+
catch {
|
|
230
|
+
return 0;
|
|
231
|
+
}
|
|
232
|
+
for (const entry of entries) {
|
|
233
|
+
const fullPath = path.join(dirPath, entry.name);
|
|
234
|
+
if (entry.isSymbolicLink())
|
|
235
|
+
continue; // skip symlinks to avoid loops
|
|
236
|
+
if (entry.isDirectory()) {
|
|
237
|
+
total += calculateDirSize(fullPath, depth + 1);
|
|
238
|
+
}
|
|
239
|
+
else if (entry.isFile()) {
|
|
240
|
+
try {
|
|
241
|
+
total += fs.statSync(fullPath).size;
|
|
242
|
+
}
|
|
243
|
+
catch {
|
|
244
|
+
/* skip unreadable files */
|
|
245
|
+
}
|
|
246
|
+
}
|
|
247
|
+
}
|
|
248
|
+
return total;
|
|
249
|
+
}
|
|
250
|
+
/** Remove all runtime artifacts for a group folder (workspace, sessions, ipc, env, memory). */
|
|
251
|
+
export function removeFlowArtifacts(folder) {
|
|
252
|
+
fs.rmSync(path.join(GROUPS_DIR, folder), { recursive: true, force: true });
|
|
253
|
+
fs.rmSync(path.join(DATA_DIR, 'sessions', folder), {
|
|
254
|
+
recursive: true,
|
|
255
|
+
force: true,
|
|
256
|
+
});
|
|
257
|
+
fs.rmSync(path.join(DATA_DIR, 'ipc', folder), {
|
|
258
|
+
recursive: true,
|
|
259
|
+
force: true,
|
|
260
|
+
});
|
|
261
|
+
fs.rmSync(path.join(DATA_DIR, 'env', folder), {
|
|
262
|
+
recursive: true,
|
|
263
|
+
force: true,
|
|
264
|
+
});
|
|
265
|
+
fs.rmSync(path.join(DATA_DIR, 'memory', folder), {
|
|
266
|
+
recursive: true,
|
|
267
|
+
force: true,
|
|
268
|
+
});
|
|
269
|
+
deleteContainerEnvConfig(folder);
|
|
270
|
+
}
|