@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,135 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* lib/region-shot.js —— 把用户在预览上圈的那一块截下来。
|
|
3
|
+
*
|
|
4
|
+
* 为什么在服务端截而不是前端:产物是 iframe 里的**真实网页**,前端要把它变成
|
|
5
|
+
* 位图只能靠 html2canvas 那类重写渲染器的库 —— 那玩意儿在有 3D、滤镜、
|
|
6
|
+
* 自定义字体、伪元素的页面上出来的东西跟屏幕上不是一回事,而这张图恰恰是要
|
|
7
|
+
* 给 agent 当"眼见为实"的证据用的。chromium 真跑一遍才是它自己的样子,
|
|
8
|
+
* 而且这台机器上封面和 screenshot_canvas 早就是这么干的(lib/cover.js)。
|
|
9
|
+
*
|
|
10
|
+
* 取景:以圈为中心带一圈余量。只截圈内的话 agent 拿到一块没有上下文的碎片,
|
|
11
|
+
* 分不清"这是页面哪儿";余量给到它周围一点参照。
|
|
12
|
+
*/
|
|
13
|
+
|
|
14
|
+
import path from 'node:path';
|
|
15
|
+
import { openArtifactPage, launchPerceptionBrowser, degradedNote } from '../engine/mcp/tools/helpers/perception-page.js';
|
|
16
|
+
import fs from 'node:fs/promises';
|
|
17
|
+
|
|
18
|
+
/** 圈外多留这么多像素当上下文 */
|
|
19
|
+
export const PADDING = 32;
|
|
20
|
+
|
|
21
|
+
/** 出图长边上限,跟 screenshot 工具同口径(API 侧本来就会缩到这个规格) */
|
|
22
|
+
export const OUT_LONG_EDGE = 1200;
|
|
23
|
+
|
|
24
|
+
/** 截图串行:一台 1 核机器上同时开两个 chromium 会双双卡住 */
|
|
25
|
+
let chain = Promise.resolve();
|
|
26
|
+
function serialize(fn) {
|
|
27
|
+
const next = chain.then(fn, fn);
|
|
28
|
+
chain = next.catch(() => {});
|
|
29
|
+
return next;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* @param {object} p
|
|
34
|
+
* @param {string} p.absPath 产物页面的绝对路径
|
|
35
|
+
* @param {{x,y,w,h}} p.region 页面坐标(CSS px,含滚动量)
|
|
36
|
+
* @param {{width,height}} p.viewport 用户当时的取景宽高
|
|
37
|
+
* @param {string} [p.projectId] 有它就走 http(与用户预览同源)
|
|
38
|
+
* @param {string} [p.workspaceRoot] 同上,两个都给才走 http
|
|
39
|
+
* @returns {Promise<{ buffer: Buffer, clip: {x,y,width,height}, degraded: string|null }>}
|
|
40
|
+
*/
|
|
41
|
+
export async function renderRegionShot({ absPath, region, viewport, projectId, workspaceRoot }) {
|
|
42
|
+
return serialize(async () => {
|
|
43
|
+
let browser;
|
|
44
|
+
try {
|
|
45
|
+
browser = await launchPerceptionBrowser();
|
|
46
|
+
// 圈选截的是**用户圈的那一块**,所以更要跟用户看到的同源:file:// 下
|
|
47
|
+
// fetch 回来的内容不会出现在图里,agent 会以为用户圈了一块空白。
|
|
48
|
+
const opened = await openArtifactPage(browser, {
|
|
49
|
+
projectId, workspaceRoot, absPath, viewport, deviceScaleFactor: 1, timeout: 20_000,
|
|
50
|
+
});
|
|
51
|
+
const ctx = opened.context;
|
|
52
|
+
const page = opened.page;
|
|
53
|
+
await opened.goto();
|
|
54
|
+
// 字体加载完再截(CJK 子集是 lazy 的,不显式 load 会截到 fallback 字形)
|
|
55
|
+
await page.evaluate(async () => {
|
|
56
|
+
document.body.offsetHeight;
|
|
57
|
+
await Promise.all(Array.from(document.fonts).map(f => f.load().catch(() => {})));
|
|
58
|
+
await document.fonts.ready;
|
|
59
|
+
await new Promise(r => requestAnimationFrame(() => requestAnimationFrame(r)));
|
|
60
|
+
}).catch(() => { /* 页面自己抛错也照截 */ });
|
|
61
|
+
// deck 的 standalone-fit 会在真实 viewport 下再缩一次,抹掉(同 cover.js)
|
|
62
|
+
await page.addStyleTag({ content: `
|
|
63
|
+
body { margin: 0 !important; padding: 0 !important; }
|
|
64
|
+
body.__nd-fit-active > .__nd-deck-wrap > .__nd-page-frame {
|
|
65
|
+
width: ${viewport.width}px !important; height: ${viewport.height}px !important;
|
|
66
|
+
display: block !important; overflow: visible !important;
|
|
67
|
+
}
|
|
68
|
+
body.__nd-fit-active section[data-page] { transform: none !important; }
|
|
69
|
+
` }).catch(() => {});
|
|
70
|
+
await page.waitForTimeout(500);
|
|
71
|
+
|
|
72
|
+
const want = {
|
|
73
|
+
x: Math.max(0, region.x - PADDING),
|
|
74
|
+
y: Math.max(0, region.y - PADDING),
|
|
75
|
+
w: region.w + PADDING * 2,
|
|
76
|
+
h: region.h + PADDING * 2,
|
|
77
|
+
};
|
|
78
|
+
|
|
79
|
+
// 滚到目标位置再按视口坐标裁。**滚动会被文档末尾夹住**,所以要把真实
|
|
80
|
+
// 落点读回来算裁剪框,不然页尾附近截出来的是错位的一块。
|
|
81
|
+
const scroll = await page.evaluate(([x, y]) => {
|
|
82
|
+
window.scrollTo(x, y);
|
|
83
|
+
const e = document.scrollingElement || document.documentElement;
|
|
84
|
+
return [e.scrollLeft, e.scrollTop];
|
|
85
|
+
}, [want.x, want.y]);
|
|
86
|
+
|
|
87
|
+
const clip = {
|
|
88
|
+
x: Math.max(0, want.x - scroll[0]),
|
|
89
|
+
y: Math.max(0, want.y - scroll[1]),
|
|
90
|
+
width: Math.min(want.w, viewport.width),
|
|
91
|
+
height: Math.min(want.h, viewport.height),
|
|
92
|
+
};
|
|
93
|
+
clip.width = Math.max(1, Math.min(clip.width, viewport.width - clip.x));
|
|
94
|
+
clip.height = Math.max(1, Math.min(clip.height, viewport.height - clip.y));
|
|
95
|
+
|
|
96
|
+
// ⚠️ 滚完要**等一拍**再截。上面那个 500ms 花在滚动**之前**,滚完立刻截 →
|
|
97
|
+
// 视口里新进来的懒加载图(`loading=lazy` / IntersectionObserver)还没解码,
|
|
98
|
+
// 图里是空的。而 `get_pending_changes` 把这张图当作"用户所见的 ground truth"
|
|
99
|
+
// 交给 agent —— 空白处会被当成"用户圈了一块空白"。
|
|
100
|
+
await page.evaluate(async () => {
|
|
101
|
+
// 给 IO 回调一次机会,再等两帧确保解码后的图上了屏
|
|
102
|
+
await new Promise(r => setTimeout(r, 250));
|
|
103
|
+
await Promise.all([...document.images].filter(i => !i.complete)
|
|
104
|
+
.map(i => new Promise((res) => { i.addEventListener('load', res, { once: true });
|
|
105
|
+
i.addEventListener('error', res, { once: true }); setTimeout(res, 1200); })));
|
|
106
|
+
await new Promise(r => requestAnimationFrame(() => requestAnimationFrame(r)));
|
|
107
|
+
}).catch(() => { /* 页面自己抛错也照截 */ });
|
|
108
|
+
|
|
109
|
+
const png = await page.screenshot({ type: 'png', clip });
|
|
110
|
+
await ctx.close();
|
|
111
|
+
|
|
112
|
+
const { default: sharp } = await import('sharp');
|
|
113
|
+
const buffer = await sharp(png)
|
|
114
|
+
.resize({ width: OUT_LONG_EDGE, height: OUT_LONG_EDGE, fit: 'inside', withoutEnlargement: true })
|
|
115
|
+
.webp({ quality: 82 })
|
|
116
|
+
.toBuffer();
|
|
117
|
+
// note 非空 = 没走成 http。圈选截图尤其在意这个:file:// 下 fetch 回来的
|
|
118
|
+
// 内容不会出现在图里,而这张图是被当"ground truth"用的
|
|
119
|
+
return { buffer, clip, degraded: degradedNote(opened) };
|
|
120
|
+
} finally {
|
|
121
|
+
await browser?.close().catch(() => {});
|
|
122
|
+
}
|
|
123
|
+
});
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
/** 圈选截图在会话工作区里的落点(agent 按这个相对路径找得到) */
|
|
127
|
+
export const SHOT_DIR = 'region-shots';
|
|
128
|
+
|
|
129
|
+
export async function saveRegionShot(sessionRoot, id, buffer) {
|
|
130
|
+
const dir = path.join(sessionRoot, SHOT_DIR);
|
|
131
|
+
await fs.mkdir(dir, { recursive: true });
|
|
132
|
+
const rel = `${SHOT_DIR}/${id}.webp`;
|
|
133
|
+
await fs.writeFile(path.join(sessionRoot, rel), buffer);
|
|
134
|
+
return rel;
|
|
135
|
+
}
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* lib/safe-path.js — 工作区内路径的越界检查。
|
|
3
|
+
*
|
|
4
|
+
* ⚠️ **词法检查不够。** `path.resolve` + `path.relative` 挡得住 `../..`,
|
|
5
|
+
* 挡不住工作区里一个**指向外面的软链** —— 那条路径逐字看都在工作区内,
|
|
6
|
+
* 解引用之后在 `/etc` 或者 `.env` 上。
|
|
7
|
+
*
|
|
8
|
+
* 这个洞在这个仓库里已经出现过两次形状相同的实例(导出收集器、docx 页图路由),
|
|
9
|
+
* 第二次是照着第一次的**调用形状**抄的、没抄它的**正确性**。所以判据收成一份:
|
|
10
|
+
* 谁要在工作区里按用户给的相对路径开文件,就 import 这里,别再各写一遍。
|
|
11
|
+
*
|
|
12
|
+
* 读和写的判据不一样,分两个函数:
|
|
13
|
+
* - 读:目标必须存在,realpath 之后仍在工作区内
|
|
14
|
+
* - 写:目标可能还不存在(正要创建),所以查**父目录**的 realpath;
|
|
15
|
+
* 另外目标自己若已经是软链,一律拒 —— 顺着它写等于把别人的文件覆盖掉
|
|
16
|
+
*/
|
|
17
|
+
|
|
18
|
+
import path from 'node:path';
|
|
19
|
+
import { promises as fs } from 'node:fs';
|
|
20
|
+
|
|
21
|
+
/** 词法层:拼出绝对路径并确认字面上没跑出去。跑出去返回 null */
|
|
22
|
+
function lexical(rootAbs, rel) {
|
|
23
|
+
const abs = path.resolve(rootAbs, String(rel || ''));
|
|
24
|
+
const within = path.relative(rootAbs, abs);
|
|
25
|
+
if (within.startsWith('..') || path.isAbsolute(within)) return null;
|
|
26
|
+
return abs;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* 读用:解析一个工作区内的相对路径。
|
|
31
|
+
* @returns {Promise<string|null>} 绝对路径;越界(含软链穿透)返回 null
|
|
32
|
+
* 目标不存在**不算越界** —— 让调用方自己去 stat 报 404,这里只管边界
|
|
33
|
+
*/
|
|
34
|
+
export async function safeResolveRead(workspaceRoot, rel) {
|
|
35
|
+
const abs = lexical(path.resolve(workspaceRoot), rel);
|
|
36
|
+
if (!abs) return null;
|
|
37
|
+
try {
|
|
38
|
+
const realRoot = await fs.realpath(workspaceRoot);
|
|
39
|
+
const real = await fs.realpath(abs);
|
|
40
|
+
const rw = path.relative(realRoot, real);
|
|
41
|
+
if (rw.startsWith('..') || path.isAbsolute(rw)) return null;
|
|
42
|
+
} catch (err) {
|
|
43
|
+
// ENOENT:文件还没写出来,边界上没问题,留给调用方 stat
|
|
44
|
+
if (err?.code !== 'ENOENT') return null;
|
|
45
|
+
}
|
|
46
|
+
return abs;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
/**
|
|
50
|
+
* 写用:解析一个工作区内的写入目标。
|
|
51
|
+
* @returns {Promise<string|null>} 绝对路径;越界 / 目标是软链 返回 null
|
|
52
|
+
*/
|
|
53
|
+
export async function safeResolveWrite(workspaceRoot, rel) {
|
|
54
|
+
const abs = lexical(path.resolve(workspaceRoot), rel);
|
|
55
|
+
if (!abs) return null;
|
|
56
|
+
try {
|
|
57
|
+
const realRoot = await fs.realpath(workspaceRoot);
|
|
58
|
+
// 目标本身若已存在且是软链 → 拒。顺着它写会覆盖软链指向的那个文件,
|
|
59
|
+
// 而那个文件可以在工作区外(lstat 不解引用,这是判据的关键)
|
|
60
|
+
try {
|
|
61
|
+
if ((await fs.lstat(abs)).isSymbolicLink()) return null;
|
|
62
|
+
} catch (err) {
|
|
63
|
+
if (err?.code !== 'ENOENT') return null; // 目标还不存在是正常的
|
|
64
|
+
}
|
|
65
|
+
// 父目录必须真的在工作区内(父目录是软链的话,新建的文件会落到外面)
|
|
66
|
+
const realParent = await fs.realpath(path.dirname(abs));
|
|
67
|
+
const rw = path.relative(realRoot, realParent);
|
|
68
|
+
if (rw.startsWith('..') || path.isAbsolute(rw)) return null;
|
|
69
|
+
} catch {
|
|
70
|
+
return null; // 父目录都解析不了,不给写
|
|
71
|
+
}
|
|
72
|
+
return abs;
|
|
73
|
+
}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* server/lib/sdk-session.js — SDK session API 调用 helper
|
|
3
|
+
*
|
|
4
|
+
* SDK 的 listSessions / getSessionMessages / forkSession 等同步 API 读
|
|
5
|
+
* `process.env.CLAUDE_CONFIG_DIR` 决定从哪找 JSONL(probe 验证过:probe-listsessions.mjs)。
|
|
6
|
+
* 我们 S1 把 CLAUDE_CONFIG_DIR per-project 设到 <workspace>/.claude/,
|
|
7
|
+
* 但那是 query() spawn binary 时通过 env option 传的——server 进程自己的
|
|
8
|
+
* process.env.CLAUDE_CONFIG_DIR 默认是空。
|
|
9
|
+
*
|
|
10
|
+
* 多 project 并发调 SDK session API 时 process.env mutation 会互相覆盖,
|
|
11
|
+
* 所以串行化(按单一 key 'sdk-config' mutex)。读操作很快(<10ms 级别),
|
|
12
|
+
* 串行不影响并发性能。
|
|
13
|
+
*
|
|
14
|
+
* 用法:
|
|
15
|
+
* const sessions = await withConfigDir(wsClaudeDir, () =>
|
|
16
|
+
* listSessions({ dir: wsRoot })
|
|
17
|
+
* );
|
|
18
|
+
*/
|
|
19
|
+
|
|
20
|
+
import { mutex } from 'async-mutex-lite';
|
|
21
|
+
|
|
22
|
+
export async function withConfigDir(configDir, fn) {
|
|
23
|
+
return mutex('sdk-config', async () => {
|
|
24
|
+
const prev = process.env.CLAUDE_CONFIG_DIR;
|
|
25
|
+
try {
|
|
26
|
+
process.env.CLAUDE_CONFIG_DIR = configDir;
|
|
27
|
+
return await fn();
|
|
28
|
+
} finally {
|
|
29
|
+
if (prev === undefined) delete process.env.CLAUDE_CONFIG_DIR;
|
|
30
|
+
else process.env.CLAUDE_CONFIG_DIR = prev;
|
|
31
|
+
}
|
|
32
|
+
});
|
|
33
|
+
}
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* server/lib/showcase-store.js — 个人作品橱窗(2026-07-30)
|
|
3
|
+
*
|
|
4
|
+
* 一条 showcase = 一件做完的作品 + 它沉淀出来的 skill。
|
|
5
|
+
* 跟"模板市场"的区别:模板是别人替你预设风格,橱窗是**你自己探索出来的结论**被
|
|
6
|
+
* 固化下来——第一次仍然是从问题长出骨架,第二次开始你有资格复用自己的结论。
|
|
7
|
+
*
|
|
8
|
+
* 归属:user_id 必填,只有本人(和 admin)看得到自己的橱窗。将来做跨用户市场时
|
|
9
|
+
* 再加 published 字段和审核,不在这一版(SKILL.md 整段进 agent 上下文,
|
|
10
|
+
* 陌生人的 skill 等于让人往你会话里写指令,得先有审核范围才能开)。
|
|
11
|
+
*
|
|
12
|
+
* artifact_rel 存 'tasks/<任务>/<入口>',封面走 lib/cover.js 现截现缓存。
|
|
13
|
+
*/
|
|
14
|
+
|
|
15
|
+
import db from '../engine/runs/store.js';
|
|
16
|
+
|
|
17
|
+
db.exec(`
|
|
18
|
+
CREATE TABLE IF NOT EXISTS showcase (
|
|
19
|
+
id TEXT PRIMARY KEY,
|
|
20
|
+
user_id TEXT NOT NULL,
|
|
21
|
+
project_id TEXT,
|
|
22
|
+
task_id TEXT,
|
|
23
|
+
artifact_rel TEXT,
|
|
24
|
+
skill_name TEXT,
|
|
25
|
+
title TEXT NOT NULL,
|
|
26
|
+
note TEXT,
|
|
27
|
+
created_at TEXT NOT NULL DEFAULT (datetime('now'))
|
|
28
|
+
);
|
|
29
|
+
|
|
30
|
+
CREATE INDEX IF NOT EXISTS idx_showcase_user_created ON showcase(user_id, created_at DESC);
|
|
31
|
+
`);
|
|
32
|
+
|
|
33
|
+
function newId() {
|
|
34
|
+
return `sc_${Date.now().toString(36)}_${Math.random().toString(36).slice(2, 6)}`;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
function rowToEntry(row) {
|
|
38
|
+
if (!row) return null;
|
|
39
|
+
return {
|
|
40
|
+
id: row.id,
|
|
41
|
+
userId: row.user_id,
|
|
42
|
+
projectId: row.project_id,
|
|
43
|
+
taskId: row.task_id,
|
|
44
|
+
artifactRel: row.artifact_rel,
|
|
45
|
+
skillName: row.skill_name,
|
|
46
|
+
title: row.title,
|
|
47
|
+
note: row.note,
|
|
48
|
+
createdAt: row.created_at,
|
|
49
|
+
};
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
/** 同一件产物只留一条(重复固化就是更新,不是堆一堆重复卡片) */
|
|
53
|
+
export function upsertEntry({ userId, projectId, taskId, artifactRel, skillName, title, note }) {
|
|
54
|
+
if (!userId) throw new Error('userId required');
|
|
55
|
+
const existing = artifactRel
|
|
56
|
+
? db.prepare('SELECT * FROM showcase WHERE user_id = ? AND project_id IS ? AND artifact_rel IS ?')
|
|
57
|
+
.get(userId, projectId ?? null, artifactRel)
|
|
58
|
+
: null;
|
|
59
|
+
if (existing) {
|
|
60
|
+
db.prepare(`UPDATE showcase
|
|
61
|
+
SET task_id = ?, skill_name = ?, title = ?, note = ?, created_at = datetime('now')
|
|
62
|
+
WHERE id = ?`)
|
|
63
|
+
.run(taskId ?? null, skillName ?? null, title, note ?? null, existing.id);
|
|
64
|
+
return rowToEntry(db.prepare('SELECT * FROM showcase WHERE id = ?').get(existing.id));
|
|
65
|
+
}
|
|
66
|
+
const id = newId();
|
|
67
|
+
db.prepare(`INSERT INTO showcase (id, user_id, project_id, task_id, artifact_rel, skill_name, title, note)
|
|
68
|
+
VALUES (?, ?, ?, ?, ?, ?, ?, ?)`)
|
|
69
|
+
.run(id, userId, projectId ?? null, taskId ?? null, artifactRel ?? null,
|
|
70
|
+
skillName ?? null, title, note ?? null);
|
|
71
|
+
return rowToEntry(db.prepare('SELECT * FROM showcase WHERE id = ?').get(id));
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
export function listEntries(userId) {
|
|
75
|
+
if (!userId) return [];
|
|
76
|
+
return db.prepare('SELECT * FROM showcase WHERE user_id = ? ORDER BY created_at DESC')
|
|
77
|
+
.all(userId).map(rowToEntry);
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
export function getEntry(id) {
|
|
81
|
+
return rowToEntry(db.prepare('SELECT * FROM showcase WHERE id = ?').get(id));
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
export function removeEntry(id, userId) {
|
|
85
|
+
const info = db.prepare('DELETE FROM showcase WHERE id = ? AND user_id = ?').run(id, userId);
|
|
86
|
+
return info.changes > 0;
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
/** 项目删除时连带清(作品没了,卡片留着只会点出 404) */
|
|
90
|
+
export function removeEntriesForProject(projectId) {
|
|
91
|
+
db.prepare('DELETE FROM showcase WHERE project_id = ?').run(projectId);
|
|
92
|
+
}
|