@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,164 @@
1
+ /**
2
+ * server/engine/agent/session-model.js — 会话跑在哪个模型上,这里是唯一答案
3
+ *
4
+ * 在此之前这个事实有 5 个读法和 3 个写法,各不相同:
5
+ *
6
+ * 读:session-loop 内联一条 `cfg.model || env || 'kimi-k2.6'`(真正生效的那条);
7
+ * turn.js 又抄了一遍同样的链算 prevEffective;canvas.js 的 readSessionConfig
8
+ * 只读文件不带 env 兜底;sessions.js 问 `querySession.ctx?.appModel` —— 而
9
+ * activeQuerySessions 的 ctx 字段**从注册起就是 null,没有任何地方填过**,
10
+ * 那一支永远走不到;前端干脆只信自己的 localStorage。
11
+ * 写:turn.js 的 body.model(写文件 + 空闲时重启 query);canvas.js 的
12
+ * PATCH /config(任意字段透传,写了文件但不重启 query,跑着的会话不认账);
13
+ * POST /runs/:runId/model 调 SDK setModel(只改运行时,不落文件,下次 resume 变回去)。
14
+ *
15
+ * 同一个事实有五份算法,就一定会有对不上的时候 —— 比如按钮上写着 Sonnet、
16
+ * 会话实际跑 Opus,或者选了「默认」什么也没发生。
17
+ *
18
+ * 优先级(跟 session-loop 原来那条完全一致,只是搬到了一处):
19
+ * 调用方显式指定 > session-config.json 的 model > NODESIGN_MODEL > 兜底常量
20
+ *
21
+ * 「覆盖」(override) 指 session-config.json 里那一项。清掉它 = 回到全局默认,
22
+ * 这是「默认」那个选项该做的事 —— 以前它什么也不做。
23
+ */
24
+
25
+ import path from 'path';
26
+ import { promises as fs } from 'fs';
27
+ import { getQuerySession, closeQuerySession } from '../runs/active-runs.js';
28
+
29
+ const CONFIG_NAME = 'session-config.json';
30
+
31
+ /**
32
+ * NODESIGN_MODEL 没配时的兜底。原值是 kimi 网关时代的 'kimi-k2.6',08-21 深夜 kimi 行从模型表删除后
33
+ * 改成订阅默认行(一个不在表里的名字会被 resolveModelRoute 当订阅模型,兜底必须是真名);
34
+ * 正常部署 .env 里一定有 NODESIGN_MODEL,走不到这里。
35
+ */
36
+ const LEGACY_FALLBACK_MODEL = 'claude-sonnet-5[1m]';
37
+
38
+ /** 全局默认模型(不含会话覆盖)。每次现读 env —— 进程内改 env 也能生效 */
39
+ export function defaultModel() {
40
+ return process.env.NODESIGN_MODEL || LEGACY_FALLBACK_MODEL;
41
+ }
42
+
43
+ // ── config 文件的读改写串行化 ──
44
+ // turn.js(发消息时)和模型接口(用户点 picker 时)可能同时改同一个文件,
45
+ // 各自 read-modify-write 会互相吃掉对方的字段。按路径排队。
46
+ /** @type {Map<string, Promise<any>>} */
47
+ const configLocks = new Map();
48
+
49
+ function withConfigLock(sessionRoot, fn) {
50
+ const key = path.resolve(sessionRoot);
51
+ const prev = configLocks.get(key) || Promise.resolve();
52
+ const next = prev.then(fn, fn);
53
+ // 链上留的是"完成信号",不是结果,避免上一个的异常传染下一个
54
+ configLocks.set(key, next.then(() => {}, () => {}));
55
+ return next;
56
+ }
57
+
58
+ /**
59
+ * ⚠️ 参数是**会话私档目录**(`<工作区>/.nd/<sid>/`),不是工作区根。
60
+ *
61
+ * 2026-08-07 扁平化之前两者是同一个目录(一个会话一个沙盒),所以这里叫
62
+ * `sessionRoot` 也没错。现在工作区是全项目共用的 —— 传工作区根进来的话,
63
+ * 一个项目里所有会话会共用一份 session-config.json,**在一条会话里换模型,
64
+ * 另一条会话跟着变**。调用方一律传 getSessionMetaDir(pid, sid)。
65
+ */
66
+ function configPath(sessionMetaDir) {
67
+ // 传错了要当场炸,不能默默共用一份配置。这个错误的表现是"在一条会话里换了
68
+ // 模型,另一条会话也变了"——没人会把它跟一个路径参数联系起来,静默半年都可能。
69
+ if (!/[/\\]\.nd[/\\][0-9a-f-]{36}$/i.test(path.resolve(sessionMetaDir))) {
70
+ throw new Error(
71
+ `[session-model] 需要会话私档目录(<工作区>/.nd/<sid>/),拿到的是 ${sessionMetaDir}。`
72
+ + '用 getSessionMetaDir(pid, sid),别传工作区根。',
73
+ );
74
+ }
75
+ return path.join(sessionMetaDir, CONFIG_NAME);
76
+ }
77
+
78
+ /** 读整份 config(文件不存在 / 坏了 → {})。不带任何默认字段填充 */
79
+ export async function readSessionConfigFile(sessionRoot) {
80
+ try {
81
+ const raw = await fs.readFile(configPath(sessionRoot), 'utf8');
82
+ const cfg = JSON.parse(raw);
83
+ if (!cfg || typeof cfg !== 'object' || Array.isArray(cfg)) return {};
84
+ return cfg;
85
+ } catch {
86
+ return {};
87
+ }
88
+ }
89
+
90
+ /** 会话自己的模型覆盖;没设过返回 null */
91
+ export async function readSessionModelOverride(sessionRoot) {
92
+ const cfg = await readSessionConfigFile(sessionRoot);
93
+ return typeof cfg.model === 'string' && cfg.model.trim() ? cfg.model.trim() : null;
94
+ }
95
+
96
+ /**
97
+ * 这个会话实际会跑的模型。
98
+ * @returns {Promise<{ model: string, override: string|null, fallback: string }>}
99
+ * override = 用户在这个会话里选过的;null 表示跟随全局默认
100
+ */
101
+ export async function resolveSessionModel(sessionRoot) {
102
+ const override = await readSessionModelOverride(sessionRoot);
103
+ const fallback = defaultModel();
104
+ return { model: override || fallback, override, fallback };
105
+ }
106
+
107
+ /**
108
+ * 写 / 清会话的模型覆盖。
109
+ *
110
+ * @param {string} sessionRoot
111
+ * @param {string|null} model 字符串 = 设成它;null = **清掉覆盖**,回到全局默认
112
+ * @returns {Promise<{ model: string, override: string|null, changed: boolean, previous: string }>}
113
+ * changed 指"实际生效的模型变了没有" —— 调用方据此决定要不要重启空闲 query。
114
+ * 把覆盖设成跟全局默认一样的值不算变(跑着的会话本来就是那个模型)。
115
+ */
116
+ export async function writeSessionModelOverride(sessionRoot, model) {
117
+ const wanted = typeof model === 'string' && model.trim() ? model.trim() : null;
118
+ return withConfigLock(sessionRoot, async () => {
119
+ const cfg = await readSessionConfigFile(sessionRoot);
120
+ const fallback = defaultModel();
121
+ const previous = (typeof cfg.model === 'string' && cfg.model.trim()) ? cfg.model.trim() : fallback;
122
+ const next = { ...cfg, updatedAt: new Date().toISOString() };
123
+ if (wanted) next.model = wanted;
124
+ else delete next.model; // 清覆盖 = 删字段,不是写 null
125
+ const effective = wanted || fallback;
126
+ if (previous !== effective || (cfg.model || null) !== wanted) {
127
+ await fs.writeFile(configPath(sessionRoot), JSON.stringify(next, null, 2), 'utf8');
128
+ }
129
+ return { model: effective, override: wanted, changed: previous !== effective, previous };
130
+ });
131
+ }
132
+
133
+ /**
134
+ * 改模型的**完整**动作:写配置 + 让已经跑着的会话认账。
135
+ *
136
+ * 这两步必须绑在一起。以前 PATCH /config 只写文件不碰 query,配置说 Opus、
137
+ * 跑着的进程还是 Sonnet,要等下次 resume 才对上,中间这段时间界面和事实分家。
138
+ *
139
+ * 空闲时才关 query:turn 正在跑就只落配置,等这一轮结束后自然重启(前端 picker
140
+ * 在运行中是禁用的,这里是兜底)。closeQuerySession 之后下一条消息走
141
+ * startNewRunSession,从同一份 jsonl 以新模型 resume —— 对话内容无损,上下文
142
+ * 窗口 / spoofing alias / thinking 配置全按新模型重算。
143
+ *
144
+ * @param {string} sessionId
145
+ * @param {string} sessionRoot
146
+ * @param {string|null} model null = 清掉覆盖
147
+ * @param {string} reason 日志用
148
+ */
149
+ export async function applySessionModel(sessionId, sessionRoot, model, reason = 'api') {
150
+ const result = await writeSessionModelOverride(sessionRoot, model);
151
+ let restarted = false;
152
+ if (result.changed) {
153
+ const qs = getQuerySession(sessionId);
154
+ if (qs && !qs.currentRunId) {
155
+ closeQuerySession(sessionId, 'model_change');
156
+ restarted = true;
157
+ }
158
+ console.info(
159
+ `[session-model] sid=${sessionId.slice(0, 8)} ${result.previous} → ${result.model}`
160
+ + ` (${reason}${restarted ? ', query restarted' : ', applies on next resume'})`,
161
+ );
162
+ }
163
+ return { ...result, restarted };
164
+ }
@@ -0,0 +1,204 @@
1
+ /**
2
+ * skill.js — Skill 加载器(YAML frontmatter + Markdown body)
3
+ *
4
+ * 目录结构(2026-05-18 起,对齐 Claude Code plugin convention):
5
+ * server/engine/plugins/nodesign/
6
+ * .claude-plugin/plugin.json — plugin 元数据
7
+ * skills/<skill-id>/SKILL.md — 各 skill
8
+ * skills/<skill-id>/<其他起手文件>
9
+ *
10
+ * SKILL.md 格式(YAML frontmatter + body):
11
+ * ---
12
+ * name: deskskill-engine-mini
13
+ * version: 0.0.1
14
+ * description: 给 LLM 看的"什么时候用我"的描述
15
+ * ---
16
+ *
17
+ * # System Prompt
18
+ *
19
+ * 你是一个 deck 设计 agent...(任意 markdown)
20
+ *
21
+ * 本模块职责:
22
+ * - 解析 SKILL.md 的 YAML frontmatter + body(loadSkill)
23
+ * - 把 skill 起手文件(canvas.template.html 等)拷进 session cwd(ensureSkillStarterFiles)
24
+ *
25
+ * 注:批量扫 plugin 下所有 skill 走 plugin-loader.js `listInstalledPluginsDetailed`,
26
+ * 不在本模块(API 层 server/api/skills.js 用那个)。
27
+ *
28
+ * SDK 集成(在 session-loop.js):
29
+ * - plugins: [{ type: 'local', path: PLUGIN_ROOT }] → 让 SDK 识别本 plugin
30
+ * - skills: [<skill-id>] → 让 SDK 把 description 注入 system prompt
31
+ * - SKILL.md body 通过 SDK 内置 Skill 工具按需加载(不再走 systemPrompt.append 恒驻)
32
+ */
33
+
34
+ import { promises as fs } from 'fs';
35
+ import path from 'path';
36
+ import { fileURLToPath } from 'url';
37
+
38
+ const __dirname = path.dirname(fileURLToPath(import.meta.url));
39
+
40
+ /** Plugin 根目录(含 .claude-plugin/plugin.json + skills/)*/
41
+ export const PLUGIN_ROOT = path.resolve(
42
+ process.env.NODESIGN_PLUGIN_DIR || path.join(__dirname, '../plugins/nodesign')
43
+ );
44
+
45
+ /** Skills 子目录(plugin convention:<plugin>/skills/<skill-id>/SKILL.md)*/
46
+ export const SKILLS_ROOT = path.resolve(
47
+ process.env.NODESIGN_SKILLS_DIR || path.join(PLUGIN_ROOT, 'skills')
48
+ );
49
+
50
+ /**
51
+ * 加载一个 skill。返回 { id, name, version, description, systemPrompt, raw }。
52
+ * 找不到时抛 Error(code='SKILL_NOT_FOUND')。
53
+ */
54
+ export async function loadSkill(skillId) {
55
+ if (!skillId || typeof skillId !== 'string') {
56
+ throw Object.assign(new Error('loadSkill: skillId required'), { code: 'INVALID_SKILL_ID' });
57
+ }
58
+
59
+ const skillDir = path.join(SKILLS_ROOT, skillId);
60
+ const skillFile = path.join(skillDir, 'SKILL.md');
61
+
62
+ let raw;
63
+ try {
64
+ raw = await fs.readFile(skillFile, 'utf8');
65
+ } catch (err) {
66
+ if (err.code === 'ENOENT') {
67
+ throw Object.assign(
68
+ new Error(`skill not found: ${skillId} (expected at ${skillFile})`),
69
+ { code: 'SKILL_NOT_FOUND' }
70
+ );
71
+ }
72
+ throw err;
73
+ }
74
+
75
+ const { frontmatter, body } = parseFrontmatter(raw);
76
+
77
+ return {
78
+ id: skillId,
79
+ name: frontmatter.name || skillId,
80
+ version: frontmatter.version || '0.0.0',
81
+ description: frontmatter.description || '',
82
+ systemPrompt: body.trim(),
83
+ frontmatter,
84
+ raw,
85
+ };
86
+ }
87
+
88
+ /**
89
+ * 把 skill 自带的工作区起手文件(目前主要是 canvas.template.html)拷贝
90
+ * 进 session cwd,让 agent 能直接 `Read canvas.template.html` 而不必去
91
+ * 仓库相对路径找(agent 看不到 server/engine/plugins/nodesign/skills/)。
92
+ *
93
+ * **拷贝不软链**:
94
+ * - 跨平台稳(Windows symlink 要 admin / Linux bwrap 不解析 symlink)
95
+ * - 防 agent 误 Edit 改源模板(影响其他 session)
96
+ * - session 是一次性沙盒,拷贝快照语义合理
97
+ *
98
+ * **幂等**:文件已存在就不覆盖(agent 改过的、或上次 session init 拷过的不动)。
99
+ *
100
+ * **起手文件是自动发现的**(2026-07-28 从硬编码 `['canvas.template.html']` 改来):
101
+ * skill 目录里凡是文件名带 `.template.` 的都算起手文件。加第二个 skill
102
+ * (站点)时不必再回来改这里 —— 硬编码一个文件名意味着新 skill 的模板永远
103
+ * 拷不出来,而且是静默的:agent 只会 `Read` 失败然后凭空写。
104
+ *
105
+ * @param {string} sessionRoot - sessions/<sid>/ 绝对路径
106
+ * @param {string} skillId - 同 loadSkill 的 skillId
107
+ * @returns {Promise<{ copied: string[], skipped: string[] }>}
108
+ */
109
+ /** 内置 plugin 里现有的 skill id(目录名即 id) */
110
+ export async function listSkillIds() {
111
+ try {
112
+ const entries = await fs.readdir(SKILLS_ROOT, { withFileTypes: true });
113
+ return entries.filter(e => e.isDirectory() && !e.name.startsWith('.')).map(e => e.name);
114
+ } catch { return []; }
115
+ }
116
+
117
+ export async function listSkillStarterFiles(skillId) {
118
+ if (!skillId) return [];
119
+ try {
120
+ const entries = await fs.readdir(path.join(SKILLS_ROOT, skillId), { withFileTypes: true });
121
+ return entries
122
+ .filter(e => e.isFile() && e.name.includes('.template.'))
123
+ .map(e => e.name)
124
+ .sort();
125
+ } catch { return []; }
126
+ }
127
+
128
+ export async function ensureSkillStarterFiles(sessionRoot, skillId) {
129
+ const result = { copied: [], skipped: [] };
130
+ if (!sessionRoot || !skillId) return result;
131
+
132
+ const skillDir = path.join(SKILLS_ROOT, skillId);
133
+ const starterFiles = await listSkillStarterFiles(skillId);
134
+
135
+ for (const name of starterFiles) {
136
+ const src = path.join(skillDir, name);
137
+ const dst = path.join(sessionRoot, name);
138
+
139
+ let srcExists = false;
140
+ try {
141
+ await fs.access(src);
142
+ srcExists = true;
143
+ } catch { /* skill 没这个起手文件 */ }
144
+ if (!srcExists) continue;
145
+
146
+ let dstExists = false;
147
+ try {
148
+ await fs.access(dst);
149
+ dstExists = true;
150
+ } catch { /* dst 不存在,需要拷 */ }
151
+ if (dstExists) {
152
+ result.skipped.push(name);
153
+ continue;
154
+ }
155
+
156
+ try {
157
+ await fs.copyFile(src, dst);
158
+ result.copied.push(name);
159
+ } catch (err) {
160
+ console.warn(
161
+ `[skill] copy ${name} failed (${err.code || err.message}); `
162
+ + `agent will not have it in cwd`,
163
+ );
164
+ }
165
+ }
166
+
167
+ return result;
168
+ }
169
+
170
+ // ── frontmatter 解析(极简 YAML:只支持 key: value)──
171
+
172
+ const FRONTMATTER_RE = /^---\s*\n([\s\S]*?)\n---\s*\n?([\s\S]*)$/;
173
+
174
+ /**
175
+ * 解析 SKILL.md 的 frontmatter + body。
176
+ * 不支持嵌套 / 多行 / list — 只接受 `key: value` 形态。
177
+ *
178
+ * 没 frontmatter 的文件:返回 { frontmatter: {}, body: <整个文件> }
179
+ */
180
+ function parseFrontmatter(raw) {
181
+ const m = FRONTMATTER_RE.exec(raw);
182
+ if (!m) return { frontmatter: {}, body: raw };
183
+
184
+ const [, yamlText, body] = m;
185
+ const frontmatter = {};
186
+ for (const line of yamlText.split('\n')) {
187
+ const trimmed = line.trim();
188
+ if (!trimmed || trimmed.startsWith('#')) continue;
189
+ const colonIdx = trimmed.indexOf(':');
190
+ if (colonIdx < 1) continue;
191
+ const key = trimmed.slice(0, colonIdx).trim();
192
+ let value = trimmed.slice(colonIdx + 1).trim();
193
+ // 去掉成对引号
194
+ if ((value.startsWith('"') && value.endsWith('"')) ||
195
+ (value.startsWith("'") && value.endsWith("'"))) {
196
+ value = value.slice(1, -1);
197
+ }
198
+ frontmatter[key] = value;
199
+ }
200
+ return { frontmatter, body: body || '' };
201
+ }
202
+
203
+ // 暴露供单测
204
+ export { parseFrontmatter };
@@ -0,0 +1,86 @@
1
+ /**
2
+ * engine/agent/system-prompts.js — 系统提示词的加载与渲染(2026-08-19 从
3
+ * agent-shared.js 拆出)。
4
+ *
5
+ * 拆的理由是行数棘轮:"想给胖文件加功能,先拆出去一块再写"。而这一块本来就是
6
+ * agent-shared 里最不像它的东西 —— 那个文件其余部分是 SDK 消息翻译层和 options
7
+ * 默认值,跟"提示词长什么样"零耦合。放一起的唯一原因是历史。
8
+ *
9
+ * 这里的东西只有一个消费方(session-loop 组装 SDK options 时),但它决定了线上
10
+ * 每一次对话的系统提示词,改错不会报错、只会让模型行为悄悄变一档 —— 所以两份
11
+ * markdown 的加载都带兜底日志,底线段的切换带加载期断言。
12
+ */
13
+
14
+ import path from 'node:path';
15
+ import fs from 'node:fs';
16
+ import { fileURLToPath } from 'node:url';
17
+
18
+ const __dirname = path.dirname(fileURLToPath(import.meta.url));
19
+
20
+ // NoDesign agent 通用 prelude —— append 在 SDK preset 'claude_code' 之后、
21
+ // SKILL.md 之前。教 Claude Code 工具用法 + NoDesign 工作台共性约束(assets
22
+ // 必看 / 信息不足先问 / git 不自管)。所有 NoDesign agent 共用,跟具体 skill
23
+ // 解耦。模块级 readFileSync 一次性读入,避免每次 turn 重读。
24
+ export const NODESIGN_PRELUDE = (() => {
25
+ try {
26
+ // CRLF 归一:Windows git autocrlf 会把 md checkout 成 CRLF,下面 POLICY_BLOCK 按 `\n` 切块
27
+ // (.gitattributes 已钉 LF,这里是第二道保险,老 checkout 不重拉也能起)
28
+ return fs.readFileSync(path.join(__dirname, 'prompts/nodesign-prelude.md'), 'utf8').replace(/\r\n?/g, '\n').trim();
29
+ } catch (err) {
30
+ console.warn('[system-prompts] failed to load nodesign-prelude.md:', err.message);
31
+ return '';
32
+ }
33
+ })();
34
+
35
+ // 成人段与外审档联动(2026-08-08):一个旋钮同时管 GPT 外审和提示词口径,
36
+ // 两边永远一致。off=明文允许(站主/获批账号)、loose=原默认、strict=收敛。
37
+ const ADULT_POLICY = {
38
+ off: '**成人内容明确允许,不用迂回。** 露骨的性与亲密描写照常写、写到位,不留白、不淡化、\n不换成暗示手法带过,前后也不加提醒和免责声明。用户没要求收敛就不要自己收敛,写完接着干活。\n**写实风格照做** —— 照片级真实感的虚构人物不是真人,画得像不构成任何问题,生图同理。\n不做的只有两种:涉及未成年人;把可指认的现实个人(点名的公众人物、用户给的真人照片)\n放进这类内容。除此之外不要自己发明别的边界。',
39
+ loose: '成人向的亲密情节可以写,涉及未成年人是上面那条绝对红线,真实存在的个人也不写。',
40
+ strict: '露骨的成人内容在这个账号档位下不写——涉及时收敛处理、用留白带过。',
41
+ };
42
+
43
+ /**
44
+ * 「底线」那一节在 prelude 里有**两份并排写着**的版本,标记块框起来:
45
+ * `nd:policy:full`(对外开放平台的完整产物政策)和 `nd:policy:min`。渲染时留一份、
46
+ * 删一份,两个标记本身永远不进模型上下文。
47
+ *
48
+ * 为什么用显式标记而不是按标题正则切:靠 `## 底线` 到下一个 `##` 去猜边界的话,
49
+ * 以后谁在这节里加个三级标题,剥离就会剥掉半截 —— 而且不会报错,只会让线上某条
50
+ * 路径的提示词悄悄少一段。标记块是写死的边界,配下面的加载期断言,切错当场炸。
51
+ */
52
+ const POLICY_BLOCK = /<!-- nd:policy:(full|min):start -->\n([\s\S]*?)<!-- nd:policy:\1:end -->\n/g;
53
+
54
+ // 加载期断言:两份都必须在。少一份说明有人编辑 prelude 时把标记删了,那时候
55
+ // 正则会静默退化成"一份都不删"(uncensored 路径拿到完整底线)或"整节消失"。
56
+ {
57
+ const found = [...NODESIGN_PRELUDE.matchAll(POLICY_BLOCK)].map((m) => m[1]);
58
+ for (const want of ['full', 'min']) {
59
+ if (NODESIGN_PRELUDE && !found.includes(want)) {
60
+ throw new Error(`[system-prompts] nodesign-prelude.md 缺少 nd:policy:${want} 标记块 —— 提示词渲染会静默走错版本`);
61
+ }
62
+ }
63
+ }
64
+
65
+ /**
66
+ * 渲染 prelude。
67
+ *
68
+ * @param {'off'|'loose'|'strict'} level 成人段档位(moderation.levelFor 算出来的)
69
+ * @param {{uncensored?: boolean}} opts
70
+ * `uncensored: true` 时留 `nd:policy:min` 那份 —— 本地无审查权重走这条
71
+ * (model-context 表里的 `uncensored` 位,今天只有 qwen3.8-27b)。
72
+ *
73
+ * 这不是"把成人档位调到最宽":off 档改的只是成人段一句话,整节产物政策照旧在。
74
+ * min 版是**整节换掉**,站主 08-19 拍板 —— 那节的前提是"对外开放、产物能一键挂
75
+ * 到站主域名下",而这条路跑在自己租的盒子上、只对获批账号开、产物不外发,
76
+ * 前提不成立。留下的一条不随档位变,也不随谁在用变。
77
+ *
78
+ * ⚠️ 默认 false。调用方拿不到模型名时落**完整**那份,绝不落 min。
79
+ */
80
+ export function renderPrelude(level = 'loose', opts = {}) {
81
+ const keep = opts.uncensored === true ? 'min' : 'full';
82
+ return NODESIGN_PRELUDE
83
+ .replace(POLICY_BLOCK, (_all, which, body) => (which === keep ? body : ''))
84
+ .replace('{{ADULT_POLICY}}', ADULT_POLICY[level] || ADULT_POLICY.loose)
85
+ .trim();
86
+ }
@@ -0,0 +1,78 @@
1
+ /**
2
+ * task-events.js — SDK 统一任务消息族(task_*)的收口翻译(2026-08-18,
3
+ * 从 agent-shared.js handleSystemMessage 拆出)
4
+ *
5
+ * SDK 0.3.x 起「任务」统一化:task_* 消息不再只代表 Task 子代理 —— 后台
6
+ * Bash(task_type 'local_bash')、Workflow('local_workflow')等也走同一条。
7
+ * 我们 run.task.* 事件的语义是「子代理」,在这个咽喉点收口:只放真子代理
8
+ * 过去,其余不进事件流。不收口的话每条 bash 命令都会在前端顶着「子代理」
9
+ * 的名头出现(舞台便利贴/在场徽记/侧栏状态位全线噪音,2026-08-18 事故)。
10
+ */
11
+
12
+ import { Events } from './events.js';
13
+ import { recordTaskNotification } from './hooks/post-subagent-report.js';
14
+
15
+ /**
16
+ * 这条 task_* 消息说的是不是真子代理(Task 工具)。靠 subagent_type /
17
+ * task_type 区分;skip_transcript 是 SDK 标的 ambient/housekeeping 任务,
18
+ * 按 SDK 约定对用户隐藏。
19
+ */
20
+ export function isSubagentTask(msg) {
21
+ if (msg.skip_transcript) return false;
22
+ return !!msg.subagent_type || msg.task_type === 'local_agent';
23
+ }
24
+
25
+ /**
26
+ * 本轮已放行的子代理 task_id —— task_progress / task_notification 不带
27
+ * subagent_type,靠它过滤非子代理任务。挂在 ctx 上惰性建:task_* 消息
28
+ * 不带 parent_tool_use_id,进来的恒是真 ctx(不是子代理的派生 ctx),
29
+ * own property 不会写丢。
30
+ */
31
+ function subagentTaskIds(ctx) {
32
+ if (!ctx._subagentTaskIds) ctx._subagentTaskIds = new Set();
33
+ return ctx._subagentTaskIds;
34
+ }
35
+
36
+ /** handleSystemMessage 的 task_* 四兄弟统一入口 */
37
+ export function handleTaskMessage(ctx, msg) {
38
+ switch (msg.subtype) {
39
+ case 'task_started':
40
+ if (!isSubagentTask(msg)) return;
41
+ subagentTaskIds(ctx).add(msg.task_id);
42
+ ctx.emit(Events.taskStarted(
43
+ msg.task_id, msg.description, msg.subagent_type, msg.task_type, msg.prompt, msg.tool_use_id,
44
+ ));
45
+ return;
46
+
47
+ case 'task_progress':
48
+ // agentProgressSummaries: true 时每 ~30s 一次:"正在调整字号节奏" 之类
49
+ if (!subagentTaskIds(ctx).has(msg.task_id)) return;
50
+ ctx.emit(Events.taskProgress(
51
+ msg.task_id, msg.description, msg.summary, msg.last_tool_name, msg.usage, msg.tool_use_id,
52
+ ));
53
+ return;
54
+
55
+ case 'task_updated':
56
+ if (!subagentTaskIds(ctx).has(msg.task_id)) return;
57
+ ctx.emit(Events.taskUpdated(msg.task_id, msg.patch, msg.tool_use_id));
58
+ return;
59
+
60
+ case 'task_notification':
61
+ // 收尾用量记账不分任务类型(进 metadata 可观测,不进主 token 列 ——
62
+ // 防止与 result.usage 双重计数,详见 context.absorbSubagentUsage):
63
+ // 将来若接 workflow 之类带真用量的本地任务,账也不能漏。
64
+ ctx.absorbSubagentUsage?.(msg.usage);
65
+ // 报告丢失的兜底也在闸**前**:把 output_file 记下来,PostToolUse(Agent)
66
+ // 的 handler 在报告看起来是空的时候把转录路径递给主 agent(见
67
+ // hooks/post-subagent-report.js)。它按 tool_use_id 查、只被 Agent 工具
68
+ // 问到,多记非子代理任务无害;安全网不该依赖过滤闸的状态(存储有界 200 条)。
69
+ recordTaskNotification(msg);
70
+ if (!subagentTaskIds(ctx).has(msg.task_id)) return;
71
+ ctx.emit(Events.taskNotification(
72
+ msg.task_id, msg.status, msg.summary, msg.usage, msg.tool_use_id,
73
+ ));
74
+ return;
75
+
76
+ default:
77
+ }
78
+ }
@@ -0,0 +1,132 @@
1
+ # ds-extractor
2
+
3
+ You are a design system extractor. Given a finished `canvas.html`, you
4
+ read the markup + inline styles + computed style intent, and emit a
5
+ **Design System** JSON document that captures color, typography, spacing,
6
+ shadow, radius — plus recurring composition idioms.
7
+
8
+ ## Your one job
9
+
10
+ When invoked, you:
11
+
12
+ 1. **Read `canvas.html`** (the only mandatory file). If `spec.json`
13
+ exists, also read it for design intent context — but the canvas is
14
+ the source of truth.
15
+ 2. **Identify the actual visual rules** by looking at the markup and
16
+ styles. Don't invent tokens that aren't present; don't omit ones
17
+ that are clearly used.
18
+ 3. **Output one single JSON document** matching the schema below. No
19
+ commentary, no markdown — just the JSON.
20
+
21
+ You do NOT modify any file. You do NOT take screenshots (this is a
22
+ structural / textual analysis, not a visual one — `vision-checker` is
23
+ the agent for visual review).
24
+
25
+ ## Output schema
26
+
27
+ The JSON you output MUST conform to this JSON Schema:
28
+
29
+ ```json
30
+ {
31
+ "type": "object",
32
+ "required": ["meta", "colors", "typography", "spacing", "radius", "shadow"],
33
+ "properties": {
34
+ "meta": {
35
+ "type": "object",
36
+ "required": ["extractedAt", "sourceArtifact"],
37
+ "properties": {
38
+ "extractedAt": "ISO 8601 timestamp",
39
+ "sourceArtifact": "relative path to the canvas (usually 'canvas.html')",
40
+ "summary": "one-sentence vibe description"
41
+ }
42
+ },
43
+ "colors": {
44
+ "primary": "hex (e.g., '#3366FF')",
45
+ "secondary": "hex (optional)",
46
+ "accent": "hex (optional)",
47
+ "text": { "default": "hex", "100..900": "hex (only steps actually used)" },
48
+ "background": { "default": "hex", "100..900": "hex (only steps actually used)" },
49
+ "border": { "default": "hex" },
50
+ "status": { "success": "hex", "warning": "hex", "error": "hex", "info": "hex" }
51
+ },
52
+ "typography": {
53
+ "families": { "sans": "...", "serif": "...", "mono": "..." },
54
+ "scale": {
55
+ "h1": { "fontSize": <number px>, "lineHeight": <number|string>, "weight": <100-900>, "letterSpacing": "..." },
56
+ "h2": { ... },
57
+ "h3": { ... },
58
+ "h4": { ... },
59
+ "body": { ... },
60
+ "small": { ... },
61
+ "caption": { ... }
62
+ }
63
+ },
64
+ "spacing": { "xs": <number>, "sm": <number>, "md": <number>, "lg": <number>, "xl": <number>, "xxl": <number>, "page": <number> },
65
+ "radius": { "sm": <number>, "md": <number>, "lg": <number>, "pill": <number> },
66
+ "shadow": { "sm": "<css-shadow-string>", "md": "...", "lg": "..." },
67
+ "idioms": ["string list — composition patterns repeated ≥2 times in the canvas"]
68
+ }
69
+ }
70
+ ```
71
+
72
+ The full schema is at `agents/schemas/design-system.json` if you need
73
+ to verify a field shape.
74
+
75
+ ## Extraction guidelines
76
+
77
+ ### Colors
78
+ - Walk every `style="color: ..."`, `background: ...`, `border: ...`,
79
+ CSS-var references in `<style>` blocks.
80
+ - Group by semantic role (text vs background vs border vs status).
81
+ - If you see a tonal scale (multiple shades of the same hue used for
82
+ hierarchy), populate the `100..900` keys. Skip steps that aren't used.
83
+ - If a color appears once and looks accidental, omit it. Keep the
84
+ exported palette tight.
85
+
86
+ ### Typography
87
+ - Read `font-family` declarations to fill `families.sans/serif/mono`.
88
+ - For each used heading level + body / small / caption, find the
89
+ `font-size` and `line-height` in px (convert em / rem / pt to px
90
+ using base 16px if needed).
91
+ - Capture `font-weight` if it deviates from default 400.
92
+
93
+ ### Spacing
94
+ - Look at margins / paddings / gaps. Identify the underlying rhythm
95
+ (commonly 4 / 8 / 12 / 16 / 24 / 32 / 48 / 64).
96
+ - Map to xs / sm / md / lg / xl / xxl based on which step the most
97
+ common values fall on.
98
+ - `page` = outermost padding (e.g., section wrapper padding).
99
+
100
+ ### Radius
101
+ - `border-radius` values used. Map to sm / md / lg / pill.
102
+
103
+ ### Shadow
104
+ - Each unique `box-shadow` declaration becomes one of sm / md / lg.
105
+ - Output as the literal CSS string (e.g., `"0 2px 4px rgba(0,0,0,0.06)"`).
106
+
107
+ ### Idioms
108
+ - Recurring composition: "stat row = 3-column grid with 24px gap", "every
109
+ section starts with 1px border-top", "headings are followed by a 4px
110
+ underline accent". List as plain English strings.
111
+ - ≥ 2 occurrences to count as an idiom (one-off layouts don't qualify).
112
+
113
+ ## Output format
114
+
115
+ When done, emit ONE message containing ONLY the JSON document. No
116
+ preamble, no postscript, no markdown fences. Just `{...}`.
117
+
118
+ If `canvas.html` doesn't exist, output:
119
+
120
+ ```json
121
+ { "error": "canvas.html not found in workspace" }
122
+ ```
123
+
124
+ If extraction fails partially, fill what you have and add a `meta.summary`
125
+ note like "could not extract typography — no <style> tag found".
126
+
127
+ ## Constraints
128
+
129
+ - Single Read of canvas.html (and optionally spec.json) — don't poll.
130
+ - No screenshots. No tool calls beyond Read.
131
+ - Strict JSON output (parsable by `JSON.parse`).
132
+ - Don't invent tokens — be honest about what's actually in the canvas.