@waterwx/dsh-novel-forge 1.1.8 → 1.2.0-alpha
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/README.md +231 -349
- package/lib/client.js +54833 -9926
- package/lib/index.js +12137 -4734
- package/lib/types/action-guard.d.ts +61 -0
- package/lib/types/ai-scan.d.ts +36 -0
- package/lib/types/assets.d.ts +18 -3
- package/lib/types/assistant.d.ts +29 -0
- package/lib/types/author-assets.d.ts +25 -0
- package/lib/types/bookshelf.d.ts +11 -1
- package/lib/types/client/api.d.ts +153 -9
- package/lib/types/client/docx.d.ts +2 -0
- package/lib/types/client/index.d.ts +1 -1
- package/lib/types/client/llmLive.d.ts +33 -0
- package/lib/types/client/locales.d.ts +34 -9
- package/lib/types/client/panel/AdaptModeView.d.ts +5 -0
- package/lib/types/client/panel/AiTag.d.ts +1 -0
- package/lib/types/client/panel/AssistantTab.d.ts +4 -1
- package/lib/types/client/panel/AuthorAssetsView.d.ts +4 -0
- package/lib/types/client/panel/AuthorHome.d.ts +34 -0
- package/lib/types/client/panel/BookAnalysisView.d.ts +4 -0
- package/lib/types/client/panel/CmdPalette.d.ts +12 -0
- package/lib/types/client/panel/CreateBookView.d.ts +5 -1
- package/lib/types/client/panel/DirectorView.d.ts +7 -0
- package/lib/types/client/panel/GlobalAssetLibraryView.d.ts +4 -0
- package/lib/types/client/panel/IdeaInspirationView.d.ts +6 -0
- package/lib/types/client/panel/ImportModal.d.ts +7 -0
- package/lib/types/client/panel/KnowledgeBaseView.d.ts +4 -0
- package/lib/types/client/panel/LiveFeedLog.d.ts +8 -0
- package/lib/types/client/panel/MarketRadarView.d.ts +5 -0
- package/lib/types/client/panel/ModelManager.d.ts +14 -0
- package/lib/types/client/panel/NovelPanel.d.ts +6 -1
- package/lib/types/client/panel/PageHeader.d.ts +18 -0
- package/lib/types/client/panel/ProgressConsole.d.ts +17 -0
- package/lib/types/client/panel/ProgressHomeView.d.ts +5 -0
- package/lib/types/client/panel/ReasoningSection.d.ts +10 -0
- package/lib/types/client/panel/RunPanel.d.ts +5 -0
- package/lib/types/client/panel/SettingsView.d.ts +19 -0
- package/lib/types/client/panel/ShelfView.d.ts +12 -1
- package/lib/types/client/panel/SubPage.d.ts +27 -0
- package/lib/types/client/panel/toast.d.ts +2 -0
- package/lib/types/client/panel/views.d.ts +25 -7
- package/lib/types/client/run-status.d.ts +19 -0
- package/lib/types/client/text.d.ts +9 -0
- package/lib/types/docx-core.d.ts +16 -0
- package/lib/types/docx.d.ts +2 -3
- package/lib/types/engine.d.ts +152 -40
- package/lib/types/global-assets.d.ts +6 -0
- package/lib/types/index.d.ts +20 -2
- package/lib/types/llm-live.d.ts +44 -0
- package/lib/types/market-radar-scan.d.ts +19 -0
- package/lib/types/market-radar-sources.d.ts +31 -0
- package/lib/types/migrate-output.d.ts +25 -0
- package/lib/types/novel-context.d.ts +80 -0
- package/lib/types/prompt-utils.d.ts +35 -0
- package/lib/types/prompting.d.ts +10 -0
- package/lib/types/protocol.d.ts +1106 -123
- package/lib/types/review-policy.d.ts +97 -0
- package/lib/types/routes.d.ts +4 -0
- package/lib/types/run.d.ts +34 -0
- package/lib/types/stage-contract.d.ts +60 -0
- package/package.json +52 -27
- package/scripts/patch-schemastery.mjs +47 -0
- package/src/action-guard.ts +127 -0
- package/src/ai-scan.ts +179 -0
- package/src/assets.ts +1774 -520
- package/src/assistant.ts +1082 -770
- package/src/author-assets.ts +149 -0
- package/src/bookshelf.ts +187 -151
- package/src/client/api.ts +684 -387
- package/src/client/docx.ts +37 -69
- package/src/client/index.ts +3 -1
- package/src/client/llmLive.ts +101 -0
- package/src/client/locales.ts +418 -367
- package/src/client/mount.tsx +1 -1
- package/src/client/panel/AdaptModeView.tsx +535 -0
- package/src/client/panel/AiTag.tsx +7 -0
- package/src/client/panel/AssetsTab.tsx +448 -439
- package/src/client/panel/AssistantTab.tsx +355 -301
- package/src/client/panel/AuthorAssetsView.tsx +206 -0
- package/src/client/panel/AuthorHome.tsx +309 -0
- package/src/client/panel/BookAnalysisView.tsx +63 -0
- package/src/client/panel/CmdPalette.tsx +102 -0
- package/src/client/panel/CreateBookView.tsx +366 -348
- package/src/client/panel/DirectorView.tsx +200 -0
- package/src/client/panel/GlobalAssetLibraryView.tsx +168 -0
- package/src/client/panel/IdeaInspirationView.tsx +76 -0
- package/src/client/panel/ImportModal.tsx +303 -0
- package/src/client/panel/KnowledgeBaseView.tsx +125 -0
- package/src/client/panel/LiveFeedLog.tsx +67 -0
- package/src/client/panel/MarketRadarView.tsx +377 -0
- package/src/client/panel/ModelManager.tsx +205 -0
- package/src/client/panel/NovelPanel.tsx +5403 -4294
- package/src/client/panel/PageHeader.tsx +37 -0
- package/src/client/panel/ProgressConsole.tsx +63 -0
- package/src/client/panel/ReaderView.tsx +306 -306
- package/src/client/panel/ReasoningSection.tsx +31 -0
- package/src/client/panel/RunPanel.tsx +223 -0
- package/src/client/panel/SettingsView.tsx +305 -0
- package/src/client/panel/ShelfView.tsx +266 -235
- package/src/client/panel/SubPage.tsx +42 -0
- package/src/client/panel/WorldTab.tsx +202 -202
- package/src/client/panel/panel.module.css +5764 -2817
- package/src/client/panel/toast.ts +51 -0
- package/src/client/panel/views.tsx +401 -314
- package/src/client/run-status.ts +143 -0
- package/src/client/sidebar-entry.ts +1 -1
- package/src/client/text.ts +28 -0
- package/src/docx-core.ts +53 -0
- package/src/docx.ts +44 -83
- package/src/engine.ts +4231 -2746
- package/src/global-assets.ts +59 -0
- package/src/index.ts +311 -246
- package/src/llm-live.ts +68 -0
- package/src/market-radar-scan.ts +41 -0
- package/src/market-radar-sources.ts +173 -0
- package/src/migrate-output.ts +76 -0
- package/src/novel-context.ts +286 -0
- package/src/prompting.ts +26 -0
- package/src/protocol.ts +2072 -1104
- package/src/review-policy.ts +161 -0
- package/src/routes.ts +3405 -2050
- package/src/run.ts +342 -0
- package/src/stage-contract.ts +189 -0
- package/lib/client.js.map +0 -1
- package/lib/index.js.map +0 -1
- package/src/client/panel/BookshelfBar.tsx +0 -170
package/src/index.ts
CHANGED
|
@@ -1,246 +1,311 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* dsh-novel-forge — host half. Mounts the AI novel-forge workbench: docx
|
|
3
|
-
* outline import, LLM chapter planning, chapter-by-chapter generation
|
|
4
|
-
* (3000-4000 chars each), Markdown output into your chosen folder, and the
|
|
5
|
-
* /api/dsh-novel-forge route family. The browser half (./client) renders the
|
|
6
|
-
* workbench panel. Everything rides official NPM SDK packages — no dsh source
|
|
7
|
-
* changes.
|
|
8
|
-
*/
|
|
9
|
-
|
|
10
|
-
import
|
|
11
|
-
import {
|
|
12
|
-
import
|
|
13
|
-
import type {} from '@deepseek-ai/dsh-
|
|
14
|
-
import
|
|
15
|
-
import type {} from '@deepseek-ai/dsh-
|
|
16
|
-
import type {
|
|
17
|
-
import {
|
|
18
|
-
import {
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
/**
|
|
24
|
-
export const
|
|
25
|
-
|
|
26
|
-
/**
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
export
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
/**
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
const
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
}
|
|
1
|
+
/**
|
|
2
|
+
* dsh-novel-forge — host half. Mounts the AI novel-forge workbench: docx
|
|
3
|
+
* outline import, LLM chapter planning, chapter-by-chapter generation
|
|
4
|
+
* (3000-4000 chars each), Markdown output into your chosen folder, and the
|
|
5
|
+
* /api/dsh-novel-forge route family. The browser half (./client) renders the
|
|
6
|
+
* workbench panel. Everything rides official NPM SDK packages — no dsh source
|
|
7
|
+
* changes.
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
import { homedir } from 'node:os'
|
|
11
|
+
import { join } from 'node:path'
|
|
12
|
+
import type { Context } from '@deepseek-ai/cordis'
|
|
13
|
+
import type {} from '@deepseek-ai/dsh-settings'
|
|
14
|
+
import z from 'schemastery'
|
|
15
|
+
import type {} from '@deepseek-ai/dsh-host-webserver'
|
|
16
|
+
import type {} from '@deepseek-ai/dsh-llm'
|
|
17
|
+
import type {} from '@deepseek-ai/dsh-system-prompt'
|
|
18
|
+
import type { ConfigPatch, NovelConfig } from './protocol.ts'
|
|
19
|
+
import { makeRoutes } from './routes.ts'
|
|
20
|
+
import { activeBookOutputDir } from './bookshelf.ts'
|
|
21
|
+
import { loadProject } from './engine.ts'
|
|
22
|
+
|
|
23
|
+
/** Stable cordis plugin name. */
|
|
24
|
+
export const name = 'novel-forge'
|
|
25
|
+
|
|
26
|
+
/** Services required before the novel-forge surfaces can mount. */
|
|
27
|
+
export const inject = ['webServer', 'llm', 'systemPrompt', 'settings']
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* Settings namespace of the novel-forge capability — the section the web
|
|
31
|
+
* settings surface edits. Spelled here rather than imported: the browser half
|
|
32
|
+
* spells the same value and must not depend on a Host package.
|
|
33
|
+
*/
|
|
34
|
+
export const NOVEL_SETTINGS_NAMESPACE = 'dsh-novel-forge' as const
|
|
35
|
+
|
|
36
|
+
/** Plugin config, validated by the same-named schemastery schema. */
|
|
37
|
+
export interface Config {
|
|
38
|
+
/** When true (default), a system-prompt section announces the plugin to every agent. */
|
|
39
|
+
announceToAgent?: boolean
|
|
40
|
+
/** Master switch for the plugin (routes, prompt section). */
|
|
41
|
+
enabled?: boolean
|
|
42
|
+
/** Absolute path of the default docx outline to load. */
|
|
43
|
+
outlinePath?: string
|
|
44
|
+
/** Absolute output directory for chapters + project state. */
|
|
45
|
+
outputDir?: string
|
|
46
|
+
/** LLM provider route. */
|
|
47
|
+
provider?: string
|
|
48
|
+
/** LLM model id. */
|
|
49
|
+
model?: string
|
|
50
|
+
/** 任务级模型路由:正文生成模型(留空则跟随 model)。 */
|
|
51
|
+
generateModel?: string
|
|
52
|
+
/** 任务级模型路由:审稿模型(留空则跟随 model)。 */
|
|
53
|
+
reviewModel?: string
|
|
54
|
+
/** 任务级模型路由:质检模型(留空则跟随 model)。 */
|
|
55
|
+
auditModel?: string
|
|
56
|
+
/** LLM reasoning effort (off/low/high/max). */
|
|
57
|
+
reasoningEffort?: 'off' | 'low' | 'high' | 'max'
|
|
58
|
+
/** 分析类任务(提炼/拆书/反推大纲等)的推理档位;默认 low。 */
|
|
59
|
+
analysisReasoning?: 'off' | 'low' | 'high' | 'max'
|
|
60
|
+
/** Target characters per chapter. */
|
|
61
|
+
chapterChars?: number
|
|
62
|
+
/** Max output tokens per chapter call. */
|
|
63
|
+
maxTokens?: number
|
|
64
|
+
/** Review pass threshold (0-100). */
|
|
65
|
+
reviewPassScore?: number
|
|
66
|
+
/** Whether generation auto-runs review after writing. */
|
|
67
|
+
autoReview?: boolean
|
|
68
|
+
/** Whether generation auto-runs the author review (hook/continuity/trend). */
|
|
69
|
+
autoAuthorReview?: boolean
|
|
70
|
+
/** 修订/润色产出草稿后自动附带一次 AI 审查(默认开,可在设置页关闭省 token)。 */
|
|
71
|
+
autoReviewAfterRevise?: boolean
|
|
72
|
+
/** 自定义背景图(URL / dataURL / 服务端路径引用)。 */
|
|
73
|
+
themeBackground?: string
|
|
74
|
+
/** 自定义背景遮罩/模糊强度 0-80。 */
|
|
75
|
+
themeBackgroundBlur?: number
|
|
76
|
+
/** 玻璃透明度 0-100(100=当前原样)。 */
|
|
77
|
+
themeOpacity?: number
|
|
78
|
+
/** 是否启用改编模式(默认关闭)。 */
|
|
79
|
+
enableAdaptMode?: boolean
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
export const Config: z<Config> = z.object({
|
|
83
|
+
announceToAgent: z.boolean().default(true),
|
|
84
|
+
enabled: z.boolean().default(true),
|
|
85
|
+
outlinePath: z.string().default(''),
|
|
86
|
+
outputDir: z.string().default(join(homedir(), '.dsh', 'novels')),
|
|
87
|
+
provider: z.string().default('deepseek-official'),
|
|
88
|
+
model: z.string().default('deepseek-flash'),
|
|
89
|
+
generateModel: z.string().default(''),
|
|
90
|
+
reviewModel: z.string().default(''),
|
|
91
|
+
auditModel: z.string().default(''),
|
|
92
|
+
reasoningEffort: z.union(['off', 'low', 'high', 'max']).default('off'),
|
|
93
|
+
analysisReasoning: z.union(['off', 'low', 'high', 'max']).default('low'),
|
|
94
|
+
chapterChars: z.number().default(3500),
|
|
95
|
+
maxTokens: z.number().default(12000),
|
|
96
|
+
reviewPassScore: z.number().default(70),
|
|
97
|
+
autoReview: z.boolean().default(true),
|
|
98
|
+
autoAuthorReview: z.boolean().default(true),
|
|
99
|
+
autoReviewAfterRevise: z.boolean().default(true),
|
|
100
|
+
themeBackground: z.string().default(''),
|
|
101
|
+
themeBackgroundBlur: z.number().default(0),
|
|
102
|
+
themeOpacity: z.number().default(100),
|
|
103
|
+
enableAdaptMode: z.boolean().default(true),
|
|
104
|
+
})
|
|
105
|
+
|
|
106
|
+
/** Schema defaults, re-read for hand-built test contexts. */
|
|
107
|
+
const DEFAULT_ANNOUNCE = true
|
|
108
|
+
const DEFAULT_OUTLINE_PATH = ''
|
|
109
|
+
const DEFAULT_OUTPUT_DIR = join(homedir(), '.dsh', 'novels')
|
|
110
|
+
const DEFAULT_PROVIDER = 'deepseek-official'
|
|
111
|
+
const DEFAULT_MODEL = 'deepseek-flash'
|
|
112
|
+
const DEFAULT_REASONING_EFFORT = 'off' as const
|
|
113
|
+
const DEFAULT_ANALYSIS_REASONING = 'low' as const
|
|
114
|
+
const DEFAULT_CHAPTER_CHARS = 3500
|
|
115
|
+
const DEFAULT_MAX_TOKENS = 12000
|
|
116
|
+
const DEFAULT_REVIEW_PASS_SCORE = 70
|
|
117
|
+
const DEFAULT_AUTO_REVIEW = true
|
|
118
|
+
const DEFAULT_AUTO_AUTHOR_REVIEW = true
|
|
119
|
+
const DEFAULT_AUTO_REVIEW_AFTER_REVISE = true
|
|
120
|
+
|
|
121
|
+
/** Order of the announcement section within the tool-guidance band. */
|
|
122
|
+
const SECTION_ORDER = 160
|
|
123
|
+
|
|
124
|
+
/** Model-facing announcement: plugin presence, capabilities, and limits. */
|
|
125
|
+
export const NOVEL_GUIDANCE = '本机已安装 dsh-novel-forge 插件(AI 编译小说工作台):侧边栏「小说工坊」入口。能力:读取 docx 大纲或粘贴大纲文本;用 LLM 提炼道藏(人设/世界观/金手指规则/写作红线);生成卷计划与章节计划;逐章调用 LLM 生成 3000-4000 字正文并保存为 Markdown(默认输出到用户主目录 ~/.dsh/novels);每章自动生成摘要(叙事记忆)、自动 AI 审稿(人设/设定/红线/文笔/爽点/逻辑),支持按审稿意见重写、去 AI 味润色、暗线(伏笔)管理、批量连写与全本导出(txt/md)。限制:生成消耗 LLM API 额度;输出目录与模型可在插件设置中修改;章节正文质量取决于大纲完整度。用户提到「小说 / 大纲 / 写小说 / 章节 / 审稿 / 润色」时即指本插件,请据此协作。'
|
|
126
|
+
|
|
127
|
+
/** Resolve a config-like value into the full runtime config. */
|
|
128
|
+
export function resolveConfig(value: Partial<Config> | undefined): NovelConfig {
|
|
129
|
+
return {
|
|
130
|
+
outlinePath: value?.outlinePath ?? DEFAULT_OUTLINE_PATH,
|
|
131
|
+
outputDir: value?.outputDir ?? DEFAULT_OUTPUT_DIR,
|
|
132
|
+
provider: value?.provider ?? DEFAULT_PROVIDER,
|
|
133
|
+
model: value?.model ?? DEFAULT_MODEL,
|
|
134
|
+
generateModel: value?.generateModel,
|
|
135
|
+
reviewModel: value?.reviewModel,
|
|
136
|
+
auditModel: value?.auditModel,
|
|
137
|
+
reasoningEffort: value?.reasoningEffort ?? DEFAULT_REASONING_EFFORT,
|
|
138
|
+
analysisReasoning: value?.analysisReasoning ?? DEFAULT_ANALYSIS_REASONING,
|
|
139
|
+
chapterChars: value?.chapterChars ?? DEFAULT_CHAPTER_CHARS,
|
|
140
|
+
maxTokens: value?.maxTokens ?? DEFAULT_MAX_TOKENS,
|
|
141
|
+
reviewPassScore: value?.reviewPassScore ?? DEFAULT_REVIEW_PASS_SCORE,
|
|
142
|
+
autoReview: value?.autoReview ?? DEFAULT_AUTO_REVIEW,
|
|
143
|
+
autoAuthorReview: value?.autoAuthorReview ?? DEFAULT_AUTO_AUTHOR_REVIEW,
|
|
144
|
+
autoReviewAfterRevise: value?.autoReviewAfterRevise ?? DEFAULT_AUTO_REVIEW_AFTER_REVISE,
|
|
145
|
+
themeBackground: value?.themeBackground ?? '',
|
|
146
|
+
themeBackgroundBlur: value?.themeBackgroundBlur ?? 0,
|
|
147
|
+
themeOpacity: value?.themeOpacity ?? 100,
|
|
148
|
+
enableAdaptMode: value?.enableAdaptMode ?? true,
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
/**
|
|
153
|
+
* Mount the routes and announcement.
|
|
154
|
+
* @param ctx - host plugin context carrying webServer/llm/systemPrompt.
|
|
155
|
+
* @param config - resolved plugin config (schema defaults applied by the loader).
|
|
156
|
+
*/
|
|
157
|
+
export function apply(ctx: Context, config?: Config): void {
|
|
158
|
+
// The live source the routes read: the settings section once the web
|
|
159
|
+
// settings surface is served, the composition entry otherwise.
|
|
160
|
+
let current: () => Config = () => config ?? {}
|
|
161
|
+
const resolve = (): NovelConfig => {
|
|
162
|
+
const resolved = resolveConfig(current())
|
|
163
|
+
// 书架激活的书优先决定输出目录(settings 仍可改默认值)。
|
|
164
|
+
const shelfDir = activeBookOutputDir()
|
|
165
|
+
if (shelfDir === undefined) return resolved
|
|
166
|
+
// 本书参数覆盖(novel-project.json.bookSettings;未设字段回退全局)。
|
|
167
|
+
const bs = loadProject(shelfDir)?.bookSettings
|
|
168
|
+
const book: NovelConfig = { ...resolved, outputDir: shelfDir }
|
|
169
|
+
if (bs === undefined) return book
|
|
170
|
+
if (bs.provider !== undefined) book.provider = bs.provider
|
|
171
|
+
if (bs.model !== undefined) book.model = bs.model
|
|
172
|
+
if (bs.reasoningEffort !== undefined) book.reasoningEffort = bs.reasoningEffort
|
|
173
|
+
if (bs.analysisReasoning !== undefined) book.analysisReasoning = bs.analysisReasoning
|
|
174
|
+
if (bs.chapterChars !== undefined) book.chapterChars = bs.chapterChars
|
|
175
|
+
if (bs.maxTokens !== undefined) book.maxTokens = bs.maxTokens
|
|
176
|
+
if (bs.reviewPassScore !== undefined) book.reviewPassScore = bs.reviewPassScore
|
|
177
|
+
if (bs.autoReview !== undefined) book.autoReview = bs.autoReview
|
|
178
|
+
if (bs.autoAuthorReview !== undefined) book.autoAuthorReview = bs.autoAuthorReview
|
|
179
|
+
if (bs.autoReviewAfterRevise !== undefined) book.autoReviewAfterRevise = bs.autoReviewAfterRevise
|
|
180
|
+
return book
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
const patchConfig = async (patch: ConfigPatch): Promise<NovelConfig> => {
|
|
184
|
+
const next: ConfigPatch = {}
|
|
185
|
+
if (patch.outlinePath !== undefined) next.outlinePath = patch.outlinePath
|
|
186
|
+
if (patch.outputDir !== undefined) next.outputDir = patch.outputDir
|
|
187
|
+
if (patch.provider !== undefined) next.provider = patch.provider
|
|
188
|
+
if (patch.model !== undefined) next.model = patch.model
|
|
189
|
+
if (patch.generateModel !== undefined) next.generateModel = patch.generateModel
|
|
190
|
+
if (patch.reviewModel !== undefined) next.reviewModel = patch.reviewModel
|
|
191
|
+
if (patch.auditModel !== undefined) next.auditModel = patch.auditModel
|
|
192
|
+
if (patch.reasoningEffort !== undefined) next.reasoningEffort = patch.reasoningEffort
|
|
193
|
+
if (patch.analysisReasoning !== undefined) next.analysisReasoning = patch.analysisReasoning
|
|
194
|
+
if (patch.chapterChars !== undefined) next.chapterChars = patch.chapterChars
|
|
195
|
+
if (patch.maxTokens !== undefined) next.maxTokens = patch.maxTokens
|
|
196
|
+
if (patch.reviewPassScore !== undefined) next.reviewPassScore = patch.reviewPassScore
|
|
197
|
+
if (patch.autoReview !== undefined) next.autoReview = patch.autoReview
|
|
198
|
+
if (patch.autoAuthorReview !== undefined) next.autoAuthorReview = patch.autoAuthorReview
|
|
199
|
+
if (patch.autoReviewAfterRevise !== undefined) next.autoReviewAfterRevise = patch.autoReviewAfterRevise
|
|
200
|
+
if (patch.themeBackground !== undefined) next.themeBackground = patch.themeBackground
|
|
201
|
+
if (patch.themeBackgroundBlur !== undefined) next.themeBackgroundBlur = patch.themeBackgroundBlur
|
|
202
|
+
if (patch.themeOpacity !== undefined) next.themeOpacity = patch.themeOpacity
|
|
203
|
+
if (patch.enableAdaptMode !== undefined) next.enableAdaptMode = patch.enableAdaptMode
|
|
204
|
+
// Persist through the settings seam when available; otherwise keep in memory.
|
|
205
|
+
// (ctx.get is the non-strict service access — no inject requirement, same
|
|
206
|
+
// pattern installSettingsSection itself uses.)
|
|
207
|
+
const settings = ctx.get('settings')
|
|
208
|
+
if (settings !== undefined) {
|
|
209
|
+
await settings.update(NOVEL_SETTINGS_NAMESPACE, next as Record<string, unknown>)
|
|
210
|
+
} else {
|
|
211
|
+
current = () => ({ ...current(), ...next })
|
|
212
|
+
}
|
|
213
|
+
return resolve()
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
let disposeSection: (() => void) | undefined
|
|
217
|
+
let disposeRoutes: (() => void) | undefined
|
|
218
|
+
|
|
219
|
+
const sync = (): void => {
|
|
220
|
+
if (disposeSection !== undefined) {
|
|
221
|
+
disposeSection()
|
|
222
|
+
disposeSection = undefined
|
|
223
|
+
}
|
|
224
|
+
if (disposeRoutes !== undefined) {
|
|
225
|
+
disposeRoutes()
|
|
226
|
+
disposeRoutes = undefined
|
|
227
|
+
}
|
|
228
|
+
const value = resolve()
|
|
229
|
+
if (!(current().enabled ?? true)) return
|
|
230
|
+
if (current().announceToAgent ?? DEFAULT_ANNOUNCE) {
|
|
231
|
+
disposeSection = ctx.systemPrompt.section({
|
|
232
|
+
name: 'plugin:dsh-novel-forge',
|
|
233
|
+
order: SECTION_ORDER,
|
|
234
|
+
text: NOVEL_GUIDANCE,
|
|
235
|
+
})
|
|
236
|
+
}
|
|
237
|
+
const routes = makeRoutes({ ctx, getConfig: resolve, patchConfig, rawConfig: () => current() })
|
|
238
|
+
disposeRoutes = ctx.effect(
|
|
239
|
+
() => {
|
|
240
|
+
const disposers = routes.map(route => ctx.webServer.register(route))
|
|
241
|
+
return () => { for (const dispose of disposers) dispose() }
|
|
242
|
+
},
|
|
243
|
+
'dsh-novel-forge: routes',
|
|
244
|
+
)
|
|
245
|
+
void value
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
ctx.settings.installSection(ctx, NOVEL_SETTINGS_NAMESPACE, Config, config ?? {}, {
|
|
249
|
+
setSource: (source) => {
|
|
250
|
+
current = source
|
|
251
|
+
sync()
|
|
252
|
+
},
|
|
253
|
+
onChange: sync,
|
|
254
|
+
})
|
|
255
|
+
|
|
256
|
+
// 运行时 skill:章节批量生产与值守处理(只在本插件环境可用)。
|
|
257
|
+
// 通过 ctx.get('skills') 获取注册表(服务存在时注册,缺失则跳过,不影响其他能力)。
|
|
258
|
+
const skillsService = ctx.get('skills') as { register?: (skill: { name: string; provider: string; description: string; content: string }) => () => void } | undefined
|
|
259
|
+
// [disabled 2026-09-05] 运行时 skill 注册按用户要求暂时关闭(排查 .length 报错)。
|
|
260
|
+
const enableRuntimeSkill: boolean = false
|
|
261
|
+
if (enableRuntimeSkill && skillsService !== undefined && skillsService.register !== undefined) {
|
|
262
|
+
const disposeSkill = skillsService.register({
|
|
263
|
+
name: 'novel-forge-chapter-batch',
|
|
264
|
+
provider: 'dsh-novel-forge',
|
|
265
|
+
description: '批量生成小说章节并值守处理审稿未过的章节(豁免/按意见修订/验证模式/重新生成),依赖 dsh-novel-forge 的 /api/dsh-novel-forge/* 路由。',
|
|
266
|
+
content: [
|
|
267
|
+
'# 小说章节批量生产与值守处理',
|
|
268
|
+
'',
|
|
269
|
+
'## 0. 值守纪律(最高优先级,违反即空转)',
|
|
270
|
+
'1. 启动后台任务后立即停下,不轮询、不连续 wait、不做无意义动作——后台任务完成时运行时自动通知。',
|
|
271
|
+
'2. 只有收到「任务完成」通知或用户新消息时,才继续下一步(读结果→处理未过→汇报)。',
|
|
272
|
+
'3. 等待期间可做与当前任务无关的其他有用工作,但不得为等任务而空转。',
|
|
273
|
+
'4. 任务因 web 重启中断时:待 3080 恢复后,复位卡死章节(generating/error → reset)再续跑,并向用户说明中断原因。',
|
|
274
|
+
'',
|
|
275
|
+
'## 1. 前置检查',
|
|
276
|
+
'1. `GET /api/dsh-novel-forge/status` 确认服务在跑,读项目章节数。',
|
|
277
|
+
'2. 列出目标区间 pending 章节号,避免重复生成已处理章节。',
|
|
278
|
+
'3. **串行执行,禁止并行写 project.json**(并发会互相覆盖状态,实测教训)。',
|
|
279
|
+
'',
|
|
280
|
+
'## 2. 批量生成',
|
|
281
|
+
'逐章串行调用 `POST /generate` `{ chapterNo, skipReview: false }`(走完整质量门:生成→摘要+编年录→审稿→复盘)。',
|
|
282
|
+
'响应为 NDJSON,找 review 帧取 score/passed。每章 2-5 分钟;失败重试 1 次;后台跑并告知预计时长。',
|
|
283
|
+
'用 Node 脚本(fetch 逐章循环),避免 PowerShell 转义坑,用完删除。',
|
|
284
|
+
'',
|
|
285
|
+
'## 3. 未过章节分级处理',
|
|
286
|
+
'### A. 无 high → 豁免通过:`POST /chapter/approve`(主观项不磨)',
|
|
287
|
+
'### B. 有 high 且明确 → 按意见修订 + 验证模式:',
|
|
288
|
+
'1. 拼指令 `按审稿意见修订(优先处理):\n[severity] item → suggestion`(high 优先,无 high 取前 3 medium)',
|
|
289
|
+
'2. `POST /rewrite` 产草稿;3. `POST /chapter/check` 带 `previousReport` 走验证模式(只核对原意见解决+只挑新增 high)',
|
|
290
|
+
'4. 判定 `passed || 无 high` 可接受;5. `POST /draft/apply` 带 passed 报告落盘 approved',
|
|
291
|
+
'6. 不可接受 → 第二轮修订(指令更精确)→ 再验证;每章最多修 2 轮,仍不过保留草稿待人工',
|
|
292
|
+
'### C. 结构性 high(修订改不好)→ `POST /draft/discard` 后 `POST /generate` 重写,剩主观项再走 A',
|
|
293
|
+
'### D. error 状态 → `POST /chapter/reset` 后 `POST /generate`',
|
|
294
|
+
'注意:approved 但 review 有 high 的保留不动;修订指令越精确越有效(具体数字/行为一次就过)。',
|
|
295
|
+
'',
|
|
296
|
+
'## 4. 收尾',
|
|
297
|
+
'`GET /status` 验证目标区间全 approved;可选刷新剧情线(`POST /plotlines` op=refresh,注意只读最近 8 章摘要);汇报通过/未过/失败与遗留项。',
|
|
298
|
+
'',
|
|
299
|
+
'## 5. 已知陷阱',
|
|
300
|
+
'- 并发写 project.json 互相覆盖 → 必须串行',
|
|
301
|
+
'- rewrite 草稿可能偶发不落盘 → 应用前检查 pendingDraft,不存在则重跑',
|
|
302
|
+
'- PowerShell 调 API 引号/中文转义是坑 → 用 Node 脚本',
|
|
303
|
+
'- 验证模式 500 偶发 → 重试一次',
|
|
304
|
+
].join('\n'),
|
|
305
|
+
})
|
|
306
|
+
ctx.effect(() => disposeSkill, 'dsh-novel-forge: skill')
|
|
307
|
+
}
|
|
308
|
+
|
|
309
|
+
// Initial registration from the composition entry.
|
|
310
|
+
sync()
|
|
311
|
+
}
|