@noob-stupid/dsh-plugin-console 0.3.21 → 0.3.24

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
@@ -18,6 +18,24 @@
18
18
  [![GitHub Release](https://img.shields.io/github/v/release/Noob-stupid/dsh-plugin-hub?style=flat-square)](https://github.com/Noob-stupid/dsh-plugin-hub/releases)[![dsh.so security](https://www.dsh.so/badge/dsh-plugin-hub.svg)](https://www.dsh.so/artifact/dsh-plugin-hub)
19
19
  [![dsh.so install](https://www.dsh.so/badge/install/dsh-plugin-hub.svg)](https://www.dsh.so/artifact/dsh-plugin-hub)
20
20
 
21
+
22
+
23
+ > ## 🎉 v0.3.24 — AI Empower released (2026-08-31)
24
+ >
25
+ > Give the Plugin Console a package name or GitHub repo: the local AI reads its docs,
26
+ > drafts a **deployment plan** (install / write config / start service / health check),
27
+ > and — after your confirmation — executes it safely. Server-type components get an
28
+ > automatic control card (start/stop/status/open + dropdown). An OpenViking template
29
+ > deploys a local memory server in one flow.
30
+ >
31
+ > **Install:**
32
+ > ```bash
33
+ > npm i -g @noob-stupid/dsh-plugin-console
34
+ > # or in DSH: dsh plugin --profile web add @noob-stupid/dsh-plugin-console
35
+ > ```
36
+ > **Try it:** Plugin Console → **AI 赋能** button → enter e.g. `OpenViking` → confirm the plan.
37
+ >
38
+ > Feedback: [GitHub Issues](https://github.com/Noob-stupid/dsh-plugin-hub/issues).
21
39
  A **plugin management panel** for the DeepSeek Harness (DSH) Web GUI: one-click
22
40
  enable/disable of installed plugins, a **multi-source plugin marketplace**
23
41
  (GitHub / Gitee / custom sources) with one-click install, an **auto-collected
package/README.zh.md CHANGED
@@ -6,6 +6,22 @@
6
6
 
7
7
  # DSH 插件中心(dsh-plugin-hub)
8
8
 
9
+ > ## 🎉 v0.3.24 正式发布 — AI 赋能一键部署(2026-08-31)
10
+ >
11
+ > 在插件控制台输入 npm 包名或 GitHub 仓库,本地 AI 读取文档自动生成**部署计划**,
12
+ > 你确认后安全执行;服务器类组件自动生成控制卡片(启动/停止/状态/打开 + 下拉)。
13
+ > 内置 OpenViking 模板,一条指令部署本地记忆服务器。
14
+ >
15
+ > **安装:**
16
+ > ```bash
17
+ > npm i -g @noob-stupid/dsh-plugin-console
18
+ > # 或在 DSH 中更新:dsh plugin --profile web add @noob-stupid/dsh-plugin-console
19
+ > ```
20
+ > **体验入口:** 插件控制台 → 右上角 **AI 赋能** 按钮 → 输入如 `OpenViking` → 确认计划 → 自动部署。
21
+ >
22
+ > 反馈:[GitHub Issues](https://github.com/Noob-stupid/dsh-plugin-hub/issues)。
23
+
24
+
9
25
  [![](https://img.shields.io/badge/powered_by-dsh-4D6BFE?style=flat-square&logo=deepseek&logoColor=white)](https://github.com/deepseek-ai/deepseek-harness)
10
26
  [![Awesome DSH Plugin](https://awesome-dsh-plugin.com/badge.svg)](https://awesome-dsh-plugin.com)
11
27
  [![GitHub stars](https://img.shields.io/github/stars/Noob-stupid/dsh-plugin-hub?style=flat-square&logo=github)](https://github.com/Noob-stupid/dsh-plugin-hub/stargazers)
package/lib/client.js CHANGED
@@ -6,7 +6,7 @@ window.__ModuleLoader__.load({
6
6
  Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
7
7
  let react = require("react");
8
8
  //#region styles
9
- const css = ".pc_section{width:100%;max-width:760px;color:var(--dsw-alias-label-primary);flex-direction:column;gap:14px;display:flex}.pc_section h3{margin:0;font-size:13px;font-weight:600;line-height:20px}.pc_message{color:var(--dsw-alias-label-tertiary);font-size:13px;line-height:20px;margin:0}.pc_messageRow{display:flex;align-items:center;gap:10px}.pc_spinner{width:14px;height:14px;border:2px solid var(--dsw-alias-border-l2);border-top-color:var(--dsw-alias-state-business-primary);border-radius:50%;animation:pcspin 1s linear infinite;flex:none}@keyframes pcspin{to{transform:rotate(360deg)}}.pc_message[data-error=true]{color:var(--dsw-alias-state-error-primary)}.pc_list{grid-template-columns:repeat(2,minmax(0,1fr));gap:10px;margin:0;padding:0;list-style:none;display:grid}.pc_row{border:1px solid var(--dsw-alias-border-l2);background:var(--dsw-alias-bg-layer-3);border-radius:10px;min-width:0;padding:10px 12px;flex-direction:column;gap:6px;display:flex}.pc_rowTop{align-items:center;gap:8px;display:flex}.pc_name{font-size:13px;font-weight:600;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;flex:1}.pc_tag{border:1px solid var(--dsw-alias-border-l2);color:var(--dsw-alias-label-tertiary);border-radius:999px;padding:1px 8px;font-size:11px;line-height:16px;flex:none}.pc_tag[data-enabled=true]{border-color:var(--dsw-alias-state-success-primary);color:var(--dsw-alias-state-success-primary)}.pc_tag[data-user=true]{border-color:var(--dsw-alias-state-business-primary);color:var(--dsw-alias-state-business-primary)}.pc_tag[data-skill=true]{border-color:var(--dsw-alias-state-warning-primary);color:var(--dsw-alias-state-warning-primary)}.pc_tag[data-system=true]{border-color:var(--dsw-alias-border-l2);color:var(--dsw-alias-label-tertiary);background:var(--dsw-alias-bg-layer-2)}.pc_tag[data-disabled=true]{border-color:var(--dsw-alias-state-error-primary);color:var(--dsw-alias-state-error-primary)}.pc_tag[data-suite=true]{border-color:var(--dsw-alias-state-success-primary);color:var(--dsw-alias-state-success-primary)}.pc_tag[data-error=true]{border-color:var(--dsw-alias-state-error-primary);color:var(--dsw-alias-state-error-primary)}.pc_tabGroup{display:inline-flex;gap:4px;align-items:center}.pc_meta{color:var(--dsw-alias-label-tertiary);font-size:11px;line-height:16px;gap:6px;display:flex;align-items:center}.pc_phase[data-phase=failed]{color:var(--dsw-alias-state-error-primary)}.pc_phase[data-phase=active]{color:var(--dsw-alias-state-success-primary)}.pc_toggle{border:1px solid var(--dsw-alias-border-l2);color:var(--dsw-alias-label-primary);background:0 0;font:inherit;cursor:pointer;border-radius:6px;padding:4px 12px;align-self:flex-start}.pc_toggle:hover:not(:disabled){border-color:var(--dsw-alias-state-business-primary)}.pc_toggle:disabled{opacity:.5;cursor:default}.pc_search{gap:8px;display:flex}.pc_search input{border:1px solid var(--dsw-alias-border-l2);background:var(--dsw-alias-bg-layer-1);height:34px;flex:1;color:var(--dsw-alias-label-primary);font:inherit;border-radius:8px;outline:none;padding:0 12px;font-size:13px}.pc_search input:focus-visible{border-color:var(--dsw-alias-state-business-primary)}.pc_search button{border:1px solid var(--dsw-alias-border-l2);color:var(--dsw-alias-label-primary);background:var(--dsw-alias-bg-layer-2);font:inherit;cursor:pointer;border-radius:8px;padding:0 14px;font-size:13px}.pc_search button:hover{border-color:var(--dsw-alias-state-business-primary)}.pc_market{flex-direction:column;gap:10px;margin:0;padding:0;list-style:none;display:flex}.pc_item{border:1px solid var(--dsw-alias-border-l2);background:var(--dsw-alias-bg-layer-3);border-radius:10px;min-width:0;padding:10px 12px;flex-direction:column;gap:6px;display:flex}.pc_itemTop{align-items:center;gap:8px;display:flex}.pc_itemTop a{color:var(--dsw-alias-state-business-primary);font-size:13px;font-weight:600;text-decoration:none;flex:1;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.pc_stars{color:var(--dsw-alias-label-tertiary);font-size:11px;flex:none}.pc_desc{color:var(--dsw-alias-label-tertiary);font-size:12px;line-height:18px;margin:0}.pc_item button{border:1px solid var(--dsw-alias-border-l2);color:var(--dsw-alias-label-primary);background:0 0;font:inherit;cursor:pointer;border-radius:6px;padding:3px 10px;align-self:flex-start;font-size:12px}.pc_item button:hover:not(:disabled){border-color:var(--dsw-alias-state-business-primary)}.pc_item button:disabled{opacity:.5;cursor:default}.pc_detail{border:1px solid var(--dsw-alias-border-l2);background:var(--dsw-alias-bg-layer-3);border-radius:10px;padding:10px 12px;flex-direction:column;gap:6px;display:flex}.pc_status{color:var(--dsw-alias-label-tertiary);font-size:12px;line-height:18px;margin:0}.pc_note{color:var(--dsw-alias-label-tertiary);font-size:11px;line-height:16px;margin:0}.pc_marketHead{display:flex;align-items:center;justify-content:space-between;gap:10px}.pc_descWrap{flex-direction:column;display:flex}.pc_installedHead{display:flex;align-items:center;gap:10px}.pc_backTopWrap{position:fixed;top:56px;right:20px;z-index:100;display:flex;flex-direction:column;gap:8px}.pc_backTop{width:34px;height:34px;border-radius:50%;display:inline-flex;align-items:center;justify-content:center;background:rgba(22,27,34,.72);border:1px solid var(--dsw-alias-border-l2);color:var(--dsw-alias-label-primary);cursor:pointer;backdrop-filter:blur(4px);opacity:.7}.pc_backTop:hover{opacity:1;border-color:var(--dsw-alias-state-business-primary)}.pc_descTopbar{display:flex;justify-content:flex-end;margin:2px 0}.pc_ghpill{border:1px solid var(--dsw-alias-border-l2);color:var(--dsw-alias-label-tertiary);border-radius:999px;padding:1px 10px;font-size:11px;line-height:18px}.pc_ghpill[data-on=true]{border-color:var(--dsw-alias-state-success-primary);color:var(--dsw-alias-state-success-primary)}.pc_extraBtn{opacity:.75;border:1px solid var(--dsw-alias-border-l2);color:var(--dsw-alias-label-primary);background:0 0;font:inherit;cursor:pointer;border-radius:6px;padding:4px 10px;font-size:12px;line-height:16px;flex:none;transition:opacity .15s ease,border-color 1.5s ease,color 1.5s ease}.pc_extraBtn:hover{opacity:1}.pc_extraBtn[data-active=true]{opacity:1;border-color:var(--dsw-alias-state-business-primary);color:var(--dsw-alias-state-business-primary)}.pc_extraBtn[data-flash=true]{border-color:var(--dsw-alias-state-business-primary);color:var(--dsw-alias-state-business-primary)}.pc_view{border:1px solid var(--dsw-alias-border-l2);color:var(--dsw-alias-label-primary);background:0 0;font:inherit;cursor:pointer;border-radius:6px;padding:2px 10px;font-size:12px;line-height:18px;flex:none}.pc_floatPanel{position:fixed;left:20px;top:50%;transform:translateY(-50%);width:340px;max-height:75vh;overflow-y:auto;border:1px solid var(--dsw-alias-border-l2);background:var(--dsw-alias-bg-layer-3);border-radius:10px;padding:12px;box-shadow:0 8px 24px rgba(0,0,0,.18);z-index:50;flex-direction:column;gap:8px;display:flex}.pc_refresh{position:sticky;bottom:14px;align-self:flex-end;margin-top:8px;z-index:60;opacity:.45;border:1px solid var(--dsw-alias-border-l2);color:var(--dsw-alias-label-secondary);background:transparent;font:inherit;cursor:pointer;border-radius:8px;padding:6px 10px;display:flex;align-items:center;gap:6px;font-size:12px;line-height:16px;transition:opacity .15s ease}.pc_refresh:hover{opacity:.9}.pc_date{color:var(--dsw-alias-label-tertiary);font-size:11px;line-height:16px;flex:none}.pc_searchInner{position:relative;flex:1;display:flex;align-items:center;min-width:0}.pc_searchInner input{padding-right:32px;width:100%}.pc_starBtn{border:1px solid var(--dsw-alias-border-l2);background:var(--dsw-alias-bg-layer-2);color:var(--dsw-alias-label-tertiary);cursor:pointer;font-size:13px;line-height:1;padding:4px 8px;border-radius:6px;opacity:.75;transition:opacity .15s ease;flex:none}.pc_starBtn:hover{opacity:1}.pc_starBtn[data-active=true]{opacity:1;color:var(--dsw-alias-state-business-primary)}.pc_restartBtn{position:fixed;right:20px;top:50%;transform:translateY(-50%);z-index:60;opacity:.45;border:1px solid var(--dsw-alias-border-l2);color:var(--dsw-alias-label-secondary);background:transparent;font:inherit;cursor:pointer;border-radius:8px;padding:6px 10px;display:flex;align-items:center;gap:6px;font-size:12px;line-height:16px;transition:opacity .15s ease}.pc_restartBtn:hover{opacity:.9}.pc_relaunchFloat{position:fixed;left:20px;top:50%;transform:translateY(-50%);z-index:70;opacity:.85;border:1px solid var(--dsw-alias-state-business-primary);color:var(--dsw-alias-state-business-primary);background:var(--dsw-alias-bg-layer-3);font:inherit;cursor:pointer;border-radius:8px;padding:6px 10px;display:flex;align-items:center;gap:6px;font-size:12px;line-height:16px;transition:opacity .15s ease}.pc_relaunchFloat:hover{opacity:1}.pc_aiToggle{position:fixed;right:20px;top:20px;z-index:70;opacity:.45;border:1px solid var(--dsw-alias-border-l2);color:var(--dsw-alias-label-secondary);background:transparent;font:inherit;cursor:pointer;border-radius:8px;padding:6px 10px;display:flex;align-items:center;gap:6px;font-size:12px;line-height:16px;transition:opacity .15s ease}.pc_aiToggle:hover{opacity:.9}.pc_aiToggle[data-active=true]{opacity:1;border-color:var(--dsw-alias-state-business-primary);color:var(--dsw-alias-state-business-primary)}.pc_sourcesFloat{position:fixed;right:20px;top:76px;z-index:70;opacity:.85;border:1px solid var(--dsw-alias-border-l2);color:var(--dsw-alias-label-primary);background:var(--dsw-alias-bg-layer-3);font:inherit;cursor:pointer;border-radius:8px;padding:6px 10px;display:flex;align-items:center;gap:6px;font-size:12px;line-height:16px;transition:opacity .15s ease}.pc_sourcesFloat:hover{opacity:1;border-color:var(--dsw-alias-state-business-primary);color:var(--dsw-alias-state-business-primary)}.pc_modeFloat{position:fixed;right:20px;top:176px;z-index:70;opacity:.85;border:1px solid var(--dsw-alias-border-l2);color:var(--dsw-alias-label-primary);background:var(--dsw-alias-bg-layer-3);font:inherit;cursor:pointer;border-radius:8px;padding:6px 10px;display:flex;align-items:center;gap:6px;font-size:12px;line-height:16px;transition:opacity .15s ease}.pc_modeFloat:hover{opacity:1;border-color:var(--dsw-alias-state-business-primary);color:var(--dsw-alias-state-business-primary)}.pc_modeFloat[data-mode=true]{opacity:1;border-color:var(--dsw-alias-state-warning-primary);color:var(--dsw-alias-state-warning-primary)}.pc_trashBtn{border:0;background:0 0;color:var(--dsw-alias-label-tertiary);cursor:pointer;font-size:12px;line-height:16px;padding:2px 6px;flex:none;display:flex;align-items:center;border-radius:6px;transition:color .15s ease}.pc_trashBtn:hover{color:var(--dsw-alias-state-error-primary)}.pc_consentRemember{display:flex;align-items:center;gap:6px;font-size:12px;line-height:16px;color:var(--dsw-alias-label-secondary);cursor:pointer}.pc_modalBackdrop{position:fixed;inset:0;background:rgba(0,0,0,.45);z-index:1000;display:flex;align-items:center;justify-content:center}.pc_modalCard{border:1px solid var(--dsw-alias-border-l2);background:var(--dsw-alias-bg-layer-1);border-radius:12px;padding:16px 18px;max-width:420px;width:calc(100% - 48px);flex-direction:column;gap:10px;display:flex;box-shadow:0 12px 32px rgba(0,0,0,.25)}.pc_ghpill{background:0 0;cursor:pointer}.pc_ghwrap{position:relative;display:inline-flex}.pc_sourceMenu{position:absolute;right:0;top:calc(100% + 6px);z-index:90;border:1px solid var(--dsw-alias-border-l2);background:var(--dsw-alias-bg-layer-1);border-radius:8px;padding:4px;flex-direction:column;display:flex;min-width:120px;box-shadow:0 8px 24px rgba(0,0,0,.18)}.pc_sourceOpt{border:0;background:0 0;color:var(--dsw-alias-label-primary);font:inherit;cursor:pointer;border-radius:6px;padding:6px 10px;text-align:left;font-size:12px;line-height:16px}.pc_sourceOpt[data-active=true]{color:var(--dsw-alias-state-business-primary);font-weight:600}.pc_sourceOpt:hover{background:var(--dsw-alias-bg-layer-3)}";
9
+ const css = ".pc_section{width:100%;max-width:760px;color:var(--dsw-alias-label-primary);flex-direction:column;gap:14px;display:flex}.pc_section h3{margin:0;font-size:13px;font-weight:600;line-height:20px}.pc_message{color:var(--dsw-alias-label-tertiary);font-size:13px;line-height:20px;margin:0}.pc_messageRow{display:flex;align-items:center;gap:10px}.pc_spinner{width:14px;height:14px;border:2px solid var(--dsw-alias-border-l2);border-top-color:var(--dsw-alias-state-business-primary);border-radius:50%;animation:pcspin 1s linear infinite;flex:none}@keyframes pcspin{to{transform:rotate(360deg)}}.pc_message[data-error=true]{color:var(--dsw-alias-state-error-primary)}.pc_list{grid-template-columns:repeat(2,minmax(0,1fr));gap:10px;margin:0;padding:0;list-style:none;display:grid}.pc_row{border:1px solid var(--dsw-alias-border-l2);background:var(--dsw-alias-bg-layer-3);border-radius:10px;min-width:0;padding:10px 12px;flex-direction:column;gap:6px;display:flex}.pc_rowTop{align-items:center;gap:8px;display:flex}.pc_name{font-size:13px;font-weight:600;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;flex:1}.pc_tag{border:1px solid var(--dsw-alias-border-l2);color:var(--dsw-alias-label-tertiary);border-radius:999px;padding:1px 8px;font-size:11px;line-height:16px;flex:none}.pc_tag[data-enabled=true]{border-color:var(--dsw-alias-state-success-primary);color:var(--dsw-alias-state-success-primary)}.pc_tag[data-user=true]{border-color:var(--dsw-alias-state-business-primary);color:var(--dsw-alias-state-business-primary)}.pc_tag[data-skill=true]{border-color:var(--dsw-alias-state-warning-primary);color:var(--dsw-alias-state-warning-primary)}.pc_tag[data-system=true]{border-color:var(--dsw-alias-border-l2);color:var(--dsw-alias-label-tertiary);background:var(--dsw-alias-bg-layer-2)}.pc_tag[data-disabled=true]{border-color:var(--dsw-alias-state-error-primary);color:var(--dsw-alias-state-error-primary)}.pc_tag[data-suite=true]{border-color:var(--dsw-alias-state-success-primary);color:var(--dsw-alias-state-success-primary)}.pc_tag[data-error=true]{border-color:var(--dsw-alias-state-error-primary);color:var(--dsw-alias-state-error-primary)}.pc_tabGroup{display:inline-flex;gap:4px;align-items:center}.pc_meta{color:var(--dsw-alias-label-tertiary);font-size:11px;line-height:16px;gap:6px;display:flex;align-items:center}.pc_phase[data-phase=failed]{color:var(--dsw-alias-state-error-primary)}.pc_phase[data-phase=active]{color:var(--dsw-alias-state-success-primary)}.pc_toggle{border:1px solid var(--dsw-alias-border-l2);color:var(--dsw-alias-label-primary);background:0 0;font:inherit;cursor:pointer;border-radius:6px;padding:4px 12px;align-self:flex-start}.pc_toggle:hover:not(:disabled){border-color:var(--dsw-alias-state-business-primary)}.pc_toggle:disabled{opacity:.5;cursor:default}.pc_search{gap:8px;display:flex}.pc_search input{border:1px solid var(--dsw-alias-border-l2);background:var(--dsw-alias-bg-layer-1);height:34px;flex:1;color:var(--dsw-alias-label-primary);font:inherit;border-radius:8px;outline:none;padding:0 12px;font-size:13px}.pc_search input:focus-visible{border-color:var(--dsw-alias-state-business-primary)}.pc_search button{border:1px solid var(--dsw-alias-border-l2);color:var(--dsw-alias-label-primary);background:var(--dsw-alias-bg-layer-2);font:inherit;cursor:pointer;border-radius:8px;padding:0 14px;font-size:13px}.pc_search button:hover{border-color:var(--dsw-alias-state-business-primary)}.pc_market{flex-direction:column;gap:10px;margin:0;padding:0;list-style:none;display:flex}.pc_item{border:1px solid var(--dsw-alias-border-l2);background:var(--dsw-alias-bg-layer-3);border-radius:10px;min-width:0;padding:10px 12px;flex-direction:column;gap:6px;display:flex}.pc_itemTop{align-items:center;gap:8px;display:flex}.pc_itemTop a{color:var(--dsw-alias-state-business-primary);font-size:13px;font-weight:600;text-decoration:none;flex:1;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.pc_stars{color:var(--dsw-alias-label-tertiary);font-size:11px;flex:none}.pc_desc{color:var(--dsw-alias-label-tertiary);font-size:12px;line-height:18px;margin:0}.pc_item button{border:1px solid var(--dsw-alias-border-l2);color:var(--dsw-alias-label-primary);background:0 0;font:inherit;cursor:pointer;border-radius:6px;padding:3px 10px;align-self:flex-start;font-size:12px}.pc_item button:hover:not(:disabled){border-color:var(--dsw-alias-state-business-primary)}.pc_item button:disabled{opacity:.5;cursor:default}.pc_detail{border:1px solid var(--dsw-alias-border-l2);background:var(--dsw-alias-bg-layer-3);border-radius:10px;padding:10px 12px;flex-direction:column;gap:6px;display:flex}.pc_status{color:var(--dsw-alias-label-tertiary);font-size:12px;line-height:18px;margin:0}.pc_note{color:var(--dsw-alias-label-tertiary);font-size:11px;line-height:16px;margin:0}.pc_marketHead{display:flex;align-items:center;justify-content:space-between;gap:10px}.pc_descWrap{flex-direction:column;display:flex}.pc_installedHead{display:flex;align-items:center;gap:10px}.pc_backTopWrap{position:fixed;top:56px;right:20px;z-index:100;display:flex;flex-direction:column;gap:8px}.pc_backTop{width:34px;height:34px;border-radius:50%;display:inline-flex;align-items:center;justify-content:center;background:rgba(22,27,34,.72);border:1px solid var(--dsw-alias-border-l2);color:var(--dsw-alias-label-primary);cursor:pointer;backdrop-filter:blur(4px);opacity:.7}.pc_backTop:hover{opacity:1;border-color:var(--dsw-alias-state-business-primary)}.pc_descTopbar{display:flex;justify-content:flex-end;margin:2px 0}.pc_ghpill{border:1px solid var(--dsw-alias-border-l2);color:var(--dsw-alias-label-tertiary);border-radius:999px;padding:1px 10px;font-size:11px;line-height:18px}.pc_ghpill[data-on=true]{border-color:var(--dsw-alias-state-success-primary);color:var(--dsw-alias-state-success-primary)}.pc_extraBtn{opacity:.75;border:1px solid var(--dsw-alias-border-l2);color:var(--dsw-alias-label-primary);background:0 0;font:inherit;cursor:pointer;border-radius:6px;padding:4px 10px;font-size:12px;line-height:16px;flex:none;transition:opacity .15s ease,border-color 1.5s ease,color 1.5s ease}.pc_extraBtn:hover{opacity:1}.pc_extraBtn[data-active=true]{opacity:1;border-color:var(--dsw-alias-state-business-primary);color:var(--dsw-alias-state-business-primary)}.pc_extraBtn[data-flash=true]{border-color:var(--dsw-alias-state-business-primary);color:var(--dsw-alias-state-business-primary)}.pc_view{border:1px solid var(--dsw-alias-border-l2);color:var(--dsw-alias-label-primary);background:0 0;font:inherit;cursor:pointer;border-radius:6px;padding:2px 10px;font-size:12px;line-height:18px;flex:none}.pc_floatPanel{position:fixed;left:20px;top:50%;transform:translateY(-50%);width:340px;max-height:75vh;overflow-y:auto;border:1px solid var(--dsw-alias-border-l2);background:var(--dsw-alias-bg-layer-3);border-radius:10px;padding:12px;box-shadow:0 8px 24px rgba(0,0,0,.18);z-index:50;flex-direction:column;gap:8px;display:flex}.pc_refresh{position:sticky;bottom:14px;align-self:flex-end;margin-top:8px;z-index:60;opacity:.45;border:1px solid var(--dsw-alias-border-l2);color:var(--dsw-alias-label-secondary);background:transparent;font:inherit;cursor:pointer;border-radius:8px;padding:6px 10px;display:flex;align-items:center;gap:6px;font-size:12px;line-height:16px;transition:opacity .15s ease}.pc_refresh:hover{opacity:.9}.pc_date{color:var(--dsw-alias-label-tertiary);font-size:11px;line-height:16px;flex:none}.pc_searchInner{position:relative;flex:1;display:flex;align-items:center;min-width:0}.pc_searchInner input{padding-right:32px;width:100%}.pc_starBtn{border:1px solid var(--dsw-alias-border-l2);background:var(--dsw-alias-bg-layer-2);color:var(--dsw-alias-label-tertiary);cursor:pointer;font-size:13px;line-height:1;padding:4px 8px;border-radius:6px;opacity:.75;transition:opacity .15s ease;flex:none}.pc_starBtn:hover{opacity:1}.pc_starBtn[data-active=true]{opacity:1;color:var(--dsw-alias-state-business-primary)}.pc_restartBtn{position:fixed;right:20px;top:50%;transform:translateY(-50%);z-index:60;opacity:.45;border:1px solid var(--dsw-alias-border-l2);color:var(--dsw-alias-label-secondary);background:transparent;font:inherit;cursor:pointer;border-radius:8px;padding:6px 10px;display:flex;align-items:center;gap:6px;font-size:12px;line-height:16px;transition:opacity .15s ease}.pc_restartBtn:hover{opacity:.9}.pc_relaunchFloat{position:fixed;left:20px;top:50%;transform:translateY(-50%);z-index:70;opacity:.85;border:1px solid var(--dsw-alias-state-business-primary);color:var(--dsw-alias-state-business-primary);background:var(--dsw-alias-bg-layer-3);font:inherit;cursor:pointer;border-radius:8px;padding:6px 10px;display:flex;align-items:center;gap:6px;font-size:12px;line-height:16px;transition:opacity .15s ease}.pc_relaunchFloat:hover{opacity:1}.pc_aiToggle{position:fixed;right:20px;top:20px;z-index:70;opacity:.45;border:1px solid var(--dsw-alias-border-l2);color:var(--dsw-alias-label-secondary);background:transparent;font:inherit;cursor:pointer;border-radius:8px;padding:6px 10px;display:flex;align-items:center;gap:6px;font-size:12px;line-height:16px;transition:opacity .15s ease}.pc_aiToggle:hover{opacity:.9}.pc_aiToggle[data-active=true]{opacity:1;border-color:var(--dsw-alias-state-business-primary);color:var(--dsw-alias-state-business-primary)}.pc_sourcesFloat{position:fixed;right:20px;top:104px;z-index:70;opacity:.85;border:1px solid var(--dsw-alias-border-l2);color:var(--dsw-alias-label-primary);background:var(--dsw-alias-bg-layer-3);font:inherit;cursor:pointer;border-radius:8px;padding:6px 10px;display:flex;align-items:center;gap:6px;font-size:12px;line-height:16px;transition:opacity .15s ease}.pc_sourcesFloat:hover{opacity:1;border-color:var(--dsw-alias-state-business-primary);color:var(--dsw-alias-state-business-primary)}.pc_modeFloat{position:fixed;right:20px;top:204px;z-index:70;opacity:.85;border:1px solid var(--dsw-alias-border-l2);color:var(--dsw-alias-label-primary);background:var(--dsw-alias-bg-layer-3);font:inherit;cursor:pointer;border-radius:8px;padding:6px 10px;display:flex;align-items:center;gap:6px;font-size:12px;line-height:16px;transition:opacity .15s ease}.pc_modeFloat:hover{opacity:1;border-color:var(--dsw-alias-state-business-primary);color:var(--dsw-alias-state-business-primary)}.pc_modeFloat[data-mode=true]{opacity:1;border-color:var(--dsw-alias-state-warning-primary);color:var(--dsw-alias-state-warning-primary)}.pc_trashBtn{border:0;background:0 0;color:var(--dsw-alias-label-tertiary);cursor:pointer;font-size:12px;line-height:16px;padding:2px 6px;flex:none;display:flex;align-items:center;border-radius:6px;transition:color .15s ease}.pc_trashBtn:hover{color:var(--dsw-alias-state-error-primary)}.pc_consentRemember{display:flex;align-items:center;gap:6px;font-size:12px;line-height:16px;color:var(--dsw-alias-label-secondary);cursor:pointer}.pc_modalBackdrop{position:fixed;inset:0;background:rgba(0,0,0,.45);z-index:1000;display:flex;align-items:center;justify-content:center}.pc_modalCard{border:1px solid var(--dsw-alias-border-l2);background:var(--dsw-alias-bg-layer-1);border-radius:12px;padding:16px 18px;max-width:420px;width:calc(100% - 48px);flex-direction:column;gap:10px;display:flex;box-shadow:0 12px 32px rgba(0,0,0,.25)}.pc_ghpill{background:0 0;cursor:pointer}.pc_ghwrap{position:relative;display:inline-flex}.pc_sourceMenu{position:absolute;right:0;top:calc(100% + 6px);z-index:90;border:1px solid var(--dsw-alias-border-l2);background:var(--dsw-alias-bg-layer-1);border-radius:8px;padding:4px;flex-direction:column;display:flex;min-width:120px;box-shadow:0 8px 24px rgba(0,0,0,.18)}.pc_sourceOpt{border:0;background:0 0;color:var(--dsw-alias-label-primary);font:inherit;cursor:pointer;border-radius:6px;padding:6px 10px;text-align:left;font-size:12px;line-height:16px}.pc_sourceOpt[data-active=true]{color:var(--dsw-alias-state-business-primary);font-weight:600}.pc_sourceOpt:hover{background:var(--dsw-alias-bg-layer-3)}.pc_aiEmpower{position:fixed;right:20px;top:48px;z-index:70;opacity:.45;border:1px solid var(--dsw-alias-border-l2);color:var(--dsw-alias-label-secondary);background:transparent;font:inherit;cursor:pointer;border-radius:8px;padding:6px 10px;display:flex;align-items:center;gap:6px;font-size:12px;line-height:16px;transition:opacity .15s ease}.pc_aiEmpower:hover{opacity:.9}.pc_aiEmpower[data-busy=true]{opacity:1;border-color:var(--dsw-alias-state-warning-primary);color:var(--dsw-alias-state-warning-primary)}.pc_aiEmpower[data-active=true]{opacity:1;border-color:var(--dsw-alias-state-business-primary);color:var(--dsw-alias-state-business-primary)}.pc_compCard{position:fixed;left:8px;top:20px;width:320px;max-height:70vh;overflow-y:auto;border:1px solid var(--dsw-alias-border-l2);background:var(--dsw-alias-bg-layer-3);border-radius:10px;padding:12px;box-shadow:0 8px 24px rgba(0,0,0,.18);z-index:45;flex-direction:column;gap:8px;display:flex}.pc_compCard .pc_rowTop{flex-wrap:wrap}.pc_compCard .pc_toggle{padding:3px 8px;font-size:11px}.pc_compCard[data-hidden=true]{display:none}.pc_compDrop{background:rgba(24,28,34,.92);backdrop-filter:blur(6px);border:1px solid var(--dsw-alias-border-l2);border-radius:8px;padding:4px;box-shadow:0 8px 24px rgba(0,0,0,.35);flex-direction:column;display:flex;gap:2px}.pc_compDropRow{border:0;background:0 0;color:var(--dsw-alias-label-primary);font:inherit;cursor:pointer;border-radius:6px;padding:6px 8px;text-align:left;font-size:12px;line-height:16px;flex-direction:column;display:flex;gap:2px}.pc_compDropRow:hover{background:var(--dsw-alias-bg-layer-2)}.pc_compDropUrl{color:var(--dsw-alias-label-tertiary);font-size:11px;word-break:break-all}.pc_compToggle{position:fixed;left:336px;top:20px;z-index:46;opacity:.55;border:1px solid var(--dsw-alias-border-l2);color:var(--dsw-alias-label-secondary);background:rgba(22,27,34,.72);font:inherit;cursor:pointer;width:20px;height:20px;border-radius:50%;display:flex;align-items:center;justify-content:center;font-size:11px;line-height:1;backdrop-filter:blur(4px);transition:opacity .15s ease}.pc_compToggle:hover{opacity:1;border-color:var(--dsw-alias-state-business-primary)}";
10
10
  const tagId = "@noob-stupid/dsh-plugin-console/PluginConsoleTab.css";
11
11
  if (typeof document !== "undefined" && document.querySelector("style[data-plugin-css=" + JSON.stringify(tagId) + "]") === null) {
12
12
  const tag = document.createElement("style");
@@ -42,6 +42,12 @@ window.__ModuleLoader__.load({
42
42
  restartBtn: "pc_restartBtn",
43
43
  relaunchFloat: "pc_relaunchFloat",
44
44
  aiToggle: "pc_aiToggle",
45
+ aiEmpower: "pc_aiEmpower",
46
+ compCard: "pc_compCard",
47
+ compDrop: "pc_compDrop",
48
+ compDropRow: "pc_compDropRow",
49
+ compDropUrl: "pc_compDropUrl",
50
+ compToggle: "pc_compToggle",
45
51
  sourcesFloat: "pc_sourcesFloat",
46
52
  modeFloat: "pc_modeFloat",
47
53
  trashBtn: "pc_trashBtn",
@@ -171,6 +177,36 @@ window.__ModuleLoader__.load({
171
177
  aiConsentApprove: "同意,继续(可能产生费用)",
172
178
  aiConsentDecline: "取消",
173
179
  aiFallbackLabel: "AI 兜底",
180
+ aiEmpowerLabel: "AI 赋能",
181
+ aiEmpowerTitle: "AI 赋能:读文档自动规划并部署组件;识别到服务器类组件后自动加启动/停止按钮",
182
+ aiEmpowerSrcTitle: "输入要部署的组件(npm 包名或 GitHub 仓库,如 Noob-stupid/dsh-plugin-hub 或 @noob-stupid/dsh-plugin-console)",
183
+ aiEmpowerPlanTitle: "AI 赋能部署计划",
184
+ aiEmpowerType: "类型",
185
+ aiEmpowerTypeMapping: { "pure-plugin": "纯插件", "service": "服务器组件", "config-only": "仅配置" },
186
+ aiEmpowerSteps: "执行步骤(可勾选)",
187
+ aiEmpowerRun: "执行所选步骤",
188
+ aiEmpowerCancel: "取消",
189
+ aiEmpowerWaiting: "等待 AI 分析文档并生成计划(约 1-5 分钟,会调用 DeepSeek API,可能产生费用)…",
190
+ aiEmpowerExecuting: "AI 赋能执行中…",
191
+ aiEmpowerDone: "AI 赋能部署完成",
192
+ aiEmpowerFail: "AI 赋能失败",
193
+ aiEmpowerLogs: "执行日志",
194
+ aiEmpowerComponents: "已识别的服务器组件",
195
+ compStart: "启动",
196
+ compStop: "停止",
197
+ compStatus: "状态",
198
+ compOpen: "打开",
199
+ compRunning: "运行中",
200
+ compStopped: "未运行",
201
+ compHealthy: "健康",
202
+ compUnhealthy: "不健康",
203
+ compRefresh: "刷新组件状态",
204
+ compDropTitle: "展开所有服务器,点击打开网址",
205
+ compNoUrl: "暂无网址",
206
+ compCollapseTitle: "收缩组件卡片",
207
+ compExpandTitle: "展开组件卡片",
208
+ compActionDone: "{action}:{name}",
209
+ noComponents: "暂无已识别的服务器组件(用 AI 赋能部署后自动出现)",
174
210
  aiFallbackTitle: "关闭后:常规通道失败将直接取消安装,不再调用模型 API(零费用)",
175
211
  aiFallbackOff: "已按设置关闭 AI 兜底,本次安装已取消(零费用)",
176
212
  aiConsentRemember: "以后不再提醒(自动同意 AI 兜底,可能产生费用;可随时在插件市场页面最底部恢复提醒)",
@@ -252,6 +288,10 @@ window.__ModuleLoader__.load({
252
288
  skillDeleteConfirm: "确认删除技能「{name}」?",
253
289
  skillDeletedMsg: "技能已删除",
254
290
  skillsEmpty: "技能库为空(自动收录 agent-skills / claude-skills / dsh-skill topic 仓库)",
291
+ skillsEmptyHint: "这里的「已安装技能」只统计用户技能目录(~/.dsh/skills);插件自带的技能(如 openviking-memory)随插件在技能中心提供,不在本列表",
292
+ pluginSkillsTitle: "插件自带技能",
293
+ pluginSkillTag: "插件自带",
294
+ pluginSkillsHint: "由已安装插件提供、只读展示;如需在会话中调用,直接使用技能名即可(如 openviking-memory)",
255
295
  skillSystemTag: "系统",
256
296
  skillSystemTitle: "系统自带技能(~/.dsh/skills/.system 等保留目录),禁止删除",
257
297
  skillDisable: "停用",
@@ -408,6 +448,36 @@ window.__ModuleLoader__.load({
408
448
  aiConsentApprove: "Agree, continue (may incur costs)",
409
449
  aiConsentDecline: "Cancel",
410
450
  aiFallbackLabel: "AI fallback",
451
+ aiEmpowerLabel: "AI Empower",
452
+ aiEmpowerTitle: "AI Empower: read docs, plan and deploy a component automatically; server-type components get start/stop buttons",
453
+ aiEmpowerSrcTitle: "Component to deploy (npm package or GitHub repo)",
454
+ aiEmpowerPlanTitle: "AI Empower deployment plan",
455
+ aiEmpowerType: "Type",
456
+ aiEmpowerTypeMapping: { "pure-plugin": "Pure plugin", "service": "Server component", "config-only": "Config only" },
457
+ aiEmpowerSteps: "Steps to execute (selectable)",
458
+ aiEmpowerRun: "Run selected steps",
459
+ aiEmpowerCancel: "Cancel",
460
+ aiEmpowerWaiting: "AI is analyzing the docs and drafting the plan (1-5 min, calls DeepSeek API, may incur costs)…",
461
+ aiEmpowerExecuting: "AI Empower executing…",
462
+ aiEmpowerDone: "AI Empower deployment finished",
463
+ aiEmpowerFail: "AI Empower failed",
464
+ aiEmpowerLogs: "Execution log",
465
+ aiEmpowerComponents: "Detected server components",
466
+ compStart: "Start",
467
+ compStop: "Stop",
468
+ compStatus: "Status",
469
+ compOpen: "Open",
470
+ compRunning: "Running",
471
+ compStopped: "Stopped",
472
+ compHealthy: "Healthy",
473
+ compUnhealthy: "Unhealthy",
474
+ compRefresh: "Refresh component status",
475
+ compDropTitle: "Expand to open all server URLs",
476
+ compNoUrl: "No URL yet",
477
+ compCollapseTitle: "Collapse server card",
478
+ compExpandTitle: "Expand server card",
479
+ compActionDone: "{action}: {name}",
480
+ noComponents: "No server components yet (they appear after an AI Empower deployment)",
411
481
  aiFallbackTitle: "When off: if regular channels fail, the install is cancelled directly without calling the model API (zero cost)",
412
482
  aiFallbackOff: "AI fallback is disabled by your setting; this install was cancelled (zero cost)",
413
483
  aiConsentRemember: "Don't ask again (auto-approve the AI fallback, may incur costs; restore reminders anytime at the very bottom of the market)",
@@ -489,6 +559,10 @@ window.__ModuleLoader__.load({
489
559
  skillDeleteConfirm: "Remove skill「{name}」?",
490
560
  skillDeletedMsg: "Skill removed",
491
561
  skillsEmpty: "Skill library is empty (auto-collected from agent-skills / claude-skills / dsh-skill topic repos)",
562
+ skillsEmptyHint: "This list only counts user skills (~/.dsh/skills); plugin-provided skills (e.g. openviking-memory) appear in the Skill Center, not here",
563
+ pluginSkillsTitle: "Plugin-provided skills",
564
+ pluginSkillTag: "Plugin",
565
+ pluginSkillsHint: "Provided by installed plugins, read-only; invoke by skill name (e.g. openviking-memory)",
492
566
  skillSystemTag: "System",
493
567
  skillSystemTitle: "System-owned skill (~/.dsh/skills/.system etc.), cannot be removed",
494
568
  skillDisable: "Disable",
@@ -604,6 +678,8 @@ window.__ModuleLoader__.load({
604
678
  if (!response.ok) throw new Error("GitHub 请求失败 (HTTP " + response.status + ")");
605
679
  return response.json();
606
680
  }
681
+ /** 对路径中的每一段做百分号编码,保留 "/" 作为分隔符,防止特殊字符篡改目标 URL。 */
682
+ const encodePathSegments = (value) => String(value).split("/").map(encodeURIComponent).join("/");
607
683
  /** raw 文件多通道竞速:官方直连 + 常见 gh 镜像,谁先成功用谁;非 GitHub 平台走对应 raw 地址。 */
608
684
  const RAW_CANDIDATES = [
609
685
  (repo, branch, file) => `${GITHUB_RAW}/${repo}/${branch}/${file}`,
@@ -612,6 +688,9 @@ window.__ModuleLoader__.load({
612
688
  (repo, branch, file) => `https://mirror.ghproxy.com/https://raw.githubusercontent.com/${repo}/${branch}/${file}`,
613
689
  ];
614
690
  async function fetchRawText(repo, branch, file, source = "github") {
691
+ repo = encodePathSegments(repo);
692
+ branch = encodePathSegments(branch);
693
+ file = encodePathSegments(file);
615
694
  if (source === "gitee") {
616
695
  try {
617
696
  const res = await fetch(`https://gitee.com/${repo}/raw/${branch}/${file}`, { signal: AbortSignal.timeout(15000) });
@@ -649,8 +728,13 @@ window.__ModuleLoader__.load({
649
728
  function summarizeSkillFrontmatter(text) {
650
729
  const fm = text.startsWith("---") ? text.slice(3, text.indexOf("\n---", 3)) : "";
651
730
  if (fm === "") return null;
731
+ const KEY_PATTERNS = {
732
+ name: /^name:\s*(.*)$/mu,
733
+ description: /^description:\s*(.*)$/mu,
734
+ whenToUse: /^whenToUse:\s*(.*)$/mu,
735
+ };
652
736
  const pick = (key) => {
653
- const m = fm.match(new RegExp("^" + key + ":\\s*(.*)$", "mu"));
737
+ const m = fm.match(KEY_PATTERNS[key]);
654
738
  if (!m) return "";
655
739
  const first = m[1].trim();
656
740
  if (first.startsWith("|")) {
@@ -845,6 +929,7 @@ window.__ModuleLoader__.load({
845
929
  });
846
930
  // 已安装技能清单(~/.dsh/skills),[{name, dir|file}],用于技能条目「已装」标记与已安装区展示
847
931
  const [installedSkills, setInstalledSkills] = react.useState([]);
932
+ const [pluginSkills, setPluginSkills] = react.useState([]);
848
933
  // 原始页面标题(AI 授权标题闪烁后恢复用)
849
934
  const originalTitleRef = react.useRef("");
850
935
  // 技能删除两步确认:存待确认技能名
@@ -930,7 +1015,132 @@ window.__ModuleLoader__.load({
930
1015
  const [aiFallback, setAiFallback] = react.useState(() => {
931
1016
  try { return localStorage.getItem("pc-ai-fallback-v2") !== "off"; } catch { return true; }
932
1017
  });
1018
+ // AI 赋能:来源输入 / 计划确认 / 执行进度
1019
+ const [aiOpen, setAiOpen] = react.useState(false);
1020
+ const [aiSource, setAiSource] = react.useState("");
1021
+ const [aiJob, setAiJob] = react.useState(null);
1022
+ const [aiSelected, setAiSelected] = react.useState({});
1023
+ const [aiRunning, setAiRunning] = react.useState(false);
1024
+ // 服务器组件清单(AI 赋能注册的服务型组件)
1025
+ const [comps, setComps] = react.useState([]);
1026
+ const [compBusy, setCompBusy] = react.useState(null);
1027
+ const [compDropOpen, setCompDropOpen] = react.useState(false);
1028
+ const [compCollapsed, setCompCollapsed] = react.useState(() => {
1029
+ try { return localStorage.getItem("pc-comp-collapsed") === "1"; } catch { return false; }
1030
+ });
1031
+ const sectionRef = react.useRef(null);
1032
+ const [compCardTop, setCompCardTop] = react.useState(null);
1033
+ function measureCompCardTop() {
1034
+ const host = sectionRef.current;
1035
+ if (!host) return;
1036
+ const secTop = host.getBoundingClientRect().top;
1037
+ let top = secTop;
1038
+ let node = host.parentElement;
1039
+ while (node) {
1040
+ const r = node.getBoundingClientRect();
1041
+ if (r.height > 10 && r.height <= 1600) {
1042
+ if (r.top < top - 28) { top = r.top; break; }
1043
+ if (r.top > top + 2) break;
1044
+ }
1045
+ node = node.parentElement;
1046
+ }
1047
+ setCompCardTop(`${Math.max(8, Math.round(top))}px`);
1048
+ }
1049
+ async function refreshComps() {
1050
+ try {
1051
+ const data = await call("/plugin-console/components", {});
1052
+ if (data && data.ok === true) setComps(data.components ?? []);
1053
+ else setComps([]);
1054
+ } catch { setComps([]); }
1055
+ window.setTimeout(measureCompCardTop, 50);
1056
+ }
1057
+ async function aiPoll(jobId) {
1058
+ try {
1059
+ const data = await call("/plugin-console/ai-empower/status", { jobId });
1060
+ if (data && data.ok === true) {
1061
+ setAiJob(data);
1062
+ if (data.status === "running") {
1063
+ window.setTimeout(() => aiPoll(jobId), 2500);
1064
+ return;
1065
+ }
1066
+ if (data.status === "plan-ready") {
1067
+ const sel = {};
1068
+ for (const s of data.steps ?? []) sel[s.index] = true;
1069
+ setAiSelected(sel);
1070
+ }
1071
+ setAiRunning(false);
1072
+ refreshComps();
1073
+ }
1074
+ } catch (error) {
1075
+ setAiJob((prev) => ({ ...(prev ?? {}), status: "failed", error: error.message }));
1076
+ setAiRunning(false);
1077
+ }
1078
+ }
1079
+ function openAiFor(source) {
1080
+ setAiSource(String(source ?? ""));
1081
+ setAiJob(null);
1082
+ setAiRunning(false);
1083
+ setAiOpen(true);
1084
+ }
1085
+ async function aiStartPlan() {
1086
+ const source = aiSource.trim();
1087
+ if (source === "") return;
1088
+ setAiJob(null);
1089
+ setAiRunning(true); try {
1090
+ const data = await call("/plugin-console/ai-empower/plan", { source });
1091
+ if (data && data.ok === true) { setAiJob({ jobId: data.jobId, source, status: "running", stage: "planning" }); aiPoll(data.jobId); }
1092
+ else { setAiRunning(false); setMessage(t("failed") + ":" + (data?.error ?? "未知")); }
1093
+ } catch (error) {
1094
+ setAiRunning(false);
1095
+ setMessage(t("failed") + ":" + friendlyGithubError(error).message);
1096
+ }
1097
+ }
1098
+ async function aiRun() {
1099
+ if (aiJob === null || aiJob.status !== "plan-ready") return;
1100
+ const steps = (aiJob.steps ?? []).map((s) => s.index).filter((i) => aiSelected[i]);
1101
+ setAiRunning(true);
1102
+ try {
1103
+ await call("/plugin-console/ai-empower/run", { jobId: aiJob.jobId, steps });
1104
+ aiPoll(aiJob.jobId);
1105
+ } catch (error) {
1106
+ setAiRunning(false);
1107
+ setMessage(t("failed") + ":" + friendlyGithubError(error).message);
1108
+ }
1109
+ }
1110
+ async function aiCancel() {
1111
+ if (aiJob === null) return;
1112
+ try { await call("/plugin-console/ai-empower/cancel", { jobId: aiJob.jobId }); } catch {}
1113
+ setAiRunning(false);
1114
+ await aiPoll(aiJob.jobId).catch(() => {});
1115
+ }
1116
+ async function compAction(id, action) {
1117
+ setCompBusy(id);
1118
+ try {
1119
+ const data = await call("/plugin-console/component/" + action, { id });
1120
+ setMessage(t("compActionDone").replace("{action}", t(action === "start" ? "compStart" : "compStop")).replace("{name}", data?.name ?? id));
1121
+ } catch (error) {
1122
+ setMessage(t("failed") + ":" + error.message);
1123
+ }
1124
+ setCompBusy(null);
1125
+ refreshComps();
1126
+ }
1127
+ async function compStatusOne(id) {
1128
+ setCompBusy(id);
1129
+ try {
1130
+ const data = await call("/plugin-console/component/status", { id });
1131
+ if (data && data.ok === true) {
1132
+ setMessage(data.running ? (data.healthy ? t("compHealthy") : t("compUnhealthy")) + ":" + (data.name ?? id) : t("compStopped") + ":" + (data.name ?? id));
1133
+ }
1134
+ } catch (error) {
1135
+ setMessage(t("failed") + ":" + error.message);
1136
+ }
1137
+ setCompBusy(null);
1138
+ refreshComps();
1139
+ }
933
1140
  const aiAutoDeclinedRef = react.useRef({});
1141
+ react.useEffect(() => {
1142
+ if (state.status === "ready") { refreshComps(); window.setTimeout(measureCompCardTop, 120); }
1143
+ }, [state.status]);
934
1144
  // 已关闭的失败提示(用户点 × 后不再显示,服务重启后重置)
935
1145
  const [dismissedFailures, setDismissedFailures] = react.useState({});
936
1146
  // 软件源管理
@@ -1001,7 +1211,10 @@ window.__ModuleLoader__.load({
1001
1211
  // 挂载时拉取已安装技能清单(技能条目「已装」标记用)
1002
1212
  react.useEffect(() => {
1003
1213
  call("/plugin-console/skills-installed").then(
1004
- (data) => { if (data && Array.isArray(data.skills)) setInstalledSkills(data.skills); },
1214
+ (data) => {
1215
+ if (data && Array.isArray(data.skills)) setInstalledSkills(data.skills);
1216
+ if (data && Array.isArray(data.pluginSkills)) setPluginSkills(data.pluginSkills);
1217
+ },
1005
1218
  () => {},
1006
1219
  );
1007
1220
  }, []);
@@ -1072,15 +1285,26 @@ window.__ModuleLoader__.load({
1072
1285
  };
1073
1286
  run();
1074
1287
  }, [market, state]);
1075
- // Hub 自身更新检测:只在远程 npm 有更新时显示按钮
1288
+ // Hub 自身更新检测:semver 比较,且识别 beta/next 测试版(本地已是测试版最新则不提示)
1076
1289
  react.useEffect(() => {
1077
1290
  if (state.status !== "ready") return;
1078
1291
  const current = state.data.selfVersion ?? null;
1079
1292
  if (!current) return;
1293
+ const ver = (v) => {
1294
+ try {
1295
+ const m = String(v).match(/^(\d+)\.(\d+)\.(\d+)/);
1296
+ return m ? Number(m[1]) * 1000000 + Number(m[2]) * 1000 + Number(m[3]) : 0;
1297
+ } catch { return 0; }
1298
+ };
1080
1299
  call("/plugin-console/check-update", { packageName: "@noob-stupid/dsh-plugin-console" }).then(
1081
1300
  (data) => {
1082
- if (data && data.error === null && typeof data.latest === "string" && data.latest !== current) {
1083
- setSelfUpdate({ latest: data.latest });
1301
+ if (data && data.error === null) {
1302
+ const candidates = [data.latest, data.next, data.beta].filter((v) => typeof v === "string" && v !== "");
1303
+ let better = null;
1304
+ for (const v of candidates) {
1305
+ if (ver(v) > ver(current) && (better === null || ver(v) > ver(better))) better = v;
1306
+ }
1307
+ setSelfUpdate(better === null ? null : { latest: better });
1084
1308
  } else {
1085
1309
  setSelfUpdate(null);
1086
1310
  }
@@ -1772,7 +1996,13 @@ window.__ModuleLoader__.load({
1772
1996
  className: styles.toggle,
1773
1997
  disabled: (updateCheck !== null && updateCheck.status === "checking") || installing === entry.moduleName,
1774
1998
  onClick: () => checkUpdate(entry),
1775
- }, updateCheck !== null && updateCheck.status === "checking" ? t("checkingUpdate") : t("checkUpdate"))) : null,
1999
+ }, updateCheck !== null && updateCheck.status === "checking" ? t("checkingUpdate") : t("checkUpdate")),
2000
+ el("button", {
2001
+ type: "button",
2002
+ className: styles.toggle,
2003
+ title: t("aiEmpowerTitle"),
2004
+ onClick: () => openAiFor(String(entry.moduleName ?? entry.repo ?? "")),
2005
+ }, t("aiEmpowerLabel"))) : null,
1776
2006
  updateCheck !== null && updateCheck.status === "ready"
1777
2007
  ? (updateCheck.error
1778
2008
  ? el("p", { className: styles.status, "data-error": "true" }, t("updateCheckFailed") + ":" + updateCheck.error)
@@ -1957,6 +2187,12 @@ window.__ModuleLoader__.load({
1957
2187
  disabled: repoInfo !== null && repoInfo.status === "loading" && repoInfo.repo === item.fullName,
1958
2188
  onClick: () => inspect(item),
1959
2189
  }, t("view")),
2190
+ el("button", {
2191
+ type: "button",
2192
+ className: styles.toggle,
2193
+ title: t("aiEmpowerTitle"),
2194
+ onClick: () => openAiFor(item.fullName),
2195
+ }, t("aiEmpowerLabel")),
1960
2196
  // 技能条目(技能市场模式 / 技能索引数据):走技能安装通道(clone → ~/.dsh/skills)
1961
2197
  item.skillTopics !== undefined
1962
2198
  ? (() => {
@@ -2132,10 +2368,88 @@ window.__ModuleLoader__.load({
2132
2368
  && (frameworkStatus.status === "done" || frameworkStatus.status === "failed")
2133
2369
  && (() => { try { return localStorage.getItem("pc-fw-dismiss-terminal") === frameworkStatus.status; } catch { return false; } })();
2134
2370
  const fwShowCard = frameworkStatus !== null && frameworkStatus.status !== "idle" && !fwStatusDismissed && !fwTerminalDismissed;
2135
- return el("section", { className: styles.section, "aria-busy": state.status === "loading" },
2371
+ return el("section", { ref: sectionRef, className: styles.section, "aria-busy": state.status === "loading" },
2136
2372
  state.status === "ready" && state.data.compat && !state.data.compat.supported
2137
2373
  ? el("p", { className: styles.message, "data-error": "true" }, state.data.compat.notice)
2138
2374
  : null,
2375
+ state.status === "ready" && comps.length > 0 && details === null && repoInfo === null
2376
+ ? el("button", {
2377
+ type: "button",
2378
+ className: styles.compToggle,
2379
+ style: compCardTop !== null ? { top: compCardTop, left: compCollapsed ? "8px" : "336px" } : { left: compCollapsed ? "8px" : "336px" },
2380
+ title: t(compCollapsed ? "compExpandTitle" : "compCollapseTitle"),
2381
+ "aria-label": t(compCollapsed ? "compExpandTitle" : "compCollapseTitle"),
2382
+ onClick: () => setCompCollapsed((v) => {
2383
+ const next = !v;
2384
+ try { localStorage.setItem("pc-comp-collapsed", next ? "1" : "0"); } catch {}
2385
+ return next;
2386
+ }),
2387
+ }, compCollapsed ? "▶" : "◀")
2388
+ : null,
2389
+ state.status === "ready" && comps.length > 0 && !compCollapsed && details === null && repoInfo === null
2390
+ ? el("div", { className: styles.compCard, style: compCardTop !== null ? { top: compCardTop } : undefined },
2391
+ el("div", { className: styles.rowTop },
2392
+ el("strong", { className: styles.name }, t("aiEmpowerComponents") + "(" + comps.length + ")"),
2393
+ el("button", { type: "button", className: styles.toggle, onClick: refreshComps }, t("compRefresh")),
2394
+ comps.length > 1
2395
+ ? el("button", { type: "button", className: styles.toggle, "data-active": compDropOpen ? "true" : "false", title: t("compDropTitle"), onClick: () => setCompDropOpen((v) => !v) }, compDropOpen ? "▴" : "▾")
2396
+ : null),
2397
+ compDropOpen && comps.length > 1
2398
+ ? el("div", { className: styles.compDrop },
2399
+ comps.map((c) =>
2400
+ el("button", { key: c.id, type: "button", className: styles.compDropRow, onClick: () => { if (c.uiUrl) window.open(c.uiUrl, "_blank", "noopener"); } },
2401
+ el("span", null, c.name + "(" + t(c.running ? "compRunning" : "compStopped") + ")"),
2402
+ el("span", { className: styles.compDropUrl }, c.uiUrl ?? t("compNoUrl")))))
2403
+ : null,
2404
+ comps.map((c) =>
2405
+ el("div", { key: c.id, className: styles.rowTop, style: { flexWrap: "wrap" } },
2406
+ el("strong", { className: styles.name, style: { flex: "1 1 auto" } }, c.name),
2407
+ el("span", { className: styles.tag, "data-enabled": c.running ? "true" : "false" }, c.running ? t("compRunning") : t("compStopped")),
2408
+ (c.running && c.healthy !== null) ? el("span", { className: styles.tag, "data-enabled": c.healthy ? "true" : "false" }, c.healthy ? t("compHealthy") : t("compUnhealthy")) : null,
2409
+ el("div", { className: styles.rowTop },
2410
+ el("button", { type: "button", className: styles.toggle, disabled: c.running || compBusy === c.id, onClick: () => compAction(c.id, "start") }, t("compStart")),
2411
+ el("button", { type: "button", className: styles.toggle, disabled: !c.running || compBusy === c.id, onClick: () => compAction(c.id, "stop") }, t("compStop")),
2412
+ el("button", { type: "button", className: styles.toggle, disabled: compBusy === c.id, onClick: () => compStatusOne(c.id) }, t("compStatus")),
2413
+ el("button", { type: "button", className: styles.toggle, disabled: !c.uiUrl, title: c.uiUrl ?? "", onClick: () => { if (c.uiUrl) window.open(c.uiUrl, "_blank", "noopener"); } }, t("compOpen"))))))
2414
+ : null,
2415
+ aiOpen
2416
+ ? el("div", { className: styles.modalBackdrop },
2417
+ el("div", { className: styles.modalCard, style: { maxWidth: 560 } },
2418
+ el("div", { className: styles.rowTop, style: { justifyContent: "space-between" } },
2419
+ el("strong", { className: styles.name }, t("aiEmpowerLabel")),
2420
+ el("button", { type: "button", className: styles.trashBtn, style: { fontSize: 14, padding: "4px 8px" }, title: t("closeModal"), "aria-label": t("closeModal"), onClick: () => setAiOpen(false) }, "✕")),
2421
+ aiJob === null || (aiJob.status === "running" && aiJob.stage === "planning")
2422
+ ? el("div", { style: { flexDirection: "column", gap: "10px", display: "flex" } },
2423
+ el("section", null,
2424
+ el("input", { type: "text", placeholder: t("aiEmpowerSrcTitle"), value: aiSource, disabled: aiRunning, onChange: (event) => setAiSource(event.currentTarget.value), onKeyDown: (event) => { if (event.key === "Enter" && !aiRunning) aiStartPlan(); }, style: { width: "100%", boxSizing: "border-box", font: "inherit", padding: "6px 10px", borderRadius: 8, border: "1px solid var(--dsw-alias-border-l2)", background: "var(--dsw-alias-bg-layer-1)", color: "var(--dsw-alias-label-primary)" } }),
2425
+ el("div", { className: styles.rowTop },
2426
+ el("button", { type: "button", className: styles.toggle, disabled: aiRunning || aiSource.trim() === "", onClick: aiStartPlan }, t("aiEmpowerLabel")),
2427
+ aiRunning ? el("span", { className: styles.message }, t("aiEmpowerWaiting")) : null)))
2428
+ : aiJob.status === "plan-ready"
2429
+ ? el("div", { style: { flexDirection: "column", gap: "10px", display: "flex" } },
2430
+ el("div", { className: styles.rowTop },
2431
+ el("strong", { className: styles.name }, (aiJob.displayName ?? aiJob.source) + ":"),
2432
+ el("span", { className: styles.tag }, t("aiEmpowerType") + ":" + (t("aiEmpowerTypeMapping")[aiJob.type] ?? aiJob.type ?? "?"))),
2433
+ aiJob.summary ? el("p", { className: styles.message }, aiJob.summary) : null,
2434
+ (aiJob.servers ?? []).length > 0
2435
+ ? el("p", { className: styles.message }, (aiJob.servers ?? []).map((s) => (s.name ?? "?") + (s.port ? "(端口 " + s.port + ")" : "")).join("、"))
2436
+ : null,
2437
+ el("strong", { className: styles.name }, t("aiEmpowerSteps")),
2438
+ el("div", { style: { flexDirection: "column", gap: "6px", display: "flex", maxHeight: 220, overflowY: "auto" } },
2439
+ (aiJob.steps ?? []).map((s) =>
2440
+ el("label", { key: s.index, className: styles.consentRemember, style: { alignItems: "flex-start" } },
2441
+ el("input", { type: "checkbox", checked: aiSelected[s.index] === true, onChange: (event) => setAiSelected((prev) => ({ ...prev, [s.index]: event.currentTarget.checked })) }),
2442
+ el("span", null, (s.index + 1) + ". [" + s.action + "] " + (s.description ?? "") + (s.path ? " → " + s.path : "")))),
2443
+ el("div", { className: styles.rowTop },
2444
+ el("button", { type: "button", className: styles.toggle, onClick: aiRun }, t("aiEmpowerRun")),
2445
+ el("button", { type: "button", className: styles.toggle, onClick: () => { setAiJob(null); setAiRunning(false); } }, t("aiEmpowerCancel")))))
2446
+ : el("div", { style: { flexDirection: "column", gap: "10px", display: "flex" } },
2447
+ el("p", { className: styles.message, "data-error": aiJob.status === "failed" ? "true" : undefined }, aiJob.status === "done" ? t("aiEmpowerDone") : aiJob.status === "failed" ? t("aiEmpowerFail") + ":" + (aiJob.error ?? "?") : t("aiEmpowerExecuting") + "(" + (aiJob.progress?.done ?? 0) + "/" + (aiJob.progress?.total ?? "?") + ")"),
2448
+ el("pre", { style: { maxHeight: 200, overflow: "auto", whiteSpace: "pre-wrap", wordBreak: "break-all", background: "var(--dsw-alias-bg-layer-2)", border: "1px solid var(--dsw-alias-border-l2)", borderRadius: 8, padding: 8, fontSize: 11, margin: 0, fontFamily: "inherit" } }, aiJob.logText ?? ""),
2449
+ el("div", { className: styles.rowTop },
2450
+ (aiJob.status === "running") ? el("button", { type: "button", className: styles.toggle, onClick: aiCancel }, t("aiEmpowerCancel")) : null,
2451
+ el("button", { type: "button", className: styles.toggle, onClick: () => { setAiJob(null); } }, t("closeModal"))))))
2452
+ : null,
2139
2453
  state.status === "ready" && state.data.framework && state.data.framework.upgraded === true && !frameworkNoticeDismissed
2140
2454
  ? el("div", { className: styles.messageRow },
2141
2455
  el("p", { className: styles.message }, t("frameworkUpgradeNotice").replace("{from}", state.data.framework.from ?? "?").replace("{to}", state.data.framework.version ?? "?").replace("{dir}", state.data.framework.backupDir ?? "").replace("{patch}", state.data.framework.patchApplied === true ? t("frameworkPatchApplied") : t("frameworkPatchSkipped") + (state.data.framework.patchNote ? ":" + state.data.framework.patchNote : ""))),
@@ -2271,6 +2585,15 @@ setMessage(t("failed") + ":" + (data?.reason ?? "未知"));
2271
2585
  return next;
2272
2586
  }),
2273
2587
  }, t("aiFallbackLabel")),
2588
+ el("button", {
2589
+ type: "button",
2590
+ className: styles.aiEmpower,
2591
+ "data-busy": aiRunning ? "true" : "false",
2592
+ "data-active": aiOpen ? "true" : "false",
2593
+ title: t("aiEmpowerTitle"),
2594
+ "aria-label": t("aiEmpowerLabel"),
2595
+ onClick: () => setAiOpen((v) => !v),
2596
+ }, t("aiEmpowerLabel")),
2274
2597
  el("button", {
2275
2598
  type: "button",
2276
2599
  className: styles.sourcesFloat,
@@ -2461,9 +2784,22 @@ setMessage(t("failed") + ":" + (data?.reason ?? "未知"));
2461
2784
  )
2462
2785
  : null),
2463
2786
  mode === "skills"
2464
- ? (installedSkills.length === 0
2465
- ? el("p", { className: styles.status }, t("skillsEmpty"))
2466
- : el("ul", { className: styles.list }, installedSkills.map((sk) =>
2787
+ ? (installedSkills.length === 0 && pluginSkills.length === 0
2788
+ ? el("p", { className: styles.status, "data-error": "false" }, t("skillsEmpty") + "。" + t("skillsEmptyHint"))
2789
+ : el("div", { style: { flexDirection: "column", gap: 8, display: "flex" } },
2790
+ pluginSkills.length > 0
2791
+ ? el("div", { className: styles.detail },
2792
+ el("strong", { className: styles.name }, t("pluginSkillsTitle") + "(" + pluginSkills.length + ")"),
2793
+ el("p", { className: styles.status }, t("pluginSkillsHint")),
2794
+ el("ul", { className: styles.list }, pluginSkills.map((sk) =>
2795
+ el("li", { key: sk.name, className: styles.row },
2796
+ el("div", { className: styles.rowTop },
2797
+ el("code", { className: styles.name }, sk.name),
2798
+ el("span", { className: styles.tag, "data-system": "true" }, t("pluginSkillTag") + (sk.provider ? " · " + sk.provider : ""))),
2799
+ sk.description ? el("p", { className: styles.desc }, sk.description) : null))))
2800
+ : null,
2801
+ installedSkills.length > 0
2802
+ ? el("ul", { className: styles.list }, installedSkills.map((sk) =>
2467
2803
  el("li", { key: sk.name, className: styles.row },
2468
2804
  el("div", { className: styles.rowTop },
2469
2805
  el("code", { className: styles.name }, sk.name),
@@ -2496,7 +2832,8 @@ setMessage(t("failed") + ":" + (data?.reason ?? "未知"));
2496
2832
  title: t("skillDelete"),
2497
2833
  "aria-label": t("skillDelete"),
2498
2834
  onClick: () => setConfirmSkillDelete(sk.name),
2499
- }, "✕"))))))
2835
+ }, "✕")))))
2836
+ : null))
2500
2837
  : (state.status === "loading"
2501
2838
  ? el("p", { className: styles.status }, t("loading"))
2502
2839
  : state.status === "error"