dsh-plugin-marketplace 0.2.3 → 0.2.5
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 +4 -0
- package/README.zh.md +4 -0
- package/client.js +35 -5
- package/index.js +55 -4
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -4,6 +4,10 @@
|
|
|
4
4
|
|
|
5
5
|
**GitHub**: [Scorp1o117/dsh-plugin-marketplace](https://github.com/Scorp1o117/dsh-plugin-marketplace) · **npm**: [dsh-plugin-marketplace](https://www.npmjs.com/package/dsh-plugin-marketplace)
|
|
6
6
|
|
|
7
|
+
[](https://github.com/Scorp1o117/dsh-enhancement-suite) [](https://www.npmjs.com/package/dsh-enhancement-suite)
|
|
8
|
+
|
|
9
|
+
Part of the [DeepSeek Harness Enhancement Suite](https://github.com/Scorp1o117/dsh-enhancement-suite) — Vision · Soul/Persona · Long-term Memory · Plugin Marketplace.
|
|
10
|
+
|
|
7
11
|
A **plugin marketplace** inside the DeepSeek Harness Web UI: browse
|
|
8
12
|
[github.com/topics/dsh-plugin](https://github.com/topics/dsh-plugin) right
|
|
9
13
|
from the settings page — no terminal needed.
|
package/README.zh.md
CHANGED
|
@@ -2,6 +2,10 @@
|
|
|
2
2
|
|
|
3
3
|
**GitHub**: [Scorp1o117/dsh-plugin-marketplace](https://github.com/Scorp1o117/dsh-plugin-marketplace) · **npm**: [dsh-plugin-marketplace](https://www.npmjs.com/package/dsh-plugin-marketplace) · [English](README.md)
|
|
4
4
|
|
|
5
|
+
[](https://github.com/Scorp1o117/dsh-enhancement-suite) [](https://www.npmjs.com/package/dsh-enhancement-suite)
|
|
6
|
+
|
|
7
|
+
属于 [DeepSeek Harness Enhancement Suite](https://github.com/Scorp1o117/dsh-enhancement-suite) —— Vision · Soul/Persona · 长期记忆 · 插件市场。
|
|
8
|
+
|
|
5
9
|
在 DeepSeek Harness **Web UI 里内置的插件市场**:直接在设置页浏览
|
|
6
10
|
[github.com/topics/dsh-plugin](https://github.com/topics/dsh-plugin),无需打开终端。
|
|
7
11
|
|
package/client.js
CHANGED
|
@@ -64,6 +64,7 @@ window.__ModuleLoader__.load({
|
|
|
64
64
|
empty: "没有找到插件",
|
|
65
65
|
install: "安装方式",
|
|
66
66
|
installHint: "在 profile 目录执行 pnpm 安装,然后在 cordis.patch.yml 挂载:",
|
|
67
|
+
installHintBundle: "标准 bundle 插件:dsh plugin add 后由 bundle 层自动挂载,无需手动改 cordis.patch.yml:",
|
|
67
68
|
readme: "README 摘要",
|
|
68
69
|
readmeEmpty: "(该仓库没有 README)",
|
|
69
70
|
openRepo: "打开 GitHub 仓库 ↗",
|
|
@@ -94,6 +95,7 @@ window.__ModuleLoader__.load({
|
|
|
94
95
|
empty: "No plugins found",
|
|
95
96
|
install: "Install",
|
|
96
97
|
installHint: "Run pnpm in your profile dir, then mount in cordis.patch.yml:",
|
|
98
|
+
installHintBundle: "Standard bundle plugin: dsh plugin add auto-mounts it via its bundle layer — no manual cordis.patch.yml edit needed:",
|
|
97
99
|
readme: "README summary",
|
|
98
100
|
readmeEmpty: "(no README in this repo)",
|
|
99
101
|
openRepo: "Open GitHub repo ↗",
|
|
@@ -163,6 +165,24 @@ window.__ModuleLoader__.load({
|
|
|
163
165
|
for (var i = 0; i < bin.length; i++) bytes[i] = bin.charCodeAt(i);
|
|
164
166
|
return new TextDecoder("utf-8").decode(bytes);
|
|
165
167
|
}
|
|
168
|
+
// npm metadata (bundle detection for the install hint); CORS-enabled, cached.
|
|
169
|
+
var npmCache = new Map(); // pkg -> {isBundle} | null
|
|
170
|
+
async function fetchNpmInfo(pkg) {
|
|
171
|
+
if (npmCache.has(pkg)) return npmCache.get(pkg);
|
|
172
|
+
var promise = fetch("https://registry.npmjs.org/" + encodeURIComponent(pkg) + "/latest", {
|
|
173
|
+
headers: { Accept: "application/vnd.npm.install-v1+json" }
|
|
174
|
+
}).then(function (res) {
|
|
175
|
+
if (!res.ok) return null;
|
|
176
|
+
return res.json();
|
|
177
|
+
}).then(function (data) {
|
|
178
|
+
if (!data || !data.dsh) return { isBundle: false };
|
|
179
|
+
return { isBundle: typeof data.dsh.bundle === "object" && data.dsh.bundle !== null && typeof data.dsh.bundle.patch === "string" && data.dsh.bundle.patch.length > 0 };
|
|
180
|
+
}).catch(function () {
|
|
181
|
+
return null;
|
|
182
|
+
});
|
|
183
|
+
npmCache.set(pkg, promise);
|
|
184
|
+
return promise;
|
|
185
|
+
}
|
|
166
186
|
|
|
167
187
|
// ── components ────────────────────────────────────────────────────────
|
|
168
188
|
function PluginCard(props) {
|
|
@@ -224,11 +244,13 @@ window.__ModuleLoader__.load({
|
|
|
224
244
|
h("a", { className: "__mp_link", href: "https://www.npmjs.com/search?q=" + encodeURIComponent(installName), target: "_blank", rel: "noreferrer" }, props.t("openNpm"))
|
|
225
245
|
),
|
|
226
246
|
h("div", { className: "__mp_detailTitle" }, props.t("install")),
|
|
227
|
-
h("div", { className: "__mp_readme" }, props.t("installHint")),
|
|
247
|
+
h("div", { className: "__mp_readme" }, props.bundleInfo === true ? props.t("installHintBundle") : props.t("installHint")),
|
|
228
248
|
h("code", { className: "__mp_code" },
|
|
229
249
|
"dsh plugin --profile web add " + installName + "\n" +
|
|
230
|
-
|
|
231
|
-
|
|
250
|
+
(props.bundleInfo === true
|
|
251
|
+
? "# standard bundle: auto-mounted via dsh.profile.bundles"
|
|
252
|
+
: "# then in $DSH_HOME/profiles/web/cordis.patch.yml:\n" +
|
|
253
|
+
"- insert:\n - id: " + installName + "\n name: '" + installName + "'")
|
|
232
254
|
),
|
|
233
255
|
btn,
|
|
234
256
|
statusNode,
|
|
@@ -260,7 +282,7 @@ window.__ModuleLoader__.load({
|
|
|
260
282
|
var t = props.t;
|
|
261
283
|
var scope = props.scope;
|
|
262
284
|
var api = props.api;
|
|
263
|
-
var state = react.useState({ q: "", sort: "stars", page: 1, items: [], loading: false, error: null, total: 0, open: null, readme: null, readmeLoading: false, readmeError: false, readmeRateLimited: false });
|
|
285
|
+
var state = react.useState({ q: "", sort: "stars", page: 1, items: [], loading: false, error: null, total: 0, open: null, readme: null, readmeLoading: false, readmeError: false, readmeRateLimited: false, bundleInfo: null });
|
|
264
286
|
var s = state[0], set = state[1];
|
|
265
287
|
// install-state subscription (stable pattern: useState + subscribe, never
|
|
266
288
|
// depend on getSnapshot() reference identity).
|
|
@@ -342,6 +364,14 @@ window.__ModuleLoader__.load({
|
|
|
342
364
|
return Object.assign({}, prev, { readmeLoading: false, readmeError: !rateLimited, readmeRateLimited: rateLimited });
|
|
343
365
|
});
|
|
344
366
|
});
|
|
367
|
+
// npm metadata: does the target package declare dsh.bundle.patch?
|
|
368
|
+
// (determines whether the manual cordis.patch.yml row is needed)
|
|
369
|
+
fetchNpmInfo(plugin.fullName.split("/")[1]).then(function (info) {
|
|
370
|
+
set(function (prev) {
|
|
371
|
+
if (!prev.open || prev.open.fullName !== plugin.fullName) return prev;
|
|
372
|
+
return Object.assign({}, prev, { bundleInfo: info ? Boolean(info.isBundle) : null });
|
|
373
|
+
});
|
|
374
|
+
});
|
|
345
375
|
}, [s.open]);
|
|
346
376
|
var submit = function (e) {
|
|
347
377
|
e.preventDefault();
|
|
@@ -364,7 +394,7 @@ window.__ModuleLoader__.load({
|
|
|
364
394
|
var open = s.open && s.open.fullName === p.fullName;
|
|
365
395
|
return h("div", { key: p.fullName, className: "__mp_item" },
|
|
366
396
|
h(PluginCard, { plugin: p, t: t, onOpen: function () { openDetail(p); } }),
|
|
367
|
-
open ? h(DetailPanel, { plugin: s.open, t: t, readme: s.readme, readmeLoading: s.readmeLoading, readmeError: s.readmeError, readmeRateLimited: s.readmeRateLimited, lang: s.open.lang, installState: installState && installState.status === "ready" ? installState.value.installState : null, onInstall: onInstall, ghError: t("ghError"), explainState: installState && installState.status === "ready" ? installState.value.aiExplainResult : null, explainError: s.explainError, onExplain: onExplain }) : null
|
|
397
|
+
open ? h(DetailPanel, { plugin: s.open, t: t, readme: s.readme, readmeLoading: s.readmeLoading, readmeError: s.readmeError, readmeRateLimited: s.readmeRateLimited, lang: s.open.lang, installState: installState && installState.status === "ready" ? installState.value.installState : null, onInstall: onInstall, ghError: t("ghError"), explainState: installState && installState.status === "ready" ? installState.value.aiExplainResult : null, explainError: s.explainError, onExplain: onExplain, bundleInfo: s.bundleInfo }) : null
|
|
368
398
|
);
|
|
369
399
|
})
|
|
370
400
|
),
|
package/index.js
CHANGED
|
@@ -109,21 +109,72 @@ function apply(ctx, config) {
|
|
|
109
109
|
return process.argv[1];
|
|
110
110
|
}
|
|
111
111
|
|
|
112
|
-
/**
|
|
112
|
+
/** Read the profile manifest (its `dsh.profile.bundles` list), or null. */
|
|
113
|
+
function profileManifest(profile) {
|
|
114
|
+
try {
|
|
115
|
+
const dshHome = process.env.DSH_HOME || join(os.homedir(), ".dsh");
|
|
116
|
+
return JSON.parse(readFileSync(join(dshHome, "profiles", profile, "package.json"), "utf8"));
|
|
117
|
+
} catch {
|
|
118
|
+
return null;
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
/** Whether the installed package declares dsh.bundle.patch (auto-mounted via its bundle layer). */
|
|
123
|
+
function isBundlePackage(pkg, profile) {
|
|
124
|
+
try {
|
|
125
|
+
const dshHome = process.env.DSH_HOME || join(os.homedir(), ".dsh");
|
|
126
|
+
const manifest = JSON.parse(readFileSync(join(dshHome, "profiles", profile, "node_modules", pkg, "package.json"), "utf8"));
|
|
127
|
+
return typeof manifest?.dsh?.bundle?.patch === "string" && manifest.dsh.bundle.patch.length > 0;
|
|
128
|
+
} catch {
|
|
129
|
+
return false;
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
/**
|
|
134
|
+
* Append a mount row for `pkg` to cordis.patch.yml (idempotent).
|
|
135
|
+
* Bundle plugins (already in `dsh.profile.bundles`, or whose installed
|
|
136
|
+
* package declares `dsh.bundle.patch`) are mounted by their own bundle
|
|
137
|
+
* layer — adding a manual row would create a duplicate loader entry and
|
|
138
|
+
* abort boot. A STALE manual row left by an earlier non-bundle version is
|
|
139
|
+
* removed instead, so reinstalling a bundle plugin never breaks boot.
|
|
140
|
+
*/
|
|
113
141
|
function ensureMounted(pkg, profile) {
|
|
114
142
|
const dshHome = process.env.DSH_HOME || join(os.homedir(), ".dsh");
|
|
115
143
|
const patchPath = join(dshHome, "profiles", profile, "cordis.patch.yml");
|
|
116
144
|
if (!existsSync(patchPath)) return " (patch file not found; mount manually)";
|
|
145
|
+
const bundles = profileManifest(profile)?.dsh?.profile?.bundles ?? [];
|
|
146
|
+
if (bundles.includes(pkg) || isBundlePackage(pkg, profile)) {
|
|
147
|
+
// Bundle layer auto-mounts; remove any stale manual row (pre-bundle).
|
|
148
|
+
const src = readFileSync(patchPath, "utf8");
|
|
149
|
+
const cleaned = src.replace(new RegExp(`[ \\t]*- id: [^\\n]*\\n[ \\t]*name: ['"]${pkg.replace(/[.*+?^${}()|[\]\\]/g, "\\$&")}['"]\\n?`, "g"), "");
|
|
150
|
+
if (cleaned !== src) {
|
|
151
|
+
writeFileSync(patchPath, cleaned, "utf8");
|
|
152
|
+
return " (removed stale manual mount row; bundle layer auto-mounts)";
|
|
153
|
+
}
|
|
154
|
+
return bundles.includes(pkg) ? " (already mounted via dsh.profile.bundles)" : " (bundle plugin: auto-mounted via its own dsh.bundle layer)";
|
|
155
|
+
}
|
|
117
156
|
const id = pkg.replace(/^@[^/]+\//, "").replace(/[^a-z0-9-]/g, "-") || pkg;
|
|
118
157
|
let src = readFileSync(patchPath, "utf8");
|
|
119
158
|
if (src.includes(`name: '${pkg}'`) || src.includes(`name: "${pkg}"`)) return " (already mounted)";
|
|
120
|
-
// Append under the first `- insert:` list; match its indentation (4 spaces
|
|
121
|
-
// is the convention used by dsh profiles).
|
|
122
|
-
const insertAt = src.search(/^- insert:\s*$/m);
|
|
123
159
|
const row = `\n - id: ${id}\n name: '${pkg}'`;
|
|
160
|
+
// A flow-style empty array (`[]`, optionally after comment lines) is a
|
|
161
|
+
// valid empty patch: replace it with the block-style insert list.
|
|
162
|
+
// Appending after `[]` would produce two top-level documents in one
|
|
163
|
+
// stream and crash boot with a YAML parse error.
|
|
164
|
+
const emptyArray = /^(\s*(?:#[^\n]*\n?)*)\[\s*\]\s*$/.exec(src);
|
|
165
|
+
if (emptyArray) {
|
|
166
|
+
writeFileSync(patchPath, `${emptyArray[1]}- insert:${row}\n`, "utf8");
|
|
167
|
+
return " (mounted in cordis.patch.yml)";
|
|
168
|
+
}
|
|
169
|
+
// Insert under the first `- insert:` list; match its indentation (4
|
|
170
|
+
// spaces is the convention used by dsh profiles).
|
|
171
|
+
const insertAt = src.search(/^- insert:\s*$/m);
|
|
124
172
|
if (insertAt >= 0) {
|
|
125
173
|
src = src.slice(0, insertAt + "- insert:".length) + row + src.slice(insertAt + "- insert:".length);
|
|
126
174
|
} else {
|
|
175
|
+
// A non-empty flow-style array cannot be safely extended with string
|
|
176
|
+
// surgery — refuse rather than corrupt the file.
|
|
177
|
+
if (/^\s*\[/.test(src)) return " (patch file uses a flow-style array; mount manually)";
|
|
127
178
|
src += `\n- insert:${row}\n`;
|
|
128
179
|
}
|
|
129
180
|
writeFileSync(patchPath, src, "utf8");
|