@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
@@ -0,0 +1,501 @@
1
+ /**
2
+ * Workspace resolution and lifecycle.
3
+ *
4
+ * A workspace is one directory the user chooses on first initialization. It
5
+ * owns the shared scan/analyze artifacts, the SurrealDB data directory, and all
6
+ * project directories. Project directories live at:
7
+ *
8
+ * <workspace>/projects/<uuid>/
9
+ *
10
+ * Workspace-level state is deliberately small:
11
+ *
12
+ * <workspace>/.fancy/workspace.json non-secret pointers and server config
13
+ * <workspace>/.fancy/secrets.json root + per-project DB credentials (0600)
14
+ * <workspace>/.fancy-deployed scan/topic phase used by the shared tools
15
+ * <workspace>/扫榜结果/ shared scan output
16
+ * <workspace>/对标/ shared reference-novel analysis
17
+ * <workspace>/references/ shared writing references
18
+ * <workspace>/projects/<uuid>/ one project
19
+ *
20
+ * The global pointer at ~/.config/fancy-webnovel/workspace.json lets commands
21
+ * and the MCP server find the workspace when the caller's cwd is elsewhere.
22
+ * FANCY_HOME always wins.
23
+ */
24
+
25
+ import fs from 'fs';
26
+ import os from 'os';
27
+ import path from 'path';
28
+ import crypto from 'crypto';
29
+
30
+ export const WORKSPACE_SCHEMA_VERSION = 1;
31
+ export const WORKSPACE_DIR = '.fancy';
32
+ export const WORKSPACE_FILE = 'workspace.json';
33
+ export const SECRETS_FILE = 'secrets.json';
34
+ export const PROJECTS_DIR = 'projects';
35
+ export const SCAN_DIR = '扫榜结果';
36
+ export const ANALYZE_DIR = '对标';
37
+ export const REFERENCES_DIR = 'references';
38
+ export const REFERENCE_SOURCES_DIR = '参考资料';
39
+ export const GLOBAL_REGISTRY_FILE = 'workspaces.json';
40
+
41
+ export class WorkspaceError extends Error {
42
+ constructor(code, message, detail) {
43
+ super(message);
44
+ this.name = 'WorkspaceError';
45
+ this.code = code;
46
+ this.detail = detail;
47
+ }
48
+ }
49
+
50
+ export function expandHome(value) {
51
+ const text = String(value || '');
52
+ if (text === '~') return os.homedir();
53
+ if (text.startsWith('~/') || text.startsWith('~\\')) {
54
+ return path.join(os.homedir(), text.slice(2));
55
+ }
56
+ return text;
57
+ }
58
+
59
+ export function resolvePath(value, base) {
60
+ const expanded = expandHome(value);
61
+ if (!expanded) return expanded;
62
+ if (path.isAbsolute(expanded)) return path.resolve(expanded);
63
+ return path.resolve(base || process.cwd(), expanded);
64
+ }
65
+
66
+ function pad(value, width = 2) {
67
+ return String(value).padStart(width, '0');
68
+ }
69
+
70
+ /**
71
+ * ISO-8601 in the machine's local timezone, including its offset.
72
+ * Example (UTC+8): 2026-11-11T17:16:08.219+08:00
73
+ */
74
+ export function localIsoString(date = new Date()) {
75
+ const d = date instanceof Date ? date : new Date(date);
76
+ if (Number.isNaN(d.getTime())) return '';
77
+ const offsetMinutes = -d.getTimezoneOffset();
78
+ const sign = offsetMinutes >= 0 ? '+' : '-';
79
+ const abs = Math.abs(offsetMinutes);
80
+ return d.getFullYear() + '-' + pad(d.getMonth() + 1) + '-' + pad(d.getDate())
81
+ + 'T' + pad(d.getHours()) + ':' + pad(d.getMinutes()) + ':' + pad(d.getSeconds())
82
+ + '.' + pad(d.getMilliseconds(), 3)
83
+ + sign + pad(Math.floor(abs / 60)) + ':' + pad(abs % 60);
84
+ }
85
+
86
+ export function formatLocalTime(value) {
87
+ if (value === undefined || value === null || value === '') return '';
88
+ const d = value instanceof Date ? value : new Date(value);
89
+ return Number.isNaN(d.getTime()) ? String(value) : localIsoString(d);
90
+ }
91
+
92
+ export function workspaceMetaPath(root) {
93
+ return path.join(root, WORKSPACE_DIR, WORKSPACE_FILE);
94
+ }
95
+
96
+ export function workspaceSecretsPath(root) {
97
+ return path.join(root, WORKSPACE_DIR, SECRETS_FILE);
98
+ }
99
+
100
+ export function deployedPath(root) {
101
+ return path.join(root, '.fancy-deployed');
102
+ }
103
+
104
+ export function globalPointerPath() {
105
+ const configHome = process.env.XDG_CONFIG_HOME
106
+ ? path.resolve(process.env.XDG_CONFIG_HOME)
107
+ : path.join(os.homedir(), '.config');
108
+ return path.join(configHome, 'fancy-webnovel', 'workspace.json');
109
+ }
110
+
111
+ export function globalRegistryPath() {
112
+ const configHome = process.env.XDG_CONFIG_HOME
113
+ ? path.resolve(process.env.XDG_CONFIG_HOME)
114
+ : path.join(os.homedir(), '.config');
115
+ return path.join(configHome, 'fancy-webnovel', GLOBAL_REGISTRY_FILE);
116
+ }
117
+
118
+ export function workspacePaths(root) {
119
+ const base = path.resolve(root);
120
+ return {
121
+ root: base,
122
+ fancy: path.join(base, WORKSPACE_DIR),
123
+ projects: path.join(base, PROJECTS_DIR),
124
+ scan: path.join(base, SCAN_DIR),
125
+ analyze: path.join(base, ANALYZE_DIR),
126
+ references: path.join(base, REFERENCES_DIR),
127
+ referenceSources: path.join(base, REFERENCE_SOURCES_DIR),
128
+ tmp: path.join(base, '.tmp'),
129
+ surreal: path.join(base, WORKSPACE_DIR, 'surreal'),
130
+ surrealData: path.join(base, WORKSPACE_DIR, 'surreal', 'data'),
131
+ surrealLog: path.join(base, WORKSPACE_DIR, 'surreal', 'server.log'),
132
+ surrealPid: path.join(base, WORKSPACE_DIR, 'surreal', 'server.pid'),
133
+ project: (id) => path.join(base, PROJECTS_DIR, String(id)),
134
+ };
135
+ }
136
+
137
+ export function readJsonFile(file) {
138
+ try {
139
+ return JSON.parse(fs.readFileSync(file, 'utf8'));
140
+ } catch {
141
+ return null;
142
+ }
143
+ }
144
+
145
+ export function atomicWriteJson(file, value, opts = {}) {
146
+ fs.mkdirSync(path.dirname(file), { recursive: true });
147
+ const tmp = path.join(path.dirname(file), `.${path.basename(file)}.${process.pid}.${Date.now()}.tmp`);
148
+ const body = JSON.stringify(value, null, 2) + '\n';
149
+ fs.writeFileSync(tmp, body, { encoding: 'utf8', mode: opts.mode ?? 0o600 });
150
+ fs.renameSync(tmp, file);
151
+ try { fs.chmodSync(file, opts.mode ?? 0o600); } catch { /* best effort */ }
152
+ return file;
153
+ }
154
+
155
+ export function isWorkspace(root) {
156
+ if (!root) return false;
157
+ // Full workspaces have .fancy/workspace.json. A directory that only has
158
+ // .fancy-deployed is still a valid workspace for the shared scan/topic/
159
+ // analyze tools (and for lightweight/legacy workspaces created before the
160
+ // registry existed). Project management tools that need secrets/server
161
+ // config will report a clear error if the full config is absent.
162
+ return fs.existsSync(workspaceMetaPath(root)) || fs.existsSync(deployedPath(root));
163
+ }
164
+
165
+ export function readWorkspace(root) {
166
+ const file = workspaceMetaPath(root);
167
+ const data = readJsonFile(file);
168
+ if (!data || typeof data !== 'object' || Array.isArray(data)) {
169
+ throw new WorkspaceError('WORKSPACE_UNREADABLE', '无法读取工作区状态文件:' + file);
170
+ }
171
+ if (data.schema_version !== undefined && Number(data.schema_version) > WORKSPACE_SCHEMA_VERSION) {
172
+ throw new WorkspaceError('WORKSPACE_VERSION', '工作区数据版本高于当前插件支持版本:' + data.schema_version + '。请升级 fancy-webnovel 后重试。');
173
+ }
174
+ return data;
175
+ }
176
+
177
+ /**
178
+ * Walk up from a directory looking for a workspace marker. This handles the
179
+ * common case where the DSH session cwd is a project directory
180
+ * (`A/projects/<uuid>`) or a subdirectory of it.
181
+ */
182
+ export function findWorkspace(start) {
183
+ if (!start) return null;
184
+ let current = path.resolve(start);
185
+ try {
186
+ if (fs.existsSync(current) && !fs.statSync(current).isDirectory()) current = path.dirname(current);
187
+ } catch { /* fall through */ }
188
+ for (;;) {
189
+ if (isWorkspace(current)) return current;
190
+ const parent = path.dirname(current);
191
+ if (parent === current) return null;
192
+ current = parent;
193
+ }
194
+ }
195
+
196
+ function normalizeRegistryEntry(entry) {
197
+ if (!entry || typeof entry !== 'object') return null;
198
+ const workspace = typeof entry.workspace === 'string' ? path.resolve(expandHome(entry.workspace)) : '';
199
+ if (!workspace) return null;
200
+ return {
201
+ workspace,
202
+ workspace_id: typeof entry.workspace_id === 'string' ? entry.workspace_id : '',
203
+ name: typeof entry.name === 'string' && entry.name ? entry.name : path.basename(workspace),
204
+ created_at: typeof entry.created_at === 'string' ? entry.created_at : '',
205
+ updated_at: typeof entry.updated_at === 'string' ? entry.updated_at : '',
206
+ last_used: typeof entry.last_used === 'string' ? entry.last_used : (entry.updated_at || ''),
207
+ };
208
+ }
209
+
210
+ /**
211
+ * Read the machine-level workspace registry. The registry is append/update:
212
+ * every initialized workspace has one entry. A legacy single-pointer file
213
+ * (`workspace.json`) is still read so existing installs are not lost.
214
+ */
215
+ export function readGlobalRegistry() {
216
+ const file = globalRegistryPath();
217
+ const raw = readJsonFile(file);
218
+ const entries = [];
219
+ if (raw && Array.isArray(raw.workspaces)) {
220
+ for (const entry of raw.workspaces) {
221
+ const normalized = normalizeRegistryEntry(entry);
222
+ if (normalized) entries.push(normalized);
223
+ }
224
+ }
225
+ // Merge the legacy single pointer if it is not already in the registry.
226
+ const legacy = readJsonFile(globalPointerPath());
227
+ if (legacy && typeof legacy.workspace === 'string') {
228
+ const normalized = normalizeRegistryEntry({ workspace: legacy.workspace, updated_at: legacy.updated_at, last_used: legacy.updated_at });
229
+ if (normalized && !entries.some((item) => item.workspace === normalized.workspace)) {
230
+ // Fill identity from the workspace itself when possible.
231
+ try {
232
+ const meta = readWorkspace(normalized.workspace);
233
+ normalized.workspace_id = meta.workspace_id || '';
234
+ normalized.name = meta.name || normalized.name;
235
+ normalized.created_at = meta.created_at || '';
236
+ } catch { /* lightweight or missing */ }
237
+ entries.push(normalized);
238
+ }
239
+ }
240
+ const deduped = new Map();
241
+ for (const entry of entries) deduped.set(entry.workspace, entry);
242
+ return {
243
+ schema_version: WORKSPACE_SCHEMA_VERSION,
244
+ updated_at: raw && typeof raw.updated_at === 'string' ? raw.updated_at : '',
245
+ workspaces: [...deduped.values()],
246
+ };
247
+ }
248
+
249
+ export function writeGlobalRegistry(registry) {
250
+ const value = {
251
+ schema_version: WORKSPACE_SCHEMA_VERSION,
252
+ updated_at: localIsoString(),
253
+ workspaces: (registry && Array.isArray(registry.workspaces) ? registry.workspaces : [])
254
+ .map(normalizeRegistryEntry)
255
+ .filter(Boolean),
256
+ };
257
+ atomicWriteJson(globalRegistryPath(), value, { mode: 0o600 });
258
+ return value;
259
+ }
260
+
261
+ export function registerGlobalWorkspace(root, opts = {}) {
262
+ const workspacePath = path.resolve(root);
263
+ const registry = readGlobalRegistry();
264
+ const now = localIsoString();
265
+ const index = registry.workspaces.findIndex((entry) => entry.workspace === workspacePath);
266
+ let meta = null;
267
+ try { meta = readWorkspace(workspacePath); } catch { /* lightweight */ }
268
+ const previous = index >= 0 ? registry.workspaces[index] : null;
269
+ const entry = {
270
+ workspace: workspacePath,
271
+ workspace_id: opts.workspace_id || (meta && meta.workspace_id) || (previous && previous.workspace_id) || '',
272
+ name: opts.name || (meta && meta.name) || (previous && previous.name) || path.basename(workspacePath),
273
+ created_at: (meta && meta.created_at) || (previous && previous.created_at) || now,
274
+ updated_at: now,
275
+ last_used: opts.touch === false ? ((previous && previous.last_used) || now) : now,
276
+ };
277
+ if (index >= 0) registry.workspaces[index] = entry;
278
+ else registry.workspaces.push(entry);
279
+ writeGlobalRegistry(registry);
280
+ // Keep the legacy pointer in sync for older plugin versions.
281
+ atomicWriteJson(globalPointerPath(), {
282
+ schema_version: WORKSPACE_SCHEMA_VERSION,
283
+ workspace: workspacePath,
284
+ updated_at: now,
285
+ }, { mode: 0o600 });
286
+ return entry;
287
+ }
288
+
289
+ export function listGlobalWorkspaces() {
290
+ return readGlobalRegistry().workspaces
291
+ .map((entry) => ({ ...entry, exists: isWorkspace(entry.workspace) }))
292
+ .sort((a, b) => (Date.parse(b.last_used) || 0) - (Date.parse(a.last_used) || 0));
293
+ }
294
+
295
+ export function readGlobalPointer() {
296
+ // Prefer the legacy last-used pointer; fall back to the newest existing
297
+ // registry entry when an old install predates the registry file.
298
+ const legacy = readJsonFile(globalPointerPath());
299
+ if (legacy && typeof legacy.workspace === 'string') return legacy;
300
+ const existing = listGlobalWorkspaces().find((entry) => entry.exists);
301
+ return existing ? { workspace: existing.workspace, updated_at: existing.last_used } : null;
302
+ }
303
+
304
+ export function writeGlobalPointer(root) {
305
+ const entry = registerGlobalWorkspace(root);
306
+ return { file: globalRegistryPath(), entry };
307
+ }
308
+
309
+ /**
310
+ * Resolve the workspace from, in order:
311
+ * 1. an explicit path (CLI/MCP argument)
312
+ * 2. FANCY_HOME
313
+ * 3. an ancestor of cwd containing .fancy/workspace.json
314
+ * 4. the global pointer written by init
315
+ */
316
+ export function resolveWorkspace(opts = {}) {
317
+ const explicit = opts.explicit || opts.workspace || process.env.FANCY_HOME;
318
+ if (explicit) {
319
+ const root = resolvePath(explicit, opts.cwd || process.cwd());
320
+ if (!isWorkspace(root)) {
321
+ throw new WorkspaceError(
322
+ 'WORKSPACE_NOT_INITIALIZED',
323
+ '指定目录不是 fancy-webnovel 工作区:' + root + '(缺少 .fancy/workspace.json)。请先运行 /fancy-bootstrap 初始化工作区。',
324
+ );
325
+ }
326
+ return root;
327
+ }
328
+
329
+ const found = findWorkspace(opts.cwd || process.cwd());
330
+ if (found) return found;
331
+
332
+ // The machine-level registry is a convenience for long-lived MCP sessions
333
+ // and explicit "use the last workspace" flows. Slash commands should not
334
+ // silently target the last workspace when the user runs them from an
335
+ // unrelated directory; require cwd/FANCY_HOME unless the caller opts in.
336
+ if (opts.allowGlobal) {
337
+ const existingWorkspaces = listGlobalWorkspaces().filter((entry) => entry.exists);
338
+ if (existingWorkspaces.length) return existingWorkspaces[0].workspace;
339
+
340
+ const pointer = readGlobalPointer();
341
+ if (pointer && pointer.workspace) {
342
+ const root = path.resolve(expandHome(pointer.workspace));
343
+ if (isWorkspace(root)) return root;
344
+ }
345
+ }
346
+
347
+ const required = opts.required !== false;
348
+ if (!required) return null;
349
+ throw new WorkspaceError(
350
+ 'WORKSPACE_NOT_INITIALIZED',
351
+ '当前目录不是 fancy-webnovel 工作区。请先运行 /fancy-bootstrap 初始化工作区。',
352
+ );
353
+ }
354
+
355
+ function randomPassword(bytes = 24) {
356
+ return crypto.randomBytes(bytes).toString('base64url');
357
+ }
358
+
359
+ export function defaultSurrealConfig(root) {
360
+ return {
361
+ url: 'http://127.0.0.1:8000/rpc',
362
+ bind: '127.0.0.1:8000',
363
+ namespace: 'fancy',
364
+ registry_database: 'fancy_registry',
365
+ material_database: 'material_library',
366
+ data_dir: workspacePaths(root).surrealData,
367
+ pid_file: workspacePaths(root).surrealPid,
368
+ log_file: workspacePaths(root).surrealLog,
369
+ };
370
+ }
371
+
372
+ export function readSecrets(root) {
373
+ const file = workspaceSecretsPath(root);
374
+ const data = readJsonFile(file);
375
+ if (!data || typeof data !== 'object' || Array.isArray(data)) {
376
+ throw new WorkspaceError('SECRETS_UNREADABLE', '无法读取工作区密钥文件:' + file);
377
+ }
378
+ return data;
379
+ }
380
+
381
+ export function writeSecrets(root, secrets) {
382
+ return atomicWriteJson(workspaceSecretsPath(root), secrets, { mode: 0o600 });
383
+ }
384
+
385
+ export function ensureSecrets(root) {
386
+ const file = workspaceSecretsPath(root);
387
+ if (fs.existsSync(file)) return readSecrets(root);
388
+ const secrets = {
389
+ schema_version: WORKSPACE_SCHEMA_VERSION,
390
+ surreal: { username: 'root', password: randomPassword(24) },
391
+ projects: {},
392
+ };
393
+ writeSecrets(root, secrets);
394
+ return secrets;
395
+ }
396
+
397
+ export function ensureDeployed(root, phase = 'initialized') {
398
+ const file = deployedPath(root);
399
+ if (fs.existsSync(file)) return readJsonFile(file);
400
+ const data = {
401
+ deployed_at: localIsoString(),
402
+ project_root_abs: path.resolve(root),
403
+ workspace: true,
404
+ phase,
405
+ };
406
+ atomicWriteJson(file, data, { mode: 0o600 });
407
+ return data;
408
+ }
409
+
410
+ export function workspaceTopicPath(root) {
411
+ return path.join(root, '_novel_topic.json');
412
+ }
413
+
414
+ export function workspaceSuggestionPath(root) {
415
+ return path.join(root, '_novel_topic_suggestion.json');
416
+ }
417
+
418
+ /**
419
+ * Create the workspace directory tree and its non-secret config. This is
420
+ * idempotent: calling it on an existing workspace returns the existing config
421
+ * without overwriting secrets or the active project.
422
+ */
423
+ export function initWorkspace(rootRaw, opts = {}) {
424
+ const root = resolvePath(rootRaw, opts.cwd || process.cwd());
425
+ if (!root) throw new WorkspaceError('WORKSPACE_PATH_REQUIRED', '请提供工作区目录。');
426
+ if (fs.existsSync(root) && !fs.statSync(root).isDirectory()) {
427
+ throw new WorkspaceError('WORKSPACE_NOT_DIRECTORY', '工作区路径不是目录:' + root);
428
+ }
429
+ fs.mkdirSync(root, { recursive: true });
430
+ const paths = workspacePaths(root);
431
+ for (const dir of [paths.fancy, paths.projects, paths.scan, paths.analyze, paths.references, paths.referenceSources, paths.tmp, paths.surrealData]) {
432
+ fs.mkdirSync(dir, { recursive: true });
433
+ }
434
+
435
+ const existing = fs.existsSync(workspaceMetaPath(root)) ? readWorkspace(root) : null;
436
+ if (existing) {
437
+ ensureSecrets(root);
438
+ ensureDeployed(root, existing.phase || 'initialized');
439
+ writeGlobalPointer(root);
440
+ return { root, created: false, workspace: existing, paths };
441
+ }
442
+
443
+ const workspace = {
444
+ schema_version: WORKSPACE_SCHEMA_VERSION,
445
+ workspace_id: crypto.randomUUID(),
446
+ name: opts.name || path.basename(root),
447
+ created_at: localIsoString(),
448
+ updated_at: localIsoString(),
449
+ active_project: null,
450
+ surreal: defaultSurrealConfig(root),
451
+ };
452
+ atomicWriteJson(workspaceMetaPath(root), workspace, { mode: 0o600 });
453
+ ensureSecrets(root);
454
+ ensureDeployed(root, 'initialized');
455
+ writeGlobalPointer(root);
456
+ return { root, created: true, workspace, paths };
457
+ }
458
+
459
+ export function updateWorkspace(root, patch) {
460
+ const current = readWorkspace(root);
461
+ const next = {
462
+ ...current,
463
+ ...patch,
464
+ surreal: { ...(current.surreal || {}), ...(patch && patch.surreal ? patch.surreal : {}) },
465
+ updated_at: localIsoString(),
466
+ };
467
+ atomicWriteJson(workspaceMetaPath(root), next, { mode: 0o600 });
468
+ return next;
469
+ }
470
+
471
+ export function setActiveProject(root, projectId) {
472
+ return updateWorkspace(root, { active_project: projectId || null });
473
+ }
474
+
475
+ export function getActiveProject(root) {
476
+ try {
477
+ return readWorkspace(root).active_project || null;
478
+ } catch {
479
+ return null;
480
+ }
481
+ }
482
+
483
+ export function workspaceSummary(root) {
484
+ const workspace = readWorkspace(root);
485
+ const paths = workspacePaths(root);
486
+ return {
487
+ root: paths.root,
488
+ workspace_id: workspace.workspace_id,
489
+ name: workspace.name,
490
+ active_project: workspace.active_project || null,
491
+ surreal: workspace.surreal,
492
+ paths: {
493
+ projects: paths.projects,
494
+ scan: paths.scan,
495
+ analyze: paths.analyze,
496
+ references: paths.references,
497
+ referenceSources: paths.referenceSources,
498
+ exports: null,
499
+ },
500
+ };
501
+ }
package/package.json CHANGED
@@ -1,23 +1,80 @@
1
1
  {
2
2
  "name": "@cloud411716/fancy-webnovel",
3
- "version": "1.0.46",
3
+ "version": "3.0.0",
4
4
  "type": "module",
5
5
  "main": "index.js",
6
6
  "exports": {
7
- ".": "./index.js"
7
+ ".": "./index.js",
8
+ "./client": "./client.js"
9
+ },
10
+ "imports": {
11
+ "#infra": "./lib/infra.js",
12
+ "#scraper-utils": "./lib/scraper-utils.js",
13
+ "#error-codes": "./lib/error-codes.js",
14
+ "#atomic-write": "./lib/atomic-write.js",
15
+ "#topic-schema": "./lib/topic-schema.js",
16
+ "#topic-fields": "./lib/topic-fields.js",
17
+ "#llm-wiki": "./lib/llm-wiki.js",
18
+ "#deployed-state": "./lib/deployed-state.js",
19
+ "#chinese-num": "./lib/chinese-num.js",
20
+ "#topic-decision": "./lib/topic-decision.js",
21
+ "#path-guard": "./lib/path-guard.js",
22
+ "#direction-labels": "./lib/direction-labels.js",
23
+ "#story-builder/*": "./plugins/fancy-story-builder/*",
24
+ "#chapter-order": "./lib/chapter-order.js",
25
+ "#workspace": "./lib/workspace.js",
26
+ "#projects": "./lib/projects.js",
27
+ "#db-init": "./lib/db-init.js",
28
+ "#surreal-server": "./lib/surreal-server.js",
29
+ "#material-db": "./lib/material-db.js"
30
+ },
31
+ "engines": {
32
+ "node": ">=18.0.0"
33
+ },
34
+ "scripts": {
35
+ "test": "node tests/run.js",
36
+ "sync:profile": "bash scripts/sync-profile.sh"
8
37
  },
9
38
  "files": [
10
39
  "index.js",
11
- "infra.js",
40
+ "lib/",
41
+ "templates.js",
12
42
  "cordis.patch.yml",
13
- "commands/"
43
+ "README.md",
44
+ "docs/",
45
+ "plugins/",
46
+ "client.js"
14
47
  ],
15
48
  "dependencies": {
16
- "playwright-core": "1.48.0"
49
+ "@surrealdb/node": "3.2.0",
50
+ "@surrealdb/node-native": "4.0.1",
51
+ "graphology": "^0.26.0",
52
+ "playwright-core": "^1.48.0",
53
+ "surrealdb": "2.2.2",
54
+ "tesseract.js": "^7.0.0"
55
+ },
56
+ "peerDependencies": {
57
+ "@deepseek-ai/dsh-llm": "^0.1.1-rc.2",
58
+ "@deepseek-ai/dsh-tools": "*"
59
+ },
60
+ "peerDependenciesMeta": {
61
+ "@deepseek-ai/dsh-llm": {
62
+ "optional": true
63
+ },
64
+ "@deepseek-ai/dsh-tools": {
65
+ "optional": true
66
+ }
17
67
  },
18
68
  "dsh": {
19
69
  "bundle": {
20
70
  "patch": "./cordis.patch.yml"
71
+ },
72
+ "client": {
73
+ "platform": "web"
21
74
  }
75
+ },
76
+ "bin": {
77
+ "storydb": "./plugins/fancy-story-builder/storydb/cli.js",
78
+ "fancy-mcp": "./plugins/fancy-story-builder/storydb/mcp.js"
22
79
  }
23
80
  }