@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.
Files changed (372) hide show
  1. package/LICENSE +661 -0
  2. package/README.md +140 -0
  3. package/bin/nodesign.js +176 -0
  4. package/package.json +104 -0
  5. package/server/api/README.md +45 -0
  6. package/server/api/_guard.js +75 -0
  7. package/server/api/admin.js +187 -0
  8. package/server/api/assets/docx-page.js +103 -0
  9. package/server/api/assets/helpers.js +22 -0
  10. package/server/api/assets/notes.js +144 -0
  11. package/server/api/assets.js +896 -0
  12. package/server/api/board.js +57 -0
  13. package/server/api/browse.js +138 -0
  14. package/server/api/canvas.js +444 -0
  15. package/server/api/chatai.js +324 -0
  16. package/server/api/exports/build-standalone.js +980 -0
  17. package/server/api/exports/cards.js +118 -0
  18. package/server/api/exports/docx-pdf.js +46 -0
  19. package/server/api/exports/export-page.js +142 -0
  20. package/server/api/exports/handoff.js +147 -0
  21. package/server/api/exports.js +690 -0
  22. package/server/api/instruction.js +75 -0
  23. package/server/api/local.js +115 -0
  24. package/server/api/me.js +116 -0
  25. package/server/api/memory.js +185 -0
  26. package/server/api/pending-changes.js +366 -0
  27. package/server/api/plugins.js +164 -0
  28. package/server/api/projects.js +220 -0
  29. package/server/api/publish.js +64 -0
  30. package/server/api/recent.js +73 -0
  31. package/server/api/sessions.js +589 -0
  32. package/server/api/skills.js +77 -0
  33. package/server/api/standalone-fit.js +168 -0
  34. package/server/api/turn-compose.js +180 -0
  35. package/server/api/turn-inflight.js +44 -0
  36. package/server/api/turn.js +669 -0
  37. package/server/auth/README.md +17 -0
  38. package/server/auth/middleware.js +138 -0
  39. package/server/auth/origin-guard.js +71 -0
  40. package/server/auth/session.js +108 -0
  41. package/server/auth/tier.js +113 -0
  42. package/server/auth/users-store.js +348 -0
  43. package/server/edit/README.md +29 -0
  44. package/server/engine/README.md +113 -0
  45. package/server/engine/agent/agent-shared.js +550 -0
  46. package/server/engine/agent/auto-mode-default-hard-deny.txt +4 -0
  47. package/server/engine/agent/auto-mode-rules.js +78 -0
  48. package/server/engine/agent/context.js +418 -0
  49. package/server/engine/agent/events.js +464 -0
  50. package/server/engine/agent/hooks/canvas-validate.js +199 -0
  51. package/server/engine/agent/hooks/failure.js +120 -0
  52. package/server/engine/agent/hooks/file-events.js +75 -0
  53. package/server/engine/agent/hooks/lifecycle.js +233 -0
  54. package/server/engine/agent/hooks/post-canvas-focus.js +80 -0
  55. package/server/engine/agent/hooks/post-guidance.js +206 -0
  56. package/server/engine/agent/hooks/post-subagent-report.js +104 -0
  57. package/server/engine/agent/hooks/post-trim.js +58 -0
  58. package/server/engine/agent/hooks/pre-board-neighborhood.js +34 -0
  59. package/server/engine/agent/hooks/pre-defaults.js +84 -0
  60. package/server/engine/agent/hooks/pre-injectors.js +277 -0
  61. package/server/engine/agent/hooks/pre-performance-log-guard.js +78 -0
  62. package/server/engine/agent/hooks/pre-starter-files.js +61 -0
  63. package/server/engine/agent/hooks/pre-workspace-scope-guard.js +78 -0
  64. package/server/engine/agent/hooks/site-validate.js +94 -0
  65. package/server/engine/agent/hooks/tool-prompts.js +31 -0
  66. package/server/engine/agent/hooks/turn-state-memory.js +56 -0
  67. package/server/engine/agent/hooks/user-prompt-submit.js +215 -0
  68. package/server/engine/agent/hooks.js +354 -0
  69. package/server/engine/agent/init-contract.js +69 -0
  70. package/server/engine/agent/isolation.js +159 -0
  71. package/server/engine/agent/model-context.js +359 -0
  72. package/server/engine/agent/model-table.js +292 -0
  73. package/server/engine/agent/plugin-loader.js +266 -0
  74. package/server/engine/agent/prompts/nodesign-prelude.md +336 -0
  75. package/server/engine/agent/prompts/tools/ask-user-question-protocol.md +71 -0
  76. package/server/engine/agent/prompts/tools/direct-edit-protocol.md +98 -0
  77. package/server/engine/agent/prompts/tools/generate-image-cookbook.gemini-gateway.md +583 -0
  78. package/server/engine/agent/prompts/tools/generate-image-cookbook.md +483 -0
  79. package/server/engine/agent/prompts/tools/hybrid-reference.md +100 -0
  80. package/server/engine/agent/prompts/tools/paint-still-cookbook.md +270 -0
  81. package/server/engine/agent/prompts/tools/roll-film-cookbook.md +70 -0
  82. package/server/engine/agent/prompts/tools/site-reference.md +106 -0
  83. package/server/engine/agent/prompts/tools/tweaks-syntax.md +125 -0
  84. package/server/engine/agent/prompts/tools/vision-checker-dispatch.md +48 -0
  85. package/server/engine/agent/session-loop.js +1024 -0
  86. package/server/engine/agent/session-model.js +164 -0
  87. package/server/engine/agent/skill.js +204 -0
  88. package/server/engine/agent/system-prompts.js +86 -0
  89. package/server/engine/agent/task-events.js +78 -0
  90. package/server/engine/agents/ds-extractor.md +132 -0
  91. package/server/engine/agents/explorer.md +190 -0
  92. package/server/engine/agents/index.js +230 -0
  93. package/server/engine/agents/schemas/design-system.json +156 -0
  94. package/server/engine/agents/schemas/tweak-schema.json +113 -0
  95. package/server/engine/agents/tweak-proposer.md +127 -0
  96. package/server/engine/agents/vision-checker.md +254 -0
  97. package/server/engine/browse/capture.js +422 -0
  98. package/server/engine/browse/card.js +137 -0
  99. package/server/engine/browse/handover.js +59 -0
  100. package/server/engine/browse/page-digest.js +119 -0
  101. package/server/engine/browse/refs.js +196 -0
  102. package/server/engine/browse/registry.js +307 -0
  103. package/server/engine/browse/screencast.js +170 -0
  104. package/server/engine/browse/state.js +121 -0
  105. package/server/engine/chatai/chat-log.js +79 -0
  106. package/server/engine/chatai/index.js +193 -0
  107. package/server/engine/chatai/openai-compat.js +152 -0
  108. package/server/engine/chatai/orchestrate.js +309 -0
  109. package/server/engine/chatai/perform.js +40 -0
  110. package/server/engine/chatai/summarize.js +79 -0
  111. package/server/engine/mcp/capability-gate.js +52 -0
  112. package/server/engine/mcp/index.js +339 -0
  113. package/server/engine/mcp/param-sanitizer.js +142 -0
  114. package/server/engine/mcp/tools/arrange-on-board.js +124 -0
  115. package/server/engine/mcp/tools/artifact-session.js +278 -0
  116. package/server/engine/mcp/tools/browse-computer.js +421 -0
  117. package/server/engine/mcp/tools/browse-find-batch.js +183 -0
  118. package/server/engine/mcp/tools/browse-screenshot.js +157 -0
  119. package/server/engine/mcp/tools/browse.js +480 -0
  120. package/server/engine/mcp/tools/build-docx.js +101 -0
  121. package/server/engine/mcp/tools/clear-pending-changes.js +99 -0
  122. package/server/engine/mcp/tools/create-on-board.js +155 -0
  123. package/server/engine/mcp/tools/crystallize-skill.js +168 -0
  124. package/server/engine/mcp/tools/deliver-files.js +0 -0
  125. package/server/engine/mcp/tools/explain-style.js +237 -0
  126. package/server/engine/mcp/tools/export-handoff.js +133 -0
  127. package/server/engine/mcp/tools/expose-tweaks.js +149 -0
  128. package/server/engine/mcp/tools/generate-image.js +842 -0
  129. package/server/engine/mcp/tools/get-computed-styles.js +164 -0
  130. package/server/engine/mcp/tools/get-pending-changes.js +205 -0
  131. package/server/engine/mcp/tools/h3box-ssh.js +84 -0
  132. package/server/engine/mcp/tools/helpers/acquire-page.js +82 -0
  133. package/server/engine/mcp/tools/helpers/motion-lab.js +554 -0
  134. package/server/engine/mcp/tools/helpers/motion-scroll.js +112 -0
  135. package/server/engine/mcp/tools/helpers/perception-page.js +215 -0
  136. package/server/engine/mcp/tools/helpers/reference-download.js +103 -0
  137. package/server/engine/mcp/tools/helpers/rembg-bridge.py +80 -0
  138. package/server/engine/mcp/tools/helpers/rembg.js +273 -0
  139. package/server/engine/mcp/tools/helpers/shot-pipeline.js +280 -0
  140. package/server/engine/mcp/tools/highlight.js +68 -0
  141. package/server/engine/mcp/tools/list-pages.js +222 -0
  142. package/server/engine/mcp/tools/lookup-tags.js +59 -0
  143. package/server/engine/mcp/tools/navigate-to-page.js +60 -0
  144. package/server/engine/mcp/tools/organize-board.js +64 -0
  145. package/server/engine/mcp/tools/paint-still.js +434 -0
  146. package/server/engine/mcp/tools/pin-to-board.js +130 -0
  147. package/server/engine/mcp/tools/preview-deck.js +130 -0
  148. package/server/engine/mcp/tools/profile-scroll.js +305 -0
  149. package/server/engine/mcp/tools/publish-site.js +102 -0
  150. package/server/engine/mcp/tools/query-elements.js +195 -0
  151. package/server/engine/mcp/tools/read-board.js +103 -0
  152. package/server/engine/mcp/tools/read-document.js +93 -0
  153. package/server/engine/mcp/tools/read-page.js +264 -0
  154. package/server/engine/mcp/tools/read-tavern-json.js +143 -0
  155. package/server/engine/mcp/tools/record-decision.js +140 -0
  156. package/server/engine/mcp/tools/relate-on-board.js +110 -0
  157. package/server/engine/mcp/tools/remove-background.js +391 -0
  158. package/server/engine/mcp/tools/report-issue.js +115 -0
  159. package/server/engine/mcp/tools/roll-film.js +266 -0
  160. package/server/engine/mcp/tools/screenshot-docx.js +138 -0
  161. package/server/engine/mcp/tools/screenshot-url.js +195 -0
  162. package/server/engine/mcp/tools/screenshot.js +583 -0
  163. package/server/engine/mcp/tools/tier-gate.js +96 -0
  164. package/server/engine/mcp/tools/trace-motion.js +215 -0
  165. package/server/engine/mcp/tools/web-search.js +548 -0
  166. package/server/engine/motion/inventory.js +349 -0
  167. package/server/engine/perception/session.js +235 -0
  168. package/server/engine/plugins/nodesign/.claude-plugin/plugin.json +5 -0
  169. package/server/engine/plugins/nodesign/skills/deskskill-engine-mini/SKILL.md +192 -0
  170. package/server/engine/plugins/nodesign/skills/deskskill-engine-mini/canvas.template.html +667 -0
  171. package/server/engine/plugins/nodesign/skills/deskskill-engine-mini/patterns/hybrid-grid.md +22 -0
  172. package/server/engine/plugins/nodesign/skills/deskskill-engine-mini/patterns/image-led-cover.md +25 -0
  173. package/server/engine/plugins/nodesign/skills/deskskill-engine-mini/patterns/portrait.md +23 -0
  174. package/server/engine/plugins/nodesign/skills/deskskill-engine-mini/patterns/quote-backdrop.md +21 -0
  175. package/server/engine/plugins/nodesign/skills/deskskill-engine-mini/patterns/section-divider.md +23 -0
  176. package/server/engine/plugins/nodesign/skills/deskskill-engine-mini/patterns/text-led.md +21 -0
  177. package/server/engine/plugins/nodesign/skills/docx-craft/SKILL.md +162 -0
  178. package/server/engine/plugins/nodesign/skills/docx-craft/references/token-schema.md +407 -0
  179. package/server/engine/plugins/nodesign/skills/docx-craft//346/226/207/346/241/243.template.json +80 -0
  180. package/server/engine/plugins/nodesign/skills/rp-craft/SKILL.md +228 -0
  181. 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
  182. package/server/engine/plugins/nodesign/skills/rp-craft//346/274/224/345/207/272.template.js +268 -0
  183. package/server/engine/plugins/nodesign/skills/site-craft/SKILL.md +323 -0
  184. package/server/engine/plugins/nodesign/skills/site-craft/patterns/build-lane.md +134 -0
  185. package/server/engine/plugins/nodesign/skills/site-craft/patterns/cutout-collage.md +61 -0
  186. package/server/engine/plugins/nodesign/skills/site-craft/patterns/mock-app.md +163 -0
  187. package/server/engine/plugins/nodesign/skills/site-craft/patterns/page-transitions.md +86 -0
  188. package/server/engine/runs/active-runs.js +698 -0
  189. package/server/engine/runs/live-turn.js +252 -0
  190. package/server/engine/runs/store.js +364 -0
  191. package/server/engine/runs/turn-relay.js +217 -0
  192. package/server/engine/runtime/workspace.js +164 -0
  193. package/server/index.js +235 -0
  194. package/server/lib/artifact-file-path.js +80 -0
  195. package/server/lib/artifact-target.js +284 -0
  196. package/server/lib/asset-refs.js +239 -0
  197. package/server/lib/async-queue.js +104 -0
  198. package/server/lib/auto-relations.js +130 -0
  199. package/server/lib/binding-types.js +60 -0
  200. package/server/lib/board-kind-sizes.js +62 -0
  201. package/server/lib/board-relations.js +133 -0
  202. package/server/lib/browse-proxy.js +180 -0
  203. package/server/lib/canvas-id.js +36 -0
  204. package/server/lib/cover.js +227 -0
  205. package/server/lib/danbooru-tags.js +291 -0
  206. package/server/lib/doc-extract.js +156 -0
  207. package/server/lib/docx/build-from-source.js +260 -0
  208. package/server/lib/docx/build.js +490 -0
  209. package/server/lib/docx/dump-styles.js +287 -0
  210. package/server/lib/docx/fonts/nodesign-cjk.conf +164 -0
  211. package/server/lib/docx/merge-runs.js +129 -0
  212. package/server/lib/docx/numbering.js +218 -0
  213. package/server/lib/docx/order.js +152 -0
  214. package/server/lib/docx/rawzip.js +172 -0
  215. package/server/lib/docx/render.js +92 -0
  216. package/server/lib/docx/text-lint.js +225 -0
  217. package/server/lib/docx/tokens.js +335 -0
  218. package/server/lib/docx/units.js +29 -0
  219. package/server/lib/docx/xml.js +291 -0
  220. package/server/lib/docx-pages.js +187 -0
  221. package/server/lib/export-collect.js +289 -0
  222. package/server/lib/export-package.js +210 -0
  223. package/server/lib/html-srcset.js +145 -0
  224. package/server/lib/image-variant.js +460 -0
  225. package/server/lib/ingress/forward-openai-chat.js +305 -0
  226. package/server/lib/ingress/openai-chat.js +491 -0
  227. package/server/lib/ingress/session-notice.js +66 -0
  228. package/server/lib/ingress/session-routes.js +68 -0
  229. package/server/lib/ingress/slot-probe.js +130 -0
  230. package/server/lib/ingress/upstream-billing.js +52 -0
  231. package/server/lib/ingress/upstream-fail-streak.js +72 -0
  232. package/server/lib/ingress/upstream-truncation.js +48 -0
  233. package/server/lib/issues-store.js +182 -0
  234. package/server/lib/kinds/deck.js +72 -0
  235. package/server/lib/kinds/docx.js +283 -0
  236. package/server/lib/kinds/file-kinds.js +113 -0
  237. package/server/lib/kinds/index.js +202 -0
  238. package/server/lib/kinds/site.js +161 -0
  239. package/server/lib/model-ingress.js +543 -0
  240. package/server/lib/moderation.js +255 -0
  241. package/server/lib/notice-store.js +103 -0
  242. package/server/lib/plugin-install.js +104 -0
  243. package/server/lib/plugin-validator.js +721 -0
  244. package/server/lib/publish-store.js +115 -0
  245. package/server/lib/quick-summary.js +52 -0
  246. package/server/lib/quota.js +270 -0
  247. package/server/lib/rate-window.js +29 -0
  248. package/server/lib/reference-assets.js +92 -0
  249. package/server/lib/region-shot.js +135 -0
  250. package/server/lib/safe-path.js +73 -0
  251. package/server/lib/sdk-session.js +33 -0
  252. package/server/lib/showcase-store.js +92 -0
  253. package/server/lib/site-publish.js +465 -0
  254. package/server/lib/ssrf-guard.js +432 -0
  255. package/server/lib/task-scan.js +158 -0
  256. package/server/lib/tavern-json.js +154 -0
  257. package/server/lib/video-variant.js +237 -0
  258. package/server/lib/workspace-path.js +33 -0
  259. package/server/ops/fix-sdk-musl.mjs +40 -0
  260. package/server/ops/install-macos-fonts.sh +114 -0
  261. package/server/ops/macos-fonts.conf +336 -0
  262. package/server/ops/sandbox-shim/bwrap +57 -0
  263. package/server/projects/assets-summary.js +119 -0
  264. package/server/projects/auto-name.js +53 -0
  265. package/server/projects/board-store.js +640 -0
  266. package/server/projects/move-entry.js +103 -0
  267. package/server/projects/store.js +270 -0
  268. package/server/projects/ui-config.js +54 -0
  269. package/server/projects/workspace-templates.js +53 -0
  270. package/server/projects/workspace.js +1025 -0
  271. package/server/runtime/capabilities.js +153 -0
  272. package/server/runtime/local-config.js +177 -0
  273. package/server/runtime/local-env.js +88 -0
  274. package/server/runtime/platform.js +342 -0
  275. package/server/runtime/profile.js +72 -0
  276. package/server/services/rembg-launcher.js +313 -0
  277. package/server/services/rembg-service.py +334 -0
  278. package/server/shared/README.md +25 -0
  279. package/server/shared/deck.js +54 -0
  280. package/server/shared/time.js +49 -0
  281. package/server/style-pipeline/README.md +53 -0
  282. package/server/ws/broker.js +36 -0
  283. package/server/ws/browse-channel.js +177 -0
  284. package/server/ws/index.js +386 -0
  285. package/web/README.md +89 -0
  286. package/web/dist/assets/BrowserWindow-Dc7J7CYc.js +11 -0
  287. package/web/dist/assets/DeckWindow-4G0xX7P3.js +16 -0
  288. package/web/dist/assets/DocxWindow-BXQsoPkh.js +16 -0
  289. package/web/dist/assets/KaTeX_AMS-Regular-BQhdFMY1.woff2 +0 -0
  290. package/web/dist/assets/KaTeX_AMS-Regular-DMm9YOAa.woff +0 -0
  291. package/web/dist/assets/KaTeX_AMS-Regular-DRggAlZN.ttf +0 -0
  292. package/web/dist/assets/KaTeX_Caligraphic-Bold-ATXxdsX0.ttf +0 -0
  293. package/web/dist/assets/KaTeX_Caligraphic-Bold-BEiXGLvX.woff +0 -0
  294. package/web/dist/assets/KaTeX_Caligraphic-Bold-Dq_IR9rO.woff2 +0 -0
  295. package/web/dist/assets/KaTeX_Caligraphic-Regular-CTRA-rTL.woff +0 -0
  296. package/web/dist/assets/KaTeX_Caligraphic-Regular-Di6jR-x-.woff2 +0 -0
  297. package/web/dist/assets/KaTeX_Caligraphic-Regular-wX97UBjC.ttf +0 -0
  298. package/web/dist/assets/KaTeX_Fraktur-Bold-BdnERNNW.ttf +0 -0
  299. package/web/dist/assets/KaTeX_Fraktur-Bold-BsDP51OF.woff +0 -0
  300. package/web/dist/assets/KaTeX_Fraktur-Bold-CL6g_b3V.woff2 +0 -0
  301. package/web/dist/assets/KaTeX_Fraktur-Regular-CB_wures.ttf +0 -0
  302. package/web/dist/assets/KaTeX_Fraktur-Regular-CTYiF6lA.woff2 +0 -0
  303. package/web/dist/assets/KaTeX_Fraktur-Regular-Dxdc4cR9.woff +0 -0
  304. package/web/dist/assets/KaTeX_Main-Bold-Cx986IdX.woff2 +0 -0
  305. package/web/dist/assets/KaTeX_Main-Bold-Jm3AIy58.woff +0 -0
  306. package/web/dist/assets/KaTeX_Main-Bold-waoOVXN0.ttf +0 -0
  307. package/web/dist/assets/KaTeX_Main-BoldItalic-DxDJ3AOS.woff2 +0 -0
  308. package/web/dist/assets/KaTeX_Main-BoldItalic-DzxPMmG6.ttf +0 -0
  309. package/web/dist/assets/KaTeX_Main-BoldItalic-SpSLRI95.woff +0 -0
  310. package/web/dist/assets/KaTeX_Main-Italic-3WenGoN9.ttf +0 -0
  311. package/web/dist/assets/KaTeX_Main-Italic-BMLOBm91.woff +0 -0
  312. package/web/dist/assets/KaTeX_Main-Italic-NWA7e6Wa.woff2 +0 -0
  313. package/web/dist/assets/KaTeX_Main-Regular-B22Nviop.woff2 +0 -0
  314. package/web/dist/assets/KaTeX_Main-Regular-Dr94JaBh.woff +0 -0
  315. package/web/dist/assets/KaTeX_Main-Regular-ypZvNtVU.ttf +0 -0
  316. package/web/dist/assets/KaTeX_Math-BoldItalic-B3XSjfu4.ttf +0 -0
  317. package/web/dist/assets/KaTeX_Math-BoldItalic-CZnvNsCZ.woff2 +0 -0
  318. package/web/dist/assets/KaTeX_Math-BoldItalic-iY-2wyZ7.woff +0 -0
  319. package/web/dist/assets/KaTeX_Math-Italic-DA0__PXp.woff +0 -0
  320. package/web/dist/assets/KaTeX_Math-Italic-flOr_0UB.ttf +0 -0
  321. package/web/dist/assets/KaTeX_Math-Italic-t53AETM-.woff2 +0 -0
  322. package/web/dist/assets/KaTeX_SansSerif-Bold-CFMepnvq.ttf +0 -0
  323. package/web/dist/assets/KaTeX_SansSerif-Bold-D1sUS0GD.woff2 +0 -0
  324. package/web/dist/assets/KaTeX_SansSerif-Bold-DbIhKOiC.woff +0 -0
  325. package/web/dist/assets/KaTeX_SansSerif-Italic-C3H0VqGB.woff2 +0 -0
  326. package/web/dist/assets/KaTeX_SansSerif-Italic-DN2j7dab.woff +0 -0
  327. package/web/dist/assets/KaTeX_SansSerif-Italic-YYjJ1zSn.ttf +0 -0
  328. package/web/dist/assets/KaTeX_SansSerif-Regular-BNo7hRIc.ttf +0 -0
  329. package/web/dist/assets/KaTeX_SansSerif-Regular-CS6fqUqJ.woff +0 -0
  330. package/web/dist/assets/KaTeX_SansSerif-Regular-DDBCnlJ7.woff2 +0 -0
  331. package/web/dist/assets/KaTeX_Script-Regular-C5JkGWo-.ttf +0 -0
  332. package/web/dist/assets/KaTeX_Script-Regular-D3wIWfF6.woff2 +0 -0
  333. package/web/dist/assets/KaTeX_Script-Regular-D5yQViql.woff +0 -0
  334. package/web/dist/assets/KaTeX_Size1-Regular-C195tn64.woff +0 -0
  335. package/web/dist/assets/KaTeX_Size1-Regular-Dbsnue_I.ttf +0 -0
  336. package/web/dist/assets/KaTeX_Size1-Regular-mCD8mA8B.woff2 +0 -0
  337. package/web/dist/assets/KaTeX_Size2-Regular-B7gKUWhC.ttf +0 -0
  338. package/web/dist/assets/KaTeX_Size2-Regular-Dy4dx90m.woff2 +0 -0
  339. package/web/dist/assets/KaTeX_Size2-Regular-oD1tc_U0.woff +0 -0
  340. package/web/dist/assets/KaTeX_Size3-Regular-CTq5MqoE.woff +0 -0
  341. package/web/dist/assets/KaTeX_Size3-Regular-DgpXs0kz.ttf +0 -0
  342. package/web/dist/assets/KaTeX_Size4-Regular-BF-4gkZK.woff +0 -0
  343. package/web/dist/assets/KaTeX_Size4-Regular-DWFBv043.ttf +0 -0
  344. package/web/dist/assets/KaTeX_Size4-Regular-Dl5lxZxV.woff2 +0 -0
  345. package/web/dist/assets/KaTeX_Typewriter-Regular-C0xS9mPB.woff +0 -0
  346. package/web/dist/assets/KaTeX_Typewriter-Regular-CO6r4hn1.woff2 +0 -0
  347. package/web/dist/assets/KaTeX_Typewriter-Regular-D3Ib7_Hf.ttf +0 -0
  348. package/web/dist/assets/SiteWindow-D6eS-2gM.js +18 -0
  349. package/web/dist/assets/again-D8tfgKKO.webp +0 -0
  350. package/web/dist/assets/caveat-nd-CP6HlsNg.woff2 +0 -0
  351. package/web/dist/assets/clock-xJhvfYpS.webp +0 -0
  352. package/web/dist/assets/film-cut-BBKMwH7q.webp +0 -0
  353. package/web/dist/assets/film-still-BZ_DL9Tt.webp +0 -0
  354. package/web/dist/assets/index-C0KUf6Wd.css +1 -0
  355. package/web/dist/assets/index-Cbl3ATvp.js +2125 -0
  356. package/web/dist/assets/ink-desk-BRbxeDsR.webp +0 -0
  357. package/web/dist/assets/ink-night-Bz4BT3XH.webp +0 -0
  358. package/web/dist/assets/ink-portrait-C0DM4V8Q.webp +0 -0
  359. package/web/dist/assets/longcang-regular-C0uvTuKa.woff2 +0 -0
  360. package/web/dist/assets/lxgw-nd-bold-DbRfiTgo.woff2 +0 -0
  361. package/web/dist/assets/lxgw-nd-regular-CpMw9Ehg.woff2 +0 -0
  362. package/web/dist/assets/pending-edit-apply-DTjo2geW.js +31 -0
  363. package/web/dist/assets/question-8H4HSqxh.webp +0 -0
  364. package/web/dist/assets/reject-BaPJqElj.webp +0 -0
  365. package/web/dist/assets/rotate-cw-LMT3x1Zj.js +11 -0
  366. package/web/dist/assets/rp-portrait-CbrGpew2.webp +0 -0
  367. package/web/dist/assets/rp-street-C4jVKjxx.webp +0 -0
  368. package/web/dist/assets/square-dashed-mouse-pointer-CBoAkfhi.js +11 -0
  369. package/web/dist/assets/tangle-DIxHVNuT.webp +0 -0
  370. package/web/dist/assets/thisone-BylZKglV.webp +0 -0
  371. package/web/dist/assets/thumb-DO1wfK1I.webp +0 -0
  372. package/web/dist/index.html +18 -0
@@ -0,0 +1,339 @@
1
+ /**
2
+ * server/engine/mcp/index.js — Nodesign 内置 MCP server
3
+ *
4
+ * 暴露给 agent 的自定义工具集(in-process,via SDK 的 createSdkMcpServer):
5
+ *
6
+ * 感知层(playwright headless 跑出真实渲染元数据):
7
+ * screenshot_canvas / list_pages / read_page / query_elements / get_computed_styles
8
+ * 控制层(emit 事件让前端同步):
9
+ * navigate_to_page / highlight / preview_deck
10
+ * 反馈层(用户在 canvas 上的直接编辑 + 评论 buffer):
11
+ * get_pending_changes / clear_pending_changes
12
+ * 产物层(NoDesign 差异化能力):
13
+ * expose_tweaks / export_handoff / record_decision
14
+ * 研究层:
15
+ * web_search(4 provider,CJK auto baidu)
16
+ *
17
+ * 调用约定(SDK 自动给 tool name 加前缀):
18
+ * tool 名在 agent 端是 mcp__nodesign__<tool>,比如 mcp__nodesign__screenshot_canvas
19
+ *
20
+ * 实例化策略:
21
+ * 每个 runAgent 创建一个新的 MCP server 实例(through createNodesignMcpServer)。
22
+ * 开销小(in-process,没起 process),但能让 deps(workspaceRoot / projectId / ctx)
23
+ * 绑死到当前 turn 的上下文,避免 cross-talk。
24
+ *
25
+ * 安全:
26
+ * tool handler 在 SDK 进程内(本服务器进程)跑,不通过 stdio/sse/http。
27
+ * handler 自己不做沙盒,由 PreToolUse hook + workspace cwd 隔离兜底。
28
+ */
29
+
30
+ import { createSdkMcpServer } from '@anthropic-ai/claude-agent-sdk';
31
+ import { withParamSanitizer } from './param-sanitizer.js';
32
+ import { withCapabilityGate } from './capability-gate.js';
33
+ import { makeScreenshotCanvasTool } from './tools/screenshot.js';
34
+ import { makeScreenshotUrlTool } from './tools/screenshot-url.js';
35
+ import { makeExportHandoffTool } from './tools/export-handoff.js';
36
+ import { makeRecordDecisionTool } from './tools/record-decision.js';
37
+ import { makeWebSearchTool } from './tools/web-search.js';
38
+ import { withTierGate } from './tools/tier-gate.js'; // 档位闸包装器(auth/tier.js;按项目 owner)
39
+ import { makeReadPageTool } from './tools/read-page.js';
40
+ import { makeListPagesTool } from './tools/list-pages.js';
41
+ import { makeQueryElementsTool } from './tools/query-elements.js';
42
+ import { makeProfileScrollTool } from './tools/profile-scroll.js';
43
+ import { makeExplainStyleTool } from './tools/explain-style.js';
44
+ import { makeTraceMotionTool } from './tools/trace-motion.js';
45
+ import {
46
+ makeBrowserNavigateTool, makeBrowserReadTool, makeBrowserClickTool,
47
+ makeBrowserRequestHelpTool, makeBrowserCaptureTool,
48
+ } from './tools/browse.js';
49
+ import { makeBrowserScreenshotTool } from './tools/browse-screenshot.js';
50
+ import { makeBrowserComputerTool } from './tools/browse-computer.js';
51
+ import { makeBrowserFindTool, makeBrowserBatchTool } from './tools/browse-find-batch.js';
52
+ import { makeArtifactOpenTool, makeArtifactComputerTool, makeArtifactFindTool, makeArtifactMotionTool, makeArtifactBatchTool } from './tools/artifact-session.js';
53
+ import { makeGetComputedStylesTool } from './tools/get-computed-styles.js';
54
+ import { makeNavigateToPageTool } from './tools/navigate-to-page.js';
55
+ import { makeHighlightTool } from './tools/highlight.js';
56
+ import { makePreviewDeckTool } from './tools/preview-deck.js';
57
+ import { makeBuildDocxTool } from './tools/build-docx.js';
58
+ import { makeExposeTweaksTool } from './tools/expose-tweaks.js';
59
+ import { makeGetPendingChangesTool } from './tools/get-pending-changes.js';
60
+ import { makeClearPendingChangesTool } from './tools/clear-pending-changes.js';
61
+ import { makeGenerateImageTool } from './tools/generate-image.js';
62
+ import { makeRemoveBackgroundTool } from './tools/remove-background.js';
63
+ import { makePinToBoardTool } from './tools/pin-to-board.js';
64
+ import { makeRelateOnBoardTool } from './tools/relate-on-board.js';
65
+ import { makeReadBoardTool } from './tools/read-board.js';
66
+ import { makeArrangeOnBoardTool } from './tools/arrange-on-board.js';
67
+ import { makeCreateOnBoardTool } from './tools/create-on-board.js';
68
+ import { makeOrganizeBoardTool } from './tools/organize-board.js';
69
+ import { makeReadDocumentTool } from './tools/read-document.js';
70
+ import { makeReadTavernJsonTool } from './tools/read-tavern-json.js';
71
+ import { makeDeliverFilesTool } from './tools/deliver-files.js';
72
+ import { makeCrystallizeSkillTool } from './tools/crystallize-skill.js';
73
+ import { makePublishSiteTool } from './tools/publish-site.js';
74
+ import { makeReportIssueTool } from './tools/report-issue.js';
75
+ import { makeRollFilmTool } from './tools/roll-film.js';
76
+ import { makePaintStillTool } from './tools/paint-still.js';
77
+ import { makeLookupTagsTool } from './tools/lookup-tags.js';
78
+
79
+ /**
80
+ * 创建 Nodesign 的 MCP server,绑定当前 run 的依赖。
81
+ *
82
+ * @param {object} deps
83
+ * @param {string} deps.workspaceRoot 绝对路径,project workspace(sessions/<sid>/)
84
+ * @param {string} [deps.sharedRoot] project shared/ 根(跨 session 共享 assets / .claude)
85
+ * @param {string} [deps.projectId]
86
+ * @param {string} [deps.sessionId] NoDesign sessionId(活跃产物指针 / 会话级锁 / 出处记账用)
87
+ * @param {import('../agent/context.js').AgentContext} [deps.ctx] EventBus 入口
88
+ * @returns SDK MCP server config(喂给 query options.mcpServers)
89
+ */
90
+ // 常驻 schema 白名单(2026-07-23 订阅模式 token 瘦身):
91
+ // 高频 + schema 小的工具第一 turn 就注入 prompt;不在名单里的走 SDK 默认
92
+ // deferred —— system prompt 只留工具名,agent 用 ToolSearch 按需拉 schema
93
+ // (需要 ENABLE_TOOL_SEARCH=true + allowlist 含 ToolSearch,见 session-loop)。
94
+ // 实测 defer 掉 generate_image/web_search/remove_background 等 6 个胖工具
95
+ // 省 ~80k 字符常驻 schema。prelude 的工具速查表仍列全部工具名 + 一句话用途,
96
+ // agent 知道存在什么、需要时先 ToolSearch("select:mcp__nodesign__<tool>")。
97
+ // (kimi 时代曾全局 alwaysLoad —— kimi 不认 ToolSearch;claude 系模型原生受训,可放心 defer)
98
+ const ALWAYS_LOAD_TOOLS = new Set([
99
+ 'screenshot_canvas', 'read_page', 'list_pages', 'query_elements',
100
+ 'get_computed_styles', 'navigate_to_page', 'highlight', 'preview_deck',
101
+ 'record_decision', 'get_pending_changes', 'clear_pending_changes',
102
+ // screenshot_url 常驻:explorer 显式 tools 列表没有 ToolSearch,defer 了它就
103
+ // 永远拉不到 schema;schema 本身很小(4 字段),常驻成本可忽略
104
+ 'screenshot_url',
105
+ // ⭐ 浏览通道六件(2026-08-18 当天从 deferred 改常驻;08-21 加到九件,见下)。
106
+ //
107
+ // 它们最初跟着「新工具默认 deferred」的习惯走,而那条习惯的依据是**成本**:
108
+ // defer 掉的是 expose_tweaks / paint_still / roll_film 那种十万字符级的胖子。
109
+ // 这六个实测 description 合计 4980 字符、连参数区 ≈ 6.9k —— 比那几个小
110
+ // 30~100 倍,塞进被缓存的 system prompt 每轮增量约等于零。
111
+ //
112
+ // 而 deferred 的代价是具体的:agent 只看得到**名字**。`browser_capture`
113
+ // 这个名字一个字都没说它能带回调色板、字体、命中的 CSS 规则和结构三数 ——
114
+ // 于是它不会去搜这个 schema,做 deck / 做文档的会话里整条浏览通道等于不存在
115
+ // (只有加载了 site-craft skill 才会被点名)。**名字自解释的工具可以 defer,
116
+ // 卖点藏在描述里的不能。**
117
+ 'browser_navigate', 'browser_read', 'browser_click', 'browser_screenshot',
118
+ 'browser_request_help', 'browser_capture',
119
+ // 08-21 加的三件(坐标/引用级操作 + 词法找元 + 一次往返跑一串)。同一条理由:
120
+ // browser_batch 的卖点"省掉模型往返"和 browser_find 的"ref 比像素稳"都在描述里,
121
+ // 名字本身不卖;三件描述合计 ≈ 5.5k 字符,缓存里每轮增量约等于零。
122
+ 'browser_computer', 'browser_find', 'browser_batch',
123
+ // 08-21 产物会话五件:成品检查的交互半边(状态跨调用留着)。artifact_batch 的卖点
124
+ // "一趟跑完 + 结尾截图"和 live:true 的存在都只在描述里说,常驻。
125
+ 'artifact_open', 'artifact_computer', 'artifact_find', 'artifact_batch', 'artifact_motion',
126
+ ]);
127
+
128
+ export function createNodesignMcpServer({ workspaceRoot, sharedRoot, projectId, sessionId, ctx } = {}) {
129
+ // 浏览通道里能被 browser_batch 串起来的七件:先建一次,batch 拿**同一批实例**
130
+ // (projectId/ctx 绑在 handler 里,不能再造第二份)。只有 request_help 不进
131
+ // batch(它阻塞等人);capture 在里面 —— 逐页采 token 正是 batch 要省的那种回合。
132
+ const browseBatchable = [
133
+ makeBrowserNavigateTool({ projectId, ctx }),
134
+ makeBrowserReadTool({ projectId }),
135
+ makeBrowserClickTool({ projectId }),
136
+ makeBrowserScreenshotTool({ projectId, workspaceRoot }),
137
+ // 采集:把可复用的东西(调色板/字体/CSS/结构骨架/截图)落进
138
+ // assets/references/web/ + 出处 sidecar —— 下个会话还在
139
+ makeBrowserCaptureTool({ projectId, workspaceRoot, sessionId, ctx }),
140
+ // 坐标/引用级指针与键盘 + 词法找元(08-21,形状照 browser_toolset_20260801)
141
+ makeBrowserComputerTool({ projectId, ctx }),
142
+ makeBrowserFindTool({ projectId }),
143
+ ];
144
+ // 五个能骑到产物会话上(live:true)的量具:先建一次,artifact_batch 拿同一批实例
145
+ const screenshotCanvas = makeScreenshotCanvasTool({ workspaceRoot, projectId, sessionId, ctx });
146
+ const queryElements = makeQueryElementsTool({ workspaceRoot, projectId, sessionId, ctx });
147
+ const getComputedStyles = makeGetComputedStylesTool({ workspaceRoot, projectId, sessionId, ctx });
148
+ const explainStyle = makeExplainStyleTool({ workspaceRoot, projectId, sessionId });
149
+ const traceMotion = makeTraceMotionTool({ workspaceRoot, projectId, sessionId });
150
+ // 产物会话四件(engine/perception/session.js):artifact_open / computer / find / motion
151
+ const artifactSessionTools = [
152
+ makeArtifactOpenTool({ projectId, workspaceRoot, sessionId }),
153
+ makeArtifactComputerTool({ projectId }),
154
+ makeArtifactFindTool({ projectId }),
155
+ // 自己的产物靠什么在动:跟 browser_capture motion 同一份引擎(engine/motion/inventory.js)
156
+ makeArtifactMotionTool({ projectId, workspaceRoot, sessionId }),
157
+ ];
158
+ const tools = [
159
+ // C9 screenshot_canvas — playwright headless 截图 → image content block
160
+ screenshotCanvas,
161
+
162
+ // screenshot_url — 外部 URL 截图(2026-07-29)。explorer 找视觉参考不再
163
+ // 只能 WebFetch 文本转述;主 agent 也能直接看参考站。http/https only。
164
+ makeScreenshotUrlTool({ projectId, ctx }),
165
+
166
+ // deliver_files — agent 挑好的产物直接进用户浏览器下载列表(emit run.download_ready)
167
+ makeDeliverFilesTool({ workspaceRoot, projectId, sessionId, ctx }),
168
+
169
+ // read_document — Word / Excel / PowerPoint 的读取口(2026-08-07)。
170
+ // 普通 Read 读这三种只会拿到二进制乱码**而且不报错**,agent 会拿着空气
171
+ // 往下干。PDF 不在此列:Read 原生支持,真跑验过。
172
+ makeReadDocumentTool({ workspaceRoot, sharedRoot }),
173
+
174
+ // 酒馆 JSON 读取口:预设/角色卡/世界书先摘要再取正文(Read 会被 460KB 预设撑爆)
175
+ makeReadTavernJsonTool({ workspaceRoot, sharedRoot }),
176
+
177
+ // C10 export_handoff — 复用 exports.js 的 buildHandoffZip,写到 workspace/exports/
178
+ makeExportHandoffTool({ workspaceRoot, sharedRoot, projectId, sessionId, ctx }),
179
+
180
+ // C11 record_decision — 写入 spec.json decisions[] 设计意图档案
181
+ makeRecordDecisionTool({ workspaceRoot, sessionId, ctx }),
182
+
183
+ // crystallize_skill — 把探索出来的方法论固化成用户自己的 skill + 作品进橱窗
184
+ // (2026-07-30)。用户明确要求才调;写的是判断依据不是成品 HTML。
185
+ makeCrystallizeSkillTool({ projectId, sessionId, ctx }),
186
+
187
+ // publish_site — 站点一键上线 Cloudflare Pages(2026-08-02)。用户明确要求
188
+ // 才调(发公网是外发动作);额度按项目 owner 算,与站点窗按钮共用一套闸门。
189
+ makePublishSiteTool({ projectId }),
190
+
191
+ // roll_film — 自部署 MiniMax-H3 视频产线(2026-08-08)。默认走站主 5090
192
+ // 盒子(h3box over SSH;Modal 余额告急降备用档,NODESIGN_FILM_BACKEND=modal
193
+ // 显式才走,绝不自动回退烧余额);配方恒定 Turbo8步 ≤12.25s;试用号拒
194
+ // (owner 闸门同 publish_site)。视觉 QC 归用户,工具只回文本路径。
195
+ makeRollFilmTool({ workspaceRoot, sharedRoot, projectId, sessionId, ctx }),
196
+
197
+ // paint_still — 站主本地 GPU 盒子生图(NoobAI/Anima,2026-08-08)。盒子
198
+ // 在线才可用(NODESIGN_H3BOX_SSH);动漫向/视频关键帧首选,通用生图仍走
199
+ // generate_image。视觉 QC 归用户。
200
+ makePaintStillTool({ workspaceRoot, sharedRoot, projectId, sessionId, ctx }),
201
+
202
+ // lookup_tags — danbooru 标签画前查询(2026-08-20)。paint_still 画完的体检
203
+ // 只能事后说"这批图别信";这个把查的动作挪到画前,配手册的起手纪律
204
+ // (先理解用户要什么 → 想候选标签 → 查 → 再画)。薄壳,逻辑与体检共用
205
+ // lib/danbooru-tags.js。deferred:名字自解释,且 paint_still 描述里点了名。
206
+ makeLookupTagsTool(),
207
+
208
+ // report_issue — agent 给维护者写信(08-02 由 report_friction 扩容改名):
209
+ // bug / friction / idea 三类走同一张 issues 表。跟 PostToolUseFailure 的
210
+ // 自动记录分工:自动层记"发生了什么",这层补"为什么难受、期望怎样"。
211
+ makeReportIssueTool({ projectId, sessionId, ctx }),
212
+
213
+ // web_search — 4 provider 联网搜索(baidu/tavily/exa/zhipu,CJK auto route to baidu)
214
+ // 移植自 ~/.deskclaw/skills/deskclaw-search-pro/scripts/search.py,0 外部依赖。
215
+ // WebFetch 不在这里 — 用 SDK 内置(session-loop.js DEFAULT_TOOL_ALLOWLIST 启用),
216
+ // 它自带 LLM summarize 能控制上下文,不需要自实现。
217
+ withTierGate(makeWebSearchTool({ workspaceRoot, sharedRoot, ctx }), 'webSearch', projectId), // basic 档有日上限
218
+
219
+ // S1c canvas 焕新升级 — read_page 让 agent 精确读 canvas.html 任意页
220
+ // (`<section data-page="N">` 一段),不必 Read 整文件 + Grep + offset/limit。
221
+ // 解 2026-05-02 用户观察"agent 只看第一页"痛点。
222
+ makeReadPageTool({ workspaceRoot, sessionId, ctx }),
223
+
224
+ // ── Canvas 焕新 C1(2026-05-02):完整 agent "感知 + 操作" 工具链 ──
225
+ // 感知层:list_pages / query_elements / get_computed_styles —— playwright
226
+ // headless 跑出来真实 render 后的元数据,agent 不再盲改
227
+ makeListPagesTool({ workspaceRoot, projectId, sessionId, ctx }),
228
+ queryElements,
229
+
230
+ // profile_scroll — 滚动性能量具(2026-08-18)。用户说"一顿一顿"时 agent
231
+ // 以前手里零工具,只能自己起 http server 手搓 rAF 采样。不进常驻工具表
232
+ // (deferred,用得上时自然搜到),schema 不白占每轮上下文。
233
+ makeProfileScrollTool({ workspaceRoot, projectId, sessionId }),
234
+
235
+ // explain_style — CSS 级联诊断(2026-08-18)。computed 只给结果不给原因,
236
+ // 而 agent 手里没有 devtools 的 Styles 面板。同样走 deferred。
237
+ explainStyle,
238
+
239
+ // trace_motion — 动画数值示波器(2026-08-19,iss_mszv782a_toab)。缓动/过冲/
240
+ // 硬切从"看静帧猜"变成逐帧采样量出来。deferred:名字自解释,且常驻的
241
+ // screenshot_canvas 描述里点了名(胶片条管看、这个管量)。
242
+ traceMotion,
243
+
244
+ // ── 浏览通道(2026-08-18):agent 真的会用浏览器 ──
245
+ // 常驻一个 chromium(按项目键、带持久 profile),所以能点链接、翻子页、
246
+ // 留住登录态。跟 screenshot_url 的分工:那个是一次性一张图,这组是有会话的逛。
247
+ // ⭐ 每个请求都过 lib/ssrf-guard.js 的出网闸(含跳转每一跳、iframe、子资源),
248
+ // 闸在工具实现体里,agent 关不掉。**全部常驻**(理由见 ALWAYS_LOAD_TOOLS)。
249
+ //
250
+ // ⚠️ 刻意**没给 explorer 子代理**:registry 按 projectId 键 = 主 agent 和
251
+ // 子代理共用同一只浏览器、同一个 page。mutex 保证不撕裂,但保证不了不抢 ——
252
+ // 子代理一导航就把主 agent 正在看的那一页带走了,症状是"我刚打开的页面
253
+ // 自己变了"。要给的话得先有"借用后还原"(存 URL、用完 goto 回去),
254
+ // 或者第二个常驻名额;1 vCPU 上后者不成立。
255
+ ...browseBatchable,
256
+ // 一次往返跑一串(串行、遇错即停、结尾补截图)—— 省的是模型回合数
257
+ makeBrowserBatchTool({ tools: browseBatchable }),
258
+ // ── 产物会话(2026-08-21):成品检查的交互半边 ──
259
+ // artifact_open 把产物开进常驻会话,artifact_computer/find 对着它点、敲、找;
260
+ // 五个量具 live:true 就量会话里现在这一页;artifact_batch 一趟跑一串。
261
+ ...artifactSessionTools,
262
+ makeArtifactBatchTool({ tools: [...artifactSessionTools, screenshotCanvas, queryElements, getComputedStyles, explainStyle, traceMotion] }),
263
+ // 撞验证墙时举手叫人(阻塞等,默认 120 秒超时 —— 人可能就走了)
264
+ makeBrowserRequestHelpTool({ projectId, ctx }),
265
+ getComputedStyles,
266
+
267
+ // 控制层:emit 反向事件给前端,server 主动操作 canvas UI
268
+ makeNavigateToPageTool({ ctx }),
269
+ makeHighlightTool({ ctx }),
270
+ // 把 deck 摊到用户眼前(= 用户双击那张卡):收起态→内嵌渲染,展开态→最大化窗
271
+ makePreviewDeckTool({ ctx, sessionId, workspaceRoot }),
272
+
273
+ // word 形态的构建道:token JSON(真相源)→ .docx(产物)。
274
+ // agent 拿到的是一条命令不是一个构建系统 —— 写 JSON、调它、再 screenshot 看。
275
+ makeBuildDocxTool({ workspaceRoot, sessionId, ctx }),
276
+
277
+ // 反馈层:用户在 canvas 上的直接编辑 + 评论 buffer
278
+ // 前端在 chat 时由 turn.js 注入 system 提示,agent 主动调下面两个工具读 + 清
279
+ makeGetPendingChangesTool({ workspaceRoot, ctx }),
280
+ makeClearPendingChangesTool({ workspaceRoot, ctx }),
281
+
282
+ // Tweaks 协议:agent 暴露 deck 专属可调参数 schema → 前端按 schema 渲染控件
283
+ makeExposeTweaksTool({ workspaceRoot, ctx }),
284
+
285
+ // 图片生成(gemini-3.1-flash-image-preview / Nano Banana 2,via NoDesk passthrough)
286
+ // 落档优先 sharedRoot/assets/generated/,fallback workspaceRoot/assets/generated/。
287
+ // 跨 session 共享靠 sessions/<sid>/assets softlink → shared/assets。
288
+ withTierGate(makeGenerateImageTool({ workspaceRoot, sharedRoot, ctx }), 'imageGen', projectId, ctx), // 先过日限,出图记 $0.20/张
289
+
290
+ // 抠图(rembg U²-Net,server 端 spawn .venv-rembg python subprocess)
291
+ // 任何 workspace 里的图都能抠,输出 RGBA PNG 到 assets/generated/<name>.png。
292
+ // 跟 generate_image 解耦:generate_image 只生图,想透明叠加单独调本工具。
293
+ makeRemoveBackgroundTool({ workspaceRoot, sharedRoot, projectId, ctx }),
294
+
295
+ // 工作台分区画布(2026-07-27):agent 协助摆放 —— 把产物/文档/deck 钉进
296
+ // 某 session 的工作区。写 board.json(board-store 单锁)+ 广播 board.updated。
297
+ makePinToBoardTool({ sharedRoot, projectId, ctx }),
298
+
299
+ // 关系线(2026-08-07):agent 把「这版改自那版」「这两个是对照」这类
300
+ // **只有它知道**的关系画到画布上。画布知道每个产物是什么,但不知道它们
301
+ // 之间是什么关系 —— 那是北极星(排出有版面感的布局)真正缺的那一块。
302
+ makeRelateOnBoardTool({ projectId, ctx }),
303
+ // agent 摆位四件(2026-08-14):看版面 / 语义摆位·立主角 / 落手写便签 / 归纳收纳
304
+ makeReadBoardTool({ projectId }),
305
+ makeArrangeOnBoardTool({ projectId, ctx }),
306
+ makeCreateOnBoardTool({ projectId, ctx }),
307
+ makeOrganizeBoardTool({ projectId, ctx }),
308
+
309
+ // 注:Phase Image-2 的 request_image_approval 工具已废弃(2026-05-06)。
310
+ // generate_image 的 CallToolResult 已返 image content block,前端自动渲染;
311
+ // agent 在 caption / 自然回话邀请反馈,下一轮用户 chat 即天然 gate。
312
+ ].map((t) => (
313
+ // SDK 用 _meta['anthropic/alwaysLoad'] 标记常驻(tool() 第 5 参的等价物,
314
+ // 集中在这打标避免改 16 个工具文件)
315
+ ALWAYS_LOAD_TOOLS.has(t.name)
316
+ ? { ...t, _meta: { ...t._meta, 'anthropic/alwaysLoad': true } }
317
+ : t
318
+ )).map((t) => (
319
+ // 本机能力闸(08-22):缺 chromium / LibreOffice / 钥匙 的工具,描述前缀「不可用 + 装法」、调用期拦住。
320
+ // 对照表在 capability-gate.js 一份;没探过(单测)原样放行
321
+ withCapabilityGate(t)
322
+ )).map((t) => (
323
+ // 参数标签泄漏消毒(2026-08-19):上游偶发把下一个参数吞进上一个字符串参数
324
+ // (</rationale><parameter name="scope">… 原样落进 tool_use.input,会话
325
+ // 008fe16c 4/4 实锤)。挂在出口包全部工具 —— 哪个工具中招看 recordIssue。
326
+ withParamSanitizer(t, { projectId, sessionId })
327
+ ));
328
+
329
+ const server = createSdkMcpServer({
330
+ name: 'nodesign',
331
+ version: '0.1.0',
332
+ tools,
333
+ });
334
+ // 开局契约自检用(2026-08-14 灭门案第 3 层):预期工具名从构造本 server 的
335
+ // 同一份 tools 数组上取,不另立第二份清单 —— 第二真相源会漂移。session-loop
336
+ // 收到 system:init 时拿它跟 SDK 实际注册进会话的工具对账。
337
+ server.toolNames = tools.map((t) => t.name);
338
+ return server;
339
+ }
@@ -0,0 +1,142 @@
1
+ /**
2
+ * mcp/param-sanitizer.js — 工具参数标签泄漏消毒(2026-08-19)
3
+ *
4
+ * 病根(jsonl 实锤,会话 008fe16c,claude-opus-5[1m],订阅直连非中转站):
5
+ * 模型把工具调用的闭合标签写错(用参数名当标签),上游解析器抢救时把下一个
6
+ * 参数整个吞进上一个字符串参数 —— tool_use.input 落地已经是:
7
+ *
8
+ * rationale: "…正文</rationale>\n<parameter name=\"scope\">庄家(对手)"
9
+ * scope: (缺失)
10
+ *
11
+ * 同会话 record_decision 4/4 全中,泄漏进了用户画布上的决策便利贴。上游改
12
+ * 不了,在自己边界拆回来。record_decision 只是被看见的那个 —— 同样的泄漏
13
+ * 落在别的工具的长文本参数上会静默进产物,所以消毒挂在 MCP server 出口
14
+ * (createNodesignMcpServer 包所有工具),不是只修一个工具。
15
+ *
16
+ * 判据收得窄,几乎不可能误伤正文里合法讨论 XML 的内容,三个条件全中才动手:
17
+ * 1. 字符串值里出现 `</{本参数名}>` 且其后紧跟 `<parameter name="X">`
18
+ * 2. X 是这个工具 schema 里真实存在的参数
19
+ * 3. X 在这次调用里缺失,且 schema 声明它是字符串型
20
+ * 拆完 recordIssue 记自动层一笔 —— 发生率必须可见,静默修复=永远不知道
21
+ * 上游好没好。消毒本身 fail-soft:它绝不能变成新的故障源。
22
+ */
23
+
24
+ import { recordIssue, signatureOf } from '../../lib/issues-store.js';
25
+
26
+ /** zod v4:拆掉 optional/nullable/default 的壳看里面是不是 string */
27
+ function unwrapsToString(zodType) {
28
+ let t = zodType;
29
+ for (let i = 0; i < 6 && t; i += 1) {
30
+ const def = t._zod?.def;
31
+ if (!def) return false;
32
+ if (def.type === 'string') return true;
33
+ if (def.type === 'optional' || def.type === 'nullable' || def.type === 'default') {
34
+ t = def.innerType;
35
+ continue;
36
+ }
37
+ return false;
38
+ }
39
+ return false;
40
+ }
41
+
42
+ const escapeRe = (s) => s.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
43
+
44
+ /**
45
+ * 单个字符串值的泄漏拆解。返回 null(没泄漏)或
46
+ * { clean, recovered: { 参数名: 值 } }。
47
+ *
48
+ * 链式泄漏也拆:value 可能连吞多个参数(A</A><parameter name="B">b</B>
49
+ * <parameter name="C">c…),逐段切;段尾多出的 `</当前参数名>` 一并剥掉。
50
+ */
51
+ export function splitLeakedParams(paramName, value, { stringKeys, presentKeys }) {
52
+ if (typeof value !== 'string') return null;
53
+ const boundaryOf = (name, s, from) => {
54
+ const re = new RegExp(`</${escapeRe(name)}>\\s*<parameter name="([A-Za-z0-9_-]{1,64})">`, 'g');
55
+ re.lastIndex = from;
56
+ let m;
57
+ while ((m = re.exec(s)) !== null) {
58
+ const next = m[1];
59
+ if (stringKeys.has(next) && !presentKeys.has(next)) return { index: m.index, end: re.lastIndex, next };
60
+ // 命中了形状但下一个名字不合格 —— 不是我们认识的泄漏,继续找不如收手:
61
+ // 这种"半像"的情况宁可不动(判据要窄)。
62
+ return null;
63
+ }
64
+ return null;
65
+ };
66
+
67
+ const first = boundaryOf(paramName, value, 0);
68
+ if (!first) return null;
69
+
70
+ const recovered = {};
71
+ const clean = value.slice(0, first.index);
72
+ let curKey = first.next;
73
+ let rest = value.slice(first.end);
74
+ for (let hops = 0; hops < 8; hops += 1) {
75
+ const nb = boundaryOf(curKey, rest, 0);
76
+ if (!nb) break;
77
+ recovered[curKey] = rest.slice(0, nb.index);
78
+ curKey = nb.next;
79
+ rest = rest.slice(nb.end);
80
+ }
81
+ // 最后一段:剥掉可能挂在尾巴上的 `</当前参数名>`
82
+ recovered[curKey] = rest.replace(new RegExp(`\\s*</${escapeRe(curKey)}>\\s*$`), '');
83
+ return { clean, recovered };
84
+ }
85
+
86
+ /**
87
+ * 消毒一份 args(不改原对象)。返回 { args, leaks } —— leaks 为空数组表示没动。
88
+ * @param {object} args SDK 校验后的入参
89
+ * @param {object} rawShape tool() 的 inputSchema(zod raw shape)
90
+ */
91
+ export function desmearArgs(args, rawShape) {
92
+ const leaks = [];
93
+ if (!args || typeof args !== 'object' || !rawShape) return { args, leaks };
94
+ const stringKeys = new Set(Object.keys(rawShape).filter((k) => unwrapsToString(rawShape[k])));
95
+ const presentKeys = new Set(Object.keys(args).filter((k) => args[k] !== undefined));
96
+ let out = args;
97
+ for (const k of Object.keys(args)) {
98
+ if (!stringKeys.has(k)) continue;
99
+ const hit = splitLeakedParams(k, args[k], { stringKeys, presentKeys });
100
+ if (!hit) continue;
101
+ if (out === args) out = { ...args };
102
+ out[k] = hit.clean;
103
+ for (const [rk, rv] of Object.entries(hit.recovered)) {
104
+ out[rk] = rv;
105
+ presentKeys.add(rk);
106
+ }
107
+ leaks.push({ from: k, recovered: Object.keys(hit.recovered) });
108
+ }
109
+ return { args: out, leaks };
110
+ }
111
+
112
+ /**
113
+ * 包一个 tool() 定义:handler 前置消毒。fail-soft —— 消毒抛错就原样放行。
114
+ * @param {object} toolDef tool() 的返回值({ name, inputSchema, handler, … })
115
+ * @param {object} [deps] { projectId, sessionId } 供 recordIssue 归因
116
+ */
117
+ export function withParamSanitizer(toolDef, deps = {}) {
118
+ const inner = toolDef.handler;
119
+ if (typeof inner !== 'function' || !toolDef.inputSchema) return toolDef;
120
+ toolDef.handler = async (args, extra) => {
121
+ let finalArgs = args;
122
+ try {
123
+ const { args: cleaned, leaks } = desmearArgs(args, toolDef.inputSchema);
124
+ if (leaks.length) {
125
+ finalArgs = cleaned;
126
+ try {
127
+ recordIssue({
128
+ source: 'auto',
129
+ toolName: toolDef.name,
130
+ summary: `工具参数标签泄漏(上游解析):${toolDef.name} 的 ${leaks.map((l) => l.from).join(',')} 吞了 ${leaks.map((l) => l.recovered.join('+')).join(',')},已在边界拆回`,
131
+ detail: `模型把闭合标签写错、上游解析器把后续参数吞进前一个字符串参数。本层已恢复。leaks=${JSON.stringify(leaks)}`,
132
+ projectId: deps.projectId,
133
+ sessionId: deps.sessionId,
134
+ signature: signatureOf(`param-leak|${toolDef.name}`),
135
+ });
136
+ } catch { /* 记账失败不挡工具 */ }
137
+ }
138
+ } catch { /* 消毒自身故障 → 原样放行 */ }
139
+ return inner(finalArgs, extra);
140
+ };
141
+ return toolDef;
142
+ }
@@ -0,0 +1,124 @@
1
+ /**
2
+ * mcp/tools/arrange-on-board.js —— arrange_on_board(2026-08-14,agent 摆位批·写侧)
3
+ *
4
+ * 语义摆位,**不给裸坐标**(用户拍板):agent 说的是"把 A 摆到 B 旁边"这种
5
+ * 人话,坐标由这里算出来、写进跟用户拖拽**同一个 layout 权威**(board.objects
6
+ * 的 x/y)—— 布局引擎、用户、agent 三方从此不打架。撞上已有的卡就往下让
7
+ * (跟入座避让同一个精神),落盘带 by:'agent' 出处。
8
+ *
9
+ * feature/unfeature 立/撤主角:写 board.hero,前端 pickHero 的推断被它压过。
10
+ */
11
+
12
+ import { tool } from '@anthropic-ai/claude-agent-sdk';
13
+ import { z } from 'zod';
14
+ import { readBoard, patchBoard } from '../../../projects/board-store.js';
15
+ import { estimateSize } from '../../../lib/board-kind-sizes.js';
16
+ import { normalizeCanvasId, layerOf } from '../../../lib/canvas-id.js';
17
+
18
+ const GAP_X = 24;
19
+ const GAP_Y = 16;
20
+
21
+ const hit = (a, b) => !(a.x + a.w <= b.x || b.x + b.w <= a.x || a.y + a.h <= b.y || b.y + b.h <= a.y);
22
+
23
+ /** 撞卡往下让:同一层的既有条目当障碍(subject 自己除外) */
24
+ function nudgeDown(pos, size, obstacles) {
25
+ const r = { ...pos, ...size };
26
+ for (let guard = 0; guard < 60; guard += 1) {
27
+ const blocker = obstacles.find(o => hit(r, o));
28
+ if (!blocker) return { x: r.x, y: r.y };
29
+ r.y = blocker.y + blocker.h + GAP_Y;
30
+ }
31
+ return { x: r.x, y: r.y };
32
+ }
33
+
34
+ export function makeArrangeOnBoardTool({ projectId, ctx }) {
35
+ return tool(
36
+ 'arrange_on_board',
37
+ `Move things on the canvas by MEANING, not pixels. Read the chart first (read_board).
38
+
39
+ Actions:
40
+ - beside: put subject immediately right of anchor (same row)
41
+ - below: put subject under anchor (same column)
42
+ - feature: make subject THE hero of the desktop (rendered one size up); no anchor
43
+ - unfeature: drop the explicit hero, back to automatic judgment; no subject needed
44
+
45
+ Rules the tool enforces: subject and anchor must live in the same folder layer
46
+ (move files with mv first); collisions push the subject downward; your placements
47
+ are marked by:'agent' so the user can tell who seated what. The user can always
48
+ drag things afterwards — their move wins, do not fight it.`,
49
+ {
50
+ action: z.enum(['beside', 'below', 'feature', 'unfeature'])
51
+ .describe('What to do (see list above)'),
52
+ subject: z.string().min(1).max(300).optional()
53
+ .describe('Canvas id of the thing to move / feature (required except for unfeature)'),
54
+ anchor: z.string().min(1).max(300).optional()
55
+ .describe('Canvas id to place the subject relative to (required for beside/below)'),
56
+ },
57
+ async ({ action, subject: rawSubject, anchor: rawAnchor }) => {
58
+ if (!projectId) {
59
+ return { content: [{ type: 'text', text: 'No project bound.' }], isError: true };
60
+ }
61
+ const err = (t) => ({ content: [{ type: 'text', text: t }], isError: true });
62
+
63
+ if (action === 'unfeature') {
64
+ await patchBoard(projectId, { hero: null });
65
+ try { ctx?.emit?.({ type: 'board.updated', sessionId: null, summary: '撤了显式主角' }); } catch { /* fail-soft */ }
66
+ return { content: [{ type: 'text', text: 'Hero override cleared — automatic judgment resumes.' }] };
67
+ }
68
+
69
+ const subject = normalizeCanvasId(rawSubject);
70
+ if (!subject) return err('subject id 不合法。');
71
+
72
+ if (action === 'feature') {
73
+ await patchBoard(projectId, { hero: subject });
74
+ try { ctx?.emit?.({ type: 'board.updated', sessionId: null, summary: `立了主角:${subject}` }); } catch { /* fail-soft */ }
75
+ return { content: [{ type: 'text', text: `Featured: ${subject} now renders as the hero.` }] };
76
+ }
77
+
78
+ const anchor = normalizeCanvasId(rawAnchor);
79
+ if (!anchor) return err('beside/below 需要 anchor。');
80
+ if (anchor === subject) return err('subject 和 anchor 是同一件东西。');
81
+
82
+ const board = await readBoard(projectId);
83
+ const anchorEntry = board.objects?.[anchor];
84
+ if (!anchorEntry || !Number.isFinite(anchorEntry.x)) {
85
+ return err(`锚点 ${anchor} 还没有座位(read_board 里看不到的东西没法当锚)。`);
86
+ }
87
+ const known = new Set(Object.keys(board.zones || {}));
88
+ const subjectEntry = board.objects?.[subject] || null;
89
+ const anchorLayer = layerOf(anchor, anchorEntry, known);
90
+ if (subjectEntry) {
91
+ const subjectLayer = layerOf(subject, subjectEntry, known);
92
+ if (subjectLayer !== anchorLayer) {
93
+ return err(`不同层(${subjectLayer || '根'} vs ${anchorLayer || '根'}):先 mv 文件到同一个文件夹,画布跟着走。`);
94
+ }
95
+ }
96
+
97
+ const aSize = estimateSize(anchor, anchorEntry);
98
+ const sSize = estimateSize(subject, subjectEntry);
99
+ const want = action === 'beside'
100
+ ? { x: Math.round(anchorEntry.x + aSize.w + GAP_X), y: Math.round(anchorEntry.y) }
101
+ : { x: Math.round(anchorEntry.x), y: Math.round(anchorEntry.y + aSize.h + GAP_Y) };
102
+
103
+ const obstacles = Object.entries(board.objects || {})
104
+ .filter(([id, e]) => id !== subject && Number.isFinite(e?.x)
105
+ && layerOf(id, e, known) === anchorLayer)
106
+ .map(([id, e]) => ({ ...estimateSize(id, e), x: e.x, y: e.y }));
107
+ const pos = nudgeDown(want, sSize, obstacles);
108
+
109
+ await patchBoard(projectId, {
110
+ objects: { [subject]: { ...(subjectEntry || {}), x: pos.x, y: pos.y, by: 'agent' } },
111
+ });
112
+ try {
113
+ ctx?.emit?.({ type: 'board.updated', sessionId: null, summary: `摆了 ${subject}` });
114
+ } catch { /* fail-soft */ }
115
+ return {
116
+ content: [{
117
+ type: 'text',
118
+ text: `Placed ${subject} ${action} ${anchor} at (${pos.x},${pos.y})`
119
+ + (pos.y !== want.y ? ' (nudged down to avoid overlap)' : '') + '.',
120
+ }],
121
+ };
122
+ },
123
+ );
124
+ }