@dsh-plugin/dsh-loader 1.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/README.md +226 -0
- package/README.zh-CN.md +202 -0
- package/bin/dshloader.mjs +42 -0
- package/cordis.patch.yml +8 -0
- package/package.json +59 -0
- package/src/adapters/dsh-1-x.js +236 -0
- package/src/adapters/index.js +59 -0
- package/src/api.js +56 -0
- package/src/client.js +330 -0
- package/src/index.js +105 -0
- package/src/registry.js +242 -0
- package/src/services/services.js +28 -0
- package/src/services/settings.js +174 -0
- package/src/services/web.js +71 -0
- package/src/setup.mjs +125 -0
- package/src/stable/agent.d.ts +1 -0
- package/src/stable/agent.js +2 -0
- package/src/stable/llm.d.ts +1 -0
- package/src/stable/llm.js +2 -0
- package/src/stable/runtime.d.ts +1 -0
- package/src/stable/runtime.js +5 -0
- package/src/stable/schema-form.d.ts +1 -0
- package/src/stable/schema-form.js +2 -0
- package/src/stable/settings.d.ts +1 -0
- package/src/stable/settings.js +2 -0
- package/src/stable/tools.d.ts +1 -0
- package/src/stable/tools.js +5 -0
- package/src/stable/ui-primitives.d.ts +4 -0
- package/src/stable/ui-primitives.js +6 -0
- package/src/stable/ui-settings.d.ts +6 -0
- package/src/stable/ui-settings.js +4 -0
- package/src/stable/ui-slots.d.ts +1 -0
- package/src/stable/ui-slots.js +2 -0
- package/src/stable/web-react.d.ts +1 -0
- package/src/stable/web-react.js +2 -0
- package/src/version.js +5 -0
package/README.md
ADDED
|
@@ -0,0 +1,226 @@
|
|
|
1
|
+
# dshloader
|
|
2
|
+
|
|
3
|
+
[English](#english) | [中文](README.zh-CN.md)
|
|
4
|
+
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
## English
|
|
8
|
+
|
|
9
|
+
A runtime compatibility shim for **dsh** (DeepSeek Harness) cordis bundle
|
|
10
|
+
plugins. dshloader decouples third-party plugins from dsh's internal service
|
|
11
|
+
names, module paths, package names, and RPC details through a version-aware
|
|
12
|
+
**adapter registry**, so that when dsh upgrades and breaks internal APIs, you
|
|
13
|
+
only upgrade dshloader — plugins keep working unchanged.
|
|
14
|
+
|
|
15
|
+
### Why
|
|
16
|
+
|
|
17
|
+
dsh is moving fast and its internal surface changes between releases:
|
|
18
|
+
|
|
19
|
+
- `httpServer` was renamed to `webServer` — old plugins that inject
|
|
20
|
+
`httpServer` hang forever.
|
|
21
|
+
- Deep source imports like
|
|
22
|
+
`@deepseek-ai/dsh-client-runtime/src/client/sessions/context-provenance.ts`
|
|
23
|
+
break when dsh ships no `src/`.
|
|
24
|
+
- Client UI packages like `@deepseek-ai/dsh-client-ui-primitives` could be
|
|
25
|
+
renamed in future dsh versions, breaking every plugin that imports them
|
|
26
|
+
directly.
|
|
27
|
+
- The official `dsh-host-apiproxy` hardcodes a settings namespace whitelist,
|
|
28
|
+
so third-party settings cards never appear in the Web UI.
|
|
29
|
+
|
|
30
|
+
dshloader absorbs these (and future) breaks behind a **stable API**:
|
|
31
|
+
`ctx.dshLoader` on the host, `window.__dshLoader__` in the browser, and
|
|
32
|
+
`@dsh-plugin/dsh-loader/*` stable subpaths for package imports.
|
|
33
|
+
|
|
34
|
+
### Quick start
|
|
35
|
+
|
|
36
|
+
#### 1. Install dshloader into a profile
|
|
37
|
+
|
|
38
|
+
```sh
|
|
39
|
+
dsh plugin --profile <name> add /path/to/dshloader
|
|
40
|
+
# or
|
|
41
|
+
DSH_HOME=~/.dsh npx dshloader setup <name>
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
#### 2. Plugin `package.json` — only depend on dshloader
|
|
45
|
+
|
|
46
|
+
```json
|
|
47
|
+
{
|
|
48
|
+
"dependencies": {
|
|
49
|
+
"@dsh-plugin/dsh-loader": "link:..."
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
> **Plugins must NOT declare any `@deepseek-ai/*` dependency.** All dsh
|
|
55
|
+
> packages are accessed through dshloader's stable subpaths.
|
|
56
|
+
|
|
57
|
+
#### 3. Host side — use `ctx.dshLoader`
|
|
58
|
+
|
|
59
|
+
```js
|
|
60
|
+
export const inject = ['dshLoader'];
|
|
61
|
+
|
|
62
|
+
export async function apply(ctx) {
|
|
63
|
+
// Settings: register a namespace
|
|
64
|
+
const scope = ctx.dshLoader.settings.register('my-plugin', schema);
|
|
65
|
+
|
|
66
|
+
// Web: register routes and WebSocket upgrades
|
|
67
|
+
ctx.dshLoader.web.get('/api/my-plugin/status', (req, res) => res.json({ ok: true }));
|
|
68
|
+
ctx.dshLoader.web.registerUpgrade({ path: '/ws/my-plugin', handler: fn });
|
|
69
|
+
|
|
70
|
+
// Services: read cordis services
|
|
71
|
+
const sessions = ctx.dshLoader.services.get('sessions');
|
|
72
|
+
}
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
#### 4. Import dsh packages via stable subpaths
|
|
76
|
+
|
|
77
|
+
```js
|
|
78
|
+
// Host packages
|
|
79
|
+
const { defineTool } = require('@dsh-plugin/dsh-loader/tools');
|
|
80
|
+
|
|
81
|
+
// Client UI packages (in client bundle source)
|
|
82
|
+
import { IconCloseFill14 } from '@dsh-plugin/dsh-loader/ui-primitives';
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
**Stable subpath → real dsh package mapping (dsh 1.x):**
|
|
86
|
+
|
|
87
|
+
| Stable subpath | Real dsh package |
|
|
88
|
+
|---|---|
|
|
89
|
+
| `@dsh-plugin/dsh-loader/tools` | `@deepseek-ai/dsh-tools` |
|
|
90
|
+
| `@dsh-plugin/dsh-loader/llm` | `@deepseek-ai/dsh-llm` |
|
|
91
|
+
| `@dsh-plugin/dsh-loader/agent` | `@deepseek-ai/dsh-agent` |
|
|
92
|
+
| `@dsh-plugin/dsh-loader/settings` | `@deepseek-ai/dsh-settings` |
|
|
93
|
+
| `@dsh-plugin/dsh-loader/ui-primitives` | `@deepseek-ai/dsh-client-ui-primitives` |
|
|
94
|
+
| `@dsh-plugin/dsh-loader/ui-slots` | `@deepseek-ai/dsh-client-ui-slots` |
|
|
95
|
+
| `@dsh-plugin/dsh-loader/ui-settings` | `@deepseek-ai/dsh-client-ui-settings/client` |
|
|
96
|
+
| `@dsh-plugin/dsh-loader/web-react` | `@deepseek-ai/dsh-client-web-react` |
|
|
97
|
+
| `@dsh-plugin/dsh-loader/schema-form` | `@deepseek-ai/dsh-client-schema-form` |
|
|
98
|
+
| `@dsh-plugin/dsh-loader/runtime` | `@deepseek-ai/dsh-client-runtime/client` |
|
|
99
|
+
|
|
100
|
+
When dsh renames a package, only the dshloader adapter changes — plugin
|
|
101
|
+
source and bundle stay the same.
|
|
102
|
+
|
|
103
|
+
#### 5. Client side — use `window.__dshLoader__`
|
|
104
|
+
|
|
105
|
+
```js
|
|
106
|
+
// Read cordis client services
|
|
107
|
+
const conv = window.__dshLoader__.services.get('conversation');
|
|
108
|
+
|
|
109
|
+
// Register a package alias at runtime (fallback)
|
|
110
|
+
window.__dshLoader__.registerPackageAlias('@old/pkg', '@new/pkg');
|
|
111
|
+
```
|
|
112
|
+
|
|
113
|
+
#### 6. Build config — mark stable subpaths as external
|
|
114
|
+
|
|
115
|
+
```ts
|
|
116
|
+
const CLIENT_EXTERNALS = [
|
|
117
|
+
'react', 'react/jsx-runtime', 'react-dom', 'react-dom/client', 'cordis',
|
|
118
|
+
'@dsh-plugin/dsh-loader/ui-primitives',
|
|
119
|
+
'@dsh-plugin/dsh-loader/ui-slots',
|
|
120
|
+
'@dsh-plugin/dsh-loader/ui-settings',
|
|
121
|
+
'@dsh-plugin/dsh-loader/web-react',
|
|
122
|
+
'@dsh-plugin/dsh-loader/schema-form',
|
|
123
|
+
'@dsh-plugin/dsh-loader/runtime',
|
|
124
|
+
]
|
|
125
|
+
```
|
|
126
|
+
|
|
127
|
+
### How it works
|
|
128
|
+
|
|
129
|
+
```
|
|
130
|
+
plugin ──▶ ctx.dshLoader.{settings,web,services} ──▶ dshloader adapter
|
|
131
|
+
│
|
|
132
|
+
▼
|
|
133
|
+
real dsh (current version)
|
|
134
|
+
|
|
135
|
+
plugin bundle ──▶ require('@dsh-plugin/dsh-loader/ui-primitives')
|
|
136
|
+
│
|
|
137
|
+
▼ (__ModuleLoader__ wrapper maps stable name)
|
|
138
|
+
require('@deepseek-ai/dsh-client-ui-primitives')
|
|
139
|
+
│
|
|
140
|
+
▼
|
|
141
|
+
dsh module table
|
|
142
|
+
```
|
|
143
|
+
|
|
144
|
+
1. **Version detection** reads `node_modules/@deepseek-ai/dsh/package.json`
|
|
145
|
+
(or `DSHLOADER_DSH_VERSION` for tests/override).
|
|
146
|
+
2. **AdapterRegistry** selects the best adapter for the detected version
|
|
147
|
+
(exact → range → nearest-low fallback → clear error).
|
|
148
|
+
3. The selected **adapter** registers service aliases, installs package-name
|
|
149
|
+
mapping hooks (host: `Module._resolveFilename`; client:
|
|
150
|
+
`__ModuleLoader__.load` wrapper), and (only when opted in) the settings
|
|
151
|
+
whitelist bypass bridge. All registrations use `ctx.reflect.provide` /
|
|
152
|
+
`ctx.effect`, so cordis auto-recycles them on fiber unload.
|
|
153
|
+
|
|
154
|
+
> **Load order does not matter.** cordis is reactive dependency injection:
|
|
155
|
+
> plugins declaring `inject: [...]` stay `PENDING` until the alias is
|
|
156
|
+
> provided, regardless of where dshloader sits in `cordis.patch.yml`.
|
|
157
|
+
|
|
158
|
+
### Settings whitelist bypass (`exposeAllNamespaces`)
|
|
159
|
+
|
|
160
|
+
By default dshloader **does not** bypass the official settings namespace
|
|
161
|
+
whitelist. Opt in explicitly:
|
|
162
|
+
|
|
163
|
+
- env: `DSHLOADER_EXPOSE_ALL_SETTINGS=1`
|
|
164
|
+
- profile `package.json`: `dsh.dshloader.exposeAllNamespaces: true`
|
|
165
|
+
|
|
166
|
+
> **Security trade-off**: enabling this removes the official default-deny
|
|
167
|
+
> boundary for browser settings access. Only enable it in profiles where you
|
|
168
|
+
> trust every installed plugin.
|
|
169
|
+
|
|
170
|
+
### CLI
|
|
171
|
+
|
|
172
|
+
```
|
|
173
|
+
dshloader setup <profile> Inject dshloader into a profile (dep + patch).
|
|
174
|
+
dshloader dump-config <profile> Run `dsh --profile <name> --dump-config`.
|
|
175
|
+
dshloader info [profile] Print loader version, detected dsh version,
|
|
176
|
+
selected adapter.
|
|
177
|
+
```
|
|
178
|
+
|
|
179
|
+
### Rollback / disable
|
|
180
|
+
|
|
181
|
+
- Disable per launch: `DSHLOADER_DISABLE=1 dsh web`
|
|
182
|
+
- Remove: `dsh plugin --profile <name> rm @dsh-plugin/dsh-loader`
|
|
183
|
+
|
|
184
|
+
### Project layout
|
|
185
|
+
|
|
186
|
+
```
|
|
187
|
+
src/
|
|
188
|
+
index.js host bundle entry (name / inject / apply)
|
|
189
|
+
client.js client bundle entry (immediately tier)
|
|
190
|
+
api.js DshLoaderHostAPI construction
|
|
191
|
+
registry.js AdapterRegistry + version detection
|
|
192
|
+
version.js loader version + log prefix
|
|
193
|
+
stable/ stable subpath re-exports (ui-primitives, tools, ...)
|
|
194
|
+
services/
|
|
195
|
+
settings.js settings stable API
|
|
196
|
+
web.js web stable API
|
|
197
|
+
services.js services stable API (get / alias)
|
|
198
|
+
adapters/
|
|
199
|
+
dsh-1-x.js dsh 1.x adapter
|
|
200
|
+
index.js adapter registration
|
|
201
|
+
setup.mjs profile injection + dump-config + info
|
|
202
|
+
bin/dshloader.mjs CLI entry
|
|
203
|
+
docs/
|
|
204
|
+
api.md full API reference (Chinese)
|
|
205
|
+
design.md design document (Chinese)
|
|
206
|
+
tests/ L1 (unit) / module (L2) / integration (L3)
|
|
207
|
+
examples/
|
|
208
|
+
sample-plugin/ minimal example plugin
|
|
209
|
+
dsh-aux-state/ example using ctx.dshLoader only
|
|
210
|
+
```
|
|
211
|
+
|
|
212
|
+
### Develop
|
|
213
|
+
|
|
214
|
+
```sh
|
|
215
|
+
pnpm install
|
|
216
|
+
npm test # all tests
|
|
217
|
+
npm run test:l1 # unit
|
|
218
|
+
npm run test:l2 # module
|
|
219
|
+
npm run test:l3 # integration
|
|
220
|
+
```
|
|
221
|
+
|
|
222
|
+
Node.js >= 18, `node --test`, no extra test framework.
|
|
223
|
+
|
|
224
|
+
### License
|
|
225
|
+
|
|
226
|
+
BSD-3-Clause
|
package/README.zh-CN.md
ADDED
|
@@ -0,0 +1,202 @@
|
|
|
1
|
+
# dshloader
|
|
2
|
+
|
|
3
|
+
[English](README.md) | [中文](#中文)
|
|
4
|
+
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
## 中文
|
|
8
|
+
|
|
9
|
+
**dsh**(DeepSeek Harness)cordis bundle 插件的运行时兼容层。dshloader 通过版本感知的**适配器注册表**,把第三方插件与 dsh 的内部服务名、模块路径、包名、RPC 细节解耦——dsh 升级改了内部 API 时,只需升级 dshloader,插件不用改。
|
|
10
|
+
|
|
11
|
+
### 为什么需要
|
|
12
|
+
|
|
13
|
+
dsh 迭代很快,内部 API 在版本间会变:
|
|
14
|
+
|
|
15
|
+
- `httpServer` 被重命名为 `webServer`——旧插件注入 `httpServer` 会永远挂起。
|
|
16
|
+
- 深层源码导入如
|
|
17
|
+
`@deepseek-ai/dsh-client-runtime/src/client/sessions/context-provenance.ts`
|
|
18
|
+
在 dsh 不再发布 `src/` 时直接报错。
|
|
19
|
+
- 客户端 UI 包如 `@deepseek-ai/dsh-client-ui-primitives` 在未来 dsh 版本中可能改名,直接 import 的插件全部会坏。
|
|
20
|
+
- 官方 `dsh-host-apiproxy` 硬编码了 settings namespace 白名单,第三方设置卡片无法出现在 Web UI 中。
|
|
21
|
+
|
|
22
|
+
dshloader 把这些(以及未来的)破坏性变更吸收到**稳定 API** 后面:host 侧的 `ctx.dshLoader`、浏览器侧的 `window.__dshLoader__`、以及包导入的 `@dsh-plugin/dsh-loader/*` 稳定 subpath。
|
|
23
|
+
|
|
24
|
+
### 快速上手
|
|
25
|
+
|
|
26
|
+
#### 1. 安装 dshloader 到 profile
|
|
27
|
+
|
|
28
|
+
```sh
|
|
29
|
+
dsh plugin --profile <name> add /path/to/dshloader
|
|
30
|
+
# 或
|
|
31
|
+
DSH_HOME=~/.dsh npx dshloader setup <name>
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
#### 2. 插件 `package.json`——只依赖 dshloader
|
|
35
|
+
|
|
36
|
+
```json
|
|
37
|
+
{
|
|
38
|
+
"dependencies": {
|
|
39
|
+
"@dsh-plugin/dsh-loader": "link:..."
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
> **插件不允许声明任何 `@deepseek-ai/*` 依赖。** 所有 dsh 包都通过 dshloader 的稳定 subpath 访问。
|
|
45
|
+
|
|
46
|
+
#### 3. Host 侧——用 `ctx.dshLoader`
|
|
47
|
+
|
|
48
|
+
```js
|
|
49
|
+
export const inject = ['dshLoader'];
|
|
50
|
+
|
|
51
|
+
export async function apply(ctx) {
|
|
52
|
+
// Settings:注册 namespace
|
|
53
|
+
const scope = ctx.dshLoader.settings.register('my-plugin', schema);
|
|
54
|
+
|
|
55
|
+
// Web:注册路由和 WebSocket upgrade
|
|
56
|
+
ctx.dshLoader.web.get('/api/my-plugin/status', (req, res) => res.json({ ok: true }));
|
|
57
|
+
ctx.dshLoader.web.registerUpgrade({ path: '/ws/my-plugin', handler: fn });
|
|
58
|
+
|
|
59
|
+
// Services:读取 cordis 服务
|
|
60
|
+
const sessions = ctx.dshLoader.services.get('sessions');
|
|
61
|
+
}
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
#### 4. 通过稳定 subpath 导入 dsh 包
|
|
65
|
+
|
|
66
|
+
```js
|
|
67
|
+
// Host 包
|
|
68
|
+
const { defineTool } = require('@dsh-plugin/dsh-loader/tools');
|
|
69
|
+
|
|
70
|
+
// Client UI 包(在 client bundle 源码中)
|
|
71
|
+
import { IconCloseFill14 } from '@dsh-plugin/dsh-loader/ui-primitives';
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
**稳定 subpath → dsh 真实包名映射(dsh 1.x):**
|
|
75
|
+
|
|
76
|
+
| 稳定 subpath | dsh 真实包名 |
|
|
77
|
+
|---|---|
|
|
78
|
+
| `@dsh-plugin/dsh-loader/tools` | `@deepseek-ai/dsh-tools` |
|
|
79
|
+
| `@dsh-plugin/dsh-loader/llm` | `@deepseek-ai/dsh-llm` |
|
|
80
|
+
| `@dsh-plugin/dsh-loader/agent` | `@deepseek-ai/dsh-agent` |
|
|
81
|
+
| `@dsh-plugin/dsh-loader/settings` | `@deepseek-ai/dsh-settings` |
|
|
82
|
+
| `@dsh-plugin/dsh-loader/ui-primitives` | `@deepseek-ai/dsh-client-ui-primitives` |
|
|
83
|
+
| `@dsh-plugin/dsh-loader/ui-slots` | `@deepseek-ai/dsh-client-ui-slots` |
|
|
84
|
+
| `@dsh-plugin/dsh-loader/ui-settings` | `@deepseek-ai/dsh-client-ui-settings/client` |
|
|
85
|
+
| `@dsh-plugin/dsh-loader/web-react` | `@deepseek-ai/dsh-client-web-react` |
|
|
86
|
+
| `@dsh-plugin/dsh-loader/schema-form` | `@deepseek-ai/dsh-client-schema-form` |
|
|
87
|
+
| `@dsh-plugin/dsh-loader/runtime` | `@deepseek-ai/dsh-client-runtime/client` |
|
|
88
|
+
|
|
89
|
+
dsh 改包名时,只需改 dshloader 适配器——插件源码和 bundle 不用动。
|
|
90
|
+
|
|
91
|
+
#### 5. Client 侧——用 `window.__dshLoader__`
|
|
92
|
+
|
|
93
|
+
```js
|
|
94
|
+
// 读取 cordis client 服务
|
|
95
|
+
const conv = window.__dshLoader__.services.get('conversation');
|
|
96
|
+
|
|
97
|
+
// 运行时注册包名别名(兜底用)
|
|
98
|
+
window.__dshLoader__.registerPackageAlias('@old/pkg', '@new/pkg');
|
|
99
|
+
```
|
|
100
|
+
|
|
101
|
+
#### 6. 构建配置——把稳定 subpath 加入 external
|
|
102
|
+
|
|
103
|
+
```ts
|
|
104
|
+
const CLIENT_EXTERNALS = [
|
|
105
|
+
'react', 'react/jsx-runtime', 'react-dom', 'react-dom/client', 'cordis',
|
|
106
|
+
'@dsh-plugin/dsh-loader/ui-primitives',
|
|
107
|
+
'@dsh-plugin/dsh-loader/ui-slots',
|
|
108
|
+
'@dsh-plugin/dsh-loader/ui-settings',
|
|
109
|
+
'@dsh-plugin/dsh-loader/web-react',
|
|
110
|
+
'@dsh-plugin/dsh-loader/schema-form',
|
|
111
|
+
'@dsh-plugin/dsh-loader/runtime',
|
|
112
|
+
]
|
|
113
|
+
```
|
|
114
|
+
|
|
115
|
+
### 工作原理
|
|
116
|
+
|
|
117
|
+
```
|
|
118
|
+
plugin ──▶ ctx.dshLoader.{settings,web,services} ──▶ dshloader 适配器
|
|
119
|
+
│
|
|
120
|
+
▼
|
|
121
|
+
真实 dsh(当前版本)
|
|
122
|
+
|
|
123
|
+
plugin bundle ──▶ require('@dsh-plugin/dsh-loader/ui-primitives')
|
|
124
|
+
│
|
|
125
|
+
▼(__ModuleLoader__ wrapper 映射稳定名)
|
|
126
|
+
require('@deepseek-ai/dsh-client-ui-primitives')
|
|
127
|
+
│
|
|
128
|
+
▼
|
|
129
|
+
dsh 模块表
|
|
130
|
+
```
|
|
131
|
+
|
|
132
|
+
1. **版本探测** 读取 `node_modules/@deepseek-ai/dsh/package.json`(或 `DSHLOADER_DSH_VERSION`)。
|
|
133
|
+
2. **适配器注册表** 选择最适合当前版本的适配器(精确 → 范围 → 最近低版本回退 → 报错)。
|
|
134
|
+
3. 选中的**适配器** 注册服务别名、安装包名映射钩子(host: `Module._resolveFilename`;client: `__ModuleLoader__.load` wrapper)、以及(仅在开启时)settings 白名单绕过桥接。所有注册通过 `ctx.reflect.provide` / `ctx.effect`,cordis 在 fiber 卸载时自动回收。
|
|
135
|
+
|
|
136
|
+
> **加载顺序无关。** cordis 是响应式依赖注入:声明了 `inject: [...]` 的插件会停在 `PENDING` 状态,直到依赖的服务出现,与 dshloader 在 `cordis.patch.yml` 中的位置无关。
|
|
137
|
+
|
|
138
|
+
### Settings 白名单绕过(`exposeAllNamespaces`)
|
|
139
|
+
|
|
140
|
+
默认**不绕过**官方 settings namespace 白名单。需要时显式开启:
|
|
141
|
+
|
|
142
|
+
- 环境变量:`DSHLOADER_EXPOSE_ALL_SETTINGS=1`
|
|
143
|
+
- profile `package.json`:`dsh.dshloader.exposeAllNamespaces: true`
|
|
144
|
+
|
|
145
|
+
> **安全权衡**:开启后会移除官方对浏览器 settings 访问的默认拒绝边界。仅在信任所有已安装插件的 profile 中开启。
|
|
146
|
+
|
|
147
|
+
### CLI
|
|
148
|
+
|
|
149
|
+
```
|
|
150
|
+
dshloader setup <profile> 注入 dshloader 到 profile(依赖 + patch)。
|
|
151
|
+
dshloader dump-config <profile> 运行 `dsh --profile <name> --dump-config`。
|
|
152
|
+
dshloader info [profile] 打印 loader 版本、探测到的 dsh 版本、选中的适配器。
|
|
153
|
+
```
|
|
154
|
+
|
|
155
|
+
### 回滚 / 禁用
|
|
156
|
+
|
|
157
|
+
- 单次启动禁用:`DSHLOADER_DISABLE=1 dsh web`
|
|
158
|
+
- 移除:`dsh plugin --profile <name> rm @dsh-plugin/dsh-loader`
|
|
159
|
+
|
|
160
|
+
### 项目结构
|
|
161
|
+
|
|
162
|
+
```
|
|
163
|
+
src/
|
|
164
|
+
index.js host bundle 入口(name / inject / apply)
|
|
165
|
+
client.js client bundle 入口(immediately tier)
|
|
166
|
+
api.js DshLoaderHostAPI 构造
|
|
167
|
+
registry.js 适配器注册表 + 版本探测
|
|
168
|
+
version.js loader 版本 + 日志前缀
|
|
169
|
+
stable/ 稳定 subpath re-export(ui-primitives、tools 等)
|
|
170
|
+
services/
|
|
171
|
+
settings.js settings 稳定 API
|
|
172
|
+
web.js web 稳定 API
|
|
173
|
+
services.js services 稳定 API(get / alias)
|
|
174
|
+
adapters/
|
|
175
|
+
dsh-1-x.js dsh 1.x 适配器
|
|
176
|
+
index.js 适配器注册
|
|
177
|
+
setup.mjs profile 注入 + dump-config + info
|
|
178
|
+
bin/dshloader.mjs CLI 入口
|
|
179
|
+
docs/
|
|
180
|
+
api.md 完整 API 参考
|
|
181
|
+
design.md 设计文档
|
|
182
|
+
tests/ L1(单元)/ module(L2)/ integration(L3)
|
|
183
|
+
examples/
|
|
184
|
+
sample-plugin/ 最小示例插件
|
|
185
|
+
dsh-aux-state/ 仅用 ctx.dshLoader 的示例
|
|
186
|
+
```
|
|
187
|
+
|
|
188
|
+
### 开发
|
|
189
|
+
|
|
190
|
+
```sh
|
|
191
|
+
pnpm install
|
|
192
|
+
npm test # 全部测试
|
|
193
|
+
npm run test:l1 # 单元测试
|
|
194
|
+
npm run test:l2 # 模块测试
|
|
195
|
+
npm run test:l3 # 集成测试
|
|
196
|
+
```
|
|
197
|
+
|
|
198
|
+
Node.js >= 18,`node --test`,无额外测试框架。
|
|
199
|
+
|
|
200
|
+
### 许可证
|
|
201
|
+
|
|
202
|
+
BSD-3-Clause
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
// dshloader CLI entry (M6). Subcommands: setup, dump-config, info.
|
|
3
|
+
import { setupProfile, dumpConfig, info } from '../src/setup.mjs';
|
|
4
|
+
|
|
5
|
+
const [cmd, ...rest] = process.argv.slice(2);
|
|
6
|
+
|
|
7
|
+
function usage() {
|
|
8
|
+
console.log(`dshloader <command> [args]
|
|
9
|
+
|
|
10
|
+
Commands:
|
|
11
|
+
setup <profile> Inject dshloader into a profile (dependency + patch).
|
|
12
|
+
dump-config <profile> Run \`dsh --profile <name> --dump-config\` to validate.
|
|
13
|
+
info [profile] Print dshloader version, detected dsh version, adapter.`);
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
try {
|
|
17
|
+
switch (cmd) {
|
|
18
|
+
case 'setup': {
|
|
19
|
+
const profile = rest[0];
|
|
20
|
+
if (!profile) throw new Error('setup requires a profile name');
|
|
21
|
+
setupProfile(profile);
|
|
22
|
+
break;
|
|
23
|
+
}
|
|
24
|
+
case 'dump-config': {
|
|
25
|
+
const profile = rest[0];
|
|
26
|
+
if (!profile) throw new Error('dump-config requires a profile name');
|
|
27
|
+
const { ok, output } = dumpConfig(profile);
|
|
28
|
+
process.stdout.write(output);
|
|
29
|
+
process.exit(ok ? 0 : 1);
|
|
30
|
+
}
|
|
31
|
+
case 'info': {
|
|
32
|
+
info(rest[0]);
|
|
33
|
+
break;
|
|
34
|
+
}
|
|
35
|
+
default:
|
|
36
|
+
usage();
|
|
37
|
+
process.exit(cmd ? 1 : 0);
|
|
38
|
+
}
|
|
39
|
+
} catch (error) {
|
|
40
|
+
console.error(error.message);
|
|
41
|
+
process.exit(1);
|
|
42
|
+
}
|
package/cordis.patch.yml
ADDED
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
# dsh bundle patch: mounts dshloader into a profile's cordis layer stack.
|
|
2
|
+
# Position in the `insert` list does NOT affect whether service aliases /
|
|
3
|
+
# module redirects take effect — cordis is reactive dependency injection,
|
|
4
|
+
# so downstream plugins declaring `inject: [...]` stay PENDING until the
|
|
5
|
+
# alias is provided, regardless of file order. See docs/design.md §1.2/§6.2.
|
|
6
|
+
- insert:
|
|
7
|
+
- id: dsh-loader
|
|
8
|
+
name: '@dsh-plugin/dsh-loader'
|
package/package.json
ADDED
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@dsh-plugin/dsh-loader",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "Runtime compatibility shim for dsh (DeepSeek Harness) cordis bundle plugins: decouples third-party plugins from real dsh internal service names, module paths, and RPC details via a version-aware adapter registry.",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"main": "src/index.js",
|
|
7
|
+
"exports": {
|
|
8
|
+
".": "./src/index.js",
|
|
9
|
+
"./client": "./lib/client.js",
|
|
10
|
+
"./registry": "./src/registry.js",
|
|
11
|
+
"./cordis.patch.yml": "./cordis.patch.yml",
|
|
12
|
+
"./package.json": "./package.json",
|
|
13
|
+
"./ui-primitives": "./src/stable/ui-primitives.js",
|
|
14
|
+
"./ui-settings": "./src/stable/ui-settings.js",
|
|
15
|
+
"./ui-slots": "./src/stable/ui-slots.js",
|
|
16
|
+
"./web-react": "./src/stable/web-react.js",
|
|
17
|
+
"./schema-form": "./src/stable/schema-form.js",
|
|
18
|
+
"./runtime": "./src/stable/runtime.js",
|
|
19
|
+
"./tools": "./src/stable/tools.js",
|
|
20
|
+
"./llm": "./src/stable/llm.js",
|
|
21
|
+
"./agent": "./src/stable/agent.js",
|
|
22
|
+
"./settings": "./src/stable/settings.js"
|
|
23
|
+
},
|
|
24
|
+
"bin": {
|
|
25
|
+
"dshloader": "./bin/dshloader.mjs"
|
|
26
|
+
},
|
|
27
|
+
"files": [
|
|
28
|
+
"src",
|
|
29
|
+
"bin",
|
|
30
|
+
"lib/client.js",
|
|
31
|
+
"lib/client.js.map",
|
|
32
|
+
"cordis.patch.yml",
|
|
33
|
+
"README.md"
|
|
34
|
+
],
|
|
35
|
+
"scripts": {
|
|
36
|
+
"build:client": "tsdown --config tsdown.client.config.mjs",
|
|
37
|
+
"test": "node --test \"tests/**/*.test.mjs\"",
|
|
38
|
+
"test:l1": "node --test \"tests/*.test.mjs\"",
|
|
39
|
+
"test:l2": "node --test \"tests/module/*.test.mjs\"",
|
|
40
|
+
"test:l3": "node --test \"tests/integration/*.test.mjs\"",
|
|
41
|
+
"setup": "node bin/dshloader.mjs setup"
|
|
42
|
+
},
|
|
43
|
+
"engines": {
|
|
44
|
+
"node": ">=18"
|
|
45
|
+
},
|
|
46
|
+
"dependencies": {
|
|
47
|
+
"semver": "^7.8.5"
|
|
48
|
+
},
|
|
49
|
+
"dsh": {
|
|
50
|
+
"bundle": {
|
|
51
|
+
"patch": "./cordis.patch.yml"
|
|
52
|
+
},
|
|
53
|
+
"client": {
|
|
54
|
+
"platform": "web",
|
|
55
|
+
"immediately": true
|
|
56
|
+
}
|
|
57
|
+
},
|
|
58
|
+
"license": "BSD-3-Clause"
|
|
59
|
+
}
|