@cloud411716/fancy-webnovel 1.0.46 → 3.0.0

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 (271) hide show
  1. package/client.js +369 -0
  2. package/cordis.patch.yml +4 -13
  3. package/docs/douyin-plugin.md +118 -0
  4. package/docs/material-library/README.md +404 -0
  5. package/docs/material-library/import.js +437 -0
  6. package/docs/material-library/prompt.md +370 -0
  7. package/docs/material-library/queries.surql +94 -0
  8. package/docs/material-library/sample.jsonl +14 -0
  9. package/docs/material-library/schema.surql +46 -0
  10. package/docs/mcp-profile.example.yml +24 -0
  11. package/docs/story-graph-design.md +602 -0
  12. package/docs/story-graph-validation.md +81 -0
  13. package/docs/workspace-mcp.md +284 -0
  14. package/index.js +26 -38
  15. package/lib/atomic-write.js +109 -0
  16. package/lib/chapter-order.js +100 -0
  17. package/lib/chinese-num.js +111 -0
  18. package/lib/confirmation.js +80 -0
  19. package/lib/db-init.js +123 -0
  20. package/lib/deployed-state.js +141 -0
  21. package/lib/direction-labels.js +265 -0
  22. package/lib/error-codes.js +104 -0
  23. package/lib/infra.js +594 -0
  24. package/lib/llm-wiki.js +109 -0
  25. package/lib/material-db.js +149 -0
  26. package/lib/path-guard.js +150 -0
  27. package/lib/projects.js +400 -0
  28. package/lib/scraper-utils.js +305 -0
  29. package/lib/surreal-server.js +338 -0
  30. package/lib/topic-decision.js +860 -0
  31. package/lib/topic-fields.js +251 -0
  32. package/lib/topic-schema.js +225 -0
  33. package/lib/workspace.js +501 -0
  34. package/package.json +62 -5
  35. package/plugins/fancy-analyze/analyze-definitions.md +519 -0
  36. package/plugins/fancy-analyze/command-input.js +65 -0
  37. package/plugins/fancy-analyze/index.js +2024 -0
  38. package/plugins/fancy-analyze/ingest-coverage.js +107 -0
  39. package/plugins/fancy-analyze/schema-analyze.md +627 -0
  40. package/plugins/fancy-analyze/schema-write.md +178 -0
  41. package/plugins/fancy-analyze/scripts/append-summary-chapter.js +612 -0
  42. package/plugins/fancy-analyze/scripts/split_source.js +292 -0
  43. package/plugins/fancy-analyze/summary-progress.js +495 -0
  44. package/plugins/fancy-analyze/templates.js +1179 -0
  45. package/plugins/fancy-bootstrap/index.js +313 -0
  46. package/plugins/fancy-bootstrap/scripts/init.js +177 -0
  47. package/plugins/fancy-bootstrap/scripts/path-utils.js +21 -0
  48. package/plugins/fancy-bootstrap/templates.js +71 -0
  49. package/plugins/fancy-db/index.js +172 -0
  50. package/plugins/fancy-douyin/deploy.js +134 -0
  51. package/plugins/fancy-douyin/index.js +551 -0
  52. package/plugins/fancy-douyin/prompts/01-analysis-prompt.md +84 -0
  53. package/plugins/fancy-douyin/scripts/crawl-note.cjs +194 -0
  54. package/plugins/fancy-douyin/scripts/lib.cjs +197 -0
  55. package/plugins/fancy-douyin/scripts/scan-account.cjs +128 -0
  56. package/plugins/fancy-douyin/scripts/start-chrome.cjs +203 -0
  57. package/plugins/fancy-douyin/state.js +197 -0
  58. package/plugins/fancy-douyin/templates.js +127 -0
  59. package/plugins/fancy-export/exporter.js +805 -0
  60. package/plugins/fancy-export/index.js +63 -0
  61. package/plugins/fancy-import/import-core.js +1118 -0
  62. package/plugins/fancy-import/import-driver.js +604 -0
  63. package/plugins/fancy-import/index.js +738 -0
  64. package/plugins/fancy-import/prompts/01-import-prompt.md +131 -0
  65. package/plugins/fancy-import/templates.js +58 -0
  66. package/plugins/fancy-material/exporter.js +153 -0
  67. package/plugins/fancy-material/genres.js +256 -0
  68. package/plugins/fancy-material/index.js +835 -0
  69. package/plugins/fancy-material/inventory.js +143 -0
  70. package/plugins/fancy-material/kinds.js +313 -0
  71. package/plugins/fancy-material/queue.js +55 -0
  72. package/plugins/fancy-material/scripts/query.js +254 -0
  73. package/plugins/fancy-material/templates.js +258 -0
  74. package/plugins/fancy-material/tool-deploy.js +138 -0
  75. package/plugins/fancy-material/validate.js +149 -0
  76. package/plugins/fancy-project/index.js +133 -0
  77. package/plugins/fancy-scan/existing-block.js +100 -0
  78. package/plugins/fancy-scan/index.js +708 -0
  79. package/plugins/fancy-scan/prompts/01-analysis-prompt.md +302 -0
  80. package/plugins/fancy-scan/scripts/append-block.js +725 -0
  81. package/plugins/fancy-scan/scripts/report-validate.js +824 -0
  82. package/plugins/fancy-scan/scripts/run-scan.js +667 -0
  83. package/plugins/fancy-scan/scripts/scrapers/fanqie-rank-scraper.js +923 -0
  84. package/plugins/fancy-scan/scripts/scrapers/jjwxc-rank-scraper.js +469 -0
  85. package/plugins/fancy-scan/scripts/scrapers/qimao-rank-scraper.js +629 -0
  86. package/plugins/fancy-scan/templates/blocks.js +155 -0
  87. package/plugins/fancy-scan/templates/platforms.js +372 -0
  88. package/plugins/fancy-scan/templates/report-schema.js +90 -0
  89. package/plugins/fancy-scan/templates.js +694 -0
  90. package/plugins/fancy-scan/tool-deploy.js +335 -0
  91. package/plugins/fancy-scan/wait-turn.js +46 -0
  92. package/plugins/fancy-story-builder/db/README.md +106 -0
  93. package/plugins/fancy-story-builder/db/client.js +228 -0
  94. package/plugins/fancy-story-builder/db/commands.js +145 -0
  95. package/plugins/fancy-story-builder/db/ids.js +39 -0
  96. package/plugins/fancy-story-builder/db/import.js +798 -0
  97. package/plugins/fancy-story-builder/db/project.js +299 -0
  98. package/plugins/fancy-story-builder/db/query.js +361 -0
  99. package/plugins/fancy-story-builder/db/schema.js +417 -0
  100. package/plugins/fancy-story-builder/db/stage-sync.js +238 -0
  101. package/plugins/fancy-story-builder/deploy-manifest.js +239 -0
  102. package/plugins/fancy-story-builder/deslop-driver.js +821 -0
  103. package/plugins/fancy-story-builder/flesh-output.js +168 -0
  104. package/plugins/fancy-story-builder/index.js +6861 -0
  105. package/plugins/fancy-story-builder/patch-driver.js +390 -0
  106. package/plugins/fancy-story-builder/prompts/01-skeleton-prompt.md +563 -0
  107. package/plugins/fancy-story-builder/prompts/02-flesh-prompt.md +602 -0
  108. package/plugins/fancy-story-builder/prompts/03-rhythm-prompt.md +539 -0
  109. package/plugins/fancy-story-builder/prompts/04-writing-prompt.md +225 -0
  110. package/plugins/fancy-story-builder/prompts/05-deslop-prompt.md +96 -0
  111. package/plugins/fancy-story-builder/purpose-template.md +83 -0
  112. package/plugins/fancy-story-builder/references/formulas-rhythm.json +302 -0
  113. package/plugins/fancy-story-builder/references/formulas-skeleton.json +402 -0
  114. package/plugins/fancy-story-builder/references/genre-profiles.json +3514 -0
  115. package/plugins/fancy-story-builder/references/glossary.md +594 -0
  116. package/plugins/fancy-story-builder/references/index.md +581 -0
  117. package/plugins/fancy-story-builder/references//345/206/231/344/275/234/346/214/207/345/257/274/00-index.md +174 -0
  118. package/plugins/fancy-story-builder/references//345/206/231/344/275/234/346/214/207/345/257/274/01-scene.md +150 -0
  119. package/plugins/fancy-story-builder/references//345/206/231/344/275/234/346/214/207/345/257/274/02-chapter-workflow.md +99 -0
  120. package/plugins/fancy-story-builder/references//345/206/231/344/275/234/346/214/207/345/257/274/03-opening-chapter.md +104 -0
  121. package/plugins/fancy-story-builder/references//345/206/231/344/275/234/346/214/207/345/257/274/04-dialogue-mastery.md +274 -0
  122. package/plugins/fancy-story-builder/references//345/206/231/344/275/234/346/214/207/345/257/274/05-genre-formulas.md +432 -0
  123. package/plugins/fancy-story-builder/references//345/206/231/344/275/234/346/214/207/345/257/274/06-tone-ensemble.md +259 -0
  124. package/plugins/fancy-story-builder/references//345/206/231/344/275/234/346/214/207/345/257/274/07-ledger-audit.md +222 -0
  125. package/plugins/fancy-story-builder/references//345/206/231/344/275/234/346/214/207/345/257/274/08-diagnosis-revision.md +205 -0
  126. package/plugins/fancy-story-builder/references//345/206/231/344/275/234/346/214/207/345/257/274/09-reading-power.md +135 -0
  127. package/plugins/fancy-story-builder/references//345/206/231/344/275/234/346/214/207/345/257/274/10-deslop.md +129 -0
  128. package/plugins/fancy-story-builder/references//350/212/202/345/245/217/346/214/207/345/257/274/00-index.md +98 -0
  129. package/plugins/fancy-story-builder/references//350/212/202/345/245/217/346/214/207/345/257/274/01-upgrade-rhythm.md +300 -0
  130. package/plugins/fancy-story-builder/references//350/212/202/345/245/217/346/214/207/345/257/274/02-arc-rhythm.md +168 -0
  131. package/plugins/fancy-story-builder/references//350/212/202/345/245/217/346/214/207/345/257/274/03-highpoint-hooks.md +189 -0
  132. package/plugins/fancy-story-builder/references//350/212/202/345/245/217/346/214/207/345/257/274/04-satisfaction.md +226 -0
  133. package/plugins/fancy-story-builder/references//350/212/202/345/245/217/346/214/207/345/257/274/05-foreshadow-density.md +195 -0
  134. package/plugins/fancy-story-builder/references//350/212/202/345/245/217/346/214/207/345/257/274/06-chapter-outline.md +226 -0
  135. package/plugins/fancy-story-builder/references//350/212/202/345/245/217/346/214/207/345/257/274/07-audit.md +181 -0
  136. package/plugins/fancy-story-builder/references//350/241/200/350/202/211/346/214/207/345/257/274/00-index.md +100 -0
  137. package/plugins/fancy-story-builder/references//350/241/200/350/202/211/346/214/207/345/257/274/01-character.md +243 -0
  138. package/plugins/fancy-story-builder/references//350/241/200/350/202/211/346/214/207/345/257/274/02-worldbuilding.md +245 -0
  139. package/plugins/fancy-story-builder/references//350/241/200/350/202/211/346/214/207/345/257/274/03-relations.md +185 -0
  140. package/plugins/fancy-story-builder/references//350/241/200/350/202/211/346/214/207/345/257/274/04-emotion-methods.md +286 -0
  141. package/plugins/fancy-story-builder/references//350/241/200/350/202/211/346/214/207/345/257/274/05-audit.md +187 -0
  142. package/plugins/fancy-story-builder/references//351/252/250/346/236/266/346/214/207/345/257/274/00-index.md +91 -0
  143. package/plugins/fancy-story-builder/references//351/252/250/346/236/266/346/214/207/345/257/274/01-positioning.md +209 -0
  144. package/plugins/fancy-story-builder/references//351/252/250/346/236/266/346/214/207/345/257/274/02-packaging.md +135 -0
  145. package/plugins/fancy-story-builder/references//351/252/250/346/236/266/346/214/207/345/257/274/03-entities.md +305 -0
  146. package/plugins/fancy-story-builder/references//351/252/250/346/236/266/346/214/207/345/257/274/04-conflict.md +328 -0
  147. package/plugins/fancy-story-builder/references//351/252/250/346/236/266/346/214/207/345/257/274/05-quota.md +187 -0
  148. package/plugins/fancy-story-builder/references//351/252/250/346/236/266/346/214/207/345/257/274/06-audit.md +160 -0
  149. package/plugins/fancy-story-builder/rhythm-output.js +140 -0
  150. package/plugins/fancy-story-builder/schema-setting.json +1100 -0
  151. package/plugins/fancy-story-builder/schema-setting.md +232 -0
  152. package/plugins/fancy-story-builder/scripts/ai-patterns.js +619 -0
  153. package/plugins/fancy-story-builder/scripts/brief-validate.js +164 -0
  154. package/plugins/fancy-story-builder/scripts/check-guide-compliance.js +289 -0
  155. package/plugins/fancy-story-builder/scripts/content-quality.js +416 -0
  156. package/plugins/fancy-story-builder/scripts/deslop-validate.js +143 -0
  157. package/plugins/fancy-story-builder/scripts/error-classes.js +112 -0
  158. package/plugins/fancy-story-builder/scripts/flesh-validate.js +1312 -0
  159. package/plugins/fancy-story-builder/scripts/formula-engine.js +112 -0
  160. package/plugins/fancy-story-builder/scripts/graph-utils.js +1484 -0
  161. package/plugins/fancy-story-builder/scripts/label-coverage.js +171 -0
  162. package/plugins/fancy-story-builder/scripts/prompt-drift-check.js +441 -0
  163. package/plugins/fancy-story-builder/scripts/reading-power.js +298 -0
  164. package/plugins/fancy-story-builder/scripts/reference-log.js +110 -0
  165. package/plugins/fancy-story-builder/scripts/rhythm-validate.js +1312 -0
  166. package/plugins/fancy-story-builder/scripts/schema-pages.js +2668 -0
  167. package/plugins/fancy-story-builder/scripts/semantic-lint.js +381 -0
  168. package/plugins/fancy-story-builder/scripts/skeleton-quota.js +145 -0
  169. package/plugins/fancy-story-builder/scripts/skeleton-validate.js +1973 -0
  170. package/plugins/fancy-story-builder/scripts/stage-batches.js +1405 -0
  171. package/plugins/fancy-story-builder/scripts/stage-contract.js +429 -0
  172. package/plugins/fancy-story-builder/scripts/story-artifacts.js +933 -0
  173. package/plugins/fancy-story-builder/scripts/upstream-query.js +198 -0
  174. package/plugins/fancy-story-builder/scripts/v2/baseline.js +112 -0
  175. package/plugins/fancy-story-builder/scripts/v2/cast.js +517 -0
  176. package/plugins/fancy-story-builder/scripts/v2/context.js +434 -0
  177. package/plugins/fancy-story-builder/scripts/v2/contracts.js +493 -0
  178. package/plugins/fancy-story-builder/scripts/v2/core.js +419 -0
  179. package/plugins/fancy-story-builder/scripts/v2/engine.js +1068 -0
  180. package/plugins/fancy-story-builder/scripts/v2/export-markdown.js +794 -0
  181. package/plugins/fancy-story-builder/scripts/v2/genre.js +195 -0
  182. package/plugins/fancy-story-builder/scripts/v2/knowledge.js +321 -0
  183. package/plugins/fancy-story-builder/scripts/v2/macro.js +409 -0
  184. package/plugins/fancy-story-builder/scripts/v2/manifest.js +538 -0
  185. package/plugins/fancy-story-builder/scripts/v2/migrate.js +137 -0
  186. package/plugins/fancy-story-builder/scripts/v2/prompts.js +1335 -0
  187. package/plugins/fancy-story-builder/scripts/v2/rhythm.js +618 -0
  188. package/plugins/fancy-story-builder/scripts/v2/scale.js +843 -0
  189. package/plugins/fancy-story-builder/scripts/v2/snapshot.js +121 -0
  190. package/plugins/fancy-story-builder/scripts/v2/stages.js +273 -0
  191. package/plugins/fancy-story-builder/scripts/v2/writing.js +216 -0
  192. package/plugins/fancy-story-builder/scripts/writing-rules.js +167 -0
  193. package/plugins/fancy-story-builder/scripts/writing-validate.js +682 -0
  194. package/plugins/fancy-story-builder/skeleton-output.js +199 -0
  195. package/plugins/fancy-story-builder/stage-driver.js +1049 -0
  196. package/plugins/fancy-story-builder/storydb/README.md +271 -0
  197. package/plugins/fancy-story-builder/storydb/cli.js +458 -0
  198. package/plugins/fancy-story-builder/storydb/client.js +351 -0
  199. package/plugins/fancy-story-builder/storydb/compile.js +452 -0
  200. package/plugins/fancy-story-builder/storydb/context.js +288 -0
  201. package/plugins/fancy-story-builder/storydb/definitions.js +699 -0
  202. package/plugins/fancy-story-builder/storydb/driver.js +106 -0
  203. package/plugins/fancy-story-builder/storydb/dsh-tools.js +313 -0
  204. package/plugins/fancy-story-builder/storydb/export.js +175 -0
  205. package/plugins/fancy-story-builder/storydb/ids.js +31 -0
  206. package/plugins/fancy-story-builder/storydb/impact.js +170 -0
  207. package/plugins/fancy-story-builder/storydb/import.js +559 -0
  208. package/plugins/fancy-story-builder/storydb/mcp.js +99 -0
  209. package/plugins/fancy-story-builder/storydb/prompts.js +952 -0
  210. package/plugins/fancy-story-builder/storydb/stage-command.js +379 -0
  211. package/plugins/fancy-story-builder/storydb/stage.js +426 -0
  212. package/plugins/fancy-story-builder/storydb/store.js +891 -0
  213. package/plugins/fancy-story-builder/storydb/tools.js +592 -0
  214. package/plugins/fancy-story-builder/storydb/validation.js +183 -0
  215. package/plugins/fancy-story-builder/templates/v2/10-scale/scale-template.json +144 -0
  216. package/plugins/fancy-story-builder/templates/v2/20-macro/emotion-template.json +15 -0
  217. package/plugins/fancy-story-builder/templates/v2/20-macro/endings-template.json +16 -0
  218. package/plugins/fancy-story-builder/templates/v2/20-macro/lines-template.json +28 -0
  219. package/plugins/fancy-story-builder/templates/v2/20-macro/roles-template.json +10 -0
  220. package/plugins/fancy-story-builder/templates/v2/20-macro/story-template.json +19 -0
  221. package/plugins/fancy-story-builder/templates/v2/20-macro/volumes-template.json +24 -0
  222. package/plugins/fancy-story-builder/templates/v2/20-macro/world-template.json +24 -0
  223. package/plugins/fancy-story-builder/templates/v2/30-cast/characters-template.json +49 -0
  224. package/plugins/fancy-story-builder/templates/v2/30-cast/climaxes-template.json +14 -0
  225. package/plugins/fancy-story-builder/templates/v2/30-cast/conflicts-template.json +30 -0
  226. package/plugins/fancy-story-builder/templates/v2/30-cast/factions-template.json +18 -0
  227. package/plugins/fancy-story-builder/templates/v2/30-cast/foreshadows-template.json +29 -0
  228. package/plugins/fancy-story-builder/templates/v2/30-cast/information-template.json +29 -0
  229. package/plugins/fancy-story-builder/templates/v2/30-cast/items-template.json +13 -0
  230. package/plugins/fancy-story-builder/templates/v2/30-cast/locations-template.json +12 -0
  231. package/plugins/fancy-story-builder/templates/v2/30-cast/payoffs-template.json +16 -0
  232. package/plugins/fancy-story-builder/templates/v2/30-cast/relations-template.json +44 -0
  233. package/plugins/fancy-story-builder/templates/v2/40-rhythm/chapter-map-template.json +81 -0
  234. package/plugins/fancy-story-builder/templates/v2/40-rhythm/outline-template.json +162 -0
  235. package/plugins/fancy-story-builder/templates/v2/50-writing/ledger-template.json +46 -0
  236. package/plugins/fancy-story-builder/templates/v2/60-deslop/report-template.json +10 -0
  237. package/plugins/fancy-story-builder/templates//350/212/202/345/245/217//346/227/266/345/272/217/346/230/240/345/260/204/346/250/241/346/235/277.json +427 -0
  238. package/plugins/fancy-story-builder/templates//350/212/202/345/245/217//347/253/240/350/212/202/347/273/206/347/272/262/346/250/241/346/235/277.json +34 -0
  239. package/plugins/fancy-story-builder/templates//350/212/202/345/245/217//347/253/240/350/212/202/350/247/204/345/210/222/346/250/241/346/235/277.json +24 -0
  240. package/plugins/fancy-story-builder/templates//350/212/202/345/245/217//350/212/202/345/245/217/345/217/202/346/225/260/346/250/241/346/235/277.json +27 -0
  241. package/plugins/fancy-story-builder/templates//350/241/200/350/202/211//344/270/226/347/225/214/350/247/202/350/247/204/345/210/231/346/250/241/346/235/277.json +168 -0
  242. package/plugins/fancy-story-builder/templates//350/241/200/350/202/211//345/205/263/351/224/256/351/201/223/345/205/267/346/250/241/346/235/277.json +184 -0
  243. package/plugins/fancy-story-builder/templates//350/241/200/350/202/211//345/205/266/344/273/226/345/256/236/344/275/223/346/250/241/346/235/277.json +62 -0
  244. package/plugins/fancy-story-builder/templates//350/241/200/350/202/211//345/206/262/347/252/201/346/250/241/346/235/277.json +131 -0
  245. package/plugins/fancy-story-builder/templates//350/241/200/350/202/211//345/211/247/346/203/205/346/250/241/346/235/277.json +85 -0
  246. package/plugins/fancy-story-builder/templates//350/241/200/350/202/211//345/212/277/345/212/233/346/250/241/346/235/277.json +103 -0
  247. package/plugins/fancy-story-builder/templates//350/241/200/350/202/211//345/234/260/347/202/271/346/250/241/346/235/277.json +68 -0
  248. package/plugins/fancy-story-builder/templates//350/241/200/350/202/211//347/273/223/345/261/200/346/250/241/346/235/277.json +142 -0
  249. package/plugins/fancy-story-builder/templates//350/241/200/350/202/211//350/247/222/350/211/262/346/250/241/346/235/277.json +292 -0
  250. package/plugins/fancy-story-builder/templates//351/252/250/346/236/266//345/205/263/347/263/273/346/250/241/346/235/277.json +266 -0
  251. package/plugins/fancy-story-builder/templates//351/252/250/346/236/266//345/256/236/344/275/223/346/250/241/346/235/277.json +336 -0
  252. package/plugins/fancy-story-builder/templates//351/252/250/346/236/266//345/261/202/347/272/247/346/250/241/346/235/277.json +65 -0
  253. package/plugins/fancy-story-builder/templates//351/252/250/346/236/266//346/241/206/346/236/266/346/250/241/346/235/277.json +308 -0
  254. package/plugins/fancy-story-builder/templates.js +1446 -0
  255. package/plugins/fancy-story-builder/wiki-references.js +540 -0
  256. package/plugins/fancy-story-builder/writing-driver.js +1388 -0
  257. package/plugins/fancy-topic/index.js +1057 -0
  258. package/plugins/fancy-topic/prompts/01-exploration-prompt.md +115 -0
  259. package/plugins/fancy-topic/scripts/save-suggestion.js +219 -0
  260. package/plugins/fancy-topic/scripts/select-topic.js +317 -0
  261. package/plugins/fancy-topic/templates.js +476 -0
  262. package/plugins/fancy-topic/tool-deploy.js +200 -0
  263. package/commands/bootstrap/index.js +0 -164
  264. package/commands/bootstrap/init-project.js +0 -121
  265. package/commands/scan/index.js +0 -283
  266. package/commands/scan/platforms/fanqie.js +0 -146
  267. package/commands/scan/platforms/jinjiang.js +0 -143
  268. package/commands/scan/platforms/qidian.js +0 -102
  269. package/commands/scan/platforms/qimao.js +0 -127
  270. package/commands/scan/scraper.js +0 -195
  271. package/infra.js +0 -160
package/client.js ADDED
@@ -0,0 +1,369 @@
1
+ /**
2
+ * fancy-webnovel 浏览器半边。
3
+ *
4
+ * 目标:新会话里执行 /fancy-* 时,
5
+ * 1) **看得见**初始化成功/失败的结果;
6
+ * 2) **LLM 完全不被触发**(不发 provider 请求、零额度、零噪音);
7
+ * 3) **会话日志保持干净**(插件一条都不写),agent 之后照常能跑。
8
+ *
9
+ * ── 为什么必须放在浏览器半边 ──────────────────────────────────────────────
10
+ * DSH 的聊天区有一个硬门槛(dsh-client-ui-chat/lib/client.js:5385):
11
+ *
12
+ * isActive: (snapshot) => snapshot.order.some((key) => snapshot.nodes.get(key)?.kind !== "command")
13
+ *
14
+ * 只有 kind !== "command" 的节点才会点亮聊天区。而命令结果在 DSH 里**就是**
15
+ * 一个 kind === "command" 的 flow 节点(dsh-client-ui-chat/lib/client.js:5514),
16
+ * 所以「插件不碰会话」= 聊天区永远不亮 = 用户什么都看不到。这就是「彻底本地」
17
+ * 方案让人眼前一空的真正原因,不是渲染坏了,是**门就没开**。
18
+ *
19
+ * 而宿主侧点亮只有一条路:在会话里写一条 user/message 占住 surface 节点 0。
20
+ * DSH 规定 surface 第一个节点必须是 agent 的 system/message(protected first
21
+ * surface head),插件一占,agent 之后一跑整个会话就读不回来 —— 磁盘上已经
22
+ * 有 160 个这样的坏会话。所以宿主侧的路是死的。
23
+ *
24
+ * ── 浏览器半边怎么开这扇门 ────────────────────────────────────────────────
25
+ * 1) package.json 声明 dsh.client + exports["./client"]
26
+ * → dsh-client-modules/lib/index.js:479 扫 host Loader entries 找 dsh.client
27
+ * → dsh-client-modules/lib/index.js:630-636 要求 platform === "web" 且有
28
+ * exports["./client"];扫到的包进 window.__DSH_BOOT__,浏览器加载它的 client bundle。
29
+ * 这是 DSH 自带的「双面包」机制(官方示范:dsh-client-ui-goal 的 host 半边
30
+ * 就是空的 `function apply() {}`),**不需要改 harness 一行代码**。
31
+ *
32
+ * 2) 注册一个自己的 conversation-node Definition(kind = "fancy-notice"),
33
+ * 匹配 command/run + command/done,只对 fancy-* 命令产出节点。
34
+ * 它的 kind 不是 "command" → 聊天区被点亮 → 结果可见。
35
+ * 完成结果在 command/done 的位置渲染成展开的绿/红边框卡片:
36
+ * 原生命令卡默认收成一行、需要点击展开,这里直接把多行内容铺开,
37
+ * 让阶段完成/失败和 help/query 等结果都一眼可见。
38
+ *
39
+ * 3) 空白会话里聊天区不挂载 → chat target 永远不会自动激活 →
40
+ * conversationPhase(dsh-client-ui-conversation/lib/client.js:14011)
41
+ * 恒为 "blank" → 死循环。所以这里主动订阅当前会话的 chat target,
42
+ * 强制 assembler 激活它(dsh-client-ui-conversation/lib/client.js:794)。
43
+ *
44
+ * ── 铁律 ──────────────────────────────────────────────────────────────────
45
+ * 浏览器半边**只读**会话事件,一个字都不往会话里写。
46
+ * 会话的写入权仍然只有宿主插件,插件也仍然只写命令自己的结果(command/done)。
47
+ */
48
+ window.__ModuleLoader__.load({
49
+ id: "@cloud411716/fancy-webnovel",
50
+ factory: (require) => {
51
+ var module = { exports: {} };
52
+ var exports = module.exports;
53
+ Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
54
+
55
+ var react = require("react");
56
+
57
+ /** 我们自己的 conversation-node kind(非 "command",专用于点亮聊天区)。 */
58
+ var NOTICE_KIND = "fancy-notice";
59
+ /** 只对这些命令产出节点,别人的命令一概不碰。 */
60
+ var FANCY_COMMANDS = [
61
+ "fancy-bootstrap",
62
+ "fancy-analyze",
63
+ "fancy-import",
64
+ "fancy-scan",
65
+ "fancy-build",
66
+ "fancy-export",
67
+ "fancy-topic",
68
+ "fancy-db",
69
+ "fancy-project",
70
+ "fancy-material",
71
+ "fancy-douyin"
72
+ ];
73
+
74
+ /** 复刻 dsh-client-ui-chat 的 contextLocation(那边是包内私有函数)。 */
75
+ function contextLocation(context) {
76
+ return context.start?.location ?? context.matches[0]?.location ?? { kind: "unresolved" };
77
+ }
78
+
79
+ /**
80
+ * 把 command/run + command/done 折算成一条 fancy 通知。
81
+ *
82
+ * 所有 fancy-* 命令的完成结果都锚定到 command/done 的位置,
83
+ * 用展开的多行卡片显示;DSH 原生命令卡仍保留在命令开始处。
84
+ */
85
+ var noticeDefinition = {
86
+ kind: NOTICE_KIND,
87
+ target: "chat",
88
+ match: function (event) {
89
+ if (event.type === "command/run") return { id: String(event.data.commandId), role: "start" };
90
+ if (event.type === "command/done") return { id: String(event.data.commandId), role: "update" };
91
+ return null;
92
+ },
93
+ start: function (_context, match) {
94
+ return {
95
+ name: match.event.data.name ?? "",
96
+ args: match.event.data.args ?? null,
97
+ outcome: null,
98
+ doneSeq: null,
99
+ doneLocation: null
100
+ };
101
+ },
102
+ update: function (context, match) {
103
+ if (match.event.type !== "command/done") return context.state;
104
+ return {
105
+ name: context.state?.name ?? "",
106
+ args: context.state?.args ?? null,
107
+ outcome: {
108
+ kind: match.event.data.kind,
109
+ text: match.event.data.text ?? ""
110
+ },
111
+ doneSeq: match.event.seq,
112
+ doneLocation: match.location ?? null
113
+ };
114
+ },
115
+ buildViewNode: function (context) {
116
+ var state = context.state;
117
+ if (state === undefined) return null;
118
+ if (FANCY_COMMANDS.indexOf(state.name) === -1) return null;
119
+ return {
120
+ key: context.key,
121
+ kind: NOTICE_KIND,
122
+ id: context.id,
123
+ target: "chat",
124
+ // 完成通知锚在 command/done 的位置,才会跟在最后一批 assistant
125
+ // 消息之后;否则会被几十条逐批消息顶到上面,用户看不到。
126
+ anchorSeq: state.doneSeq ?? context.startSeq ?? context.matches[0].event.seq,
127
+ // 用 command/done 的 location,通知才会落在最后一个 turn/step 里;
128
+ // 沿用命令开始时的 location 会把它重新拽回第一个 turn。
129
+ location: state.doneLocation ?? contextLocation(context),
130
+ visibility: "visible",
131
+ data: state
132
+ };
133
+ }
134
+ };
135
+
136
+ /**
137
+ * 所有 fancy-* 命令的结果都渲染成展开的带边框卡片。
138
+ *
139
+ * 和 DSH 原生命令卡的区别:原生命令卡默认收成一行,要点一下才展开;
140
+ * 这里直接用 pre-wrap 保留换行,并把成功/失败用绿/红边框区分。
141
+ * 仍然只读 command/run + command/done,不写会话、不唤醒 agent。
142
+ */
143
+ function FancyNoticeNodeView(props) {
144
+ var data = props?.node?.data ?? {};
145
+ var outcome = data.outcome;
146
+ var known = FANCY_COMMANDS.indexOf(data.name) !== -1;
147
+ var baseProps = {
148
+ "data-fancy-notice": data.name ?? "",
149
+ "data-fancy-notice-kind": outcome?.kind ?? ""
150
+ };
151
+
152
+ if (outcome === null || outcome === undefined || !known) {
153
+ return react.createElement("div", {
154
+ ...baseProps,
155
+ style: { display: "none" }
156
+ });
157
+ }
158
+
159
+ var failed = outcome.kind === "error";
160
+ var accent = failed
161
+ ? "var(--dsw-alias-state-error-primary, #d92d20)"
162
+ : "var(--dsw-alias-state-success-primary, #12b76a)";
163
+ var background = failed
164
+ ? "rgba(217, 45, 32, 0.08)"
165
+ : "rgba(18, 183, 106, 0.08)";
166
+ var args = String(data.args ?? "").trim();
167
+ var label = (data.name ?? "") + (args === "" ? "" : " " + args);
168
+
169
+ return react.createElement("div", {
170
+ ...baseProps,
171
+ role: "status",
172
+ "aria-live": "polite",
173
+ style: {
174
+ margin: "12px 0",
175
+ padding: "12px 14px",
176
+ border: "1px solid " + accent,
177
+ borderLeftWidth: "4px",
178
+ borderRadius: "10px",
179
+ background: background,
180
+ lineHeight: 1.6,
181
+ fontSize: "13px",
182
+ maxHeight: "60vh",
183
+ overflow: "auto"
184
+ }
185
+ },
186
+ react.createElement("div", {
187
+ key: "command",
188
+ style: {
189
+ marginBottom: "6px",
190
+ color: "var(--dsw-alias-label-secondary, inherit)",
191
+ fontSize: "12px",
192
+ opacity: 0.75
193
+ }
194
+ }, label),
195
+ react.createElement("div", {
196
+ key: "body",
197
+ style: {
198
+ whiteSpace: "pre-wrap",
199
+ wordBreak: "break-word"
200
+ }
201
+ }, outcome.text || (failed ? "命令失败" : "命令完成")));
202
+ }
203
+
204
+ var RUNNING_STYLE_ID = "@cloud411716/fancy-webnovel/running";
205
+
206
+ /**
207
+ * 执行中提示的动画样式。
208
+ *
209
+ * 用 CSS 动画而不是 JS 定时器:不触发 React 重渲染,也不会因为
210
+ * 命令跑了几十分钟而堆积定时器。原地转圈 + 三点呼吸足够让用户
211
+ * 知道任务还在跑。
212
+ */
213
+ function ensureRunningStyle() {
214
+ if (typeof document === "undefined" || document.head === undefined) return;
215
+ if (document.querySelector("style[data-plugin-css=" + JSON.stringify(RUNNING_STYLE_ID) + "]") !== null) return;
216
+ var tag = document.createElement("style");
217
+ tag.dataset.plugin = "@cloud411716/fancy-webnovel";
218
+ tag.dataset.pluginCss = RUNNING_STYLE_ID;
219
+ tag.textContent = [
220
+ "@keyframes fancy-command-spin{to{transform:rotate(360deg)}}",
221
+ "@keyframes fancy-command-dot{0%,80%,100%{opacity:.25}40%{opacity:1}}"
222
+ ].join("");
223
+ document.head.appendChild(tag);
224
+ }
225
+
226
+ /**
227
+ * 原生命令卡的替代:命令执行期间显示一条轻量记录;命令完成后返回
228
+ * null,把“执行过这个命令”的记录交给 command/done 位置的展开卡片。
229
+ *
230
+ * 这样不会出现「顶部一张收起卡 + 底部一张展开卡」的重复记录。
231
+ * 执行中的转圈和三点是 CSS 动画,原地变化,证明任务没卡死。
232
+ */
233
+ function FancyCommandRecordView(props) {
234
+ var node = props?.node;
235
+ if (node === undefined || node === null) return null;
236
+ if (node.outcome !== null && node.outcome !== undefined) return null;
237
+ var args = String(node.args ?? "").trim();
238
+ var label = (node.name ?? "命令") + (args === "" ? "" : " " + args);
239
+ return react.createElement("div", {
240
+ "data-fancy-command-running": node.name ?? "",
241
+ role: "status",
242
+ "aria-live": "polite",
243
+ style: {
244
+ display: "flex",
245
+ alignItems: "center",
246
+ gap: "8px",
247
+ margin: "4px 0",
248
+ padding: "6px 10px",
249
+ border: "1px solid var(--dsw-alias-border-l2, rgba(127, 127, 127, 0.25))",
250
+ borderRadius: "8px",
251
+ color: "var(--dsw-alias-label-secondary, inherit)",
252
+ fontSize: "13px",
253
+ lineHeight: 1.5
254
+ }
255
+ },
256
+ react.createElement("span", {
257
+ key: "spinner",
258
+ "data-fancy-command-spinner": "true",
259
+ "aria-hidden": "true",
260
+ style: {
261
+ display: "inline-block",
262
+ width: "12px",
263
+ height: "12px",
264
+ border: "2px solid currentColor",
265
+ borderTopColor: "transparent",
266
+ borderRadius: "50%",
267
+ animation: "fancy-command-spin 0.8s linear infinite",
268
+ flexShrink: 0
269
+ }
270
+ }),
271
+ react.createElement("span", { key: "label" }, label + " 执行中"),
272
+ react.createElement("span", {
273
+ key: "dots",
274
+ "data-fancy-command-dots": "true",
275
+ "aria-hidden": "true",
276
+ style: { display: "inline-flex", gap: "2px" }
277
+ },
278
+ react.createElement("span", {
279
+ key: "dot-0",
280
+ style: {
281
+ animation: "fancy-command-dot 1.2s ease-in-out infinite",
282
+ animationDelay: "0s",
283
+ opacity: 0.25
284
+ }
285
+ }, "."),
286
+ react.createElement("span", {
287
+ key: "dot-1",
288
+ style: {
289
+ animation: "fancy-command-dot 1.2s ease-in-out infinite",
290
+ animationDelay: "0.15s",
291
+ opacity: 0.25
292
+ }
293
+ }, "."),
294
+ react.createElement("span", {
295
+ key: "dot-2",
296
+ style: {
297
+ animation: "fancy-command-dot 1.2s ease-in-out infinite",
298
+ animationDelay: "0.3s",
299
+ opacity: 0.25
300
+ }
301
+ }, ".")));
302
+ }
303
+
304
+ /**
305
+ * 客户端插件体。
306
+ * @param ctx - client root context.
307
+ */
308
+ function apply(ctx) {
309
+ ensureRunningStyle();
310
+ ctx.uiConversation.events.register(noticeDefinition);
311
+
312
+ // 注册我们这个 kind 的渲染器。不注册的话 DSH 会走 JsonBlock 兜底,
313
+ // 聊天区里会冒出一行「未知 surface 事件:fancy-notice」。
314
+ ctx.slots.inject("conversation.chat.node", function () {
315
+ return ctx.slots.register({
316
+ name: "conversation.chat.node",
317
+ key: NOTICE_KIND
318
+ }, FancyNoticeNodeView);
319
+ });
320
+
321
+ // 接管 fancy-* 的原生命令卡:执行期间显示一条轻量记录,完成后隐藏,
322
+ // 避免和 command/done 位置的展开卡片重复。
323
+ FANCY_COMMANDS.forEach(function (name) {
324
+ ctx.slots.inject("conversation.chat.commandview", function () {
325
+ return ctx.slots.register({
326
+ name: "conversation.chat.commandview",
327
+ key: name
328
+ }, FancyCommandRecordView);
329
+ });
330
+ });
331
+
332
+ /*
333
+ * 强制激活当前会话的 chat target。
334
+ *
335
+ * 空白会话里聊天区不挂载 → chat target 不会被激活 →
336
+ * conversation.activeTargets 为空 → conversationPhase 恒为 "blank" →
337
+ * ConversationSession 直接 return null → 聊天区永远不出现。
338
+ * 这里订阅一次 chat target,逼 assembler 把它激活
339
+ * (dsh-client-ui-conversation/lib/index.js:794 activateTarget)。
340
+ */
341
+ var disposeCurrent = null;
342
+ ctx.effect(function () {
343
+ var sessions = ctx.sessions;
344
+ return sessions.list.subscribe(function () {
345
+ var current = sessions.list.getSnapshot().current;
346
+ if (typeof current !== "string" || current.length === 0) return;
347
+ if (disposeCurrent !== null) {
348
+ try {
349
+ disposeCurrent();
350
+ } catch {}
351
+ disposeCurrent = null;
352
+ }
353
+ try {
354
+ var binding = ctx.uiConversation.binding(current);
355
+ if (binding === undefined) return;
356
+ disposeCurrent = binding.target("chat").subscribe(function () {});
357
+ } catch (error) {
358
+ disposeCurrent = null;
359
+ ctx.logger.warn("fancy-webnovel: chat target activation failed: %s", String(error));
360
+ }
361
+ });
362
+ });
363
+ }
364
+
365
+ exports.apply = apply;
366
+ exports.inject = ["uiConversation", "sessions", "slots"];
367
+ return module.exports;
368
+ }
369
+ });
package/cordis.patch.yml CHANGED
@@ -1,17 +1,8 @@
1
- # @cloud411716/fancy-webnovel — DSH Cordis Plugin Patch
1
+ # fancy-webnovel plugin entry.
2
2
  #
3
- # IMPORTANT: This plugin creates project directories at user-specified paths
4
- # (which may be OUTSIDE the DSH workspace). Therefore it REQUIRES
5
- # `danger-full-access` sandbox mode. Add this to your profile's patch, e.g.:
6
- #
7
- # - insert:
8
- # - id: sandbox-policy
9
- # config:
10
- # mode: danger-full-access
11
- #
12
- # All other services used (commands, userQuestions, timer, events, session)
13
- # are part of dsh-base and available in every DSH UI (tui, web, desktop).
14
-
3
+ # DSH also gets native tools (fancy_workspace / fancy_db / fancy_story_*), so
4
+ # the MCP client bridge is optional. To expose the portable MCP server as
5
+ # mcp__fancy__* tools, see docs/mcp-profile.example.yml.
15
6
  - insert:
16
7
  - id: fancy-webnovel
17
8
  name: "@cloud411716/fancy-webnovel"
@@ -0,0 +1,118 @@
1
+ # fancy-douyin 抖音图文采集
2
+
3
+ 把记录在案的抖音账号主页下的图文作品,逐篇截图、交给 LLM 提取素材,并写入
4
+ 工作区素材库(`source=other`,kind 不受内置列表限制)。
5
+
6
+ ## 命令
7
+
8
+ ```text
9
+ /fancy-douyin add <主页地址>
10
+ /fancy-douyin list
11
+ /fancy-douyin remove <序号|地址>
12
+ /fancy-douyin
13
+ /fancy-douyin help
14
+ ```
15
+
16
+ `add` 只接受账号主页,例如:
17
+
18
+ ```text
19
+ https://www.douyin.com/user/MS4wLjABAAAA...
20
+ ```
21
+
22
+ 单篇作品地址、视频地址和短链会被拒绝。
23
+
24
+ ## 无参数运行
25
+
26
+ ```text
27
+ 依次扫描账号主页
28
+ → 收集所有 /note/ 图文作品
29
+ → 跳过 crawled.json 中已成功入库的作品
30
+ → 打开作品,下载轮播原图并转成 JPG(避免滚动时截到半张图)
31
+ → 给 LLM 发送截图路径和素材提取提示词
32
+ → LLM 写 JSONL
33
+ → 校验并写入 material_library(source=other)
34
+ → 记录作品 ID
35
+ → 继续下一篇
36
+ ```
37
+
38
+ 失败的作品不会写入采集记录,下次运行会重试。
39
+
40
+ ## 登录与验证
41
+
42
+ Chrome 使用工作区内的独立 profile:
43
+
44
+ ```text
45
+ <工作区>/.fancy/douyin/chrome-profile/
46
+ ```
47
+
48
+ cookies 会额外镜像到:
49
+
50
+ ```text
51
+ <工作区>/.fancy/douyin/login-state.json
52
+ ```
53
+
54
+ 遇到登录框、滑块、图形验证或短信验证时,插件会弹窗:
55
+
56
+ ```text
57
+ 已登陆 / 取消
58
+ ```
59
+
60
+ 请在打开的 Chrome 窗口里手动完成登录或验证,然后点「已登陆」。插件不会自动
61
+ 绕过验证,也不会保存账号密码。
62
+
63
+ 运行模式:
64
+
65
+ - 第一次运行、或没有有效登录态时:有头 Chrome,方便手动登录;本次会继续用有头跑完。
66
+ - login-state.json 里有抖音登录 cookie 时:后续运行自动使用无头 Chrome。
67
+ - 无头运行中如果登录态失效:插件会自动关闭无头 Chrome,切换到有头 Chrome
68
+ 让你登录,登录完成后再继续。
69
+ - 每次运行结束都会关闭 Chrome;登录态留在 profile 和 login-state.json 中。
70
+
71
+ ## 数据目录
72
+
73
+ ```text
74
+ <工作区>/.fancy/douyin/
75
+ ├── accounts.json # 账号主页记录
76
+ ├── crawled.json # 已成功入库的作品 ID
77
+ ├── login-state.json # cookies 镜像(0600)
78
+ ├── chrome-profile/ # Chrome 登录态
79
+ ├── chrome.json # CDP 端口、profile 与 headed/headless 模式
80
+ └── output/<时间>/ # 截图、note.json、materials.jsonl
81
+ ```
82
+
83
+ 浏览器脚本部署在:
84
+
85
+ ```text
86
+ <工作区>/.fancy-tools/fancy-douyin/
87
+ ├── start-chrome.cjs
88
+ ├── scan-account.cjs
89
+ ├── crawl-note.cjs
90
+ ├── lib.cjs
91
+ └── package.json
92
+ ```
93
+
94
+ ## 素材格式
95
+
96
+ 每篇作品的 LLM 输出写到:
97
+
98
+ ```text
99
+ <作品目录>/materials.jsonl
100
+ ```
101
+
102
+ 字段:
103
+
104
+ ```text
105
+ id / kind / name / tags / genres / note / source
106
+ ```
107
+
108
+ `source` 固定为 `other`。`kind` 可以是任意非空字符串;如果内容能归入内置
109
+ material kind,优先使用内置 kind。插件会强制把 source 改成 other,然后运行
110
+ `.fancy-tools/fancy-material/validate.js` 校验,再写入数据库。
111
+
112
+ ## 注意
113
+
114
+ - 只采集个人研究/已授权账号,不要批量抓取或二次分发受版权保护的图片和文案。
115
+ - 截图不是页面视口截图,而是下载每页轮播原图后再转成 JPEG,所以不会因为
116
+ 滚动、懒加载或页面动画截到残缺图片。
117
+ - 抖音 DOM/类名会变,`scan-account.cjs` 和 `crawl-note.cjs` 的选择器可能需要维护。
118
+ - 每次运行结束会自动关闭 Chrome;登录态保存在 profile 和 login-state.json 中,下次运行自动复用。