@max-null/dsh-plugin-center 0.1.1 → 0.1.3
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/client.js +220 -90
- package/dist/engine.d.ts +8 -2
- package/dist/engine.js +2 -2
- package/dist/rpc.js +8 -2
- package/dist/update.d.ts +15 -7
- package/dist/update.js +39 -16
- package/package.json +1 -1
package/client.js
CHANGED
|
@@ -203,9 +203,143 @@ async function checkWhatNew() {
|
|
|
203
203
|
} catch {
|
|
204
204
|
}
|
|
205
205
|
}
|
|
206
|
-
var SOURCE_LABEL = { official: "\u5B98\u65B9", installed: "\u7528\u6237\u5B89\u88C5", local: "\u672C\u5730\u5F00\u53D1", builtin: "\u5185\u7F6E" };
|
|
207
206
|
var CATEGORIES = ["ui", "usage", "theme", "model", "session", "memory", "tools", "vision", "skill", "workflow", "notify", "dev", "market", "fun"];
|
|
207
|
+
var STRINGS = {
|
|
208
|
+
zh: {
|
|
209
|
+
title: "\u63D2\u4EF6\u4E2D\u5FC3",
|
|
210
|
+
tabInstalled: "\u5DF2\u5B89\u88C5",
|
|
211
|
+
tabMarket: "\u5E02\u573A",
|
|
212
|
+
tabUpdates: "\u66F4\u65B0",
|
|
213
|
+
headSummary: "\u5DF2\u5B89\u88C5 {a} \xB7 \u6709\u66F4\u65B0 {b} \xB7 \u5931\u6548 {c}",
|
|
214
|
+
check: "\u68C0\u67E5\u66F4\u65B0",
|
|
215
|
+
checking: "\u68C0\u67E5\u4E2D\u2026",
|
|
216
|
+
updateAll: "\u66F4\u65B0\u5168\u90E8\uFF08{n}\uFF09",
|
|
217
|
+
searchInstalled: "\u641C\u7D22\u5DF2\u5B89\u88C5\u63D2\u4EF6",
|
|
218
|
+
allSources: "\u5168\u90E8\u6765\u6E90",
|
|
219
|
+
srcOfficial: "\u5B98\u65B9",
|
|
220
|
+
srcInstalled: "\u7528\u6237\u5B89\u88C5",
|
|
221
|
+
srcLocal: "\u672C\u5730\u5F00\u53D1",
|
|
222
|
+
srcBuiltin: "\u5185\u7F6E",
|
|
223
|
+
all: "\u5168\u90E8",
|
|
224
|
+
searchMarket: "\u641C\u7D22\u793E\u533A\u63D2\u4EF6",
|
|
225
|
+
allMarkets: "\u5168\u90E8\u6E90",
|
|
226
|
+
gridDouble: "\u53CC\u5217\u7F51\u683C",
|
|
227
|
+
gridSingle: "\u5355\u5217\u5217\u8868",
|
|
228
|
+
loadFailed: "\u52A0\u8F7D\u5931\u8D25\uFF1A{e}",
|
|
229
|
+
loading: "\u52A0\u8F7D\u4E2D\u2026",
|
|
230
|
+
marketLoading: "\u52A0\u8F7D\u5E02\u573A\u76EE\u5F55\u4E2D\u2026",
|
|
231
|
+
loadMore: "\u6B63\u5728\u52A0\u8F7D\u66F4\u591A\u2026\uFF08\u5DF2\u52A0\u8F7D {n} \u4E2A\uFF09",
|
|
232
|
+
checkingUpdates: "\u68C0\u67E5\u66F4\u65B0\u4E2D\u2026",
|
|
233
|
+
noUpdates: "\u6CA1\u6709\u53EF\u7528\u7684\u66F4\u65B0\u3002",
|
|
234
|
+
recheck: "\u91CD\u65B0\u68C0\u67E5",
|
|
235
|
+
incompat: "\u4E0D\u517C\u5BB9\u5F53\u524D DSH",
|
|
236
|
+
update: "\u66F4\u65B0",
|
|
237
|
+
updating: "\u66F4\u65B0\u4E2D\u2026",
|
|
238
|
+
install: "\u5B89\u88C5",
|
|
239
|
+
installing: "\u5B89\u88C5\u4E2D\u2026",
|
|
240
|
+
pendingRestart: "\u5F85\u91CD\u542F\u751F\u6548",
|
|
241
|
+
installedTag: "\u5DF2\u5B89\u88C5",
|
|
242
|
+
disabledTag: "\u5DF2\u7981\u7528",
|
|
243
|
+
requiresDsh: "\u8981\u6C42 DSH {r}",
|
|
244
|
+
installQueued: "\u5DF2\u53D1\u8D77\u5B89\u88C5 {n}\uFF0C\u91CD\u542F dsh web \u540E\u751F\u6548",
|
|
245
|
+
installFailed: "\u5B89\u88C5\u5931\u8D25\uFF1A{e}",
|
|
246
|
+
installNotApplied: "\u5B89\u88C5\u672A\u751F\u6548",
|
|
247
|
+
updatedOne: "\u5DF2\u66F4\u65B0 {n}\uFF0C\u91CD\u542F dsh web \u540E\u751F\u6548",
|
|
248
|
+
updateFailed: "\u66F4\u65B0\u5931\u8D25\uFF1A{e}",
|
|
249
|
+
updateNotApplied: "\u66F4\u65B0\u672A\u751F\u6548",
|
|
250
|
+
updatedMany: "\u5DF2\u66F4\u65B0 {n} \u4E2A\u63D2\u4EF6\uFF0C\u91CD\u542F dsh web \u540E\u751F\u6548",
|
|
251
|
+
updateSummary: "\u66F4\u65B0\u5B8C\u6210\uFF1A\u6210\u529F {a}\uFF0C\u5931\u8D25 {b}\uFF08{c}\uFF09",
|
|
252
|
+
whatsNewTitle: "\u63D2\u4EF6\u66F4\u65B0",
|
|
253
|
+
whatsNewSub: "{n} \u4E2A\u63D2\u4EF6\u6709\u65B0\u7248\u672C",
|
|
254
|
+
later: "\u7A0D\u540E",
|
|
255
|
+
markAllRead: "\u5168\u90E8\u6807\u8BB0\u5DF2\u8BFB",
|
|
256
|
+
updateNow: "\u7ACB\u5373\u66F4\u65B0",
|
|
257
|
+
close: "\u5173\u95ED",
|
|
258
|
+
checkFail: "\u68C0\u67E5\u66F4\u65B0\u5931\u8D25\uFF0C\u8BF7\u7A0D\u540E\u91CD\u8BD5",
|
|
259
|
+
foundUpdates: "\u53D1\u73B0 {n} \u4E2A\u53EF\u66F4\u65B0\u63D2\u4EF6",
|
|
260
|
+
allUpToDate: "\u6240\u6709\u63D2\u4EF6\u5747\u4E3A\u6700\u65B0"
|
|
261
|
+
},
|
|
262
|
+
en: {
|
|
263
|
+
title: "Plugin Center",
|
|
264
|
+
tabInstalled: "Installed",
|
|
265
|
+
tabMarket: "Market",
|
|
266
|
+
tabUpdates: "Updates",
|
|
267
|
+
headSummary: "{a} installed \xB7 {b} updates \xB7 {c} failed",
|
|
268
|
+
check: "Check updates",
|
|
269
|
+
checking: "Checking\u2026",
|
|
270
|
+
updateAll: "Update all\uFF08{n}\uFF09",
|
|
271
|
+
searchInstalled: "Search installed plugins",
|
|
272
|
+
allSources: "All sources",
|
|
273
|
+
srcOfficial: "Official",
|
|
274
|
+
srcInstalled: "User installed",
|
|
275
|
+
srcLocal: "Local dev",
|
|
276
|
+
srcBuiltin: "Built-in",
|
|
277
|
+
all: "All",
|
|
278
|
+
searchMarket: "Search community plugins",
|
|
279
|
+
allMarkets: "All sources",
|
|
280
|
+
gridDouble: "Two-column grid",
|
|
281
|
+
gridSingle: "Single-column list",
|
|
282
|
+
loadFailed: "Failed to load: {e}",
|
|
283
|
+
loading: "Loading\u2026",
|
|
284
|
+
marketLoading: "Loading market catalog\u2026",
|
|
285
|
+
loadMore: "Loading more\u2026 ({n} loaded)",
|
|
286
|
+
checkingUpdates: "Checking for updates\u2026",
|
|
287
|
+
noUpdates: "No updates available.",
|
|
288
|
+
recheck: "Check again",
|
|
289
|
+
incompat: "Incompatible with current DSH",
|
|
290
|
+
update: "Update",
|
|
291
|
+
updating: "Updating\u2026",
|
|
292
|
+
install: "Install",
|
|
293
|
+
installing: "Installing\u2026",
|
|
294
|
+
pendingRestart: "Restart pending",
|
|
295
|
+
installedTag: "Installed",
|
|
296
|
+
disabledTag: "Disabled",
|
|
297
|
+
requiresDsh: "Requires DSH {r}",
|
|
298
|
+
installQueued: "Install of {n} started; restart dsh web to take effect",
|
|
299
|
+
installFailed: "Install failed: {e}",
|
|
300
|
+
installNotApplied: "Install did not take effect",
|
|
301
|
+
updatedOne: "Updated {n}; restart dsh web to take effect",
|
|
302
|
+
updateFailed: "Update failed: {e}",
|
|
303
|
+
updateNotApplied: "Update did not take effect",
|
|
304
|
+
updatedMany: "Updated {n} plugins; restart dsh web to take effect",
|
|
305
|
+
updateSummary: "Update done: {a} succeeded, {b} failed ({c})",
|
|
306
|
+
whatsNewTitle: "Plugin updates",
|
|
307
|
+
whatsNewSub: "{n} plugins have new versions",
|
|
308
|
+
later: "Later",
|
|
309
|
+
markAllRead: "Mark all read",
|
|
310
|
+
updateNow: "Update now",
|
|
311
|
+
close: "Close",
|
|
312
|
+
checkFail: "Failed to check updates, please retry later",
|
|
313
|
+
foundUpdates: "{n} updates found",
|
|
314
|
+
allUpToDate: "All plugins are up to date"
|
|
315
|
+
}
|
|
316
|
+
};
|
|
317
|
+
var localeId = "zh";
|
|
318
|
+
var localeListeners = /* @__PURE__ */ new Set();
|
|
319
|
+
function adoptLocale(id) {
|
|
320
|
+
const next = id === "en" ? "en" : "zh";
|
|
321
|
+
if (next === localeId) return;
|
|
322
|
+
localeId = next;
|
|
323
|
+
localeListeners.forEach((l) => l());
|
|
324
|
+
}
|
|
325
|
+
function fmt(tpl, vars = {}) {
|
|
326
|
+
return tpl.replace(/\{(\w+)\}/g, (_, k) => String(vars[k] ?? ""));
|
|
327
|
+
}
|
|
328
|
+
function useT() {
|
|
329
|
+
const [id, setId] = (0, import_react.useState)(localeId);
|
|
330
|
+
(0, import_react.useEffect)(() => {
|
|
331
|
+
const l = () => {
|
|
332
|
+
setId(localeId);
|
|
333
|
+
};
|
|
334
|
+
localeListeners.add(l);
|
|
335
|
+
return () => {
|
|
336
|
+
localeListeners.delete(l);
|
|
337
|
+
};
|
|
338
|
+
}, []);
|
|
339
|
+
return (key, vars) => fmt(STRINGS[id][key] ?? STRINGS.zh[key], vars);
|
|
340
|
+
}
|
|
208
341
|
function InstalledView({ search, category, source }) {
|
|
342
|
+
const t = useT();
|
|
209
343
|
const [items, setItems] = (0, import_react.useState)(installedCache);
|
|
210
344
|
const [error, setError] = (0, import_react.useState)(null);
|
|
211
345
|
(0, import_react.useEffect)(() => {
|
|
@@ -224,11 +358,14 @@ function InstalledView({ search, category, source }) {
|
|
|
224
358
|
alive = false;
|
|
225
359
|
};
|
|
226
360
|
}, []);
|
|
227
|
-
if (error !== null) return /* @__PURE__ */ (0, import_jsx_runtime.
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
361
|
+
if (error !== null) return /* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", { className: "pc-sub", children: t("loadFailed", { e: error }) });
|
|
362
|
+
if (items === null) return /* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", { className: "pc-sub", children: t("loading") });
|
|
363
|
+
const srcLabel = {
|
|
364
|
+
official: t("srcOfficial"),
|
|
365
|
+
installed: t("srcInstalled"),
|
|
366
|
+
local: t("srcLocal"),
|
|
367
|
+
builtin: t("srcBuiltin")
|
|
368
|
+
};
|
|
232
369
|
const q = search.trim().toLowerCase();
|
|
233
370
|
const filtered = items.filter((p) => {
|
|
234
371
|
const matchSearch = q === "" || p.displayName.toLowerCase().includes(q) || (p.description ?? "").toLowerCase().includes(q) || p.name.toLowerCase().includes(q);
|
|
@@ -243,22 +380,20 @@ function InstalledView({ search, category, source }) {
|
|
|
243
380
|
"v",
|
|
244
381
|
p.version
|
|
245
382
|
] }),
|
|
246
|
-
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: `pc-badge ${p.source}`, children:
|
|
383
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: `pc-badge ${p.source}`, children: srcLabel[p.source] }),
|
|
247
384
|
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "pc-spacer" }),
|
|
248
385
|
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: `pc-dot${p.fiberPhase === "failed" ? " failed" : ""}` })
|
|
249
386
|
] }),
|
|
250
387
|
p.description !== null && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "pc-desc", children: p.description }),
|
|
251
388
|
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "pc-meta", children: [
|
|
252
389
|
p.categories.map((c) => /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "pc-tag", children: c }, c)),
|
|
253
|
-
p.compatRange !== null && /* @__PURE__ */ (0, import_jsx_runtime.
|
|
254
|
-
|
|
255
|
-
p.compatRange
|
|
256
|
-
] }),
|
|
257
|
-
!p.enabled && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "pc-tag", children: "\u5DF2\u7981\u7528" })
|
|
390
|
+
p.compatRange !== null && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "pc-tag", children: t("requiresDsh", { r: p.compatRange }) }),
|
|
391
|
+
!p.enabled && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "pc-tag", children: t("disabledTag") })
|
|
258
392
|
] })
|
|
259
393
|
] }, p.entryId)) });
|
|
260
394
|
}
|
|
261
395
|
function MarketView({ category, single, source, search, onCount }) {
|
|
396
|
+
const t = useT();
|
|
262
397
|
const [items, setItems] = (0, import_react.useState)(marketCache[source]?.plugins ?? []);
|
|
263
398
|
const [done, setDone] = (0, import_react.useState)(marketCache[source]?.done ?? false);
|
|
264
399
|
const [error, setError] = (0, import_react.useState)(null);
|
|
@@ -296,20 +431,19 @@ function MarketView({ category, single, source, search, onCount }) {
|
|
|
296
431
|
if (timer !== void 0) clearTimeout(timer);
|
|
297
432
|
};
|
|
298
433
|
}, [source, onCount]);
|
|
299
|
-
if (error !== null) return /* @__PURE__ */ (0, import_jsx_runtime.
|
|
300
|
-
|
|
301
|
-
error
|
|
302
|
-
] });
|
|
303
|
-
if (items.length === 0 && !done) return /* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", { className: "pc-sub", children: "\u52A0\u8F7D\u5E02\u573A\u76EE\u5F55\u4E2D\u2026" });
|
|
434
|
+
if (error !== null) return /* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", { className: "pc-sub", children: t("loadFailed", { e: error }) });
|
|
435
|
+
if (items.length === 0 && !done) return /* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", { className: "pc-sub", children: t("marketLoading") });
|
|
304
436
|
const q = search.trim().toLowerCase();
|
|
305
437
|
const filtered = items.filter((m) => {
|
|
306
438
|
const matchSearch = q === "" || m.name.toLowerCase().includes(q) || (m.description.zh || m.description.en).toLowerCase().includes(q);
|
|
307
439
|
return matchSearch && (category === null || m.categories.includes(category));
|
|
308
440
|
});
|
|
441
|
+
const descOf = (m) => localeId === "en" ? m.description.en || m.description.zh : m.description.zh || m.description.en;
|
|
309
442
|
const install = (m) => {
|
|
310
443
|
setBusy(m.name);
|
|
311
444
|
void rpc("install", { spec: m.spec }).then(
|
|
312
|
-
() => {
|
|
445
|
+
(v) => {
|
|
446
|
+
if (v !== true) throw new Error(t("installNotApplied"));
|
|
313
447
|
setBusy(null);
|
|
314
448
|
pendingInstall.add(m.spec);
|
|
315
449
|
for (const key of Object.keys(marketCache)) {
|
|
@@ -317,11 +451,11 @@ function MarketView({ category, single, source, search, onCount }) {
|
|
|
317
451
|
if (c !== void 0) c.plugins = c.plugins.map((p) => p.name === m.name ? { ...p, installed: true } : p);
|
|
318
452
|
}
|
|
319
453
|
setItems((prev) => prev.map((p) => p.name === m.name ? { ...p, installed: true } : p));
|
|
320
|
-
showToast(
|
|
454
|
+
showToast(t("installQueued", { n: m.name }));
|
|
321
455
|
},
|
|
322
456
|
(e) => {
|
|
323
457
|
setBusy(null);
|
|
324
|
-
showToast(
|
|
458
|
+
showToast(t("installFailed", { e: e instanceof Error ? e.message : String(e) }), "error");
|
|
325
459
|
}
|
|
326
460
|
);
|
|
327
461
|
};
|
|
@@ -338,27 +472,24 @@ function MarketView({ category, single, source, search, onCount }) {
|
|
|
338
472
|
m.version
|
|
339
473
|
] })
|
|
340
474
|
] }),
|
|
341
|
-
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "pc-desc", children: m
|
|
475
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "pc-desc", children: descOf(m) }),
|
|
342
476
|
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "pc-meta", children: [
|
|
343
477
|
m.categories.map((c) => /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "pc-tag", children: c }, c)),
|
|
344
478
|
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "pc-spacer" }),
|
|
345
|
-
m.installed || pendingInstall.has(m.spec) ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "pc-tag", children: pendingInstall.has(m.spec) ? "
|
|
479
|
+
m.installed || pendingInstall.has(m.spec) ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "pc-tag", children: pendingInstall.has(m.spec) ? t("pendingRestart") : t("installedTag") }) : /* @__PURE__ */ (0, import_jsx_runtime.jsx)("button", { className: "pc-btn primary", disabled: busy !== null, onClick: () => {
|
|
346
480
|
install(m);
|
|
347
|
-
}, children: busy === m.name ? "
|
|
481
|
+
}, children: busy === m.name ? t("installing") : t("install") })
|
|
348
482
|
] })
|
|
349
483
|
] }, m.name)) }),
|
|
350
|
-
!done && /* @__PURE__ */ (0, import_jsx_runtime.
|
|
351
|
-
"\u6B63\u5728\u52A0\u8F7D\u66F4\u591A\u2026\uFF08\u5DF2\u52A0\u8F7D ",
|
|
352
|
-
items.length,
|
|
353
|
-
" \u4E2A\uFF09"
|
|
354
|
-
] })
|
|
484
|
+
!done && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", { className: "pc-sub", children: t("loadMore", { n: items.length }) })
|
|
355
485
|
] });
|
|
356
486
|
}
|
|
357
487
|
function UpdatesView({ updates, refresh, updateOne, busy }) {
|
|
358
|
-
|
|
488
|
+
const t = useT();
|
|
489
|
+
if (updates === null) return /* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", { className: "pc-sub", children: t("checkingUpdates") });
|
|
359
490
|
if (updates.length === 0) return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { children: [
|
|
360
|
-
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", { className: "pc-sub", children: "
|
|
361
|
-
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("button", { className: "pc-btn", onClick: refresh, children: "
|
|
491
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", { className: "pc-sub", children: t("noUpdates") }),
|
|
492
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("button", { className: "pc-btn", onClick: refresh, children: t("recheck") })
|
|
362
493
|
] });
|
|
363
494
|
return /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { children: updates.map((u) => /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "pc-card", children: [
|
|
364
495
|
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "pc-row", children: [
|
|
@@ -366,16 +497,17 @@ function UpdatesView({ updates, refresh, updateOne, busy }) {
|
|
|
366
497
|
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "pc-ver", children: u.fromVersion }),
|
|
367
498
|
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "pc-ver", children: "\u2192" }),
|
|
368
499
|
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { style: { color: "var(--dsw-alias-state-business-primary)", fontWeight: 500 }, children: u.toVersion }),
|
|
369
|
-
u.compat === "incompatible" && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "pc-tag danger", children: "
|
|
500
|
+
u.compat === "incompatible" && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "pc-tag danger", children: t("incompat") }),
|
|
370
501
|
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "pc-spacer" }),
|
|
371
502
|
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("button", { className: "pc-btn primary", disabled: busy !== null, onClick: () => {
|
|
372
503
|
updateOne(u.name);
|
|
373
|
-
}, children: busy === u.name || busy === "__all__" ? "
|
|
504
|
+
}, children: busy === u.name || busy === "__all__" ? t("updating") : t("update") })
|
|
374
505
|
] }),
|
|
375
506
|
u.changelog.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("ul", { className: "pc-wn-list", children: u.changelog.slice(0, 5).map((line, i) => /* @__PURE__ */ (0, import_jsx_runtime.jsx)("li", { children: line }, i)) })
|
|
376
507
|
] }, u.name)) });
|
|
377
508
|
}
|
|
378
509
|
function CenterPanel({ variant = "section" }) {
|
|
510
|
+
const t = useT();
|
|
379
511
|
const [view, setView] = (0, import_react.useState)("installed");
|
|
380
512
|
const [category, setCategory] = (0, import_react.useState)(null);
|
|
381
513
|
const [single, setSingle] = (0, import_react.useState)(false);
|
|
@@ -398,15 +530,15 @@ function CenterPanel({ variant = "section" }) {
|
|
|
398
530
|
setUpdates(digests);
|
|
399
531
|
setChecking(false);
|
|
400
532
|
if (!silent) {
|
|
401
|
-
showToast(digests.length > 0 ?
|
|
533
|
+
showToast(digests.length > 0 ? t("foundUpdates", { n: digests.length }) : t("allUpToDate"));
|
|
402
534
|
}
|
|
403
535
|
},
|
|
404
536
|
() => {
|
|
405
537
|
setChecking(false);
|
|
406
|
-
if (!silent) showToast("
|
|
538
|
+
if (!silent) showToast(t("checkFail"), "error");
|
|
407
539
|
}
|
|
408
540
|
);
|
|
409
|
-
}, []);
|
|
541
|
+
}, [t]);
|
|
410
542
|
(0, import_react.useEffect)(() => {
|
|
411
543
|
void rpc("listInstalled").then(
|
|
412
544
|
(v) => {
|
|
@@ -447,13 +579,14 @@ function CenterPanel({ variant = "section" }) {
|
|
|
447
579
|
const updateOne = (name) => {
|
|
448
580
|
setBusyUpdate(name);
|
|
449
581
|
void rpc("update", { name }).then(
|
|
450
|
-
() => {
|
|
582
|
+
(v) => {
|
|
583
|
+
if (v !== true) throw new Error(t("updateNotApplied"));
|
|
451
584
|
setBusyUpdate(null);
|
|
452
|
-
showToast(
|
|
585
|
+
showToast(t("updatedOne", { n: name }));
|
|
453
586
|
},
|
|
454
587
|
(e) => {
|
|
455
588
|
setBusyUpdate(null);
|
|
456
|
-
showToast(
|
|
589
|
+
showToast(t("updateFailed", { e: e instanceof Error ? e.message : String(e) }), "error");
|
|
457
590
|
}
|
|
458
591
|
);
|
|
459
592
|
};
|
|
@@ -464,7 +597,8 @@ function CenterPanel({ variant = "section" }) {
|
|
|
464
597
|
const failures = [];
|
|
465
598
|
for (const u of updates) {
|
|
466
599
|
try {
|
|
467
|
-
await rpc("update", { name: u.name });
|
|
600
|
+
const v = await rpc("update", { name: u.name });
|
|
601
|
+
if (v !== true) throw new Error(t("updateNotApplied"));
|
|
468
602
|
okCount++;
|
|
469
603
|
} catch (e) {
|
|
470
604
|
failures.push(`${u.name}\uFF1A${e instanceof Error ? e.message : String(e)}`);
|
|
@@ -472,9 +606,9 @@ function CenterPanel({ variant = "section" }) {
|
|
|
472
606
|
}
|
|
473
607
|
setBusyUpdate(null);
|
|
474
608
|
if (failures.length === 0) {
|
|
475
|
-
showToast(
|
|
609
|
+
showToast(t("updatedMany", { n: okCount }));
|
|
476
610
|
} else {
|
|
477
|
-
showToast(
|
|
611
|
+
showToast(t("updateSummary", { a: okCount, b: failures.length, c: failures.join("\uFF1B") }), "error");
|
|
478
612
|
}
|
|
479
613
|
refreshUpdates();
|
|
480
614
|
};
|
|
@@ -485,48 +619,37 @@ function CenterPanel({ variant = "section" }) {
|
|
|
485
619
|
count !== null && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "pc-count", children: count })
|
|
486
620
|
] }, v);
|
|
487
621
|
const tabs = /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "pc-tabs", children: [
|
|
488
|
-
tab("installed", "
|
|
489
|
-
tab("market", "
|
|
490
|
-
tab("updates", "
|
|
622
|
+
tab("installed", t("tabInstalled"), installedCount),
|
|
623
|
+
tab("market", t("tabMarket"), marketCount),
|
|
624
|
+
tab("updates", t("tabUpdates"), updates?.length ?? 0)
|
|
491
625
|
] });
|
|
492
626
|
const head = (showTitle) => /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "pc-head", children: [
|
|
493
|
-
showTitle && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "pc-title", children: "
|
|
494
|
-
/* @__PURE__ */ (0, import_jsx_runtime.
|
|
495
|
-
"\u5DF2\u5B89\u88C5 ",
|
|
496
|
-
installedCount,
|
|
497
|
-
" \xB7 \u6709\u66F4\u65B0 ",
|
|
498
|
-
updates?.length ?? 0,
|
|
499
|
-
" \xB7 \u5931\u6548 ",
|
|
500
|
-
failedCount
|
|
501
|
-
] }),
|
|
627
|
+
showTitle && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "pc-title", children: t("title") }),
|
|
628
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "pc-sub", children: t("headSummary", { a: installedCount, b: updates?.length ?? 0, c: failedCount }) }),
|
|
502
629
|
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "pc-spacer" }),
|
|
503
630
|
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("button", { className: "pc-btn", disabled: checking, onClick: () => {
|
|
504
631
|
refreshUpdates();
|
|
505
|
-
}, children: checking ? "
|
|
506
|
-
/* @__PURE__ */ (0, import_jsx_runtime.
|
|
632
|
+
}, children: checking ? t("checking") : t("check") }),
|
|
633
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("button", { className: "pc-btn primary", disabled: !updates?.length || busyUpdate !== null, onClick: () => {
|
|
507
634
|
void updateAll();
|
|
508
|
-
}, children:
|
|
509
|
-
"\u66F4\u65B0\u5168\u90E8\uFF08",
|
|
510
|
-
updates?.length ?? 0,
|
|
511
|
-
"\uFF09"
|
|
512
|
-
] })
|
|
635
|
+
}, children: t("updateAll", { n: updates?.length ?? 0 }) })
|
|
513
636
|
] });
|
|
514
637
|
const installedToolbar = view === "installed" ? /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "pc-toolbar", children: [
|
|
515
638
|
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("input", { className: "pc-search", value: search, onChange: (e) => {
|
|
516
639
|
setSearch(e.target.value);
|
|
517
|
-
}, placeholder: "
|
|
640
|
+
}, placeholder: t("searchInstalled") }),
|
|
518
641
|
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("select", { className: "pc-select", value: installedSource ?? "", onChange: (e) => {
|
|
519
642
|
setInstalledSource(e.target.value === "" ? null : e.target.value);
|
|
520
643
|
}, children: [
|
|
521
|
-
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("option", { value: "", children: "
|
|
522
|
-
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("option", { value: "official", children: "
|
|
523
|
-
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("option", { value: "installed", children: "
|
|
524
|
-
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("option", { value: "local", children: "
|
|
525
|
-
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("option", { value: "builtin", children: "
|
|
644
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("option", { value: "", children: t("allSources") }),
|
|
645
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("option", { value: "official", children: t("srcOfficial") }),
|
|
646
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("option", { value: "installed", children: t("srcInstalled") }),
|
|
647
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("option", { value: "local", children: t("srcLocal") }),
|
|
648
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("option", { value: "builtin", children: t("srcBuiltin") })
|
|
526
649
|
] }),
|
|
527
650
|
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("button", { className: `pc-chip${installedCategory === null ? " active" : ""}`, onClick: () => {
|
|
528
651
|
setInstalledCategory(null);
|
|
529
|
-
}, children: "
|
|
652
|
+
}, children: t("all") }),
|
|
530
653
|
CATEGORIES.map((c) => /* @__PURE__ */ (0, import_jsx_runtime.jsx)("button", { className: `pc-chip${installedCategory === c ? " active" : ""}`, onClick: () => {
|
|
531
654
|
setInstalledCategory(c);
|
|
532
655
|
}, children: c }, c))
|
|
@@ -534,23 +657,23 @@ function CenterPanel({ variant = "section" }) {
|
|
|
534
657
|
const marketToolbar = view === "market" ? /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "pc-toolbar", children: [
|
|
535
658
|
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("input", { className: "pc-search", value: marketSearch, onChange: (e) => {
|
|
536
659
|
setMarketSearch(e.target.value);
|
|
537
|
-
}, placeholder: "
|
|
660
|
+
}, placeholder: t("searchMarket") }),
|
|
538
661
|
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("select", { className: "pc-select", value: source, onChange: (e) => {
|
|
539
662
|
setSource(e.target.value);
|
|
540
663
|
setCategory(null);
|
|
541
664
|
}, children: [
|
|
542
665
|
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("option", { value: "awesome", children: "awesome-dsh-plugin" }),
|
|
543
666
|
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("option", { value: "oh-my-dsh", children: "Oh-My-DSH" }),
|
|
544
|
-
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("option", { value: "all", children: "
|
|
667
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("option", { value: "all", children: t("allMarkets") })
|
|
545
668
|
] }),
|
|
546
669
|
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("button", { className: `pc-chip${category === null ? " active" : ""}`, onClick: () => {
|
|
547
670
|
setCategory(null);
|
|
548
|
-
}, children: "
|
|
671
|
+
}, children: t("all") }),
|
|
549
672
|
CATEGORIES.map((c) => /* @__PURE__ */ (0, import_jsx_runtime.jsx)("button", { className: `pc-chip${category === c ? " active" : ""}`, onClick: () => {
|
|
550
673
|
setCategory(c);
|
|
551
674
|
}, children: c }, c)),
|
|
552
675
|
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "pc-spacer" }),
|
|
553
|
-
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("button", { type: "button", title: single ? "
|
|
676
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("button", { type: "button", title: single ? t("gridDouble") : t("gridSingle"), "aria-label": single ? t("gridDouble") : t("gridSingle"), className: "pc-iconbtn", onClick: () => {
|
|
554
677
|
setSingle((v) => !v);
|
|
555
678
|
}, children: single ? /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("svg", { width: "14", height: "14", viewBox: "0 0 16 16", fill: "none", stroke: "currentColor", strokeWidth: "1.5", children: [
|
|
556
679
|
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("rect", { x: "2", y: "2", width: "5", height: "5", rx: "1" }),
|
|
@@ -589,7 +712,8 @@ function CenterPanel({ variant = "section" }) {
|
|
|
589
712
|
] });
|
|
590
713
|
}
|
|
591
714
|
function HeaderButton() {
|
|
592
|
-
|
|
715
|
+
const t = useT();
|
|
716
|
+
return /* @__PURE__ */ (0, import_jsx_runtime.jsx)("button", { type: "button", title: t("title"), "aria-label": t("title"), className: "pc-headerbtn", onClick: openOverlay, children: /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("svg", { width: "16", height: "16", viewBox: "0 0 16 16", fill: "none", stroke: "currentColor", strokeWidth: "1.5", children: [
|
|
593
717
|
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("rect", { x: "2", y: "2", width: "5", height: "5", rx: "1" }),
|
|
594
718
|
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("rect", { x: "9", y: "2", width: "5", height: "5", rx: "1" }),
|
|
595
719
|
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("rect", { x: "2", y: "9", width: "5", height: "5", rx: "1" }),
|
|
@@ -597,13 +721,14 @@ function HeaderButton() {
|
|
|
597
721
|
] }) });
|
|
598
722
|
}
|
|
599
723
|
function OverlayPanel() {
|
|
724
|
+
const t = useT();
|
|
600
725
|
const open = useOverlayOpen();
|
|
601
726
|
if (!open) return null;
|
|
602
|
-
return /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "pc-overlay", role: "presentation", children: /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "pc-panel", role: "dialog", "aria-modal": "true", "aria-label": "
|
|
727
|
+
return /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "pc-overlay", role: "presentation", children: /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "pc-panel", role: "dialog", "aria-modal": "true", "aria-label": t("title"), children: [
|
|
603
728
|
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "pc-panel-head", children: [
|
|
604
|
-
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "pc-title", children: "
|
|
729
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "pc-title", children: t("title") }),
|
|
605
730
|
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "pc-spacer" }),
|
|
606
|
-
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("button", { type: "button", className: "pc-close", onClick: closeOverlay, "aria-label": "
|
|
731
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("button", { type: "button", className: "pc-close", onClick: closeOverlay, "aria-label": t("close"), children: "\u2715" })
|
|
607
732
|
] }),
|
|
608
733
|
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "pc-panel-body", children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(CenterPanel, { variant: "overlay" }) })
|
|
609
734
|
] }) });
|
|
@@ -614,6 +739,7 @@ function Toast() {
|
|
|
614
739
|
return /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: `pc-toast ${t.kind}`, children: t.message });
|
|
615
740
|
}
|
|
616
741
|
function WhatsNewDialog() {
|
|
742
|
+
const t = useT();
|
|
617
743
|
const open = useWhatsNewOpen();
|
|
618
744
|
const [busy, setBusy] = (0, import_react.useState)(false);
|
|
619
745
|
if (!open || whatsNewDigests.length === 0) return null;
|
|
@@ -623,7 +749,8 @@ function WhatsNewDialog() {
|
|
|
623
749
|
const failures = [];
|
|
624
750
|
for (const u of whatsNewDigests) {
|
|
625
751
|
try {
|
|
626
|
-
await rpc("update", { name: u.name });
|
|
752
|
+
const v = await rpc("update", { name: u.name });
|
|
753
|
+
if (v !== true) throw new Error(t("updateNotApplied"));
|
|
627
754
|
okCount++;
|
|
628
755
|
} catch (e) {
|
|
629
756
|
failures.push(`${u.name}\uFF1A${e instanceof Error ? e.message : String(e)}`);
|
|
@@ -631,21 +758,18 @@ function WhatsNewDialog() {
|
|
|
631
758
|
}
|
|
632
759
|
setBusy(false);
|
|
633
760
|
if (failures.length === 0) {
|
|
634
|
-
showToast(
|
|
761
|
+
showToast(t("updatedMany", { n: okCount }));
|
|
635
762
|
closeWhatsNew();
|
|
636
763
|
} else {
|
|
637
|
-
showToast(
|
|
764
|
+
showToast(t("updateSummary", { a: okCount, b: failures.length, c: failures.join("\uFF1B") }), "error");
|
|
638
765
|
}
|
|
639
766
|
};
|
|
640
|
-
return /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "pc-overlay", role: "presentation", children: /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "pc-panel", style: { width: "540px" }, role: "dialog", "aria-modal": "true", "aria-label": "
|
|
767
|
+
return /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "pc-overlay", role: "presentation", children: /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "pc-panel", style: { width: "540px" }, role: "dialog", "aria-modal": "true", "aria-label": t("whatsNewTitle"), children: [
|
|
641
768
|
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "pc-panel-head", children: [
|
|
642
|
-
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "pc-title", children: "
|
|
643
|
-
/* @__PURE__ */ (0, import_jsx_runtime.
|
|
644
|
-
whatsNewDigests.length,
|
|
645
|
-
" \u4E2A\u63D2\u4EF6\u6709\u65B0\u7248\u672C"
|
|
646
|
-
] }),
|
|
769
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "pc-title", children: t("whatsNewTitle") }),
|
|
770
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "pc-sub", style: { marginTop: 0 }, children: t("whatsNewSub", { n: whatsNewDigests.length }) }),
|
|
647
771
|
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "pc-spacer" }),
|
|
648
|
-
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("button", { type: "button", className: "pc-close", onClick: closeWhatsNew, "aria-label": "
|
|
772
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("button", { type: "button", className: "pc-close", onClick: closeWhatsNew, "aria-label": t("close"), children: "\u2715" })
|
|
649
773
|
] }),
|
|
650
774
|
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "pc-panel-body", style: { overflow: "auto" }, children: [
|
|
651
775
|
whatsNewDigests.map((u) => /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "pc-wn-item", children: [
|
|
@@ -658,11 +782,11 @@ function WhatsNewDialog() {
|
|
|
658
782
|
u.changelog.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("ul", { className: "pc-wn-list", children: u.changelog.slice(0, 5).map((line, i) => /* @__PURE__ */ (0, import_jsx_runtime.jsx)("li", { children: line }, i)) })
|
|
659
783
|
] }, u.name)),
|
|
660
784
|
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { style: { display: "flex", justifyContent: "flex-end", gap: "8px", marginTop: "16px" }, children: [
|
|
661
|
-
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("button", { className: "pc-btn", onClick: closeWhatsNew, children: "
|
|
662
|
-
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("button", { className: "pc-btn", onClick: closeWhatsNew, children: "
|
|
785
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("button", { className: "pc-btn", onClick: closeWhatsNew, children: t("later") }),
|
|
786
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("button", { className: "pc-btn", onClick: closeWhatsNew, children: t("markAllRead") }),
|
|
663
787
|
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("button", { className: "pc-btn primary", disabled: busy, onClick: () => {
|
|
664
788
|
void updateNow();
|
|
665
|
-
}, children: busy ? "
|
|
789
|
+
}, children: busy ? t("updating") : t("updateNow") })
|
|
666
790
|
] })
|
|
667
791
|
] })
|
|
668
792
|
] }) });
|
|
@@ -675,11 +799,17 @@ function apply(ctx) {
|
|
|
675
799
|
if (result.ok) return result.value;
|
|
676
800
|
throw new Error(result.error?.message ?? `plugin-center: ${endpoint} failed`);
|
|
677
801
|
};
|
|
802
|
+
const locale = ctx.get?.("locale");
|
|
803
|
+
const initial = locale?.getLocale?.()?.active;
|
|
804
|
+
if (typeof initial === "string") adoptLocale(initial);
|
|
805
|
+
ctx.on?.("locale/change", (snap) => {
|
|
806
|
+
adoptLocale(snap?.active);
|
|
807
|
+
});
|
|
678
808
|
ctx.slots.inject("settings.section", () => ctx.slots.register({
|
|
679
809
|
name: "settings.section",
|
|
680
810
|
id: "plugin-center",
|
|
681
811
|
order: 50,
|
|
682
|
-
label: () =>
|
|
812
|
+
label: () => STRINGS[localeId].title
|
|
683
813
|
}, CenterPanel));
|
|
684
814
|
ctx.slots.inject("conversation.session.header.utilities", () => ctx.slots.register({
|
|
685
815
|
name: "conversation.session.header.utilities",
|
package/dist/engine.d.ts
CHANGED
|
@@ -65,8 +65,14 @@ export declare class PluginCenterEngine extends Service {
|
|
|
65
65
|
listMarket(source?: MarketSource): Promise<MarketSnapshot>;
|
|
66
66
|
/** Detect updates for every installed third-party/local plugin, TTL-cached. */
|
|
67
67
|
checkUpdates(sinceIso: string): Promise<UpdateDigest[]>;
|
|
68
|
-
install(spec: string): Promise<
|
|
69
|
-
|
|
68
|
+
install(spec: string): Promise<{
|
|
69
|
+
ok: boolean;
|
|
70
|
+
detail: string;
|
|
71
|
+
}>;
|
|
72
|
+
update(name: string): Promise<{
|
|
73
|
+
ok: boolean;
|
|
74
|
+
detail: string;
|
|
75
|
+
}>;
|
|
70
76
|
/** 串行执行一次 pnpm 操作并失效缓存(无论成败都放行链条后续任务)。 */
|
|
71
77
|
private enqueuePnpm;
|
|
72
78
|
/** Temporary diagnostics for the empty-update bug; removed once root-caused. */
|
package/dist/engine.js
CHANGED
|
@@ -231,10 +231,10 @@ export class PluginCenterEngine extends Service {
|
|
|
231
231
|
/** 串行执行一次 pnpm 操作并失效缓存(无论成败都放行链条后续任务)。 */
|
|
232
232
|
enqueuePnpm(op) {
|
|
233
233
|
const run = this.pnpmChain.then(async () => {
|
|
234
|
-
const
|
|
234
|
+
const result = await op();
|
|
235
235
|
this.installedNamesCache = null;
|
|
236
236
|
this.updatesCache = null;
|
|
237
|
-
return
|
|
237
|
+
return result;
|
|
238
238
|
});
|
|
239
239
|
this.pnpmChain = run.catch(() => { });
|
|
240
240
|
return run;
|
package/dist/rpc.js
CHANGED
|
@@ -28,13 +28,19 @@ export class PluginCenterRpc extends Service {
|
|
|
28
28
|
const spec = payload?.spec;
|
|
29
29
|
if (typeof spec !== 'string' || spec === '')
|
|
30
30
|
return internal('install: spec is required');
|
|
31
|
-
|
|
31
|
+
const result = await ctx.pluginCenter.install(spec);
|
|
32
|
+
if (!result.ok)
|
|
33
|
+
return internal(`install ${spec} 失败:${result.detail}`);
|
|
34
|
+
return { ok: true, value: true };
|
|
32
35
|
}
|
|
33
36
|
case 'update': {
|
|
34
37
|
const name = payload?.name;
|
|
35
38
|
if (typeof name !== 'string' || name === '')
|
|
36
39
|
return internal('update: name is required');
|
|
37
|
-
|
|
40
|
+
const result = await ctx.pluginCenter.update(name);
|
|
41
|
+
if (!result.ok)
|
|
42
|
+
return internal(`update ${name} 失败:${result.detail}`);
|
|
43
|
+
return { ok: true, value: true };
|
|
38
44
|
}
|
|
39
45
|
case 'debug':
|
|
40
46
|
return { ok: true, value: await ctx.pluginCenter.debug() };
|
package/dist/update.d.ts
CHANGED
|
@@ -17,14 +17,22 @@ export declare function fetchCommitChangelog(repoUrl: string | null, sinceIso: s
|
|
|
17
17
|
* DSH version via the plugin's peer-dependency range.
|
|
18
18
|
*/
|
|
19
19
|
export declare function detectUpdate(name: string, localVersion: string, repoUrl: string | null, compatRange: string | null, localDshVersion: string, sinceIso: string): Promise<UpdateDigest | null>;
|
|
20
|
+
/** One pnpm invocation result: success plus a failure detail for diagnostics. */
|
|
21
|
+
export interface PnpmResult {
|
|
22
|
+
ok: boolean;
|
|
23
|
+
detail: string;
|
|
24
|
+
}
|
|
25
|
+
/** pnpm 候选命令:GUI 进程 PATH 常缺用户级 npm 全局目录;且 Windows 上
|
|
26
|
+
* CreateProcess 只找 pnpm.exe(.cmd/.ps1 必须经 shell)——2026-08-17 实测
|
|
27
|
+
* spawn('pnpm', shell:false) 直接 ENOENT,更新永远假成功。 */
|
|
28
|
+
export declare function pnpmCandidates(): string[];
|
|
20
29
|
/**
|
|
21
|
-
* Run pnpm in the profile directory
|
|
22
|
-
*
|
|
23
|
-
* the only result this layer needs.
|
|
24
|
-
* @returns the child exit code, or 1 when spawn itself fails.
|
|
30
|
+
* Run pnpm in the profile directory, trying each candidate command in turn.
|
|
31
|
+
* Output inherits the process stdio (no pipe capture — the host sandbox
|
|
32
|
+
* forbids named-pipe stdio); the exit code is the only result this layer needs.
|
|
25
33
|
*/
|
|
26
|
-
export declare function runPnpm(args: readonly string[], cwd: string): Promise<
|
|
34
|
+
export declare function runPnpm(args: readonly string[], cwd: string): Promise<PnpmResult>;
|
|
27
35
|
/** Install a package into the web profile, mirroring `dsh plugin add` semantics. */
|
|
28
|
-
export declare function installPlugin(packageSpec: string, profileDir: string): Promise<
|
|
36
|
+
export declare function installPlugin(packageSpec: string, profileDir: string): Promise<PnpmResult>;
|
|
29
37
|
/** Update a package to latest, mirroring `dsh plugin add <pkg>` (pnpm installs latest). */
|
|
30
|
-
export declare function updatePlugin(packageName: string, profileDir: string): Promise<
|
|
38
|
+
export declare function updatePlugin(packageName: string, profileDir: string): Promise<PnpmResult>;
|
package/dist/update.js
CHANGED
|
@@ -4,6 +4,9 @@
|
|
|
4
4
|
* first (many community repos ship no release/tag/CHANGELOG — verified §7.2).
|
|
5
5
|
*/
|
|
6
6
|
import { spawn } from 'node:child_process';
|
|
7
|
+
import { existsSync } from 'node:fs';
|
|
8
|
+
import { homedir } from 'node:os';
|
|
9
|
+
import { join } from 'node:path';
|
|
7
10
|
import { compareVersions, satisfies } from "./semver.js";
|
|
8
11
|
const UA = { 'User-Agent': 'dsh-plugin-center' };
|
|
9
12
|
/** Latest published version on the npm registry; null when unreachable/unpublished. */
|
|
@@ -67,34 +70,54 @@ export async function detectUpdate(name, localVersion, repoUrl, compatRange, loc
|
|
|
67
70
|
compatRange,
|
|
68
71
|
};
|
|
69
72
|
}
|
|
70
|
-
/**
|
|
71
|
-
*
|
|
72
|
-
*
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
73
|
+
/** pnpm 候选命令:GUI 进程 PATH 常缺用户级 npm 全局目录;且 Windows 上
|
|
74
|
+
* CreateProcess 只找 pnpm.exe(.cmd/.ps1 必须经 shell)——2026-08-17 实测
|
|
75
|
+
* spawn('pnpm', shell:false) 直接 ENOENT,更新永远假成功。 */
|
|
76
|
+
export function pnpmCandidates() {
|
|
77
|
+
const commands = ['pnpm', 'pnpm.cmd'];
|
|
78
|
+
const userNpm = join(homedir(), 'AppData', 'Roaming', 'npm');
|
|
79
|
+
for (const name of ['pnpm.cmd', 'pnpm.exe', 'pnpm']) {
|
|
80
|
+
const candidate = join(userNpm, name);
|
|
81
|
+
if (existsSync(candidate))
|
|
82
|
+
commands.push(candidate);
|
|
83
|
+
}
|
|
84
|
+
return commands;
|
|
85
|
+
}
|
|
86
|
+
function runOne(command, args, cwd) {
|
|
77
87
|
return new Promise((resolve) => {
|
|
78
88
|
let child;
|
|
79
89
|
try {
|
|
80
|
-
|
|
90
|
+
// shell: true —— Windows 下 .cmd shim 必须经 shell 才能 spawn。
|
|
91
|
+
child = spawn(command, [...args], { cwd, stdio: 'inherit', shell: true });
|
|
81
92
|
}
|
|
82
|
-
catch {
|
|
83
|
-
resolve(
|
|
93
|
+
catch (e) {
|
|
94
|
+
resolve({ ok: false, detail: e instanceof Error ? e.message : String(e) });
|
|
84
95
|
return;
|
|
85
96
|
}
|
|
86
|
-
child.on('error',
|
|
87
|
-
child.on('close', code => resolve(code ?? 1));
|
|
97
|
+
child.on('error', e => resolve({ ok: false, detail: e.message }));
|
|
98
|
+
child.on('close', code => resolve(code === 0 ? { ok: true, detail: '' } : { ok: false, detail: `exit code ${code ?? 1}` }));
|
|
88
99
|
});
|
|
89
100
|
}
|
|
101
|
+
/**
|
|
102
|
+
* Run pnpm in the profile directory, trying each candidate command in turn.
|
|
103
|
+
* Output inherits the process stdio (no pipe capture — the host sandbox
|
|
104
|
+
* forbids named-pipe stdio); the exit code is the only result this layer needs.
|
|
105
|
+
*/
|
|
106
|
+
export async function runPnpm(args, cwd) {
|
|
107
|
+
let last = { ok: false, detail: 'no pnpm candidate found' };
|
|
108
|
+
for (const command of pnpmCandidates()) {
|
|
109
|
+
last = await runOne(command, args, cwd);
|
|
110
|
+
if (last.ok)
|
|
111
|
+
return last;
|
|
112
|
+
}
|
|
113
|
+
return last;
|
|
114
|
+
}
|
|
90
115
|
/** Install a package into the web profile, mirroring `dsh plugin add` semantics. */
|
|
91
116
|
export async function installPlugin(packageSpec, profileDir) {
|
|
92
117
|
// `-w` is required: every profile ships a pnpm-workspace.yaml.
|
|
93
|
-
|
|
94
|
-
return code === 0;
|
|
118
|
+
return runPnpm(['add', '-w', packageSpec], profileDir);
|
|
95
119
|
}
|
|
96
120
|
/** Update a package to latest, mirroring `dsh plugin add <pkg>` (pnpm installs latest). */
|
|
97
121
|
export async function updatePlugin(packageName, profileDir) {
|
|
98
|
-
|
|
99
|
-
return code === 0;
|
|
122
|
+
return runPnpm(['add', '-w', packageName], profileDir);
|
|
100
123
|
}
|
package/package.json
CHANGED