dshmarket 1.16.2 → 1.16.4

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
@@ -62,7 +62,7 @@ Installs prefer npm tarballs over full-repo GitHub downloads whenever a plugin p
62
62
  - The install endpoint accepts same-origin POST only; the market never phones home
63
63
  - Backups can contain credentials from your profile config — the UI warns before export and upload; WebDAV sync is https-only, refuses private-network targets, and never stores your password in the browser
64
64
  - The restart endpoint additionally requires a direct loopback client (forwarded requests are rejected) and relaunches the exact DSH entry, arguments, environment, and working directory
65
- - One-click restart launches a detached replacement. If DSH is managed by systemd, launchd, pm2, or another supervisor, turn the restart button off and let the supervisor own restarts instead; the pending-change notice remains visible but the button is hidden. Either flip **Allow restart** off in **Settings → Plugins → Plugin configuration**, or write it into the profile patch — where it has to sit under `config:`, because the loader passes only that sub-object to a plugin and a top-level `allowRestart:` is silently ignored (#227 by @Fantasymax):
65
+ - One-click restart launches a detached replacement. **When this host is systemd's own service process the button is hidden automatically** the market would otherwise kill the takeover process along with the unit's cgroup and the service would not come back. The pending-change notice stays visible and says so. Detection requires both a systemd marker AND being the unit's main process, because `INVOCATION_ID` is inherited by every descendant of a unit (an ordinary terminal included) and hiding the button for those would be the worse bug. pm2 and launchd are not detected, so those deployments need the explicit setting below. Either flip **Allow restart** off in **Settings → Plugins → Plugin configuration**, or write it into the profile patch — where it has to sit under `config:`, because the loader passes only that sub-object to a plugin and a top-level `allowRestart:` is silently ignored (#227 by @Fantasymax):
66
66
 
67
67
  ```yaml
68
68
  - id: dsh-market
package/README.zh.md CHANGED
@@ -59,7 +59,7 @@ dsh plugin --profile web add dshmarket
59
59
  - 安装接口只接受同源 POST;市场不会向任何地方上报数据
60
60
  - 备份可能包含 profile 配置里的密钥——导出与上传前 UI 会明确提醒;WebDAV 同步仅限 https、拒绝内网地址,且密码永不落盘浏览器
61
61
  - 重启接口还要求客户端直接来自环回地址(拒绝代理转发请求),并使用原入口、参数、环境和工作目录重新启动 DSH
62
- - 一键重启会启动脱离终端的替代进程。若 DSH systemd、launchd、pm2 等进程管理器托管,请关掉重启按钮,交由管理器负责重启;待重启提示仍会显示,但按钮会隐藏。两种做法:在**设置 → 插件 → 插件配置**里关掉「允许重启」,或者写进 profile 补丁——注意必须嵌在 `config:` 下面,因为 loader 只把这个子对象传给插件,写在顶层会静默失效(#227,感谢 @Fantasymax):
62
+ - 一键重启会启动脱离终端的替代进程。**当本进程就是 systemd 服务的主进程时,按钮会自动隐藏**——否则市场重启会连带杀掉 cgroup 里的接管进程,服务起不来,待重启提示会说明原因。判定要求「systemd 标记」和「本进程是该 unit 的主进程」同时成立:`INVOCATION_ID` 会被 unit 的所有后代继承(包括普通终端),只看它会误伤一大批本来能正常重启的机器。pm2 launchd 不做检测,这类部署需要下面的显式配置。两种做法:在**设置 → 插件 → 插件配置**里关掉「允许重启」,或者写进 profile 补丁——注意必须嵌在 `config:` 下面,因为 loader 只把这个子对象传给插件,写在顶层会静默失效(#227,感谢 @Fantasymax):
63
63
 
64
64
  ```yaml
65
65
  - id: dsh-market
package/client/client.js CHANGED
@@ -83,6 +83,7 @@ window.__ModuleLoader__.load({ id: "dshmarket", factory: (require) => {
83
83
  uninstalling: "卸载中…",
84
84
  uninstallConfirmDesc: "将从当前 profile 中移除该插件。",
85
85
  restartHint: "重启方式:关闭当前 dsh 进程后重新运行(例如 dsh web)",
86
+ restartHintSupervised: "本进程是 {0} 服务的主进程,重启交给它负责——市场自己重启会连带杀掉 cgroup 里的接管进程,服务将起不来。请执行 systemctl restart <你的 unit>。确认你的配置能承受市场自行重启(如 KillMode=process),可在本插件配置里手动打开「允许重启」。",
86
87
  confirmTitle: "安装",
87
88
  confirmWarn: "插件是社区第三方代码。安装即表示你信任该来源;构建脚本默认被禁止执行。",
88
89
  cancel: "取消",
@@ -439,6 +440,7 @@ window.__ModuleLoader__.load({ id: "dshmarket", factory: (require) => {
439
440
  uninstalling: "Removing…",
440
441
  uninstallConfirmDesc: "This removes the plugin from the current profile.",
441
442
  restartHint: "To restart: stop the current dsh process and run it again (e.g. dsh web)",
443
+ restartHintSupervised: "This process is {0}'s own service process, so restarts belong to it — restarting from here would kill the takeover process along with the cgroup and the service would not come back. Use systemctl restart &lt;your unit&gt;. If your unit can survive a self-restart (KillMode=process), turn Allow restart on in this plugin's configuration.",
442
444
  confirmTitle: "Install",
443
445
  confirmWarn: "Plugins are third-party community code. Installing means you trust this source; build scripts are blocked by default.",
444
446
  cancel: "Cancel",
@@ -770,8 +772,8 @@ window.__ModuleLoader__.load({ id: "dshmarket", factory: (require) => {
770
772
  }
771
773
  /** Heuristic: plugins that target a terminal surface rather than the web UI. */
772
774
  function looksTerminal(plugin, lang) {
773
- const desc = plugin.description && (plugin.description[lang] || plugin.description.en) || "";
774
- return /\b(tui|cli|tty|terminal)\b|终端|命令行/i.test(plugin.name + " " + desc);
775
+ const positiveDesc = (plugin.description && (plugin.description[lang] || plugin.description.en) || "").replace(/\b(?:no|without)\b[^.!?;:,。!?;\n]{0,80}\b(?:tui|cli|tty|terminal)\b/gi, "").replace(/(?:无需|无须|不需要|不用)[^。!?;\n]{0,48}(?:tui|cli|tty|terminal|终端|命令行)/gi, "");
776
+ return /\b(tui|cli|tty|terminal)\b|终端|命令行/i.test(plugin.name + " " + positiveDesc);
775
777
  }
776
778
  /** Days per TimeRange (`all` has no cutoff and is handled by the caller). */
777
779
  const TIME_RANGE_DAYS = {
@@ -3719,6 +3721,8 @@ window.__ModuleLoader__.load({ id: "dshmarket", factory: (require) => {
3719
3721
  const [busyUrl, setBusyUrl] = (0, react.useState)(null);
3720
3722
  /** Consecutive idle polls with a pending install that never landed (#32). */
3721
3723
  const idleStrikes = (0, react.useRef)(0);
3724
+ /** Same idle-strike bookkeeping for an update whose response was lost. */
3725
+ const updateIdleStrikes = (0, react.useRef)(0);
3722
3726
  const [doneUrls, setDoneUrls] = (0, react.useState)([]);
3723
3727
  const [installError, setInstallError] = (0, react.useState)(null);
3724
3728
  const [compatibilityNotice, setCompatibilityNotice] = (0, react.useState)(null);
@@ -3828,6 +3832,8 @@ window.__ModuleLoader__.load({ id: "dshmarket", factory: (require) => {
3828
3832
  const [bootId, setBootId] = (0, react.useState)(null);
3829
3833
  /** One-click restart (#14 by @ysyyhhh): server capability + in-flight state. */
3830
3834
  const [restartEnabled, setRestartEnabled] = (0, react.useState)(false);
3835
+ /** Supervisor the host detected around itself, when it named one (#229). */
3836
+ const [supervisor, setSupervisor] = (0, react.useState)(null);
3831
3837
  const [restarting, setRestarting] = (0, react.useState)(false);
3832
3838
  const [showTop, setShowTop] = (0, react.useState)(false);
3833
3839
  const [backupBusy, setBackupBusy] = (0, react.useState)(false);
@@ -3946,6 +3952,7 @@ window.__ModuleLoader__.load({ id: "dshmarket", factory: (require) => {
3946
3952
  } catch {}
3947
3953
  }
3948
3954
  setRestartEnabled(status.restart === true);
3955
+ setSupervisor(typeof status.supervisor === "string" ? status.supervisor : null);
3949
3956
  if (typeof status.version === "string" && status.version !== "") setVersion(status.version);
3950
3957
  }).catch(() => {});
3951
3958
  refreshInstalled();
@@ -4001,6 +4008,8 @@ window.__ModuleLoader__.load({ id: "dshmarket", factory: (require) => {
4001
4008
  (0, react.useEffect)(() => {
4002
4009
  const pending = readSession("dshm-pending");
4003
4010
  if (pending !== null && typeof pending.url === "string") setBusyUrl(pending.url);
4011
+ const updating = readSession("dshm-updating");
4012
+ if (updating !== null && typeof updating.name === "string" && updating.name !== "") setUpdatingName(updating.name);
4004
4013
  }, []);
4005
4014
  (0, react.useEffect)(() => {
4006
4015
  if (busyUrl === null && updatingName === null) {
@@ -4055,6 +4064,14 @@ window.__ModuleLoader__.load({ id: "dshmarket", factory: (require) => {
4055
4064
  setInstallError(t("installFail") + " — " + t("exportLog"));
4056
4065
  }
4057
4066
  }
4067
+ if (updatingName !== null && status.busy !== true) {
4068
+ if (++updateIdleStrikes.current >= 2) {
4069
+ updateIdleStrikes.current = 0;
4070
+ sessionStorage.removeItem("dshm-updating");
4071
+ setUpdatingName(null);
4072
+ refreshInstalled();
4073
+ }
4074
+ } else updateIdleStrikes.current = 0;
4058
4075
  }
4059
4076
  }).catch(() => {});
4060
4077
  }, 2e3);
@@ -4404,6 +4421,8 @@ window.__ModuleLoader__.load({ id: "dshmarket", factory: (require) => {
4404
4421
  setActivationWarnings([]);
4405
4422
  setStaleName(null);
4406
4423
  setUpdatingName(name);
4424
+ updateIdleStrikes.current = 0;
4425
+ sessionStorage.setItem("dshm-updating", JSON.stringify({ name }));
4407
4426
  return fetch("/dsh-market/update", {
4408
4427
  method: "POST",
4409
4428
  headers: { "content-type": "application/json" },
@@ -4415,6 +4434,8 @@ window.__ModuleLoader__.load({ id: "dshmarket", factory: (require) => {
4415
4434
  status: res.status,
4416
4435
  body
4417
4436
  }))).then(({ status, body }) => {
4437
+ sessionStorage.removeItem("dshm-updating");
4438
+ setUpdatingName(null);
4418
4439
  if (body.cancelled === true) {
4419
4440
  refreshInstalled();
4420
4441
  if (body.partial === true) setInstallError(t("partialNote"));
@@ -4447,7 +4468,7 @@ window.__ModuleLoader__.load({ id: "dshmarket", factory: (require) => {
4447
4468
  const detail = text(body.error) || humanOutput([text(body.stderr), text(body.stdout)].filter(Boolean).join("\n")) || "exit " + body.exitCode;
4448
4469
  setInstallError(t("updateFail") + ": " + name + " — " + detail.trim().slice(-600));
4449
4470
  }
4450
- }).catch((error) => setInstallError(t("updateFail") + ": " + String(error))).finally(() => setUpdatingName(null));
4471
+ }).catch(() => {});
4451
4472
  }, [refreshInstalled, t]);
4452
4473
  const doUseSkin = (0, react.useCallback)((name) => {
4453
4474
  setInstallError(null);
@@ -5534,7 +5555,7 @@ window.__ModuleLoader__.load({ id: "dshmarket", factory: (require) => {
5534
5555
  ]
5535
5556
  }),
5536
5557
  /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Tooltip, {
5537
- label: t("restartHint"),
5558
+ label: supervisor === null ? t("restartHint") : t("restartHintSupervised").replace("{0}", supervisor),
5538
5559
  side: "bottom",
5539
5560
  children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
5540
5561
  className: Market_module_css_default.bannerHint,