@cloud411716/fancy-webnovel 2.2.0 → 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.
- package/client.js +201 -15
- package/cordis.patch.yml +5 -0
- package/docs/douyin-plugin.md +118 -0
- package/docs/material-library/README.md +404 -0
- package/docs/material-library/import.js +437 -0
- package/docs/material-library/prompt.md +370 -0
- package/docs/material-library/queries.surql +94 -0
- package/docs/material-library/sample.jsonl +14 -0
- package/docs/material-library/schema.surql +46 -0
- package/docs/mcp-profile.example.yml +24 -0
- package/docs/story-graph-design.md +602 -0
- package/docs/story-graph-validation.md +81 -0
- package/docs/workspace-mcp.md +284 -0
- package/index.js +11 -7
- package/lib/atomic-write.js +109 -0
- package/lib/chapter-order.js +100 -0
- package/lib/confirmation.js +80 -0
- package/lib/db-init.js +123 -0
- package/lib/deployed-state.js +11 -11
- package/lib/direction-labels.js +265 -0
- package/lib/error-codes.js +31 -31
- package/lib/infra.js +29 -4
- package/lib/material-db.js +149 -0
- package/lib/path-guard.js +150 -0
- package/lib/projects.js +400 -0
- package/lib/scraper-utils.js +140 -0
- package/lib/surreal-server.js +338 -0
- package/lib/topic-decision.js +860 -0
- package/lib/topic-fields.js +251 -0
- package/lib/topic-schema.js +225 -0
- package/lib/workspace.js +501 -0
- package/package.json +29 -4
- package/plugins/fancy-analyze/analyze-definitions.md +519 -0
- package/plugins/fancy-analyze/index.js +1128 -77
- package/plugins/fancy-analyze/ingest-coverage.js +107 -0
- package/plugins/fancy-analyze/schema-analyze.md +355 -107
- package/plugins/fancy-analyze/scripts/append-summary-chapter.js +612 -0
- package/plugins/fancy-analyze/summary-progress.js +71 -7
- package/plugins/fancy-analyze/templates.js +437 -139
- package/plugins/fancy-bootstrap/index.js +276 -131
- package/plugins/fancy-bootstrap/scripts/init.js +59 -25
- package/plugins/fancy-bootstrap/templates.js +35 -18
- package/plugins/fancy-db/index.js +172 -0
- package/plugins/fancy-douyin/deploy.js +134 -0
- package/plugins/fancy-douyin/index.js +551 -0
- package/plugins/fancy-douyin/prompts/01-analysis-prompt.md +84 -0
- package/plugins/fancy-douyin/scripts/crawl-note.cjs +194 -0
- package/plugins/fancy-douyin/scripts/lib.cjs +197 -0
- package/plugins/fancy-douyin/scripts/scan-account.cjs +128 -0
- package/plugins/fancy-douyin/scripts/start-chrome.cjs +203 -0
- package/plugins/fancy-douyin/state.js +197 -0
- package/plugins/fancy-douyin/templates.js +127 -0
- package/plugins/fancy-export/exporter.js +805 -0
- package/plugins/fancy-export/index.js +63 -0
- package/plugins/fancy-import/import-driver.js +29 -6
- package/plugins/fancy-import/index.js +13 -5
- package/plugins/fancy-import/prompts/01-import-prompt.md +39 -14
- package/plugins/fancy-import/templates.js +17 -8
- package/plugins/fancy-material/exporter.js +153 -0
- package/plugins/fancy-material/genres.js +256 -0
- package/plugins/fancy-material/index.js +835 -0
- package/plugins/fancy-material/inventory.js +143 -0
- package/plugins/fancy-material/kinds.js +313 -0
- package/plugins/fancy-material/queue.js +55 -0
- package/plugins/fancy-material/scripts/query.js +254 -0
- package/plugins/fancy-material/templates.js +258 -0
- package/plugins/fancy-material/tool-deploy.js +138 -0
- package/plugins/fancy-material/validate.js +149 -0
- package/plugins/fancy-project/index.js +133 -0
- package/plugins/fancy-scan/existing-block.js +100 -0
- package/plugins/fancy-scan/index.js +410 -21
- package/plugins/fancy-scan/prompts/01-analysis-prompt.md +302 -0
- package/plugins/fancy-scan/scripts/append-block.js +725 -0
- package/plugins/fancy-scan/scripts/report-validate.js +824 -0
- package/plugins/fancy-scan/scripts/run-scan.js +169 -22
- package/plugins/fancy-scan/scripts/scrapers/fanqie-rank-scraper.js +145 -16
- package/plugins/fancy-scan/scripts/scrapers/jjwxc-rank-scraper.js +179 -49
- package/plugins/fancy-scan/scripts/scrapers/qimao-rank-scraper.js +38 -20
- package/plugins/fancy-scan/templates/blocks.js +155 -0
- package/plugins/fancy-scan/templates/platforms.js +372 -0
- package/plugins/fancy-scan/templates/report-schema.js +90 -0
- package/plugins/fancy-scan/templates.js +545 -146
- package/plugins/fancy-scan/tool-deploy.js +335 -0
- package/plugins/fancy-scan/wait-turn.js +46 -0
- package/plugins/fancy-story-builder/db/README.md +106 -0
- package/plugins/fancy-story-builder/db/client.js +228 -0
- package/plugins/fancy-story-builder/db/commands.js +145 -0
- package/plugins/fancy-story-builder/db/ids.js +39 -0
- package/plugins/fancy-story-builder/db/import.js +798 -0
- package/plugins/fancy-story-builder/db/project.js +299 -0
- package/plugins/fancy-story-builder/db/query.js +361 -0
- package/plugins/fancy-story-builder/db/schema.js +417 -0
- package/plugins/fancy-story-builder/db/stage-sync.js +238 -0
- package/plugins/fancy-story-builder/flesh-output.js +19 -6
- package/plugins/fancy-story-builder/index.js +4105 -396
- package/plugins/fancy-story-builder/patch-driver.js +390 -0
- package/plugins/fancy-story-builder/prompts/01-skeleton-prompt.md +29 -19
- package/plugins/fancy-story-builder/prompts/02-flesh-prompt.md +72 -10
- package/plugins/fancy-story-builder/prompts/03-rhythm-prompt.md +40 -5
- package/plugins/fancy-story-builder/prompts/04-writing-prompt.md +37 -0
- package/plugins/fancy-story-builder/purpose-template.md +42 -66
- package/plugins/fancy-story-builder/references/formulas-skeleton.json +229 -56
- package/plugins/fancy-story-builder/references/genre-profiles.json +3514 -0
- package/plugins/fancy-story-builder/references/glossary.md +15 -6
- package/plugins/fancy-story-builder/references/index.md +97 -6
- package/plugins/fancy-story-builder/references//350/212/202/345/245/217/346/214/207/345/257/274/00-index.md +1 -1
- package/plugins/fancy-story-builder/references//350/212/202/345/245/217/346/214/207/345/257/274/07-audit.md +25 -7
- package/plugins/fancy-story-builder/references//350/241/200/350/202/211/346/214/207/345/257/274/01-character.md +5 -1
- package/plugins/fancy-story-builder/references//350/241/200/350/202/211/346/214/207/345/257/274/03-relations.md +6 -2
- package/plugins/fancy-story-builder/references//350/241/200/350/202/211/346/214/207/345/257/274/05-audit.md +15 -5
- package/plugins/fancy-story-builder/references//351/252/250/346/236/266/346/214/207/345/257/274/00-index.md +1 -1
- package/plugins/fancy-story-builder/references//351/252/250/346/236/266/346/214/207/345/257/274/01-positioning.md +2 -1
- package/plugins/fancy-story-builder/references//351/252/250/346/236/266/346/214/207/345/257/274/03-entities.md +1 -1
- package/plugins/fancy-story-builder/references//351/252/250/346/236/266/346/214/207/345/257/274/06-audit.md +19 -1
- package/plugins/fancy-story-builder/schema-setting.json +1100 -0
- package/plugins/fancy-story-builder/schema-setting.md +201 -293
- package/plugins/fancy-story-builder/scripts/brief-validate.js +164 -0
- package/plugins/fancy-story-builder/scripts/content-quality.js +4 -1
- package/plugins/fancy-story-builder/scripts/flesh-validate.js +135 -12
- package/plugins/fancy-story-builder/scripts/graph-utils.js +146 -47
- package/plugins/fancy-story-builder/scripts/reference-log.js +110 -0
- package/plugins/fancy-story-builder/scripts/rhythm-validate.js +100 -4
- package/plugins/fancy-story-builder/scripts/schema-pages.js +2668 -0
- package/plugins/fancy-story-builder/scripts/semantic-lint.js +5 -1
- package/plugins/fancy-story-builder/scripts/skeleton-validate.js +158 -17
- package/plugins/fancy-story-builder/scripts/stage-batches.js +36 -2
- package/plugins/fancy-story-builder/scripts/stage-contract.js +12 -0
- package/plugins/fancy-story-builder/scripts/story-artifacts.js +933 -0
- package/plugins/fancy-story-builder/scripts/upstream-query.js +1 -1
- package/plugins/fancy-story-builder/scripts/v2/baseline.js +112 -0
- package/plugins/fancy-story-builder/scripts/v2/cast.js +517 -0
- package/plugins/fancy-story-builder/scripts/v2/context.js +434 -0
- package/plugins/fancy-story-builder/scripts/v2/contracts.js +493 -0
- package/plugins/fancy-story-builder/scripts/v2/core.js +419 -0
- package/plugins/fancy-story-builder/scripts/v2/engine.js +1068 -0
- package/plugins/fancy-story-builder/scripts/v2/export-markdown.js +794 -0
- package/plugins/fancy-story-builder/scripts/v2/genre.js +195 -0
- package/plugins/fancy-story-builder/scripts/v2/knowledge.js +321 -0
- package/plugins/fancy-story-builder/scripts/v2/macro.js +409 -0
- package/plugins/fancy-story-builder/scripts/v2/manifest.js +538 -0
- package/plugins/fancy-story-builder/scripts/v2/migrate.js +137 -0
- package/plugins/fancy-story-builder/scripts/v2/prompts.js +1335 -0
- package/plugins/fancy-story-builder/scripts/v2/rhythm.js +618 -0
- package/plugins/fancy-story-builder/scripts/v2/scale.js +843 -0
- package/plugins/fancy-story-builder/scripts/v2/snapshot.js +121 -0
- package/plugins/fancy-story-builder/scripts/v2/stages.js +273 -0
- package/plugins/fancy-story-builder/scripts/v2/writing.js +216 -0
- package/plugins/fancy-story-builder/storydb/README.md +271 -0
- package/plugins/fancy-story-builder/storydb/cli.js +458 -0
- package/plugins/fancy-story-builder/storydb/client.js +351 -0
- package/plugins/fancy-story-builder/storydb/compile.js +452 -0
- package/plugins/fancy-story-builder/storydb/context.js +288 -0
- package/plugins/fancy-story-builder/storydb/definitions.js +699 -0
- package/plugins/fancy-story-builder/storydb/driver.js +106 -0
- package/plugins/fancy-story-builder/storydb/dsh-tools.js +313 -0
- package/plugins/fancy-story-builder/storydb/export.js +175 -0
- package/plugins/fancy-story-builder/storydb/ids.js +31 -0
- package/plugins/fancy-story-builder/storydb/impact.js +170 -0
- package/plugins/fancy-story-builder/storydb/import.js +559 -0
- package/plugins/fancy-story-builder/storydb/mcp.js +99 -0
- package/plugins/fancy-story-builder/storydb/prompts.js +952 -0
- package/plugins/fancy-story-builder/storydb/stage-command.js +379 -0
- package/plugins/fancy-story-builder/storydb/stage.js +426 -0
- package/plugins/fancy-story-builder/storydb/store.js +891 -0
- package/plugins/fancy-story-builder/storydb/tools.js +592 -0
- package/plugins/fancy-story-builder/storydb/validation.js +183 -0
- package/plugins/fancy-story-builder/templates/v2/10-scale/scale-template.json +144 -0
- package/plugins/fancy-story-builder/templates/v2/20-macro/emotion-template.json +15 -0
- package/plugins/fancy-story-builder/templates/v2/20-macro/endings-template.json +16 -0
- package/plugins/fancy-story-builder/templates/v2/20-macro/lines-template.json +28 -0
- package/plugins/fancy-story-builder/templates/v2/20-macro/roles-template.json +10 -0
- package/plugins/fancy-story-builder/templates/v2/20-macro/story-template.json +19 -0
- package/plugins/fancy-story-builder/templates/v2/20-macro/volumes-template.json +24 -0
- package/plugins/fancy-story-builder/templates/v2/20-macro/world-template.json +24 -0
- package/plugins/fancy-story-builder/templates/v2/30-cast/characters-template.json +49 -0
- package/plugins/fancy-story-builder/templates/v2/30-cast/climaxes-template.json +14 -0
- package/plugins/fancy-story-builder/templates/v2/30-cast/conflicts-template.json +30 -0
- package/plugins/fancy-story-builder/templates/v2/30-cast/factions-template.json +18 -0
- package/plugins/fancy-story-builder/templates/v2/30-cast/foreshadows-template.json +29 -0
- package/plugins/fancy-story-builder/templates/v2/30-cast/information-template.json +29 -0
- package/plugins/fancy-story-builder/templates/v2/30-cast/items-template.json +13 -0
- package/plugins/fancy-story-builder/templates/v2/30-cast/locations-template.json +12 -0
- package/plugins/fancy-story-builder/templates/v2/30-cast/payoffs-template.json +16 -0
- package/plugins/fancy-story-builder/templates/v2/30-cast/relations-template.json +44 -0
- package/plugins/fancy-story-builder/templates/v2/40-rhythm/chapter-map-template.json +81 -0
- package/plugins/fancy-story-builder/templates/v2/40-rhythm/outline-template.json +162 -0
- package/plugins/fancy-story-builder/templates/v2/50-writing/ledger-template.json +46 -0
- package/plugins/fancy-story-builder/templates/v2/60-deslop/report-template.json +10 -0
- 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 +267 -44
- 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 +121 -19
- 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 +129 -21
- 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
- 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 +76 -17
- 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 +56 -8
- 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 +63 -13
- 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 +34 -6
- 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 +81 -18
- 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 +202 -42
- 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 +160 -27
- 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 +217 -41
- 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 +215 -41
- package/plugins/fancy-story-builder/templates.js +272 -74
- package/plugins/fancy-story-builder/wiki-references.js +429 -93
- package/plugins/fancy-story-builder/writing-driver.js +336 -9
- package/plugins/fancy-topic/index.js +414 -61
- package/plugins/fancy-topic/prompts/01-exploration-prompt.md +115 -0
- package/plugins/fancy-topic/scripts/save-suggestion.js +219 -0
- package/plugins/fancy-topic/scripts/select-topic.js +106 -63
- package/plugins/fancy-topic/templates.js +225 -122
- package/plugins/fancy-topic/tool-deploy.js +200 -0
- package/lib/safe-rm.js +0 -94
|
@@ -3,41 +3,58 @@
|
|
|
3
3
|
*/
|
|
4
4
|
|
|
5
5
|
export const bootstrap = {
|
|
6
|
-
askPath(question = '📂
|
|
6
|
+
askPath(question = '📂 请输入工作区目录(或输入 . 表示当前目录)') {
|
|
7
7
|
return { id: 'path', question };
|
|
8
8
|
},
|
|
9
9
|
|
|
10
|
-
notify({ type, projectRoot, err, custom }) {
|
|
10
|
+
notify({ type, projectRoot, err, custom, arg }) {
|
|
11
11
|
switch (type) {
|
|
12
|
+
case 'help':
|
|
13
|
+
return `📁 工作区初始化 用法
|
|
14
|
+
|
|
15
|
+
/fancy-bootstrap 交互式选择工作区目录
|
|
16
|
+
/fancy-bootstrap <工作区目录> 在指定目录初始化工作区
|
|
17
|
+
/fancy-bootstrap help 显示本帮助
|
|
18
|
+
|
|
19
|
+
作用:初始化工作区目录、SurrealDB、素材库和共享目录,是其他 fancy-webnovel 插件的前置步骤。`;
|
|
20
|
+
|
|
12
21
|
case 'no_path':
|
|
13
|
-
return '⚠️
|
|
22
|
+
return '⚠️ 未提供工作区路径,操作已取消。';
|
|
14
23
|
case 'using_cwd':
|
|
15
24
|
return `📂 使用当前目录:${projectRoot}`;
|
|
16
25
|
case 'invalid_path':
|
|
17
|
-
|
|
26
|
+
// 原因由调用方拼进来(路径校验的规则不止一条,说清楚是哪条)
|
|
27
|
+
return '❌ 路径不合法:' + (arg || '路径中不能包含 .. 或 shell 特殊字符') +
|
|
28
|
+
(projectRoot ? `\n路径:${projectRoot}` : '');
|
|
29
|
+
case 'dir_not_found':
|
|
30
|
+
return `❌ 工作区目录不存在:${projectRoot || ''}\n` +
|
|
31
|
+
'请先创建该目录,或选择一个已存在的目录。';
|
|
32
|
+
case 'not_a_directory':
|
|
33
|
+
return `❌ 工作区路径不是目录:${projectRoot || ''}\n` +
|
|
34
|
+
'请选择一个目录路径。';
|
|
18
35
|
case 'already_initialized':
|
|
19
|
-
return '⚠️
|
|
36
|
+
return '⚠️ 该目录已完成初始化(.fancy-deployed 已存在)。';
|
|
20
37
|
case 'confirm':
|
|
21
|
-
return `❓
|
|
38
|
+
return `❓ 确认将以下路径作为工作区目录?\n${projectRoot}`;
|
|
22
39
|
case 'cancelled':
|
|
23
|
-
return '🚫
|
|
40
|
+
return '🚫 操作已取消。';
|
|
24
41
|
case 'no_provider':
|
|
25
|
-
return '⚠️
|
|
42
|
+
return '⚠️ 当前运行环境不支持交互式提问。请在 dsh-tui 中运行该命令,或确认 Web UI 已正确加载交互组件。';
|
|
26
43
|
case 'no_project_root':
|
|
27
|
-
return '❌
|
|
44
|
+
return '❌ 无法确定工作区:' + (err || '会话未提供当前工作目录') + '。请在工作区目录下运行该命令。';
|
|
28
45
|
case 'ask_failed':
|
|
29
|
-
return '❌
|
|
46
|
+
return '❌ 交互提问失败:' + (err || '未知错误');
|
|
30
47
|
case 'confirm_invalid':
|
|
31
|
-
return '❌
|
|
48
|
+
return '❌ 无法识别回复:' + (custom || '') + '。请选择「确认创建」,或输入「确认创建」。';
|
|
32
49
|
case 'success':
|
|
33
|
-
return `✅
|
|
50
|
+
return `✅ 工作区初始化完成。
|
|
34
51
|
下一步:
|
|
35
|
-
1. 启动
|
|
36
|
-
2. 在
|
|
37
|
-
3. 在
|
|
38
|
-
4. 在
|
|
39
|
-
5. 将 API
|
|
40
|
-
6. 运行 /fancy-scan
|
|
52
|
+
1. 启动 llm-wiki,并在使用本插件期间保持其运行;
|
|
53
|
+
2. 在 llm-wiki 设置中配置 LLM 模型信息,并合理设置上下文窗口大小;
|
|
54
|
+
3. 在 llm-wiki 设置中关闭“资料文件夹自动监控”;
|
|
55
|
+
4. 在 llm-wiki 设置中启用本地 HTTP API 并设置访问令牌;
|
|
56
|
+
5. 将 API 地址与访问令牌写入 .fancy-deployed 文件;
|
|
57
|
+
6. 运行 /fancy-scan 扫榜。`;
|
|
41
58
|
case 'error':
|
|
42
59
|
return '❌ 初始化失败:' + (typeof err === 'function' ? err() : err);
|
|
43
60
|
default:
|
|
@@ -0,0 +1,172 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* fancy-db — workspace SurrealDB server and project database management.
|
|
3
|
+
*
|
|
4
|
+
* Usage:
|
|
5
|
+
* /fancy-db status
|
|
6
|
+
* /fancy-db install
|
|
7
|
+
* /fancy-db start | stop
|
|
8
|
+
* /fancy-db init [all|project <名字|UUID>]
|
|
9
|
+
* /fancy-db list
|
|
10
|
+
* /fancy-db remove <名字|UUID> [--purge]
|
|
11
|
+
*/
|
|
12
|
+
|
|
13
|
+
import { askUserQuestion, notify } from '#infra';
|
|
14
|
+
import { readWorkspace, resolveWorkspace } from '#workspace';
|
|
15
|
+
import { deleteProject, listProjects, getProject } from '#projects';
|
|
16
|
+
import { initDatabases, initProjectDatabase, initWorkspaceDatabase } from '#db-init';
|
|
17
|
+
import { installSurreal, serverStatus, startServer, stopServer } from '#surreal-server';
|
|
18
|
+
|
|
19
|
+
export const inject = ['commands', 'userQuestions'];
|
|
20
|
+
|
|
21
|
+
function helpText() {
|
|
22
|
+
return `🗄️ 数据库管理 用法
|
|
23
|
+
|
|
24
|
+
/fancy-db status 查看服务与项目库状态
|
|
25
|
+
/fancy-db install 下载工作区专用 surreal 二进制
|
|
26
|
+
/fancy-db start 启动 SurrealDB 服务
|
|
27
|
+
/fancy-db stop 停止 SurrealDB 服务
|
|
28
|
+
/fancy-db init [all] 初始化工作区、注册表、素材库、全部项目库(schema + 校验函数)
|
|
29
|
+
/fancy-db init project <名字|UUID> 只初始化指定项目库
|
|
30
|
+
/fancy-db list 列出所有项目库
|
|
31
|
+
/fancy-db remove <名字|UUID> [--purge]
|
|
32
|
+
删除项目库(默认把项目目录移到 .fancy/trash)
|
|
33
|
+
/fancy-db help 显示本帮助`;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
function workspaceOf(session) {
|
|
37
|
+
return resolveWorkspace({ cwd: session?.header?.cwd, required: true });
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
function formatStatus(status) {
|
|
41
|
+
const lines = [
|
|
42
|
+
'🗄️ SurrealDB 状态',
|
|
43
|
+
'工作区:' + status.workspace,
|
|
44
|
+
'服务:' + (status.running ? '运行中' : '未运行'),
|
|
45
|
+
'端点:' + status.endpoint,
|
|
46
|
+
'PID:' + (status.pid || '—'),
|
|
47
|
+
'二进制:' + (status.binary || '未安装(运行 /fancy-db install)'),
|
|
48
|
+
'数据目录:' + status.data_dir,
|
|
49
|
+
];
|
|
50
|
+
try {
|
|
51
|
+
const workspace = readWorkspace(status.workspace);
|
|
52
|
+
lines.push('素材库:' + (workspace.surreal?.material_database || 'material_library'));
|
|
53
|
+
} catch { /* older/lightweight workspace */ }
|
|
54
|
+
if (status.health?.version) lines.push('版本:' + status.health.version);
|
|
55
|
+
if (status.running && status.credentials_ok === false) {
|
|
56
|
+
lines.push('⚠️ 服务正在运行,但当前工作区的 root 凭据无法通过认证;该端口可能被其他工作区的 SurrealDB 占用。');
|
|
57
|
+
lines.push(' 运行 /fancy-db start 可自动切换到空闲端口,或先停止占用该端口的进程。');
|
|
58
|
+
}
|
|
59
|
+
if (status.stale_pid) lines.push('⚠️ 检测到失效的 PID 文件:' + status.stale_pid);
|
|
60
|
+
if (status.health?.error) lines.push('健康检查:' + status.health.error);
|
|
61
|
+
return lines.join('\n');
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
function formatProjects(projects) {
|
|
65
|
+
if (!projects.length) return '当前工作区尚未创建任何项目。请运行 /fancy-topic setup 创建项目。';
|
|
66
|
+
const lines = ['📚 项目库', ''];
|
|
67
|
+
for (const p of projects) {
|
|
68
|
+
lines.push('- ' + p.name + ' `' + p.project_id + '`');
|
|
69
|
+
lines.push(' 库:' + p.db_name + ' 用户:' + p.db_user + ' 状态:' + p.status);
|
|
70
|
+
lines.push(' 目录:' + p.dir);
|
|
71
|
+
}
|
|
72
|
+
return lines.join('\n');
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
export async function apply(ctx) {
|
|
76
|
+
ctx.commands.register({
|
|
77
|
+
name: 'fancy-db',
|
|
78
|
+
description: '🗄️ 数据库管理 ( usage: /fancy-db status|install|start|stop|init|list|remove | help )',
|
|
79
|
+
input: { hint: '.' },
|
|
80
|
+
handler: async (invocation) => {
|
|
81
|
+
const session = invocation.agent.session;
|
|
82
|
+
const raw = String(invocation.rawInput || '').trim();
|
|
83
|
+
const tokens = raw.split(/\s+/).filter(Boolean);
|
|
84
|
+
const command = (tokens[0] || 'status').toLowerCase();
|
|
85
|
+
|
|
86
|
+
if (command === 'help' || command === '?') return notify(session, 'success', helpText());
|
|
87
|
+
|
|
88
|
+
let root;
|
|
89
|
+
try {
|
|
90
|
+
root = workspaceOf(session);
|
|
91
|
+
} catch (err) {
|
|
92
|
+
return notify(session, 'error', '❌ ' + (err && err.message ? err.message : String(err)));
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
try {
|
|
96
|
+
if (command === 'status') {
|
|
97
|
+
return notify(session, 'success', formatStatus(await serverStatus(root)));
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
if (command === 'install') {
|
|
101
|
+
const result = await installSurreal(root);
|
|
102
|
+
return notify(session, 'success', '✅ surreal 已就绪:' + result.path + '\n' + result.version);
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
if (command === 'start') {
|
|
106
|
+
const result = await startServer(root);
|
|
107
|
+
return notify(session, 'success', (result.started ? '✅ 已启动' : 'ℹ️ 已在运行') + '\n' + formatStatus(await serverStatus(root)));
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
if (command === 'stop') {
|
|
111
|
+
const result = await stopServer(root);
|
|
112
|
+
return notify(session, 'success', result.stopped ? '✅ 已停止 SurrealDB。' : 'ℹ️ SurrealDB 当前未运行,无需停止。');
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
if (command === 'init') {
|
|
116
|
+
const target = tokens[1] && tokens[1].toLowerCase();
|
|
117
|
+
if (target === 'project') {
|
|
118
|
+
const name = tokens.slice(2).join(' ').trim();
|
|
119
|
+
if (!name) return notify(session, 'error', '用法:/fancy-db init project <名字|UUID>');
|
|
120
|
+
const result = await initProjectDatabase(root, name);
|
|
121
|
+
return notify(session, 'success', '✅ 已初始化项目库:' + result.project.name + '\n库:' + result.project.database + '\nschema ' + result.schema_version + ' / validation ' + result.validation_version);
|
|
122
|
+
}
|
|
123
|
+
const result = await initDatabases(root, { all: true });
|
|
124
|
+
const failed = result.projects.filter((p) => !p.ok);
|
|
125
|
+
return notify(session, failed.length ? 'error' : 'success', [
|
|
126
|
+
'✅ 工作区数据库初始化完成',
|
|
127
|
+
'服务:' + (result.server.running ? '运行中' : '未运行'),
|
|
128
|
+
'schema:' + result.schema_version + ' validation:' + result.validation_version,
|
|
129
|
+
'素材库:' + (result.material?.database || 'material_library') + '(schema ' + (result.material?.schema_version || 6) + ')',
|
|
130
|
+
'项目库:' + result.projects.length + ' 个' + (failed.length ? ',失败 ' + failed.length + ' 个' : ''),
|
|
131
|
+
...failed.map((f) => '❌ ' + f.project.name + ':' + f.error),
|
|
132
|
+
].join('\n'));
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
if (command === 'list') {
|
|
136
|
+
return notify(session, 'success', formatProjects(await listProjects(root)));
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
if (command === 'remove') {
|
|
140
|
+
const target = tokens.slice(1).filter((t) => !t.startsWith('--')).join(' ').trim();
|
|
141
|
+
if (!target) return notify(session, 'error', '用法:/fancy-db remove <名字|UUID> [--purge]');
|
|
142
|
+
const project = await getProject(root, target);
|
|
143
|
+
if (!project) return notify(session, 'error', '未找到匹配的项目:' + target + '。可运行 /fancy-project list 查看当前工作区的项目。');
|
|
144
|
+
const asked = await askUserQuestion(ctx, invocation, [{
|
|
145
|
+
id: 'confirm_remove',
|
|
146
|
+
header: '删除项目库',
|
|
147
|
+
question: '确定删除《' + project.name + '》的数据库吗?\n项目:' + project.project_id + '\n库:' + project.db_name,
|
|
148
|
+
options: [
|
|
149
|
+
{ label: '取消', description: '不删除任何东西' },
|
|
150
|
+
{ label: '删除数据库', description: '删除数据库,项目目录移到 .fancy/trash' },
|
|
151
|
+
{ label: '删除数据库并清除目录', description: '不可恢复:同时删除项目目录' },
|
|
152
|
+
],
|
|
153
|
+
}]);
|
|
154
|
+
if (!asked.ok) return notify(session, 'error', asked.failure.message);
|
|
155
|
+
const selected = asked.answer?.answers?.[0]?.selected?.[0];
|
|
156
|
+
if (selected !== '删除数据库' && selected !== '删除数据库并清除目录') {
|
|
157
|
+
return notify(session, 'success', '操作已取消。');
|
|
158
|
+
}
|
|
159
|
+
const result = await deleteProject(root, project.project_id, {
|
|
160
|
+
confirm: true,
|
|
161
|
+
purge: selected === '删除数据库并清除目录',
|
|
162
|
+
});
|
|
163
|
+
return notify(session, 'success', '✅ 已删除《' + result.project.name + '》的数据库' + (result.purged ? '和项目目录' : ',项目目录已移入 .fancy/trash'));
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
return notify(session, 'error', '不支持子命令:' + command + '\n\n' + helpText());
|
|
167
|
+
} catch (err) {
|
|
168
|
+
return notify(session, 'error', '❌ ' + (err && err.message ? err.message : String(err)));
|
|
169
|
+
}
|
|
170
|
+
},
|
|
171
|
+
});
|
|
172
|
+
}
|
|
@@ -0,0 +1,134 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Deploy the Douyin browser scripts into the workspace.
|
|
3
|
+
*
|
|
4
|
+
* The plugin package lives under node_modules and its scripts must be runnable
|
|
5
|
+
* with a stable workspace-local path. Scripts are CommonJS and resolve
|
|
6
|
+
* playwright-core through NODE_PATH, which the plugin passes when spawning.
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
import fs from 'fs';
|
|
10
|
+
import path from 'path';
|
|
11
|
+
import { createRequire } from 'module';
|
|
12
|
+
import { fileURLToPath } from 'url';
|
|
13
|
+
|
|
14
|
+
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
|
15
|
+
const HERE = __dirname;
|
|
16
|
+
const requireHere = createRequire(import.meta.url);
|
|
17
|
+
|
|
18
|
+
export const DOUYIN_TOOL_DIR = path.join('.fancy-tools', 'fancy-douyin');
|
|
19
|
+
export const TOOL_FILES = ['lib.cjs', 'start-chrome.cjs', 'scan-account.cjs', 'crawl-note.cjs'];
|
|
20
|
+
|
|
21
|
+
export function playwrightNodePath() {
|
|
22
|
+
try {
|
|
23
|
+
const entry = requireHere.resolve('playwright-core');
|
|
24
|
+
return path.dirname(path.dirname(entry));
|
|
25
|
+
} catch {
|
|
26
|
+
return '';
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
function ensureRealDir(dir, label, problems) {
|
|
31
|
+
try {
|
|
32
|
+
const stat = fs.lstatSync(dir);
|
|
33
|
+
if (stat.isSymbolicLink()) {
|
|
34
|
+
problems.push(label + ' 不能是符号链接:' + dir);
|
|
35
|
+
return false;
|
|
36
|
+
}
|
|
37
|
+
if (!stat.isDirectory()) {
|
|
38
|
+
problems.push(label + ' 不是目录:' + dir);
|
|
39
|
+
return false;
|
|
40
|
+
}
|
|
41
|
+
return true;
|
|
42
|
+
} catch (err) {
|
|
43
|
+
if (!err || err.code !== 'ENOENT') {
|
|
44
|
+
problems.push(label + ' 无法检查:' + (err && err.message ? err.message : String(err)));
|
|
45
|
+
return false;
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
try {
|
|
49
|
+
fs.mkdirSync(dir);
|
|
50
|
+
return true;
|
|
51
|
+
} catch (err) {
|
|
52
|
+
problems.push('创建目录失败 ' + dir + ':' + (err && err.message ? err.message : String(err)));
|
|
53
|
+
return false;
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
function writeToolFile(file, content, problems) {
|
|
58
|
+
try {
|
|
59
|
+
try {
|
|
60
|
+
const stat = fs.lstatSync(file);
|
|
61
|
+
if (stat.isSymbolicLink() || !stat.isFile()) {
|
|
62
|
+
problems.push('拒绝覆盖非普通文件:' + file);
|
|
63
|
+
return false;
|
|
64
|
+
}
|
|
65
|
+
} catch (err) {
|
|
66
|
+
if (!err || err.code !== 'ENOENT') throw err;
|
|
67
|
+
}
|
|
68
|
+
const tmp = file + '.' + process.pid + '.' + Date.now() + '.tmp';
|
|
69
|
+
fs.writeFileSync(tmp, content, { encoding: 'utf8', mode: 0o644 });
|
|
70
|
+
fs.renameSync(tmp, file);
|
|
71
|
+
return true;
|
|
72
|
+
} catch (err) {
|
|
73
|
+
problems.push('写入失败 ' + file + ':' + (err && err.message ? err.message : String(err)));
|
|
74
|
+
return false;
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
/**
|
|
79
|
+
* @param {string} root workspace directory
|
|
80
|
+
* @returns {{ok: boolean, dir: string, nodePath: string, scripts: {start: string, scan: string, note: string}, problems: string[]}}
|
|
81
|
+
*/
|
|
82
|
+
export function deployDouyinTools(root) {
|
|
83
|
+
const problems = [];
|
|
84
|
+
const workspace = path.resolve(root);
|
|
85
|
+
const fancyTools = path.join(workspace, '.fancy-tools');
|
|
86
|
+
const target = path.join(workspace, DOUYIN_TOOL_DIR);
|
|
87
|
+
|
|
88
|
+
if (!ensureRealDir(fancyTools, '.fancy-tools', problems)
|
|
89
|
+
|| !ensureRealDir(target, '.fancy-tools/fancy-douyin', problems)) {
|
|
90
|
+
return { ok: false, dir: target, nodePath: '', scripts: { start: '', scan: '', note: '' }, problems };
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
for (const name of TOOL_FILES) {
|
|
94
|
+
let source;
|
|
95
|
+
try {
|
|
96
|
+
source = fs.readFileSync(path.join(HERE, 'scripts', name), 'utf8');
|
|
97
|
+
} catch (err) {
|
|
98
|
+
problems.push('读取脚本失败 ' + name + ':' + (err && err.message ? err.message : String(err)));
|
|
99
|
+
continue;
|
|
100
|
+
}
|
|
101
|
+
writeToolFile(path.join(target, name), source, problems);
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
writeToolFile(path.join(target, 'package.json'), JSON.stringify({
|
|
105
|
+
name: 'fancy-tools-fancy-douyin',
|
|
106
|
+
private: true,
|
|
107
|
+
type: 'commonjs',
|
|
108
|
+
}, null, 2) + '\n', problems);
|
|
109
|
+
|
|
110
|
+
const nodePath = playwrightNodePath();
|
|
111
|
+
if (!nodePath) {
|
|
112
|
+
problems.push('找不到 playwright-core。请确认 fancy-webnovel 依赖完整。');
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
writeToolFile(path.join(target, 'README.txt'), [
|
|
116
|
+
'本目录由 fancy-douyin 自动部署。',
|
|
117
|
+
'浏览器脚本:start-chrome.cjs / scan-account.cjs / crawl-note.cjs',
|
|
118
|
+
'登录态与采集记录在 <工作区>/.fancy/douyin/ 下。',
|
|
119
|
+
'不要手工修改本目录脚本;重新运行 /fancy-douyin 会刷新。',
|
|
120
|
+
'',
|
|
121
|
+
].join('\n'), problems);
|
|
122
|
+
|
|
123
|
+
return {
|
|
124
|
+
ok: problems.length === 0,
|
|
125
|
+
dir: target,
|
|
126
|
+
nodePath,
|
|
127
|
+
scripts: {
|
|
128
|
+
start: path.join(target, 'start-chrome.cjs'),
|
|
129
|
+
scan: path.join(target, 'scan-account.cjs'),
|
|
130
|
+
note: path.join(target, 'crawl-note.cjs'),
|
|
131
|
+
},
|
|
132
|
+
problems,
|
|
133
|
+
};
|
|
134
|
+
}
|