@xiaobuyu/nodesign 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 +661 -0
- package/README.md +140 -0
- package/bin/nodesign.js +176 -0
- package/package.json +104 -0
- package/server/api/README.md +45 -0
- package/server/api/_guard.js +75 -0
- package/server/api/admin.js +187 -0
- package/server/api/assets/docx-page.js +103 -0
- package/server/api/assets/helpers.js +22 -0
- package/server/api/assets/notes.js +144 -0
- package/server/api/assets.js +896 -0
- package/server/api/board.js +57 -0
- package/server/api/browse.js +138 -0
- package/server/api/canvas.js +444 -0
- package/server/api/chatai.js +324 -0
- package/server/api/exports/build-standalone.js +980 -0
- package/server/api/exports/cards.js +118 -0
- package/server/api/exports/docx-pdf.js +46 -0
- package/server/api/exports/export-page.js +142 -0
- package/server/api/exports/handoff.js +147 -0
- package/server/api/exports.js +690 -0
- package/server/api/instruction.js +75 -0
- package/server/api/local.js +115 -0
- package/server/api/me.js +116 -0
- package/server/api/memory.js +185 -0
- package/server/api/pending-changes.js +366 -0
- package/server/api/plugins.js +164 -0
- package/server/api/projects.js +220 -0
- package/server/api/publish.js +64 -0
- package/server/api/recent.js +73 -0
- package/server/api/sessions.js +589 -0
- package/server/api/skills.js +77 -0
- package/server/api/standalone-fit.js +168 -0
- package/server/api/turn-compose.js +180 -0
- package/server/api/turn-inflight.js +44 -0
- package/server/api/turn.js +669 -0
- package/server/auth/README.md +17 -0
- package/server/auth/middleware.js +138 -0
- package/server/auth/origin-guard.js +71 -0
- package/server/auth/session.js +108 -0
- package/server/auth/tier.js +113 -0
- package/server/auth/users-store.js +348 -0
- package/server/edit/README.md +29 -0
- package/server/engine/README.md +113 -0
- package/server/engine/agent/agent-shared.js +550 -0
- package/server/engine/agent/auto-mode-default-hard-deny.txt +4 -0
- package/server/engine/agent/auto-mode-rules.js +78 -0
- package/server/engine/agent/context.js +418 -0
- package/server/engine/agent/events.js +464 -0
- package/server/engine/agent/hooks/canvas-validate.js +199 -0
- package/server/engine/agent/hooks/failure.js +120 -0
- package/server/engine/agent/hooks/file-events.js +75 -0
- package/server/engine/agent/hooks/lifecycle.js +233 -0
- package/server/engine/agent/hooks/post-canvas-focus.js +80 -0
- package/server/engine/agent/hooks/post-guidance.js +206 -0
- package/server/engine/agent/hooks/post-subagent-report.js +104 -0
- package/server/engine/agent/hooks/post-trim.js +58 -0
- package/server/engine/agent/hooks/pre-board-neighborhood.js +34 -0
- package/server/engine/agent/hooks/pre-defaults.js +84 -0
- package/server/engine/agent/hooks/pre-injectors.js +277 -0
- package/server/engine/agent/hooks/pre-performance-log-guard.js +78 -0
- package/server/engine/agent/hooks/pre-starter-files.js +61 -0
- package/server/engine/agent/hooks/pre-workspace-scope-guard.js +78 -0
- package/server/engine/agent/hooks/site-validate.js +94 -0
- package/server/engine/agent/hooks/tool-prompts.js +31 -0
- package/server/engine/agent/hooks/turn-state-memory.js +56 -0
- package/server/engine/agent/hooks/user-prompt-submit.js +215 -0
- package/server/engine/agent/hooks.js +354 -0
- package/server/engine/agent/init-contract.js +69 -0
- package/server/engine/agent/isolation.js +159 -0
- package/server/engine/agent/model-context.js +359 -0
- package/server/engine/agent/model-table.js +292 -0
- package/server/engine/agent/plugin-loader.js +266 -0
- package/server/engine/agent/prompts/nodesign-prelude.md +336 -0
- package/server/engine/agent/prompts/tools/ask-user-question-protocol.md +71 -0
- package/server/engine/agent/prompts/tools/direct-edit-protocol.md +98 -0
- package/server/engine/agent/prompts/tools/generate-image-cookbook.gemini-gateway.md +583 -0
- package/server/engine/agent/prompts/tools/generate-image-cookbook.md +483 -0
- package/server/engine/agent/prompts/tools/hybrid-reference.md +100 -0
- package/server/engine/agent/prompts/tools/paint-still-cookbook.md +270 -0
- package/server/engine/agent/prompts/tools/roll-film-cookbook.md +70 -0
- package/server/engine/agent/prompts/tools/site-reference.md +106 -0
- package/server/engine/agent/prompts/tools/tweaks-syntax.md +125 -0
- package/server/engine/agent/prompts/tools/vision-checker-dispatch.md +48 -0
- package/server/engine/agent/session-loop.js +1024 -0
- package/server/engine/agent/session-model.js +164 -0
- package/server/engine/agent/skill.js +204 -0
- package/server/engine/agent/system-prompts.js +86 -0
- package/server/engine/agent/task-events.js +78 -0
- package/server/engine/agents/ds-extractor.md +132 -0
- package/server/engine/agents/explorer.md +190 -0
- package/server/engine/agents/index.js +230 -0
- package/server/engine/agents/schemas/design-system.json +156 -0
- package/server/engine/agents/schemas/tweak-schema.json +113 -0
- package/server/engine/agents/tweak-proposer.md +127 -0
- package/server/engine/agents/vision-checker.md +254 -0
- package/server/engine/browse/capture.js +422 -0
- package/server/engine/browse/card.js +137 -0
- package/server/engine/browse/handover.js +59 -0
- package/server/engine/browse/page-digest.js +119 -0
- package/server/engine/browse/refs.js +196 -0
- package/server/engine/browse/registry.js +307 -0
- package/server/engine/browse/screencast.js +170 -0
- package/server/engine/browse/state.js +121 -0
- package/server/engine/chatai/chat-log.js +79 -0
- package/server/engine/chatai/index.js +193 -0
- package/server/engine/chatai/openai-compat.js +152 -0
- package/server/engine/chatai/orchestrate.js +309 -0
- package/server/engine/chatai/perform.js +40 -0
- package/server/engine/chatai/summarize.js +79 -0
- package/server/engine/mcp/capability-gate.js +52 -0
- package/server/engine/mcp/index.js +339 -0
- package/server/engine/mcp/param-sanitizer.js +142 -0
- package/server/engine/mcp/tools/arrange-on-board.js +124 -0
- package/server/engine/mcp/tools/artifact-session.js +278 -0
- package/server/engine/mcp/tools/browse-computer.js +421 -0
- package/server/engine/mcp/tools/browse-find-batch.js +183 -0
- package/server/engine/mcp/tools/browse-screenshot.js +157 -0
- package/server/engine/mcp/tools/browse.js +480 -0
- package/server/engine/mcp/tools/build-docx.js +101 -0
- package/server/engine/mcp/tools/clear-pending-changes.js +99 -0
- package/server/engine/mcp/tools/create-on-board.js +155 -0
- package/server/engine/mcp/tools/crystallize-skill.js +168 -0
- package/server/engine/mcp/tools/deliver-files.js +0 -0
- package/server/engine/mcp/tools/explain-style.js +237 -0
- package/server/engine/mcp/tools/export-handoff.js +133 -0
- package/server/engine/mcp/tools/expose-tweaks.js +149 -0
- package/server/engine/mcp/tools/generate-image.js +842 -0
- package/server/engine/mcp/tools/get-computed-styles.js +164 -0
- package/server/engine/mcp/tools/get-pending-changes.js +205 -0
- package/server/engine/mcp/tools/h3box-ssh.js +84 -0
- package/server/engine/mcp/tools/helpers/acquire-page.js +82 -0
- package/server/engine/mcp/tools/helpers/motion-lab.js +554 -0
- package/server/engine/mcp/tools/helpers/motion-scroll.js +112 -0
- package/server/engine/mcp/tools/helpers/perception-page.js +215 -0
- package/server/engine/mcp/tools/helpers/reference-download.js +103 -0
- package/server/engine/mcp/tools/helpers/rembg-bridge.py +80 -0
- package/server/engine/mcp/tools/helpers/rembg.js +273 -0
- package/server/engine/mcp/tools/helpers/shot-pipeline.js +280 -0
- package/server/engine/mcp/tools/highlight.js +68 -0
- package/server/engine/mcp/tools/list-pages.js +222 -0
- package/server/engine/mcp/tools/lookup-tags.js +59 -0
- package/server/engine/mcp/tools/navigate-to-page.js +60 -0
- package/server/engine/mcp/tools/organize-board.js +64 -0
- package/server/engine/mcp/tools/paint-still.js +434 -0
- package/server/engine/mcp/tools/pin-to-board.js +130 -0
- package/server/engine/mcp/tools/preview-deck.js +130 -0
- package/server/engine/mcp/tools/profile-scroll.js +305 -0
- package/server/engine/mcp/tools/publish-site.js +102 -0
- package/server/engine/mcp/tools/query-elements.js +195 -0
- package/server/engine/mcp/tools/read-board.js +103 -0
- package/server/engine/mcp/tools/read-document.js +93 -0
- package/server/engine/mcp/tools/read-page.js +264 -0
- package/server/engine/mcp/tools/read-tavern-json.js +143 -0
- package/server/engine/mcp/tools/record-decision.js +140 -0
- package/server/engine/mcp/tools/relate-on-board.js +110 -0
- package/server/engine/mcp/tools/remove-background.js +391 -0
- package/server/engine/mcp/tools/report-issue.js +115 -0
- package/server/engine/mcp/tools/roll-film.js +266 -0
- package/server/engine/mcp/tools/screenshot-docx.js +138 -0
- package/server/engine/mcp/tools/screenshot-url.js +195 -0
- package/server/engine/mcp/tools/screenshot.js +583 -0
- package/server/engine/mcp/tools/tier-gate.js +96 -0
- package/server/engine/mcp/tools/trace-motion.js +215 -0
- package/server/engine/mcp/tools/web-search.js +548 -0
- package/server/engine/motion/inventory.js +349 -0
- package/server/engine/perception/session.js +235 -0
- package/server/engine/plugins/nodesign/.claude-plugin/plugin.json +5 -0
- package/server/engine/plugins/nodesign/skills/deskskill-engine-mini/SKILL.md +192 -0
- package/server/engine/plugins/nodesign/skills/deskskill-engine-mini/canvas.template.html +667 -0
- package/server/engine/plugins/nodesign/skills/deskskill-engine-mini/patterns/hybrid-grid.md +22 -0
- package/server/engine/plugins/nodesign/skills/deskskill-engine-mini/patterns/image-led-cover.md +25 -0
- package/server/engine/plugins/nodesign/skills/deskskill-engine-mini/patterns/portrait.md +23 -0
- package/server/engine/plugins/nodesign/skills/deskskill-engine-mini/patterns/quote-backdrop.md +21 -0
- package/server/engine/plugins/nodesign/skills/deskskill-engine-mini/patterns/section-divider.md +23 -0
- package/server/engine/plugins/nodesign/skills/deskskill-engine-mini/patterns/text-led.md +21 -0
- package/server/engine/plugins/nodesign/skills/docx-craft/SKILL.md +162 -0
- package/server/engine/plugins/nodesign/skills/docx-craft/references/token-schema.md +407 -0
- package/server/engine/plugins/nodesign/skills/docx-craft//346/226/207/346/241/243.template.json +80 -0
- package/server/engine/plugins/nodesign/skills/rp-craft/SKILL.md +228 -0
- package/server/engine/plugins/nodesign/skills/rp-craft/patterns//346/274/224/345/207/272/351/241/265-/346/234/200/345/260/217/345/256/236/347/216/260.html +78 -0
- package/server/engine/plugins/nodesign/skills/rp-craft//346/274/224/345/207/272.template.js +268 -0
- package/server/engine/plugins/nodesign/skills/site-craft/SKILL.md +323 -0
- package/server/engine/plugins/nodesign/skills/site-craft/patterns/build-lane.md +134 -0
- package/server/engine/plugins/nodesign/skills/site-craft/patterns/cutout-collage.md +61 -0
- package/server/engine/plugins/nodesign/skills/site-craft/patterns/mock-app.md +163 -0
- package/server/engine/plugins/nodesign/skills/site-craft/patterns/page-transitions.md +86 -0
- package/server/engine/runs/active-runs.js +698 -0
- package/server/engine/runs/live-turn.js +252 -0
- package/server/engine/runs/store.js +364 -0
- package/server/engine/runs/turn-relay.js +217 -0
- package/server/engine/runtime/workspace.js +164 -0
- package/server/index.js +235 -0
- package/server/lib/artifact-file-path.js +80 -0
- package/server/lib/artifact-target.js +284 -0
- package/server/lib/asset-refs.js +239 -0
- package/server/lib/async-queue.js +104 -0
- package/server/lib/auto-relations.js +130 -0
- package/server/lib/binding-types.js +60 -0
- package/server/lib/board-kind-sizes.js +62 -0
- package/server/lib/board-relations.js +133 -0
- package/server/lib/browse-proxy.js +180 -0
- package/server/lib/canvas-id.js +36 -0
- package/server/lib/cover.js +227 -0
- package/server/lib/danbooru-tags.js +291 -0
- package/server/lib/doc-extract.js +156 -0
- package/server/lib/docx/build-from-source.js +260 -0
- package/server/lib/docx/build.js +490 -0
- package/server/lib/docx/dump-styles.js +287 -0
- package/server/lib/docx/fonts/nodesign-cjk.conf +164 -0
- package/server/lib/docx/merge-runs.js +129 -0
- package/server/lib/docx/numbering.js +218 -0
- package/server/lib/docx/order.js +152 -0
- package/server/lib/docx/rawzip.js +172 -0
- package/server/lib/docx/render.js +92 -0
- package/server/lib/docx/text-lint.js +225 -0
- package/server/lib/docx/tokens.js +335 -0
- package/server/lib/docx/units.js +29 -0
- package/server/lib/docx/xml.js +291 -0
- package/server/lib/docx-pages.js +187 -0
- package/server/lib/export-collect.js +289 -0
- package/server/lib/export-package.js +210 -0
- package/server/lib/html-srcset.js +145 -0
- package/server/lib/image-variant.js +460 -0
- package/server/lib/ingress/forward-openai-chat.js +305 -0
- package/server/lib/ingress/openai-chat.js +491 -0
- package/server/lib/ingress/session-notice.js +66 -0
- package/server/lib/ingress/session-routes.js +68 -0
- package/server/lib/ingress/slot-probe.js +130 -0
- package/server/lib/ingress/upstream-billing.js +52 -0
- package/server/lib/ingress/upstream-fail-streak.js +72 -0
- package/server/lib/ingress/upstream-truncation.js +48 -0
- package/server/lib/issues-store.js +182 -0
- package/server/lib/kinds/deck.js +72 -0
- package/server/lib/kinds/docx.js +283 -0
- package/server/lib/kinds/file-kinds.js +113 -0
- package/server/lib/kinds/index.js +202 -0
- package/server/lib/kinds/site.js +161 -0
- package/server/lib/model-ingress.js +543 -0
- package/server/lib/moderation.js +255 -0
- package/server/lib/notice-store.js +103 -0
- package/server/lib/plugin-install.js +104 -0
- package/server/lib/plugin-validator.js +721 -0
- package/server/lib/publish-store.js +115 -0
- package/server/lib/quick-summary.js +52 -0
- package/server/lib/quota.js +270 -0
- package/server/lib/rate-window.js +29 -0
- package/server/lib/reference-assets.js +92 -0
- package/server/lib/region-shot.js +135 -0
- package/server/lib/safe-path.js +73 -0
- package/server/lib/sdk-session.js +33 -0
- package/server/lib/showcase-store.js +92 -0
- package/server/lib/site-publish.js +465 -0
- package/server/lib/ssrf-guard.js +432 -0
- package/server/lib/task-scan.js +158 -0
- package/server/lib/tavern-json.js +154 -0
- package/server/lib/video-variant.js +237 -0
- package/server/lib/workspace-path.js +33 -0
- package/server/ops/fix-sdk-musl.mjs +40 -0
- package/server/ops/install-macos-fonts.sh +114 -0
- package/server/ops/macos-fonts.conf +336 -0
- package/server/ops/sandbox-shim/bwrap +57 -0
- package/server/projects/assets-summary.js +119 -0
- package/server/projects/auto-name.js +53 -0
- package/server/projects/board-store.js +640 -0
- package/server/projects/move-entry.js +103 -0
- package/server/projects/store.js +270 -0
- package/server/projects/ui-config.js +54 -0
- package/server/projects/workspace-templates.js +53 -0
- package/server/projects/workspace.js +1025 -0
- package/server/runtime/capabilities.js +153 -0
- package/server/runtime/local-config.js +177 -0
- package/server/runtime/local-env.js +88 -0
- package/server/runtime/platform.js +342 -0
- package/server/runtime/profile.js +72 -0
- package/server/services/rembg-launcher.js +313 -0
- package/server/services/rembg-service.py +334 -0
- package/server/shared/README.md +25 -0
- package/server/shared/deck.js +54 -0
- package/server/shared/time.js +49 -0
- package/server/style-pipeline/README.md +53 -0
- package/server/ws/broker.js +36 -0
- package/server/ws/browse-channel.js +177 -0
- package/server/ws/index.js +386 -0
- package/web/README.md +89 -0
- package/web/dist/assets/BrowserWindow-Dc7J7CYc.js +11 -0
- package/web/dist/assets/DeckWindow-4G0xX7P3.js +16 -0
- package/web/dist/assets/DocxWindow-BXQsoPkh.js +16 -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/SiteWindow-D6eS-2gM.js +18 -0
- package/web/dist/assets/again-D8tfgKKO.webp +0 -0
- package/web/dist/assets/caveat-nd-CP6HlsNg.woff2 +0 -0
- package/web/dist/assets/clock-xJhvfYpS.webp +0 -0
- package/web/dist/assets/film-cut-BBKMwH7q.webp +0 -0
- package/web/dist/assets/film-still-BZ_DL9Tt.webp +0 -0
- package/web/dist/assets/index-C0KUf6Wd.css +1 -0
- package/web/dist/assets/index-Cbl3ATvp.js +2125 -0
- package/web/dist/assets/ink-desk-BRbxeDsR.webp +0 -0
- package/web/dist/assets/ink-night-Bz4BT3XH.webp +0 -0
- package/web/dist/assets/ink-portrait-C0DM4V8Q.webp +0 -0
- package/web/dist/assets/longcang-regular-C0uvTuKa.woff2 +0 -0
- package/web/dist/assets/lxgw-nd-bold-DbRfiTgo.woff2 +0 -0
- package/web/dist/assets/lxgw-nd-regular-CpMw9Ehg.woff2 +0 -0
- package/web/dist/assets/pending-edit-apply-DTjo2geW.js +31 -0
- package/web/dist/assets/question-8H4HSqxh.webp +0 -0
- package/web/dist/assets/reject-BaPJqElj.webp +0 -0
- package/web/dist/assets/rotate-cw-LMT3x1Zj.js +11 -0
- package/web/dist/assets/rp-portrait-CbrGpew2.webp +0 -0
- package/web/dist/assets/rp-street-C4jVKjxx.webp +0 -0
- package/web/dist/assets/square-dashed-mouse-pointer-CBoAkfhi.js +11 -0
- package/web/dist/assets/tangle-DIxHVNuT.webp +0 -0
- package/web/dist/assets/thisone-BylZKglV.webp +0 -0
- package/web/dist/assets/thumb-DO1wfK1I.webp +0 -0
- package/web/dist/index.html +18 -0
|
@@ -0,0 +1,342 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* server/runtime/platform.js — 跨平台决策的单一来源
|
|
3
|
+
*
|
|
4
|
+
* 设计原则:所有跟 OS / 工具 / 外部状态相关的决策**只在这里做**。
|
|
5
|
+
* 业务文件(session-loop.js / session-loop.js / sessions.js / workspace.js)
|
|
6
|
+
* 通过 `import { platform } from '../runtime/platform.js'` 读决策结果,
|
|
7
|
+
* 不再自己 `process.env.HOME || os.homedir()` 拼凑。
|
|
8
|
+
*
|
|
9
|
+
* 为什么需要这个文件:
|
|
10
|
+
* 2026-05 Linux 服务器部署踩到 3 类跨平台坑:
|
|
11
|
+
* 1. CLAUDE_CONFIG_DIR per-session vs 全局(SDK 设计假设全局)
|
|
12
|
+
* 2. bwrap sandbox 不解析 symlink(Mac sandbox-exec 没事)
|
|
13
|
+
* 3. WebFetch preflight 假设 OAuth token 存在(gateway key 模式没 OAuth)
|
|
14
|
+
* 每个坑都涉及多文件改动且容易漏。集中后增/改决策只改这一个文件。
|
|
15
|
+
*
|
|
16
|
+
* 启动建议:在 server/index.js 早期调 `platform.dump()` 打日志,
|
|
17
|
+
* 运维一眼能看到当前平台/路径/sandbox/preflight 配置。
|
|
18
|
+
*/
|
|
19
|
+
|
|
20
|
+
import fs from 'node:fs';
|
|
21
|
+
import os from 'node:os';
|
|
22
|
+
import path from 'node:path';
|
|
23
|
+
import { fileURLToPath } from 'node:url';
|
|
24
|
+
import { profile } from './profile.js';
|
|
25
|
+
|
|
26
|
+
const isLinux = process.platform === 'linux';
|
|
27
|
+
|
|
28
|
+
/** 服务端仓库根(server/runtime/platform.js → ../..),.env 就躺在这 */
|
|
29
|
+
const repoRoot = path.resolve(path.dirname(fileURLToPath(import.meta.url)), '../..');
|
|
30
|
+
const isMac = process.platform === 'darwin';
|
|
31
|
+
const isWin = process.platform === 'win32';
|
|
32
|
+
|
|
33
|
+
/**
|
|
34
|
+
* CLAUDE 配置目录(SDK JSONL / settings 的全局根)
|
|
35
|
+
*
|
|
36
|
+
* SDK binary 把 session JSONL 落到 `<configDir>/projects/<encoded-cwd>/<sid>.jsonl`,
|
|
37
|
+
* encoded-cwd = sessionRoot.replace(/[^a-zA-Z0-9]/g, '-')。这个路径是 SDK 内部
|
|
38
|
+
* 硬编码(不能 per-session 自定义)—— 所有 NoDesign session 必须共用一个 configDir,
|
|
39
|
+
* 通过不同 cwd 的编码自然分隔。
|
|
40
|
+
*
|
|
41
|
+
* NODESIGN_CONFIG_DIR 可显式覆盖(容器化 / 多实例共享持久化卷场景)。
|
|
42
|
+
*/
|
|
43
|
+
const claudeConfigDir = process.env.NODESIGN_CONFIG_DIR
|
|
44
|
+
|| path.join(process.env.HOME || os.homedir(), '.claude');
|
|
45
|
+
|
|
46
|
+
/**
|
|
47
|
+
* 本机有没有能跑内置 Claude 行的凭据(本地版 picker 的闸,08-22):
|
|
48
|
+
* - ANTHROPIC_API_KEY 在 env 里(设置页存进 .env 后 setEnvValues 会同步进 process.env,所以每次现查)
|
|
49
|
+
* - 或 `claude login` 过:<claudeConfigDir>/.credentials.json(Linux/Windows 的 OAuth 落盘处),
|
|
50
|
+
* 或 .claude.json 里有 oauthAccount(mac 走钥匙串时凭据不落文件,但这个账号记录在)。
|
|
51
|
+
* 只是「像是配过」的判据,不是验资格 —— 真假由第一次会话说了算;没配时 picker 空着、
|
|
52
|
+
* 设置页指路,比让用户点一个必失败的 Claude 行强。hosted 不走这条(订阅行按档位放)。
|
|
53
|
+
*/
|
|
54
|
+
export function claudeAuthPresent() {
|
|
55
|
+
if (process.env.ANTHROPIC_API_KEY) return 'api_key';
|
|
56
|
+
if (fs.existsSync(path.join(claudeConfigDir, '.credentials.json'))) return 'login';
|
|
57
|
+
for (const f of [path.join(claudeConfigDir, '.claude.json'), path.join(process.env.HOME || os.homedir(), '.claude.json')]) {
|
|
58
|
+
try { if (JSON.parse(fs.readFileSync(f, 'utf8'))?.oauthAccount) return 'login'; } catch { /* 没这个文件或不是 JSON */ }
|
|
59
|
+
}
|
|
60
|
+
return null;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
/**
|
|
64
|
+
* Sandbox 是否开启
|
|
65
|
+
*
|
|
66
|
+
* 2026-08-15 实测重开(此前因 bwrap 不解析 session root 的 symlink 关了三个月;
|
|
67
|
+
* 08-07 工作区扁平化之后软链已经一条都没有,那个阻碍自然消失)。
|
|
68
|
+
* bwrap 0.8.0 在位、user namespace 可用,探针跑通:写只落 cwd、/tmp 与 HOME 都
|
|
69
|
+
* 拒、凭据读不到、外网走代理照常、npm i / vite build / chromium / ffmpeg / codex
|
|
70
|
+
* 全部能跑。
|
|
71
|
+
*
|
|
72
|
+
* ⚠️ 沙盒**只管 Bash**。Read / Grep / Glob / Write 这些结构化工具是 SDK 进程内
|
|
73
|
+
* 实现,不进 bwrap —— 它们靠 protectedPathRules() 那套 permissions.deny 拦。
|
|
74
|
+
* 两套东西缺一不可,改一边记得看另一边。
|
|
75
|
+
*
|
|
76
|
+
* 开关仍然显式:`NODESIGN_SANDBOX=on`。exp 实例已在 ecosystem.exp.config.cjs 里
|
|
77
|
+
* 打开,生产要等 exp 跑一段没事再说 —— 别把它改成默认开,那等于让一次 merge
|
|
78
|
+
* 顺手改了线上进程模型。
|
|
79
|
+
*/
|
|
80
|
+
const sandboxEnabled = process.env.NODESIGN_SANDBOX === 'on';
|
|
81
|
+
|
|
82
|
+
/**
|
|
83
|
+
* 权限模式 & auto 模式分类器
|
|
84
|
+
*
|
|
85
|
+
* 历史上一直是 `bypassPermissions`(没有人能回答权限弹窗,只能全放)。
|
|
86
|
+
* SDK 还有个 `auto`:**用一个模型分类器**判每次工具调用该放行还是该拦,
|
|
87
|
+
* 规则从 settings 的 `autoMode` 段注入它的系统提示(见 agent/auto-mode-rules.js)。
|
|
88
|
+
*
|
|
89
|
+
* 分类器用哪个模型由 `CLAUDE_CODE_AUTO_MODE_MODEL` 定 —— 判"这个动作越不越界"
|
|
90
|
+
* 是个需要判断力的活,用强模型,别在这儿省。
|
|
91
|
+
*
|
|
92
|
+
* 开关:`NODESIGN_PERMISSION_MODE=auto`(exp 已开,生产仍是 bypass)。
|
|
93
|
+
* 分类器判不了、升级到 host 的那些调用走 canUseTool,处理策略见
|
|
94
|
+
* `NODESIGN_AUTO_MODE_ESCALATION`(allow=记账放行/deny=拦)。
|
|
95
|
+
*/
|
|
96
|
+
const permissionModeDefault =
|
|
97
|
+
process.env.NODESIGN_PERMISSION_MODE === 'auto' ? 'auto' : 'bypassPermissions';
|
|
98
|
+
const autoModeEnabled = permissionModeDefault === 'auto';
|
|
99
|
+
const autoModeModel = process.env.NODESIGN_AUTO_MODE_MODEL || 'opus';
|
|
100
|
+
const autoModeEscalation =
|
|
101
|
+
process.env.NODESIGN_AUTO_MODE_ESCALATION === 'deny' ? 'deny' : 'allow';
|
|
102
|
+
|
|
103
|
+
/**
|
|
104
|
+
* WebFetch preflight 是否跳过
|
|
105
|
+
*
|
|
106
|
+
* SDK binary 内置 `nV7()` preflight:调 Anthropic 服务器侧域名分类 API(需 OAuth
|
|
107
|
+
* claude.ai 登录态)。NoDesign 走 API key gateway(NODESIGN_GATEWAY_KEY)模式
|
|
108
|
+
* 不存在 OAuth → preflight 永远 check_failed → DomainCheckFailedError
|
|
109
|
+
* "Unable to verify if domain X is safe to fetch ... blocking claude.ai"。
|
|
110
|
+
*
|
|
111
|
+
* skipWebFetchPreflight 是 SDK 官方为 enterprise / 自托管场景留的开关。
|
|
112
|
+
* 我们走 gateway 模式 → 永远关。
|
|
113
|
+
*/
|
|
114
|
+
const skipWebFetchPreflight = true;
|
|
115
|
+
|
|
116
|
+
/**
|
|
117
|
+
* 凭据/敏感目录黑名单(sandbox.filesystem.denyRead + 结构化工具的 deny 规则共用)
|
|
118
|
+
*
|
|
119
|
+
* 函数形式(不是常量)—— 因为 sandbox.deny 在 spawn SDK 子进程时才计算,
|
|
120
|
+
* 此时如果 HOME env 被改了(比如 ecosystem 显式传 HOME=/home/nodesign),
|
|
121
|
+
* 应该读改后的 homedir。
|
|
122
|
+
*
|
|
123
|
+
* 2026-08-15 补齐:原来只有 .ssh/.aws/.gnupg/gh —— **`.env` 不在里面**,而站主
|
|
124
|
+
* 的中转站 key、CF token、admin 密码、鉴权 secret 全在那一个文件里。这是开沙盒
|
|
125
|
+
* 之前最该补的一条。目录条目按目录整个拦(实测:目录级 denyRead 拦得住 `cat`
|
|
126
|
+
* 目录里的文件,拦不住 `ls` 看文件名 —— 文件名不是秘密,接受)。
|
|
127
|
+
*/
|
|
128
|
+
/** 数据根(给 credentialBlacklist 用;跟 protectedPathRules 收到的那个同源) */
|
|
129
|
+
// ⚠️ 变量名跟 projects/workspace.js 对齐(PROJECTS_DATA_DIR)。08-22 之前这里读的是 PROJECTS_DATA_ROOT,
|
|
130
|
+
// 而 .env.example 与生产 .env 写的都是 PROJECTS_DATA_DIR —— 两个名字一个事实,数据根一旦不是默认值,
|
|
131
|
+
// 凭据黑名单就盖错目录。旧名留作兜底,别再新增读它的地方。
|
|
132
|
+
const PROJECTS_DATA_ROOT_FOR_DENY = path.resolve(
|
|
133
|
+
process.env.PROJECTS_DATA_DIR || process.env.PROJECTS_DATA_ROOT || path.join(repoRoot, 'server', 'projects-data'),
|
|
134
|
+
);
|
|
135
|
+
|
|
136
|
+
function credentialBlacklist() {
|
|
137
|
+
const home = os.homedir();
|
|
138
|
+
return [
|
|
139
|
+
...siblingEnvFiles(), // ⭐ 本仓 + 同机其它仓的 .env(见下)
|
|
140
|
+
path.join(home, '.ssh'),
|
|
141
|
+
path.join(home, '.aws'),
|
|
142
|
+
path.join(home, '.gnupg'),
|
|
143
|
+
path.join(home, '.config', 'gh'), // GitHub CLI token
|
|
144
|
+
path.join(home, '.config', 'gcloud'), // GCP 凭据(这台机器就跑在 GCP 上)
|
|
145
|
+
path.join(home, 'apikey'), // CF token / openai key 的文件版
|
|
146
|
+
path.join(home, '.claude', '.credentials.json'), // 订阅 OAuth
|
|
147
|
+
path.join(home, '.claude.json'),
|
|
148
|
+
path.join(home, '.codex'), // codex CLI 登录态
|
|
149
|
+
path.join(home, '.wrangler'), // CF OAuth
|
|
150
|
+
path.join(home, '.npmrc'),
|
|
151
|
+
path.join(home, '.git-credentials'),
|
|
152
|
+
'/etc/shadow',
|
|
153
|
+
'/etc/passwd',
|
|
154
|
+
'/etc/sudoers',
|
|
155
|
+
// ⛔ 这份清单里**禁止出现中段通配模式**(形如 `<数据根>/*/.browser`)。
|
|
156
|
+
//
|
|
157
|
+
// 2026-08-18 生产事故:那条 .browser 通配把整个生产的 Bash 弄死了 3 小时。
|
|
158
|
+
// 机制(sandbox-runtime 的 sandbox-manager / expandGlobPattern):
|
|
159
|
+
// - 尾部 `/**` 会被剥掉,目录整棵 = 1 条 bwrap 挂载,便宜;
|
|
160
|
+
// - 但**中段的 `*`** 触发展开:拿静态前缀当 baseDir,`readdirSync`
|
|
161
|
+
// **递归走完整棵树**,匹配的每个文件每个目录各成一条 deny 路径拼进
|
|
162
|
+
// 每条 Bash 命令的 argv。chromium 缓存边逛边涨 → 4000+ 条 ≈ 600KB
|
|
163
|
+
// → 超过内核单 arg 上限(MAX_ARG_STRLEN 128KB)→ 所有命令 E2BIG。
|
|
164
|
+
// 开沙盒当天验收是过的 —— 用户逛了 20 分钟站,缓存长过判据线才炸。
|
|
165
|
+
//
|
|
166
|
+
// `.browser`(cookie jar,装着用户在第三方站的真实登录态)现在由两层盖住:
|
|
167
|
+
// - Bash:`dataRoot` 整个在 denyRead(isolation.js),1 条路径,不展开;
|
|
168
|
+
// - Read/Grep/Glob:pre-workspace-scope-guard 按「在数据根里、不在本
|
|
169
|
+
// 工作区里」判,实测 deny;钩子若静默死,init 契约自检会杀会话兜底。
|
|
170
|
+
// 逃生舱:同机上还有别的东西要拦时不用改代码(冒号分隔绝对路径)。
|
|
171
|
+
// exp 用它拦生产的数据根 —— 两个实例同用户同机器,否则互相读得到。
|
|
172
|
+
...(process.env.NODESIGN_DENY_READ_EXTRA || '').split(':').map(s => s.trim()).filter(Boolean),
|
|
173
|
+
].filter((p) => {
|
|
174
|
+
// 上面那条禁令的代码形态:带通配的条目直接丢弃并吼出来,宁可少拦一条
|
|
175
|
+
// 也不能再把整个 Bash 弄死。尾部 /** 不会出现在这份清单里(protectedPathRules
|
|
176
|
+
// 才负责加),所以这里见到任何 glob 字符都算违规。
|
|
177
|
+
if (!/[*?[\]]/.test(p)) return true;
|
|
178
|
+
console.error(`[platform] credentialBlacklist 拒收通配条目(会被沙盒递归展开撑爆 argv,2026-08-18 事故):${p}`);
|
|
179
|
+
return false;
|
|
180
|
+
});
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
/**
|
|
184
|
+
* 本仓 + 同机兄弟仓的 .env
|
|
185
|
+
*
|
|
186
|
+
* 2026-08-15 真跑抓到的洞:只拦 `repoRoot/.env` 的话,exp 会话里
|
|
187
|
+
* `cat ~/projects/Nodesign/.env` 照样出内容 —— 生产的中转站 key、CF token、
|
|
188
|
+
* admin 密码全在那份里。同一台机器上并排放着 Nodesign / Nodesign-canvas /
|
|
189
|
+
* SillyTavern / claude-tavern-bridge,每个都有自己的 .env,一个都不能漏。
|
|
190
|
+
*/
|
|
191
|
+
function siblingEnvFiles() {
|
|
192
|
+
const parent = path.dirname(repoRoot);
|
|
193
|
+
const out = [path.join(repoRoot, '.env')];
|
|
194
|
+
let dirs = [];
|
|
195
|
+
try { dirs = fs.readdirSync(parent, { withFileTypes: true }); } catch { return out; }
|
|
196
|
+
for (const d of dirs) {
|
|
197
|
+
if (!d.isDirectory()) continue;
|
|
198
|
+
const dir = path.join(parent, d.name);
|
|
199
|
+
if (dir === repoRoot) continue;
|
|
200
|
+
let files = [];
|
|
201
|
+
try { files = fs.readdirSync(dir); } catch { continue; }
|
|
202
|
+
for (const f of files) {
|
|
203
|
+
if (f !== '.env' && !f.startsWith('.env.')) continue;
|
|
204
|
+
if (f === '.env.example' || f === '.env.sample') continue; // 示例文件没秘密,留着给人看
|
|
205
|
+
out.push(path.join(dir, f));
|
|
206
|
+
}
|
|
207
|
+
}
|
|
208
|
+
return out;
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
/**
|
|
212
|
+
* 沙盒里要抹掉的环境变量名
|
|
213
|
+
*
|
|
214
|
+
* 实测(2026-08-15):filesystem 那层拦得住 `cat .env`,但**拦不住 `env`** ——
|
|
215
|
+
* 服务端进程自己的 process.env 原样传给了 Bash 子进程,key 就明晃晃躺在那。
|
|
216
|
+
* SDK 的 sandbox.credentials.envVars 支持 deny(沙盒内直接 unset),拿它堵。
|
|
217
|
+
*
|
|
218
|
+
* 匹配靠命名规律而不是写死清单:以后 .env 里加新 key 自动就被盖住,不用记得
|
|
219
|
+
* 回来改这里。ANTHROPIC_* 也一起抹 —— agent 在 Bash 里没有任何理由自己拿它。
|
|
220
|
+
*/
|
|
221
|
+
const SECRET_ENV_RE = /(KEY|TOKEN|SECRET|PASSWORD|PASSWD|CREDENTIAL)S?$/i;
|
|
222
|
+
|
|
223
|
+
function secretEnvVarNames(env = process.env) {
|
|
224
|
+
return Object.keys(env).filter(
|
|
225
|
+
name => SECRET_ENV_RE.test(name) || /^ANTHROPIC_(API_KEY|AUTH_TOKEN)$/.test(name),
|
|
226
|
+
);
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
/**
|
|
230
|
+
* 结构化工具(Read / Grep / Glob / Write / Edit)的 deny 规则
|
|
231
|
+
*
|
|
232
|
+
* ⚠️ 这不是锦上添花,是沙盒的另一半。2026-08-15 探针实测:
|
|
233
|
+
* - sandbox.filesystem.* **只作用于 Bash**;Read/Grep/Glob/Write 在 SDK 进程内
|
|
234
|
+
* 跑,完全不进 bwrap —— 沙盒开着照样 `Read .env`,还能往 cwd 外 Write 文件。
|
|
235
|
+
* - additionalDirectories **不是围栏**(它只是"额外允许",不限制别处);
|
|
236
|
+
* bypassPermissions 下 Read 想读哪读哪。
|
|
237
|
+
* - permissions.deny 规则在 bypassPermissions 下**照样生效** —— 但路径必须写
|
|
238
|
+
* **双斜杠**绝对形式 `Read(//home/x/**)`。写成单斜杠 `Read(/home/x/**)`
|
|
239
|
+
* 不报错、不生效,静默失效(这一条踩过,别再踩)。
|
|
240
|
+
*/
|
|
241
|
+
function protectedPathRules({ dataRoot } = {}) {
|
|
242
|
+
const rules = [];
|
|
243
|
+
for (const p of credentialBlacklist()) {
|
|
244
|
+
// 目录 → `/**`,文件 → 精确匹配;两条都发,多余的那条不会误伤
|
|
245
|
+
for (const target of [`/${p}`, `/${p}/**`]) {
|
|
246
|
+
rules.push(`Read(${target})`, `Write(${target})`, `Edit(${target})`);
|
|
247
|
+
}
|
|
248
|
+
}
|
|
249
|
+
// 仓库自身只读:agent 的活全在工作区里,没理由改服务端源码(改得动就等于
|
|
250
|
+
// 能改自己的 prelude / hook / 闸门 —— 隔离的地板就没了)。
|
|
251
|
+
// ⚠️ 生产的数据根是仓库里的 `server/projects-data` —— 一刀切 deny 整个 repoRoot
|
|
252
|
+
// 会把 agent 自己的工作区一起封死。所以沿通往数据根的那条路**逐层下探**,
|
|
253
|
+
// 每层封兄弟、只放行那一支(只封顶层不够:那样整个 server/ 都得放行 =
|
|
254
|
+
// 平台源码对 agent 可写)。exp 的数据根在仓库外,直接封整个仓库。
|
|
255
|
+
for (const child of repoChildrenOutside(dataRoot)) {
|
|
256
|
+
rules.push(`Write(/${child}/**)`, `Edit(/${child}/**)`);
|
|
257
|
+
}
|
|
258
|
+
return rules;
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
/**
|
|
262
|
+
* repoRoot 下与数据根无关的条目(数据根落在仓库内时用来避让)
|
|
263
|
+
*
|
|
264
|
+
* 数据根在仓库外(exp)→ 直接返回 repoRoot,整个仓库禁写。
|
|
265
|
+
* 数据根在仓库内(生产是 `server/projects-data`)→ **沿着通往数据根的那条路
|
|
266
|
+
* 逐层下探**,每层把兄弟条目都封掉,只放行通往数据根的那一支。
|
|
267
|
+
* ⚠️ 只封顶层是不够的:那样整个 `server/` 都得放行,等于平台源码对 agent 可写
|
|
268
|
+
* (2026-08-15 拿生产真实布局算了一遍才看见)。
|
|
269
|
+
*/
|
|
270
|
+
function repoChildrenOutside(dataRoot) {
|
|
271
|
+
const resolvedData = dataRoot ? path.resolve(dataRoot) : null;
|
|
272
|
+
const dataInsideRepo = resolvedData
|
|
273
|
+
&& (resolvedData === repoRoot || resolvedData.startsWith(`${repoRoot}${path.sep}`));
|
|
274
|
+
if (!dataInsideRepo) return [repoRoot];
|
|
275
|
+
const segments = resolvedData.slice(repoRoot.length + 1).split(path.sep).filter(Boolean);
|
|
276
|
+
const out = [];
|
|
277
|
+
let level = repoRoot;
|
|
278
|
+
for (const keepOut of segments) {
|
|
279
|
+
let names = [];
|
|
280
|
+
try { names = fs.readdirSync(level); } catch { break; }
|
|
281
|
+
for (const name of names) {
|
|
282
|
+
if (name === keepOut || name === '.git') continue;
|
|
283
|
+
out.push(path.join(level, name));
|
|
284
|
+
}
|
|
285
|
+
level = path.join(level, keepOut);
|
|
286
|
+
}
|
|
287
|
+
return out;
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
/**
|
|
291
|
+
* 启动时 dump 平台决策到日志
|
|
292
|
+
*
|
|
293
|
+
* 让运维一眼看到:当前进程跑在什么 OS / HOME 是什么 / claudeConfigDir 指哪 /
|
|
294
|
+
* sandbox 开没开 / preflight 关没关。Linux 服务器排查问题的第一信号。
|
|
295
|
+
*/
|
|
296
|
+
function dump() {
|
|
297
|
+
const info = {
|
|
298
|
+
profile: profile.name,
|
|
299
|
+
...(profile.isLocal ? { dataRoot: profile.dataRoot, listenHost: profile.listenHost, serveWeb: profile.serveWeb } : {}),
|
|
300
|
+
os: process.platform,
|
|
301
|
+
arch: process.arch,
|
|
302
|
+
node: process.version,
|
|
303
|
+
home: os.homedir(),
|
|
304
|
+
claudeConfigDir,
|
|
305
|
+
sandboxEnabled,
|
|
306
|
+
skipWebFetchPreflight,
|
|
307
|
+
// 沙盒真开没开一眼看到,另外报一下两道闸各盖了多少条 —— 归零就是配错了
|
|
308
|
+
protectedPaths: credentialBlacklist().length,
|
|
309
|
+
scrubbedEnvVars: secretEnvVarNames().length,
|
|
310
|
+
permissionMode: permissionModeDefault,
|
|
311
|
+
...(autoModeEnabled ? { autoModeModel, autoModeEscalation } : {}),
|
|
312
|
+
};
|
|
313
|
+
console.log('[platform]', JSON.stringify(info, null, 2));
|
|
314
|
+
return info;
|
|
315
|
+
}
|
|
316
|
+
|
|
317
|
+
export const platform = {
|
|
318
|
+
// 部署形态(runtime/profile.js):hosted | local。消费方一律从这里读,不直接 import profile.js
|
|
319
|
+
profile: profile.name,
|
|
320
|
+
isLocal: profile.isLocal,
|
|
321
|
+
dataRoot: profile.dataRoot,
|
|
322
|
+
cacheRoot: profile.cacheRoot,
|
|
323
|
+
listenHost: profile.listenHost,
|
|
324
|
+
serveWeb: profile.serveWeb,
|
|
325
|
+
webDistDir: profile.webDistDir,
|
|
326
|
+
isLinux,
|
|
327
|
+
isMac,
|
|
328
|
+
isWin,
|
|
329
|
+
repoRoot,
|
|
330
|
+
claudeConfigDir,
|
|
331
|
+
claudeAuthPresent,
|
|
332
|
+
sandboxEnabled,
|
|
333
|
+
permissionModeDefault,
|
|
334
|
+
autoModeEnabled,
|
|
335
|
+
autoModeModel,
|
|
336
|
+
autoModeEscalation,
|
|
337
|
+
skipWebFetchPreflight,
|
|
338
|
+
credentialBlacklist,
|
|
339
|
+
secretEnvVarNames,
|
|
340
|
+
protectedPathRules,
|
|
341
|
+
dump,
|
|
342
|
+
};
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* server/runtime/profile.js — 部署形态(profile)的单一来源。**必须是 server/index.js 的第一个 import**。
|
|
3
|
+
*
|
|
4
|
+
* 两种形态(2026-08-22 用户拍板):
|
|
5
|
+
* - hosted:线上多用户站(登录墙 / 档位 / 额度 / 外审 / 邀请码 / 管理台)。不设 NODESIGN_PROFILE 就是它,
|
|
6
|
+
* 生产 .env 一个字不用改。
|
|
7
|
+
* - local :本地分发版(npx 起在用户自己机器上)。**默认单租户**:登录墙钉死关闭、请求者恒为本机 admin
|
|
8
|
+
* (auth/users-store.js LOCAL_OWNER)、SaaS 那套界面由前端按 /api/auth/status 的 profile 藏起来。
|
|
9
|
+
* 代码不删,只是藏 —— 同一份代码两种形态,靠这里一个开关分岔。
|
|
10
|
+
*
|
|
11
|
+
* 为什么要抢在所有 import 之前跑:DB_PATH / PROJECTS_DATA_DIR / WORKSPACE_DIR 这些都是各模块**加载时**从
|
|
12
|
+
* process.env 读一次就冻结的(store.js / workspace.js / runtime/workspace.js)。本地版要把它们整体挪到
|
|
13
|
+
* 用户数据目录(默认 ~/.nodesign),唯一不改那些模块的办法就是在它们加载前把 env 的默认值填好。
|
|
14
|
+
* ESM 按 import 顺序深度优先求值,所以 index.js 第一行 import 本文件即可保证顺序。
|
|
15
|
+
*
|
|
16
|
+
* 本地版的 .env 住在数据目录(<dataRoot>/.env),不住仓库根:npx 装出来的包目录不是用户该碰的地方。
|
|
17
|
+
* 已在进程 env 里的值不被文件覆盖(跟 node --env-file 同语义),命令行/bin 传的参数优先。
|
|
18
|
+
*/
|
|
19
|
+
|
|
20
|
+
import fs from 'node:fs';
|
|
21
|
+
import os from 'node:os';
|
|
22
|
+
import path from 'node:path';
|
|
23
|
+
import { fileURLToPath } from 'node:url';
|
|
24
|
+
|
|
25
|
+
const repoRoot = path.resolve(path.dirname(fileURLToPath(import.meta.url)), '../..');
|
|
26
|
+
|
|
27
|
+
const PROFILES = Object.freeze(['hosted', 'local']);
|
|
28
|
+
const raw = process.env.NODESIGN_PROFILE || 'hosted';
|
|
29
|
+
if (!PROFILES.includes(raw)) {
|
|
30
|
+
// 拼错不许静默落成 hosted:local 拼成 locl 会把一台笔记本的 4001 端口开到 0.0.0.0 且没有登录墙
|
|
31
|
+
throw new Error(`NODESIGN_PROFILE=${raw} 不认识,只能是 ${PROFILES.join(' | ')}`);
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
export const profileName = raw;
|
|
35
|
+
export const isLocal = raw === 'local';
|
|
36
|
+
|
|
37
|
+
/** 本地版数据根;hosted 下不用(各路径仍按老规矩各自取 env / 仓库相对路径) */
|
|
38
|
+
export const dataRoot = isLocal
|
|
39
|
+
? path.resolve(process.env.NODESIGN_DATA_DIR || path.join(os.homedir(), '.nodesign'))
|
|
40
|
+
: null;
|
|
41
|
+
|
|
42
|
+
function setDefault(name, value) {
|
|
43
|
+
if (process.env[name] === undefined || process.env[name] === '') process.env[name] = value;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
if (isLocal) {
|
|
47
|
+
fs.mkdirSync(dataRoot, { recursive: true });
|
|
48
|
+
// 用户配置(钥匙等):<dataRoot>/.env。不存在就算了,存在但格式坏要炸(别静默跑成没钥匙)
|
|
49
|
+
const envFile = path.join(dataRoot, '.env');
|
|
50
|
+
if (fs.existsSync(envFile)) process.loadEnvFile(envFile);
|
|
51
|
+
setDefault('DB_PATH', path.join(dataRoot, 'nodesign.db'));
|
|
52
|
+
setDefault('PROJECTS_DATA_DIR', path.join(dataRoot, 'projects'));
|
|
53
|
+
setDefault('WORKSPACE_DIR', path.join(dataRoot, 'runs'));
|
|
54
|
+
setDefault('ARTIFACT_DIR', path.join(dataRoot, 'runs'));
|
|
55
|
+
setDefault('NODESIGN_CACHE_DIR', path.join(dataRoot, 'cache'));
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
/** 服务端缓存根(封面 / 图片变体 / 视频变体 / docx 页图)。hosted = 仓库 server/.cache(与 pm2 cwd=仓库根时的老值相同) */
|
|
59
|
+
export const cacheRoot = process.env.NODESIGN_CACHE_DIR
|
|
60
|
+
? path.resolve(process.env.NODESIGN_CACHE_DIR)
|
|
61
|
+
: path.join(repoRoot, 'server', '.cache');
|
|
62
|
+
|
|
63
|
+
/** 监听地址。本地版只绑环回(没有登录墙,绝不能上 0.0.0.0);hosted 不传 = Node 默认全地址,nginx 在前面 */
|
|
64
|
+
export const listenHost = process.env.NODESIGN_HOST || (isLocal ? '127.0.0.1' : undefined);
|
|
65
|
+
|
|
66
|
+
/** 是否由 Node 直接托管前端构建产物(hosted 由 nginx 发 dist,这里关;本地版没有 nginx,开) */
|
|
67
|
+
export const serveWeb = process.env.NODESIGN_SERVE_WEB
|
|
68
|
+
? process.env.NODESIGN_SERVE_WEB === '1'
|
|
69
|
+
: isLocal;
|
|
70
|
+
export const webDistDir = path.join(repoRoot, 'web', 'dist');
|
|
71
|
+
|
|
72
|
+
export const profile = Object.freeze({ name: profileName, isLocal, dataRoot, cacheRoot, listenHost, serveWeb, webDistDir });
|