@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,284 +1,284 @@
1
- /**
2
- * artifact-target.js — 产物寻址(2026-07-28 由 canvas-target.js 泛化;
3
- * 2026-07-29 形态判定与解析下沉到 kinds/ 注册表;2026-08-07 任务层退役)
4
- *
5
- * 定死三件事:
6
- *
7
- * **① 产物有形态(kind),文件就是真相。** 判定规则在 kinds/ 注册表:
8
- * canvas.html → deck;index.html(工作区根 / 声明的产物根 / 约定构建目录)
9
- * → site。
10
- *
11
- * **② 寻址永远带着 kind 一起返回。** 下游(截图 / 分页 / 导出 / fit 注入)必须
12
- * 能看见"这是 deck 还是 site",才可能给出对的行为。kind 每次 resolve 按工作区
13
- * 现状重算,不缓存。
14
- *
15
- * **③ 源和产物可以分开。** 构建型站点的源在工作区根、产物在 dist/ 之类的产物根。
16
- * resolve 返回 workspaceRoot(源,agent 的地盘)和 artifactDir(被预览 / 导出 /
17
- * 发布的根),deck 和手写站点两者相同。消费方要"看"产物就用 artifactDir,
18
- * 别再自己 dirname(entryPath)。
19
- *
20
- * 寻址顺序(越靠前越明确):
21
- * ① 调用方显式给的 path
22
- * ② 本会话的"当前产物"——最近写过 / 截过 / 预览过哪份就是哪份
23
- * ③ 工作区只有一份产物就是它;多份就报错列出来让调用方指定
24
- *
25
- * ## 2026-08-07:第 ③ 条曾经是第 ④ 条
26
- *
27
- * 中间夹着一条「认领绑在本会话名下那个任务」——`tasks/<任务>/.nd-task.json`
28
- * 里记着 sessionId,寻址时按当前会话去反查它的任务。那条规则存在的前提是
29
- * **一个项目里有多个任务、而每个任务属于一个会话**;线上数据说这个前提从来
30
- * 没成立过(13 个有产物的项目,每个都恰好一个任务)。
31
- *
32
- * 任务层拆掉之后,"这个会话的产物"这个问题本身就没有意义了 —— 产物属于项目,
33
- * 谁开的对话都看得见同一批。剩下的歧义(工作区里有多份产物、调用方没说哪份)
34
- * 由 ② 的最近使用和 ③ 的显式报错解决,这两条本来就在,而且不依赖任何绑定。
35
- */
36
-
37
- import path from 'node:path';
38
- import fs from 'node:fs/promises';
39
- import {
40
- KINDS, kindDef, readTaskMarker, taskManifest, artifactOfPath, can,
41
- } from './kinds/index.js';
42
-
43
- export const KIND_DECK = 'deck';
44
- export const KIND_SITE = 'site';
45
-
46
- /** 每种形态的入口文件名(从注册表派生;改形态契约去 kinds/) */
47
- export const ENTRY_FILE = Object.freeze(
48
- Object.fromEntries(Object.values(KINDS).map(k => [k.id, k.entryFile])),
49
- );
50
-
51
- /**
52
- * 产物入口**长什么样**(扩展名集合),同样从注册表派生。
53
- * `.html` / `.htm` 是 deck+site 的形状,`.docx` 是 word 的 —— 加形态时这里
54
- * 自动跟上,不用再回来补一条正则。
55
- */
56
- const ENTRY_EXTS = new Set(
57
- Object.values(KINDS)
58
- .map(k => (k.entryFile.match(/\.[a-z0-9]+$/i) || [''])[0].toLowerCase())
59
- .filter(Boolean)
60
- .concat('.htm'), // html 的老写法,注册表里只写了 .html
61
- );
62
-
63
- // 形态判定与解析的权威在 kinds/,这里转发老名字(消费方 import 不用改两次)
64
- export { readTaskMarker, taskManifest, kindDef, artifactOfPath };
65
- export { formatAllowed, docxClaimedFiles, isDirArtifact } from './kinds/index.js';
66
- export { can };
67
-
68
- /** sessionId → { path, kind }。会话结束不清也无妨(几个短字符串) */
69
- const activeArtifact = new Map();
70
-
71
- /**
72
- * 记住这个会话正在做哪份产物(写文件 / 截图 / 预览时调)。
73
- *
74
- * 只认**产物入口的形状**(ENTRY_EXTS,从注册表派生)。以前还认"任务内的非
75
- * html"(改 style.css 也算在这个任务上干活,寻址时走任务入口),任务层没了之后
76
- * 那一支失去了落点:改 `style.css` 说明不了在做哪一份 deck,工作区根上可能并排
77
- * 放着好几份。认不出来就不认,让 resolve 走"只有一份就是它 / 多份报错"那条 ——
78
- * 比猜一个错的默认目标强。
79
- */
80
- export function setActiveArtifact(sessionId, relPath, kind) {
81
- if (!sessionId || typeof relPath !== 'string') return;
82
- const p = normalizeRel(relPath);
83
- if (/\.template\.(html?|css)$/i.test(p)) return; // 模板不是产物
84
- const ext = (p.match(/\.[a-z0-9]+$/i) || [''])[0].toLowerCase();
85
- if (!ENTRY_EXTS.has(ext)) return;
86
- activeArtifact.set(sessionId, { path: p, kind: kind || null });
87
- }
88
-
89
- export function getActiveArtifact(sessionId) {
90
- return (sessionId && activeArtifact.get(sessionId)) || null;
91
- }
92
-
93
- export function clearActiveArtifact(sessionId) {
94
- activeArtifact.delete(sessionId);
95
- }
96
-
97
- function normalizeRel(p) {
98
- return String(p).replace(/\\/g, '/').replace(/^\.\//, '');
99
- }
100
-
101
- async function exists(p) {
102
- try { await fs.access(p); return true; } catch { return false; }
103
- }
104
-
105
- /** workspace 内才算数(resolve 不出根) */
106
- function insideWorkspace(workspaceRoot, absPath) {
107
- const root = path.resolve(workspaceRoot);
108
- return absPath === root || absPath.startsWith(root + path.sep);
109
- }
110
-
111
- /**
112
- * 站点的页面清单(相对产物根)。
113
- */
114
- export async function listSitePages(workspaceRoot) {
115
- const m = await KINDS[KIND_SITE].manifest(workspaceRoot, null);
116
- return m.pages;
117
- }
118
-
119
- /**
120
- * 推断某个 html 路径的产物形态。
121
- *
122
- * 多产物平权:canvas.html 跟一个根站并存时仍然是 deck、about.html 是站点子页 ——
123
- * 同名文件形态可以不同,问 artifacts 清单而不是猜文件名。清单里找不到(文件
124
- * 还没写出来)才退回文件名约定。
125
- */
126
- export async function kindOfPath(workspaceRoot, relPath) {
127
- const p = normalizeRel(relPath);
128
- const base = path.posix.basename(p);
129
- const m = await taskManifest(workspaceRoot);
130
- if (m) {
131
- const art = artifactOfPath(m, p);
132
- if (art) return art.kind;
133
- if (m.kind) return m.kind;
134
- }
135
- if (base === ENTRY_FILE[KIND_DECK]) return KIND_DECK;
136
- if (base === ENTRY_FILE[KIND_SITE]) return KIND_SITE;
137
- // 清单里没有(文件还没写出来)时按扩展名回退。.docx 只可能是 word 形态,
138
- // 让它落进 deck 会让感知层拿 playwright 去开一个二进制包。
139
- const ext = (p.match(/\.[a-z0-9]+$/i) || [''])[0].toLowerCase();
140
- const byExt = Object.values(KINDS).find(k => k.entryFile.toLowerCase().endsWith(ext) && ext !== '.html');
141
- if (byExt) return byExt.id;
142
- return KIND_DECK; // 认不出的散装 .html 一律按 deck
143
- }
144
-
145
- /** 这个工作区现有的产物入口(平权,没有主次) */
146
- export async function listWorkspaceArtifacts(workspaceRoot) {
147
- const m = await taskManifest(workspaceRoot);
148
- return (m?.artifacts || []).map(a => ({ rel: a.entryRel, kind: a.kind }));
149
- }
150
-
151
- /** 兼容旧名:只要 deck 那些 */
152
- export async function listWorkspaceDecks(workspaceRoot) {
153
- return (await listWorkspaceArtifacts(workspaceRoot))
154
- .filter(a => a.kind === KIND_DECK)
155
- .map(a => a.rel);
156
- }
157
-
158
- /**
159
- * 解析产物目标。
160
- *
161
- * @param {string} workspaceRoot
162
- * @param {string|null} relPath 调用方显式给的路径(优先级最高)
163
- * @param {string|null} sessionId
164
- * @returns {Promise<{ ok: true, absPath: string, relPath: string, kind: 'deck'|'site',
165
- * task: string|null, taskDir: string|null,
166
- * artifactDir: string|null, artifactRel: string|null }
167
- * | { ok: false, message: string }>}
168
- * taskDir 源(任务根,agent 的地盘)
169
- * artifactDir 产物根(被预览 / 导出 / 发布的目录);deck 和手写站点 = taskDir
170
- */
171
- export async function resolveArtifactTarget(workspaceRoot, relPath, sessionId) {
172
- const decorate = async (rel) => {
173
- const p = normalizeRel(rel);
174
- // 按所属产物定 kind 和产物根(多产物平权:canvas.html 是 deck、
175
- // v2/index.html 是另一个站,root 各归各)
176
- let kind = null;
177
- let root = '';
178
- let artifact = null;
179
- const m = await taskManifest(workspaceRoot);
180
- if (m) {
181
- artifact = artifactOfPath(m, p);
182
- if (artifact) { kind = artifact.kind; root = artifact.root || ''; }
183
- else if (m.kind) { kind = m.kind; root = m.root || ''; }
184
- }
185
- if (!kind) kind = await kindOfPath(workspaceRoot, p);
186
- return {
187
- ok: true,
188
- absPath: path.resolve(workspaceRoot, p),
189
- relPath: p,
190
- kind,
191
- // task / taskDir 保留成兼容字段:任务层没了,"源目录"永远是工作区根。
192
- // 下游有十几处读 taskDir 当"agent 的地盘",给它对的值比改十几处安全。
193
- task: null,
194
- taskDir: workspaceRoot,
195
- artifact, // 所属产物条目(manifest.artifacts 里那条;散文件为 null)
196
- artifactDir: root ? path.join(workspaceRoot, root) : workspaceRoot,
197
- artifactRel: root || '.',
198
- };
199
- };
200
-
201
- const tryPath = async (rel) => {
202
- if (!rel) return null;
203
- const abs = path.resolve(workspaceRoot, rel);
204
- if (!insideWorkspace(workspaceRoot, abs)) return null;
205
- return (await exists(abs)) ? decorate(rel) : null;
206
- };
207
-
208
- if (relPath) {
209
- const hit = await tryPath(relPath);
210
- if (hit) { setActiveArtifact(sessionId, hit.relPath, hit.kind); return hit; }
211
- const abs = path.resolve(workspaceRoot, relPath);
212
- return {
213
- ok: false,
214
- message: insideWorkspace(workspaceRoot, abs)
215
- ? `${relPath} not found. Write it first, or check the path.`
216
- : 'path escapes workspace',
217
- };
218
- }
219
-
220
- const active = getActiveArtifact(sessionId);
221
- if (active?.path) {
222
- const activeHit = await tryPath(active.path);
223
- if (activeHit) return activeHit;
224
- }
225
-
226
- const all = await listWorkspaceArtifacts(workspaceRoot);
227
- if (all.length === 1) {
228
- setActiveArtifact(sessionId, all[0].rel, all[0].kind);
229
- return decorate(all[0].rel);
230
- }
231
- if (all.length > 1) {
232
- return {
233
- ok: false,
234
- message: 'Multiple artifacts in this workspace — pass path explicitly:\n'
235
- + all.map(a => `- ${a.rel} (${a.kind})`).join('\n'),
236
- };
237
- }
238
- return {
239
- ok: false,
240
- message: 'No artifact found. Deck = canvas.html, site = index.html — '
241
- + 'write one first, or pass path explicitly.',
242
- };
243
- }
244
-
245
- /**
246
- * 浏览器类工具的统一闸门:目标形态不能用浏览器打开就别往下走。
247
- *
248
- * 用法是 resolveCanvasTarget 之后立刻过一道:
249
- * const target = await resolveCanvasTarget(...);
250
- * if (!target.ok) return err(target.message);
251
- * const gate = requireBrowsable(target);
252
- * if (gate) return err(gate);
253
- *
254
- * 不把它塞进 resolveArtifactTarget 内部,是因为导出、寻址这些**正当**需要拿到
255
- * 不可浏览目标的调用方也走那个函数,在源头拦会误伤。
256
- *
257
- * @returns {string|null} 不可浏览时返回给 agent 的说明,可浏览返回 null
258
- */
259
- export function requireBrowsable(target) {
260
- if (!target?.kind || can(target.kind, 'browsable')) return null;
261
- const who = target.relPath || target.kind;
262
- // 可渲染形态(docx)要给对路的替代方案。说"直接 Read 它的文件"是**错的建议**
263
- // —— 那是个二进制 zip,Read 出来是乱码,而它其实是**看得见**的,只是要先渲染。
264
- if (can(target.kind, 'renderable')) {
265
- return `${who} 是 ${target.kind} 形态,没有 DOM,读页面 / 查元素 / 取计算样式这几个工具对它无效。`
266
- + '看长相用 screenshot(它会渲染成页图,可以带 pages 参数指定范围);'
267
- + '看结构读它的 token 源(同名 .json),别去 Read 那个 .docx 本身,它是二进制包。';
268
- }
269
- return `${who} 是 ${target.kind} 形态,没有可以用浏览器打开的入口,`
270
- + '截图 / 读页面 / 查元素这类工具对它没有意义。直接 Read 它的文件。';
271
- }
272
-
273
- /** 给各工具复用的 path 参数描述(保持措辞一致) */
274
- export const ARTIFACT_PATH_DESC =
275
- 'Relative path of the artifact entry (deck: "canvas.html", site: "index.html" '
276
- + 'or its built output like "dist/index.html", word: "文档.docx"). '
277
- + 'Omit to use the artifact you are currently working on.';
278
-
279
- // ── 兼容层:老名字继续可用,内部全部走上面的实现 ────────────────────────────
280
- export const CANVAS_PATH_DESC = ARTIFACT_PATH_DESC;
281
- export const resolveCanvasTarget = resolveArtifactTarget;
282
- export const setActiveDeck = (sessionId, relPath) => setActiveArtifact(sessionId, relPath);
283
- export const getActiveDeck = (sessionId) => getActiveArtifact(sessionId)?.path || null;
284
- export const clearActiveDeck = clearActiveArtifact;
1
+ /**
2
+ * artifact-target.js — 产物寻址(2026-07-28 由 canvas-target.js 泛化;
3
+ * 2026-07-29 形态判定与解析下沉到 kinds/ 注册表;2026-08-07 任务层退役)
4
+ *
5
+ * 定死三件事:
6
+ *
7
+ * **① 产物有形态(kind),文件就是真相。** 判定规则在 kinds/ 注册表:
8
+ * canvas.html → deck;index.html(工作区根 / 声明的产物根 / 约定构建目录)
9
+ * → site。
10
+ *
11
+ * **② 寻址永远带着 kind 一起返回。** 下游(截图 / 分页 / 导出 / fit 注入)必须
12
+ * 能看见"这是 deck 还是 site",才可能给出对的行为。kind 每次 resolve 按工作区
13
+ * 现状重算,不缓存。
14
+ *
15
+ * **③ 源和产物可以分开。** 构建型站点的源在工作区根、产物在 dist/ 之类的产物根。
16
+ * resolve 返回 workspaceRoot(源,agent 的地盘)和 artifactDir(被预览 / 导出 /
17
+ * 发布的根),deck 和手写站点两者相同。消费方要"看"产物就用 artifactDir,
18
+ * 别再自己 dirname(entryPath)。
19
+ *
20
+ * 寻址顺序(越靠前越明确):
21
+ * ① 调用方显式给的 path
22
+ * ② 本会话的"当前产物"——最近写过 / 截过 / 预览过哪份就是哪份
23
+ * ③ 工作区只有一份产物就是它;多份就报错列出来让调用方指定
24
+ *
25
+ * ## 2026-08-07:第 ③ 条曾经是第 ④ 条
26
+ *
27
+ * 中间夹着一条「认领绑在本会话名下那个任务」——`tasks/<任务>/.nd-task.json`
28
+ * 里记着 sessionId,寻址时按当前会话去反查它的任务。那条规则存在的前提是
29
+ * **一个项目里有多个任务、而每个任务属于一个会话**;线上数据说这个前提从来
30
+ * 没成立过(13 个有产物的项目,每个都恰好一个任务)。
31
+ *
32
+ * 任务层拆掉之后,"这个会话的产物"这个问题本身就没有意义了 —— 产物属于项目,
33
+ * 谁开的对话都看得见同一批。剩下的歧义(工作区里有多份产物、调用方没说哪份)
34
+ * 由 ② 的最近使用和 ③ 的显式报错解决,这两条本来就在,而且不依赖任何绑定。
35
+ */
36
+
37
+ import path from 'node:path';
38
+ import fs from 'node:fs/promises';
39
+ import {
40
+ KINDS, kindDef, readTaskMarker, taskManifest, artifactOfPath, can,
41
+ } from './kinds/index.js';
42
+
43
+ export const KIND_DECK = 'deck';
44
+ export const KIND_SITE = 'site';
45
+
46
+ /** 每种形态的入口文件名(从注册表派生;改形态契约去 kinds/) */
47
+ export const ENTRY_FILE = Object.freeze(
48
+ Object.fromEntries(Object.values(KINDS).map(k => [k.id, k.entryFile])),
49
+ );
50
+
51
+ /**
52
+ * 产物入口**长什么样**(扩展名集合),同样从注册表派生。
53
+ * `.html` / `.htm` 是 deck+site 的形状,`.docx` 是 word 的 —— 加形态时这里
54
+ * 自动跟上,不用再回来补一条正则。
55
+ */
56
+ const ENTRY_EXTS = new Set(
57
+ Object.values(KINDS)
58
+ .map(k => (k.entryFile.match(/\.[a-z0-9]+$/i) || [''])[0].toLowerCase())
59
+ .filter(Boolean)
60
+ .concat('.htm'), // html 的老写法,注册表里只写了 .html
61
+ );
62
+
63
+ // 形态判定与解析的权威在 kinds/,这里转发老名字(消费方 import 不用改两次)
64
+ export { readTaskMarker, taskManifest, kindDef, artifactOfPath };
65
+ export { formatAllowed, docxClaimedFiles, isDirArtifact } from './kinds/index.js';
66
+ export { can };
67
+
68
+ /** sessionId → { path, kind }。会话结束不清也无妨(几个短字符串) */
69
+ const activeArtifact = new Map();
70
+
71
+ /**
72
+ * 记住这个会话正在做哪份产物(写文件 / 截图 / 预览时调)。
73
+ *
74
+ * 只认**产物入口的形状**(ENTRY_EXTS,从注册表派生)。以前还认"任务内的非
75
+ * html"(改 style.css 也算在这个任务上干活,寻址时走任务入口),任务层没了之后
76
+ * 那一支失去了落点:改 `style.css` 说明不了在做哪一份 deck,工作区根上可能并排
77
+ * 放着好几份。认不出来就不认,让 resolve 走"只有一份就是它 / 多份报错"那条 ——
78
+ * 比猜一个错的默认目标强。
79
+ */
80
+ export function setActiveArtifact(sessionId, relPath, kind) {
81
+ if (!sessionId || typeof relPath !== 'string') return;
82
+ const p = normalizeRel(relPath);
83
+ if (/\.template\.(html?|css)$/i.test(p)) return; // 模板不是产物
84
+ const ext = (p.match(/\.[a-z0-9]+$/i) || [''])[0].toLowerCase();
85
+ if (!ENTRY_EXTS.has(ext)) return;
86
+ activeArtifact.set(sessionId, { path: p, kind: kind || null });
87
+ }
88
+
89
+ export function getActiveArtifact(sessionId) {
90
+ return (sessionId && activeArtifact.get(sessionId)) || null;
91
+ }
92
+
93
+ export function clearActiveArtifact(sessionId) {
94
+ activeArtifact.delete(sessionId);
95
+ }
96
+
97
+ function normalizeRel(p) {
98
+ return String(p).replace(/\\/g, '/').replace(/^\.\//, '');
99
+ }
100
+
101
+ async function exists(p) {
102
+ try { await fs.access(p); return true; } catch { return false; }
103
+ }
104
+
105
+ /** workspace 内才算数(resolve 不出根) */
106
+ function insideWorkspace(workspaceRoot, absPath) {
107
+ const root = path.resolve(workspaceRoot);
108
+ return absPath === root || absPath.startsWith(root + path.sep);
109
+ }
110
+
111
+ /**
112
+ * 站点的页面清单(相对产物根)。
113
+ */
114
+ export async function listSitePages(workspaceRoot) {
115
+ const m = await KINDS[KIND_SITE].manifest(workspaceRoot, null);
116
+ return m.pages;
117
+ }
118
+
119
+ /**
120
+ * 推断某个 html 路径的产物形态。
121
+ *
122
+ * 多产物平权:canvas.html 跟一个根站并存时仍然是 deck、about.html 是站点子页 ——
123
+ * 同名文件形态可以不同,问 artifacts 清单而不是猜文件名。清单里找不到(文件
124
+ * 还没写出来)才退回文件名约定。
125
+ */
126
+ export async function kindOfPath(workspaceRoot, relPath) {
127
+ const p = normalizeRel(relPath);
128
+ const base = path.posix.basename(p);
129
+ const m = await taskManifest(workspaceRoot);
130
+ if (m) {
131
+ const art = artifactOfPath(m, p);
132
+ if (art) return art.kind;
133
+ if (m.kind) return m.kind;
134
+ }
135
+ if (base === ENTRY_FILE[KIND_DECK]) return KIND_DECK;
136
+ if (base === ENTRY_FILE[KIND_SITE]) return KIND_SITE;
137
+ // 清单里没有(文件还没写出来)时按扩展名回退。.docx 只可能是 word 形态,
138
+ // 让它落进 deck 会让感知层拿 playwright 去开一个二进制包。
139
+ const ext = (p.match(/\.[a-z0-9]+$/i) || [''])[0].toLowerCase();
140
+ const byExt = Object.values(KINDS).find(k => k.entryFile.toLowerCase().endsWith(ext) && ext !== '.html');
141
+ if (byExt) return byExt.id;
142
+ return KIND_DECK; // 认不出的散装 .html 一律按 deck
143
+ }
144
+
145
+ /** 这个工作区现有的产物入口(平权,没有主次) */
146
+ export async function listWorkspaceArtifacts(workspaceRoot) {
147
+ const m = await taskManifest(workspaceRoot);
148
+ return (m?.artifacts || []).map(a => ({ rel: a.entryRel, kind: a.kind }));
149
+ }
150
+
151
+ /** 兼容旧名:只要 deck 那些 */
152
+ export async function listWorkspaceDecks(workspaceRoot) {
153
+ return (await listWorkspaceArtifacts(workspaceRoot))
154
+ .filter(a => a.kind === KIND_DECK)
155
+ .map(a => a.rel);
156
+ }
157
+
158
+ /**
159
+ * 解析产物目标。
160
+ *
161
+ * @param {string} workspaceRoot
162
+ * @param {string|null} relPath 调用方显式给的路径(优先级最高)
163
+ * @param {string|null} sessionId
164
+ * @returns {Promise<{ ok: true, absPath: string, relPath: string, kind: 'deck'|'site',
165
+ * task: string|null, taskDir: string|null,
166
+ * artifactDir: string|null, artifactRel: string|null }
167
+ * | { ok: false, message: string }>}
168
+ * taskDir 源(任务根,agent 的地盘)
169
+ * artifactDir 产物根(被预览 / 导出 / 发布的目录);deck 和手写站点 = taskDir
170
+ */
171
+ export async function resolveArtifactTarget(workspaceRoot, relPath, sessionId) {
172
+ const decorate = async (rel) => {
173
+ const p = normalizeRel(rel);
174
+ // 按所属产物定 kind 和产物根(多产物平权:canvas.html 是 deck、
175
+ // v2/index.html 是另一个站,root 各归各)
176
+ let kind = null;
177
+ let root = '';
178
+ let artifact = null;
179
+ const m = await taskManifest(workspaceRoot);
180
+ if (m) {
181
+ artifact = artifactOfPath(m, p);
182
+ if (artifact) { kind = artifact.kind; root = artifact.root || ''; }
183
+ else if (m.kind) { kind = m.kind; root = m.root || ''; }
184
+ }
185
+ if (!kind) kind = await kindOfPath(workspaceRoot, p);
186
+ return {
187
+ ok: true,
188
+ absPath: path.resolve(workspaceRoot, p),
189
+ relPath: p,
190
+ kind,
191
+ // task / taskDir 保留成兼容字段:任务层没了,"源目录"永远是工作区根。
192
+ // 下游有十几处读 taskDir 当"agent 的地盘",给它对的值比改十几处安全。
193
+ task: null,
194
+ taskDir: workspaceRoot,
195
+ artifact, // 所属产物条目(manifest.artifacts 里那条;散文件为 null)
196
+ artifactDir: root ? path.join(workspaceRoot, root) : workspaceRoot,
197
+ artifactRel: root || '.',
198
+ };
199
+ };
200
+
201
+ const tryPath = async (rel) => {
202
+ if (!rel) return null;
203
+ const abs = path.resolve(workspaceRoot, rel);
204
+ if (!insideWorkspace(workspaceRoot, abs)) return null;
205
+ return (await exists(abs)) ? decorate(rel) : null;
206
+ };
207
+
208
+ if (relPath) {
209
+ const hit = await tryPath(relPath);
210
+ if (hit) { setActiveArtifact(sessionId, hit.relPath, hit.kind); return hit; }
211
+ const abs = path.resolve(workspaceRoot, relPath);
212
+ return {
213
+ ok: false,
214
+ message: insideWorkspace(workspaceRoot, abs)
215
+ ? `${relPath} not found. Write it first, or check the path.`
216
+ : 'path escapes workspace',
217
+ };
218
+ }
219
+
220
+ const active = getActiveArtifact(sessionId);
221
+ if (active?.path) {
222
+ const activeHit = await tryPath(active.path);
223
+ if (activeHit) return activeHit;
224
+ }
225
+
226
+ const all = await listWorkspaceArtifacts(workspaceRoot);
227
+ if (all.length === 1) {
228
+ setActiveArtifact(sessionId, all[0].rel, all[0].kind);
229
+ return decorate(all[0].rel);
230
+ }
231
+ if (all.length > 1) {
232
+ return {
233
+ ok: false,
234
+ message: 'Multiple artifacts in this workspace — pass path explicitly:\n'
235
+ + all.map(a => `- ${a.rel} (${a.kind})`).join('\n'),
236
+ };
237
+ }
238
+ return {
239
+ ok: false,
240
+ message: 'No artifact found. Deck = canvas.html, site = index.html — '
241
+ + 'write one first, or pass path explicitly.',
242
+ };
243
+ }
244
+
245
+ /**
246
+ * 浏览器类工具的统一闸门:目标形态不能用浏览器打开就别往下走。
247
+ *
248
+ * 用法是 resolveCanvasTarget 之后立刻过一道:
249
+ * const target = await resolveCanvasTarget(...);
250
+ * if (!target.ok) return err(target.message);
251
+ * const gate = requireBrowsable(target);
252
+ * if (gate) return err(gate);
253
+ *
254
+ * 不把它塞进 resolveArtifactTarget 内部,是因为导出、寻址这些**正当**需要拿到
255
+ * 不可浏览目标的调用方也走那个函数,在源头拦会误伤。
256
+ *
257
+ * @returns {string|null} 不可浏览时返回给 agent 的说明,可浏览返回 null
258
+ */
259
+ export function requireBrowsable(target) {
260
+ if (!target?.kind || can(target.kind, 'browsable')) return null;
261
+ const who = target.relPath || target.kind;
262
+ // 可渲染形态(docx)要给对路的替代方案。说"直接 Read 它的文件"是**错的建议**
263
+ // —— 那是个二进制 zip,Read 出来是乱码,而它其实是**看得见**的,只是要先渲染。
264
+ if (can(target.kind, 'renderable')) {
265
+ return `${who} 是 ${target.kind} 形态,没有 DOM,读页面 / 查元素 / 取计算样式这几个工具对它无效。`
266
+ + '看长相用 screenshot(它会渲染成页图,可以带 pages 参数指定范围);'
267
+ + '看结构读它的 token 源(同名 .json),别去 Read 那个 .docx 本身,它是二进制包。';
268
+ }
269
+ return `${who} 是 ${target.kind} 形态,没有可以用浏览器打开的入口,`
270
+ + '截图 / 读页面 / 查元素这类工具对它没有意义。直接 Read 它的文件。';
271
+ }
272
+
273
+ /** 给各工具复用的 path 参数描述(保持措辞一致) */
274
+ export const ARTIFACT_PATH_DESC =
275
+ 'Relative path of the artifact entry (deck: "canvas.html", site: "index.html" '
276
+ + 'or its built output like "dist/index.html", word: "文档.docx"). '
277
+ + 'Omit to use the artifact you are currently working on.';
278
+
279
+ // ── 兼容层:老名字继续可用,内部全部走上面的实现 ────────────────────────────
280
+ export const CANVAS_PATH_DESC = ARTIFACT_PATH_DESC;
281
+ export const resolveCanvasTarget = resolveArtifactTarget;
282
+ export const setActiveDeck = (sessionId, relPath) => setActiveArtifact(sessionId, relPath);
283
+ export const getActiveDeck = (sessionId) => getActiveArtifact(sessionId)?.path || null;
284
+ export const clearActiveDeck = clearActiveArtifact;