dshmarket 1.2.4 → 1.3.1
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/LICENSE +21 -0
- package/README.md +8 -0
- package/README.zh.md +8 -0
- package/client/client.js +158 -58
- package/client/client.js.map +1 -1
- package/data/registry-snapshot.json +4512 -975
- package/lib/dsh-cli.js +37 -6
- package/lib/index.js +4 -1
- package/lib/restart.js +86 -0
- package/lib/routes.js +53 -1
- package/lib/types/dsh-cli.d.ts +4 -1
- package/lib/types/restart.d.ts +37 -0
- package/lib/types/routes.d.ts +2 -0
- package/package.json +12 -4
- package/src/client/MarketSection.tsx +94 -5
- package/src/client/locales.ts +8 -0
- package/src/client/market-data.ts +3 -3
- package/src/dsh-cli.ts +36 -7
- package/src/index.ts +4 -1
- package/src/restart.ts +96 -0
- package/src/routes.ts +54 -1
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 fkysly and dsh-market contributors
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
CHANGED
|
@@ -32,6 +32,7 @@ Restart `dsh web`, then open **Settings → Plugin Market**.
|
|
|
32
32
|
- **One-click install** — confirm the source, watch live progress; most plugins go live after a page refresh, no restart
|
|
33
33
|
- **Updates** — per-plugin update checks (npm version or pinned commit vs HEAD), one-click update, or update everything at once; the market updates itself the same way
|
|
34
34
|
- **Uninstall** — two-step confirm; plugins installed this session are removed live
|
|
35
|
+
- **Restart when needed** — changes that cannot hot-load show a one-click restart beside the pending-change banner; the action is restricted to same-origin loopback requests
|
|
35
36
|
- **Zero jargon** — if a component is missing (pnpm), the market detects it and offers a one-click automatic setup
|
|
36
37
|
- **Log export** — one click produces a sanitized plain-text log for bug reports (home paths and credential shapes are masked; nothing is ever sent anywhere)
|
|
37
38
|
|
|
@@ -45,8 +46,15 @@ Installs prefer npm tarballs over full-repo GitHub downloads whenever a plugin p
|
|
|
45
46
|
- Build scripts stay blocked by default (pnpm ≥10); allowing one is your explicit per-package choice
|
|
46
47
|
- Terminal/CLI-surface plugins are flagged before you install them into the web profile
|
|
47
48
|
- The install endpoint accepts same-origin POST only; the market never phones home
|
|
49
|
+
- The restart endpoint additionally requires a direct loopback client (forwarded requests are rejected) and relaunches the exact DSH entry, arguments, environment, and working directory
|
|
50
|
+
- One-click restart launches a detached replacement. If DSH is managed by systemd, launchd, pm2, or another supervisor, set the plugin option `allowRestart: false` and let the supervisor own restarts instead; the pending-change notice remains visible but the button is hidden
|
|
51
|
+
- For terminal-attached launches, the detached replacement keeps running after the original terminal closes
|
|
48
52
|
- Listing ≠ endorsement: plugins are third-party code, install sources you trust
|
|
49
53
|
|
|
54
|
+
## Submit your plugin
|
|
55
|
+
|
|
56
|
+
**This repo is the market app, not the catalog.** The plugin list comes from the curated [awesome-dsh-plugin](https://github.com/awesome-dsh-plugin/awesome-dsh-plugin) registry — to get your plugin listed in the market, open a PR **there** (one entry in the list; the site and this market pick it up automatically, usually within a day). Please don't PR plugin entries against this repo.
|
|
57
|
+
|
|
50
58
|
## Data source
|
|
51
59
|
|
|
52
60
|
Live from [awesome-dsh-plugin.com/plugins.json](https://awesome-dsh-plugin.com/plugins.json) — curated entries, npm mapping, and star counts refreshed daily by CI — with a bundled snapshot as offline fallback.
|
package/README.zh.md
CHANGED
|
@@ -32,6 +32,7 @@ dsh plugin --profile web add dshmarket
|
|
|
32
32
|
- **一键安装**——确认来源,实时进度;多数插件刷新页面即可用,无需重启
|
|
33
33
|
- **更新**——逐插件检测(npm 版本或锁定 commit 对比 HEAD),一键更新或全部更新;市场自己也走同一通道升级
|
|
34
34
|
- **卸载**——两步确认防误触;本次会话装的插件即点即卸
|
|
35
|
+
- **按需重启**——无法热加载的变更会在待重启提示旁显示一键重启;操作仅接受本机同源请求
|
|
35
36
|
- **零术语**——缺组件(pnpm)时市场自己发现、一键自动装好,全程不见命令行
|
|
36
37
|
- **导出日志**——一键生成脱敏纯文本日志方便反馈(home 路径与密钥形状已打码;任何数据都不会被上传)
|
|
37
38
|
|
|
@@ -45,8 +46,15 @@ dsh plugin --profile web add dshmarket
|
|
|
45
46
|
- 构建脚本默认禁止执行(pnpm ≥10),放行与否由你按包显式决定
|
|
46
47
|
- 终端/命令行类插件装进网页版前会被明确提醒
|
|
47
48
|
- 安装接口只接受同源 POST;市场不会向任何地方上报数据
|
|
49
|
+
- 重启接口还要求客户端直接来自环回地址(拒绝代理转发请求),并使用原入口、参数、环境和工作目录重新启动 DSH
|
|
50
|
+
- 一键重启会启动脱离终端的替代进程。若 DSH 由 systemd、launchd、pm2 等进程管理器托管,请设置插件选项 `allowRestart: false`,交由管理器负责重启;待重启提示仍会显示,但按钮会隐藏
|
|
51
|
+
- 从终端启动时,替代进程脱离原终端,关闭原终端后仍会继续运行
|
|
48
52
|
- 收录 ≠ 背书:插件是第三方代码,请只安装你信任的来源
|
|
49
53
|
|
|
54
|
+
## 提交你的插件
|
|
55
|
+
|
|
56
|
+
**这个仓库是市场应用本身,不是插件目录。** 市场里的插件列表来自精选列表 [awesome-dsh-plugin](https://github.com/awesome-dsh-plugin/awesome-dsh-plugin)——想让你的插件上架,请去**那边**提 PR(在列表里加一条即可,站点和本市场会自动收录,通常一天内生效)。请不要往本仓库提插件条目。
|
|
57
|
+
|
|
50
58
|
## 数据源
|
|
51
59
|
|
|
52
60
|
实时来自 [awesome-dsh-plugin.com/plugins.json](https://awesome-dsh-plugin.com/plugins.json)——精选条目、npm 映射、star 数由 CI 每日刷新——内置快照做离线兜底。
|
package/client/client.js
CHANGED
|
@@ -43,6 +43,10 @@ window.__ModuleLoader__.load({ id: "dshmarket", factory: (require) => {
|
|
|
43
43
|
busyWait: "已有操作正在进行,请等它结束(同一时间只执行一个安装/更新/卸载)",
|
|
44
44
|
approveBuilds: "放行构建脚本并重试",
|
|
45
45
|
buildsSkipped: "该插件需要运行构建脚本才能工作,出于安全默认被拦下。点击下方按钮为它放行并重装:",
|
|
46
|
+
restartNow: "立即重启",
|
|
47
|
+
restarting: "正在重启…",
|
|
48
|
+
restartFail: "重启失败",
|
|
49
|
+
restartTimeout: "等待 DeepSeek Harness 启动超时",
|
|
46
50
|
updateNow: "立即更新",
|
|
47
51
|
updateFail: "更新失败",
|
|
48
52
|
upToDate: "已是最新",
|
|
@@ -107,6 +111,10 @@ window.__ModuleLoader__.load({ id: "dshmarket", factory: (require) => {
|
|
|
107
111
|
busyWait: "Another operation is already running — please wait for it to finish (one install/update/uninstall at a time)",
|
|
108
112
|
approveBuilds: "Allow build scripts and retry",
|
|
109
113
|
buildsSkipped: "This plugin needs its build scripts to run; they are blocked by default for safety. Click below to allow them and reinstall:",
|
|
114
|
+
restartNow: "Restart now",
|
|
115
|
+
restarting: "Restarting…",
|
|
116
|
+
restartFail: "Restart failed",
|
|
117
|
+
restartTimeout: "Timed out waiting for DeepSeek Harness to start",
|
|
110
118
|
updateNow: "Update now",
|
|
111
119
|
updateFail: "Update failed",
|
|
112
120
|
upToDate: "Up to date",
|
|
@@ -227,10 +235,10 @@ window.__ModuleLoader__.load({ id: "dshmarket", factory: (require) => {
|
|
|
227
235
|
return matchInstalledName(plugin, installed) !== null;
|
|
228
236
|
}
|
|
229
237
|
/**
|
|
230
|
-
* The brand mark
|
|
231
|
-
*
|
|
238
|
+
* The header brand mark now lives in MarketSection.tsx as an inline SVG
|
|
239
|
+
* (official-style monochrome glyph, fill="currentColor") so it follows the
|
|
240
|
+
* active theme; the colored assets/logo.svg tile is no longer inlined here.
|
|
232
241
|
*/
|
|
233
|
-
const LOGO_URI = "data:image/svg+xml," + encodeURIComponent("<svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 128 128\"><rect width=\"128\" height=\"128\" rx=\"28\" fill=\"#f6f2ea\"/><g transform=\"translate(15.7 16.7) scale(0.3) translate(-112 -78)\"><g fill=\"#2b2620\"><rect x=\"112\" y=\"112\" width=\"88\" height=\"88\" rx=\"14\"/><rect x=\"212\" y=\"112\" width=\"88\" height=\"88\" rx=\"14\"/><rect x=\"112\" y=\"212\" width=\"88\" height=\"88\" rx=\"14\"/><rect x=\"212\" y=\"212\" width=\"88\" height=\"88\" rx=\"14\"/><rect x=\"112\" y=\"312\" width=\"88\" height=\"88\" rx=\"14\"/><rect x=\"212\" y=\"312\" width=\"88\" height=\"88\" rx=\"14\"/><rect x=\"312\" y=\"212\" width=\"88\" height=\"88\" rx=\"14\"/><rect x=\"312\" y=\"312\" width=\"88\" height=\"88\" rx=\"14\"/></g><rect x=\"346\" y=\"78\" width=\"88\" height=\"88\" rx=\"14\" fill=\"#c0392b\" transform=\"rotate(9 390 122)\"/></g></svg>");
|
|
234
242
|
/** Four representative colors for a theme card's preview strip. */
|
|
235
243
|
function themeSwatch(def) {
|
|
236
244
|
const tk = def.tokens || {};
|
|
@@ -285,61 +293,61 @@ window.__ModuleLoader__.load({ id: "dshmarket", factory: (require) => {
|
|
|
285
293
|
}
|
|
286
294
|
var Market_module_css_default = {
|
|
287
295
|
"warnBtn": "eGUBIq_warnBtn",
|
|
288
|
-
"grow": "eGUBIq_grow",
|
|
289
|
-
"sentinel": "eGUBIq_sentinel",
|
|
290
|
-
"body": "eGUBIq_body",
|
|
291
|
-
"titleRow": "eGUBIq_titleRow",
|
|
292
|
-
"tag": "eGUBIq_tag",
|
|
293
|
-
"sect": "eGUBIq_sect",
|
|
294
|
-
"catsRow": "eGUBIq_catsRow",
|
|
295
|
-
"dangerArmed": "eGUBIq_dangerArmed",
|
|
296
|
-
"catsCollapsed": "eGUBIq_catsCollapsed",
|
|
297
|
-
"head": "eGUBIq_head",
|
|
298
296
|
"star": "eGUBIq_star",
|
|
299
|
-
"
|
|
300
|
-
"
|
|
301
|
-
"
|
|
302
|
-
"
|
|
297
|
+
"catsWrap": "eGUBIq_catsWrap",
|
|
298
|
+
"row1": "eGUBIq_row1",
|
|
299
|
+
"owner": "eGUBIq_owner",
|
|
300
|
+
"spin": "eGUBIq_spin",
|
|
301
|
+
"empty": "eGUBIq_empty",
|
|
302
|
+
"sub": "eGUBIq_sub",
|
|
303
|
+
"dangerArmed": "eGUBIq_dangerArmed",
|
|
304
|
+
"warnLine": "eGUBIq_warnLine",
|
|
305
|
+
"tabs": "eGUBIq_tabs",
|
|
306
|
+
"title": "eGUBIq_title",
|
|
307
|
+
"sort": "eGUBIq_sort",
|
|
303
308
|
"tab": "eGUBIq_tab",
|
|
304
|
-
"av": "eGUBIq_av",
|
|
305
|
-
"searchInline": "eGUBIq_searchInline",
|
|
306
|
-
"desc": "eGUBIq_desc",
|
|
307
|
-
"foot": "eGUBIq_foot",
|
|
308
309
|
"themesGrid": "eGUBIq_themesGrid",
|
|
309
|
-
"
|
|
310
|
+
"root": "eGUBIq_root",
|
|
311
|
+
"grow": "eGUBIq_grow",
|
|
312
|
+
"staleAction": "eGUBIq_staleAction",
|
|
313
|
+
"cmdDetails": "eGUBIq_cmdDetails",
|
|
314
|
+
"cancelBtn": "eGUBIq_cancelBtn",
|
|
315
|
+
"nm": "eGUBIq_nm",
|
|
316
|
+
"desc": "eGUBIq_desc",
|
|
317
|
+
"catsToggle": "eGUBIq_catsToggle",
|
|
318
|
+
"irow": "eGUBIq_irow",
|
|
319
|
+
"loading": "eGUBIq_loading",
|
|
320
|
+
"cmd": "eGUBIq_cmd",
|
|
321
|
+
"restart": "eGUBIq_restart",
|
|
322
|
+
"sect": "eGUBIq_sect",
|
|
323
|
+
"titleRow": "eGUBIq_titleRow",
|
|
324
|
+
"catsRow": "eGUBIq_catsRow",
|
|
325
|
+
"dangerBtn": "eGUBIq_dangerBtn",
|
|
310
326
|
"dot": "eGUBIq_dot",
|
|
311
|
-
"
|
|
312
|
-
"descTight": "eGUBIq_descTight",
|
|
313
|
-
"row1": "eGUBIq_row1",
|
|
327
|
+
"cats": "eGUBIq_cats",
|
|
314
328
|
"progress": "eGUBIq_progress",
|
|
315
|
-
"err": "eGUBIq_err",
|
|
316
|
-
"title": "eGUBIq_title",
|
|
317
|
-
"restart": "eGUBIq_restart",
|
|
318
|
-
"cmdSummary": "eGUBIq_cmdSummary",
|
|
319
|
-
"loading": "eGUBIq_loading",
|
|
320
329
|
"sp": "eGUBIq_sp",
|
|
330
|
+
"err": "eGUBIq_err",
|
|
321
331
|
"spec": "eGUBIq_spec",
|
|
322
|
-
"
|
|
323
|
-
"
|
|
324
|
-
"
|
|
332
|
+
"pct": "eGUBIq_pct",
|
|
333
|
+
"tag": "eGUBIq_tag",
|
|
334
|
+
"catsCollapsed": "eGUBIq_catsCollapsed",
|
|
335
|
+
"src": "eGUBIq_src",
|
|
336
|
+
"cmdSummary": "eGUBIq_cmdSummary",
|
|
325
337
|
"on": "eGUBIq_on",
|
|
326
|
-
"
|
|
327
|
-
"
|
|
338
|
+
"sentinel": "eGUBIq_sentinel",
|
|
339
|
+
"searchInline": "eGUBIq_searchInline",
|
|
340
|
+
"foot": "eGUBIq_foot",
|
|
341
|
+
"swatches": "eGUBIq_swatches",
|
|
328
342
|
"modalNote": "eGUBIq_modalNote",
|
|
329
|
-
"
|
|
330
|
-
"
|
|
343
|
+
"body": "eGUBIq_body",
|
|
344
|
+
"descTight": "eGUBIq_descTight",
|
|
345
|
+
"av": "eGUBIq_av",
|
|
346
|
+
"top": "eGUBIq_top",
|
|
331
347
|
"okState": "eGUBIq_okState",
|
|
332
|
-
"
|
|
333
|
-
"
|
|
334
|
-
"
|
|
335
|
-
"nm": "eGUBIq_nm",
|
|
336
|
-
"sort": "eGUBIq_sort",
|
|
337
|
-
"warnLine": "eGUBIq_warnLine",
|
|
338
|
-
"irow": "eGUBIq_irow",
|
|
339
|
-
"cats": "eGUBIq_cats",
|
|
340
|
-
"catsToggle": "eGUBIq_catsToggle",
|
|
341
|
-
"dangerBtn": "eGUBIq_dangerBtn",
|
|
342
|
-
"catsWrap": "eGUBIq_catsWrap"
|
|
348
|
+
"card": "eGUBIq_card",
|
|
349
|
+
"head": "eGUBIq_head",
|
|
350
|
+
"grid": "eGUBIq_grid"
|
|
343
351
|
};
|
|
344
352
|
//#endregion
|
|
345
353
|
//#region src/client/MarketSection.tsx
|
|
@@ -368,6 +376,31 @@ window.__ModuleLoader__.load({ id: "dshmarket", factory: (require) => {
|
|
|
368
376
|
});
|
|
369
377
|
}
|
|
370
378
|
/**
|
|
379
|
+
* Official-style market glyph: the shared block-grid brand mark converted to
|
|
380
|
+
* the official monochrome icon form (16×16, fill="currentColor") so it
|
|
381
|
+
* follows the active theme. Mirrors the settings-nav glyph used for the
|
|
382
|
+
* "market" section id.
|
|
383
|
+
*/
|
|
384
|
+
function MarketLogo({ size = 16, style }) {
|
|
385
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("svg", {
|
|
386
|
+
width: size,
|
|
387
|
+
height: size,
|
|
388
|
+
viewBox: "0 0 16 16",
|
|
389
|
+
fill: "none",
|
|
390
|
+
xmlns: "http://www.w3.org/2000/svg",
|
|
391
|
+
"aria-hidden": "true",
|
|
392
|
+
style,
|
|
393
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("path", {
|
|
394
|
+
fill: "currentColor",
|
|
395
|
+
d: "M2.35 1.75H4.95A0.6 0.6 0 0 1 5.55 2.35V4.95A0.6 0.6 0 0 1 4.95 5.55H2.35A0.6 0.6 0 0 1 1.75 4.95V2.35A0.6 0.6 0 0 1 2.35 1.75ZM6.7 1.75H9.3A0.6 0.6 0 0 1 9.9 2.35V4.95A0.6 0.6 0 0 1 9.3 5.55H6.7A0.6 0.6 0 0 1 6.1 4.95V2.35A0.6 0.6 0 0 1 6.7 1.75ZM2.35 6.1H4.95A0.6 0.6 0 0 1 5.55 6.7V9.3A0.6 0.6 0 0 1 4.95 9.9H2.35A0.6 0.6 0 0 1 1.75 9.3V6.7A0.6 0.6 0 0 1 2.35 6.1ZM6.7 6.1H9.3A0.6 0.6 0 0 1 9.9 6.7V9.3A0.6 0.6 0 0 1 9.3 9.9H6.7A0.6 0.6 0 0 1 6.1 9.3V6.7A0.6 0.6 0 0 1 6.7 6.1ZM11.05 6.1H13.65A0.6 0.6 0 0 1 14.25 6.7V9.3A0.6 0.6 0 0 1 13.65 9.9H11.05A0.6 0.6 0 0 1 10.45 9.3V6.7A0.6 0.6 0 0 1 11.05 6.1ZM2.35 10.45H4.95A0.6 0.6 0 0 1 5.55 11.05V13.65A0.6 0.6 0 0 1 4.95 14.25H2.35A0.6 0.6 0 0 1 1.75 13.65V11.05A0.6 0.6 0 0 1 2.35 10.45ZM6.7 10.45H9.3A0.6 0.6 0 0 1 9.9 11.05V13.65A0.6 0.6 0 0 1 9.3 14.25H6.7A0.6 0.6 0 0 1 6.1 13.65V11.05A0.6 0.6 0 0 1 6.7 10.45ZM11.05 10.45H13.65A0.6 0.6 0 0 1 14.25 11.05V13.65A0.6 0.6 0 0 1 13.65 14.25H11.05A0.6 0.6 0 0 1 10.45 13.65V11.05A0.6 0.6 0 0 1 11.05 10.45Z"
|
|
396
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("path", {
|
|
397
|
+
fill: "currentColor",
|
|
398
|
+
d: "M11.05 1.75H13.65A0.6 0.6 0 0 1 14.25 2.35V4.95A0.6 0.6 0 0 1 13.65 5.55H11.05A0.6 0.6 0 0 1 10.45 4.95V2.35A0.6 0.6 0 0 1 11.05 1.75Z",
|
|
399
|
+
transform: "rotate(9 12.35 3.65)"
|
|
400
|
+
})]
|
|
401
|
+
});
|
|
402
|
+
}
|
|
403
|
+
/**
|
|
371
404
|
* Module-scope caches so re-entering the section renders instantly instead
|
|
372
405
|
* of refetching and rebuilding from a spinner (#30 by @StarsTom). Module
|
|
373
406
|
* state survives section switches; a background refetch keeps it current.
|
|
@@ -396,6 +429,8 @@ window.__ModuleLoader__.load({ id: "dshmarket", factory: (require) => {
|
|
|
396
429
|
const [cat, setCat] = (0, react.useState)("all");
|
|
397
430
|
const [confirming, setConfirming] = (0, react.useState)(null);
|
|
398
431
|
const [busyUrl, setBusyUrl] = (0, react.useState)(null);
|
|
432
|
+
/** Consecutive idle polls with a pending install that never landed (#32). */
|
|
433
|
+
const idleStrikes = (0, react.useRef)(0);
|
|
399
434
|
const [doneUrls, setDoneUrls] = (0, react.useState)([]);
|
|
400
435
|
const [installError, setInstallError] = (0, react.useState)(null);
|
|
401
436
|
const [updates, setUpdates] = (0, react.useState)({});
|
|
@@ -429,6 +464,9 @@ window.__ModuleLoader__.load({ id: "dshmarket", factory: (require) => {
|
|
|
429
464
|
const [envFixing, setEnvFixing] = (0, react.useState)(false);
|
|
430
465
|
const [envFailed, setEnvFailed] = (0, react.useState)(false);
|
|
431
466
|
const [bootId, setBootId] = (0, react.useState)(null);
|
|
467
|
+
/** One-click restart (#14 by @ysyyhhh): server capability + in-flight state. */
|
|
468
|
+
const [restartEnabled, setRestartEnabled] = (0, react.useState)(false);
|
|
469
|
+
const [restarting, setRestarting] = (0, react.useState)(false);
|
|
432
470
|
const [showTop, setShowTop] = (0, react.useState)(false);
|
|
433
471
|
const bodyRef = (0, react.useRef)(null);
|
|
434
472
|
const [sort, setSort] = (0, react.useState)("hot");
|
|
@@ -453,6 +491,7 @@ window.__ModuleLoader__.load({ id: "dshmarket", factory: (require) => {
|
|
|
453
491
|
fetch("/dsh-market/status", { cache: "no-store" }).then((res) => res.json()).then((status) => {
|
|
454
492
|
setEnvReady(status.pnpm !== false);
|
|
455
493
|
if (typeof status.boot === "string") setBootId(status.boot);
|
|
494
|
+
setRestartEnabled(status.restart === true);
|
|
456
495
|
}).catch(() => {});
|
|
457
496
|
refreshInstalled();
|
|
458
497
|
}, [refreshInstalled]);
|
|
@@ -492,7 +531,10 @@ window.__ModuleLoader__.load({ id: "dshmarket", factory: (require) => {
|
|
|
492
531
|
body: "{}"
|
|
493
532
|
}).then((res) => res.json()).then((body) => {
|
|
494
533
|
if (body.ok) setEnvReady(true);
|
|
495
|
-
else
|
|
534
|
+
else {
|
|
535
|
+
setEnvFailed(true);
|
|
536
|
+
if (typeof body.error === "string") setInstallError(body.error);
|
|
537
|
+
}
|
|
496
538
|
}).catch(() => setEnvFailed(true)).finally(() => setEnvFixing(false));
|
|
497
539
|
}, []);
|
|
498
540
|
(0, react.useEffect)(() => {
|
|
@@ -519,9 +561,15 @@ window.__ModuleLoader__.load({ id: "dshmarket", factory: (require) => {
|
|
|
519
561
|
setInstalled(status.installed || {});
|
|
520
562
|
if (readSession("dshm-pending") !== null && busyUrl !== null) {
|
|
521
563
|
if (data !== null && data.plugins.some((p) => p.url === busyUrl && isInstalled(p, status.installed || {}))) {
|
|
564
|
+
idleStrikes.current = 0;
|
|
522
565
|
sessionStorage.removeItem("dshm-pending");
|
|
523
566
|
setDoneUrls((urls) => urls.includes(busyUrl) ? urls : urls.concat(busyUrl));
|
|
524
567
|
setBusyUrl(null);
|
|
568
|
+
} else if (++idleStrikes.current >= 2) {
|
|
569
|
+
idleStrikes.current = 0;
|
|
570
|
+
sessionStorage.removeItem("dshm-pending");
|
|
571
|
+
setBusyUrl(null);
|
|
572
|
+
setInstallError(t("installFail") + " — " + t("exportLog"));
|
|
525
573
|
}
|
|
526
574
|
}
|
|
527
575
|
}
|
|
@@ -602,6 +650,57 @@ window.__ModuleLoader__.load({ id: "dshmarket", factory: (require) => {
|
|
|
602
650
|
setInstallError(t("installFail") + ": " + String(error));
|
|
603
651
|
}).finally(() => setBusyUrl(null));
|
|
604
652
|
}, [refreshInstalled, t]);
|
|
653
|
+
/**
|
|
654
|
+
* Restart the host and reload once the boot id changes (#14 by @ysyyhhh).
|
|
655
|
+
* The 202 races the process's SIGTERM, so network errors on the initial
|
|
656
|
+
* request are expected and treated as "restart under way".
|
|
657
|
+
*/
|
|
658
|
+
const doRestart = (0, react.useCallback)(() => {
|
|
659
|
+
if (bootId === null || restarting) return;
|
|
660
|
+
const previousBoot = bootId;
|
|
661
|
+
setRestarting(true);
|
|
662
|
+
setInstallError(null);
|
|
663
|
+
const awaitNewBoot = () => {
|
|
664
|
+
const deadline = Date.now() + 6e4;
|
|
665
|
+
const poll = () => {
|
|
666
|
+
fetch("/dsh-market/status", { cache: "no-store" }).then((res) => res.json()).then((next) => {
|
|
667
|
+
if (typeof next.boot === "string" && next.boot !== previousBoot) {
|
|
668
|
+
location.reload();
|
|
669
|
+
return;
|
|
670
|
+
}
|
|
671
|
+
retry();
|
|
672
|
+
}).catch(retry);
|
|
673
|
+
};
|
|
674
|
+
const retry = () => {
|
|
675
|
+
if (Date.now() > deadline) {
|
|
676
|
+
setRestarting(false);
|
|
677
|
+
setInstallError(t("restartTimeout"));
|
|
678
|
+
return;
|
|
679
|
+
}
|
|
680
|
+
setTimeout(poll, 1500);
|
|
681
|
+
};
|
|
682
|
+
poll();
|
|
683
|
+
};
|
|
684
|
+
fetch("/dsh-market/restart", {
|
|
685
|
+
method: "POST",
|
|
686
|
+
headers: { "content-type": "application/json" },
|
|
687
|
+
body: "{}"
|
|
688
|
+
}).then((res) => res.json().then((body) => ({
|
|
689
|
+
status: res.status,
|
|
690
|
+
body
|
|
691
|
+
}))).then(({ status, body }) => {
|
|
692
|
+
if (status !== 202 || body.ok !== true) {
|
|
693
|
+
setRestarting(false);
|
|
694
|
+
setInstallError(t("restartFail") + ": " + String(body.error || "HTTP " + String(status)));
|
|
695
|
+
return;
|
|
696
|
+
}
|
|
697
|
+
awaitNewBoot();
|
|
698
|
+
}).catch(awaitNewBoot);
|
|
699
|
+
}, [
|
|
700
|
+
bootId,
|
|
701
|
+
restarting,
|
|
702
|
+
t
|
|
703
|
+
]);
|
|
605
704
|
/** Cancel the running plugin command (#6 by @qichuang321). */
|
|
606
705
|
const doCancel = (0, react.useCallback)(() => {
|
|
607
706
|
fetch("/dsh-market/cancel", {
|
|
@@ -951,15 +1050,9 @@ window.__ModuleLoader__.load({ id: "dshmarket", factory: (require) => {
|
|
|
951
1050
|
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
952
1051
|
className: Market_module_css_default.titleRow,
|
|
953
1052
|
children: [
|
|
954
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(
|
|
955
|
-
|
|
956
|
-
|
|
957
|
-
height: 22,
|
|
958
|
-
alt: "",
|
|
959
|
-
style: {
|
|
960
|
-
borderRadius: "5px",
|
|
961
|
-
flexShrink: 0
|
|
962
|
-
}
|
|
1053
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(MarketLogo, {
|
|
1054
|
+
size: 22,
|
|
1055
|
+
style: { flexShrink: 0 }
|
|
963
1056
|
}),
|
|
964
1057
|
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("h2", {
|
|
965
1058
|
className: Market_module_css_default.title,
|
|
@@ -1092,6 +1185,13 @@ window.__ModuleLoader__.load({ id: "dshmarket", factory: (require) => {
|
|
|
1092
1185
|
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
1093
1186
|
title: t("restartHint"),
|
|
1094
1187
|
children: "ℹ️"
|
|
1188
|
+
}),
|
|
1189
|
+
restartEnabled && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Button, {
|
|
1190
|
+
variant: "primary",
|
|
1191
|
+
size: "sm",
|
|
1192
|
+
disabled: restarting || busyUrl !== null || updatingName !== null || removingName !== null,
|
|
1193
|
+
onClick: doRestart,
|
|
1194
|
+
children: restarting ? t("restarting") : t("restartNow")
|
|
1095
1195
|
})
|
|
1096
1196
|
]
|
|
1097
1197
|
})
|