dshmarket 1.2.4 → 1.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.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,6 +46,9 @@ 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
 
50
54
  ## Data source
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,6 +46,9 @@ 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
 
50
54
  ## 数据源
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",
@@ -284,62 +292,62 @@ window.__ModuleLoader__.load({ id: "dshmarket", factory: (require) => {
284
292
  document.head.appendChild(tag);
285
293
  }
286
294
  var Market_module_css_default = {
287
- "warnBtn": "eGUBIq_warnBtn",
295
+ "dangerBtn": "eGUBIq_dangerBtn",
296
+ "top": "eGUBIq_top",
297
+ "catsWrap": "eGUBIq_catsWrap",
298
+ "catsToggle": "eGUBIq_catsToggle",
299
+ "grid": "eGUBIq_grid",
288
300
  "grow": "eGUBIq_grow",
289
- "sentinel": "eGUBIq_sentinel",
290
- "body": "eGUBIq_body",
291
301
  "titleRow": "eGUBIq_titleRow",
292
- "tag": "eGUBIq_tag",
302
+ "staleAction": "eGUBIq_staleAction",
303
+ "modalNote": "eGUBIq_modalNote",
304
+ "star": "eGUBIq_star",
293
305
  "sect": "eGUBIq_sect",
306
+ "row1": "eGUBIq_row1",
307
+ "av": "eGUBIq_av",
308
+ "warnBtn": "eGUBIq_warnBtn",
309
+ "pct": "eGUBIq_pct",
310
+ "loading": "eGUBIq_loading",
311
+ "cancelBtn": "eGUBIq_cancelBtn",
312
+ "spec": "eGUBIq_spec",
294
313
  "catsRow": "eGUBIq_catsRow",
314
+ "cats": "eGUBIq_cats",
315
+ "themesGrid": "eGUBIq_themesGrid",
316
+ "progress": "eGUBIq_progress",
317
+ "descTight": "eGUBIq_descTight",
318
+ "irow": "eGUBIq_irow",
319
+ "card": "eGUBIq_card",
320
+ "owner": "eGUBIq_owner",
321
+ "warnLine": "eGUBIq_warnLine",
322
+ "nm": "eGUBIq_nm",
295
323
  "dangerArmed": "eGUBIq_dangerArmed",
324
+ "desc": "eGUBIq_desc",
325
+ "empty": "eGUBIq_empty",
326
+ "src": "eGUBIq_src",
327
+ "body": "eGUBIq_body",
328
+ "err": "eGUBIq_err",
296
329
  "catsCollapsed": "eGUBIq_catsCollapsed",
330
+ "tab": "eGUBIq_tab",
331
+ "on": "eGUBIq_on",
332
+ "sort": "eGUBIq_sort",
333
+ "cmd": "eGUBIq_cmd",
334
+ "tag": "eGUBIq_tag",
297
335
  "head": "eGUBIq_head",
298
- "star": "eGUBIq_star",
299
- "swatches": "eGUBIq_swatches",
300
- "card": "eGUBIq_card",
301
- "grid": "eGUBIq_grid",
336
+ "okState": "eGUBIq_okState",
302
337
  "root": "eGUBIq_root",
303
- "tab": "eGUBIq_tab",
304
- "av": "eGUBIq_av",
305
- "searchInline": "eGUBIq_searchInline",
306
- "desc": "eGUBIq_desc",
307
- "foot": "eGUBIq_foot",
308
- "themesGrid": "eGUBIq_themesGrid",
309
- "src": "eGUBIq_src",
338
+ "tabs": "eGUBIq_tabs",
310
339
  "dot": "eGUBIq_dot",
311
- "spin": "eGUBIq_spin",
312
- "descTight": "eGUBIq_descTight",
313
- "row1": "eGUBIq_row1",
314
- "progress": "eGUBIq_progress",
315
- "err": "eGUBIq_err",
316
340
  "title": "eGUBIq_title",
317
- "restart": "eGUBIq_restart",
318
- "cmdSummary": "eGUBIq_cmdSummary",
319
- "loading": "eGUBIq_loading",
320
341
  "sp": "eGUBIq_sp",
321
- "spec": "eGUBIq_spec",
322
- "staleAction": "eGUBIq_staleAction",
323
- "cancelBtn": "eGUBIq_cancelBtn",
324
- "tabs": "eGUBIq_tabs",
325
- "on": "eGUBIq_on",
326
- "top": "eGUBIq_top",
327
342
  "sub": "eGUBIq_sub",
328
- "modalNote": "eGUBIq_modalNote",
343
+ "restart": "eGUBIq_restart",
344
+ "swatches": "eGUBIq_swatches",
329
345
  "cmdDetails": "eGUBIq_cmdDetails",
330
- "owner": "eGUBIq_owner",
331
- "okState": "eGUBIq_okState",
332
- "empty": "eGUBIq_empty",
333
- "cmd": "eGUBIq_cmd",
334
- "pct": "eGUBIq_pct",
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"
346
+ "foot": "eGUBIq_foot",
347
+ "spin": "eGUBIq_spin",
348
+ "sentinel": "eGUBIq_sentinel",
349
+ "cmdSummary": "eGUBIq_cmdSummary",
350
+ "searchInline": "eGUBIq_searchInline"
343
351
  };
344
352
  //#endregion
345
353
  //#region src/client/MarketSection.tsx
@@ -429,6 +437,9 @@ window.__ModuleLoader__.load({ id: "dshmarket", factory: (require) => {
429
437
  const [envFixing, setEnvFixing] = (0, react.useState)(false);
430
438
  const [envFailed, setEnvFailed] = (0, react.useState)(false);
431
439
  const [bootId, setBootId] = (0, react.useState)(null);
440
+ /** One-click restart (#14 by @ysyyhhh): server capability + in-flight state. */
441
+ const [restartEnabled, setRestartEnabled] = (0, react.useState)(false);
442
+ const [restarting, setRestarting] = (0, react.useState)(false);
432
443
  const [showTop, setShowTop] = (0, react.useState)(false);
433
444
  const bodyRef = (0, react.useRef)(null);
434
445
  const [sort, setSort] = (0, react.useState)("hot");
@@ -453,6 +464,7 @@ window.__ModuleLoader__.load({ id: "dshmarket", factory: (require) => {
453
464
  fetch("/dsh-market/status", { cache: "no-store" }).then((res) => res.json()).then((status) => {
454
465
  setEnvReady(status.pnpm !== false);
455
466
  if (typeof status.boot === "string") setBootId(status.boot);
467
+ setRestartEnabled(status.restart === true);
456
468
  }).catch(() => {});
457
469
  refreshInstalled();
458
470
  }, [refreshInstalled]);
@@ -602,6 +614,57 @@ window.__ModuleLoader__.load({ id: "dshmarket", factory: (require) => {
602
614
  setInstallError(t("installFail") + ": " + String(error));
603
615
  }).finally(() => setBusyUrl(null));
604
616
  }, [refreshInstalled, t]);
617
+ /**
618
+ * Restart the host and reload once the boot id changes (#14 by @ysyyhhh).
619
+ * The 202 races the process's SIGTERM, so network errors on the initial
620
+ * request are expected and treated as "restart under way".
621
+ */
622
+ const doRestart = (0, react.useCallback)(() => {
623
+ if (bootId === null || restarting) return;
624
+ const previousBoot = bootId;
625
+ setRestarting(true);
626
+ setInstallError(null);
627
+ const awaitNewBoot = () => {
628
+ const deadline = Date.now() + 6e4;
629
+ const poll = () => {
630
+ fetch("/dsh-market/status", { cache: "no-store" }).then((res) => res.json()).then((next) => {
631
+ if (typeof next.boot === "string" && next.boot !== previousBoot) {
632
+ location.reload();
633
+ return;
634
+ }
635
+ retry();
636
+ }).catch(retry);
637
+ };
638
+ const retry = () => {
639
+ if (Date.now() > deadline) {
640
+ setRestarting(false);
641
+ setInstallError(t("restartTimeout"));
642
+ return;
643
+ }
644
+ setTimeout(poll, 1500);
645
+ };
646
+ poll();
647
+ };
648
+ fetch("/dsh-market/restart", {
649
+ method: "POST",
650
+ headers: { "content-type": "application/json" },
651
+ body: "{}"
652
+ }).then((res) => res.json().then((body) => ({
653
+ status: res.status,
654
+ body
655
+ }))).then(({ status, body }) => {
656
+ if (status !== 202 || body.ok !== true) {
657
+ setRestarting(false);
658
+ setInstallError(t("restartFail") + ": " + String(body.error || "HTTP " + String(status)));
659
+ return;
660
+ }
661
+ awaitNewBoot();
662
+ }).catch(awaitNewBoot);
663
+ }, [
664
+ bootId,
665
+ restarting,
666
+ t
667
+ ]);
605
668
  /** Cancel the running plugin command (#6 by @qichuang321). */
606
669
  const doCancel = (0, react.useCallback)(() => {
607
670
  fetch("/dsh-market/cancel", {
@@ -1092,6 +1155,13 @@ window.__ModuleLoader__.load({ id: "dshmarket", factory: (require) => {
1092
1155
  /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
1093
1156
  title: t("restartHint"),
1094
1157
  children: "ℹ️"
1158
+ }),
1159
+ restartEnabled && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Button, {
1160
+ variant: "primary",
1161
+ size: "sm",
1162
+ disabled: restarting || busyUrl !== null || updatingName !== null || removingName !== null,
1163
+ onClick: doRestart,
1164
+ children: restarting ? t("restarting") : t("restartNow")
1095
1165
  })
1096
1166
  ]
1097
1167
  })