@noob-stupid/dsh-plugin-console 0.5.87 → 0.5.92

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
@@ -461,12 +461,72 @@ Verification matrix, raw logs and the one framework-side caveat (`0.1.2-rc.1` sh
461
461
 
462
462
  ---
463
463
 
464
+ ### Update integrity
465
+
466
+ An update can be requested for a specific version. That version is carried into every download channel; if
467
+ a channel ends up with a different version, the job reports a failure with both versions named, and the
468
+ existing copy on disk is kept rather than replaced. The manifest and lock record a **version number** for
469
+ such packages — a normal install does not produce a `link:`.
470
+
471
+ Installs and updates are tracked per target: two different plugins can update at the same time, while the
472
+ same plugin cannot be started twice.
473
+
474
+ A finished install leaves two small records under `<DSH_HOME>/plugin-console/`:
475
+
476
+ - `pending-restart.json` — plugins that are installed but not yet loaded, so the panel can keep showing
477
+ "installed · takes effect after restart" across a host restart. Entries are dropped once the plugin is
478
+ actually loaded.
479
+ - `install-jobs.json` — the last fifty settled jobs, with the requested version, the version that landed,
480
+ and the error if any. A failure can therefore still be explained after the process that produced it is gone.
481
+
482
+ Drift between the manifest and the lock is reconciled in the background with a cooldown, without blocking
483
+ `/state`. Source-type dependencies (`link:`, `file:`, git) are excluded from that automation. A mismatch is
484
+ reported as a failure only when it would actually lose the plugin; a lock or version drift is shown as a
485
+ collapsible note instead.
486
+
487
+ ### Dev links (`link`)
488
+
489
+ `link:` is a **development** mechanism — ordinary download installs and updates do not produce it. The
490
+ `link` entry in the feature-pack toolbar is the working surface for it:
491
+
492
+ - pick a registered dev root from the dropdown, or type an absolute path;
493
+ - **Pin** is a two-step action: it first returns a read-only plan, and executes only after you confirm. No
494
+ code path skips that confirmation;
495
+ - each row shows the target directory and its last-modified time, which is how you tell whether it is the
496
+ directory you are editing right now;
497
+ - per-row actions appear only when they apply — **fill dependencies**, **build shims** (no pnpm run), and
498
+ **unpin**;
499
+ - **Browse…** is rendered only when the desktop preload bridge is present. On a web instance it is not
500
+ rendered, rather than shown as a button that cannot work.
501
+
502
+ ### Disable provenance
503
+
504
+ A row can be off for different reasons, and the panel distinguishes them:
505
+
506
+ - **disabled by this console's patch** — removing the patch block enables it normally;
507
+ - **disabled by the plugin or the framework default** — the console can only write an explicit
508
+ `disabled: false` override, which is what the **forced on by patch** badge refers to;
509
+ - **force-enable had no effect** — the override exists but the row still is not running, and no peer veto
510
+ explains it. That override can be revoked from the row.
511
+
512
+ ### Operational details
513
+
514
+ - Market enrichment runs behind a single limiter: four concurrent requests, one in flight per URL, a
515
+ ten-minute memory for failures and a five-minute success cache. A batch of unreachable mirrors therefore
516
+ cannot saturate the connection or delay installs.
517
+ - `Disk audit` lists what occupies `<DSH_HOME>/plugin-console` and deletes only after an explicit
518
+ confirmation. The framework rollback point counts as protected.
519
+ - The gate panel shows the current pending adaptations up front and keeps its history collapsed.
520
+
464
521
  ## Known limitations
465
522
 
466
- - Host code changes require a **service restart**: the panel's restart button only points at the
467
- **official path** (restart inside the desktop client; the console never kills or spawns a
468
- process — a manual relaunch would fight the host for the port and produce
469
- "The application could not start or stopped unexpectedly"); client changes just need a page refresh;
523
+ - Host code changes require a **service restart**; client changes only need a page refresh.
524
+ - On a **desktop-hosted** instance the console **refuses** to restart and says so: the official path is to
525
+ fully quit and reopen the client. It never kills or spawns a process there — a manual relaunch would
526
+ fight the host for the port and produce "The application could not start or stopped unexpectedly".
527
+ - On a **standalone (web) instance** the restart button is available: it relaunches detached, waits for
528
+ the port to free, and lets the old process exit gracefully. The toolbar entry is rendered only where it
529
+ can work — a desktop-hosted instance does not show it.
470
530
  - Live GitHub search depends on GitHub reachability (browser-direct + server fallback;
471
531
  during network-blackout windows retry later);
472
532
  - Version detection works for npm-published packages; skill-type repos have no version concept;
package/lib/client.js CHANGED
@@ -464,9 +464,9 @@ window.__ModuleLoader__.load({
464
464
  stageInstalling: "下载安装中(可离开本页,后台继续)",
465
465
  stageConfiguring: "写入启用配置",
466
466
  stageRepairing: "本地 AI 接管安装中…",
467
- restartService: "重启服务(官方路径)",
467
+ restartService: "重启服务",
468
468
  restartHostedNotice: "本实例由桌面端外壳托管 —— 请在桌面端内重启:完全退出 DeepSeek Harness 再打开(控制台不自行 kill / spawn,以免与宿主争端口)。",
469
- restarting: "正在按官方路径处理重启请求…",
469
+ restarting: "正在处理重启请求…",
470
470
  autoReload: "页面即将自动刷新…",
471
471
  aiRepaired: "本地 AI 已接管并完成修复,请刷新页面查看",
472
472
  deletePlugin: "删除插件(移除配置并卸载包)",
@@ -1111,9 +1111,9 @@ window.__ModuleLoader__.load({
1111
1111
  stageInstalling: "Downloading & installing (safe to leave this page)",
1112
1112
  stageConfiguring: "Writing enable config",
1113
1113
  stageRepairing: "Local AI is taking over the install…",
1114
- restartService: "Restart service (official path)",
1114
+ restartService: "Restart service",
1115
1115
  restartHostedNotice: "This instance is hosted by the desktop shell - restart it there by fully quitting and reopening DeepSeek Harness.",
1116
- restarting: "Handling the restart request via the official path…",
1116
+ restarting: "Handling the restart request…",
1117
1117
  autoReload: "Page will auto-reload…",
1118
1118
  aiRepaired: "Local AI took over and finished the repair; refresh the page",
1119
1119
  deletePlugin: "Delete plugin (remove config and uninstall package)",
@@ -1771,12 +1771,24 @@ function healthSignature(health) {
1771
1771
  }
1772
1772
  /** 重启服务(按钮 / 工具项 / 自更新后共用):服务端在「本实例由桌面端外壳托管」等场景会**拒绝**
1773
1773
  * 并回结构化短句 —— 必须把它显示出来,否则按钮看起来是「点了没反应」(旧实现把错误吞掉了)。 */
1774
+ // ★ 0.5.92:本函数是模块级的,**不能用组件的 t()** —— 用了就抛 ReferenceError,
1775
+ // 点击直接异常、请求都发不出去(这正是"点了没反应"的真因)。文案用常量,永不抛。
1776
+ const RESTART_IN_PROGRESS_NOTE = "正在处理重启请求…";
1777
+ const RESTART_FAILED_NOTE = "重启请求失败";
1774
1778
  function restartServiceViaServer(setMessage, options) {
1775
1779
  const reload = (options ?? {}).reload !== false;
1776
- if (reload) setMessage(t("restarting"));
1780
+ if (reload) setMessage(RESTART_IN_PROGRESS_NOTE);
1777
1781
  call("/plugin-console/restart", {}).then(
1778
- () => { if (reload) window.setTimeout(() => window.location.reload(), 12000); },
1779
- (error) => { setMessage(error !== null && typeof error.message === "string" && error.message !== "" ? error.message : t("safetySwitchFailed")); },
1782
+ (data) => {
1783
+ // ★ 0.5.89:显示服务端那段说明(发生了什么、要不要等、等多久)
1784
+ const note = data !== null && typeof data === "object" && typeof data.message === "string" && data.message !== ""
1785
+ ? data.message
1786
+ : RESTART_IN_PROGRESS_NOTE;
1787
+ setMessage(note);
1788
+ if (reload) window.setTimeout(() => window.location.reload(), 12000);
1789
+ else if (typeof options?.onDone === "function") options.onDone(data);
1790
+ },
1791
+ (error) => { setMessage(error !== null && typeof error.message === "string" && error.message !== "" ? error.message : RESTART_FAILED_NOTE); },
1780
1792
  );
1781
1793
  }
1782
1794
  /** 超时类错误附上可操作提示(网络黑洞期的最常见表现)。 */
@@ -5748,9 +5760,12 @@ function healthSignature(health) {
5748
5760
  dragRef.current.base = cur;
5749
5761
  dragRef.current.active = false;
5750
5762
  dragRef.current.justDragged = false;
5751
- try { e.currentTarget.setPointerCapture?.(e.pointerId); } catch {}
5763
+ // ★ 0.5.91:不要在这里捕获指针(会把内部按钮的 click 吞到容器上)。先记下,长按确认拖动后再捕获。
5764
+ dragRef.current.captureEl = e.currentTarget;
5765
+ dragRef.current.captureId = e.pointerId;
5752
5766
  dragRef.current.long = window.setTimeout(() => {
5753
5767
  dragRef.current.active = true;
5768
+ try { dragRef.current.captureEl?.setPointerCapture?.(dragRef.current.captureId); } catch {}
5754
5769
  setDragPos(cur);
5755
5770
  }, 450);
5756
5771
  },
@@ -5766,6 +5781,7 @@ function healthSignature(health) {
5766
5781
  },
5767
5782
  onPointerUp: () => {
5768
5783
  if (dragRef.current.long !== null) { window.clearTimeout(dragRef.current.long); dragRef.current.long = null; }
5784
+ try { dragRef.current.captureEl?.releasePointerCapture?.(dragRef.current.captureId); } catch {}
5769
5785
  if (dragRef.current.active) {
5770
5786
  const p = dragPos;
5771
5787
  if (p !== null) {
@@ -19,6 +19,13 @@ function resolveDshBin() {
19
19
  const requireLocal = createRequire(join(pluginRoot(), 'package.json'))
20
20
  return join(dirname(requireLocal.resolve('@deepseek-ai/dsh/package.json')), 'lib', 'bin.js')
21
21
  } catch {
22
+ // ★ 0.5.88:npx / 启动器拉起的实例,插件根里没有 @deepseek-ai/dsh,上面必然失败。
23
+ // 此时用**当前进程自己**的入口反推 —— process.argv[1] 就是拉起本进程的那个脚本。
24
+ // 只在它确实存在且是文件时采用(绝不凭空造一个路径),否则仍如实返回 null。
25
+ try {
26
+ const entry = typeof process.argv[1] === 'string' ? process.argv[1] : ''
27
+ if (entry !== '' && /[\\/]bin\.js$/u.test(entry) && existsSync(entry)) return entry
28
+ } catch {}
22
29
  return null
23
30
  }
24
31
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@noob-stupid/dsh-plugin-console",
3
- "version": "0.5.87",
3
+ "version": "0.5.92",
4
4
  "description": "DSH 框架升级安全与插件升级门控:一键升级、失败自动回滚、升级后回滚上版、旧插件不适配自动禁用;内置多源插件市场为发现层,插件源全部可自定义,可指向公司内网私有源 / 私有索引 / 本地 Git 仓库,纯内网离线可用 | Framework upgrade safety & plugin version gating for DSH, with a customizable multi-source plugin market: point every source at internal mirrors or a local file:// repo for intranet-only, offline installs.",
5
5
  "repository": {
6
6
  "type": "git",