dsh-plugin-plugins 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 ADDED
@@ -0,0 +1,7 @@
1
+ # dsh-plugin-manager
2
+
3
+ DSH 插件管理。按来源区分:官方(@deepseek-ai)、第三方、自定义(dsh-plugin),支持启用/禁用/删除。
4
+
5
+ - 启用/禁用:修改 profile 的 `dsh.profile.bundles`(移入/移出)。
6
+ - 删除:移出 bundles + 提示用 `dsh plugin remove` 卸载。
7
+ - 需重启 DSH 生效。
@@ -0,0 +1,4 @@
1
+ # dsh-plugin-plugins —— DSH bundle patch
2
+ - insert:
3
+ - id: dsh-plugin-plugins
4
+ name: dsh-plugin-plugins
package/lib/client.js ADDED
@@ -0,0 +1,117 @@
1
+ window.__ModuleLoader__.load({
2
+ id: "dsh-plugin-plugins",
3
+ factory: (require) => {
4
+ var module = { exports: {} };
5
+ var exports = module.exports;
6
+ Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
7
+ let react_jsx_runtime = require("react/jsx-runtime");
8
+ let react = require("react");
9
+
10
+ var CSS = "" +
11
+ ".dshpm_section{max-width:760px;flex-direction:column;gap:12px;display:flex;color:var(--dsw-alias-label-primary)}" +
12
+ ".dshpm_heading{font-size:16px;font-weight:600;margin:0}" +
13
+ ".dshpm_intro{font-size:13px;color:var(--dsw-alias-label-tertiary);margin:0 0 4px}" +
14
+ ".dshpm_card{border:1px solid var(--dsw-alias-border-l2);background:var(--dsw-alias-bg-layer-3);border-radius:12px;list-style:none;transition:border-color .16s,background .16s}" +
15
+ ".dshpm_card:hover{border-color:var(--dsw-alias-label-dimmed)}" +
16
+ ".dshpm_header{appearance:none;width:100%;font:inherit;color:inherit;text-align:left;cursor:pointer;background:0 0;border:0;border-radius:12px;align-items:center;gap:12px;padding:14px 16px;display:flex}" +
17
+ ".dshpm_headText{flex-direction:column;flex:1;gap:4px;min-width:0;display:flex}" +
18
+ ".dshpm_name{color:var(--dsw-alias-label-primary);font-size:15px;font-weight:600;line-height:1.4;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}" +
19
+ ".dshpm_sub{color:var(--dsw-alias-label-tertiary);font-size:13px;line-height:1.5;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}" +
20
+ ".dshpm_chevron{color:var(--dsw-alias-label-tertiary);flex:none;transition:transform .16s}" +
21
+ ".dshpm_badge{white-space:nowrap;border-radius:999px;padding:1px 8px;font-size:11px;font-weight:500;line-height:17px;flex:none}" +
22
+ ".dshpm_official{background:var(--dsw-alias-bg-module-platform);color:var(--dsw-alias-label-secondary)}" +
23
+ ".dshpm_thirdparty{background:var(--dsw-alias-bg-module-platform);color:var(--dsw-alias-label-secondary)}" +
24
+ ".dshpm_mine{background:var(--dsw-alias-state-business-primary);color:#fff}" +
25
+ ".dshpm_body{border-top:1px solid var(--dsw-alias-border-l2);margin:0 16px;padding-bottom:12px}" +
26
+ ".dshpm_desc{color:var(--dsw-alias-label-secondary);font-size:13px;line-height:1.6;margin:12px 0}" +
27
+ ".dshpm_meta{color:var(--dsw-alias-label-tertiary);font-size:12px;margin:0 0 12px}" +
28
+ ".dshpm_actions{align-items:center;gap:8px;display:flex;flex-wrap:wrap}" +
29
+ ".dshpm_btn{appearance:none;cursor:pointer;border:1px solid var(--dsw-alias-border-l2);border-radius:8px;padding:5px 14px;font-size:13px;line-height:1.5;background:var(--dsw-alias-bg-layer-3);color:var(--dsw-alias-label-secondary)}" +
30
+ ".dshpm_btn:hover{color:var(--dsw-alias-label-primary);border-color:var(--dsw-alias-label-dimmed)}" +
31
+ ".dshpm_btn.dshpm_del{color:var(--dsw-alias-state-error-primary)}" +
32
+ ".dshpm_note{color:var(--dsw-alias-label-tertiary);font-size:12px;line-height:1.5;margin:8px 0 0}";
33
+
34
+ var zh = {
35
+ "nav": "插件管理", "title": "插件管理",
36
+ "desc": "按来源区分:官方(@deepseek-ai)/ 第三方 / 自定义(dsh-plugin)。点插件标题可展开说明。启用/禁用会修改 profile 的 bundle 列表;删除会卸载。",
37
+ "official": "官方", "thirdparty": "第三方", "mine": "自定义",
38
+ "enabled": "已启用", "disabled": "已停用", "enable": "启用", "disable": "停用", "delete": "删除",
39
+ "empty": "暂无插件", "noDesc": "(无说明)", "confirmDel": "确定删除插件 %s 吗?", "note": "启用/禁用/删除后需重启 DSH 生效。"
40
+ };
41
+ var en = {
42
+ "nav": "Plugin Manager", "title": "Plugin Manager",
43
+ "desc": "By source: official (@deepseek-ai) / third-party / custom (dsh-plugin). Click a plugin to expand. Enable/disable edits the profile bundle list; delete uninstalls.",
44
+ "official": "Official", "thirdparty": "3rd-party", "mine": "Custom",
45
+ "enabled": "Enabled", "disabled": "Disabled", "enable": "Enable", "disable": "Disable", "delete": "Delete",
46
+ "empty": "No plugins", "noDesc": "(no description)", "confirmDel": "Delete plugin %s?", "note": "Changes take effect after restarting DSH."
47
+ };
48
+
49
+ function catLabel(cat, t) { return cat === "official" ? t("official") : cat === "mine" ? t("mine") : t("thirdparty"); }
50
+
51
+ function PluginManager({ t }) {
52
+ var listState = react.useState(null);
53
+ var plugins = listState[0]; var set = listState[1];
54
+ var expState = react.useState({});
55
+ var expanded = expState[0]; var setExpanded = expState[1];
56
+ function load() { fetch("/dsh-plugin-manager/plugins.json").then(function(r){return r.json()}).then(function(d){ set(d.plugins || []) }).catch(function(){ set([]) }); }
57
+ react.useEffect(load, []);
58
+ function toggle(name) {
59
+ var next = Object.assign({}, expanded);
60
+ next[name] = !next[name];
61
+ setExpanded(next);
62
+ }
63
+ function setEnabledRow(p, enabled) {
64
+ fetch("/dsh-plugin-manager/plugins.json", { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ action: "setEnabled", name: p.name, enabled: enabled }) }).then(load);
65
+ }
66
+ function removeRow(p) {
67
+ if (window.confirm(t("confirmDel").replace("%s", p.name))) {
68
+ fetch("/dsh-plugin-manager/plugins.json", { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ action: "remove", name: p.name }) }).then(load);
69
+ }
70
+ }
71
+ return react_jsx_runtime.jsx("div", { className: "dshpm_section", children: [
72
+ react_jsx_runtime.jsx("h3", { className: "dshpm_heading", children: t("title") }),
73
+ react_jsx_runtime.jsx("p", { className: "dshpm_intro", children: t("desc") }),
74
+ (plugins === null) ? null : plugins.length === 0 ? react_jsx_runtime.jsx("p", { className: "dshpm_note", children: t("empty") })
75
+ : plugins.map(function (p) {
76
+ var catCls = p.category === "official" ? "dshpm_official" : p.category === "mine" ? "dshpm_mine" : "dshpm_thirdparty";
77
+ var open = !!expanded[p.name];
78
+ return react_jsx_runtime.jsx("div", { className: "dshpm_card", key: p.name, children: [
79
+ react_jsx_runtime.jsx("button", { className: "dshpm_header", onClick: function () { toggle(p.name) }, children: [
80
+ react_jsx_runtime.jsx("span", { className: "dshpm_headText", children: [
81
+ react_jsx_runtime.jsx("span", { className: "dshpm_name", children: p.name }),
82
+ react_jsx_runtime.jsx("span", { className: "dshpm_sub", children: (p.description || t("noDesc")) })
83
+ ]}),
84
+ react_jsx_runtime.jsx("span", { className: "dshpm_badge dshpm_" + p.category, children: catLabel(p.category, t) }),
85
+ react_jsx_runtime.jsx("span", { className: "dshpm_chevron", children: open ? "▾" : "▸" })
86
+ ]}),
87
+ open ? react_jsx_runtime.jsx("div", { className: "dshpm_body", children: [
88
+ react_jsx_runtime.jsx("p", { className: "dshpm_desc", children: (p.description || t("noDesc")) }),
89
+ react_jsx_runtime.jsx("p", { className: "dshpm_meta", children: (p.version ? p.version + " · " : "") + (p.enabled ? t("enabled") : t("disabled")) }),
90
+ react_jsx_runtime.jsx("div", { className: "dshpm_actions", children: [
91
+ react_jsx_runtime.jsx("button", { className: "dshpm_btn", onClick: function () { setEnabledRow(p, !p.enabled) }, children: p.enabled ? t("disable") : t("enable") }),
92
+ react_jsx_runtime.jsx("button", { className: "dshpm_btn dshpm_del", onClick: function () { removeRow(p) }, children: t("delete") })
93
+ ]})
94
+ ]}) : null
95
+ ]});
96
+ }),
97
+ react_jsx_runtime.jsx("p", { className: "dshpm_note", children: t("note") })
98
+ ] });
99
+ }
100
+
101
+ var NS = "dsh-plugin-plugins";
102
+ var inject = ["slots", "locale"];
103
+ function apply(ctx) {
104
+ if (typeof document !== "undefined" && !document.getElementById("dshpm-style")) {
105
+ var st = document.createElement("style"); st.id = "dshpm-style"; st.textContent = CSS; document.head.appendChild(st);
106
+ }
107
+ ctx.effect(function () { return ctx.locale.register(NS, { zh: zh, en: en }) }, "dsh-plugin-manager: dict");
108
+ var t = ctx.locale.bind(NS);
109
+ ctx.slots.inject("settings.plugins.tab", function () { return ctx.slots.register({
110
+ name: "settings.plugins.tab", id: "plugin-manager", order: 10, label: function () { return t("nav") }, locale: NS, inject: function () { return { t: t } }
111
+ }, PluginManager) });
112
+ }
113
+ exports.apply = apply;
114
+ exports.inject = inject;
115
+ return module.exports;
116
+ }
117
+ });
package/lib/index.js ADDED
@@ -0,0 +1,91 @@
1
+ import path from 'node:path'
2
+ import fs from 'node:fs'
3
+ import os from 'node:os'
4
+ import { fileURLToPath } from 'node:url'
5
+
6
+ const DSH_HOME = process.env.DSH_HOME || path.join(os.homedir(), '.dsh')
7
+ const PROFILE = path.join(DSH_HOME, 'profiles', 'web')
8
+ const PROF_PKG = path.join(PROFILE, 'package.json')
9
+ const NM = path.join(PROFILE, 'node_modules')
10
+
11
+ function category(name) {
12
+ if (name.startsWith('@deepseek-ai/')) return 'official'
13
+ if (name.startsWith('dsh-plugin-')) return 'mine'
14
+ return 'thirdparty'
15
+ }
16
+ function readBundles() {
17
+ try {
18
+ const pkg = JSON.parse(fs.readFileSync(PROF_PKG, 'utf8'))
19
+ return { bundles: (pkg.dsh && pkg.dsh.profile && Array.isArray(pkg.dsh.profile.bundles)) ? pkg.dsh.profile.bundles : [] }
20
+ } catch (e) { return { bundles: [] } }
21
+ }
22
+ function writeBundles(bundles) {
23
+ const pkg = JSON.parse(fs.readFileSync(PROF_PKG, 'utf8'))
24
+ if (!pkg.dsh) pkg.dsh = {}
25
+ if (!pkg.dsh.profile) pkg.dsh.profile = {}
26
+ pkg.dsh.profile.bundles = bundles
27
+ fs.writeFileSync(PROF_PKG, JSON.stringify(pkg, null, 2), 'utf8')
28
+ }
29
+ function readPkgDesc(name) {
30
+ const p = path.join(NM, name, 'package.json')
31
+ try { const j = JSON.parse(fs.readFileSync(p, 'utf8')); return { description: j.description || '', version: j.version || '' } } catch (e) { return { description: '', version: '' } }
32
+ }
33
+ function listPlugins() {
34
+ const { bundles } = readBundles()
35
+ const seen = new Set()
36
+ const plugins = []
37
+ for (const name of bundles) {
38
+ if (seen.has(name)) continue
39
+ seen.add(name)
40
+ const info = readPkgDesc(name)
41
+ plugins.push({ name, category: category(name), enabled: true, installed: true, description: info.description, version: info.version })
42
+ }
43
+ // 也扫描 node_modules 里已安装但不在 bundles 的(可能是被禁用/但没卸载)
44
+ let installed = []
45
+ try { installed = fs.readdirSync(NM).filter(n => n.startsWith('dsh-plugin-') || n.startsWith('dsh-whale') || n.startsWith('@deepseek-ai')) } catch (e) {}
46
+ for (const name of installed) {
47
+ if (seen.has(name)) continue
48
+ const info = readPkgDesc(name)
49
+ plugins.push({ name, category: category(name), enabled: false, installed: true, description: info.description, version: info.version })
50
+ }
51
+ const order = { official: 0, thirdparty: 1, mine: 2 }
52
+ plugins.sort((a, b) => (order[a.category] - order[b.category]) || a.name.localeCompare(b.name))
53
+ return { ok: true, plugins }
54
+ }
55
+ function setEnabled(name, enabled) {
56
+ if (!/^[@A-Za-z0-9_-]+(\/[A-Za-z0-9_-]+)?$/.test(name)) throw new Error('invalid plugin name')
57
+ const cur = readBundles()
58
+ const bundles = cur.bundles.slice()
59
+ const idx = bundles.indexOf(name)
60
+ if (enabled && idx === -1) bundles.push(name)
61
+ if (!enabled && idx !== -1) bundles.splice(idx, 1)
62
+ writeBundles(bundles)
63
+ return { ok: true }
64
+ }
65
+ async function removePlugin(name, ctx) {
66
+ if (!/^[@A-Za-z0-9_-]+(\/[A-Za-z0-9_-]+)?$/.test(name)) throw new Error('invalid plugin name')
67
+ const cur = readBundles()
68
+ writeBundles(cur.bundles.filter((b) => b !== name))
69
+ return { ok: true }
70
+ }
71
+
72
+ const CLIENT_JS = ''
73
+ const name = 'dsh-plugin-plugins'
74
+ const inject = ['webServer']
75
+ function apply(ctx) {
76
+ const json = (res, o) => { res.writeHead(200, { 'Content-Type': 'application/json; charset=utf-8', 'Cache-Control': 'no-store' }); res.end(JSON.stringify(o)) }
77
+ const route = { kind: 'exact' }
78
+ ctx.webServer.register({ kind: 'exact', path: '/dsh-plugin-manager/plugins.json', handler: (req, res) => {
79
+ if (req.method === 'POST') {
80
+ let body = ''; req.on('data', c => body += c); req.on('end', () => { try {
81
+ const p = JSON.parse(body || '{}')
82
+ if (p.action === 'setEnabled') return json(res, setEnabled(String(p.name), !!p.enabled))
83
+ if (p.action === 'remove') return removePlugin(String(p.name), ctx).then(r => json(res, r)).catch(e => json(res, { ok: false, error: String(e && e.message || e) }))
84
+ json(res, { ok: false, error: 'unknown action' })
85
+ } catch (e) { json(res, { ok: false, error: String(e && e.message || e) }) } })
86
+ } else {
87
+ json(res, listPlugins())
88
+ }
89
+ }})
90
+ }
91
+ export { name, inject, apply }
package/package.json ADDED
@@ -0,0 +1,44 @@
1
+ {
2
+ "name": "dsh-plugin-plugins",
3
+ "version": "1.0.0",
4
+ "description": "DSH 插件管理:区分官方/第三方/自定义插件,支持启用、禁用、删除",
5
+ "keywords": [
6
+ "dsh",
7
+ "dsh-plugin",
8
+ "deepseek-harness",
9
+ "plugin-manager"
10
+ ],
11
+ "type": "module",
12
+ "main": "lib/index.js",
13
+ "files": [
14
+ "lib",
15
+ "cordis.patch.yml",
16
+ "README.md"
17
+ ],
18
+ "dsh": {
19
+ "bundle": {
20
+ "patch": "./cordis.patch.yml"
21
+ },
22
+ "client": {
23
+ "inject": [
24
+ "@deepseek-ai/dsh-client-runtime",
25
+ "@deepseek-ai/dsh-client-locale",
26
+ "@deepseek-ai/dsh-client-ui-settings"
27
+ ],
28
+ "platform": "web"
29
+ }
30
+ },
31
+ "exports": {
32
+ ".": "./lib/index.js",
33
+ "./client": "./lib/client.js",
34
+ "./package.json": "./package.json"
35
+ },
36
+ "license": "MIT",
37
+ "publishConfig": {
38
+ "access": "public"
39
+ },
40
+ "repository": {
41
+ "type": "git",
42
+ "url": "git+https://github.com/mengyu-arch/dsh-plugins.git"
43
+ }
44
+ }