@xiaobuyu/nodesign 0.0.1 → 0.0.3

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 (358) hide show
  1. package/README.md +156 -106
  2. package/bin/nodesign.js +0 -0
  3. package/package.json +1 -1
  4. package/server/api/README.md +45 -45
  5. package/server/api/_guard.js +75 -75
  6. package/server/api/admin.js +187 -187
  7. package/server/api/assets/docx-page.js +103 -103
  8. package/server/api/assets/helpers.js +42 -22
  9. package/server/api/assets/notes.js +194 -144
  10. package/server/api/assets.js +891 -896
  11. package/server/api/board.js +134 -57
  12. package/server/api/browse.js +138 -138
  13. package/server/api/canvas.js +444 -444
  14. package/server/api/chatai.js +324 -324
  15. package/server/api/exports/build-standalone.js +980 -980
  16. package/server/api/exports/cards.js +118 -118
  17. package/server/api/exports/docx-pdf.js +46 -46
  18. package/server/api/exports/export-page.js +142 -142
  19. package/server/api/exports/handoff.js +147 -147
  20. package/server/api/exports.js +690 -690
  21. package/server/api/instruction.js +75 -75
  22. package/server/api/me.js +116 -116
  23. package/server/api/memory.js +185 -185
  24. package/server/api/pending-changes.js +366 -366
  25. package/server/api/plugins.js +164 -164
  26. package/server/api/projects.js +220 -220
  27. package/server/api/publish.js +64 -64
  28. package/server/api/recent.js +73 -73
  29. package/server/api/sessions.js +589 -589
  30. package/server/api/skills.js +77 -77
  31. package/server/api/standalone-fit.js +168 -168
  32. package/server/api/turn-compose.js +180 -180
  33. package/server/api/turn-inflight.js +44 -44
  34. package/server/auth/README.md +17 -17
  35. package/server/auth/middleware.js +138 -138
  36. package/server/auth/origin-guard.js +71 -71
  37. package/server/auth/session.js +108 -108
  38. package/server/auth/tier.js +113 -113
  39. package/server/auth/users-store.js +348 -348
  40. package/server/edit/README.md +29 -29
  41. package/server/engine/README.md +113 -113
  42. package/server/engine/agent/agent-shared.js +550 -550
  43. package/server/engine/agent/auto-mode-default-hard-deny.txt +4 -4
  44. package/server/engine/agent/auto-mode-rules.js +78 -78
  45. package/server/engine/agent/context.js +418 -418
  46. package/server/engine/agent/events.js +465 -464
  47. package/server/engine/agent/hooks/canvas-validate.js +199 -199
  48. package/server/engine/agent/hooks/failure.js +120 -120
  49. package/server/engine/agent/hooks/file-events.js +75 -75
  50. package/server/engine/agent/hooks/lifecycle.js +233 -233
  51. package/server/engine/agent/hooks/post-canvas-focus.js +80 -80
  52. package/server/engine/agent/hooks/post-guidance.js +206 -206
  53. package/server/engine/agent/hooks/post-subagent-report.js +104 -104
  54. package/server/engine/agent/hooks/post-trim.js +58 -58
  55. package/server/engine/agent/hooks/pre-board-neighborhood.js +34 -34
  56. package/server/engine/agent/hooks/pre-defaults.js +84 -84
  57. package/server/engine/agent/hooks/pre-injectors.js +277 -277
  58. package/server/engine/agent/hooks/pre-performance-log-guard.js +78 -78
  59. package/server/engine/agent/hooks/pre-starter-files.js +61 -61
  60. package/server/engine/agent/hooks/pre-workspace-scope-guard.js +78 -78
  61. package/server/engine/agent/hooks/site-validate.js +94 -94
  62. package/server/engine/agent/hooks/tool-prompts.js +31 -31
  63. package/server/engine/agent/hooks/turn-state-memory.js +56 -56
  64. package/server/engine/agent/hooks/user-prompt-submit.js +258 -215
  65. package/server/engine/agent/hooks.js +354 -354
  66. package/server/engine/agent/init-contract.js +69 -69
  67. package/server/engine/agent/isolation.js +159 -159
  68. package/server/engine/agent/model-table.js +292 -292
  69. package/server/engine/agent/plugin-loader.js +266 -266
  70. package/server/engine/agent/prompts/nodesign-prelude.md +388 -336
  71. package/server/engine/agent/prompts/tools/ask-user-question-protocol.md +71 -71
  72. package/server/engine/agent/prompts/tools/direct-edit-protocol.md +98 -98
  73. package/server/engine/agent/prompts/tools/generate-image-cookbook.gemini-gateway.md +583 -583
  74. package/server/engine/agent/prompts/tools/generate-image-cookbook.md +483 -483
  75. package/server/engine/agent/prompts/tools/hybrid-reference.md +100 -100
  76. package/server/engine/agent/prompts/tools/paint-still-cookbook.md +270 -270
  77. package/server/engine/agent/prompts/tools/roll-film-cookbook.md +70 -70
  78. package/server/engine/agent/prompts/tools/site-reference.md +106 -106
  79. package/server/engine/agent/prompts/tools/tweaks-syntax.md +125 -125
  80. package/server/engine/agent/prompts/tools/vision-checker-dispatch.md +48 -48
  81. package/server/engine/agent/session-loop.js +1035 -1024
  82. package/server/engine/agent/session-model.js +164 -164
  83. package/server/engine/agent/skill.js +204 -204
  84. package/server/engine/agent/task-events.js +78 -78
  85. package/server/engine/agents/ds-extractor.md +132 -132
  86. package/server/engine/agents/explorer.md +190 -190
  87. package/server/engine/agents/index.js +230 -230
  88. package/server/engine/agents/schemas/design-system.json +156 -156
  89. package/server/engine/agents/schemas/tweak-schema.json +113 -113
  90. package/server/engine/agents/tweak-proposer.md +127 -127
  91. package/server/engine/agents/vision-checker.md +254 -254
  92. package/server/engine/browse/capture.js +422 -422
  93. package/server/engine/browse/card.js +137 -137
  94. package/server/engine/browse/handover.js +59 -59
  95. package/server/engine/browse/page-digest.js +119 -119
  96. package/server/engine/browse/refs.js +196 -196
  97. package/server/engine/browse/registry.js +307 -307
  98. package/server/engine/browse/screencast.js +170 -170
  99. package/server/engine/browse/state.js +121 -121
  100. package/server/engine/chatai/chat-log.js +79 -79
  101. package/server/engine/chatai/index.js +193 -193
  102. package/server/engine/chatai/openai-compat.js +152 -152
  103. package/server/engine/chatai/orchestrate.js +309 -309
  104. package/server/engine/chatai/perform.js +40 -40
  105. package/server/engine/chatai/summarize.js +79 -79
  106. package/server/engine/mcp/capability-gate.js +52 -52
  107. package/server/engine/mcp/index.js +351 -339
  108. package/server/engine/mcp/param-sanitizer.js +142 -142
  109. package/server/engine/mcp/tools/arrange-on-board.js +124 -124
  110. package/server/engine/mcp/tools/artifact-session.js +278 -278
  111. package/server/engine/mcp/tools/browse-computer.js +421 -421
  112. package/server/engine/mcp/tools/browse-find-batch.js +183 -183
  113. package/server/engine/mcp/tools/browse-screenshot.js +157 -157
  114. package/server/engine/mcp/tools/browse.js +480 -480
  115. package/server/engine/mcp/tools/build-docx.js +101 -101
  116. package/server/engine/mcp/tools/clear-pending-changes.js +99 -99
  117. package/server/engine/mcp/tools/create-on-board.js +152 -155
  118. package/server/engine/mcp/tools/crystallize-skill.js +168 -168
  119. package/server/engine/mcp/tools/edit-sketch.js +167 -0
  120. package/server/engine/mcp/tools/explain-style.js +237 -237
  121. package/server/engine/mcp/tools/export-handoff.js +133 -133
  122. package/server/engine/mcp/tools/expose-tweaks.js +149 -149
  123. package/server/engine/mcp/tools/generate-image.js +842 -842
  124. package/server/engine/mcp/tools/get-computed-styles.js +164 -164
  125. package/server/engine/mcp/tools/get-pending-changes.js +205 -205
  126. package/server/engine/mcp/tools/h3box-ssh.js +84 -84
  127. package/server/engine/mcp/tools/helpers/acquire-page.js +82 -82
  128. package/server/engine/mcp/tools/helpers/motion-lab.js +554 -554
  129. package/server/engine/mcp/tools/helpers/motion-scroll.js +112 -112
  130. package/server/engine/mcp/tools/helpers/perception-page.js +215 -215
  131. package/server/engine/mcp/tools/helpers/reference-download.js +103 -103
  132. package/server/engine/mcp/tools/helpers/rembg-bridge.py +80 -80
  133. package/server/engine/mcp/tools/helpers/shot-pipeline.js +280 -280
  134. package/server/engine/mcp/tools/highlight.js +68 -68
  135. package/server/engine/mcp/tools/list-pages.js +222 -222
  136. package/server/engine/mcp/tools/look-at-board.js +124 -0
  137. package/server/engine/mcp/tools/lookup-tags.js +59 -59
  138. package/server/engine/mcp/tools/navigate-to-page.js +60 -60
  139. package/server/engine/mcp/tools/organize-board.js +64 -64
  140. package/server/engine/mcp/tools/paint-still.js +434 -434
  141. package/server/engine/mcp/tools/pin-to-board.js +130 -130
  142. package/server/engine/mcp/tools/preview-deck.js +130 -130
  143. package/server/engine/mcp/tools/profile-scroll.js +305 -305
  144. package/server/engine/mcp/tools/publish-site.js +102 -102
  145. package/server/engine/mcp/tools/query-elements.js +195 -195
  146. package/server/engine/mcp/tools/read-board.js +190 -103
  147. package/server/engine/mcp/tools/read-document.js +93 -93
  148. package/server/engine/mcp/tools/read-page.js +264 -264
  149. package/server/engine/mcp/tools/read-tavern-json.js +143 -143
  150. package/server/engine/mcp/tools/read-user-view.js +59 -0
  151. package/server/engine/mcp/tools/record-decision.js +140 -140
  152. package/server/engine/mcp/tools/relate-on-board.js +112 -110
  153. package/server/engine/mcp/tools/report-issue.js +115 -115
  154. package/server/engine/mcp/tools/roll-film.js +266 -266
  155. package/server/engine/mcp/tools/screenshot-docx.js +138 -138
  156. package/server/engine/mcp/tools/screenshot-url.js +195 -195
  157. package/server/engine/mcp/tools/screenshot.js +583 -583
  158. package/server/engine/mcp/tools/sketch-on-board.js +337 -0
  159. package/server/engine/mcp/tools/tier-gate.js +96 -96
  160. package/server/engine/mcp/tools/trace-motion.js +215 -215
  161. package/server/engine/mcp/tools/web-search.js +548 -548
  162. package/server/engine/mcp/tools/write-on-board.js +130 -0
  163. package/server/engine/motion/inventory.js +349 -349
  164. package/server/engine/perception/session.js +235 -235
  165. package/server/engine/plugins/nodesign/.claude-plugin/plugin.json +5 -5
  166. package/server/engine/plugins/nodesign/skills/deskskill-engine-mini/SKILL.md +199 -192
  167. package/server/engine/plugins/nodesign/skills/deskskill-engine-mini/canvas.template.html +667 -667
  168. package/server/engine/plugins/nodesign/skills/deskskill-engine-mini/patterns/hybrid-grid.md +22 -22
  169. package/server/engine/plugins/nodesign/skills/deskskill-engine-mini/patterns/image-led-cover.md +25 -25
  170. package/server/engine/plugins/nodesign/skills/deskskill-engine-mini/patterns/portrait.md +23 -23
  171. package/server/engine/plugins/nodesign/skills/deskskill-engine-mini/patterns/quote-backdrop.md +21 -21
  172. package/server/engine/plugins/nodesign/skills/deskskill-engine-mini/patterns/section-divider.md +23 -23
  173. package/server/engine/plugins/nodesign/skills/deskskill-engine-mini/patterns/text-led.md +21 -21
  174. package/server/engine/plugins/nodesign/skills/docx-craft/SKILL.md +169 -162
  175. package/server/engine/plugins/nodesign/skills/docx-craft/references/token-schema.md +407 -407
  176. package/server/engine/plugins/nodesign/skills/docx-craft//346/226/207/346/241/243.template.json +80 -80
  177. package/server/engine/plugins/nodesign/skills/rp-craft/SKILL.md +235 -228
  178. 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 -78
  179. package/server/engine/plugins/nodesign/skills/rp-craft//346/274/224/345/207/272.template.js +268 -268
  180. package/server/engine/plugins/nodesign/skills/site-craft/SKILL.md +330 -323
  181. package/server/engine/plugins/nodesign/skills/site-craft/patterns/build-lane.md +134 -134
  182. package/server/engine/plugins/nodesign/skills/site-craft/patterns/cutout-collage.md +61 -61
  183. package/server/engine/plugins/nodesign/skills/site-craft/patterns/mock-app.md +163 -163
  184. package/server/engine/plugins/nodesign/skills/site-craft/patterns/page-transitions.md +86 -86
  185. package/server/engine/runs/active-runs.js +698 -698
  186. package/server/engine/runs/board-tasklist.js +130 -0
  187. package/server/engine/runs/live-turn.js +252 -252
  188. package/server/engine/runs/store.js +364 -364
  189. package/server/engine/runs/turn-relay.js +217 -217
  190. package/server/engine/runtime/workspace.js +164 -164
  191. package/server/index.js +3 -0
  192. package/server/lib/artifact-file-path.js +80 -80
  193. package/server/lib/artifact-target.js +284 -284
  194. package/server/lib/asset-refs.js +239 -239
  195. package/server/lib/async-queue.js +104 -104
  196. package/server/lib/auto-relations.js +130 -130
  197. package/server/lib/binding-types.js +72 -60
  198. package/server/lib/board-graph-export.js +191 -0
  199. package/server/lib/board-groups.js +167 -0
  200. package/server/lib/board-hero.js +76 -0
  201. package/server/lib/board-kind-sizes.js +85 -62
  202. package/server/lib/board-relations.js +133 -133
  203. package/server/lib/browse-proxy.js +180 -180
  204. package/server/lib/canvas-id.js +36 -36
  205. package/server/lib/chalk.js +121 -0
  206. package/server/lib/cover.js +227 -227
  207. package/server/lib/danbooru-tags.js +291 -291
  208. package/server/lib/doc-extract.js +156 -156
  209. package/server/lib/docx/build-from-source.js +260 -260
  210. package/server/lib/docx/build.js +490 -490
  211. package/server/lib/docx/dump-styles.js +287 -287
  212. package/server/lib/docx/fonts/nodesign-cjk.conf +164 -164
  213. package/server/lib/docx/merge-runs.js +129 -129
  214. package/server/lib/docx/numbering.js +218 -218
  215. package/server/lib/docx/order.js +152 -152
  216. package/server/lib/docx/rawzip.js +172 -172
  217. package/server/lib/docx/render.js +92 -92
  218. package/server/lib/docx/text-lint.js +225 -225
  219. package/server/lib/docx/tokens.js +335 -335
  220. package/server/lib/docx/units.js +29 -29
  221. package/server/lib/docx/xml.js +291 -291
  222. package/server/lib/docx-pages.js +187 -187
  223. package/server/lib/export-collect.js +289 -289
  224. package/server/lib/export-package.js +210 -210
  225. package/server/lib/html-srcset.js +145 -145
  226. package/server/lib/image-variant.js +460 -460
  227. package/server/lib/ingress/forward-openai-chat.js +305 -305
  228. package/server/lib/ingress/openai-chat.js +491 -491
  229. package/server/lib/ingress/session-notice.js +66 -66
  230. package/server/lib/ingress/session-routes.js +68 -68
  231. package/server/lib/ingress/slot-probe.js +130 -130
  232. package/server/lib/ingress/upstream-billing.js +52 -52
  233. package/server/lib/ingress/upstream-fail-streak.js +72 -72
  234. package/server/lib/ingress/upstream-truncation.js +48 -48
  235. package/server/lib/issues-store.js +182 -182
  236. package/server/lib/kinds/deck.js +72 -72
  237. package/server/lib/kinds/docx.js +283 -283
  238. package/server/lib/kinds/file-kinds.js +113 -113
  239. package/server/lib/kinds/index.js +202 -202
  240. package/server/lib/kinds/site.js +161 -161
  241. package/server/lib/model-ingress.js +543 -543
  242. package/server/lib/moderation.js +255 -255
  243. package/server/lib/notice-store.js +103 -103
  244. package/server/lib/plugin-install.js +104 -104
  245. package/server/lib/plugin-validator.js +721 -721
  246. package/server/lib/publish-store.js +115 -115
  247. package/server/lib/quick-summary.js +52 -52
  248. package/server/lib/quota.js +270 -270
  249. package/server/lib/rate-window.js +29 -29
  250. package/server/lib/reference-assets.js +92 -92
  251. package/server/lib/region-shot.js +135 -135
  252. package/server/lib/safe-path.js +73 -73
  253. package/server/lib/sdk-session.js +33 -33
  254. package/server/lib/showcase-store.js +92 -92
  255. package/server/lib/site-publish.js +465 -465
  256. package/server/lib/sketch-layout.js +277 -0
  257. package/server/lib/ssrf-guard.js +432 -432
  258. package/server/lib/task-scan.js +158 -158
  259. package/server/lib/tavern-json.js +154 -154
  260. package/server/lib/video-variant.js +237 -237
  261. package/server/lib/workspace-path.js +33 -33
  262. package/server/ops/fix-sdk-musl.mjs +40 -40
  263. package/server/ops/install-macos-fonts.sh +114 -114
  264. package/server/ops/macos-fonts.conf +336 -336
  265. package/server/ops/sandbox-shim/bwrap +57 -57
  266. package/server/projects/assets-summary.js +119 -119
  267. package/server/projects/auto-name.js +53 -53
  268. package/server/projects/board-limits.js +12 -0
  269. package/server/projects/board-sanitize.js +252 -0
  270. package/server/projects/board-store.js +507 -640
  271. package/server/projects/move-entry.js +103 -103
  272. package/server/projects/store.js +270 -270
  273. package/server/projects/ui-config.js +60 -54
  274. package/server/projects/viewpoint-store.js +78 -0
  275. package/server/projects/workspace-templates.js +53 -53
  276. package/server/projects/workspace.js +1025 -1025
  277. package/server/runtime/local-config.js +177 -177
  278. package/server/runtime/profile.js +72 -72
  279. package/server/services/rembg-service.py +334 -334
  280. package/server/shared/README.md +25 -25
  281. package/server/shared/deck.js +54 -54
  282. package/server/shared/time.js +49 -49
  283. package/server/style-pipeline/README.md +53 -53
  284. package/server/ws/broker.js +39 -36
  285. package/server/ws/browse-channel.js +177 -177
  286. package/server/ws/index.js +386 -386
  287. package/web/README.md +89 -89
  288. package/web/dist/assets/BrowserWindow-P_vIdh-G.js +11 -0
  289. package/web/dist/assets/{DeckWindow-4G0xX7P3.js → DeckWindow-BpLv5082.js} +3 -3
  290. package/web/dist/assets/{DocxWindow-BXQsoPkh.js → DocxWindow-DSAU1-tD.js} +4 -4
  291. package/web/dist/assets/MermaidBlock-DR1GikCv.js +313 -0
  292. package/web/dist/assets/SiteWindow-92uBuRRR.js +18 -0
  293. package/web/dist/assets/abnfDiagram-VCTEODGH-uA8Esix4.js +1 -0
  294. package/web/dist/assets/arc-D_khH_xN.js +1 -0
  295. package/web/dist/assets/architectureDiagram-5GKGNRK7-DydJHqOt.js +36 -0
  296. package/web/dist/assets/blockDiagram-NRAW4CY4-B7wBbKiW.js +129 -0
  297. package/web/dist/assets/c4Diagram-UCG6FXSJ-6mrH0x7Y.js +38 -0
  298. package/web/dist/assets/channel-DGkccMO1.js +1 -0
  299. package/web/dist/assets/chunk-2Q5K7J3B-Cuikho5Q.js +1 -0
  300. package/web/dist/assets/chunk-5VM5RSS4-3-KZ5AUz.js +15 -0
  301. package/web/dist/assets/chunk-F27PBJKO-Bkwq2gGU.js +1 -0
  302. package/web/dist/assets/chunk-G27WJ6UU-BLyQzdGE.js +231 -0
  303. package/web/dist/assets/chunk-JWPE2WC7-B7HxxQds.js +1 -0
  304. package/web/dist/assets/chunk-LCL6LL3I-CtXrnUwF.js +206 -0
  305. package/web/dist/assets/chunk-POPQ4Y6H-dxddZlXt.js +1 -0
  306. package/web/dist/assets/chunk-SVP7TREG-DpkmrgHn.js +88 -0
  307. package/web/dist/assets/chunk-XXDRQBXY-BwZ7RbpQ.js +1 -0
  308. package/web/dist/assets/classDiagram-DTDB5LWJ-BGscw3OD.js +1 -0
  309. package/web/dist/assets/classDiagram-v2-JRS7N3AN-BGscw3OD.js +1 -0
  310. package/web/dist/assets/cose-bilkent-JH36ORCC-DGFOsMUt.js +1 -0
  311. package/web/dist/assets/cynefin-OW5HDTMX-DUwn3ehS.js +178 -0
  312. package/web/dist/assets/cynefinDiagram-5FMLGOSQ-DlLAOZ9x.js +62 -0
  313. package/web/dist/assets/cytoscape.esm-BjAbmLdb.js +331 -0
  314. package/web/dist/assets/dagre-3AP2YEHR-CyourLfz.js +4 -0
  315. package/web/dist/assets/defaultLocale-DX6XiGOO.js +1 -0
  316. package/web/dist/assets/diagram-S7CK7UJ4-BTMbx2np.js +30 -0
  317. package/web/dist/assets/diagram-UQ7AKVKN-UK94_1-P.js +41 -0
  318. package/web/dist/assets/diagram-VSXAHHWV-C7zS-v9v.js +3 -0
  319. package/web/dist/assets/diagram-VX7I27RA-DKJRa6CK.js +24 -0
  320. package/web/dist/assets/diagram-Z3DM3KII-kUbAfGAp.js +24 -0
  321. package/web/dist/assets/ebnfDiagram-PWID7BFC-CVW16_vb.js +1 -0
  322. package/web/dist/assets/erDiagram-SSCWMZ5O-BRQMFQ5y.js +99 -0
  323. package/web/dist/assets/flowDiagram-A5DVABFB-S34LpLtK.js +168 -0
  324. package/web/dist/assets/ganttDiagram-EL5Y4UJY-BFiqk1-G.js +292 -0
  325. package/web/dist/assets/gitGraphDiagram-WWUBYQGX-LZsrdyyv.js +106 -0
  326. package/web/dist/assets/index-GAamh1p4.js +2152 -0
  327. package/web/dist/assets/infoDiagram-RXCK75RN-0DvN6KIp.js +2 -0
  328. package/web/dist/assets/init-Gi6I4Gst.js +1 -0
  329. package/web/dist/assets/ishikawaDiagram-5VMMS53U-CaY3A-Bx.js +70 -0
  330. package/web/dist/assets/journeyDiagram-EYS64GPL-BBeiaCrj.js +139 -0
  331. package/web/dist/assets/kanban-definition-3QL26DDD-ux42DRXQ.js +89 -0
  332. package/web/dist/assets/layout-CnXiF_NB.js +1 -0
  333. package/web/dist/assets/linear-BMW0u6G6.js +1 -0
  334. package/web/dist/assets/mindmap-definition-FBJOCRG2-hUxSpdKn.js +96 -0
  335. package/web/dist/assets/ordinal-Cboi1Yqb.js +1 -0
  336. package/web/dist/assets/pegDiagram-XKGWAZYB-aCaQPH0w.js +1 -0
  337. package/web/dist/assets/{pending-edit-apply-DTjo2geW.js → pending-edit-apply-AcdOQ9Ij.js} +6 -6
  338. package/web/dist/assets/pieDiagram-E7YTZNPT-CmOEZ8zg.js +39 -0
  339. package/web/dist/assets/quadrantDiagram-AXDQQJYC-VW6fAhsV.js +7 -0
  340. package/web/dist/assets/railroadDiagram-O6MQD6OU-CKJ7Ba5K.js +1 -0
  341. package/web/dist/assets/requirementDiagram-EFPCY7ZU-pNGkq9Vw.js +84 -0
  342. package/web/dist/assets/{rotate-cw-LMT3x1Zj.js → rotate-cw-KAfChbGV.js} +1 -1
  343. package/web/dist/assets/sankeyDiagram-P5KCCOFB-BIMmDTfZ.js +40 -0
  344. package/web/dist/assets/sequenceDiagram-WJ2MYXX4-BBW7wzKY.js +162 -0
  345. package/web/dist/assets/sizeCapture-X5ZJPWSS-DdkXmLYR.js +1 -0
  346. package/web/dist/assets/{square-dashed-mouse-pointer-CBoAkfhi.js → square-dashed-mouse-pointer-CDUlhUP7.js} +1 -1
  347. package/web/dist/assets/stateDiagram-HBIQ2CUA-W313Uz16.js +1 -0
  348. package/web/dist/assets/stateDiagram-v2-4QOOHH4V-DDhJBZzf.js +1 -0
  349. package/web/dist/assets/swimlanes-XN3QIQJK-C29TL26K.js +1 -0
  350. package/web/dist/assets/swimlanesDiagram-VK2B7HYN-BfN-Xdmu.js +8 -0
  351. package/web/dist/assets/timeline-definition-24CTP7MA-CF0JMObX.js +120 -0
  352. package/web/dist/assets/vennDiagram-4TSXK5OY-DrDFsaH3.js +34 -0
  353. package/web/dist/assets/wardleyDiagram-VM6X3IG4-CVrnRIS_.js +78 -0
  354. package/web/dist/assets/xychartDiagram-S5SC5T6Z-BvkN1zsT.js +7 -0
  355. package/web/dist/index.html +17 -17
  356. package/web/dist/assets/BrowserWindow-Dc7J7CYc.js +0 -11
  357. package/web/dist/assets/SiteWindow-D6eS-2gM.js +0 -18
  358. package/web/dist/assets/index-Cbl3ATvp.js +0 -2125
@@ -1,158 +1,158 @@
1
- /**
2
- * task-scan.js — 任务目录的统一扫描规则(2026-07-29)
3
- *
4
- * 站点接进真实工程形态(构建步骤 / node_modules / 源目录)后,「扫任务目录」
5
- * 这件事在五个地方各写各的:页面清单、整站 zip、断链检测、导出勾选、素材墙。
6
- * 五份跳过规则各自为政的结果是:node_modules 被整站 zip 原样打包、构建缓存
7
- * 触发刷新记账 —— 而且每一处都是静默的。所以收拢到这一个模块。
8
- *
9
- * 两层规则:
10
- * ① 硬清单 —— 永远不看的目录(依赖 / 构建缓存 / VCS)。不可配置,
11
- * 因为「不小心把 node_modules 打进 zip」不该是一个能配出来的状态。
12
- * ② `.ndignore` —— 任务根下的忽略档,gitignore 语法子集,agent 自己写。
13
- * 支持:# 注释、尾 `/`(只匹配目录)、头 `/`(锚定任务根)、`*` `?` `**`。
14
- * 不支持 `!` 反选(要精细控制就少写点通配)。
15
- */
16
-
17
- import path from 'node:path';
18
- import fs from 'node:fs/promises';
19
-
20
- /**
21
- * 工作区里**不是产物**的那些名字。形态扫描(deck / site)一律跳过。
22
- *
23
- * 2026-08-07 扁平化之后才需要这张表:以前形态扫描的根是 `tasks/<任务>/`,
24
- * 一个只装产物的干净目录;现在根是项目工作区,产物跟基础设施住在一起。
25
- * 不挡的话有两个实测会发生的假产物:
26
- * - `canvas.template.html` / `site.template.html`(skill 拷进 cwd 的起手
27
- * 模板)会各自变成一张 deck 卡 —— deck 认"顶层任意 .html";
28
- * - `exports/` 里导出过一个站就会多出一张站点卡 —— 站点认"子目录里有
29
- * index.html",而导出包正好长这样。
30
- *
31
- * 点开头的(.claude / .nd / .git)扫描器本来就跳,不用列。
32
- */
33
- // `agent-memory` 是老符号链接时代的残骸目录名(正规位置是 .claude/agent-memory/),
34
- // 留在这里是为了它万一还在也不会作为一张文件夹卡出现在桌面上。
35
- export const RESERVED_DIRS = new Set(['assets', 'exports', 'notes', 'node_modules', 'agent-memory']);
36
-
37
- /**
38
- * 不当产物看的文件:起手模板(拷进来是给 agent 抄/改的,不是成品)。
39
- * 2026-08-15 加 js/mjs —— RP 的管线模块 `演出.template.js` 走同一条起手文件路,
40
- * 规则只认 html/css 的话它会当成一张产物卡上墙。
41
- */
42
- export function isReservedFile(name) {
43
- return /\.template\.(html?|css|jsx?|mjs)$/i.test(name) || RESERVED_FILES.has(name);
44
- }
45
-
46
- /**
47
- * 基础设施文件:住在工作区里,但**不是用户的东西**,不该在桌面上出现。
48
- *
49
- * 2026-08-08 加。在这之前只挡起手模板,于是 `board.json`(画布自己的布局档)
50
- * 会作为一张 .json 文件卡上墙 —— 画布把自己的存档画在自己身上。
51
- */
52
- export const RESERVED_FILES = new Set([
53
- 'board.json', // 画布布局,画布自己的存档
54
- '.nd-project.json', // 产物标记(形态兜底)
55
- '.gitignore',
56
- '.ndignore',
57
- ]);
58
-
59
- /** 永远不扫的目录名(任何深度命中即剪枝) */
60
- export const HARD_IGNORE_DIRS = new Set([
61
- 'node_modules', '.git', '.wrangler', '.cache', '.next', '.astro', '.nuxt',
62
- '.svelte-kit', '.output', '.parcel-cache', '.turbo', '.vite',
63
- 'venv', '.venv', '__pycache__', 'coverage',
64
- ]);
65
-
66
- /** 展品目录:站点的试作放这里,单独渲染成卡,不算站点页面、不进 zip */
67
- export const DRAFTS_DIR = '_drafts';
68
-
69
- export const NDIGNORE_FILE = '.ndignore';
70
-
71
- /** gitignore 子集 → RegExp。认不出的行按字面量处理(转义后精确匹配)。 */
72
- function compilePattern(line) {
73
- let pat = line.trim();
74
- if (!pat || pat.startsWith('#')) return null;
75
- const dirOnly = pat.endsWith('/');
76
- if (dirOnly) pat = pat.slice(0, -1);
77
- const anchored = pat.startsWith('/');
78
- if (anchored) pat = pat.slice(1);
79
- // 转义正则元字符,再还原通配语义(** → 任意层,* → 段内任意,? → 段内单字符)
80
- let re = pat
81
- .replace(/[.+^${}()|[\]\\]/g, '\\$&')
82
- .replace(/\*\*/g, '\uE000')
83
- .replace(/\*/g, '[^/]*')
84
- .replace(/\?/g, '[^/]')
85
- .replace(/\uE000/g, '.*');
86
- // 无斜杠的裸模式(`*.bak`)匹配任何一层的条目名;带斜杠 / 锚定的按路径匹配
87
- const body = (!anchored && !pat.includes('/')) ? `(?:^|/)${re}` : `^${re}`;
88
- return { re: new RegExp(`${body}(?:/|$)`), dirOnly };
89
- }
90
-
91
- /**
92
- * 读任务的 `.ndignore`,返回匹配器 `(rel, isDir) => boolean`。
93
- * rel 相对任务根、'/' 分隔、不带前导斜杠。没有忽略档 → 只有硬清单生效。
94
- */
95
- export async function loadIgnore(taskDir) {
96
- let rules = [];
97
- try {
98
- const raw = await fs.readFile(path.join(taskDir, NDIGNORE_FILE), 'utf8');
99
- rules = raw.split('\n').map(compilePattern).filter(Boolean);
100
- } catch { /* 没有 .ndignore:正常 */ }
101
- return (rel, isDir = false) => {
102
- const segs = rel.split('/');
103
- // 硬清单:路径里任何一段命中都算(父目录被剪枝时子路径根本走不到这,
104
- // 这里再查一遍是给「直接拿 rel 来问」的调用方兜底)
105
- if (segs.some(s => HARD_IGNORE_DIRS.has(s))) return true;
106
- for (const r of rules) {
107
- if (r.dirOnly && !isDir && !r.re.test(rel + '/')) continue;
108
- if (r.re.test(rel)) return true;
109
- }
110
- return false;
111
- };
112
- }
113
-
114
- /**
115
- * 递归列任务目录下的文件(应用硬清单 + .ndignore + 跳 dot 条目)。
116
- *
117
- * @param {string} rootDir 绝对路径(任务根或产物根)
118
- * @param {object} [opts]
119
- * @param {number} [opts.maxDepth=6]
120
- * @param {Function} [opts.ignore] loadIgnore 的返回值;不传就现读
121
- * @param {string} [opts.ignoreBase] ignore 规则的基准目录(缺省 = rootDir)。
122
- * 产物根是子目录时,.ndignore 在任务根,
123
- * rel 要先换算回任务根视角再问匹配器。
124
- * @param {boolean} [opts.includeDrafts=false] 是否进 `_drafts/`
125
- * @returns {Promise<Array<{rel: string, abs: string, name: string, size: number}>>}
126
- */
127
- export async function walkTaskFiles(rootDir, opts = {}) {
128
- const { maxDepth = 6, includeDrafts = false } = opts;
129
- const ignoreBase = opts.ignoreBase || rootDir;
130
- const ignore = opts.ignore || await loadIgnore(ignoreBase);
131
- const out = [];
132
- const toIgnoreRel = (abs) => path.relative(ignoreBase, abs).split(path.sep).join('/');
133
-
134
- const walk = async (dir, prefix, depth) => {
135
- if (depth > maxDepth) return;
136
- let entries;
137
- try { entries = await fs.readdir(dir, { withFileTypes: true }); } catch { return; }
138
- for (const e of entries) {
139
- if (e.name.startsWith('.')) continue;
140
- const abs = path.join(dir, e.name);
141
- const rel = prefix ? `${prefix}/${e.name}` : e.name;
142
- if (e.isDirectory()) {
143
- if (HARD_IGNORE_DIRS.has(e.name)) continue;
144
- if (!includeDrafts && e.name === DRAFTS_DIR) continue;
145
- if (ignore(toIgnoreRel(abs), true)) continue;
146
- await walk(abs, rel, depth + 1);
147
- continue;
148
- }
149
- if (!e.isFile()) continue;
150
- if (ignore(toIgnoreRel(abs), false)) continue;
151
- let size = 0;
152
- try { size = (await fs.stat(abs)).size; } catch { continue; }
153
- out.push({ rel, abs, name: e.name, size });
154
- }
155
- };
156
- await walk(rootDir, '', 1);
157
- return out;
158
- }
1
+ /**
2
+ * task-scan.js — 任务目录的统一扫描规则(2026-07-29)
3
+ *
4
+ * 站点接进真实工程形态(构建步骤 / node_modules / 源目录)后,「扫任务目录」
5
+ * 这件事在五个地方各写各的:页面清单、整站 zip、断链检测、导出勾选、素材墙。
6
+ * 五份跳过规则各自为政的结果是:node_modules 被整站 zip 原样打包、构建缓存
7
+ * 触发刷新记账 —— 而且每一处都是静默的。所以收拢到这一个模块。
8
+ *
9
+ * 两层规则:
10
+ * ① 硬清单 —— 永远不看的目录(依赖 / 构建缓存 / VCS)。不可配置,
11
+ * 因为「不小心把 node_modules 打进 zip」不该是一个能配出来的状态。
12
+ * ② `.ndignore` —— 任务根下的忽略档,gitignore 语法子集,agent 自己写。
13
+ * 支持:# 注释、尾 `/`(只匹配目录)、头 `/`(锚定任务根)、`*` `?` `**`。
14
+ * 不支持 `!` 反选(要精细控制就少写点通配)。
15
+ */
16
+
17
+ import path from 'node:path';
18
+ import fs from 'node:fs/promises';
19
+
20
+ /**
21
+ * 工作区里**不是产物**的那些名字。形态扫描(deck / site)一律跳过。
22
+ *
23
+ * 2026-08-07 扁平化之后才需要这张表:以前形态扫描的根是 `tasks/<任务>/`,
24
+ * 一个只装产物的干净目录;现在根是项目工作区,产物跟基础设施住在一起。
25
+ * 不挡的话有两个实测会发生的假产物:
26
+ * - `canvas.template.html` / `site.template.html`(skill 拷进 cwd 的起手
27
+ * 模板)会各自变成一张 deck 卡 —— deck 认"顶层任意 .html";
28
+ * - `exports/` 里导出过一个站就会多出一张站点卡 —— 站点认"子目录里有
29
+ * index.html",而导出包正好长这样。
30
+ *
31
+ * 点开头的(.claude / .nd / .git)扫描器本来就跳,不用列。
32
+ */
33
+ // `agent-memory` 是老符号链接时代的残骸目录名(正规位置是 .claude/agent-memory/),
34
+ // 留在这里是为了它万一还在也不会作为一张文件夹卡出现在桌面上。
35
+ export const RESERVED_DIRS = new Set(['assets', 'exports', 'notes', 'node_modules', 'agent-memory']);
36
+
37
+ /**
38
+ * 不当产物看的文件:起手模板(拷进来是给 agent 抄/改的,不是成品)。
39
+ * 2026-08-15 加 js/mjs —— RP 的管线模块 `演出.template.js` 走同一条起手文件路,
40
+ * 规则只认 html/css 的话它会当成一张产物卡上墙。
41
+ */
42
+ export function isReservedFile(name) {
43
+ return /\.template\.(html?|css|jsx?|mjs)$/i.test(name) || RESERVED_FILES.has(name);
44
+ }
45
+
46
+ /**
47
+ * 基础设施文件:住在工作区里,但**不是用户的东西**,不该在桌面上出现。
48
+ *
49
+ * 2026-08-08 加。在这之前只挡起手模板,于是 `board.json`(画布自己的布局档)
50
+ * 会作为一张 .json 文件卡上墙 —— 画布把自己的存档画在自己身上。
51
+ */
52
+ export const RESERVED_FILES = new Set([
53
+ 'board.json', // 画布布局,画布自己的存档
54
+ '.nd-project.json', // 产物标记(形态兜底)
55
+ '.gitignore',
56
+ '.ndignore',
57
+ ]);
58
+
59
+ /** 永远不扫的目录名(任何深度命中即剪枝) */
60
+ export const HARD_IGNORE_DIRS = new Set([
61
+ 'node_modules', '.git', '.wrangler', '.cache', '.next', '.astro', '.nuxt',
62
+ '.svelte-kit', '.output', '.parcel-cache', '.turbo', '.vite',
63
+ 'venv', '.venv', '__pycache__', 'coverage',
64
+ ]);
65
+
66
+ /** 展品目录:站点的试作放这里,单独渲染成卡,不算站点页面、不进 zip */
67
+ export const DRAFTS_DIR = '_drafts';
68
+
69
+ export const NDIGNORE_FILE = '.ndignore';
70
+
71
+ /** gitignore 子集 → RegExp。认不出的行按字面量处理(转义后精确匹配)。 */
72
+ function compilePattern(line) {
73
+ let pat = line.trim();
74
+ if (!pat || pat.startsWith('#')) return null;
75
+ const dirOnly = pat.endsWith('/');
76
+ if (dirOnly) pat = pat.slice(0, -1);
77
+ const anchored = pat.startsWith('/');
78
+ if (anchored) pat = pat.slice(1);
79
+ // 转义正则元字符,再还原通配语义(** → 任意层,* → 段内任意,? → 段内单字符)
80
+ let re = pat
81
+ .replace(/[.+^${}()|[\]\\]/g, '\\$&')
82
+ .replace(/\*\*/g, '\uE000')
83
+ .replace(/\*/g, '[^/]*')
84
+ .replace(/\?/g, '[^/]')
85
+ .replace(/\uE000/g, '.*');
86
+ // 无斜杠的裸模式(`*.bak`)匹配任何一层的条目名;带斜杠 / 锚定的按路径匹配
87
+ const body = (!anchored && !pat.includes('/')) ? `(?:^|/)${re}` : `^${re}`;
88
+ return { re: new RegExp(`${body}(?:/|$)`), dirOnly };
89
+ }
90
+
91
+ /**
92
+ * 读任务的 `.ndignore`,返回匹配器 `(rel, isDir) => boolean`。
93
+ * rel 相对任务根、'/' 分隔、不带前导斜杠。没有忽略档 → 只有硬清单生效。
94
+ */
95
+ export async function loadIgnore(taskDir) {
96
+ let rules = [];
97
+ try {
98
+ const raw = await fs.readFile(path.join(taskDir, NDIGNORE_FILE), 'utf8');
99
+ rules = raw.split('\n').map(compilePattern).filter(Boolean);
100
+ } catch { /* 没有 .ndignore:正常 */ }
101
+ return (rel, isDir = false) => {
102
+ const segs = rel.split('/');
103
+ // 硬清单:路径里任何一段命中都算(父目录被剪枝时子路径根本走不到这,
104
+ // 这里再查一遍是给「直接拿 rel 来问」的调用方兜底)
105
+ if (segs.some(s => HARD_IGNORE_DIRS.has(s))) return true;
106
+ for (const r of rules) {
107
+ if (r.dirOnly && !isDir && !r.re.test(rel + '/')) continue;
108
+ if (r.re.test(rel)) return true;
109
+ }
110
+ return false;
111
+ };
112
+ }
113
+
114
+ /**
115
+ * 递归列任务目录下的文件(应用硬清单 + .ndignore + 跳 dot 条目)。
116
+ *
117
+ * @param {string} rootDir 绝对路径(任务根或产物根)
118
+ * @param {object} [opts]
119
+ * @param {number} [opts.maxDepth=6]
120
+ * @param {Function} [opts.ignore] loadIgnore 的返回值;不传就现读
121
+ * @param {string} [opts.ignoreBase] ignore 规则的基准目录(缺省 = rootDir)。
122
+ * 产物根是子目录时,.ndignore 在任务根,
123
+ * rel 要先换算回任务根视角再问匹配器。
124
+ * @param {boolean} [opts.includeDrafts=false] 是否进 `_drafts/`
125
+ * @returns {Promise<Array<{rel: string, abs: string, name: string, size: number}>>}
126
+ */
127
+ export async function walkTaskFiles(rootDir, opts = {}) {
128
+ const { maxDepth = 6, includeDrafts = false } = opts;
129
+ const ignoreBase = opts.ignoreBase || rootDir;
130
+ const ignore = opts.ignore || await loadIgnore(ignoreBase);
131
+ const out = [];
132
+ const toIgnoreRel = (abs) => path.relative(ignoreBase, abs).split(path.sep).join('/');
133
+
134
+ const walk = async (dir, prefix, depth) => {
135
+ if (depth > maxDepth) return;
136
+ let entries;
137
+ try { entries = await fs.readdir(dir, { withFileTypes: true }); } catch { return; }
138
+ for (const e of entries) {
139
+ if (e.name.startsWith('.')) continue;
140
+ const abs = path.join(dir, e.name);
141
+ const rel = prefix ? `${prefix}/${e.name}` : e.name;
142
+ if (e.isDirectory()) {
143
+ if (HARD_IGNORE_DIRS.has(e.name)) continue;
144
+ if (!includeDrafts && e.name === DRAFTS_DIR) continue;
145
+ if (ignore(toIgnoreRel(abs), true)) continue;
146
+ await walk(abs, rel, depth + 1);
147
+ continue;
148
+ }
149
+ if (!e.isFile()) continue;
150
+ if (ignore(toIgnoreRel(abs), false)) continue;
151
+ let size = 0;
152
+ try { size = (await fs.stat(abs)).size; } catch { continue; }
153
+ out.push({ rel, abs, name: e.name, size });
154
+ }
155
+ };
156
+ await walk(rootDir, '', 1);
157
+ return out;
158
+ }
@@ -1,154 +1,154 @@
1
- /**
2
- * tavern-json —— 酒馆(SillyTavern)导出 JSON 的解析与摘要(2026-08-15)
3
- *
4
- * 起因是一份真文件:`Izumi 0814.json`,464KB、210 条提示词,而**启用的只有 56 条**
5
- * (合计约 9.8k 字符)。agent 拿 Read 去读它 = 十几万 token 进上下文换来一堆停用
6
- * 的备选条目 —— 这不是"贵一点",是把上下文烧光还看不清结构。所以搬酒馆的东西
7
- * 进来必须先过这一层:**先给结构摘要,再按需取正文**。
8
- *
9
- * 认三种形态(酒馆导出的 JSON 就这几类):
10
- * preset Chat Completion 预设 —— `prompts[]` + `prompt_order[]`。管的是
11
- * "每次请求怎么拼上下文 + 采样参数",跟我们的 编排.yaml 同类。
12
- * 条目里那些 0 字的是 **marker**(角色描述/性格/场景/Chat History…),
13
- * 运行时由酒馆填真内容,搬过来一律丢掉 —— 我们由 编排.yaml 自己管。
14
- * card 角色卡 V2(`spec: chara_card_v2`)或 V1。人设、开场白、示例对话,
15
- * 可能内嵌世界书(character_book)。
16
- * lorebook 世界书单独导出 —— `entries`,每条带 keys(触发词)与 constant(常驻)。
17
- *
18
- * 只读不写:转成 编排.yaml 和设定文件是 agent 的活,它得按题材决定怎么分组、
19
- * 哪些不要(破甲话术在我们这条通路上没意义)。
20
- */
21
-
22
- /** 摘要里每条正文最多带多少字的引子 */
23
- const PEEK = 60;
24
-
25
- export function detectKind(doc) {
26
- if (!doc || typeof doc !== 'object') return null;
27
- if (Array.isArray(doc.prompts) && Array.isArray(doc.prompt_order)) return 'preset';
28
- if (doc.spec === 'chara_card_v2' || doc.spec === 'chara_card_v3') return 'card';
29
- if (doc.data && typeof doc.data === 'object' && typeof doc.data.first_mes === 'string') return 'card';
30
- if (typeof doc.first_mes === 'string' && typeof doc.description === 'string') return 'card';
31
- if (doc.entries && typeof doc.entries === 'object') return 'lorebook';
32
- return null;
33
- }
34
-
35
- const 引 = (s) => String(s || '').replace(/\s+/g, ' ').trim().slice(0, PEEK);
36
- const 字 = (s) => String(s || '').length;
37
-
38
- /** 预设:启用的条目按 order 顺序排开,停用的只报个数 */
39
- export function digestPreset(doc) {
40
- const byId = new Map((doc.prompts || []).map(p => [p.identifier, p]));
41
- const order = (doc.prompt_order?.[0]?.order) || [];
42
- const 启用 = [];
43
- const 停用 = [];
44
- for (const o of order) {
45
- const p = byId.get(o.identifier);
46
- if (!p) continue;
47
- const 条 = {
48
- id: p.identifier,
49
- 名字: p.name || p.identifier,
50
- 角色: p.role || 'system',
51
- 字数: 字(p.content),
52
- 占位: p.marker === true, // 运行时由酒馆填(角色卡/历史)
53
- 分隔: p.marker !== true && 字(p.content) === 0, // 纯分节标题("可选功能开始"这种)
54
- 深度: p.injection_position === 1 ? (p.injection_depth ?? 0) : null,
55
- 引子: 引(p.content),
56
- };
57
- (o.enabled ? 启用 : 停用).push(条);
58
- }
59
- const 实条 = 启用.filter(e => e.字数 > 0);
60
- return {
61
- 形态: 'preset',
62
- 参数: {
63
- temperature: doc.temperature, top_p: doc.top_p, top_k: doc.top_k,
64
- frequency_penalty: doc.frequency_penalty, presence_penalty: doc.presence_penalty,
65
- 最大上下文: doc.openai_max_context, 最大输出: doc.openai_max_tokens,
66
- reasoning_effort: doc.reasoning_effort, squash_system_messages: doc.squash_system_messages,
67
- },
68
- 启用, 停用,
69
- 合计字数: 实条.reduce((n, e) => n + e.字数, 0),
70
- 占位条目: 启用.filter(e => e.占位).map(e => e.名字),
71
- 分隔条目: 启用.filter(e => e.分隔).map(e => e.名字),
72
- };
73
- }
74
-
75
- /** 角色卡:字段清单 + 内嵌世界书条目表 */
76
- export function digestCard(doc) {
77
- const d = doc.data && typeof doc.data === 'object' ? doc.data : doc;
78
- const 字段 = ['name', 'description', 'personality', 'scenario', 'first_mes', 'mes_example',
79
- 'system_prompt', 'post_history_instructions', 'creator_notes']
80
- .map(k => ({ 字段: k, 字数: 字(d[k]), 引子: 引(d[k]) }))
81
- .filter(x => x.字数 > 0);
82
- const book = d.character_book || doc.character_book || null;
83
- return {
84
- 形态: 'card',
85
- 名字: d.name || '(无名)',
86
- 字段,
87
- 开场白备选: Array.isArray(d.alternate_greetings) ? d.alternate_greetings.length : 0,
88
- 世界书: book ? digestLorebook(book).条目 : [],
89
- };
90
- }
91
-
92
- /** 世界书:每条的触发词、常驻与否、字数 */
93
- export function digestLorebook(doc) {
94
- const raw = doc.entries;
95
- const list = Array.isArray(raw) ? raw : Object.values(raw || {});
96
- const 条目 = list.map((e, i) => ({
97
- id: String(e.uid ?? e.id ?? i),
98
- 名字: e.comment || e.name || (Array.isArray(e.key) ? e.key.join('/') : '') || `条目${i + 1}`,
99
- 触发: Array.isArray(e.key) ? e.key : (Array.isArray(e.keys) ? e.keys : []),
100
- 常驻: !!(e.constant),
101
- 停用: e.enabled === false || e.disable === true,
102
- 字数: 字(e.content),
103
- 引子: 引(e.content),
104
- }));
105
- return { 形态: 'lorebook', 条目 };
106
- }
107
-
108
- export function digest(doc) {
109
- const kind = detectKind(doc);
110
- if (kind === 'preset') return digestPreset(doc);
111
- if (kind === 'card') return digestCard(doc);
112
- if (kind === 'lorebook') return digestLorebook(doc);
113
- return null;
114
- }
115
-
116
- /**
117
- * 按名字/id 取正文。名字支持部分匹配(酒馆的条目名带 emoji,让 agent 原样抄
118
- * 一串 emoji 当参数是给自己找麻烦)。
119
- * @returns {{名字: string, 角色?: string, 正文: string}[]}
120
- */
121
- export function fetchEntries(doc, 选) {
122
- const want = (选 || []).map(s => String(s).trim()).filter(Boolean);
123
- if (!want.length) return [];
124
- const kind = detectKind(doc);
125
- let 池 = [];
126
- if (kind === 'preset') {
127
- 池 = (doc.prompts || []).map(p => ({ id: p.identifier, 名字: p.name || p.identifier, 角色: p.role || 'system', 正文: String(p.content || '') }));
128
- } else if (kind === 'lorebook' || (kind === 'card' && (doc.data?.character_book || doc.character_book))) {
129
- const book = kind === 'lorebook' ? doc : (doc.data?.character_book || doc.character_book);
130
- const list = Array.isArray(book.entries) ? book.entries : Object.values(book.entries || {});
131
- 池 = list.map((e, i) => ({
132
- id: String(e.uid ?? e.id ?? i),
133
- 名字: e.comment || e.name || (Array.isArray(e.key) ? e.key.join('/') : `条目${i + 1}`),
134
- 正文: String(e.content || ''),
135
- }));
136
- }
137
- if (kind === 'card') {
138
- const d = doc.data && typeof doc.data === 'object' ? doc.data : doc;
139
- for (const k of ['description', 'personality', 'scenario', 'first_mes', 'mes_example',
140
- 'system_prompt', 'post_history_instructions']) {
141
- if (d[k]) 池.push({ id: k, 名字: k, 正文: String(d[k]) });
142
- }
143
- if (Array.isArray(d.alternate_greetings)) {
144
- d.alternate_greetings.forEach((g, i) => 池.push({ id: `alt_greeting_${i}`, 名字: `alternate_greetings[${i}]`, 正文: String(g || '') }));
145
- }
146
- }
147
- const 出 = [];
148
- const 见 = new Set();
149
- for (const w of want) {
150
- const hit = 池.find(x => x.id === w) || 池.find(x => x.名字 === w) || 池.find(x => x.名字.includes(w));
151
- if (hit && !见.has(hit.id)) { 见.add(hit.id); 出.push(hit); }
152
- }
153
- return 出;
154
- }
1
+ /**
2
+ * tavern-json —— 酒馆(SillyTavern)导出 JSON 的解析与摘要(2026-08-15)
3
+ *
4
+ * 起因是一份真文件:`Izumi 0814.json`,464KB、210 条提示词,而**启用的只有 56 条**
5
+ * (合计约 9.8k 字符)。agent 拿 Read 去读它 = 十几万 token 进上下文换来一堆停用
6
+ * 的备选条目 —— 这不是"贵一点",是把上下文烧光还看不清结构。所以搬酒馆的东西
7
+ * 进来必须先过这一层:**先给结构摘要,再按需取正文**。
8
+ *
9
+ * 认三种形态(酒馆导出的 JSON 就这几类):
10
+ * preset Chat Completion 预设 —— `prompts[]` + `prompt_order[]`。管的是
11
+ * "每次请求怎么拼上下文 + 采样参数",跟我们的 编排.yaml 同类。
12
+ * 条目里那些 0 字的是 **marker**(角色描述/性格/场景/Chat History…),
13
+ * 运行时由酒馆填真内容,搬过来一律丢掉 —— 我们由 编排.yaml 自己管。
14
+ * card 角色卡 V2(`spec: chara_card_v2`)或 V1。人设、开场白、示例对话,
15
+ * 可能内嵌世界书(character_book)。
16
+ * lorebook 世界书单独导出 —— `entries`,每条带 keys(触发词)与 constant(常驻)。
17
+ *
18
+ * 只读不写:转成 编排.yaml 和设定文件是 agent 的活,它得按题材决定怎么分组、
19
+ * 哪些不要(破甲话术在我们这条通路上没意义)。
20
+ */
21
+
22
+ /** 摘要里每条正文最多带多少字的引子 */
23
+ const PEEK = 60;
24
+
25
+ export function detectKind(doc) {
26
+ if (!doc || typeof doc !== 'object') return null;
27
+ if (Array.isArray(doc.prompts) && Array.isArray(doc.prompt_order)) return 'preset';
28
+ if (doc.spec === 'chara_card_v2' || doc.spec === 'chara_card_v3') return 'card';
29
+ if (doc.data && typeof doc.data === 'object' && typeof doc.data.first_mes === 'string') return 'card';
30
+ if (typeof doc.first_mes === 'string' && typeof doc.description === 'string') return 'card';
31
+ if (doc.entries && typeof doc.entries === 'object') return 'lorebook';
32
+ return null;
33
+ }
34
+
35
+ const 引 = (s) => String(s || '').replace(/\s+/g, ' ').trim().slice(0, PEEK);
36
+ const 字 = (s) => String(s || '').length;
37
+
38
+ /** 预设:启用的条目按 order 顺序排开,停用的只报个数 */
39
+ export function digestPreset(doc) {
40
+ const byId = new Map((doc.prompts || []).map(p => [p.identifier, p]));
41
+ const order = (doc.prompt_order?.[0]?.order) || [];
42
+ const 启用 = [];
43
+ const 停用 = [];
44
+ for (const o of order) {
45
+ const p = byId.get(o.identifier);
46
+ if (!p) continue;
47
+ const 条 = {
48
+ id: p.identifier,
49
+ 名字: p.name || p.identifier,
50
+ 角色: p.role || 'system',
51
+ 字数: 字(p.content),
52
+ 占位: p.marker === true, // 运行时由酒馆填(角色卡/历史)
53
+ 分隔: p.marker !== true && 字(p.content) === 0, // 纯分节标题("可选功能开始"这种)
54
+ 深度: p.injection_position === 1 ? (p.injection_depth ?? 0) : null,
55
+ 引子: 引(p.content),
56
+ };
57
+ (o.enabled ? 启用 : 停用).push(条);
58
+ }
59
+ const 实条 = 启用.filter(e => e.字数 > 0);
60
+ return {
61
+ 形态: 'preset',
62
+ 参数: {
63
+ temperature: doc.temperature, top_p: doc.top_p, top_k: doc.top_k,
64
+ frequency_penalty: doc.frequency_penalty, presence_penalty: doc.presence_penalty,
65
+ 最大上下文: doc.openai_max_context, 最大输出: doc.openai_max_tokens,
66
+ reasoning_effort: doc.reasoning_effort, squash_system_messages: doc.squash_system_messages,
67
+ },
68
+ 启用, 停用,
69
+ 合计字数: 实条.reduce((n, e) => n + e.字数, 0),
70
+ 占位条目: 启用.filter(e => e.占位).map(e => e.名字),
71
+ 分隔条目: 启用.filter(e => e.分隔).map(e => e.名字),
72
+ };
73
+ }
74
+
75
+ /** 角色卡:字段清单 + 内嵌世界书条目表 */
76
+ export function digestCard(doc) {
77
+ const d = doc.data && typeof doc.data === 'object' ? doc.data : doc;
78
+ const 字段 = ['name', 'description', 'personality', 'scenario', 'first_mes', 'mes_example',
79
+ 'system_prompt', 'post_history_instructions', 'creator_notes']
80
+ .map(k => ({ 字段: k, 字数: 字(d[k]), 引子: 引(d[k]) }))
81
+ .filter(x => x.字数 > 0);
82
+ const book = d.character_book || doc.character_book || null;
83
+ return {
84
+ 形态: 'card',
85
+ 名字: d.name || '(无名)',
86
+ 字段,
87
+ 开场白备选: Array.isArray(d.alternate_greetings) ? d.alternate_greetings.length : 0,
88
+ 世界书: book ? digestLorebook(book).条目 : [],
89
+ };
90
+ }
91
+
92
+ /** 世界书:每条的触发词、常驻与否、字数 */
93
+ export function digestLorebook(doc) {
94
+ const raw = doc.entries;
95
+ const list = Array.isArray(raw) ? raw : Object.values(raw || {});
96
+ const 条目 = list.map((e, i) => ({
97
+ id: String(e.uid ?? e.id ?? i),
98
+ 名字: e.comment || e.name || (Array.isArray(e.key) ? e.key.join('/') : '') || `条目${i + 1}`,
99
+ 触发: Array.isArray(e.key) ? e.key : (Array.isArray(e.keys) ? e.keys : []),
100
+ 常驻: !!(e.constant),
101
+ 停用: e.enabled === false || e.disable === true,
102
+ 字数: 字(e.content),
103
+ 引子: 引(e.content),
104
+ }));
105
+ return { 形态: 'lorebook', 条目 };
106
+ }
107
+
108
+ export function digest(doc) {
109
+ const kind = detectKind(doc);
110
+ if (kind === 'preset') return digestPreset(doc);
111
+ if (kind === 'card') return digestCard(doc);
112
+ if (kind === 'lorebook') return digestLorebook(doc);
113
+ return null;
114
+ }
115
+
116
+ /**
117
+ * 按名字/id 取正文。名字支持部分匹配(酒馆的条目名带 emoji,让 agent 原样抄
118
+ * 一串 emoji 当参数是给自己找麻烦)。
119
+ * @returns {{名字: string, 角色?: string, 正文: string}[]}
120
+ */
121
+ export function fetchEntries(doc, 选) {
122
+ const want = (选 || []).map(s => String(s).trim()).filter(Boolean);
123
+ if (!want.length) return [];
124
+ const kind = detectKind(doc);
125
+ let 池 = [];
126
+ if (kind === 'preset') {
127
+ 池 = (doc.prompts || []).map(p => ({ id: p.identifier, 名字: p.name || p.identifier, 角色: p.role || 'system', 正文: String(p.content || '') }));
128
+ } else if (kind === 'lorebook' || (kind === 'card' && (doc.data?.character_book || doc.character_book))) {
129
+ const book = kind === 'lorebook' ? doc : (doc.data?.character_book || doc.character_book);
130
+ const list = Array.isArray(book.entries) ? book.entries : Object.values(book.entries || {});
131
+ 池 = list.map((e, i) => ({
132
+ id: String(e.uid ?? e.id ?? i),
133
+ 名字: e.comment || e.name || (Array.isArray(e.key) ? e.key.join('/') : `条目${i + 1}`),
134
+ 正文: String(e.content || ''),
135
+ }));
136
+ }
137
+ if (kind === 'card') {
138
+ const d = doc.data && typeof doc.data === 'object' ? doc.data : doc;
139
+ for (const k of ['description', 'personality', 'scenario', 'first_mes', 'mes_example',
140
+ 'system_prompt', 'post_history_instructions']) {
141
+ if (d[k]) 池.push({ id: k, 名字: k, 正文: String(d[k]) });
142
+ }
143
+ if (Array.isArray(d.alternate_greetings)) {
144
+ d.alternate_greetings.forEach((g, i) => 池.push({ id: `alt_greeting_${i}`, 名字: `alternate_greetings[${i}]`, 正文: String(g || '') }));
145
+ }
146
+ }
147
+ const 出 = [];
148
+ const 见 = new Set();
149
+ for (const w of want) {
150
+ const hit = 池.find(x => x.id === w) || 池.find(x => x.名字 === w) || 池.find(x => x.名字.includes(w));
151
+ if (hit && !见.has(hit.id)) { 见.add(hit.id); 出.push(hit); }
152
+ }
153
+ return 出;
154
+ }