@xiaobuyu/nodesign 0.0.6 → 0.0.8

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 (383) hide show
  1. package/README.md +266 -79
  2. package/README.zh-CN.md +254 -67
  3. package/package.json +2 -2
  4. package/server/api/admin.js +7 -0
  5. package/server/api/assets/docx-page.js +5 -4
  6. package/server/api/assets/helpers.js +26 -0
  7. package/server/api/assets/notes.js +50 -0
  8. package/server/api/assets.js +31 -38
  9. package/server/api/board.js +29 -1
  10. package/server/api/browse.js +3 -2
  11. package/server/api/chatai.js +9 -8
  12. package/server/api/exports/cards.js +7 -6
  13. package/server/api/exports/docx-pdf.js +4 -1
  14. package/server/api/instruction.js +3 -3
  15. package/server/api/local.js +10 -8
  16. package/server/api/projects.js +19 -3
  17. package/server/api/publish.js +3 -2
  18. package/server/api/sessions-rewind.js +220 -0
  19. package/server/api/sessions.js +58 -194
  20. package/server/api/stage.js +35 -0
  21. package/server/api/turn-model-switch.js +109 -0
  22. package/server/api/turn.js +52 -88
  23. package/server/auth/middleware.js +29 -6
  24. package/server/auth/tier.js +27 -7
  25. package/server/auth/users-store.js +14 -1
  26. package/server/engine/agent/actor-trail.js +110 -0
  27. package/server/engine/agent/agent-shared.js +22 -52
  28. package/server/engine/agent/beat-state.js +61 -0
  29. package/server/engine/agent/cast.js +322 -0
  30. package/server/engine/agent/events.js +18 -1
  31. package/server/engine/agent/hooks/beat-gate.js +73 -0
  32. package/server/engine/agent/hooks/failure.js +11 -1
  33. package/server/engine/agent/hooks/lifecycle.js +16 -41
  34. package/server/engine/agent/hooks/post-guidance.js +4 -38
  35. package/server/engine/agent/hooks/post-trim.js +1 -0
  36. package/server/engine/agent/hooks/pre-board-dirty.js +29 -0
  37. package/server/engine/agent/hooks/pre-defaults.js +206 -2
  38. package/server/engine/agent/hooks/pre-injectors.js +19 -2
  39. package/server/engine/agent/hooks/pre-peer-guard.js +88 -0
  40. package/server/engine/agent/hooks/pre-unknown-params.js +82 -0
  41. package/server/engine/agent/hooks/pre-workspace-scope-guard.js +33 -0
  42. package/server/engine/agent/hooks/resident-role-lifecycle.js +100 -0
  43. package/server/engine/agent/hooks/slot-alias.js +30 -0
  44. package/server/engine/agent/hooks/user-prompt-submit.js +208 -47
  45. package/server/engine/agent/hooks.js +80 -14
  46. package/server/engine/agent/isolation.js +25 -2
  47. package/server/engine/agent/memory-config.js +87 -0
  48. package/server/engine/agent/model-context.js +102 -18
  49. package/server/engine/agent/model-table.js +361 -58
  50. package/server/engine/agent/prompts/nodesign-prelude.md +579 -70
  51. package/server/engine/agent/prompts/tools/direct-edit-protocol.md +1 -1
  52. package/server/engine/agent/prompts/tools/generate-image-cookbook.md +26 -9
  53. package/server/engine/agent/prompts/tools/paint-still-cookbook.md +1 -1
  54. package/server/engine/agent/prompts/tools/site-reference.md +15 -4
  55. package/server/engine/agent/role-card.js +175 -0
  56. package/server/engine/agent/rp-mode.js +17 -0
  57. package/server/engine/agent/session-loop.js +55 -32
  58. package/server/engine/agent/stage-status.js +73 -0
  59. package/server/engine/agent/subagent-flight.js +73 -0
  60. package/server/engine/agent/system-prompts.js +41 -0
  61. package/server/engine/agent/tool-input-stream.js +165 -0
  62. package/server/engine/agents/explorer.md +1 -1
  63. package/server/engine/agents/index.js +1 -1
  64. package/server/engine/mcp/actor.js +59 -0
  65. package/server/engine/mcp/capability-gate.js +21 -2
  66. package/server/engine/mcp/index.js +118 -33
  67. package/server/engine/mcp/mode-profile.js +139 -0
  68. package/server/engine/mcp/server-name.js +19 -0
  69. package/server/engine/mcp/tools/artifact-session.js +2 -1
  70. package/server/engine/mcp/tools/browse-computer.js +2 -1
  71. package/server/engine/mcp/tools/browse-find-batch.js +95 -16
  72. package/server/engine/mcp/tools/browse.js +25 -3
  73. package/server/engine/mcp/tools/cast-role.js +167 -0
  74. package/server/engine/mcp/tools/crystallize-skill.js +1 -1
  75. package/server/engine/mcp/tools/draw-trend.js +104 -0
  76. package/server/engine/mcp/tools/edit-board-schema.js +56 -0
  77. package/server/engine/mcp/tools/edit-board.js +569 -0
  78. package/server/engine/mcp/tools/generate-image.js +50 -70
  79. package/server/engine/mcp/tools/helpers/codex-imagegen.js +139 -0
  80. package/server/engine/mcp/tools/helpers/rembg.js +7 -2
  81. package/server/engine/mcp/tools/helpers/shot-pipeline.js +109 -0
  82. package/server/engine/mcp/tools/list-pages.js +11 -5
  83. package/server/engine/mcp/tools/look-at-board.js +4 -3
  84. package/server/engine/mcp/tools/open-sheet.js +242 -0
  85. package/server/engine/mcp/tools/organize-board.js +45 -6
  86. package/server/engine/mcp/tools/paint-still.js +1 -1
  87. package/server/engine/mcp/tools/pin-to-board.js +103 -19
  88. package/server/engine/mcp/tools/read-board.js +123 -16
  89. package/server/engine/mcp/tools/read-tavern-json.js +87 -19
  90. package/server/engine/mcp/tools/report-issue.js +15 -7
  91. package/server/engine/mcp/tools/role-memory.js +83 -0
  92. package/server/engine/mcp/tools/roll-dice.js +82 -0
  93. package/server/engine/mcp/tools/roll-film.js +1 -1
  94. package/server/engine/mcp/tools/screenshot.js +19 -9
  95. package/server/engine/mcp/tools/set-vars.js +150 -0
  96. package/server/engine/mcp/tools/sheet-replan.js +46 -0
  97. package/server/engine/mcp/tools/web-search.js +12 -16
  98. package/server/engine/mcp/tools/write-on-board-flow.js +108 -0
  99. package/server/engine/mcp/tools/write-on-board-place.js +211 -0
  100. package/server/engine/mcp/tools/write-on-board-role-anchor.js +48 -0
  101. package/server/engine/mcp/tools/write-on-board-schema.js +125 -0
  102. package/server/engine/mcp/tools/write-on-board.js +568 -99
  103. package/server/engine/plugins/nodesign/skills/blackboard-rp/SKILL.md +215 -0
  104. package/server/engine/plugins/nodesign/skills/deskskill-engine-mini/SKILL.md +14 -7
  105. package/server/engine/plugins/nodesign/skills/docx-craft/SKILL.md +44 -5
  106. package/server/engine/plugins/nodesign/skills/docx-craft/references/token-schema.md +16 -0
  107. package/server/engine/plugins/nodesign/skills/site-craft/SKILL.md +24 -8
  108. package/server/engine/plugins/nodesign/skills/story-craft/SKILL.md +94 -0
  109. package/server/engine/plugins/nodesign/skills/story-import/SKILL.md +112 -0
  110. package/server/engine/plugins/nodesign/skills/story-intimacy/SKILL.md +72 -0
  111. package/server/engine/plugins/nodesign/skills/story-voice/SKILL.md +66 -0
  112. package/server/engine/plugins/nodesign/skills/story-voice/voices//345/206/267/347/241/254.md +33 -0
  113. package/server/engine/plugins/nodesign/skills/story-voice/voices//346/203/205/346/204/237/347/233/264/347/273/231.md +42 -0
  114. package/server/engine/plugins/nodesign/skills/story-voice/voices//346/204/217/350/257/206/346/265/201.md +42 -0
  115. package/server/engine/plugins/nodesign/skills/story-voice/voices//346/255/246/344/276/240/347/231/275/350/257/235.md +34 -0
  116. package/server/engine/plugins/nodesign/skills/story-voice/voices//347/224/237/346/264/273/346/265/201.md +35 -0
  117. package/server/engine/plugins/nodesign/skills/story-voice/voices//347/231/275/346/217/217.md +37 -0
  118. package/server/engine/plugins/nodesign/skills/story-voice/voices//350/210/236/345/217/260/345/211/247.md +32 -0
  119. package/server/engine/plugins/nodesign/skills/story-voice/voices//351/230/266/346/256/265/345/210/207/346/215/242.md +42 -0
  120. package/server/engine/runs/board-seater.js +272 -0
  121. package/server/engine/runs/board-tasklist.js +90 -48
  122. package/server/engine/runs/turn-relay.js +24 -2
  123. package/server/engine/runs/user-chalk-echo.js +44 -0
  124. package/server/index.js +2 -2
  125. package/server/lib/artifact-target.js +31 -14
  126. package/server/lib/board-anchor.js +49 -0
  127. package/server/lib/board-dirty.js +93 -0
  128. package/server/lib/board-excerpt.js +80 -0
  129. package/server/lib/board-follow.js +111 -0
  130. package/server/lib/board-groups.js +1 -5
  131. package/server/lib/board-hero.js +3 -1
  132. package/server/lib/board-kind-sizes.js +38 -3
  133. package/server/lib/board-lanes.js +58 -0
  134. package/server/lib/board-obstacles.js +80 -0
  135. package/server/lib/board-relations.js +5 -1
  136. package/server/lib/board-sheets.js +467 -0
  137. package/server/lib/board-shelf.js +86 -0
  138. package/server/lib/board-transform.js +62 -0
  139. package/server/lib/canvas-id.js +46 -4
  140. package/server/lib/chalk-flow.js +88 -0
  141. package/server/lib/chalk-rewrite.js +63 -0
  142. package/server/lib/chalk-size-pref.js +48 -0
  143. package/server/lib/chalk.js +42 -3
  144. package/server/lib/export-collect.js +3 -1
  145. package/server/lib/ingress/forward-openai-chat.js +16 -4
  146. package/server/lib/ingress/openai-chat.js +23 -12
  147. package/server/lib/ingress/session-routes.js +3 -3
  148. package/server/lib/ingress/stream-watchdog.js +53 -0
  149. package/server/lib/ingress/upstream-billing.js +15 -0
  150. package/server/lib/ingress/upstream-error-hints.js +36 -0
  151. package/server/lib/issues-store.js +3 -1
  152. package/server/lib/json-preview.js +58 -0
  153. package/server/lib/kinds/docx.js +11 -4
  154. package/server/lib/kinds/index.js +6 -16
  155. package/server/lib/kinds/site.js +40 -7
  156. package/server/lib/model-ingress.js +26 -1
  157. package/server/lib/moderation.js +10 -5
  158. package/server/lib/quota.js +26 -6
  159. package/server/lib/rect.js +50 -0
  160. package/server/lib/rewrite-refs.js +110 -0
  161. package/server/lib/screen.js +36 -0
  162. package/server/lib/sheet-state.js +19 -0
  163. package/server/lib/sketch-array.js +118 -0
  164. package/server/lib/sketch-hatch.js +99 -0
  165. package/server/lib/sketch-layout.js +269 -69
  166. package/server/lib/sketch-shapes.js +152 -0
  167. package/server/lib/sketch-stencils.js +170 -0
  168. package/server/lib/ssrf-guard.js +9 -0
  169. package/server/lib/state-table.js +237 -0
  170. package/server/lib/state-trend.js +94 -0
  171. package/server/lib/state-triggers.js +199 -0
  172. package/server/lib/task-scan.js +18 -0
  173. package/server/lib/tavern-json.js +48 -0
  174. package/server/lib/user-uploads.js +88 -0
  175. package/server/projects/board-limits.js +5 -3
  176. package/server/projects/board-sanitize.js +220 -10
  177. package/server/projects/board-store.js +137 -7
  178. package/server/projects/memory-migration.js +85 -0
  179. package/server/projects/move-entry.js +11 -1
  180. package/server/projects/session-jsonl.js +110 -0
  181. package/server/projects/store.js +56 -3
  182. package/server/projects/ui-config.js +2 -1
  183. package/server/projects/viewpoint-store.js +35 -3
  184. package/server/projects/workspace-slots.js +30 -0
  185. package/server/projects/workspace-templates.js +42 -10
  186. package/server/projects/workspace.js +25 -26
  187. package/server/runtime/capabilities.js +6 -2
  188. package/server/services/rembg-launcher.js +5 -2
  189. package/server/shared/locales.js +70 -0
  190. package/server/shared/messages-en.js +69 -0
  191. package/server/shared/messages.js +65 -0
  192. package/server/ws/broker.js +3 -0
  193. package/server/ws/index.js +43 -3
  194. package/web/README.md +1 -1
  195. package/web/dist/assets/{BrowserWindow-P_vIdh-G.js → BrowserWindow-CcaGRWz5.js} +1 -1
  196. package/web/dist/assets/DeckWindow-BIr2KGFT.js +6 -0
  197. package/web/dist/assets/{DocxWindow-DSAU1-tD.js → DocxWindow-B9KZmU5z.js} +2 -2
  198. package/web/dist/assets/{MermaidBlock-DR1GikCv.js → MermaidBlock-DrfRnz8E.js} +43 -43
  199. package/web/dist/assets/{SiteWindow-92uBuRRR.js → SiteWindow-CJOjCF7d.js} +6 -6
  200. package/web/dist/assets/{abnfDiagram-VCTEODGH-uA8Esix4.js → abnfDiagram-VCTEODGH-CUZbQGIg.js} +1 -1
  201. package/web/dist/assets/{arc-D_khH_xN.js → arc-C1-HSUny.js} +1 -1
  202. package/web/dist/assets/{architectureDiagram-5GKGNRK7-DydJHqOt.js → architectureDiagram-5GKGNRK7-B5nwvu60.js} +2 -2
  203. package/web/dist/assets/{blockDiagram-NRAW4CY4-B7wBbKiW.js → blockDiagram-NRAW4CY4-uPPuFLwR.js} +1 -1
  204. package/web/dist/assets/{c4Diagram-UCG6FXSJ-6mrH0x7Y.js → c4Diagram-UCG6FXSJ-C0mKBFlz.js} +1 -1
  205. package/web/dist/assets/channel-BxpWNOGU.js +1 -0
  206. package/web/dist/assets/{chunk-2Q5K7J3B-Cuikho5Q.js → chunk-2Q5K7J3B-Cs0rlZ4q.js} +1 -1
  207. package/web/dist/assets/{chunk-5VM5RSS4-3-KZ5AUz.js → chunk-5VM5RSS4-DtR63oYb.js} +1 -1
  208. package/web/dist/assets/{chunk-F27PBJKO-Bkwq2gGU.js → chunk-F27PBJKO-B3MRdezJ.js} +1 -1
  209. package/web/dist/assets/{chunk-G27WJ6UU-BLyQzdGE.js → chunk-G27WJ6UU-BtwntwpS.js} +1 -1
  210. package/web/dist/assets/{chunk-JWPE2WC7-B7HxxQds.js → chunk-JWPE2WC7-BHsTe5PD.js} +1 -1
  211. package/web/dist/assets/{chunk-LCL6LL3I-CtXrnUwF.js → chunk-LCL6LL3I-knY15Qdm.js} +1 -1
  212. package/web/dist/assets/{chunk-POPQ4Y6H-dxddZlXt.js → chunk-POPQ4Y6H-C51tgK_I.js} +1 -1
  213. package/web/dist/assets/{chunk-SVP7TREG-DpkmrgHn.js → chunk-SVP7TREG-CnwRZmF_.js} +1 -1
  214. package/web/dist/assets/{chunk-XXDRQBXY-BwZ7RbpQ.js → chunk-XXDRQBXY-BtXzVF-_.js} +1 -1
  215. package/web/dist/assets/classDiagram-DTDB5LWJ-DUEKmDfo.js +1 -0
  216. package/web/dist/assets/classDiagram-v2-JRS7N3AN-DUEKmDfo.js +1 -0
  217. package/web/dist/assets/{cose-bilkent-JH36ORCC-DGFOsMUt.js → cose-bilkent-JH36ORCC-CzWlGP6i.js} +1 -1
  218. package/web/dist/assets/{cynefin-OW5HDTMX-DUwn3ehS.js → cynefin-OW5HDTMX-vK64EGSv.js} +1 -1
  219. package/web/dist/assets/{cynefinDiagram-5FMLGOSQ-DlLAOZ9x.js → cynefinDiagram-5FMLGOSQ-Bs3g1Gvh.js} +1 -1
  220. package/web/dist/assets/{dagre-3AP2YEHR-CyourLfz.js → dagre-3AP2YEHR-BhgkYe8V.js} +1 -1
  221. package/web/dist/assets/{diagram-S7CK7UJ4-BTMbx2np.js → diagram-S7CK7UJ4-C_6mvD2V.js} +1 -1
  222. package/web/dist/assets/{diagram-UQ7AKVKN-UK94_1-P.js → diagram-UQ7AKVKN-qW7tssUW.js} +1 -1
  223. package/web/dist/assets/{diagram-VSXAHHWV-C7zS-v9v.js → diagram-VSXAHHWV-txJPHpvG.js} +1 -1
  224. package/web/dist/assets/{diagram-VX7I27RA-DKJRa6CK.js → diagram-VX7I27RA-CQTMsCnL.js} +1 -1
  225. package/web/dist/assets/{diagram-Z3DM3KII-kUbAfGAp.js → diagram-Z3DM3KII--cb1hoE6.js} +1 -1
  226. package/web/dist/assets/{ebnfDiagram-PWID7BFC-CVW16_vb.js → ebnfDiagram-PWID7BFC-Da6lDU77.js} +1 -1
  227. package/web/dist/assets/{erDiagram-SSCWMZ5O-BRQMFQ5y.js → erDiagram-SSCWMZ5O-DGCMrsQn.js} +1 -1
  228. package/web/dist/assets/{flowDiagram-A5DVABFB-S34LpLtK.js → flowDiagram-A5DVABFB-GdOL_qno.js} +1 -1
  229. package/web/dist/assets/{ganttDiagram-EL5Y4UJY-BFiqk1-G.js → ganttDiagram-EL5Y4UJY-Cx1F_jBK.js} +4 -4
  230. package/web/dist/assets/{gitGraphDiagram-WWUBYQGX-LZsrdyyv.js → gitGraphDiagram-WWUBYQGX-B5_6JhR3.js} +1 -1
  231. package/web/dist/assets/hand-CuvZZQGW.webp +0 -0
  232. package/web/dist/assets/index-BYKfMnmv.js +2759 -0
  233. package/web/dist/assets/index-CW7VsrVg.css +1 -0
  234. package/web/dist/assets/{infoDiagram-RXCK75RN-0DvN6KIp.js → infoDiagram-RXCK75RN-CjvKpSPA.js} +1 -1
  235. package/web/dist/assets/{ishikawaDiagram-5VMMS53U-CaY3A-Bx.js → ishikawaDiagram-5VMMS53U-CxLoAPhx.js} +1 -1
  236. package/web/dist/assets/{journeyDiagram-EYS64GPL-BBeiaCrj.js → journeyDiagram-EYS64GPL-BfvS1upJ.js} +1 -1
  237. package/web/dist/assets/{kanban-definition-3QL26DDD-ux42DRXQ.js → kanban-definition-3QL26DDD-DKPPu0w4.js} +1 -1
  238. package/web/dist/assets/{layout-CnXiF_NB.js → layout-Dhgck-Zo.js} +1 -1
  239. package/web/dist/assets/{linear-BMW0u6G6.js → linear-DMSQ-aUw.js} +1 -1
  240. package/web/dist/assets/lxgw-nd-app-bold-D1pKA1bR.woff2 +0 -0
  241. package/web/dist/assets/lxgw-nd-app-regular-BDe3d26v.woff2 +0 -0
  242. package/web/dist/assets/lxgw-nd-bold-Bufvlg03.woff2 +0 -0
  243. package/web/dist/assets/lxgw-nd-regular-DQwoI4OP.woff2 +0 -0
  244. package/web/dist/assets/lxgwwenkaiscreen-subset-100-DLPe7tEt.woff2 +0 -0
  245. package/web/dist/assets/lxgwwenkaiscreen-subset-101-d3VsZKWT.woff2 +0 -0
  246. package/web/dist/assets/lxgwwenkaiscreen-subset-102-ClzJuLq6.woff2 +0 -0
  247. package/web/dist/assets/lxgwwenkaiscreen-subset-103-DuDnQ6K-.woff2 +0 -0
  248. package/web/dist/assets/lxgwwenkaiscreen-subset-104-kPGW3Eem.woff2 +0 -0
  249. package/web/dist/assets/lxgwwenkaiscreen-subset-105-BzD54h-0.woff2 +0 -0
  250. package/web/dist/assets/lxgwwenkaiscreen-subset-106-BQ7fIzlx.woff2 +0 -0
  251. package/web/dist/assets/lxgwwenkaiscreen-subset-107-D8tDfFHI.woff2 +0 -0
  252. package/web/dist/assets/lxgwwenkaiscreen-subset-108-tGhkpwep.woff2 +0 -0
  253. package/web/dist/assets/lxgwwenkaiscreen-subset-109-7SMVSrly.woff2 +0 -0
  254. package/web/dist/assets/lxgwwenkaiscreen-subset-110-_uDKrFed.woff2 +0 -0
  255. package/web/dist/assets/lxgwwenkaiscreen-subset-111-Bio825RE.woff2 +0 -0
  256. package/web/dist/assets/lxgwwenkaiscreen-subset-112-D2ee0i-C.woff2 +0 -0
  257. package/web/dist/assets/lxgwwenkaiscreen-subset-113-JPFVDrga.woff2 +0 -0
  258. package/web/dist/assets/lxgwwenkaiscreen-subset-114-BcfxYA4w.woff2 +0 -0
  259. package/web/dist/assets/lxgwwenkaiscreen-subset-115-B1HeeOsd.woff2 +0 -0
  260. package/web/dist/assets/lxgwwenkaiscreen-subset-116-hY12tXpq.woff2 +0 -0
  261. package/web/dist/assets/lxgwwenkaiscreen-subset-117-BvuZwmre.woff2 +0 -0
  262. package/web/dist/assets/lxgwwenkaiscreen-subset-118-tzvs_H7j.woff2 +0 -0
  263. package/web/dist/assets/lxgwwenkaiscreen-subset-119-J2DWuYDN.woff2 +0 -0
  264. package/web/dist/assets/lxgwwenkaiscreen-subset-21-pDubCJV5.woff2 +0 -0
  265. package/web/dist/assets/lxgwwenkaiscreen-subset-22-ChAPMpqp.woff2 +0 -0
  266. package/web/dist/assets/lxgwwenkaiscreen-subset-23-BBx3oNpT.woff2 +0 -0
  267. package/web/dist/assets/lxgwwenkaiscreen-subset-24-CYt-Oagk.woff2 +0 -0
  268. package/web/dist/assets/lxgwwenkaiscreen-subset-25-Cx3j-ZUv.woff2 +0 -0
  269. package/web/dist/assets/lxgwwenkaiscreen-subset-26-B9AfZ3LL.woff2 +0 -0
  270. package/web/dist/assets/lxgwwenkaiscreen-subset-27-Bh2s9MNE.woff2 +0 -0
  271. package/web/dist/assets/lxgwwenkaiscreen-subset-28-C02JPoKP.woff2 +0 -0
  272. package/web/dist/assets/lxgwwenkaiscreen-subset-29-T_tbUGhk.woff2 +0 -0
  273. package/web/dist/assets/lxgwwenkaiscreen-subset-30-DMfyNzhz.woff2 +0 -0
  274. package/web/dist/assets/lxgwwenkaiscreen-subset-31-DFfdzbkg.woff2 +0 -0
  275. package/web/dist/assets/lxgwwenkaiscreen-subset-32-Bs1fz2iW.woff2 +0 -0
  276. package/web/dist/assets/lxgwwenkaiscreen-subset-33-cpB96OkZ.woff2 +0 -0
  277. package/web/dist/assets/lxgwwenkaiscreen-subset-34-BhinPYWX.woff2 +0 -0
  278. package/web/dist/assets/lxgwwenkaiscreen-subset-35-D_njRQqA.woff2 +0 -0
  279. package/web/dist/assets/lxgwwenkaiscreen-subset-36-DqXlLE3t.woff2 +0 -0
  280. package/web/dist/assets/lxgwwenkaiscreen-subset-37-j5LbvMph.woff2 +0 -0
  281. package/web/dist/assets/lxgwwenkaiscreen-subset-38-oO4jWdKm.woff2 +0 -0
  282. package/web/dist/assets/lxgwwenkaiscreen-subset-39-CabsX4rw.woff2 +0 -0
  283. package/web/dist/assets/lxgwwenkaiscreen-subset-4-PatS3YHh.woff2 +0 -0
  284. package/web/dist/assets/lxgwwenkaiscreen-subset-40-yrLg3C3e.woff2 +0 -0
  285. package/web/dist/assets/lxgwwenkaiscreen-subset-41-DBXmCQhc.woff2 +0 -0
  286. package/web/dist/assets/lxgwwenkaiscreen-subset-42-Bqij_UiQ.woff2 +0 -0
  287. package/web/dist/assets/lxgwwenkaiscreen-subset-43-D3kp0Ntc.woff2 +0 -0
  288. package/web/dist/assets/lxgwwenkaiscreen-subset-44-B_BNGvK4.woff2 +0 -0
  289. package/web/dist/assets/lxgwwenkaiscreen-subset-45-CZc0cQWs.woff2 +0 -0
  290. package/web/dist/assets/lxgwwenkaiscreen-subset-46-CHQX5Kd9.woff2 +0 -0
  291. package/web/dist/assets/lxgwwenkaiscreen-subset-47-Ci8gLrHA.woff2 +0 -0
  292. package/web/dist/assets/lxgwwenkaiscreen-subset-48-run3ugD3.woff2 +0 -0
  293. package/web/dist/assets/lxgwwenkaiscreen-subset-49-DgfiUb6u.woff2 +0 -0
  294. package/web/dist/assets/lxgwwenkaiscreen-subset-5-B0CtGbX_.woff2 +0 -0
  295. package/web/dist/assets/lxgwwenkaiscreen-subset-50-CdCG-POr.woff2 +0 -0
  296. package/web/dist/assets/lxgwwenkaiscreen-subset-51-L2okeQZ2.woff2 +0 -0
  297. package/web/dist/assets/lxgwwenkaiscreen-subset-52-BZAqyGJW.woff2 +0 -0
  298. package/web/dist/assets/lxgwwenkaiscreen-subset-53-B0xWqLeU.woff2 +0 -0
  299. package/web/dist/assets/lxgwwenkaiscreen-subset-54-BZVQX-EA.woff2 +0 -0
  300. package/web/dist/assets/lxgwwenkaiscreen-subset-55-093mkIUJ.woff2 +0 -0
  301. package/web/dist/assets/lxgwwenkaiscreen-subset-56-CbhWG8G-.woff2 +0 -0
  302. package/web/dist/assets/lxgwwenkaiscreen-subset-57-pLQuvo6c.woff2 +0 -0
  303. package/web/dist/assets/lxgwwenkaiscreen-subset-58-BHhoY50x.woff2 +0 -0
  304. package/web/dist/assets/lxgwwenkaiscreen-subset-59-D1_6DuIQ.woff2 +0 -0
  305. package/web/dist/assets/lxgwwenkaiscreen-subset-6-BnAelmYU.woff2 +0 -0
  306. package/web/dist/assets/lxgwwenkaiscreen-subset-60-HDBIuLTZ.woff2 +0 -0
  307. package/web/dist/assets/lxgwwenkaiscreen-subset-61-C-c13SeU.woff2 +0 -0
  308. package/web/dist/assets/lxgwwenkaiscreen-subset-62-kHRVOhZ2.woff2 +0 -0
  309. package/web/dist/assets/lxgwwenkaiscreen-subset-63-C0MCsvFv.woff2 +0 -0
  310. package/web/dist/assets/lxgwwenkaiscreen-subset-64-DZekguCj.woff2 +0 -0
  311. package/web/dist/assets/lxgwwenkaiscreen-subset-65-CtP6c3IU.woff2 +0 -0
  312. package/web/dist/assets/lxgwwenkaiscreen-subset-66-DLVWoMSf.woff2 +0 -0
  313. package/web/dist/assets/lxgwwenkaiscreen-subset-67-BVuOnayH.woff2 +0 -0
  314. package/web/dist/assets/lxgwwenkaiscreen-subset-68-DpxQ6Mnx.woff2 +0 -0
  315. package/web/dist/assets/lxgwwenkaiscreen-subset-69-qesBxLmC.woff2 +0 -0
  316. package/web/dist/assets/lxgwwenkaiscreen-subset-70-BBt1xN7h.woff2 +0 -0
  317. package/web/dist/assets/lxgwwenkaiscreen-subset-71-BWqZacwB.woff2 +0 -0
  318. package/web/dist/assets/lxgwwenkaiscreen-subset-72-B1wP_Yst.woff2 +0 -0
  319. package/web/dist/assets/lxgwwenkaiscreen-subset-73-C99T7jNZ.woff2 +0 -0
  320. package/web/dist/assets/lxgwwenkaiscreen-subset-74-DrUTsWBe.woff2 +0 -0
  321. package/web/dist/assets/lxgwwenkaiscreen-subset-75-C4M5XXQe.woff2 +0 -0
  322. package/web/dist/assets/lxgwwenkaiscreen-subset-76-Bq8spVWF.woff2 +0 -0
  323. package/web/dist/assets/lxgwwenkaiscreen-subset-77-D9vNupE2.woff2 +0 -0
  324. package/web/dist/assets/lxgwwenkaiscreen-subset-78-D6th7Gnu.woff2 +0 -0
  325. package/web/dist/assets/lxgwwenkaiscreen-subset-79-bezM85cS.woff2 +0 -0
  326. package/web/dist/assets/lxgwwenkaiscreen-subset-80-DD8pnhG2.woff2 +0 -0
  327. package/web/dist/assets/lxgwwenkaiscreen-subset-81-N6jtSyBs.woff2 +0 -0
  328. package/web/dist/assets/lxgwwenkaiscreen-subset-82-D6c180bZ.woff2 +0 -0
  329. package/web/dist/assets/lxgwwenkaiscreen-subset-83-em17AadM.woff2 +0 -0
  330. package/web/dist/assets/lxgwwenkaiscreen-subset-84-CJ8gdrMK.woff2 +0 -0
  331. package/web/dist/assets/lxgwwenkaiscreen-subset-85-9PPGO1eB.woff2 +0 -0
  332. package/web/dist/assets/lxgwwenkaiscreen-subset-86-CjWPf-u5.woff2 +0 -0
  333. package/web/dist/assets/lxgwwenkaiscreen-subset-87-8D_X_yOC.woff2 +0 -0
  334. package/web/dist/assets/lxgwwenkaiscreen-subset-88-9vtKFPBx.woff2 +0 -0
  335. package/web/dist/assets/lxgwwenkaiscreen-subset-89-BhE5jAKX.woff2 +0 -0
  336. package/web/dist/assets/lxgwwenkaiscreen-subset-90-D2Npfm5E.woff2 +0 -0
  337. package/web/dist/assets/lxgwwenkaiscreen-subset-91-C6Ft1-Eu.woff2 +0 -0
  338. package/web/dist/assets/lxgwwenkaiscreen-subset-97-CaExyQP9.woff2 +0 -0
  339. package/web/dist/assets/lxgwwenkaiscreen-subset-98-CPoVbsUd.woff2 +0 -0
  340. package/web/dist/assets/lxgwwenkaiscreen-subset-99-ZUQZGVgH.woff2 +0 -0
  341. package/web/dist/assets/{mindmap-definition-FBJOCRG2-hUxSpdKn.js → mindmap-definition-FBJOCRG2-CalfL_Kn.js} +1 -1
  342. package/web/dist/assets/{pegDiagram-XKGWAZYB-aCaQPH0w.js → pegDiagram-XKGWAZYB-D9mVWz7l.js} +1 -1
  343. package/web/dist/assets/pending-edit-apply-kwMFWsUe.js +31 -0
  344. package/web/dist/assets/{pieDiagram-E7YTZNPT-CmOEZ8zg.js → pieDiagram-E7YTZNPT-DELzbZNS.js} +1 -1
  345. package/web/dist/assets/{quadrantDiagram-AXDQQJYC-VW6fAhsV.js → quadrantDiagram-AXDQQJYC-TZqp4mfA.js} +1 -1
  346. package/web/dist/assets/{railroadDiagram-O6MQD6OU-CKJ7Ba5K.js → railroadDiagram-O6MQD6OU-DxX59f0i.js} +1 -1
  347. package/web/dist/assets/{requirementDiagram-EFPCY7ZU-pNGkq9Vw.js → requirementDiagram-EFPCY7ZU-DFdsJLIe.js} +1 -1
  348. package/web/dist/assets/{rotate-cw-KAfChbGV.js → rotate-cw-62liz7LX.js} +1 -1
  349. package/web/dist/assets/{sankeyDiagram-P5KCCOFB-BIMmDTfZ.js → sankeyDiagram-P5KCCOFB-ByYcd3fG.js} +1 -1
  350. package/web/dist/assets/{sequenceDiagram-WJ2MYXX4-BBW7wzKY.js → sequenceDiagram-WJ2MYXX4-Bj-ml1jS.js} +1 -1
  351. package/web/dist/assets/{sizeCapture-X5ZJPWSS-DdkXmLYR.js → sizeCapture-X5ZJPWSS-KhsXThyD.js} +1 -1
  352. package/web/dist/assets/{square-dashed-mouse-pointer-CDUlhUP7.js → square-dashed-mouse-pointer-D_VrqVpy.js} +1 -1
  353. package/web/dist/assets/{stateDiagram-HBIQ2CUA-W313Uz16.js → stateDiagram-HBIQ2CUA-BJlpHfWU.js} +1 -1
  354. package/web/dist/assets/stateDiagram-v2-4QOOHH4V-CR2kYAH2.js +1 -0
  355. package/web/dist/assets/{swimlanes-XN3QIQJK-C29TL26K.js → swimlanes-XN3QIQJK-DUB9OJVs.js} +1 -1
  356. package/web/dist/assets/swimlanesDiagram-VK2B7HYN-BitOxnG2.js +8 -0
  357. package/web/dist/assets/{timeline-definition-24CTP7MA-CF0JMObX.js → timeline-definition-24CTP7MA-C03NX82A.js} +1 -1
  358. package/web/dist/assets/{vennDiagram-4TSXK5OY-DrDFsaH3.js → vennDiagram-4TSXK5OY-B9pjzngp.js} +1 -1
  359. package/web/dist/assets/{wardleyDiagram-VM6X3IG4-CVrnRIS_.js → wardleyDiagram-VM6X3IG4-UqWq_mcT.js} +1 -1
  360. package/web/dist/assets/{xychartDiagram-S5SC5T6Z-BvkN1zsT.js → xychartDiagram-S5SC5T6Z-D2pDKtOv.js} +1 -1
  361. package/web/dist/index.html +2 -2
  362. package/server/api/memory.js +0 -185
  363. package/server/engine/agent/prompts/tools/generate-image-cookbook.gemini-gateway.md +0 -583
  364. package/server/engine/mcp/tools/arrange-on-board.js +0 -124
  365. package/server/engine/mcp/tools/create-on-board.js +0 -152
  366. package/server/engine/mcp/tools/edit-sketch.js +0 -167
  367. package/server/engine/mcp/tools/record-decision.js +0 -140
  368. package/server/engine/mcp/tools/relate-on-board.js +0 -112
  369. package/server/engine/mcp/tools/sketch-on-board.js +0 -337
  370. package/server/engine/plugins/nodesign/skills/rp-craft/SKILL.md +0 -235
  371. 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 +0 -78
  372. package/server/engine/plugins/nodesign/skills/rp-craft//346/274/224/345/207/272.template.js +0 -268
  373. package/web/dist/assets/DeckWindow-BpLv5082.js +0 -16
  374. package/web/dist/assets/channel-DGkccMO1.js +0 -1
  375. package/web/dist/assets/classDiagram-DTDB5LWJ-BGscw3OD.js +0 -1
  376. package/web/dist/assets/classDiagram-v2-JRS7N3AN-BGscw3OD.js +0 -1
  377. package/web/dist/assets/index-C0KUf6Wd.css +0 -1
  378. package/web/dist/assets/index-GAamh1p4.js +0 -2152
  379. package/web/dist/assets/lxgw-nd-bold-DbRfiTgo.woff2 +0 -0
  380. package/web/dist/assets/lxgw-nd-regular-CpMw9Ehg.woff2 +0 -0
  381. package/web/dist/assets/pending-edit-apply-AcdOQ9Ij.js +0 -31
  382. package/web/dist/assets/stateDiagram-v2-4QOOHH4V-DDhJBZzf.js +0 -1
  383. package/web/dist/assets/swimlanesDiagram-VK2B7HYN-BfN-Xdmu.js +0 -8
@@ -0,0 +1,199 @@
1
+ /**
2
+ * server/lib/state-triggers.js —— 状态表的条件触发器(2026-08-30)
3
+ *
4
+ * 「把会变的东西写在板上,设条件,需要的时候被动拿回来」的后半截。
5
+ * 前半截(值住在一张 markdown 表里)见 state-table.js。
6
+ *
7
+ * ## 声明住在正文里,不住在第二个存储里
8
+ *
9
+ * 触发器写成一段 ```nd:triggers 围栏,就在状态表那条板书的正文里 ——
10
+ * 跟 `nd:controls`(画布控件的声明)是同一个范式:**机器可读的声明住在用户看得见、
11
+ * 编得动的散文里**。这样用户能看到「现在挂着哪些条件」,也能自己删掉一条。
12
+ *
13
+ * 语法(一行一条,故意窄):
14
+ *
15
+ * ```nd:triggers
16
+ * - [好感度_苏绵 >= 5] once -> 她开始主动找你说话了,这一拍给她一个开口的机会
17
+ * - [体力 <= 2 && 时间 == 夜] on_cross -> 体力见底又是夜里:这一拍必须有代价
18
+ * ```
19
+ *
20
+ * `&&` 是唯一的连接词(全都成立才算命中)。没有 `||`、没有括号 —— 那两样一进来
21
+ * 就得写真正的表达式解析器,而**模型拼错一个变量名时静默求值成 false** 是这条线
22
+ * 最怕的那种失效。窄语法换来的是:每一种写错都能当场指出来。
23
+ *
24
+ * ## 触发的是「穿越」,不是「为真」
25
+ *
26
+ * `好感度 >= 5` 一旦满足,之后每一拍都满足。按「为真就触发」写,它会每拍注一遍,
27
+ * 上线当天就变成噪音源。所以只有两档:
28
+ * - `once` 命中一次就退休(一次性的剧情钩子)
29
+ * - `on_cross` 每次**从假变真**才触发(会来回摆的条件,比如昼夜、体力)
30
+ *
31
+ * ## 沿状态(上一次是真是假)放 `.nd/`,这次是对的
32
+ *
33
+ * `.nd/` 在 gitignore 里,所以**玩法真相不能放那儿**(那是 state-table.js 头注
34
+ * 否掉 `.nd/vars.json` 的理由)。但沿状态是**派生记账**:丢了的代价只是一次
35
+ * 「上膛不击发」,不是丢故事状态。语义写死:
36
+ * - 文件缺失 / 解析失败 → 按当前值重新上膛、**不击发**、log 一行
37
+ * - `revertWorkspace` 不回滚它(gitignore),后果是回滚后已触发过的不会重触发 —— 可接受
38
+ *
39
+ * ## 求值点 = 注入点
40
+ *
41
+ * 只在 UserPromptSubmit 里求值(读盘上的表 + 盘上的沿状态,命中就写进这一轮的
42
+ * 状态块)。不在 set_vars 里求值攒到下一轮 —— 那样中间一次进程重启就静默吞掉一次
43
+ * 触发,正是「哑掉的机制不报错」那族病。
44
+ */
45
+
46
+ import path from 'node:path';
47
+ import { promises as fs } from 'node:fs';
48
+ import { fingerprint } from '../engine/agent/hooks/turn-state-memory.js';
49
+
50
+ const FENCE_RE = /^[ \t]*```[ \t]*nd:triggers[ \t]*$/;
51
+ const FENCE_END_RE = /^[ \t]*```[ \t]*$/;
52
+ /** - [<条件>] <mode> -> <话> */
53
+ const LINE_RE = /^\s*-\s*\[([^\]]+)\]\s*(once|on_cross)\s*->\s*(.+?)\s*$/;
54
+ const OPS = ['>=', '<=', '!=', '==', '>', '<'];
55
+
56
+ export const LATCH_REL = '.nd/vars-latch.json';
57
+ /** 一条触发器的话最多这么长:它是注给模型的一句提醒,不是一段剧本 */
58
+ export const MESSAGE_MAX = 200;
59
+ export const MAX_TRIGGERS = 24;
60
+
61
+ /** 把一条条件文本拆成若干个比较(`&&` 连接,全都成立才算命中) */
62
+ function parseCond(text) {
63
+ const parts = String(text).split('&&').map((p) => p.trim()).filter(Boolean);
64
+ if (!parts.length) return { error: '条件是空的' };
65
+ const cmps = [];
66
+ for (const p of parts) {
67
+ const op = OPS.find((o) => p.includes(o));
68
+ if (!op) return { error: `「${p}」里没有比较符(用 >= <= > < == != 其中之一)` };
69
+ const idx = p.indexOf(op);
70
+ const key = p.slice(0, idx).trim();
71
+ const value = p.slice(idx + op.length).trim();
72
+ if (!key) return { error: `「${p}」缺左边的键名` };
73
+ if (!value) return { error: `「${p}」缺右边的值` };
74
+ cmps.push({ key, op, value });
75
+ }
76
+ return { cmps };
77
+ }
78
+
79
+ /**
80
+ * 从正文里解析 ```nd:triggers 围栏。
81
+ * @returns {{ triggers: Array<{id,cmps,mode,message,raw}>, errors: string[] }}
82
+ * errors 里每条都指得出是哪一行错在哪 —— 写错要能被看见,不能静默丢。
83
+ */
84
+ export function parseTriggers(body) {
85
+ const lines = String(body || '').split(/\r?\n/);
86
+ const triggers = []; const errors = [];
87
+ let inFence = false;
88
+ for (let i = 0; i < lines.length; i += 1) {
89
+ const l = lines[i];
90
+ if (!inFence) { if (FENCE_RE.test(l)) inFence = true; continue; }
91
+ if (FENCE_END_RE.test(l)) { inFence = false; continue; }
92
+ if (!l.trim()) continue;
93
+ const m = LINE_RE.exec(l);
94
+ if (!m) {
95
+ errors.push(`第 ${i + 1} 行看不懂:${l.trim().slice(0, 60)}\n (格式是 \`- [键 >= 值] once -> 要说的话\`,mode 只能是 once 或 on_cross)`);
96
+ continue;
97
+ }
98
+ const [, condText, mode, messageRaw] = m;
99
+ const { cmps, error } = parseCond(condText);
100
+ if (error) { errors.push(`第 ${i + 1} 行条件写错了:${error}`); continue; }
101
+ const message = messageRaw.slice(0, MESSAGE_MAX);
102
+ // id 只由「条件 + 档」决定:改那句提醒的措辞不该把沿状态重置掉
103
+ const id = fingerprint(`${cmps.map((c) => `${c.key}${c.op}${c.value}`).join('&&')}|${mode}`);
104
+ triggers.push({ id, cmps, mode, message, raw: l.trim() });
105
+ if (triggers.length >= MAX_TRIGGERS) {
106
+ errors.push(`触发器超过 ${MAX_TRIGGERS} 条,后面的没读 —— 挂太多等于没挂。`);
107
+ break;
108
+ }
109
+ }
110
+ if (inFence) errors.push('```nd:triggers 围栏没闭合,后面的行都没读。');
111
+ return { triggers, errors };
112
+ }
113
+
114
+ const NUM_RE = /^-?\d+(\.\d+)?$/;
115
+
116
+ /**
117
+ * 求一个比较的值。
118
+ * @returns {{ v:boolean } | { error:string }}
119
+ *
120
+ * ⛔ 跨类型的大小比较**大声拒绝**,不隐式转换:`时间 >= 戌时` 按字典序比出来的
121
+ * 结果看着像那么回事,但它不是任何人想要的语义。钳住但要说 —— 这是 B+ 没有类型
122
+ * 系统换来的代价,只能在求值时付。
123
+ */
124
+ function compare(cmp, rows) {
125
+ const row = rows.find((r) => r.key === cmp.key);
126
+ if (!row) return { error: `表里没有「${cmp.key}」这个键` };
127
+ const l = String(row.value).trim(); const r = String(cmp.value).trim();
128
+ const bothNum = NUM_RE.test(l) && NUM_RE.test(r);
129
+ if (cmp.op === '==') return { v: bothNum ? Number(l) === Number(r) : l === r };
130
+ if (cmp.op === '!=') return { v: bothNum ? Number(l) !== Number(r) : l !== r };
131
+ if (!bothNum) {
132
+ return { error: `「${cmp.key} ${cmp.op} ${cmp.value}」比不了:${NUM_RE.test(l) ? '右边' : '左边'}不是数字(大小比较只对数字生效;想比文字用 == 或 !=)` };
133
+ }
134
+ const a = Number(l); const b = Number(r);
135
+ if (cmp.op === '>=') return { v: a >= b };
136
+ if (cmp.op === '<=') return { v: a <= b };
137
+ if (cmp.op === '>') return { v: a > b };
138
+ return { v: a < b };
139
+ }
140
+
141
+ /**
142
+ * 求值一批触发器。**纯函数** —— 落盘由调用方做,好测。
143
+ *
144
+ * @param triggers parseTriggers 的产物
145
+ * @param rows 状态表的现值
146
+ * @param latch 上一次的沿状态 { [id]: { last:boolean, fired:number } }
147
+ * @param opts.fresh true = 这是本项目第一次求值(沿状态文件不存在/坏了)
148
+ * → **上膛不击发**:按当前值记下来,一条都不触发
149
+ * @returns {{ fired:Array<{id,message,raw}>, latch:object, errors:string[], armed:number, retired:number }}
150
+ */
151
+ export function evalTriggers(triggers, rows, latch = {}, { fresh = false } = {}) {
152
+ const next = {}; const fired = []; const errors = [];
153
+ let armed = 0; let retired = 0;
154
+ for (const t of triggers) {
155
+ const prev = latch[t.id] || null;
156
+ const firedCount = prev?.fired || 0;
157
+ if (t.mode === 'once' && firedCount > 0) { next[t.id] = prev; retired += 1; continue; }
158
+
159
+ let v = true; let bad = null;
160
+ for (const c of t.cmps) {
161
+ const r = compare(c, rows);
162
+ if (r.error) { bad = r.error; break; }
163
+ if (!r.v) { v = false; break; }
164
+ }
165
+ if (bad) {
166
+ errors.push(`${t.raw}\n → ${bad}`);
167
+ next[t.id] = prev || { last: false, fired: firedCount }; // 求不出来不动沿状态
168
+ armed += 1;
169
+ continue;
170
+ }
171
+
172
+ const was = prev ? !!prev.last : false;
173
+ // 上膛不击发:第一次见到这条触发器(或沿状态丢了)只记录,不触发 ——
174
+ // 否则每次进程重启都会把所有当前为真的条件重放一遍
175
+ const shouldFire = !fresh && !!prev && v && !was;
176
+ if (shouldFire) fired.push({ id: t.id, message: t.message, raw: t.raw });
177
+ next[t.id] = { last: v, fired: firedCount + (shouldFire ? 1 : 0) };
178
+ if (t.mode === 'once' && next[t.id].fired > 0) retired += 1; else armed += 1;
179
+ }
180
+ return { fired, latch: next, errors, armed, retired };
181
+ }
182
+
183
+ export async function readLatch(sharedRoot) {
184
+ try {
185
+ const raw = await fs.readFile(path.join(sharedRoot, LATCH_REL), 'utf8');
186
+ const j = JSON.parse(raw);
187
+ if (!j || typeof j !== 'object') return { latch: {}, fresh: true };
188
+ return { latch: j, fresh: false };
189
+ } catch {
190
+ // 缺失或坏了都走 fresh:按当前值重新上膛、不击发
191
+ return { latch: {}, fresh: true };
192
+ }
193
+ }
194
+
195
+ export async function writeLatch(sharedRoot, latch) {
196
+ const abs = path.join(sharedRoot, LATCH_REL);
197
+ await fs.mkdir(path.dirname(abs), { recursive: true });
198
+ await fs.writeFile(abs, JSON.stringify(latch, null, 2), 'utf8');
199
+ }
@@ -54,6 +54,11 @@ export const RESERVED_FILES = new Set([
54
54
  '.nd-project.json', // 产物标记(形态兜底)
55
55
  '.gitignore',
56
56
  '.ndignore',
57
+ // 下面三个是基础设施不是用户产物,上墙就是「画布把自己的存档画在自己身上」
58
+ // 家族(08-24 清账补齐;ui-config.json 是黑板模式开关,曾以 .json 卡上墙)
59
+ 'ui-config.json',
60
+ 'spec.json',
61
+ 'pending-changes.json',
57
62
  ]);
58
63
 
59
64
  /** 永远不扫的目录名(任何深度命中即剪枝) */
@@ -68,6 +73,19 @@ export const DRAFTS_DIR = '_drafts';
68
73
 
69
74
  export const NDIGNORE_FILE = '.ndignore';
70
75
 
76
+ /**
77
+ * 读产物标记(`.nd-project.json`)。没有 / 读不动 → null。
78
+ * 2026-08-24 从 kinds/index.js 迁来:site.js 现在要按子目录读 marker(构建型
79
+ * 子目录站),住 kinds/index.js 会循环 import。kinds/index.js 原样 re-export。
80
+ */
81
+ export async function readTaskMarker(root) {
82
+ try {
83
+ const raw = await fs.readFile(path.join(root, '.nd-project.json'), 'utf8');
84
+ const parsed = JSON.parse(raw);
85
+ return (parsed && typeof parsed === 'object') ? parsed : null;
86
+ } catch { return null; }
87
+ }
88
+
71
89
  /** gitignore 子集 → RegExp。认不出的行按字面量处理(转义后精确匹配)。 */
72
90
  function compilePattern(line) {
73
91
  let pat = line.trim();
@@ -152,3 +152,51 @@ export function fetchEntries(doc, 选) {
152
152
  }
153
153
  return 出;
154
154
  }
155
+
156
+ /**
157
+ * PNG 角色卡(2026-08-25,四方世界卡案:iss 上报「不是合法 JSON:�PNG」)。
158
+ * 酒馆的卡就是一张 PNG,角色数据 base64 后塞在 tEXt 块里:V3 用键 `ccv3`,
159
+ * V2 用键 `chara`。两个都在时 ccv3 赢(V3 是超集)。不是卡的普通 PNG 返回 null。
160
+ */
161
+ export function extractCardFromPng(buf) {
162
+ if (!Buffer.isBuffer(buf) || buf.length < 16) return null;
163
+ if (buf.readUInt32BE(0) !== 0x89504e47) return null; // \x89PNG
164
+ let off = 8;
165
+ const found = {};
166
+ while (off + 12 <= buf.length) {
167
+ const len = buf.readUInt32BE(off);
168
+ const type = buf.toString('latin1', off + 4, off + 8);
169
+ if (type === 'tEXt' && len > 0 && off + 8 + len <= buf.length) {
170
+ const data = buf.subarray(off + 8, off + 8 + len);
171
+ const nul = data.indexOf(0);
172
+ if (nul > 0) {
173
+ const key = data.toString('latin1', 0, nul);
174
+ if (key === 'ccv3' || key === 'chara') found[key] = data.subarray(nul + 1).toString('latin1');
175
+ }
176
+ }
177
+ off += 12 + len;
178
+ if (type === 'IEND') break;
179
+ }
180
+ const payload = found.ccv3 || found.chara;
181
+ if (!payload) return null;
182
+ try { return JSON.parse(Buffer.from(payload, 'base64').toString('utf8')); } catch { return null; }
183
+ }
184
+
185
+ /**
186
+ * 世界书条目全量(带正文)——给 export_book 机械搬运用。判断归 agent(常驻挑选、
187
+ * 引擎条目丢弃),搬运归机器(375 条 61.8 万字符不该流经上下文)。
188
+ */
189
+ export function listBookEntries(doc) {
190
+ const kind = detectKind(doc);
191
+ const book = kind === 'lorebook' ? doc : (doc?.data?.character_book || doc?.character_book);
192
+ if (!book) return [];
193
+ const list = Array.isArray(book.entries) ? book.entries : Object.values(book.entries || {});
194
+ return list.map((e, i) => ({
195
+ id: String(e.uid ?? e.id ?? i),
196
+ 名字: e.comment || e.name || (Array.isArray(e.key) ? e.key.join('/') : '') || `条目${i + 1}`,
197
+ 触发: Array.isArray(e.key) ? e.key : (Array.isArray(e.keys) ? e.keys : []),
198
+ 常驻: !!(e.constant),
199
+ 停用: e.enabled === false || e.disable === true,
200
+ 正文: String(e.content || ''),
201
+ }));
202
+ }
@@ -0,0 +1,88 @@
1
+ /**
2
+ * lib/user-uploads.js —— 用户上传件的落点与增删查(2026-08-28)
3
+ *
4
+ * ## 为什么单独挪出来
5
+ *
6
+ * 以前上传一律落 `shared/assets/`,而 `assets/` 在画布的文件夹清单里是**基础设施
7
+ * 目录**(RESERVED_DIRS / NOT_A_FOLDER),于是每个上传件都 homeOf 到根 —— 用户拖
8
+ * 一张参考图进来,它就直接摊在桌面上跟产物混作一堆。用户原话:「有一件事很膈应」。
9
+ *
10
+ * ## 修法:给它一个真目录,不是在画布上造一个虚拟分组
11
+ *
12
+ * 文件夹在这套体系里从来就等于磁盘上的真目录(见 api/assets.js 的收纳文件夹注释)。
13
+ * 凭空多一种只存在于画布上的分组,「文件系统即真相」这条就破了。真目录还白拿了
14
+ * 整套现成能力:文件夹卡、拖进拖出、改名、MoveTo、agent 的 mv。
15
+ *
16
+ * ⚠️ 老项目 `assets/` 里已有的上传件**不迁**:物件 id 就是路径,搬家等于改 id,
17
+ * 得连 board.json 绑定一起转发。它们照旧扫成 upload 摊在根上 —— 所以这里的列举和
18
+ * 删除都**两个落点都认**,新的优先。
19
+ */
20
+
21
+ import fs from 'fs/promises';
22
+ import path from 'path';
23
+
24
+ /** 上传落点(工作区相对)。画布上就显示成这个名字的文件夹 */
25
+ export const USER_UPLOAD_DIR = '用户内容';
26
+
27
+ /** 两个落点,新的在前 —— 列举顺序、删除尝试顺序都按它 */
28
+ const LOOKUP_DIRS = [USER_UPLOAD_DIR, 'assets'];
29
+
30
+ /** 上传写到哪(不存在就建) */
31
+ export async function ensureUploadDir(sharedRoot) {
32
+ const dir = path.join(sharedRoot, USER_UPLOAD_DIR);
33
+ await fs.mkdir(dir, { recursive: true });
34
+ return dir;
35
+ }
36
+
37
+ /** agent / 前端拿到的引用路径。cwd 是 sessions/<sid>/,所以 `../../shared/<dir>/<name>` */
38
+ export function uploadRefPath(dirRel, filename) {
39
+ return `../../shared/${dirRel}/${filename}`;
40
+ }
41
+
42
+ /**
43
+ * 列两个落点下的上传件(只看顶层文件,子目录归画布的文件夹机制管)。
44
+ * @returns {Promise<Array<{path, name, dir, size, mtime}>>} 按 mtime 新→旧
45
+ */
46
+ export async function listUploadedAssets(sharedRoot) {
47
+ const out = [];
48
+ for (const dirRel of LOOKUP_DIRS) {
49
+ let entries;
50
+ try {
51
+ entries = await fs.readdir(path.join(sharedRoot, dirRel), { withFileTypes: true });
52
+ } catch (err) {
53
+ if (err.code === 'ENOENT') continue;
54
+ throw err;
55
+ }
56
+ for (const e of entries) {
57
+ if (!e.isFile()) continue;
58
+ let stat;
59
+ try { stat = await fs.stat(path.join(sharedRoot, dirRel, e.name)); } catch { continue; }
60
+ out.push({
61
+ path: uploadRefPath(dirRel, e.name), name: e.name, dir: dirRel,
62
+ size: stat.size, mtime: stat.mtime.toISOString(),
63
+ });
64
+ }
65
+ }
66
+ out.sort((a, b) => (a.mtime < b.mtime ? 1 : -1));
67
+ return out;
68
+ }
69
+
70
+ /**
71
+ * 删一个上传件。两个落点依次试,**每个目录各自 resolve 复核**(别拿一个目录的
72
+ * 判据去删另一个目录的文件)。
73
+ * @returns {Promise<'deleted'|'not_found'|'invalid'>}
74
+ */
75
+ export async function deleteUploadedAsset(sharedRoot, filename) {
76
+ for (const dirRel of LOOKUP_DIRS) {
77
+ const dirAbs = path.join(sharedRoot, dirRel);
78
+ const filePath = path.resolve(dirAbs, filename);
79
+ if (!filePath.startsWith(dirAbs + path.sep)) return 'invalid';
80
+ try {
81
+ await fs.unlink(filePath);
82
+ return 'deleted';
83
+ } catch (err) {
84
+ if (err.code !== 'ENOENT') throw err;
85
+ }
86
+ }
87
+ return 'not_found';
88
+ }
@@ -3,10 +3,12 @@
3
3
  * board-store(写盘前查字节数)和 board-sanitize(逐类计数)都要,单独一份免循环依赖。
4
4
  */
5
5
  export const DEFAULT_BOARD_SIZE = { w: 4000, h: 2600 };
6
- export const MAX_BOARD_BYTES = 512 * 1024;
7
- export const MAX_OBJECTS = 2000;
6
+ export const MAX_BOARD_BYTES = 2 * 1024 * 1024; // 08-25 放大(板是整读整写 JSON,这是失控兜底不是风格闸)
7
+ export const MAX_OBJECTS = 8000;
8
8
  export const MAX_ZONES = 200;
9
9
  // 关系线上限。取值理由:一块板上人能看懂的线远少于这个数,1000 是防脱缰
10
10
  // (agent 循环里连画)的闸门,不是设计目标。超了直接不收,不做淘汰 ——
11
11
  // 静默丢最旧的会让"我明明画了"变成玄学。
12
- export const MAX_BINDINGS = 1000;
12
+ export const MAX_BINDINGS = 4000;
13
+ export const MAX_LANES = 60; // 线(lane 注册表):几十条线的脑图已是极限读感
14
+ export const MAX_SHEETS = 200; // 纸(sheet 注册表,2026-08-29 纸范式):一纸一屏,200 张已是长篇连载量级