dsh-maoying 0.1.20 → 0.1.21
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.en.md +4 -4
- package/README.md +2 -2
- package/docs/creator-panel.md +14 -8
- package/docs/design.md +8 -8
- package/docs/spec.md +2 -2
- package/index.mjs +126 -29
- package/lib/base.css +16 -4
- package/lib/client.js +458 -78
- package/lib/epub.mjs +6 -0
- package/lib/novel-parse.mjs +103 -19
- package/lib/panel-core.mjs +16 -7
- package/lib/self-update.mjs +118 -21
- package/lib/version.mjs +30 -2
- package/package.json +2 -1
- package/presets/maoying/agent.cordis.v2.yml +5 -0
- package/presets/maoying/agent.cordis.yml +9 -0
- package/presets/maoying/restrict.mjs +16 -3
- package/scripts/smoke-client.mjs +128 -0
|
@@ -188,6 +188,11 @@
|
|
|
188
188
|
toolName: outline
|
|
189
189
|
backgroundMode: continuable
|
|
190
190
|
agentPreset: maoying-outline
|
|
191
|
+
# 命名注意:toolName: write 与 tool-fs 提供的文件写工具 write 同名
|
|
192
|
+
# ('write' 一名双义)。宿主 registry 对同名工具的处理仓内无法核实,
|
|
193
|
+
# 故不改 toolName(行为风险);restrict.mjs MAIN_AGENT_ALLOW 单条
|
|
194
|
+
# 'write' 即同时放行两义,改名前须先核实 registry 并与 v1/工具面
|
|
195
|
+
# 一起改(详见 agent.cordis.yml 的 delegation-write 行注释)。
|
|
191
196
|
- id: delegation-write
|
|
192
197
|
name: "@deepseek-ai/dsh-tool-subagent"
|
|
193
198
|
config:
|
|
@@ -110,6 +110,9 @@
|
|
|
110
110
|
disabled: !!js process.platform !== 'win32'
|
|
111
111
|
|
|
112
112
|
# ── filesystem ──────────────────────────────────────────────────────────────
|
|
113
|
+
# 命名注意:tool-fs 的文件写工具名是 write,与下方 delegation 组 delegation-write
|
|
114
|
+
# 以 toolName: write 注册的委派工具同名('write' 一名双义——restrict.mjs 的
|
|
115
|
+
# MAIN_AGENT_ALLOW 单条同时放行两义,详见 delegation-write 行注释)。
|
|
113
116
|
|
|
114
117
|
- id: tool-fs
|
|
115
118
|
name: '@deepseek-ai/dsh-tool-fs'
|
|
@@ -232,6 +235,12 @@
|
|
|
232
235
|
- todo_write
|
|
233
236
|
|
|
234
237
|
# 写手:章节正文
|
|
238
|
+
# 命名注意:toolName: write 与本文件上方 filesystem 区 tool-fs 提供的文件
|
|
239
|
+
# 写工具 write 同名——主编工具面里 'write' 一名双义(文件写 vs 本委派)。
|
|
240
|
+
# 宿主 registry 对同名工具的处理仓内无法核实,故不改 toolName(行为风险);
|
|
241
|
+
# restrict.mjs 的 MAIN_AGENT_ALLOW 单条 'write' 即同时放行两义。改名前必须
|
|
242
|
+
# 先核实宿主 registry 对同名工具的处理,并连同 restrict.mjs 与 filesystem
|
|
243
|
+
# 区一起改。
|
|
235
244
|
- id: delegation-write
|
|
236
245
|
name: '@deepseek-ai/dsh-tool-subagent'
|
|
237
246
|
config:
|
|
@@ -17,8 +17,11 @@
|
|
|
17
17
|
* web_fetch(考据委派 research)、skill 目录(registry deny 堵 ~1.3k 注入)、
|
|
18
18
|
* 以及未列出的一切通用委派与工作流工具。
|
|
19
19
|
*
|
|
20
|
-
* write / edit 保留在白名单(见 MAIN_AGENT_ALLOW
|
|
21
|
-
* 小补(改 phase、补 frontmatter
|
|
20
|
+
* write / edit 保留在白名单(见 MAIN_AGENT_ALLOW):主编需要亲自做小修
|
|
21
|
+
* 小补(改 phase、补 frontmatter、微调摘要),这些活不值得委派一次。注意
|
|
22
|
+
* 'write' 一名双义——既是 tool-fs 的文件写工具,也是 delegation-write 以
|
|
23
|
+
* toolName: write 注册的委派工具(宿主 registry 对同名工具的处理仓内无法
|
|
24
|
+
* 核实,改名前必须先核实;详见 MAIN_AGENT_ALLOW 内注释)。
|
|
22
25
|
*
|
|
23
26
|
* Host 工具(mcp__*、插件工具)一律不硬编码——是否挂载是检测期事实,
|
|
24
27
|
* 硬编码会让未装对应插件的宿主直接出错。本文件只留 dsh 核心工具。
|
|
@@ -36,6 +39,11 @@ export const inject = ['systemPrompt', 'tools']
|
|
|
36
39
|
const MAIN_AGENT_ALLOW = new Set([
|
|
37
40
|
// ── 角色委派 ─────────────────────────────────────────────────────
|
|
38
41
|
'outline',
|
|
42
|
+
// 命名注意:'write' 一名双义——delegation-write 以 toolName: write 注册的
|
|
43
|
+
// 委派工具,与 tool-fs 提供的文件写工具(agent.cordis*.yml 的 filesystem 区)
|
|
44
|
+
// 同名。白名单按工具名匹配,这里单条 'write' 即同时放行两义。宿主 registry
|
|
45
|
+
// 对同名工具的处理仓内无法核实,故不改名;改名前必须先核实 registry 行为,
|
|
46
|
+
// 并连同两份 agent.cordis*.yml 的 toolName/filesystem 一起改。
|
|
39
47
|
'write',
|
|
40
48
|
'review',
|
|
41
49
|
'lore',
|
|
@@ -61,7 +69,9 @@ const MAIN_AGENT_ALLOW = new Set([
|
|
|
61
69
|
'job_list',
|
|
62
70
|
'job_kill',
|
|
63
71
|
// ── 主编亲笔(小修小补不过委派) ────────────────────────────────
|
|
64
|
-
'write'
|
|
72
|
+
// 这里只列 'edit':'write'(文件写语义正属此区)已由上方「角色委派」区的
|
|
73
|
+
// 同名条目放行——同一个 Set 里写两遍会被静默去重,反而掩盖「'write' 一名
|
|
74
|
+
// 双义(文件写 vs 委派工具)」这一事实,故显式只写一处并加注释。
|
|
65
75
|
'edit',
|
|
66
76
|
])
|
|
67
77
|
|
|
@@ -84,6 +94,9 @@ let warned = false
|
|
|
84
94
|
function isDelegatedChild(agent) {
|
|
85
95
|
const header = agent?.session?.header
|
|
86
96
|
if (header === undefined) return false
|
|
97
|
+
// 括号必须显式:?? 的优先级低于比较运算符,裸写 delegationDepth ?? 0 > 0
|
|
98
|
+
// 会被解析成 delegationDepth ?? (0 > 0)——当前取值侥幸同义,显式括号防未来
|
|
99
|
+
// 改默认值/比较符时悄悄变义。
|
|
87
100
|
return header.origin === 'subagent' || (header.delegationDepth ?? 0) > 0
|
|
88
101
|
}
|
|
89
102
|
|
|
@@ -0,0 +1,128 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* client.js 冒烟(npm run smoke,CI 同跑):不启浏览器、零依赖,三步校验
|
|
4
|
+
* lib/client.js 的可加载性与导出面契约——
|
|
5
|
+
* ① 语法可编译:整份源码 new Function 包裹,解析失败即红;
|
|
6
|
+
* ② ModuleLoader 拦截:stub window / document 求值,截获 load({ id, factory }),
|
|
7
|
+
* id 必须是 dsh-maoying(图行 id 约定,挂错名宿主首载即 import 报错);
|
|
8
|
+
* ③ factory 求值:stub react(五个 hook + createElement + Component——
|
|
9
|
+
* class extends 在定义期就要 Component)与宿主 UI 原语;react-dom/client
|
|
10
|
+
* 缺席由 factory 内部 catch(Node 下拿不到属正常)。导出面必须 =
|
|
11
|
+
* { inject: 含 "slots" 的数组, apply: 函数 },且 apply 两次求值:
|
|
12
|
+
* 带 slots stub 时应把入口挂到 sidebar.footer.action 键控槽(id
|
|
13
|
+
* maoying-creator),不带 slots 时护栏折返也不炸——两次都返回 disposer
|
|
14
|
+
* 函数且可安全调用。
|
|
15
|
+
* 任何一步失败 exit 1(console.error 中文说明)。
|
|
16
|
+
*/
|
|
17
|
+
import { readFileSync } from 'node:fs'
|
|
18
|
+
import path from 'node:path'
|
|
19
|
+
import { fileURLToPath } from 'node:url'
|
|
20
|
+
|
|
21
|
+
const fail = (message) => {
|
|
22
|
+
console.error(`smoke: ${message}`)
|
|
23
|
+
process.exit(1)
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
const pluginRoot = path.resolve(path.dirname(fileURLToPath(import.meta.url)), '..')
|
|
27
|
+
const source = readFileSync(path.join(pluginRoot, 'lib', 'client.js'), 'utf8')
|
|
28
|
+
|
|
29
|
+
// ① 语法可编译
|
|
30
|
+
let compiled
|
|
31
|
+
try {
|
|
32
|
+
compiled = new Function('window', 'document', source)
|
|
33
|
+
} catch (err) {
|
|
34
|
+
fail(`lib/client.js 语法不可编译:${err.message}`)
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
// ② 求值并截获 ModuleLoader.load(querySelector 给真值走 mountCss 幂等跳过支,
|
|
38
|
+
// Node 下不触碰任何真实 DOM)
|
|
39
|
+
let definition = null
|
|
40
|
+
const windowStub = {
|
|
41
|
+
__ModuleLoader__: {
|
|
42
|
+
load(def) {
|
|
43
|
+
definition = def
|
|
44
|
+
},
|
|
45
|
+
},
|
|
46
|
+
}
|
|
47
|
+
const documentStub = { currentScript: null, querySelector: () => ({}) }
|
|
48
|
+
try {
|
|
49
|
+
compiled(windowStub, documentStub)
|
|
50
|
+
} catch (err) {
|
|
51
|
+
fail(`lib/client.js 顶层求值失败:${err.message}`)
|
|
52
|
+
}
|
|
53
|
+
if (!definition || typeof definition.factory !== 'function') {
|
|
54
|
+
fail(`未截获 __ModuleLoader__.load({ factory }),实得:${JSON.stringify(definition)?.slice(0, 120)}`)
|
|
55
|
+
}
|
|
56
|
+
if (definition.id !== 'dsh-maoying') {
|
|
57
|
+
fail(`插件 id 应为 dsh-maoying(图行 id 约定),实得「${definition.id}」`)
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
// ③ factory 求值:导出面 + apply 双路
|
|
61
|
+
const reactStub = {
|
|
62
|
+
createElement: (...args) => ({ $$el: args }),
|
|
63
|
+
useState: (initial) => [initial, () => {}],
|
|
64
|
+
useEffect: () => {},
|
|
65
|
+
useCallback: (fn) => fn,
|
|
66
|
+
useRef: (initial) => ({ current: initial }),
|
|
67
|
+
useMemo: (fn) => fn(),
|
|
68
|
+
Component: class Component {},
|
|
69
|
+
}
|
|
70
|
+
const requireStub = (name) => {
|
|
71
|
+
if (name === 'react') return reactStub
|
|
72
|
+
// 宿主 UI 原语:factory 只做 Object.assign 拷贝,空对象即可(缺名图标渲染期
|
|
73
|
+
// 才会用到,本冒烟不渲染)。
|
|
74
|
+
if (name === '@deepseek-ai/dsh-client-ui-primitives') return {}
|
|
75
|
+
// react-dom/client 由 factory 内部 catch(返回 null),这里抛错即模拟缺席;
|
|
76
|
+
// 其余未知模块视为依赖面外溢,直接红。
|
|
77
|
+
throw new Error(`smoke require stub: 意外模块「${name}」`)
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
let exportsSurface = null
|
|
81
|
+
try {
|
|
82
|
+
exportsSurface = definition.factory(requireStub)
|
|
83
|
+
} catch (err) {
|
|
84
|
+
fail(`factory 求值失败:${err.message}`)
|
|
85
|
+
}
|
|
86
|
+
if (!exportsSurface || typeof exportsSurface !== 'object') fail('factory 未返回导出对象')
|
|
87
|
+
if (!Array.isArray(exportsSurface.inject) || !exportsSurface.inject.includes('slots')) {
|
|
88
|
+
fail(`exports.inject 应为含 "slots" 的依赖数组,实得:${JSON.stringify(exportsSurface.inject)}`)
|
|
89
|
+
}
|
|
90
|
+
if (typeof exportsSurface.apply !== 'function') fail('exports.apply 应为函数')
|
|
91
|
+
|
|
92
|
+
// apply ①:带 slots stub——入口应挂到 sidebar.footer.action 键控槽
|
|
93
|
+
const injectedSlots = []
|
|
94
|
+
const registeredMetas = []
|
|
95
|
+
let registerReturns = 0
|
|
96
|
+
const slotsStub = {
|
|
97
|
+
inject(slot, mount) {
|
|
98
|
+
injectedSlots.push(slot)
|
|
99
|
+
const component = mount() // 触发 register:只求值组件引用,不渲染
|
|
100
|
+
if (component === undefined) fail('slots.inject 的 mount 工厂未返回 register 结果')
|
|
101
|
+
return () => {}
|
|
102
|
+
},
|
|
103
|
+
register(meta) {
|
|
104
|
+
registeredMetas.push(meta)
|
|
105
|
+
registerReturns += 1
|
|
106
|
+
return { disposed: false }
|
|
107
|
+
},
|
|
108
|
+
}
|
|
109
|
+
const loggerWarns = []
|
|
110
|
+
const disposerA = exportsSurface.apply({
|
|
111
|
+
logger: { warn(...args) { loggerWarns.push(args.map(String).join(' ')) } },
|
|
112
|
+
slots: slotsStub,
|
|
113
|
+
})
|
|
114
|
+
if (typeof disposerA !== 'function') fail('apply(ctx) 应返回 disposer 函数')
|
|
115
|
+
if (injectedSlots.join(',') !== 'sidebar.footer.action') {
|
|
116
|
+
fail(`入口应注入 sidebar.footer.action 槽,实得:${injectedSlots.join(',') || '(无)'}`)
|
|
117
|
+
}
|
|
118
|
+
if (registerReturns !== 1 || registeredMetas[0]?.id !== 'maoying-creator') {
|
|
119
|
+
fail(`应注册 id 为 maoying-creator 的入口,实得:${JSON.stringify(registeredMetas)}`)
|
|
120
|
+
}
|
|
121
|
+
disposerA() // disposer 必须可安全调用(幂等清理不抛)
|
|
122
|
+
|
|
123
|
+
// apply ②:无 slots(护栏未声明该服务)——折返不炸,照样给出 disposer
|
|
124
|
+
const disposerB = exportsSurface.apply({ logger: { warn() {} } })
|
|
125
|
+
if (typeof disposerB !== 'function') fail('apply 无 slots 时也应返回 disposer(护栏折返语义)')
|
|
126
|
+
disposerB()
|
|
127
|
+
|
|
128
|
+
console.log('smoke 通过:client.js 编译 ✓ ModuleLoader 截获 ✓ 导出面 { inject, apply } ✓ slots 注入与 disposer ✓')
|