dsh-m 0.2.14 → 0.3.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.en.md +3 -3
- package/README.md +3 -3
- package/docs/DESIGN.md +23 -0
- package/lib/core/settings-compat.js +166 -0
- package/lib/host.js +40 -14
- package/package.json +1 -1
- package/registry.json +46 -2
package/README.en.md
CHANGED
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
[](https://www.npmjs.com/package/dsh-m)
|
|
5
5
|
[](../../actions/workflows/registry.yml)
|
|
6
6
|
[](./LICENSE)
|
|
7
|
-
[](#faq)
|
|
8
8
|
|
|
9
9
|
English · [中文](./README.md)
|
|
10
10
|
|
|
@@ -110,8 +110,8 @@ Listings over 200 entries trigger a performance notice. The market list is serve
|
|
|
110
110
|
**4. What if my custom source goes down?**
|
|
111
111
|
dsh-m serves its last successful cache for that source and marks it as cached; with no cache at all the market shows "registry unavailable" while installed plugins stay manageable. Fix the address or restore the default anytime.
|
|
112
112
|
|
|
113
|
-
**5. Which DSH Web versions
|
|
114
|
-
|
|
113
|
+
**5. Which DSH Web versions are supported?**
|
|
114
|
+
Same scope as dsh-skip-browser-auth: the public package contracts for `0.1.2-rc.1`, `0.1.5-rc.1/rc.2`, `0.1.7-rc.1`, and `0.1.7-rc.2` are all checked. DSH 0.1.7 reshaped the settings service into `SettingsForms` (the old `settings.register` is gone); dsh-m stays compatible via runtime shape detection: the whole ≤0.1.5 generation uses the `register()` scope path (0.1.5-rc.2's dsh-settings still ships the old API and is covered), while 0.1.7-rc.1 / rc.2 use Config `.volatile()` fields + `settings.update` + the `loader/volatile-update` event (byte-identical dsh-settings and the same loader across both rcs, so one implementation covers both). Unknown shapes degrade to the cordis config-file path with marketplace features unaffected. All these versions expose the dsh-m `appExit` launcher hook used by the restart button; the 0.1.5-rc.1 runtime has live-loaded dsh-m and passed `/dshm` ping plus an authenticated `303 → 200` page request, while the 0.1.7 adaptation's live E2E (install + settings write persistence) follows after release. Systemd deployments must configure `Restart=on-failure` or `Restart=always`; otherwise use the deployment's manual restart procedure.
|
|
115
115
|
|
|
116
116
|
## License
|
|
117
117
|
|
package/README.md
CHANGED
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
[](https://www.npmjs.com/package/dsh-m)
|
|
5
5
|
[](../../actions/workflows/registry.yml)
|
|
6
6
|
[](./LICENSE)
|
|
7
|
-
[](#faq)
|
|
8
8
|
|
|
9
9
|
[English](./README.en.md) · 中文
|
|
10
10
|
|
|
@@ -110,8 +110,8 @@ main 上的中间提交可能不稳定。dsh-m 只跟踪 **release / tag**(优
|
|
|
110
110
|
**4. 自定义源挂了怎么办?**
|
|
111
111
|
优先使用该源最近一次成功的缓存并标记「缓存来源」;完全没有缓存时市场显示「收录清单不可用」,已安装插件仍可正常管理。修正地址或恢复默认即可。
|
|
112
112
|
|
|
113
|
-
**5.
|
|
114
|
-
|
|
113
|
+
**5. 支持哪些 DSH Web 版本?**
|
|
114
|
+
与 dsh-skip-browser-auth 同一口径:`0.1.2-rc.1`、`0.1.5-rc.1/rc.2`、`0.1.7-rc.1`、`0.1.7-rc.2` 的公开包契约均已核对。其中 0.1.7 把 settings 服务重塑为 `SettingsForms`(旧的 `settings.register` 消失),dsh-m 以运行时形态探测双代兼容:≤0.1.5 全代际走 `register()` scope 通路(0.1.5-rc.2 的 dsh-settings 仍为旧 API,已并入核对),0.1.7-rc.1 / rc.2 走 Config `.volatile()` 字段 + `settings.update` + `loader/volatile-update` 通路(经 tarball 逐字节比对,两个 rc 的 dsh-settings 完全相同、loader 同版,一套实现通吃);形态不识别时自动降级 cordis 配置文件通路,市场主功能不受影响。重启方面各版本都提供 dsh-m 使用的 `appExit` launcher hook;0.1.5-rc.1 运行时已实际加载并完成 `/dshm` ping 与带认证页面 `303 → 200` 核验,0.1.7 适配的 live E2E(装机 + 设置页写值持久化)随发布后核验。systemd 部署需要 unit 配置 `Restart=on-failure` 或 `Restart=always`,否则请使用部署方的手动重启方式。
|
|
115
115
|
|
|
116
116
|
## License
|
|
117
117
|
|
package/docs/DESIGN.md
CHANGED
|
@@ -187,3 +187,26 @@ npm 安装 / GitHub 安装 / 升级 / 自升级 / 卸载是**同一个事务模
|
|
|
187
187
|
- 新装插件需**重启 dsh web** 才加载;HMR 仅在 `pnpm run dev:web` watcher 存活时有效。
|
|
188
188
|
- 禁止创建监听 3080 的进程;部署 unit 名称(例如 `openbmc-dsh.service`、`deepseek-harness.service` 或 `dsh-web.service`)只作运行时事实,不硬编码到 dsh-m;managed host 的重启由 `appExit` + unit `Restart` 策略完成。
|
|
189
189
|
- 插件包协议要点:`type: module`、`main` host 入口、`exports["./client"]` 指向打包产物、`dsh.client.platform: "web"`、`dsh.bundle.patch: ./cordis.patch.yml`(`- insert: - id: dsh-m; name: dsh-m`)。
|
|
190
|
+
|
|
191
|
+
## 12. settings 服务双代兼容(2026-09-27 定稿)
|
|
192
|
+
|
|
193
|
+
DSH 0.1.7 把 settings 服务重塑为 `@deepseek-ai/dsh-settings` 的 `SettingsForms`:旧的
|
|
194
|
+
`settings.register(ns, schema, …)`(get/update/watch scope)消失,改为「Config schema 标
|
|
195
|
+
`.volatile()` 的字段 + loader 原地提交 + 自动生成设置页」。经 npm tarball 逐字节比对,
|
|
196
|
+
**0.1.7-rc.1 与 rc.2 的 dsh-settings 完全相同、loader 同为 ~1.0.5**——一套新通路通吃两个 rc。
|
|
197
|
+
|
|
198
|
+
兼容层 `src/core/settings-compat.ts`,运行时形态探测(不按版本号分支):
|
|
199
|
+
|
|
200
|
+
| runtime 代际 | 判据 | 读 | 写 | 变更通知 |
|
|
201
|
+
|---|---|---|---|---|
|
|
202
|
+
| ≤0.1.5 | `settings.register` 存在 | `scope.get()` | `scope.update()` | `scope.watch()` |
|
|
203
|
+
| 0.1.7-rc.1 / rc.2 | `settings.describe`+`update` 存在 | loader 解析的 Config(Volatile 引用 `.get()`) | `settings.update(ns, patch)`(config-editor 落 profile patch,重启持久) | `loader/volatile-update` 事件 |
|
|
204
|
+
| 未识别 | 两者皆无 | cordis 配置文件 | 同左(仅启动期生效) | 无 |
|
|
205
|
+
|
|
206
|
+
不变量:
|
|
207
|
+
|
|
208
|
+
1. **controller 只见 plain 值**——所有 store 出站值统一过 `unwrapConfig`(含 Volatile 引用解包与脏值丢弃),旧 API 混合形态(老 runtime + 新 schemastery)同样被挡在边界上;
|
|
209
|
+
2. **设置集成绝不炸插件**——`wireRegistrySettings` 不抛,失败以 warn + 降级收场,webServer / tools 不受影响;
|
|
210
|
+
3. **新 API 不调 `configure({ auto: false })`**——保留 `autoGenerate`(默认开),设置页由宿主按 volatile 字段自动生成;
|
|
211
|
+
4. **`.volatile()` 受控调用**——仅当 schemastery 有该方法时标记(0.1.5-rc.3 精确 pin 3.18.2 无此方法,老 runtime 不受影响);方法重复包裹会 throw,每字段只调一次;
|
|
212
|
+
5. **写省略 `expectedRevision`**——与旧 API 相同的末写胜语义,并发防护由 controller 串行队列 + `lastSelfWrite` 回声去重承担。
|
|
@@ -0,0 +1,166 @@
|
|
|
1
|
+
const VOLATILE_WRITE = Symbol.for('cosmokit.volatile.write');
|
|
2
|
+
/** cosmokit Volatile 引用判定(鸭子类型,跨副本安全)。 */
|
|
3
|
+
export function isVolatileRef(value) {
|
|
4
|
+
return typeof value === 'object' && value !== null && VOLATILE_WRITE in value;
|
|
5
|
+
}
|
|
6
|
+
/** 解析引用为快照;普通值原样返回。深度有界(防循环与异常结构),超界截断为 undefined。 */
|
|
7
|
+
export function unwrapValue(value, depth = 4) {
|
|
8
|
+
const walk = (v, d) => {
|
|
9
|
+
if (isVolatileRef(v)) {
|
|
10
|
+
if (d <= 0)
|
|
11
|
+
return undefined;
|
|
12
|
+
return walk(v.get(), d - 1);
|
|
13
|
+
}
|
|
14
|
+
if (Array.isArray(v)) {
|
|
15
|
+
if (d <= 0)
|
|
16
|
+
return undefined;
|
|
17
|
+
return v.map((item) => walk(item, d - 1));
|
|
18
|
+
}
|
|
19
|
+
if (typeof v === 'object' && v !== null) {
|
|
20
|
+
if (d <= 0)
|
|
21
|
+
return undefined;
|
|
22
|
+
const out = {};
|
|
23
|
+
for (const [key, item] of Object.entries(v))
|
|
24
|
+
out[key] = walk(item, d - 1);
|
|
25
|
+
return out;
|
|
26
|
+
}
|
|
27
|
+
return v;
|
|
28
|
+
};
|
|
29
|
+
return walk(value, depth);
|
|
30
|
+
}
|
|
31
|
+
/** 从任意形态(plain / 带 Volatile 引用 / 未知脏值)提取已知 registry 字段,类型不符丢弃。 */
|
|
32
|
+
export function unwrapConfig(source) {
|
|
33
|
+
const raw = unwrapValue(source);
|
|
34
|
+
const out = {};
|
|
35
|
+
if (raw && typeof raw === 'object') {
|
|
36
|
+
if (typeof raw.registryUrl === 'string')
|
|
37
|
+
out.registryUrl = raw.registryUrl;
|
|
38
|
+
if (typeof raw.timeoutMs === 'number')
|
|
39
|
+
out.timeoutMs = raw.timeoutMs;
|
|
40
|
+
if (typeof raw.cacheTtlMin === 'number')
|
|
41
|
+
out.cacheTtlMin = raw.cacheTtlMin;
|
|
42
|
+
}
|
|
43
|
+
return out;
|
|
44
|
+
}
|
|
45
|
+
/** 运行时形态探测:register 在前(老服务不会带 describe/update),forms 次之。 */
|
|
46
|
+
export function detectSettingsKind(settings) {
|
|
47
|
+
const s = settings;
|
|
48
|
+
if (s && typeof s === 'object') {
|
|
49
|
+
if (typeof s.register === 'function')
|
|
50
|
+
return 'legacy';
|
|
51
|
+
if (typeof s.describe === 'function' && typeof s.update === 'function')
|
|
52
|
+
return 'forms';
|
|
53
|
+
}
|
|
54
|
+
return 'unknown';
|
|
55
|
+
}
|
|
56
|
+
/** 在 loader entries 里找本插件实例的 profile entry id(新 API 写入的 ns)。范式同 dsh-better-sidebar。 */
|
|
57
|
+
export function ownEntryId(ctx, packageName) {
|
|
58
|
+
const loader = ctx?.loader;
|
|
59
|
+
if (!loader || typeof loader.entries !== 'function')
|
|
60
|
+
return undefined;
|
|
61
|
+
const ownFiber = ctx?.fiber;
|
|
62
|
+
let fallback;
|
|
63
|
+
try {
|
|
64
|
+
for (const entry of loader.entries()) {
|
|
65
|
+
const id = entry?.options?.id;
|
|
66
|
+
if (entry?.options?.name !== packageName || typeof id !== 'string' || id === '')
|
|
67
|
+
continue;
|
|
68
|
+
if (ownFiber !== undefined && entry.fiber === ownFiber)
|
|
69
|
+
return id;
|
|
70
|
+
if (entry.options.disabled !== true && fallback === undefined)
|
|
71
|
+
fallback = id;
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
catch {
|
|
75
|
+
return undefined;
|
|
76
|
+
}
|
|
77
|
+
return fallback;
|
|
78
|
+
}
|
|
79
|
+
/** 旧 API store:scope 值出站前统一解包(防御老 runtime 解析出引用的混合形态)。 */
|
|
80
|
+
export function createLegacyRegistryStore(scope) {
|
|
81
|
+
return {
|
|
82
|
+
get: () => unwrapConfig(scope.get()),
|
|
83
|
+
update: async (patch) => {
|
|
84
|
+
await scope.update({ ...patch });
|
|
85
|
+
},
|
|
86
|
+
watch: (callback) => scope.watch((next, prev) => {
|
|
87
|
+
callback(unwrapConfig(next), unwrapConfig(prev));
|
|
88
|
+
}),
|
|
89
|
+
};
|
|
90
|
+
}
|
|
91
|
+
function subscribeVolatileUpdate(ctx, callback) {
|
|
92
|
+
const on = ctx?.on;
|
|
93
|
+
if (typeof on !== 'function')
|
|
94
|
+
return () => { };
|
|
95
|
+
const disposed = on.call(ctx, 'loader/volatile-update', () => callback());
|
|
96
|
+
return typeof disposed === 'function' ? disposed : () => { };
|
|
97
|
+
}
|
|
98
|
+
/**
|
|
99
|
+
* 新 API store(0.1.7-rc.1 / rc.2):
|
|
100
|
+
* - 读:直接解包 loader 解析出的 Config(Volatile 引用 `.get()` 即最新已提交值);
|
|
101
|
+
* - 写:`settings.update(ns, patch)`,经 config-editor 落 profile patch(重启持久),省略
|
|
102
|
+
* expectedRevision = 与旧 API 相同的末写胜语义(controller 串行队列 + lastSelfWrite 去重);
|
|
103
|
+
* - 变更通知:`loader/volatile-update`(loader 只发给本 fiber,路径含变更字段)。
|
|
104
|
+
*/
|
|
105
|
+
export function createFormsRegistryStore(ctx, settings, ns, parsedConfig) {
|
|
106
|
+
const read = () => unwrapConfig(parsedConfig);
|
|
107
|
+
let prev = read();
|
|
108
|
+
return {
|
|
109
|
+
get: read,
|
|
110
|
+
update: async (patch) => {
|
|
111
|
+
await settings.update(ns, { ...patch });
|
|
112
|
+
},
|
|
113
|
+
watch: (callback) => subscribeVolatileUpdate(ctx, () => {
|
|
114
|
+
const next = read();
|
|
115
|
+
callback(next, prev);
|
|
116
|
+
prev = next;
|
|
117
|
+
}),
|
|
118
|
+
};
|
|
119
|
+
}
|
|
120
|
+
/**
|
|
121
|
+
* 找不到本插件 loader 条目(ns 缺失)时的只读降级 store:
|
|
122
|
+
* 读与变更通知仍可用(Config 引用由 loader 解析),只有写不可持久化。
|
|
123
|
+
*/
|
|
124
|
+
export function createFormsRegistryStoreWithoutNs(ctx, parsedConfig, note) {
|
|
125
|
+
const read = () => unwrapConfig(parsedConfig);
|
|
126
|
+
let prev = read();
|
|
127
|
+
return {
|
|
128
|
+
get: read,
|
|
129
|
+
update: async () => {
|
|
130
|
+
throw new Error(`settings 命名空间不可用,registry 地址无法持久化(${note})`);
|
|
131
|
+
},
|
|
132
|
+
watch: (callback) => subscribeVolatileUpdate(ctx, () => {
|
|
133
|
+
const next = read();
|
|
134
|
+
callback(next, prev);
|
|
135
|
+
prev = next;
|
|
136
|
+
}),
|
|
137
|
+
};
|
|
138
|
+
}
|
|
139
|
+
/**
|
|
140
|
+
* 按运行时形态把 RegistrySettingsStore 接到 controller 上。
|
|
141
|
+
* 不抛:任何形态失败都以返回值 + warn 收场,绝不拖垮插件其余部分(webServer / tools)。
|
|
142
|
+
*/
|
|
143
|
+
export function wireRegistrySettings(input) {
|
|
144
|
+
const kind = detectSettingsKind(input.settings);
|
|
145
|
+
if (kind === 'legacy') {
|
|
146
|
+
const scope = input.settings.register('dshm', input.schema, {
|
|
147
|
+
base: input.parsedConfig,
|
|
148
|
+
applies: 'live',
|
|
149
|
+
});
|
|
150
|
+
input.attachStore(createLegacyRegistryStore(scope));
|
|
151
|
+
return 'legacy';
|
|
152
|
+
}
|
|
153
|
+
if (kind === 'forms') {
|
|
154
|
+
const ns = ownEntryId(input.ctx, input.packageName);
|
|
155
|
+
if (ns === undefined) {
|
|
156
|
+
const note = 'loader 中未找到本插件条目';
|
|
157
|
+
input.logger?.warn?.(`dsh-m: ${note},设置卡不可持久化,registry 地址仅剩 cordis 配置文件通路`);
|
|
158
|
+
input.attachStore(createFormsRegistryStoreWithoutNs(input.ctx, input.parsedConfig, note));
|
|
159
|
+
return 'forms-without-ns';
|
|
160
|
+
}
|
|
161
|
+
input.attachStore(createFormsRegistryStore(input.ctx, input.settings, ns, input.parsedConfig));
|
|
162
|
+
return 'forms';
|
|
163
|
+
}
|
|
164
|
+
input.logger?.warn?.('dsh-m: settings 服务形态未识别(无 register 也无 describe/update),registry 地址走 cordis 配置文件');
|
|
165
|
+
return 'unknown';
|
|
166
|
+
}
|
package/lib/host.js
CHANGED
|
@@ -3,6 +3,7 @@ import Schema from '@deepseek-ai/schemastery';
|
|
|
3
3
|
import { createApiDispatcher } from './core/host-api.js';
|
|
4
4
|
import { bindLoaderHost } from './core/live-plugin.js';
|
|
5
5
|
import { createRegistryController } from './core/registry-controller.js';
|
|
6
|
+
import { wireRegistrySettings, unwrapConfig } from './core/settings-compat.js';
|
|
6
7
|
import { registerTools } from './tools.js';
|
|
7
8
|
import { appExitFromContext, scheduleRestart } from './core/restart.js';
|
|
8
9
|
const require = createRequire(import.meta.url);
|
|
@@ -10,32 +11,57 @@ const pkg = require('../package.json');
|
|
|
10
11
|
export const name = 'dshm';
|
|
11
12
|
// dshm_* 七个工具(src/tools.ts)
|
|
12
13
|
export const inject = ['tools'];
|
|
14
|
+
/**
|
|
15
|
+
* 0.1.7+ 的 DSH schemastery 才有 `.volatile()`(标记字段进自动生成设置页 + loader 原地提交);
|
|
16
|
+
* 旧版(≤0.1.5,schemastery 3.18.2)缺方法时保持普通字段——旧 settings API 的 scope 自带 live 语义。
|
|
17
|
+
* `volatile()` 幂等性:重复包裹会 throw,这里每字段只调一次。
|
|
18
|
+
*/
|
|
19
|
+
function live(field) {
|
|
20
|
+
const volatile = field.volatile;
|
|
21
|
+
return typeof volatile === 'function' ? volatile.call(field) : field;
|
|
22
|
+
}
|
|
13
23
|
export const Config = Schema.object({
|
|
14
|
-
registryUrl: Schema.string().description('registry 地址:空值使用默认官方清单;支持 HTTPS URL、loopback HTTP URL 或本机绝对路径/file://(整体覆盖默认清单,live 生效)'),
|
|
15
|
-
timeoutMs: Schema.number().default(20000).description('上游请求超时(毫秒)'),
|
|
16
|
-
cacheTtlMin: Schema.number().default(60).description('registry 缓存时长(分钟)'),
|
|
24
|
+
registryUrl: live(Schema.string().description('registry 地址:空值使用默认官方清单;支持 HTTPS URL、loopback HTTP URL 或本机绝对路径/file://(整体覆盖默认清单,live 生效)')),
|
|
25
|
+
timeoutMs: live(Schema.number().default(20000).description('上游请求超时(毫秒)')),
|
|
26
|
+
cacheTtlMin: live(Schema.number().default(60).description('registry 缓存时长(分钟)')),
|
|
17
27
|
});
|
|
18
28
|
export function apply(ctx, config) {
|
|
19
29
|
// 卸载前的 live-disable 依赖 loader(skillhub 同款)
|
|
20
30
|
bindLoaderHost(ctx);
|
|
21
31
|
// registry controller:active config / configured / pending / rejected 分离 + generation fence;
|
|
22
|
-
// tools 与 Host API 共用同一 active config object(apply 原地更新字段,live
|
|
23
|
-
|
|
24
|
-
|
|
32
|
+
// tools 与 Host API 共用同一 active config object(apply 原地更新字段,live 生效)。
|
|
33
|
+
// unwrapConfig:0.1.7 上 volatile 字段是 cosmokit Volatile 引用,先解包成 plain 值再进 controller。
|
|
34
|
+
const controller = createRegistryController(unwrapConfig(config));
|
|
35
|
+
// DSH 0.1.2-rc.1 / 0.1.5-rc.1 / 0.1.7-rc.1 / 0.1.7-rc.2 都经 dsh-cmdline 暴露 appExit;
|
|
25
36
|
// using it avoids guessing the service unit from release-specific cgroups.
|
|
26
37
|
const appExit = appExitFromContext(ctx);
|
|
27
38
|
const restart = (port = null) => scheduleRestart(port, { appExit });
|
|
28
39
|
registerTools(ctx, controller.config, { restart });
|
|
29
|
-
//
|
|
40
|
+
// 设置页接线(双代兼容,详见 core/settings-compat.ts):
|
|
41
|
+
// - ≤0.1.5:settings.register('dshm', …) scope(get/update/watch);
|
|
42
|
+
// - 0.1.7-rc.1 / rc.2:Config `.volatile()` 字段自动生成设置页(autoGenerate 默认开,无需 configure),
|
|
43
|
+
// 读 = loader 解析的 Config 引用,写 = settings.update(ns, patch),通知 = loader/volatile-update;
|
|
44
|
+
// - 其他形态:降级 cordis 配置文件通路。任何失败只 warn,不拖垮 webServer / tools。
|
|
30
45
|
ctx.inject(['settings'], (c) => {
|
|
31
46
|
const settings = c.settings;
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
47
|
+
try {
|
|
48
|
+
const outcome = wireRegistrySettings({
|
|
49
|
+
ctx,
|
|
50
|
+
settings,
|
|
51
|
+
schema: Config,
|
|
52
|
+
parsedConfig: config,
|
|
53
|
+
packageName: pkg.name,
|
|
54
|
+
logger: ctx.logger,
|
|
55
|
+
attachStore: (store) => controller.attachStore(store),
|
|
56
|
+
});
|
|
57
|
+
if (outcome === 'legacy' || outcome === 'forms') {
|
|
58
|
+
ctx.logger?.debug?.('dsh-m: settings store attached (%s)', outcome);
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
catch (err) {
|
|
62
|
+
ctx.logger?.warn?.('dsh-m: settings 集成失败,registry 地址走 cordis 配置文件');
|
|
63
|
+
ctx.logger?.warn?.(err);
|
|
64
|
+
}
|
|
39
65
|
});
|
|
40
66
|
// 本地 API:单路由 + method 分发(防护与状态映射在 core/host-api.ts)
|
|
41
67
|
ctx.inject(['webServer'], (c) => {
|
package/package.json
CHANGED
package/registry.json
CHANGED
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
{
|
|
5
5
|
"id": "dsh-m",
|
|
6
6
|
"name": "DSH Marketplace",
|
|
7
|
-
"description": "
|
|
7
|
+
"description": "自定义清单的 DSH 插件市场,收录/安装/卸载/升级;已适配 0.1.2-rc.1、0.1.5-rc.1/rc.2 与 0.1.7-rc.1/rc.2,详见仓库。",
|
|
8
8
|
"category": "market",
|
|
9
9
|
"tags": ["市场", "插件管理"],
|
|
10
10
|
"source": "npm",
|
|
@@ -37,7 +37,7 @@
|
|
|
37
37
|
{
|
|
38
38
|
"id": "dsh-skip-browser-auth",
|
|
39
39
|
"name": "DSH Skip Browser Auth",
|
|
40
|
-
"description": "替换官方 Connection 提供方跳过 BrowserAuth,免输 Token;已适配 0.1.2-rc.1 与 0.1.
|
|
40
|
+
"description": "替换官方 Connection 提供方跳过 BrowserAuth,免输 Token;已适配 0.1.2-rc.1、0.1.5-rc.1/rc.2 与 0.1.7-rc.1/rc.2,详见仓库。",
|
|
41
41
|
"category": "tools",
|
|
42
42
|
"tags": ["BrowserAuth", "连接", "安全"],
|
|
43
43
|
"source": "npm",
|
|
@@ -56,6 +56,17 @@
|
|
|
56
56
|
"github": "iasiv5/dsh-surf",
|
|
57
57
|
"homepage": "https://github.com/iasiv5/dsh-surf"
|
|
58
58
|
},
|
|
59
|
+
{
|
|
60
|
+
"id": "dsh-obmc-web",
|
|
61
|
+
"name": "DSH OBMC Web",
|
|
62
|
+
"description": "在 DSH 里打开 OpenBMC 控制台:同源代理走 SSH 隧道,设置面板一键开启,支持 IP 探测与隧道重定向;需 SSH 跳板可达 BMC。",
|
|
63
|
+
"category": "tools",
|
|
64
|
+
"tags": ["OpenBMC", "远程管理", "SSH 隧道", "KVM"],
|
|
65
|
+
"source": "npm",
|
|
66
|
+
"npm": "@iasiv5/dsh-obmc-web",
|
|
67
|
+
"github": "iasiv5/dsh-obmc-web",
|
|
68
|
+
"homepage": "https://github.com/iasiv5/dsh-obmc-web"
|
|
69
|
+
},
|
|
59
70
|
{
|
|
60
71
|
"id": "dsh-better-sidebar",
|
|
61
72
|
"name": "DSH Better Sidebar",
|
|
@@ -176,6 +187,39 @@
|
|
|
176
187
|
"npm": "deepseek-harness-dingtalk",
|
|
177
188
|
"github": "sliverp/DeepSeek-harness-dingtalk",
|
|
178
189
|
"homepage": "https://github.com/sliverp/DeepSeek-harness-dingtalk"
|
|
190
|
+
},
|
|
191
|
+
{
|
|
192
|
+
"id": "browser-skill-dsh-plugin",
|
|
193
|
+
"name": "BrowserSkill",
|
|
194
|
+
"description": "浏览器自动化:把 BrowserSkill 的 browser_* 工具暴露给模型,让模型在对话中操作浏览器,详见仓库。",
|
|
195
|
+
"category": "tools",
|
|
196
|
+
"tags": ["浏览器", "自动化", "工具调用"],
|
|
197
|
+
"source": "npm",
|
|
198
|
+
"npm": "@wxg-prc-cpg/browser-skill-dsh-plugin",
|
|
199
|
+
"github": "Tencent/BrowserSkill",
|
|
200
|
+
"homepage": "https://github.com/Tencent/BrowserSkill/tree/main/packages/dsh-plugin-browserskill#readme"
|
|
201
|
+
},
|
|
202
|
+
{
|
|
203
|
+
"id": "dsh-weknora",
|
|
204
|
+
"name": "WeKnora",
|
|
205
|
+
"description": "知识库检索:把 WeKnora 接入模型,提供语义搜索、文档阅读与 RAG 问答等工具,可用自有知识库,详见仓库。",
|
|
206
|
+
"category": "tools",
|
|
207
|
+
"tags": ["知识库", "RAG", "检索"],
|
|
208
|
+
"source": "npm",
|
|
209
|
+
"npm": "@wxg-prc-cpg/dsh-weknora",
|
|
210
|
+
"github": "Tencent/WeKnora",
|
|
211
|
+
"homepage": "https://github.com/Tencent/WeKnora/tree/main/packages/dsh-weknora"
|
|
212
|
+
},
|
|
213
|
+
{
|
|
214
|
+
"id": "dsh-chat-manager",
|
|
215
|
+
"name": "DSH Chat Manager",
|
|
216
|
+
"description": "会话管理器:在原生侧边栏搜索归档聊天、恢复会话并安全永久删除;需按内核版本选装,详见仓库。",
|
|
217
|
+
"category": "tools",
|
|
218
|
+
"tags": ["会话管理", "归档", "聊天记录", "恢复"],
|
|
219
|
+
"source": "npm",
|
|
220
|
+
"npm": "dsh-chat-manager",
|
|
221
|
+
"github": "WSL043/dsh-chat-manager",
|
|
222
|
+
"homepage": "https://github.com/WSL043/dsh-chat-manager"
|
|
179
223
|
}
|
|
180
224
|
]
|
|
181
225
|
}
|