@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
@@ -1,195 +0,0 @@
1
- /**
2
- * scraper.js — 扫榜编排层
3
- *
4
- * 职责:
5
- * 1. 对每个平台/榜单调用对应的 platform scraper
6
- * 2. 重试逻辑(每个榜单最多 3 次,间隔 5 秒)
7
- * 3. 通过 DSH 事件实时汇报进度
8
- * 4. 生成 Markdown 文件并写入项目目录
9
- * 5. 所有操作纳入 ctx.effect() 生命周期,Ctrl+C 可终止
10
- */
11
-
12
- import { mkdirSync } from 'fs';
13
- import { join } from 'path';
14
- import { sleep, scanProgress, writeFile } from '../../infra.js';
15
- import * as qidian from './platforms/qidian.js';
16
- import * as fanqie from './platforms/fanqie.js';
17
- import * as jinjiang from './platforms/jinjiang.js';
18
- import * as qimao from './platforms/qimao.js';
19
-
20
- const SCRAPER_TIMEOUT = 30 * 60 * 1000; // 30 分钟 per rank
21
- const MAX_RETRIES = 3;
22
- const RETRY_DELAY = 5000;
23
-
24
- // Platform registry
25
- const PLATFORMS = { qidian, fanqie, jinjiang, qimao };
26
-
27
- function todayStr() {
28
- const d = new Date();
29
- return `${d.getFullYear()}${String(d.getMonth() + 1).padStart(2, '0')}${String(d.getDate()).padStart(2, '0')}`;
30
- }
31
-
32
- /**
33
- * Run scans for a list of { platform, channel, type, label } entries.
34
- * Uses ctx.effect() for lifecycle management and ctx.events for progress.
35
- *
36
- * @param {object} ctx - Cordis plugin context
37
- * @param {string} projectRoot
38
- * @param {Array} ranks - array of { platform, channel, type, label }
39
- * @param {AbortSignal} signal
40
- * @returns {Promise<{ totalBooks: number, totalFiles: number, failedRanks: Array, lastReceipt: object|null }>}
41
- */
42
- export async function runScans(ctx, projectRoot, ranks, signal) {
43
- const scanDir = join(projectRoot, '扫榜数据');
44
- mkdirSync(scanDir, { recursive: true });
45
-
46
- const today = todayStr();
47
- let totalBooks = 0;
48
- let totalFiles = 0;
49
- const failedRanks = [];
50
- let lastReceipt = null;
51
-
52
- for (const rank of ranks) {
53
- if (signal.aborted) {
54
- scanProgress(ctx, 'rank-done', {
55
- platform: rank.platform, channel: rank.channel, type: rank.type,
56
- ok: false, written: 0, failed: 0, errorMsg: 'aborted by user',
57
- });
58
- break;
59
- }
60
-
61
- const platform = PLATFORMS[rank.platform];
62
- if (!platform) {
63
- failedRanks.push({ rank, err: `unknown platform: ${rank.platform}` });
64
- continue;
65
- }
66
-
67
- scanProgress(ctx, 'start', {
68
- platform: rank.platform,
69
- channel: rank.channel,
70
- type: rank.type,
71
- label: rank.label,
72
- });
73
-
74
- let result;
75
- let attempt = 0;
76
-
77
- while (attempt < MAX_RETRIES) {
78
- if (signal.aborted) break;
79
- attempt++;
80
- try {
81
- result = await withTimeout(
82
- platform.scrapeRank(rank.id, ctx, signal),
83
- ctx,
84
- SCRAPER_TIMEOUT,
85
- );
86
- if (result.ok) break;
87
- } catch (err) {
88
- result = { ok: false, books: [], error: err.message };
89
- }
90
- if (attempt < MAX_RETRIES && !signal.aborted) {
91
- await sleep(ctx, RETRY_DELAY);
92
- }
93
- }
94
-
95
- if (signal.aborted) {
96
- failedRanks.push({ rank, err: 'aborted' });
97
- continue;
98
- }
99
-
100
- if (!result.ok || !result.books.length) {
101
- failedRanks.push({ rank, err: result.error ?? 'no books' });
102
- scanProgress(ctx, 'rank-done', {
103
- platform: rank.platform, channel: rank.channel, type: rank.type,
104
- ok: false, written: 0, failed: result.books?.length ?? 0,
105
- errorMsg: result.error,
106
- });
107
- continue;
108
- }
109
-
110
- // Write markdown file
111
- let filepath;
112
- try {
113
- const content = renderMarkdown(platform.label, rank.label, result.books);
114
- const filename = `${platform.label}${rank.label}_${today}.md`;
115
- filepath = join(scanDir, filename);
116
- await writeFile(filepath, content);
117
- totalFiles++;
118
- totalBooks += result.books.length;
119
- lastReceipt = {
120
- platform: rank.platform,
121
- date: today,
122
- scan_files: [filepath],
123
- topic_decision: join(scanDir, `topic_decision_${today}.md`),
124
- };
125
- } catch (err) {
126
- failedRanks.push({ rank, err: `write failed: ${err.message}` });
127
- scanProgress(ctx, 'rank-done', {
128
- platform: rank.platform, channel: rank.channel, type: rank.type,
129
- ok: false, written: 0, failed: result.books.length,
130
- errorMsg: `write failed: ${err.message}`,
131
- });
132
- continue;
133
- }
134
-
135
- scanProgress(ctx, 'rank-done', {
136
- platform: rank.platform, channel: rank.channel, type: rank.type,
137
- ok: true, written: result.books.length, failed: 0,
138
- });
139
- }
140
-
141
- scanProgress(ctx, 'done', {
142
- platform: ranks[0]?.platform ?? '',
143
- totalBooks,
144
- totalFiles,
145
- ok: failedRanks.length < ranks.length,
146
- });
147
-
148
- return { totalBooks, totalFiles, failedRanks, lastReceipt };
149
- }
150
-
151
- /**
152
- * Wrap a promise with a ctx.timeout deadline.
153
- * ctx.timeout(fn, ms) returns a synchronous dispose function that is safe
154
- * to call multiple times (idempotent), so this is guaranteed leak-free.
155
- */
156
- async function withTimeout(promise, ctx, ms) {
157
- let dispose;
158
- const timeout = new Promise((_, reject) => {
159
- dispose = ctx.timeout(() => reject(new Error(`timeout after ${ms}ms`)), ms);
160
- });
161
- try {
162
- return await Promise.race([promise, timeout]);
163
- } finally {
164
- try { dispose?.(); } catch (_) {}
165
- }
166
- }
167
-
168
- /**
169
- * Render books as Markdown.
170
- */
171
- function renderMarkdown(platformLabel, rankLabel, books) {
172
- const lines = [
173
- `# ${platformLabel} · ${rankLabel}`,
174
- '',
175
- `> 自动采集于 ${new Date().toLocaleString('zh-CN')}`,
176
- '',
177
- '| 排名 | 书名 | 作者 | 类别 | 字数 | 简介 |',
178
- '|------|------|------|------|------|------|',
179
- ];
180
-
181
- for (const b of books) {
182
- const title = escapeTable(b.title || '');
183
- const author = escapeTable(b.author || '');
184
- const category = escapeTable(b.category || '-');
185
- const words = b.words ? `${(b.words / 10000).toFixed(1)}万` : '-';
186
- const intro = escapeTable((b.intro || '').slice(0, 80));
187
- lines.push(`| ${b.rank ?? ''} | ${title} | ${author} | ${category} | ${words} | ${intro} |`);
188
- }
189
-
190
- return lines.join('\n') + '\n';
191
- }
192
-
193
- function escapeTable(s) {
194
- return String(s).replace(/\|/g, '\\|').replace(/\n/g, ' ');
195
- }
package/infra.js DELETED
@@ -1,160 +0,0 @@
1
- /**
2
- * infra.js — DSH Plugin Core Infrastructure
3
- *
4
- * Provides:
5
- * notify(session, kind, text) — return success/error result for TUI toast + Web left-panel
6
- * llmFollowup(invocation,prompt) — hand a prompt to the LLM as a user message
7
- * StatusLine(session) — in-place updatable status bar via surfaceOp replace
8
- * scanProgress(ctx, phase, data) — emit structured scan progress events
9
- * sleep(ctx, ms) — DSH-native promise delay via ctx.timeout()
10
- *
11
- * All fancy-* plugins import from this file.
12
- */
13
-
14
- // --------------------------------------------------------------------------
15
- // notify — return error result propagated through dsh-commands settleThrown()
16
- // --------------------------------------------------------------------------
17
- //
18
-
19
- /** Strip ANSI escape sequences from a string (defensive, prevents TTY leak). */
20
- function stripAnsi(str) {
21
- return String(str).replace(/\x1b[\s\S]*?([a-zA-Z]|$)|\x1b[()][AB012]|\x1b\][^\x07]*(?:\x07|$)/g, '$1');
22
- }
23
-
24
- /**
25
- * @param {object} session - DSH Session object (unused, kept for API compatibility)
26
- * @param {'error'|'success'} kind
27
- * @param {string} text
28
- * @returns {{ kind: 'error'|'success', text: string }} Result propagated through
29
- * dsh-commands settleThrown() → command/done → channel.notify() (TUI toast)
30
- * and command/done (Web left-panel with real commandId prefix)
31
- */
32
- export function notify(session, kind, text) {
33
- return { kind, text: stripAnsi(text) };
34
- }
35
-
36
- // --------------------------------------------------------------------------
37
- // StatusLine — in-place updatable status bar using session surfaceOp replace
38
- // --------------------------------------------------------------------------
39
-
40
- /**
41
- * Appends a single session event and updates it in-place on subsequent calls
42
- * using session's surfaceOp replace mechanism. The event always stays at the
43
- * same log position; only its content changes — visually a terminal status bar.
44
- *
45
- * Usage:
46
- * const status = new StatusLine(session);
47
- * status.update('Working…');
48
- * status.update('Done ✓');
49
- * status.clear();
50
- */
51
- export class StatusLine {
52
- #session;
53
- #seq;
54
-
55
- constructor(session) { this.#session = session; }
56
-
57
- update(text) {
58
- const event = this.#seq
59
- ? this.#session.append(
60
- 'fancy/status',
61
- { content: [{ type: 'text', text }], source: { kind: 'user' } },
62
- { surfaceOp: { op: 'replace', start: this.#seq, end: this.#seq } }
63
- )
64
- : this.#session.append(
65
- 'fancy/status',
66
- { content: [{ type: 'text', text }], source: { kind: 'user' } },
67
- { surfaceOp: 'append' }
68
- );
69
- this.#seq ??= event.seq;
70
- }
71
-
72
- clear() { this.update(''); }
73
- }
74
-
75
- // --------------------------------------------------------------------------
76
- // llmFollowup — hand a prompt to the LLM as a user message
77
- // --------------------------------------------------------------------------
78
-
79
- /**
80
- * @param {object} invocation - CommandInvocation from ctx.commands handler
81
- * @param {string|object} content
82
- */
83
- export function llmFollowup(invocation, content) {
84
- const prompt = typeof content === 'string' ? content : JSON.stringify(content);
85
- invocation.agent.followup({
86
- content: [{ type: 'text', text: prompt }],
87
- source: { kind: 'user' },
88
- });
89
- }
90
-
91
- // --------------------------------------------------------------------------
92
- // sleep — DSH-native promise delay using ctx.timeout()
93
- // --------------------------------------------------------------------------
94
-
95
- /**
96
- * Return a promise that resolves after `ms` milliseconds.
97
- * Uses ctx.timeout() (Cordis timer service) instead of native setTimeout.
98
- *
99
- * @param {object} ctx - Cordis plugin context
100
- * @param {number} ms - milliseconds to wait
101
- * @returns {Promise<void>}
102
- */
103
- export function sleep(ctx, ms) {
104
- return new Promise((resolve) => {
105
- ctx.timeout(resolve, ms);
106
- });
107
- }
108
-
109
- // --------------------------------------------------------------------------
110
- // scanProgress — emit structured scan progress via DSH events
111
- // --------------------------------------------------------------------------
112
-
113
- /**
114
- * Emit a scan progress event. These flow into the session log and can be
115
- * observed by the running agent for real-time progress feedback.
116
- *
117
- * @param {object} ctx
118
- * @param {'start'|'book'|'rank-done'|'done'} phase
119
- * @param {object} data
120
- */
121
- export function scanProgress(ctx, phase, data) {
122
- switch (phase) {
123
- case 'start':
124
- ctx.events.emit('fancy/scan:start', data.platform, data.channel, data.type, data.label);
125
- break
126
- case 'book':
127
- ctx.events.emit('fancy/scan:book', data.platform, data.channel, data.type,
128
- data.index, data.total, data.title, data.rank);
129
- break
130
- case 'rank-done':
131
- ctx.events.emit('fancy/scan:rank-done', data.platform, data.channel, data.type,
132
- data.ok, data.written, data.failed, data.errorMsg);
133
- break
134
- case 'done':
135
- ctx.events.emit('fancy/scan:done', data.platform, data.totalBooks, data.totalFiles, data.ok);
136
- break
137
- }
138
- }
139
-
140
- // --------------------------------------------------------------------------
141
- // writeFile — Node.js fs, used for project dirs outside workspace
142
- // --------------------------------------------------------------------------
143
-
144
- /**
145
- * Write text to a file, creating directories as needed.
146
- * Uses Node.js fs directly (not ctx.fs) since project roots may be
147
- * outside the DSH sandbox workspace.
148
- *
149
- * @param {string} filepath
150
- * @param {string} content
151
- * @param {string} [encoding='utf-8']
152
- */
153
- export async function writeFile(filepath, content, encoding = 'utf-8') {
154
- const { writeFileSync, mkdirSync } = await import('fs');
155
- const { dirname } = await import('path');
156
- mkdirSync(dirname(filepath), { recursive: true });
157
- writeFileSync(filepath, content, encoding);
158
- }
159
-
160
-