dsh-speak 1.8.0 → 1.8.2
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/LICENSE +21 -21
- package/README.md +529 -458
- package/README.zh-CN.md +485 -430
- package/adapters/dsh/install.ps1 +106 -81
- package/adapters/dsh/speech-hook.js +702 -614
- package/client/client.js +404 -357
- package/cordis.patch.yml +29 -5
- package/docs/DESIGN.md +106 -20
- package/docs/DESIGN.zh-CN.md +86 -19
- package/engine/speak.ps1 +214 -214
- package/engine/speak.sh +0 -0
- package/engine/speech-prompt.ps1 +24 -24
- package/engine/speech-summary.ps1 +27 -27
- package/package.json +77 -81
package/cordis.patch.yml
CHANGED
|
@@ -1,5 +1,29 @@
|
|
|
1
|
-
# dsh-speak bundle patch: auto-register the speech-hook plugin when this package
|
|
2
|
-
# is used as a DSH bundle (declared in dsh.profile.bundles).
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
1
|
+
# dsh-speak bundle patch: auto-register the speech-hook plugin when this package
|
|
2
|
+
# is used as a DSH bundle (declared in dsh.profile.bundles).
|
|
3
|
+
#
|
|
4
|
+
# The entry `id` is not decoration: since DSH 0.1.7 the settings service projects
|
|
5
|
+
# each Loader entry's own exported `Config` into the settings UI, so the entry id
|
|
6
|
+
# IS the settings namespace (the key under which this plugin's options are stored
|
|
7
|
+
# in the profile patch, and what the browser half binds). `dsh-speak` is the id
|
|
8
|
+
# this package documents; rows left over from 1.8.x name the same entry
|
|
9
|
+
# `speech-hook`, and the browser half accepts either id, so an existing profile
|
|
10
|
+
# needs no edit.
|
|
11
|
+
#
|
|
12
|
+
# The TWO rows below are deliberate, and the shape matters:
|
|
13
|
+
# * `insert` provides the entry (nothing else in a composition does),
|
|
14
|
+
# * the top-level row carries the editable `config`.
|
|
15
|
+
# DSH's config editor rewrites a `config` in place only on a TOP-LEVEL row
|
|
16
|
+
# (`config-editor.edit()` looks for a non-insert row with the same id+name and
|
|
17
|
+
# `setIn`s its config there; its `inherited()` helper strips `config` from exactly
|
|
18
|
+
# those rows). A `config` nested INSIDE the `insert` row — the shape 1.8.x used and
|
|
19
|
+
# the obvious-looking one — is not addressable that way: the editor appends a new
|
|
20
|
+
# top-level row and then rolls that write back, so the settings page answers
|
|
21
|
+
# `ok: true`, the running plugin obeys the change immediately, and the value
|
|
22
|
+
# silently reverts at the next boot. Shipping the top-level row makes settings
|
|
23
|
+
# persist from the first write (verified: UI edit → profile patch in ~0.3 s).
|
|
24
|
+
- insert:
|
|
25
|
+
- id: dsh-speak
|
|
26
|
+
name: dsh-speak
|
|
27
|
+
- id: dsh-speak
|
|
28
|
+
name: dsh-speak
|
|
29
|
+
config: {}
|
package/docs/DESIGN.md
CHANGED
|
@@ -140,20 +140,31 @@ no "reply finished" hook, so the plugin observes the session event stream:
|
|
|
140
140
|
- **Optional event announcements** (1.6.0, all off by default): `turn/end`,
|
|
141
141
|
`command/done`, `goal/change`, `tool/result` (on error), and `todo/write` each
|
|
142
142
|
have an independent toggle and announce a fixed phrase on fire (see §5).
|
|
143
|
-
- **Settings
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
the
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
143
|
+
- **Settings form** (1.6.0, reworked in 1.8.2 for DSH 0.1.7): the plugin exports a
|
|
144
|
+
static `Config` schema (`module.exports.Config`, every field `.volatile()`), and
|
|
145
|
+
the settings service projects it into the settings UI. The entry's **Loader id
|
|
146
|
+
is the settings namespace** (`dsh-speak` in `cordis.patch.yml`), so there is
|
|
147
|
+
nothing to register: `ctx.settings.describe()` reads the schema off the active
|
|
148
|
+
entry, and the browser binds the same id through `ctx.configForms`. A write
|
|
149
|
+
commits the new values into the running fiber's config *references* and emits
|
|
150
|
+
`loader/volatile-update`; the plugin re-derives `cfg` there, which is what makes
|
|
151
|
+
an edit audible without a restart. Resolution stays schema default → patch
|
|
152
|
+
`config` → UI user layer.
|
|
153
|
+
The schema must exist at module load: the Loader reads
|
|
154
|
+
`module.exports.Config` right after importing the plugin, before any context
|
|
155
|
+
exists, so it is built with `createRequire(__filename)` instead of the old
|
|
156
|
+
`ctx.baseUrl` fallback. When the schemastery peer cannot be resolved, `Config`
|
|
157
|
+
is `undefined`, the entry simply owns no settings page, and the plugin keeps
|
|
158
|
+
running on the composed patch config — graceful degradation with no version
|
|
159
|
+
check. `settings.configure({ auto: false }, ctx.fiber)` opts the entry out of
|
|
160
|
+
the shell's automatically generated form, because the browser half ships a
|
|
161
|
+
hand-written page.
|
|
162
|
+
The plugin still never imports `@deepseek-ai/dsh-settings`: DSH 0.1.2-alpha.1
|
|
163
|
+
deleted the `installSettingsSection` / `settingsNamespace` helpers (referencing
|
|
164
|
+
them is fatal — a missing named export is a module-evaluation error, and the old
|
|
165
|
+
lazy call threw `settingsNamespace is not a function` inside a timer callback,
|
|
166
|
+
which crashed the host), and 0.1.7 then deleted the
|
|
167
|
+
`settings.register(ns, schema, { base })` service API that had replaced them.
|
|
157
168
|
|
|
158
169
|
Registration snippet (also automated by `install.ps1`; npm installs use the bare
|
|
159
170
|
package name `'dsh-speak'` — this is the file-install path):
|
|
@@ -161,14 +172,60 @@ package name `'dsh-speak'` — this is the file-install path):
|
|
|
161
172
|
```yaml
|
|
162
173
|
# ~/.dsh/profiles/web/cordis.patch.yml
|
|
163
174
|
- insert:
|
|
164
|
-
- id:
|
|
175
|
+
- id: dsh-speak
|
|
165
176
|
# replace <your-username> with your Windows username
|
|
166
177
|
name: 'file:///C:/Users/<your-username>/.dsh/profiles/web/plugins/speech-hook.js'
|
|
178
|
+
- id: dsh-speak
|
|
179
|
+
name: 'file:///C:/Users/<your-username>/.dsh/profiles/web/plugins/speech-hook.js'
|
|
180
|
+
config: {}
|
|
167
181
|
```
|
|
168
182
|
|
|
169
183
|
> Node's ESM loader does not accept Windows absolute paths as plugin names — the
|
|
170
184
|
> `file:///C:/...` URL form is required.
|
|
171
185
|
|
|
186
|
+
> **Two rows, and the shape is load-bearing.** The `insert` row provides the entry;
|
|
187
|
+
> the TOP-LEVEL row is the one the settings page persists into.
|
|
188
|
+
> `config-editor.edit()` rewrites a `config` in place only after finding a
|
|
189
|
+
> top-level, non-insert row with the same id+name (`document.setIn([index,
|
|
190
|
+
> "config"], …)`), and its `inherited()` helper strips `config` from exactly those
|
|
191
|
+
> rows to compute the base layer. A `config` nested inside the `insert` row — the
|
|
192
|
+
> shape 1.8.x profiles carry, and the obvious-looking one — is not addressable that
|
|
193
|
+
> way: the editor appends a fresh top-level row, that generation's
|
|
194
|
+
> `reconcileProfilePatches` runs, and the write is then rolled back. Observed
|
|
195
|
+
> symptom: the settings page answers `ok: true`, the running plugin obeys the edit
|
|
196
|
+
> immediately (the volatile refs are committed), the patch file flickers
|
|
197
|
+
> `3874 → 4345 → 3874` bytes, and the option silently reverts on the next boot.
|
|
198
|
+
> With the two-row shape the same edit lands in-place in ~0.3 s
|
|
199
|
+
> (`scripts/settings-ui-check.py` asserts the persistence).
|
|
200
|
+
|
|
201
|
+
> **Entry id = settings namespace.** Before 1.8.2 the id was `speech-hook` and the
|
|
202
|
+
> namespace was registered in code, so the two were independent; every 1.8.x
|
|
203
|
+
> profile patch therefore still says `speech-hook`. The browser half accepts both
|
|
204
|
+
> ids (`SETTINGS_NAMESPACES`) and binds whichever the Host serves, so upgrading
|
|
205
|
+
> needs no profile edit — only the *documented* id changed, and with it the key a
|
|
206
|
+
> fresh install stores its options under.
|
|
207
|
+
|
|
208
|
+
> **One entry per profile.** Two rows mounting this plugin (usually the bundle
|
|
209
|
+
> entry plus a leftover hand-written insert) would run two speech queues and
|
|
210
|
+
> double every announcement. `apply()` claims the process with a
|
|
211
|
+
> `Symbol.for('dsh-speak.active')` flag on `globalThis` (not a module variable: two
|
|
212
|
+
> rows may spell the same file differently, and Node would then evaluate the module
|
|
213
|
+
> twice, each copy seeing its own flag); the extra instance logs and stays inert,
|
|
214
|
+
> and the claim is released when the owning fiber disposes so a surviving row can
|
|
215
|
+
> take over.
|
|
216
|
+
>
|
|
217
|
+
> The guard covers **speech only**, and a duplicate entry id has a second,
|
|
218
|
+
> nastier consequence this half cannot fix: `config-editor.entries()` keeps only
|
|
219
|
+
> uniquely-ided entries (`counts.get(entry.options.id) === 1`), and its sanity
|
|
220
|
+
> check composes the candidate patches and compares `find(row => row.id === …)`
|
|
221
|
+
> — the FIRST row for that id — against the value it is writing. With the id
|
|
222
|
+
> present twice that comparison can never match, so **every settings write is
|
|
223
|
+
> refused** with `settings/rejected: Configuration for "dsh-speak" is overridden by
|
|
224
|
+
> a home patch or command-line overlay`, while speech keeps working normally. That
|
|
225
|
+
> is why the installer, the two READMEs and this note all insist on exactly one
|
|
226
|
+
> registration path (bundle entry **or** hand-written rows, never both), and why
|
|
227
|
+
> the log line for the duplicate names the settings symptom too.
|
|
228
|
+
|
|
172
229
|
### 3.4 DSH browser half — `client/client.js`
|
|
173
230
|
|
|
174
231
|
A DSH client bundle (`window.__ModuleLoader__.load({ id: 'dsh-speak', factory })`)
|
|
@@ -191,8 +248,13 @@ that registers two pieces of UI:
|
|
|
191
248
|
(Button / DisclosureRow / Input; Toggle / Options / SettingInput helpers). Every
|
|
192
249
|
option (master switch, automatic speech, queueAllMessages, Markdown cleaning,
|
|
193
250
|
code blocks, maxChars, longTextMode, fixed prompt, approvals/questions, the five
|
|
194
|
-
optional events) is read/written through `
|
|
195
|
-
|
|
251
|
+
optional events) is read/written through `ctx.configForms.get(entryId)` — a
|
|
252
|
+
snapshot (`status` / `value` / `writable`) plus `subscribe` / `set` / `unset`,
|
|
253
|
+
the DSH 0.1.7 replacement for `settingsScope.bind({ namespace })`. The form is
|
|
254
|
+
bound inside `ctx.configForms.whileServed(SETTINGS_NAMESPACES, …)`, so the page
|
|
255
|
+
exists exactly while the Host actually serves one of those namespaces (and the
|
|
256
|
+
bound `entryId` is the one it serves); `dsh-speak` is the documented id and
|
|
257
|
+
`speech-hook` the pre-1.8.2 one.
|
|
196
258
|
|
|
197
259
|
- The package declares its browser half via `package.json`
|
|
198
260
|
`dsh.client: { platform: 'web' }` + `exports['./client']`; DSH's client-modules
|
|
@@ -287,8 +349,10 @@ config:
|
|
|
287
349
|
|
|
288
350
|
Resolution order: schema default → patch `config` (base) → UI user layer. The
|
|
289
351
|
browser dsh-speak settings page (`client/client.js`) and the patch YAML read/write
|
|
290
|
-
the same
|
|
291
|
-
|
|
352
|
+
the same profile patch: a UI write is a field operation (`set` / `unset` path op)
|
|
353
|
+
the settings service persists into the entry's `config` block. Platform note:
|
|
354
|
+
`maxChars` defaults to 0 on macOS (`say` has no ceiling) and 300 on Windows (SAPI
|
|
355
|
+
safe limit).
|
|
292
356
|
|
|
293
357
|
Full configuration guide: the README's Configuration section.
|
|
294
358
|
|
|
@@ -351,6 +415,28 @@ dependencies in the profile). This repository is prepared for that path:
|
|
|
351
415
|
Because the engine rides inside the npm package, `dsh plugin --profile web add
|
|
352
416
|
dsh-speak` alone is sufficient — no separate copying step.
|
|
353
417
|
|
|
418
|
+
### Host requirement (`engines.dsh`)
|
|
419
|
+
|
|
420
|
+
`package.json` declares `engines.dsh: >=0.1.7-rc.2`. That one field is what
|
|
421
|
+
dsh-market reads to label the catalog card (`DSH >=0.1.7-rc.2`) and to decide
|
|
422
|
+
whether the plugin survives its "compatible with current DSH" filter:
|
|
423
|
+
|
|
424
|
+
- the facts come from the package's npm `latest` manifest
|
|
425
|
+
(`{registry}/<pkg>/latest`, cached ~24 h) — the catalog YAML in
|
|
426
|
+
`awesome-dsh-plugin` has no host field, so a PR there cannot declare this;
|
|
427
|
+
- an `engines.dsh` value is an `engine` declaration. Lockstep `@deepseek-ai/dsh*`
|
|
428
|
+
**peers** count too, but non-lockstep host packages are skipped on purpose
|
|
429
|
+
(`@deepseek-ai/schemastery`, `@deepseek-ai/cordis` — the schemastery peer above
|
|
430
|
+
therefore declares nothing about the DSH version);
|
|
431
|
+
- all declarations are conjunctive and compared prerelease-aware, so
|
|
432
|
+
`>=0.1.7-rc.2` matches a `0.1.7-rc.2` host; a missing declaration shows up as
|
|
433
|
+
"host requirement undeclared", never as "incompatible";
|
|
434
|
+
- npm itself only enforces `engines.node` / `engines.npm`, so this key never
|
|
435
|
+
blocks an install — it is marketplace metadata;
|
|
436
|
+
- move the floor only after a release has been verified against the new host, and
|
|
437
|
+
update both READMEs with it: `scripts/test-manifest.js` asserts the same string
|
|
438
|
+
appears in `package.json`, `README.md` and `README.zh-CN.md`.
|
|
439
|
+
|
|
354
440
|
### Publish steps (maintainer)
|
|
355
441
|
|
|
356
442
|
```powershell
|
|
@@ -370,7 +456,7 @@ npm publish # publishConfig.registry pins
|
|
|
370
456
|
dsh plugin --profile web add dsh-speak
|
|
371
457
|
# then register in ~/.dsh/profiles/web/cordis.patch.yml:
|
|
372
458
|
# - insert:
|
|
373
|
-
# - id:
|
|
459
|
+
# - id: dsh-speak
|
|
374
460
|
# name: 'dsh-speak'
|
|
375
461
|
# restart the DSH web app
|
|
376
462
|
```
|
package/docs/DESIGN.zh-CN.md
CHANGED
|
@@ -122,17 +122,26 @@ Agent 工具会跑长任务(构建、测试、迁移、批量修改),而
|
|
|
122
122
|
事件;开 = 每条 assistant 消息立即入队朗读(中间消息也读)。
|
|
123
123
|
- **可选事件播报**(1.6.0,默认全关):`turn/end`、`command/done`、
|
|
124
124
|
`goal/change`、`tool/result`(出错时)、`todo/write` 各自独立开关(见 §5)。
|
|
125
|
-
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
`
|
|
130
|
-
|
|
131
|
-
`
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
125
|
+
- **设置表单**(1.6.0;1.8.2 为 DSH 0.1.7 重做):插件导出一份静态 `Config`
|
|
126
|
+
schema(`module.exports.Config`,每个字段都 `.volatile()`),由 settings 服务
|
|
127
|
+
投影成设置界面。条目的 **Loader id 就是 settings namespace**
|
|
128
|
+
(`cordis.patch.yml` 里的 `dsh-speak`),所以没有任何需要注册的东西:
|
|
129
|
+
`ctx.settings.describe()` 直接从已激活条目上读 schema,浏览器端用
|
|
130
|
+
`ctx.configForms` 绑定同一个 id。写入会把新值提交进运行中 fiber 的 config
|
|
131
|
+
**引用**并发出 `loader/volatile-update`,插件在那里重新解析 `cfg`——这就是
|
|
132
|
+
"改设置无需重启即刻生效"的实现。解析顺序仍是 schema 默认 → patch `config` →
|
|
133
|
+
UI 用户设置。
|
|
134
|
+
schema 必须在模块加载时就存在:Loader 在 import 之后、任何 context 存在之前
|
|
135
|
+
就要读 `module.exports.Config`,所以它用 `createRequire(__filename)` 构建,而
|
|
136
|
+
不是旧的 `ctx.baseUrl` 回退。schemastery peer 解析不到时 `Config` 为
|
|
137
|
+
`undefined`,条目就没有设置页,插件继续按组合层 patch config 工作——优雅退化,
|
|
138
|
+
无需版本判断。`settings.configure({ auto: false }, ctx.fiber)` 让条目退出 shell
|
|
139
|
+
的自动生成表单(浏览器端自带手写页面)。
|
|
140
|
+
插件仍然**不 import** `@deepseek-ai/dsh-settings`:DSH 0.1.2-alpha.1 删除了
|
|
141
|
+
`installSettingsSection` / `settingsNamespace` 两个辅助导出(引用它们是致命的
|
|
142
|
+
——缺失的具名导出会在模块求值期直接报错;旧代码在 timer 回调里懒调用,抛
|
|
143
|
+
`settingsNamespace is not a function` 把宿主打崩),0.1.7 又删掉了接替它们的
|
|
144
|
+
`settings.register(ns, schema, { base })` 服务 API。
|
|
136
145
|
|
|
137
146
|
注册片段(`install.ps1` 也会自动完成;npm 安装用裸包名 `'dsh-speak'` 即可,
|
|
138
147
|
这是文件安装方式用的路径):
|
|
@@ -140,14 +149,48 @@ Agent 工具会跑长任务(构建、测试、迁移、批量修改),而
|
|
|
140
149
|
```yaml
|
|
141
150
|
# ~/.dsh/profiles/web/cordis.patch.yml
|
|
142
151
|
- insert:
|
|
143
|
-
- id:
|
|
152
|
+
- id: dsh-speak
|
|
144
153
|
# 把 <your-username> 换成你的 Windows 用户名
|
|
145
154
|
name: 'file:///C:/Users/<your-username>/.dsh/profiles/web/plugins/speech-hook.js'
|
|
155
|
+
- id: dsh-speak
|
|
156
|
+
name: 'file:///C:/Users/<your-username>/.dsh/profiles/web/plugins/speech-hook.js'
|
|
157
|
+
config: {}
|
|
146
158
|
```
|
|
147
159
|
|
|
148
160
|
> Node 的 ESM 加载器不接受 Windows 绝对路径作为插件名——必须用
|
|
149
161
|
> `file:///C:/...` URL 形式。
|
|
150
162
|
|
|
163
|
+
> **必须两行,而且这个形状是承重的。** `insert` 行负责提供条目,**顶层行**才是设置页
|
|
164
|
+
> 写入的目标:`config-editor.edit()` 只有找到同 id+name 的顶层非 insert 行,才会用
|
|
165
|
+
> `document.setIn([index, "config"], …)` 就地改写;它的 `inherited()` 也是从这些行里
|
|
166
|
+
> 剥掉 `config` 来算 base 层。把 `config` 嵌在 `insert` 行里(1.8.x 的 profile 就是
|
|
167
|
+
> 这个形状,看起来也最自然)则完全不可寻址:编辑器会追加一个新的顶层行,这一代
|
|
168
|
+
> `reconcileProfilePatches` 跑完,然后这次写入被回滚。实测症状:设置页返回
|
|
169
|
+
> `ok: true`、运行中的插件立刻生效(volatile 引用已提交)、patch 文件在
|
|
170
|
+
> 3874 → 4345 → 3874 字节之间闪一下,然后选项在下次启动时悄悄变回去。改成两行后,
|
|
171
|
+
> 同样的修改约 0.3 秒就地落盘(`scripts/settings-ui-check.py` 会断言这一点)。
|
|
172
|
+
|
|
173
|
+
> **条目 id = settings namespace。** 1.8.2 之前条目 id 是 `speech-hook`,而
|
|
174
|
+
> namespace 是在代码里注册的,两者互不相干;所以每个 1.8.x 的 profile patch
|
|
175
|
+
> 写的都是 `speech-hook`。浏览器端两个 id 都认(`SETTINGS_NAMESPACES`),绑定
|
|
176
|
+
> Host 实际服务的那个,因此升级**不需要改 profile**——变的只是文档里写的 id,
|
|
177
|
+
> 以及全新安装时选项存放的 key。
|
|
178
|
+
|
|
179
|
+
> **一个 profile 只能有一个条目。** 同一插件挂两次(通常是 bundle 条目 + 手写 insert
|
|
180
|
+
> 残留)会跑两个语音队列,每条播报都念两遍。`apply()` 用
|
|
181
|
+
> `Symbol.for('dsh-speak.active')` 挂在 `globalThis` 上占位(不用模块变量:同一个文件
|
|
182
|
+
> 可能被写成两种说明符,Node 会求值两次,各自看到自己的标志),多余的实例只写日志、
|
|
183
|
+
> 不挂载;拥有者 fiber 销毁时会释放占位,让活着的行接管。
|
|
184
|
+
>
|
|
185
|
+
> 这个守卫**只保语音**。重复的条目 id 还有一个这一半修不了的后果:
|
|
186
|
+
> `config-editor.entries()` 只保留 id 唯一的条目(`counts.get(id) === 1`),而它的
|
|
187
|
+
> 自检会把候选 patch 组合起来、拿 `find(row => row.id === …)`(**第一个**同 id 行)
|
|
188
|
+
> 和它正要写入的值比较——id 出现两次时这个比较永远不相等,于是**每一次设置写入都被拒**:
|
|
189
|
+
> `settings/rejected: Configuration for "dsh-speak" is overridden by a home patch or
|
|
190
|
+
> command-line overlay`,而语音照常工作。所以安装脚本、两份 README 和这里都强调
|
|
191
|
+
> **只能有一种注册方式**(bundle 条目 **或** 手写行,不能两者都有),重复实例的日志行
|
|
192
|
+
> 里也会点名这个设置侧的症状。
|
|
193
|
+
|
|
151
194
|
### 3.4 DSH 浏览器端 — `client/client.js`
|
|
152
195
|
|
|
153
196
|
一个 DSH client bundle(`window.__ModuleLoader__.load({ id: 'dsh-speak',
|
|
@@ -167,8 +210,12 @@ factory })`),注册两条 UI:
|
|
|
167
210
|
用 `@deepseek-ai/dsh-client-ui-primitives` 的 Button/DisclosureRow/Input
|
|
168
211
|
绘制(Toggle/Options/SettingInput 组件),所有配置项(总开关、自动朗读、
|
|
169
212
|
queueAllMessages、Markdown 清洗、代码块、maxChars、longTextMode、固定提示语、
|
|
170
|
-
审批/提问、5 类可选事件)都通过 `
|
|
171
|
-
|
|
213
|
+
审批/提问、5 类可选事件)都通过 `ctx.configForms.get(entryId)` 读写——一份快照
|
|
214
|
+
(`status` / `value` / `writable`)加 `subscribe` / `set` / `unset`,即 DSH
|
|
215
|
+
0.1.7 用来取代 `settingsScope.bind({ namespace })` 的接口。表单在
|
|
216
|
+
`ctx.configForms.whileServed(SETTINGS_NAMESPACES, …)` 里绑定,所以页面只在 Host
|
|
217
|
+
确实服务其中之一时存在(绑定的 `entryId` 就是它服务的那个);`dsh-speak` 是
|
|
218
|
+
文档里的 id,`speech-hook` 是 1.8.2 之前那个。
|
|
172
219
|
|
|
173
220
|
- 包通过 `package.json` 的 `dsh.client: { platform: 'web' }` +
|
|
174
221
|
`exports['./client']` 声明浏览器端;DSH 的 client-modules 扫描到后自动加载。
|
|
@@ -259,8 +306,10 @@ config:
|
|
|
259
306
|
```
|
|
260
307
|
|
|
261
308
|
配置解析顺序:schema 默认值 → patch `config`(base)→ UI 用户设置(user 层)。
|
|
262
|
-
浏览器端 dsh-speak 设置页(`client/client.js`)与 patch YAML
|
|
263
|
-
|
|
309
|
+
浏览器端 dsh-speak 设置页(`client/client.js`)与 patch YAML 读写同一份 profile
|
|
310
|
+
patch:UI 写入是一组字段操作(`set` / `unset` path op),由 settings 服务落进该
|
|
311
|
+
条目的 `config` 块。平台差异:`maxChars` macOS 默认 0(`say` 无上限)、Windows
|
|
312
|
+
默认 300。
|
|
264
313
|
|
|
265
314
|
完整配置指南见 README 的"配置"一节。
|
|
266
315
|
|
|
@@ -306,8 +355,8 @@ DSH 的插件机制基于 Cordis,官方安装树外插件的路径是
|
|
|
306
355
|
- `package.json` — `name: dsh-speak`,`main: adapters/dsh/speech-hook.js`,
|
|
307
356
|
`files` 白名单精确列出发布内容(插件、`engine/*.ps1`、`install.ps1`、文档、
|
|
308
357
|
LICENSE)。`prepublishOnly` 会对插件跑 `node --check`。
|
|
309
|
-
- 插件入口就是文件安装已用的同一个 CJS 模块(`module.exports = { apply(ctx) }
|
|
310
|
-
|
|
358
|
+
- 插件入口就是文件安装已用的同一个 CJS 模块(`module.exports = { apply(ctx) }`,
|
|
359
|
+
另加 1.8.2 起用于设置表单的静态 `Config` 导出)——发布**不需要改任何代码**。
|
|
311
360
|
|
|
312
361
|
### 引擎解析(npm 安装 vs 文件安装)
|
|
313
362
|
|
|
@@ -321,6 +370,24 @@ DSH 的插件机制基于 Cordis,官方安装树外插件的路径是
|
|
|
321
370
|
因为引擎随 npm 包分发,用户只需 `dsh plugin --profile web add dsh-speak`
|
|
322
371
|
一条命令,无需额外拷贝。
|
|
323
372
|
|
|
373
|
+
### 宿主要求(`engines.dsh`)
|
|
374
|
+
|
|
375
|
+
`package.json` 声明了 `engines.dsh: >=0.1.7-rc.2`。dsh-market 只读这一个字段来标注
|
|
376
|
+
目录卡片(`DSH >=0.1.7-rc.2`)并决定插件能否通过「适配当前 DSH」筛选:
|
|
377
|
+
|
|
378
|
+
- 数据源是该包 npm `latest` manifest(`{registry}/<pkg>/latest`,缓存约 24 小时)
|
|
379
|
+
——`awesome-dsh-plugin` 里那份目录 YAML 没有宿主字段,往那里提 PR 声明不了;
|
|
380
|
+
- `engines.dsh` 属于 `engine` 声明;同版本线的 `@deepseek-ai/dsh*` **peer** 也算,
|
|
381
|
+
但非同一版本线的宿主包会被有意跳过(`@deepseek-ai/schemastery`、
|
|
382
|
+
`@deepseek-ai/cordis`)——所以上面那条 schemastery peer 不构成任何 DSH 版本声明;
|
|
383
|
+
- 所有声明取交集,比较时带 prerelease 语义,`>=0.1.7-rc.2` 能匹配 `0.1.7-rc.2`
|
|
384
|
+
宿主;完全没声明只会显示「未声明宿主要求」,不会显示成不兼容;
|
|
385
|
+
- npm 本身只强制 `engines.node` / `engines.npm`,这个键不会挡住安装,它只是市场
|
|
386
|
+
元数据;
|
|
387
|
+
- 只有在某个宿主版本上实测通过后才移动下限,并同步两份 README:
|
|
388
|
+
`scripts/test-manifest.js` 断言同一个字符串同时出现在 `package.json`、
|
|
389
|
+
`README.md`、`README.zh-CN.md`。
|
|
390
|
+
|
|
324
391
|
### 发布步骤(维护者)
|
|
325
392
|
|
|
326
393
|
```powershell
|
|
@@ -339,7 +406,7 @@ npm publish # publishConfig.registry 已
|
|
|
339
406
|
dsh plugin --profile web add dsh-speak
|
|
340
407
|
# 然后在 ~/.dsh/profiles/web/cordis.patch.yml 注册:
|
|
341
408
|
# - insert:
|
|
342
|
-
# - id:
|
|
409
|
+
# - id: dsh-speak
|
|
343
410
|
# name: 'dsh-speak'
|
|
344
411
|
# 重启 DSH web 应用
|
|
345
412
|
```
|