@yottameta/yotta-skills-plugin 0.28.3 → 0.29.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/.agents/plugins/marketplace.json +1 -1
- package/.claude-plugin/marketplace.json +1 -1
- package/package.json +1 -1
- package/plugin.json +1 -1
- package/skills/yotta-skills/SKILL.md +48 -3
- package/skills/yotta-skills/assets/view.html +99 -11
- package/skills/yotta-skills/bin/yotta-skills.js +459 -62
- package/skills/yotta-skills/lib/agent-discovery.js +99 -7
- package/skills/yotta-skills/lib/cli-help.js +31 -1
- package/skills/yotta-skills/lib/host-purge.js +167 -0
- package/skills/yotta-skills/lib/hosts-registry.js +197 -0
- package/skills/yotta-skills/lib/hub-view-server.js +101 -1
- package/skills/yotta-skills/lib/hub.js +16 -0
- package/skills/yotta-skills/lib/install-pipeline.js +19 -0
- package/skills/yotta-skills/lib/self-install.js +224 -0
- package/skills/yotta-skills/references/hub.md +36 -0
- package/skills/yotta-skills/scan-policy.json +2 -2
- package/skills/yotta-skills/scripts/yotta-skills-mcp.py +1 -1
|
@@ -12,7 +12,7 @@
|
|
|
12
12
|
"authentication": "ON_INSTALL"
|
|
13
13
|
},
|
|
14
14
|
"category": "Developer Tools",
|
|
15
|
-
"version": "0.
|
|
15
|
+
"version": "0.29.0",
|
|
16
16
|
"description": "YuanGe (元阁) - orchestration routing, inventory, and one-command installer for the YottaMeta skill family (zero dependencies, Node.js only).",
|
|
17
17
|
"interface": {
|
|
18
18
|
"displayName": "元阁 yotta-skills",
|
|
@@ -12,7 +12,7 @@
|
|
|
12
12
|
"authentication": "ON_INSTALL"
|
|
13
13
|
},
|
|
14
14
|
"category": "Developer Tools",
|
|
15
|
-
"version": "0.
|
|
15
|
+
"version": "0.29.0",
|
|
16
16
|
"description": "YuanGe (元阁) - orchestration routing, inventory, and one-command installer for the YottaMeta skill family (zero dependencies, Node.js only).",
|
|
17
17
|
"interface": {
|
|
18
18
|
"displayName": "元阁 yotta-skills",
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@yottameta/yotta-skills-plugin",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.29.0",
|
|
4
4
|
"description": "YuanGe (元阁) — orchestration and routing for the YottaMeta skill family, packaged as a YottaMeta Agent Plugin and DeepSeek Harness profile bundle.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"keywords": [
|
package/plugin.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"$schema": "https://agent-plugins.org/schemas/1.0.0/plugin.schema.json",
|
|
3
3
|
"name": "yotta-skills",
|
|
4
|
-
"version": "0.
|
|
4
|
+
"version": "0.29.0",
|
|
5
5
|
"description": "YuanGe (元阁) - orchestration routing, inventory, and one-command installer for the YottaMeta skill family (zero dependencies, Node.js only).",
|
|
6
6
|
"author": {
|
|
7
7
|
"name": "YottaMeta",
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: yotta-skills
|
|
3
|
-
version: 0.
|
|
4
|
-
description: 元阁 -- 元阁全家技能的总编排策划 + 编排路由 + 一键安装器 + 技能盘点 + 本机技能 Hub + 运行时 hook 适配。Hub 层:hub hosts
|
|
3
|
+
version: 0.29.0
|
|
4
|
+
description: 元阁 -- 元阁全家技能的总编排策划 + 编排路由 + 一键安装器 + 技能盘点 + 本机技能 Hub + 运行时 hook 适配。Hub 层:hub hosts 只读发现本机已装智能体与技能目录(文件系统优先,不读元忆;已核实 / 自动发现 / 桥接分类 + 状态细分:可用 / 残留(实体未确认)/ 未创建 / 仅标记);hub hosts add/remove/list/mark 注册自定义宿主目录与手动标记(只注册不建目录、移除不删目录;残留清理 hub hosts remove --purge 默认预览、目录入回收站 7 天);install-self / where 把元阁管理引擎独立安装(默认 ~/.yottaskills/yotta-skills,--dir 指定)并查看运行位置;hub install / update 把技能装到 ~/.yottaskills/hub 单点真源(清单 27 + 特殊家族 5,特殊家族跟随各自 npm latest);hub link --all 用 Windows junction / POSIX symlink 分发到全部已核实宿主(默认范围;--include-discovered 显式纳入自动发现目录;锁 / 数据桥接目录永不链接),元技能在链接时收敛宿主旧副本(移入 ~/.yottaskills/trash 保留 7 天、版本闸门 Hub ≥ 宿主);hub unlink 只删链接、fail-closed;非元技能不参与更新(用户自行处理);hub status 显示来源、版本、链接与异常;view 启动本机技能枢纽面板(默认 127.0.0.1:8789,六视图 + 高级 CLI 页,写操作需页面令牌与确认);兼容 agentskills.io 技能格式、Vercel Labs skills CLI 的 .agents/skills 通用目录与 .skill-lock.json v3(只读)。路由层:--route / route_request 按需求摘要给出候选组合、调用顺序、角色、置信度、依据、已装/缺失状态与安装命令,只建议不自动安装;非元阁家族已装技能按 frontmatter description 机械匹配作并列候选(标注来源与未扫描状态,只读不自动调用);M1 记忆裁决层:usage enable/mark 本地结构化记录 + decide-memory / MCP decide_memory 输出 promote / hold / demote 只读建议(需授权 provider;只建议不删除、不自动写元忆);策划层:按场景给出「该组合哪几个元技能、组合强在哪、怎么组合使用(安装与调用均由用户确认后执行)」;安装层:一条命令把 YottaMeta 已发布的全部 yotta-* 技能装进指定智能体或目录(默认 --pin 锁死清单精确版本);盘点层:--inventory / --reindex 扫描本机已装技能生成/更新注册表,新装技能自动被发现(install/update 后自动 re-index,会话开工只建议跑本地 --reindex;更新检查走手动 --check 或后台 --check --scheduled);运行时适配层:hook capabilities / evaluate / bind / unbind 按宿主能力矩阵执行六个统一事件并留证降级,元信 before_install 已接入安装管线;自包含零依赖,不依赖任何元技能;MCP 按需加载且需用户确认后写入配置(可选:list_installed_skills/describe_skill/reindex/route_request/decide_memory,不常驻,未加载降级 CLI)。支持 --list 清单 / --route 路由 / usage 使用记录 / decide-memory 记忆裁决 / hub 技能 Hub / install-self 独立安装 / where 位置查看 / install / update / update --check(只读检查)/ update --check --scheduled(后台周检)/ update --auto(家族自动更新)/ hook 适配 / --inventory / --reindex / --dry-run 预览 / --pin(默认)/ --range。触发:需要批量安装或更新元阁全家技能、单点安装并分发到多个智能体、查看本机装了哪些智能体与技能目录、接管非元技能、按场景组合多个元技能、路由或判断该用哪些技能、判断哪些技能值得长期记忆、查看或记录技能使用信号、盘点或查看本机已装技能、重扫技能注册表、给某个智能体或目录一次性铺齐 yotta-* 技能、评估宿主 hook 能力、预览安装清单、锁版本安装、或用户说 元阁/装全家/一次装齐/yotta-skills/install-all/更新全家/检查更新/自动更新/hook 适配/该用哪个技能/路由技能/记忆裁决/技能该不该记住/盘点技能/查看已装技能/技能 Hub/单点安装/链接分发/接管技能/技能枢纽面板/图形化面板/独立安装元阁/查看元阁位置/自定义宿主/残留清理/宿主状态 等。边界(Do NOT trigger):只做「组合策划 + 静态路由建议 + M1 记忆裁决只读建议 + 清单 + 下载 + 落位 + 汇总 + 盘点 + re-index + Hub 本机真源与链接分发 + hook 适配」,不含技能本体、不做技能内容开发、不 -g 污染全局、不自动安装缺失技能、不静默写宿主配置或全局记忆、不自动删除技能或记忆;家族安装先自举或调用元信装前门禁,DO NOT INSTALL 阻断,非元阁家族包不自动安装。
|
|
5
5
|
license: MIT
|
|
6
6
|
metadata:
|
|
7
7
|
zh_name: 元阁
|
|
@@ -105,11 +105,40 @@ metadata:
|
|
|
105
105
|
|
|
106
106
|
## 快速使用(安装层)
|
|
107
107
|
|
|
108
|
+
**首选流程(Hub 主路径,安装本体与智能体无关)**:
|
|
109
|
+
|
|
110
|
+
```bash
|
|
111
|
+
# 1) 看本机有哪些宿主与技能目录(状态细分:可用 / 残留 / 未创建 / 仅标记)
|
|
112
|
+
npx -y @yottameta/yotta-skills hub hosts
|
|
113
|
+
|
|
114
|
+
# 2) 装进 Hub 真源(默认 ~/.yottaskills/hub;无需指定智能体)
|
|
115
|
+
npx -y @yottameta/yotta-skills hub install
|
|
116
|
+
|
|
117
|
+
# 3) 分发到全部已核实宿主(Windows junction / macOS·Linux symlink)
|
|
118
|
+
npx -y @yottameta/yotta-skills hub link --all
|
|
119
|
+
|
|
120
|
+
# 4) 图形化面板管理(宿主矩阵 / 链接与体检 / 收编 / 回滚)
|
|
121
|
+
npx -y @yottameta/yotta-skills view
|
|
122
|
+
```
|
|
123
|
+
|
|
124
|
+
**元阁独立安装(把管理引擎装成独立 CLI,不依赖任何智能体)**:
|
|
125
|
+
|
|
126
|
+
```bash
|
|
127
|
+
# 默认装到 ~/.yottaskills/yotta-skills;--dir 指定位置
|
|
128
|
+
npx -y @yottameta/yotta-skills install-self
|
|
129
|
+
npx -y @yottameta/yotta-skills install-self --dir ~/tools/yotta-skills
|
|
130
|
+
|
|
131
|
+
# 查看元阁位置(运行中 CLI / 独立安装目录 / Hub 真源)
|
|
132
|
+
npx -y @yottameta/yotta-skills where
|
|
133
|
+
```
|
|
134
|
+
|
|
135
|
+
**指定智能体直装(兼容路径)**:
|
|
136
|
+
|
|
108
137
|
```bash
|
|
109
138
|
# 列出全家技能 + 版本 + 说明
|
|
110
139
|
npx -y @yottameta/yotta-skills --list
|
|
111
140
|
|
|
112
|
-
#
|
|
141
|
+
# 装全家到指定智能体默认用户级目录
|
|
113
142
|
npx -y @yottameta/yotta-skills install --agent codex
|
|
114
143
|
|
|
115
144
|
# 装全家到任意目录(每个技能落在 <dir>/<slug>)
|
|
@@ -188,6 +217,16 @@ npx -y @yottameta/yotta-skills hub link --all --include-discovered
|
|
|
188
217
|
npx -y @yottameta/yotta-skills hub status
|
|
189
218
|
npx -y @yottameta/yotta-skills hub doctor
|
|
190
219
|
|
|
220
|
+
# 5b) 自定义宿主注册(只注册,不创建目录、不改宿主配置;移除注册不删目录)
|
|
221
|
+
npx -y @yottameta/yotta-skills hub hosts add ~/my-agent/skills --label "我的宿主"
|
|
222
|
+
npx -y @yottameta/yotta-skills hub hosts list
|
|
223
|
+
npx -y @yottameta/yotta-skills hub hosts remove ~/my-agent/skills
|
|
224
|
+
|
|
225
|
+
# 5c) 手动标记状态 / 清理残留(清理默认预览;只删指向 Hub 的链接,目录入回收站 7 天)
|
|
226
|
+
npx -y @yottameta/yotta-skills hub hosts mark ~/my-agent/skills --state orphan
|
|
227
|
+
npx -y @yottameta/yotta-skills hub hosts remove ~/my-agent/skills --purge
|
|
228
|
+
npx -y @yottameta/yotta-skills hub hosts remove ~/my-agent/skills --purge --yes
|
|
229
|
+
|
|
191
230
|
# 6) 本地面板:预览、确认、执行、留证(仅 127.0.0.1:8789)
|
|
192
231
|
npx -y @yottameta/yotta-skills view
|
|
193
232
|
|
|
@@ -209,6 +248,12 @@ npx -y @yottameta/yotta-skills hub remove my-skill --dry-run
|
|
|
209
248
|
`hub doctor` 的 `link_scope` 检查只读报告范围外链接。逐智能体核实表见
|
|
210
249
|
发布方内部核实记录;本机可随时用 `hub hosts` 复算分类与范围。
|
|
211
250
|
|
|
251
|
+
状态细分(0.29.0 起):每条宿主目录带生命周期状态 —— **可用**(实体已确认:应用标记 /
|
|
252
|
+
用户注册 / 手动标记)、**残留**(实体未确认;可手动确认后清理)、**未创建**、
|
|
253
|
+
**仅标记**(已装实体、技能目录未创建);`hub doctor` 的 `host_state` 检查只读提示,
|
|
254
|
+
不判 fail。残留清理 = `hub hosts remove <目录> --purge`(默认预览,`--yes` 执行;
|
|
255
|
+
只删指向 Hub 的链接,非 Hub 链接 / 非技能内容随目录保留在回收站 7 天)。
|
|
256
|
+
|
|
212
257
|
宿主发现兼容 `agentskills.io` 技能格式与 Vercel Labs `skills` CLI 的
|
|
213
258
|
`.agents/skills` 通用目录;已收录 81 条宿主映射,并只读兼容
|
|
214
259
|
`.skill-lock.json` v3(`$XDG_STATE_HOME/skills/` 或 `~/.agents/`)。
|
|
@@ -50,6 +50,9 @@
|
|
|
50
50
|
.page-sub{color:var(--faint);font-size:12px;margin-top:2px}
|
|
51
51
|
.spacer{flex:1}
|
|
52
52
|
.chip{display:flex;align-items:center;gap:7px;padding:5px 10px;border:1px solid var(--line);border-radius:999px;color:var(--muted);font-size:12px;white-space:nowrap}
|
|
53
|
+
.chips{display:flex;gap:6px;flex-wrap:wrap}
|
|
54
|
+
.chip{cursor:pointer;background:transparent}
|
|
55
|
+
.chip.active{color:var(--acc);border-color:rgba(95,191,143,.45);background:rgba(95,191,143,.10)}
|
|
53
56
|
.dot{width:7px;height:7px;border-radius:50%;background:var(--acc)}
|
|
54
57
|
|
|
55
58
|
.view{display:none}.view.active{display:block}
|
|
@@ -213,10 +216,12 @@
|
|
|
213
216
|
<section class="view" id="view-hosts">
|
|
214
217
|
<div class="toolbar">
|
|
215
218
|
<label class="search"><svg class="sv" viewBox="0 0 24 24"><circle cx="11" cy="11" r="7"/><path d="m20 20-3.5-3.5"/></svg><input id="hostSearch" placeholder="过滤宿主 / 目录"></label>
|
|
216
|
-
<
|
|
219
|
+
<div class="chips" id="hostStateChips"></div>
|
|
220
|
+
<button class="btn small" id="hostAddBtn">添加自定义目录</button>
|
|
217
221
|
</div>
|
|
218
222
|
<div class="section panel">
|
|
219
|
-
<
|
|
223
|
+
<div class="faint" id="hostSummary"></div>
|
|
224
|
+
<table><thead><tr><th>宿主</th><th>技能目录</th><th>范围</th><th>目录技能</th><th>指向 Hub</th><th>异常</th><th>状态</th><th></th></tr></thead><tbody id="hostRows"></tbody></table>
|
|
220
225
|
<div class="empty" id="hostEmpty" hidden>没有匹配的宿主目录。</div>
|
|
221
226
|
</div>
|
|
222
227
|
<div class="section panel"><h3>已安装标记(无技能目录的宿主)</h3><div class="list" id="hostMarks"></div></div>
|
|
@@ -315,7 +320,7 @@
|
|
|
315
320
|
<script>
|
|
316
321
|
(function () {
|
|
317
322
|
var TOKEN = (document.querySelector('meta[name="yotta-view-token"]') || {}).content || '';
|
|
318
|
-
var state = { view: 'overview', overview: null, hosts: null, adopt: null, selected: {}, records: null, tab: 'snapshots', hostFilter: '', adoptFilter: '', ovSkillFilter: '' };
|
|
323
|
+
var state = { view: 'overview', overview: null, hosts: null, adopt: null, selected: {}, records: null, tab: 'snapshots', hostFilter: '', hostStateFilter: '', adoptFilter: '', ovSkillFilter: '' };
|
|
319
324
|
|
|
320
325
|
function $(sel) { return document.querySelector(sel); }
|
|
321
326
|
function esc(value) {
|
|
@@ -526,34 +531,115 @@
|
|
|
526
531
|
var d = await api('/api/hosts');
|
|
527
532
|
if (d.error) { toast(d.error); return; }
|
|
528
533
|
state.hosts = d;
|
|
529
|
-
|
|
534
|
+
var s = d.summary.states || {};
|
|
535
|
+
$('#hostSummary').textContent = '可用 ' + (s.available || 0) + ' · 残留 ' + (s.orphan || 0) + ' · 未创建 ' + (s.missing || 0) + ' · 仅标记 ' + (s.markerOnly || 0) + (s.ignored ? ' · 已忽略 ' + s.ignored : '') + ' · 链接 ' + d.summary.links + '(异常 ' + d.summary.brokenLinks + ')';
|
|
536
|
+
renderHostStateChips();
|
|
530
537
|
renderHosts();
|
|
531
538
|
var marks = d.installed || [];
|
|
532
539
|
$('#hostMarks').innerHTML = marks.length ? marks.map(function (m) {
|
|
533
540
|
return '<div class="row"><div class="row-main"><div class="row-title">' + esc(m.label) + '</div><div class="row-meta">' + esc(m.source || '') + (m.hasSkillsDir ? ' · 技能目录:' + esc(m.skillDir || '') : ' · 未发现技能目录') + '</div></div><span class="badge ' + (m.hasSkillsDir ? 'ok' : 'info') + '">' + esc(m.hasSkillsDir ? '可链接' : '仅标记') + '</span></div>';
|
|
534
541
|
}).join('') : '<div class="empty">没有检测到额外宿主标记。</div>';
|
|
535
542
|
}
|
|
543
|
+
function renderHostStateChips() {
|
|
544
|
+
var d = state.hosts; if (!d) return;
|
|
545
|
+
var s = d.summary.states || {};
|
|
546
|
+
var chips = [
|
|
547
|
+
['', '全部'],
|
|
548
|
+
['available', '可用 ' + (s.available || 0)],
|
|
549
|
+
['orphan', '残留 ' + (s.orphan || 0)],
|
|
550
|
+
['missing', '未创建 ' + (s.missing || 0)],
|
|
551
|
+
['marker-only', '仅标记 ' + (s.markerOnly || 0)],
|
|
552
|
+
];
|
|
553
|
+
$('#hostStateChips').innerHTML = chips.map(function (c) {
|
|
554
|
+
return '<button class="chip' + (state.hostStateFilter === c[0] ? ' active' : '') + '" data-host-state="' + esc(c[0]) + '">' + esc(c[1]) + '</button>';
|
|
555
|
+
}).join('');
|
|
556
|
+
}
|
|
557
|
+
function hostStateBadge(h) {
|
|
558
|
+
if (h.bridgeOnly) return '<span class="badge info">桥接</span>';
|
|
559
|
+
var map = { available: ['ok', '可用'], orphan: ['warn', '残留'], missing: ['', '未创建'], 'marker-only': ['info', '仅标记'], ignored: ['', '已忽略'] };
|
|
560
|
+
var item = map[h.state] || ['', h.state || '未知'];
|
|
561
|
+
return '<span class="badge ' + item[0] + '">' + esc(item[1]) + (h.stateSource === 'manual' ? '·手动' : '') + '</span>' + (h.brokenLinks ? ' <span class="badge danger">异常</span>' : '');
|
|
562
|
+
}
|
|
536
563
|
function renderHosts() {
|
|
537
564
|
var d = state.hosts; if (!d) return;
|
|
538
565
|
var q = state.hostFilter.toLowerCase();
|
|
566
|
+
var sf = state.hostStateFilter;
|
|
539
567
|
var rows = (d.hosts || []).filter(function (h) {
|
|
568
|
+
if (sf && h.state !== sf) return false;
|
|
540
569
|
if (!q) return true;
|
|
541
570
|
return String(h.label || '').toLowerCase().includes(q) || String(h.dir || '').toLowerCase().includes(q);
|
|
542
571
|
});
|
|
543
572
|
$('#hostEmpty').hidden = rows.length > 0;
|
|
544
573
|
$('#hostRows').innerHTML = rows.map(function (h) {
|
|
545
|
-
var
|
|
546
|
-
var
|
|
574
|
+
var scopeBadge = h.bridgeOnly ? '<span class="badge info">桥接·不链</span>' : (h.detection === 'user-registry' ? '<span class="badge info">用户注册</span>' : h.verified ? '<span class="badge ok">已核实</span>' : '<span class="badge warn">自动发现</span>');
|
|
575
|
+
var action = '';
|
|
576
|
+
if (h.exists && h.state === 'orphan' && !h.bridgeOnly) action += '<button class="btn small danger" data-host-purge="' + esc(h.dir) + '">清理</button> ';
|
|
577
|
+
if (h.detection === 'user-registry') action += '<button class="btn small" data-host-unregister="' + esc(h.dir) + '">移除注册</button>';
|
|
547
578
|
return '<tr><td><div class="cell-main">' + esc(h.label) + '</div><div class="cell-sub">' + esc(h.agentId || (h.known ? '已知宿主' : h.detection || '自动发现')) + '</div></td>' +
|
|
548
579
|
'<td><div class="mono">' + esc(h.dir) + '</div><button class="btn small" data-copy="' + esc(h.dir) + '">复制</button></td>' +
|
|
549
580
|
'<td>' + scopeBadge + '</td>' +
|
|
550
|
-
'<td>' + esc(h.skillCount || 0) + '</td><td>' + esc(h.hubLinks || 0) + '</td><td>' + esc(h.brokenLinks || 0) + '</td><td>' +
|
|
581
|
+
'<td>' + esc(h.skillCount || 0) + '</td><td>' + esc(h.hubLinks || 0) + '</td><td>' + esc(h.brokenLinks || 0) + '</td><td>' + hostStateBadge(h) + '</td><td>' + action + '</td></tr>';
|
|
551
582
|
}).join('');
|
|
552
583
|
}
|
|
553
584
|
$('#hostSearch').addEventListener('input', function (e) { state.hostFilter = e.target.value; renderHosts(); });
|
|
554
|
-
$('#
|
|
555
|
-
var b = e.target.closest('[data-
|
|
556
|
-
if (b)
|
|
585
|
+
$('#hostStateChips').addEventListener('click', function (e) {
|
|
586
|
+
var b = e.target.closest('[data-host-state]');
|
|
587
|
+
if (!b) return;
|
|
588
|
+
state.hostStateFilter = b.dataset.hostState;
|
|
589
|
+
renderHostStateChips();
|
|
590
|
+
renderHosts();
|
|
591
|
+
});
|
|
592
|
+
$('#hostAddBtn').addEventListener('click', function () {
|
|
593
|
+
var body = '<p class="label">注册自定义宿主目录:纳入发现 / 分发 / 收编。不创建目录、不改宿主配置。</p>' +
|
|
594
|
+
'<div class="section"><label class="label">目录(必须已存在)</label><input id="hostAddDir" placeholder="例如 D:\\my-agent\\skills"></div>' +
|
|
595
|
+
'<div class="section"><label class="label">名称(可选,默认目录名)</label><input id="hostAddLabel" placeholder="我的宿主"></div>';
|
|
596
|
+
openModal('添加自定义目录', body, {
|
|
597
|
+
okText: '注册',
|
|
598
|
+
onOk: async function () {
|
|
599
|
+
var dir = ($('#hostAddDir') && $('#hostAddDir').value || '').trim();
|
|
600
|
+
var label = ($('#hostAddLabel') && $('#hostAddLabel').value || '').trim();
|
|
601
|
+
if (!dir) { toast('请填写目录'); return; }
|
|
602
|
+
var r = await api('/api/hosts/add', { body: { dir: dir, label: label } });
|
|
603
|
+
if (r.error) { toast(r.error); return; }
|
|
604
|
+
closeModal();
|
|
605
|
+
toast('已注册:' + ((r.entry && r.entry.label) || dir));
|
|
606
|
+
loadHosts();
|
|
607
|
+
},
|
|
608
|
+
});
|
|
609
|
+
});
|
|
610
|
+
async function openHostPurgeModal(dir) {
|
|
611
|
+
var plan = await api('/api/hosts/purge-plan?dir=' + encodeURIComponent(dir));
|
|
612
|
+
if (plan.error) { toast(plan.error); return; }
|
|
613
|
+
var body = '<p class="label">只删除指向 Hub 的链接(含死链);非 Hub 链接 / 非技能内容随目录保留在回收站(7 天可恢复)。</p>' +
|
|
614
|
+
'<div class="result"><span class="badge warn">将解除链接</span><b>' + esc(plan.unlinkCount) + '</b><span>条(保留 ' + esc(plan.keptLinks) + ' 非 Hub 链接 / ' + esc(plan.keptEntries) + ' 非链接内容)</span></div>' +
|
|
615
|
+
'<div class="mono">' + esc(dir) + '</div>' +
|
|
616
|
+
'<div class="section"><label class="label">输入 purge 确认执行:</label><input id="hostPurgeConfirm" placeholder="purge"></div>';
|
|
617
|
+
confirmModal('清理残留目录?', body, '清理(移入回收站)', true, async function () {
|
|
618
|
+
var typed = ($('#hostPurgeConfirm') && $('#hostPurgeConfirm').value || '').trim();
|
|
619
|
+
if (typed !== 'purge') { toast('确认串不匹配(输入 purge)'); return; }
|
|
620
|
+
var r = await api('/api/hosts/purge', { body: { dir: dir, confirm: 'purge' } });
|
|
621
|
+
if (r.error) { toast(r.error); return; }
|
|
622
|
+
closeModal();
|
|
623
|
+
toast('已清理:解除 ' + r.unlinkCount + ' 条链接;回收站保留 7 天');
|
|
624
|
+
loadHosts();
|
|
625
|
+
});
|
|
626
|
+
}
|
|
627
|
+
$('#hostRows').addEventListener('click', async function (e) {
|
|
628
|
+
var copyBtn = e.target.closest('[data-copy]');
|
|
629
|
+
if (copyBtn) { copyText(copyBtn.dataset.copy); return; }
|
|
630
|
+
var purgeBtn = e.target.closest('[data-host-purge]');
|
|
631
|
+
if (purgeBtn) { openHostPurgeModal(purgeBtn.dataset.hostPurge); return; }
|
|
632
|
+
var unregBtn = e.target.closest('[data-host-unregister]');
|
|
633
|
+
if (unregBtn) {
|
|
634
|
+
var dir = unregBtn.dataset.hostUnregister;
|
|
635
|
+
confirmModal('移除注册?', '<p class="label">只移除自定义宿主注册,不删除目录、不改宿主配置。</p><div class="mono">' + esc(dir) + '</div>', '移除注册', false, async function () {
|
|
636
|
+
var r = await api('/api/hosts/remove', { body: { dir: dir } });
|
|
637
|
+
if (r.error) { toast(r.error); return; }
|
|
638
|
+
closeModal();
|
|
639
|
+
toast('已移除注册(目录保留)');
|
|
640
|
+
loadHosts();
|
|
641
|
+
});
|
|
642
|
+
}
|
|
557
643
|
});
|
|
558
644
|
|
|
559
645
|
async function loadAdopt() {
|
|
@@ -800,7 +886,9 @@
|
|
|
800
886
|
function renderCliHelp(d) {
|
|
801
887
|
if (!d || d.error) { $('#cliMeta').textContent = '帮助加载失败'; return; }
|
|
802
888
|
var total = 0;
|
|
803
|
-
(d.groups || []).forEach(function (g) {
|
|
889
|
+
(d.groups || []).forEach(function (g) {
|
|
890
|
+
(g.commands || []).forEach(function (c) { total += 1 + (c.subcommands || []).length; });
|
|
891
|
+
});
|
|
804
892
|
$('#cliMeta').textContent = 'v' + (d.version || '-') + ' · 命令 ' + total + ' · 选项 ' + (d.options || []).length;
|
|
805
893
|
$('#cliQuick').innerHTML = (d.quick || []).map(function (g) {
|
|
806
894
|
return '<div class="cmd-group"><h3>' + esc(g.group) + '</h3>' + (g.items || []).map(function (item) {
|