@waterwx/dsh-novel-forge 2.0.1-alpha → 2.1.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/README.md +368 -362
- package/lib/client.js +20340 -19622
- package/lib/index.js +11313 -7285
- package/lib/types/action-guard.d.ts +61 -0
- package/lib/types/ai-scan.d.ts +0 -6
- package/lib/types/assets.d.ts +20 -9
- package/lib/types/assistant.d.ts +29 -0
- package/lib/types/author-assets.d.ts +1 -3
- package/lib/types/bookshelf.d.ts +13 -3
- package/lib/types/client/api.d.ts +136 -27
- package/lib/types/client/chip-prefs.d.ts +36 -0
- package/lib/types/client/docx.d.ts +2 -2
- package/lib/types/client/llmLive.d.ts +31 -0
- package/lib/types/client/locales.d.ts +15 -70
- package/lib/types/client/mount.d.ts +0 -2
- 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/AuthorHome.d.ts +13 -11
- 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/IdeaInspirationView.d.ts +6 -0
- package/lib/types/client/panel/KnowledgeBaseView.d.ts +4 -0
- package/lib/types/client/panel/LiveFeedLog.d.ts +13 -0
- package/lib/types/client/panel/MarketRadarView.d.ts +5 -0
- package/lib/types/client/panel/ModelManager.d.ts +2 -2
- 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/PromptSlotsView.d.ts +11 -0
- package/lib/types/client/panel/SettingsView.d.ts +12 -9
- package/lib/types/client/panel/ShelfView.d.ts +10 -1
- package/lib/types/client/panel/SubPage.d.ts +27 -0
- package/lib/types/client/panel/TensionView.d.ts +18 -0
- package/lib/types/client/panel/TimelineView.d.ts +18 -0
- package/lib/types/client/panel/helpers.d.ts +5 -3
- package/lib/types/client/panel/market-radar-state.d.ts +56 -0
- package/lib/types/client/panel/outline-markdown.d.ts +112 -0
- package/lib/types/client/panel/status-badge.d.ts +13 -0
- package/lib/types/client/panel/storage-parse.d.ts +46 -0
- package/lib/types/client/panel/toast.d.ts +2 -0
- package/lib/types/client/panel/usePersistentState.d.ts +17 -0
- package/lib/types/client/panel/views.d.ts +21 -35
- package/lib/types/client/run-status.d.ts +21 -0
- package/lib/types/client/sidebar-entry.d.ts +0 -2
- package/lib/types/digest-parse.d.ts +29 -0
- package/lib/types/docx-core.d.ts +16 -0
- package/lib/types/docx.d.ts +2 -3
- package/lib/types/engine.d.ts +150 -103
- package/lib/types/global-assets.d.ts +6 -0
- package/lib/types/home.d.ts +11 -0
- package/lib/types/index.d.ts +26 -12
- package/lib/types/llm-live.d.ts +70 -0
- package/lib/types/llm-retry.d.ts +38 -0
- package/lib/types/market-radar-scan.d.ts +18 -0
- package/lib/types/market-radar-sources.d.ts +31 -0
- package/lib/types/migrate-output.d.ts +25 -0
- package/lib/types/model-capability.d.ts +119 -0
- package/lib/types/model-request.d.ts +36 -0
- package/lib/types/output-budget.d.ts +86 -0
- package/lib/types/plotline-align.d.ts +35 -0
- package/lib/types/prompt-slots.d.ts +57 -0
- package/lib/types/prompting.d.ts +10 -0
- package/lib/types/protocol.d.ts +841 -601
- package/lib/types/revision.d.ts +76 -0
- package/lib/types/routes.d.ts +9 -3
- package/lib/types/run-selection.d.ts +51 -0
- package/lib/types/run.d.ts +32 -1
- package/lib/types/settings-persist.d.ts +59 -0
- package/lib/types/snapshots.d.ts +46 -0
- package/lib/types/stage-contract.d.ts +58 -0
- package/lib/types/stream-fallback.d.ts +60 -0
- package/lib/types/strip-headings.d.ts +33 -0
- package/lib/types/tension.d.ts +53 -0
- package/lib/types/timeline.d.ts +37 -0
- package/package.json +50 -27
- package/assets/styles/arcane-thick-3d.webp +0 -0
- package/assets/styles/chinese-ink.webp +0 -0
- package/assets/styles/clay-stopmotion-3d.webp +0 -0
- package/assets/styles/dark-ghibli.webp +0 -0
- package/assets/styles/dark-gothic-3d.webp +0 -0
- package/assets/styles/film-photo.webp +0 -0
- package/assets/styles/guofeng-heavy.webp +0 -0
- package/assets/styles/guoman-2d-gufeng.webp +0 -0
- package/assets/styles/guoman-3d-tianyuan.webp +0 -0
- package/assets/styles/guoman-3d-xuanhuan.webp +0 -0
- package/assets/styles/horror-game-3d.webp +0 -0
- package/assets/styles/hyperreal-3d.webp +0 -0
- package/assets/styles/japan-cel-dark.webp +0 -0
- package/assets/styles/korean-webtoon.webp +0 -0
- package/assets/styles/low-cyberpunk-3d.webp +0 -0
- package/assets/styles/nextgen-cg.webp +0 -0
- package/assets/styles/noir-film.webp +0 -0
- package/assets/styles/openworld-survival.webp +0 -0
- package/assets/styles/paper-cut-3d.webp +0 -0
- package/assets/styles/pixar-adult-3d.webp +0 -0
- package/assets/styles/pop-marvel-2d.webp +0 -0
- package/assets/styles/sandiao-qban.webp +0 -0
- package/assets/styles/shinkai-anime.webp +0 -0
- package/assets/styles/tactical-stealth.webp +0 -0
- package/assets/styles/urban-lightreal-3d.webp +0 -0
- package/assets/styles/vaporwave-3d.webp +0 -0
- package/assets/styles/vhs90.webp +0 -0
- package/lib/types/client/panel/BookshelfBar.d.ts +0 -11
- package/lib/types/client/panel/FlowGuide.d.ts +0 -22
- package/lib/types/client/panel/MangaRoleLibrary.d.ts +0 -20
- package/lib/types/client/panel/MangaWorkspace.d.ts +0 -12
- package/lib/types/client/panel/ModelPicker.d.ts +0 -11
- package/lib/types/client/panel/ProgressHomeView.d.ts +0 -5
- package/lib/types/client/panel/PropLibrary.d.ts +0 -8
- package/lib/types/client/panel/SceneLibrary.d.ts +0 -16
- package/lib/types/client/panel/StoryboardTab.d.ts +0 -19
- package/lib/types/client/panel/StyleCard.d.ts +0 -8
- package/lib/types/client/panel/StyleGallery.d.ts +0 -1
- package/lib/types/comic-presets.d.ts +0 -12
- package/lib/types/manga-genre-rules.d.ts +0 -22
- package/lib/types/prompt-utils.d.ts +0 -35
- package/lib/types/review-policy.d.ts +0 -97
- package/lib/types/shot-language.d.ts +0 -46
- package/lib/types/story-beat-language.d.ts +0 -35
- package/lib/types/style-library.d.ts +0 -33
- package/skills/chapter-batch/SKILL.md +0 -81
- package/src/ai-scan.ts +0 -179
- package/src/assets.ts +0 -879
- package/src/assistant.ts +0 -770
- package/src/author-assets.ts +0 -147
- package/src/bookshelf.ts +0 -167
- package/src/client/api.ts +0 -617
- package/src/client/css-modules.d.ts +0 -8
- package/src/client/docx.ts +0 -69
- package/src/client/index.ts +0 -34
- package/src/client/locales.ts +0 -418
- package/src/client/mount.tsx +0 -97
- package/src/client/mount.tsx._new +0 -97
- package/src/client/mount.tsx._nf +0 -97
- package/src/client/mount.tsx._tmp +0 -97
- package/src/client/panel/AdaptModeView.tsx +0 -520
- package/src/client/panel/AssetsTab.tsx +0 -458
- package/src/client/panel/AssistantTab.tsx +0 -301
- package/src/client/panel/AuthorAssetsView.tsx +0 -208
- package/src/client/panel/AuthorHome.tsx +0 -204
- package/src/client/panel/BookshelfBar.tsx +0 -170
- package/src/client/panel/CreateBookView.tsx +0 -352
- package/src/client/panel/FlowGuide.tsx +0 -84
- package/src/client/panel/GlobalAssetLibraryView.tsx +0 -172
- package/src/client/panel/ImportModal.tsx +0 -303
- package/src/client/panel/MangaRoleLibrary.tsx +0 -1002
- package/src/client/panel/MangaWorkspace.tsx +0 -715
- package/src/client/panel/ModelManager.tsx +0 -205
- package/src/client/panel/NovelPanel.tsx +0 -4620
- package/src/client/panel/ProgressConsole.tsx +0 -64
- package/src/client/panel/ProgressHomeView.tsx +0 -87
- package/src/client/panel/PropLibrary.tsx +0 -175
- package/src/client/panel/ReaderView.tsx +0 -306
- package/src/client/panel/ReasoningSection.tsx +0 -31
- package/src/client/panel/RunPanel.tsx +0 -204
- package/src/client/panel/SceneLibrary.tsx +0 -453
- package/src/client/panel/SettingsView.tsx +0 -286
- package/src/client/panel/ShelfView.tsx +0 -242
- package/src/client/panel/StoryboardTab.tsx +0 -472
- package/src/client/panel/StyleCard.tsx +0 -90
- package/src/client/panel/StyleGallery.tsx +0 -58
- package/src/client/panel/WorldTab.tsx +0 -202
- package/src/client/panel/controller.ts +0 -45
- package/src/client/panel/helpers.ts +0 -67
- package/src/client/panel/panel.module.css +0 -4360
- package/src/client/panel/views.tsx +0 -314
- package/src/client/sidebar-entry.ts +0 -122
- package/src/client/text.ts +0 -28
- package/src/comic-presets.ts +0 -67
- package/src/docx.ts +0 -83
- package/src/engine.ts +0 -5200
- package/src/index.ts +0 -329
- package/src/manga-genre-rules.ts +0 -87
- package/src/novel-context.ts +0 -287
- package/src/prompt-utils.ts +0 -73
- package/src/protocol.ts +0 -2233
- package/src/review-policy.ts +0 -161
- package/src/routes.ts +0 -3419
- package/src/run.ts +0 -331
- package/src/shot-language.ts +0 -181
- package/src/story-beat-language.ts +0 -130
- package/src/style-library.ts +0 -84
package/src/index.ts
DELETED
|
@@ -1,329 +0,0 @@
|
|
|
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, ImageModelConfig, NovelConfig } from './protocol.ts'
|
|
19
|
-
import { makeRoutes } from './routes.ts'
|
|
20
|
-
import { activeBookOutputDir } from './bookshelf.ts'
|
|
21
|
-
|
|
22
|
-
/** Stable cordis plugin name. */
|
|
23
|
-
export const name = 'novel-forge'
|
|
24
|
-
|
|
25
|
-
/** Services required before the novel-forge surfaces can mount. */
|
|
26
|
-
export const inject = ['webServer', 'llm', 'systemPrompt', 'settings']
|
|
27
|
-
|
|
28
|
-
/**
|
|
29
|
-
* Settings namespace of the novel-forge capability — the section the web
|
|
30
|
-
* settings surface edits. Spelled here rather than imported: the browser half
|
|
31
|
-
* spells the same value and must not depend on a Host package.
|
|
32
|
-
*/
|
|
33
|
-
export const NOVEL_SETTINGS_NAMESPACE = 'dsh-novel-forge' as const
|
|
34
|
-
|
|
35
|
-
/** Plugin config, validated by the same-named schemastery schema. */
|
|
36
|
-
export interface Config {
|
|
37
|
-
/** When true (default), a system-prompt section announces the plugin to every agent. */
|
|
38
|
-
announceToAgent?: boolean
|
|
39
|
-
/** Master switch for the plugin (routes, prompt section). */
|
|
40
|
-
enabled?: boolean
|
|
41
|
-
/** Absolute path of the default docx outline to load. */
|
|
42
|
-
outlinePath?: string
|
|
43
|
-
/** Absolute output directory for chapters + project state. */
|
|
44
|
-
outputDir?: string
|
|
45
|
-
/** LLM provider route. */
|
|
46
|
-
provider?: string
|
|
47
|
-
/** LLM model id. */
|
|
48
|
-
model?: string
|
|
49
|
-
/** LLM reasoning effort (off/low/high/max). */
|
|
50
|
-
reasoningEffort?: 'off' | 'low' | 'high' | 'max'
|
|
51
|
-
/** 分析类任务(提炼/拆书/反推大纲等)的推理档位;默认 low。 */
|
|
52
|
-
analysisReasoning?: 'off' | 'low' | 'high' | 'max'
|
|
53
|
-
/** Target characters per chapter. */
|
|
54
|
-
chapterChars?: number
|
|
55
|
-
/** Max output tokens per chapter call. */
|
|
56
|
-
maxTokens?: number
|
|
57
|
-
/** Review pass threshold (0-100). */
|
|
58
|
-
reviewPassScore?: number
|
|
59
|
-
/** Whether generation auto-runs review after writing. */
|
|
60
|
-
autoReview?: boolean
|
|
61
|
-
/** Whether generation auto-runs the author review (hook/continuity/trend). */
|
|
62
|
-
autoAuthorReview?: boolean
|
|
63
|
-
/** 修订/润色产出草稿后自动附带一次 AI 审查(默认开,可在设置页关闭省 token)。 */
|
|
64
|
-
autoReviewAfterRevise?: boolean
|
|
65
|
-
/** 生图模型库(多套并存,启用一条生效)。 */
|
|
66
|
-
imageModels?: ImageModelConfig[]
|
|
67
|
-
/** 豆包/Seedream 生图 API Key(旧字段,兼容迁移用)。 */
|
|
68
|
-
imageApiKey?: string
|
|
69
|
-
/** 豆包/Seedream 生图模型 ID(旧字段,兼容迁移用)。 */
|
|
70
|
-
imageApiModel?: string
|
|
71
|
-
/** 是否启用豆包生图(旧字段,兼容迁移用)。 */
|
|
72
|
-
imageApiEnabled?: boolean
|
|
73
|
-
/** 自定义背景图(URL / dataURL / 服务端路径引用)。 */
|
|
74
|
-
themeBackground?: string
|
|
75
|
-
/** 自定义背景遮罩/模糊强度 0-80。 */
|
|
76
|
-
themeBackgroundBlur?: number
|
|
77
|
-
/** 玻璃透明度 0-100(100=当前原样)。 */
|
|
78
|
-
themeOpacity?: number
|
|
79
|
-
/** 是否启用改编模式(默认关闭)。 */
|
|
80
|
-
enableAdaptMode?: boolean
|
|
81
|
-
}
|
|
82
|
-
|
|
83
|
-
export const Config: z<Config> = z.object({
|
|
84
|
-
announceToAgent: z.boolean().default(true),
|
|
85
|
-
enabled: z.boolean().default(true),
|
|
86
|
-
outlinePath: z.string().default(''),
|
|
87
|
-
outputDir: z.string().default(join(homedir(), '.dsh', 'novels')),
|
|
88
|
-
provider: z.string().default('deepseek-official'),
|
|
89
|
-
model: z.string().default('deepseek-v4-flash'),
|
|
90
|
-
reasoningEffort: z.union(['off', 'low', 'high', 'max']).default('off'),
|
|
91
|
-
analysisReasoning: z.union(['off', 'low', 'high', 'max']).default('low'),
|
|
92
|
-
chapterChars: z.number().default(3500),
|
|
93
|
-
maxTokens: z.number().default(12000),
|
|
94
|
-
reviewPassScore: z.number().default(70),
|
|
95
|
-
autoReview: z.boolean().default(true),
|
|
96
|
-
autoAuthorReview: z.boolean().default(true),
|
|
97
|
-
autoReviewAfterRevise: z.boolean().default(true),
|
|
98
|
-
imageModels: z.array(z.object({
|
|
99
|
-
id: z.string().default(''),
|
|
100
|
-
name: z.string().default(''),
|
|
101
|
-
baseURL: z.string().default(''),
|
|
102
|
-
apiKey: z.string().default(''),
|
|
103
|
-
model: z.string().default(''),
|
|
104
|
-
enabled: z.boolean().default(false),
|
|
105
|
-
})).default([]),
|
|
106
|
-
imageApiKey: z.string().default(''),
|
|
107
|
-
imageApiModel: z.string().default(''),
|
|
108
|
-
imageApiEnabled: z.boolean().default(false),
|
|
109
|
-
themeBackground: z.string().default(''),
|
|
110
|
-
themeBackgroundBlur: z.number().default(0),
|
|
111
|
-
themeOpacity: z.number().default(100),
|
|
112
|
-
enableAdaptMode: z.boolean().default(true),
|
|
113
|
-
})
|
|
114
|
-
|
|
115
|
-
/** Schema defaults, re-read for hand-built test contexts. */
|
|
116
|
-
const DEFAULT_ANNOUNCE = true
|
|
117
|
-
const DEFAULT_OUTLINE_PATH = ''
|
|
118
|
-
const DEFAULT_OUTPUT_DIR = join(homedir(), '.dsh', 'novels')
|
|
119
|
-
const DEFAULT_PROVIDER = 'deepseek-official'
|
|
120
|
-
const DEFAULT_MODEL = 'deepseek-v4-flash'
|
|
121
|
-
const DEFAULT_REASONING_EFFORT = 'off' as const
|
|
122
|
-
const DEFAULT_ANALYSIS_REASONING = 'low' as const
|
|
123
|
-
const DEFAULT_CHAPTER_CHARS = 3500
|
|
124
|
-
const DEFAULT_MAX_TOKENS = 12000
|
|
125
|
-
const DEFAULT_REVIEW_PASS_SCORE = 70
|
|
126
|
-
const DEFAULT_AUTO_REVIEW = true
|
|
127
|
-
const DEFAULT_AUTO_AUTHOR_REVIEW = true
|
|
128
|
-
const DEFAULT_AUTO_REVIEW_AFTER_REVISE = true
|
|
129
|
-
|
|
130
|
-
/** Order of the announcement section within the tool-guidance band. */
|
|
131
|
-
const SECTION_ORDER = 160
|
|
132
|
-
|
|
133
|
-
/** Model-facing announcement: plugin presence, capabilities, and limits. */
|
|
134
|
-
export const NOVEL_GUIDANCE = '本机已安装 dsh-novel-forge 插件(AI 编译小说工作台):侧边栏「小说工坊」入口。能力:读取 docx 大纲或粘贴大纲文本;用 LLM 提炼道藏(人设/世界观/金手指规则/写作红线);生成卷计划与章节计划;逐章调用 LLM 生成 3000-4000 字正文并保存为 Markdown(默认输出到用户主目录 ~/.dsh/novels);每章自动生成摘要(叙事记忆)、自动 AI 审稿(人设/设定/红线/文笔/爽点/逻辑),支持按审稿意见重写、去 AI 味润色、暗线(伏笔)管理、批量连写与全本导出(txt/md)。限制:生成消耗 LLM API 额度;输出目录与模型可在插件设置中修改;章节正文质量取决于大纲完整度。用户提到「小说 / 大纲 / 写小说 / 章节 / 审稿 / 润色」时即指本插件,请据此协作。'
|
|
135
|
-
|
|
136
|
-
/** Resolve a config-like value into the full runtime config. */
|
|
137
|
-
export function resolveConfig(value: Partial<Config> | undefined): NovelConfig {
|
|
138
|
-
return {
|
|
139
|
-
outlinePath: value?.outlinePath ?? DEFAULT_OUTLINE_PATH,
|
|
140
|
-
outputDir: value?.outputDir ?? DEFAULT_OUTPUT_DIR,
|
|
141
|
-
provider: value?.provider ?? DEFAULT_PROVIDER,
|
|
142
|
-
model: value?.model ?? DEFAULT_MODEL,
|
|
143
|
-
reasoningEffort: value?.reasoningEffort ?? DEFAULT_REASONING_EFFORT,
|
|
144
|
-
analysisReasoning: value?.analysisReasoning ?? DEFAULT_ANALYSIS_REASONING,
|
|
145
|
-
chapterChars: value?.chapterChars ?? DEFAULT_CHAPTER_CHARS,
|
|
146
|
-
maxTokens: value?.maxTokens ?? DEFAULT_MAX_TOKENS,
|
|
147
|
-
reviewPassScore: value?.reviewPassScore ?? DEFAULT_REVIEW_PASS_SCORE,
|
|
148
|
-
autoReview: value?.autoReview ?? DEFAULT_AUTO_REVIEW,
|
|
149
|
-
autoAuthorReview: value?.autoAuthorReview ?? DEFAULT_AUTO_AUTHOR_REVIEW,
|
|
150
|
-
autoReviewAfterRevise: value?.autoReviewAfterRevise ?? DEFAULT_AUTO_REVIEW_AFTER_REVISE,
|
|
151
|
-
// 生图模型库:只有「从未保存过 imageModels」时才用旧配置迁移一条(豆包默认地址);
|
|
152
|
-
// 已保存为空数组 = 用户主动删光,保持空,不再复活。
|
|
153
|
-
imageModels: value?.imageModels !== undefined
|
|
154
|
-
? value.imageModels
|
|
155
|
-
: (value?.imageApiKey !== undefined && value.imageApiKey !== ''
|
|
156
|
-
? [{ id: 'img-legacy', name: '豆包(旧配置)', baseURL: DEFAULT_IMAGE_BASE, apiKey: value.imageApiKey, model: value.imageApiModel ?? '', enabled: true }]
|
|
157
|
-
: []),
|
|
158
|
-
// 运行时生效值 = 启用条目(兼容旧代码读取 imageApiKey/imageApiModel/imageApiEnabled)。
|
|
159
|
-
imageBaseUrl: (() => {
|
|
160
|
-
const active = (value?.imageModels ?? []).find(m => m.enabled) ?? (value?.imageModels ?? [])[0]
|
|
161
|
-
return active?.baseURL !== undefined && active.baseURL !== '' ? active.baseURL : (value?.imageApiKey !== undefined && value.imageApiKey !== '' ? DEFAULT_IMAGE_BASE : undefined)
|
|
162
|
-
})(),
|
|
163
|
-
imageApiKey: (() => {
|
|
164
|
-
const active = (value?.imageModels ?? []).find(m => m.enabled) ?? (value?.imageModels ?? [])[0]
|
|
165
|
-
return active?.apiKey ?? value?.imageApiKey
|
|
166
|
-
})(),
|
|
167
|
-
imageApiModel: (() => {
|
|
168
|
-
const active = (value?.imageModels ?? []).find(m => m.enabled) ?? (value?.imageModels ?? [])[0]
|
|
169
|
-
return active?.model ?? value?.imageApiModel
|
|
170
|
-
})(),
|
|
171
|
-
// 生图启用:总开关打开,或模型库里存在启用条目,均视为已启用(避免用户只配模型忘了开总开关)。
|
|
172
|
-
imageApiEnabled: value?.imageApiEnabled === true || (value?.imageModels ?? []).some(m => m.enabled === true),
|
|
173
|
-
themeBackground: value?.themeBackground ?? '',
|
|
174
|
-
themeBackgroundBlur: value?.themeBackgroundBlur ?? 0,
|
|
175
|
-
themeOpacity: value?.themeOpacity ?? 100,
|
|
176
|
-
enableAdaptMode: value?.enableAdaptMode ?? true,
|
|
177
|
-
}
|
|
178
|
-
}
|
|
179
|
-
|
|
180
|
-
/** 生图默认接口地址(豆包 ark,OpenAI 兼容)。 */
|
|
181
|
-
const DEFAULT_IMAGE_BASE = 'https://ark.cn-beijing.volces.com/api/v3'
|
|
182
|
-
|
|
183
|
-
/**
|
|
184
|
-
* Mount the routes and announcement.
|
|
185
|
-
* @param ctx - host plugin context carrying webServer/llm/systemPrompt.
|
|
186
|
-
* @param config - resolved plugin config (schema defaults applied by the loader).
|
|
187
|
-
*/
|
|
188
|
-
export function apply(ctx: Context, config?: Config): void {
|
|
189
|
-
// The live source the routes read: the settings section once the web
|
|
190
|
-
// settings surface is served, the composition entry otherwise.
|
|
191
|
-
let current: () => Config = () => config ?? {}
|
|
192
|
-
const resolve = (): NovelConfig => {
|
|
193
|
-
const resolved = resolveConfig(current())
|
|
194
|
-
// 书架激活的书优先决定输出目录(settings 仍可改默认值)。
|
|
195
|
-
const shelfDir = activeBookOutputDir()
|
|
196
|
-
if (shelfDir !== undefined) {
|
|
197
|
-
return { ...resolved, outputDir: shelfDir }
|
|
198
|
-
}
|
|
199
|
-
return resolved
|
|
200
|
-
}
|
|
201
|
-
|
|
202
|
-
const patchConfig = async (patch: ConfigPatch): Promise<NovelConfig> => {
|
|
203
|
-
const next: ConfigPatch = {}
|
|
204
|
-
if (patch.outlinePath !== undefined) next.outlinePath = patch.outlinePath
|
|
205
|
-
if (patch.outputDir !== undefined) next.outputDir = patch.outputDir
|
|
206
|
-
if (patch.provider !== undefined) next.provider = patch.provider
|
|
207
|
-
if (patch.model !== undefined) next.model = patch.model
|
|
208
|
-
if (patch.reasoningEffort !== undefined) next.reasoningEffort = patch.reasoningEffort
|
|
209
|
-
if (patch.analysisReasoning !== undefined) next.analysisReasoning = patch.analysisReasoning
|
|
210
|
-
if (patch.chapterChars !== undefined) next.chapterChars = patch.chapterChars
|
|
211
|
-
if (patch.maxTokens !== undefined) next.maxTokens = patch.maxTokens
|
|
212
|
-
if (patch.reviewPassScore !== undefined) next.reviewPassScore = patch.reviewPassScore
|
|
213
|
-
if (patch.autoReview !== undefined) next.autoReview = patch.autoReview
|
|
214
|
-
if (patch.autoAuthorReview !== undefined) next.autoAuthorReview = patch.autoAuthorReview
|
|
215
|
-
if (patch.autoReviewAfterRevise !== undefined) next.autoReviewAfterRevise = patch.autoReviewAfterRevise
|
|
216
|
-
if (patch.imageApiKey !== undefined) next.imageApiKey = patch.imageApiKey
|
|
217
|
-
if (patch.imageApiModel !== undefined) next.imageApiModel = patch.imageApiModel
|
|
218
|
-
if (patch.imageApiEnabled !== undefined) next.imageApiEnabled = patch.imageApiEnabled
|
|
219
|
-
if (patch.themeBackground !== undefined) next.themeBackground = patch.themeBackground
|
|
220
|
-
if (patch.themeBackgroundBlur !== undefined) next.themeBackgroundBlur = patch.themeBackgroundBlur
|
|
221
|
-
if (patch.themeOpacity !== undefined) next.themeOpacity = patch.themeOpacity
|
|
222
|
-
if (patch.enableAdaptMode !== undefined) next.enableAdaptMode = patch.enableAdaptMode
|
|
223
|
-
if (patch.imageModels !== undefined) next.imageModels = patch.imageModels
|
|
224
|
-
// Persist through the settings seam when available; otherwise keep in memory.
|
|
225
|
-
// (ctx.get is the non-strict service access — no inject requirement, same
|
|
226
|
-
// pattern installSettingsSection itself uses.)
|
|
227
|
-
const settings = ctx.get('settings')
|
|
228
|
-
if (settings !== undefined) {
|
|
229
|
-
await settings.update(NOVEL_SETTINGS_NAMESPACE, next as Record<string, unknown>)
|
|
230
|
-
} else {
|
|
231
|
-
current = () => ({ ...current(), ...next })
|
|
232
|
-
}
|
|
233
|
-
return resolve()
|
|
234
|
-
}
|
|
235
|
-
|
|
236
|
-
let disposeSection: (() => void) | undefined
|
|
237
|
-
let disposeRoutes: (() => void) | undefined
|
|
238
|
-
|
|
239
|
-
const sync = (): void => {
|
|
240
|
-
if (disposeSection !== undefined) {
|
|
241
|
-
disposeSection()
|
|
242
|
-
disposeSection = undefined
|
|
243
|
-
}
|
|
244
|
-
if (disposeRoutes !== undefined) {
|
|
245
|
-
disposeRoutes()
|
|
246
|
-
disposeRoutes = undefined
|
|
247
|
-
}
|
|
248
|
-
const value = resolve()
|
|
249
|
-
if (!(current().enabled ?? true)) return
|
|
250
|
-
if (current().announceToAgent ?? DEFAULT_ANNOUNCE) {
|
|
251
|
-
disposeSection = ctx.systemPrompt.section({
|
|
252
|
-
name: 'plugin:dsh-novel-forge',
|
|
253
|
-
order: SECTION_ORDER,
|
|
254
|
-
text: NOVEL_GUIDANCE,
|
|
255
|
-
})
|
|
256
|
-
}
|
|
257
|
-
const routes = makeRoutes({ ctx, getConfig: resolve, patchConfig })
|
|
258
|
-
disposeRoutes = ctx.effect(
|
|
259
|
-
() => {
|
|
260
|
-
const disposers = routes.map(route => ctx.webServer.register(route))
|
|
261
|
-
return () => { for (const dispose of disposers) dispose() }
|
|
262
|
-
},
|
|
263
|
-
'dsh-novel-forge: routes',
|
|
264
|
-
)
|
|
265
|
-
void value
|
|
266
|
-
}
|
|
267
|
-
|
|
268
|
-
ctx.settings.installSection(ctx, NOVEL_SETTINGS_NAMESPACE, Config, config ?? {}, {
|
|
269
|
-
setSource: (source) => {
|
|
270
|
-
current = source
|
|
271
|
-
sync()
|
|
272
|
-
},
|
|
273
|
-
onChange: sync,
|
|
274
|
-
})
|
|
275
|
-
|
|
276
|
-
// 运行时 skill:章节批量生产与值守处理(只在本插件环境可用)。
|
|
277
|
-
// 通过 ctx.get('skills') 获取注册表(服务存在时注册,缺失则跳过,不影响其他能力)。
|
|
278
|
-
const skillsService = ctx.get('skills') as { register?: (skill: { name: string; provider: string; description: string; content: string }) => () => void } | undefined
|
|
279
|
-
if (skillsService?.register !== undefined) {
|
|
280
|
-
const disposeSkill = skillsService.register({
|
|
281
|
-
name: 'novel-forge-chapter-batch',
|
|
282
|
-
provider: 'dsh-novel-forge',
|
|
283
|
-
description: '批量生成小说章节并值守处理审稿未过的章节(豁免/按意见修订/验证模式/重新生成),依赖 dsh-novel-forge 的 /api/dsh-novel-forge/* 路由。',
|
|
284
|
-
content: [
|
|
285
|
-
'# 小说章节批量生产与值守处理',
|
|
286
|
-
'',
|
|
287
|
-
'## 0. 值守纪律(最高优先级,违反即空转)',
|
|
288
|
-
'1. 启动后台任务后立即停下,不轮询、不连续 wait、不做无意义动作——后台任务完成时运行时自动通知。',
|
|
289
|
-
'2. 只有收到「任务完成」通知或用户新消息时,才继续下一步(读结果→处理未过→汇报)。',
|
|
290
|
-
'3. 等待期间可做与当前任务无关的其他有用工作,但不得为等任务而空转。',
|
|
291
|
-
'4. 任务因 web 重启中断时:待 3080 恢复后,复位卡死章节(generating/error → reset)再续跑,并向用户说明中断原因。',
|
|
292
|
-
'',
|
|
293
|
-
'## 1. 前置检查',
|
|
294
|
-
'1. `GET /api/dsh-novel-forge/status` 确认服务在跑,读项目章节数。',
|
|
295
|
-
'2. 列出目标区间 pending 章节号,避免重复生成已处理章节。',
|
|
296
|
-
'3. **串行执行,禁止并行写 project.json**(并发会互相覆盖状态,实测教训)。',
|
|
297
|
-
'',
|
|
298
|
-
'## 2. 批量生成',
|
|
299
|
-
'逐章串行调用 `POST /generate` `{ chapterNo, skipReview: false }`(走完整质量门:生成→摘要+编年录→审稿→复盘)。',
|
|
300
|
-
'响应为 NDJSON,找 review 帧取 score/passed。每章 2-5 分钟;失败重试 1 次;后台跑并告知预计时长。',
|
|
301
|
-
'用 Node 脚本(fetch 逐章循环),避免 PowerShell 转义坑,用完删除。',
|
|
302
|
-
'',
|
|
303
|
-
'## 3. 未过章节分级处理',
|
|
304
|
-
'### A. 无 high → 豁免通过:`POST /chapter/approve`(主观项不磨)',
|
|
305
|
-
'### B. 有 high 且明确 → 按意见修订 + 验证模式:',
|
|
306
|
-
'1. 拼指令 `按审稿意见修订(优先处理):\n[severity] item → suggestion`(high 优先,无 high 取前 3 medium)',
|
|
307
|
-
'2. `POST /rewrite` 产草稿;3. `POST /chapter/check` 带 `previousReport` 走验证模式(只核对原意见解决+只挑新增 high)',
|
|
308
|
-
'4. 判定 `passed || 无 high` 可接受;5. `POST /draft/apply` 带 passed 报告落盘 approved',
|
|
309
|
-
'6. 不可接受 → 第二轮修订(指令更精确)→ 再验证;每章最多修 2 轮,仍不过保留草稿待人工',
|
|
310
|
-
'### C. 结构性 high(修订改不好)→ `POST /draft/discard` 后 `POST /generate` 重写,剩主观项再走 A',
|
|
311
|
-
'### D. error 状态 → `POST /chapter/reset` 后 `POST /generate`',
|
|
312
|
-
'注意:approved 但 review 有 high 的保留不动;修订指令越精确越有效(具体数字/行为一次就过)。',
|
|
313
|
-
'',
|
|
314
|
-
'## 4. 收尾',
|
|
315
|
-
'`GET /status` 验证目标区间全 approved;可选刷新剧情线(`POST /plotlines` op=refresh,注意只读最近 8 章摘要);汇报通过/未过/失败与遗留项。',
|
|
316
|
-
'',
|
|
317
|
-
'## 5. 已知陷阱',
|
|
318
|
-
'- 并发写 project.json 互相覆盖 → 必须串行',
|
|
319
|
-
'- rewrite 草稿可能偶发不落盘 → 应用前检查 pendingDraft,不存在则重跑',
|
|
320
|
-
'- PowerShell 调 API 引号/中文转义是坑 → 用 Node 脚本',
|
|
321
|
-
'- 验证模式 500 偶发 → 重试一次',
|
|
322
|
-
].join('\n'),
|
|
323
|
-
})
|
|
324
|
-
ctx.effect(() => disposeSkill, 'dsh-novel-forge: skill')
|
|
325
|
-
}
|
|
326
|
-
|
|
327
|
-
// Initial registration from the composition entry.
|
|
328
|
-
sync()
|
|
329
|
-
}
|
package/src/manga-genre-rules.ts
DELETED
|
@@ -1,87 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* 题材规则库(可插拔)—— 漫剧分镜/提示词生成的「题材专用规则」。
|
|
3
|
-
* 参考短剧工业导演规范:不同题材用不同的视觉语言 / 打斗 / 特效规则,
|
|
4
|
-
* 按漫剧方案的 genre 注入 generateStoryboardPrompts,避免「一套模板跑所有题材」。
|
|
5
|
-
* 每个题材给一组「生成提示词时必须遵守的题材规则」;未选题材则不注入(走通用规则)。
|
|
6
|
-
*/
|
|
7
|
-
|
|
8
|
-
export interface GenreRule {
|
|
9
|
-
/** 题材 id。 */
|
|
10
|
-
id: string
|
|
11
|
-
/** 题材名(展示)。 */
|
|
12
|
-
label: string
|
|
13
|
-
/** 该题材的生成提示词核心规则(每行一条,注入生成时)。 */
|
|
14
|
-
rules: string[]
|
|
15
|
-
}
|
|
16
|
-
|
|
17
|
-
/** 内置题材(通用题材 default 为空规则)。 */
|
|
18
|
-
export const GENRES: GenreRule[] = [
|
|
19
|
-
{
|
|
20
|
-
id: 'xuanhuan',
|
|
21
|
-
label: '玄幻 / 仙侠',
|
|
22
|
-
rules: [
|
|
23
|
-
'施法必须经法宝媒介(笛/扇/印/笔等实体法宝),写清法宝本体材质+施法动作+特效出口,禁止只写"施法"。',
|
|
24
|
-
'每个角色绑定主属性+双色粒子(雷=蓝白+紫电、冰=冰蓝+银白、金=亮金+暖白、火=赤红+暗金、木=粉白+草木青),属性决定术法形态与配色。',
|
|
25
|
-
'术法攻防走"罡气→破罡→防御转攻":护体罡气被划开、防御术法炸裂化千万飞刃/剑气,防守转攻是高光点,必写。',
|
|
26
|
-
'能量具象化:光柱/雷龙/法相/领域;"光柱"要写成"洪流/弧光/波纹/光丝雨"(禁裸光柱,防呆板发光管)。',
|
|
27
|
-
'终招蓄力必写:天地灵韵向角色/法宝汇聚、能量漩涡成型,再接幂击。',
|
|
28
|
-
'对军清场:扇形冲击波/剑气暴雨洗地/能量扇镇压大军核心,战场弹坑/碎片/云层撕开反馈。',
|
|
29
|
-
],
|
|
30
|
-
},
|
|
31
|
-
{
|
|
32
|
-
id: 'wuxia',
|
|
33
|
-
label: '武侠 / 国风',
|
|
34
|
-
rules: [
|
|
35
|
-
'打斗用 Hit-Stop 三件套:碰撞瞬间 1~2 帧微停顿 + 接触点火花 + 双刃咬死高频震颤,杜绝滑步/空气假打。',
|
|
36
|
-
'武术动作链:起手/攻防/收势,标明具体招式(刀/剑/轻功/擒拿),剑走轻灵、刀走厚重。',
|
|
37
|
-
'镜头强化打击感:碰撞点微环绕、贴身横移、命中定格,禁静止对峙。',
|
|
38
|
-
'轻功/身法:衣袂翻飞、发丝飘动、残影,身形轻盈不僵硬。',
|
|
39
|
-
],
|
|
40
|
-
},
|
|
41
|
-
{
|
|
42
|
-
id: 'anime',
|
|
43
|
-
label: '二次元 / 动漫',
|
|
44
|
-
rules: [
|
|
45
|
-
'大招三段式:蓄力→释放→爆发,能量形态+爆发特效,段段画质声明复用。',
|
|
46
|
-
'双色粒子:每角色一套主色+材质+轨迹,双方互补对冲(冷 vs 暖),防混战混淆。',
|
|
47
|
-
'色指定:明确主色调+高光色+特效撞色,防发灰。',
|
|
48
|
-
'禁止评价词/裸光效/空泛"粒子";能量写"光丝雨/弧光/波纹"+方向+材质。',
|
|
49
|
-
],
|
|
50
|
-
},
|
|
51
|
-
{
|
|
52
|
-
id: 'urban',
|
|
53
|
-
label: '都市 / 现实',
|
|
54
|
-
rules: [
|
|
55
|
-
'微表情生理递进:眼神/眉心/唇部/鼻翼/喉头/手指,情绪逐层推进,不夸张不脸谱。',
|
|
56
|
-
'对白语速 3.5~5 字/秒(台词从声音层按此把控,留 0.3~0.8s 落口发酵)。',
|
|
57
|
-
'防棚拍假冷光:用真实光线(自然光/路灯/窗光/霓虹),日景不陰间冷蓝、局部留暖光。',
|
|
58
|
-
'场景真实还原(街头/室内/医院等),人物服装生活化,肢体动作自然不律動化。',
|
|
59
|
-
],
|
|
60
|
-
},
|
|
61
|
-
{
|
|
62
|
-
id: 'scifi',
|
|
63
|
-
label: '科幻 / 未来',
|
|
64
|
-
rules: [
|
|
65
|
-
'机甲/能量武器设定明确(颜色/材质/发光部位),能量轨迹具体。',
|
|
66
|
-
'霓虹/屏幕光/激光防眩光,用冷色主调+重点发光处。',
|
|
67
|
-
'宏大场景(飞船/城市/废墟)用大远景/俯拍,人物相对渺小。',
|
|
68
|
-
],
|
|
69
|
-
},
|
|
70
|
-
]
|
|
71
|
-
|
|
72
|
-
/** 通用题材(空规则,走通用生成)。 */
|
|
73
|
-
export const DEFAULT_GENRE: GenreRule = { id: 'default', label: '通用(不限定)', rules: [] }
|
|
74
|
-
|
|
75
|
-
/** 按题材 id 取规则(未找到返回通用)。 */
|
|
76
|
-
export function getGenreRules(id?: string): string[] {
|
|
77
|
-
if (id === undefined || id === '') return DEFAULT_GENRE.rules
|
|
78
|
-
const g = GENRES.find(x => x.id === id)
|
|
79
|
-
return g !== undefined ? g.rules : DEFAULT_GENRE.rules
|
|
80
|
-
}
|
|
81
|
-
|
|
82
|
-
/** 取题材 label(展示用)。 */
|
|
83
|
-
export function getGenreLabel(id?: string): string {
|
|
84
|
-
if (id === undefined || id === '') return DEFAULT_GENRE.label
|
|
85
|
-
const g = GENRES.find(x => x.id === id)
|
|
86
|
-
return g !== undefined ? g.label : id
|
|
87
|
-
}
|