@yottameta/yotta-skills-plugin 0.29.4 → 0.29.6
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 +8 -4
- package/skills/yotta-skills/assets/view.html +191 -11
- package/skills/yotta-skills/bin/yotta-skills.js +277 -86
- package/skills/yotta-skills/lib/cli-help.js +7 -2
- package/skills/yotta-skills/lib/hub-adopt.js +92 -23
- package/skills/yotta-skills/lib/hub-migrate.js +212 -1
- package/skills/yotta-skills/lib/hub-targets.js +85 -0
- package/skills/yotta-skills/lib/hub-view-server.js +238 -7
- package/skills/yotta-skills/lib/hub.js +198 -9
- package/skills/yotta-skills/lib/install-pipeline.js +53 -5
- package/skills/yotta-skills/lib/npm-pack.js +3 -0
- package/skills/yotta-skills/lib/skills-config.js +99 -5
- package/skills/yotta-skills/lib/skills-scan.js +3 -0
- package/skills/yotta-skills/references/hub.md +23 -4
- package/skills/yotta-skills/references/skill-list.md +3 -3
- package/skills/yotta-skills/scan-policy.json +3 -3
- package/skills/yotta-skills/scripts/yotta-skills-mcp.py +1 -1
- package/skills/yotta-skills/skills.json +3 -3
|
@@ -12,7 +12,7 @@
|
|
|
12
12
|
"authentication": "ON_INSTALL"
|
|
13
13
|
},
|
|
14
14
|
"category": "Developer Tools",
|
|
15
|
-
"version": "0.29.
|
|
15
|
+
"version": "0.29.6",
|
|
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.29.
|
|
15
|
+
"version": "0.29.6",
|
|
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.29.
|
|
3
|
+
"version": "0.29.6",
|
|
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.29.
|
|
4
|
+
"version": "0.29.6",
|
|
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.29.
|
|
4
|
-
description: 元阁 -- 元阁全家技能的总编排策划 + 编排路由 + 一键安装器 + 技能盘点 + 本机技能 Hub + 运行时 hook 适配。Hub 层:hub hosts 只读发现本机已装智能体与技能目录(文件系统优先,不读元忆;已核实 / 自动发现 / 桥接分类 + 状态细分:可用 / 残留(实体未确认)/ 未创建 / 仅标记);hub hosts add/remove/list/mark/exclude/include/set 注册自定义宿主目录、手动标记、不接管开关与目录覆盖(只注册不建目录、移除不删目录;残留清理 hub hosts remove --purge 默认预览、目录入回收站 7 天;YottaCode 不纳入接管);install-self / where 把元阁管理引擎独立安装(默认 ~/.yottaskills/yotta-skills,--dir 指定)并查看运行位置;hub config get/set/clear 持久化 Hub 位置(config.json;优先级 --hub > YOTTA_SKILLS_HUB > config > 默认;--move 迁移旧 Hub + 重链 +
|
|
3
|
+
version: 0.29.6
|
|
4
|
+
description: 元阁 -- 元阁全家技能的总编排策划 + 编排路由 + 一键安装器 + 技能盘点 + 本机技能 Hub + 运行时 hook 适配。Hub 层:hub hosts 只读发现本机已装智能体与技能目录(文件系统优先,不读元忆;已核实 / 自动发现 / 桥接分类 + 状态细分:可用 / 残留(实体未确认)/ 未创建 / 仅标记);hub hosts add/remove/list/mark/exclude/include/set 注册自定义宿主目录、手动标记、不接管开关与目录覆盖(只注册不建目录、移除不删目录;残留清理 hub hosts remove --purge 默认预览、目录入回收站 7 天;YottaCode 不纳入接管);install-self / where 把元阁管理引擎独立安装(默认 ~/.yottaskills/yotta-skills,--dir 指定)并查看运行位置;hub config get/set/clear/rollback 持久化 Hub 位置(config.json;优先级 --hub > YOTTA_SKILLS_HUB > config > 默认;--move 迁移旧 Hub + 重链 + 回收站;lastMigration 记录 + 一键回滚 = 位置回退;仅切换且旧 Hub 非空时记录未迁移供面板一键迁移);hub cleanup-backups 清理历史备份 / 暂存残留(回收站 7 天);hub install / update 把技能装到 ~/.yottaskills/hub 单点真源(清单 27 + 特殊家族 5,特殊家族跟随各自 npm latest;update / hub update 只升不降,--force 才显式降级);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/单点安装/链接分发/接管技能/技能枢纽面板/图形化面板/独立安装元阁/查看元阁位置/持久化 Hub 位置/换 Hub 目录/迁移 Hub/迁移回滚/回滚 Hub 位置/清理备份残留/自定义宿主/残留清理/宿主状态 等。边界(Do NOT trigger):只做「组合策划 + 静态路由建议 + M1 记忆裁决只读建议 + 清单 + 下载 + 落位 + 汇总 + 盘点 + re-index + Hub 本机真源与链接分发 + hook 适配」,不含技能本体、不做技能内容开发、不 -g 污染全局、不自动安装缺失技能、不静默写宿主配置或全局记忆、不自动删除技能或记忆;家族安装先自举或调用元信装前门禁,DO NOT INSTALL 阻断,非元阁家族包不自动安装。
|
|
5
5
|
license: MIT
|
|
6
6
|
metadata:
|
|
7
7
|
zh_name: 元阁
|
|
@@ -117,7 +117,7 @@ npx -y @yottameta/yotta-skills hub install
|
|
|
117
117
|
# 3) 分发到全部已核实宿主(Windows junction / macOS·Linux symlink)
|
|
118
118
|
npx -y @yottameta/yotta-skills hub link --all
|
|
119
119
|
|
|
120
|
-
# 4) 图形化面板管理(宿主矩阵 / 链接与体检 / 收编 / 回滚)
|
|
120
|
+
# 4) 图形化面板管理(宿主矩阵 / 链接与体检 / 收编 / 迁移向导 / 回滚)
|
|
121
121
|
npx -y @yottameta/yotta-skills view
|
|
122
122
|
```
|
|
123
123
|
|
|
@@ -127,7 +127,10 @@ npx -y @yottameta/yotta-skills view
|
|
|
127
127
|
# 持久化配置(写入 ~/.yottaskills/config.json;优先级 --hub > YOTTA_SKILLS_HUB > config > 默认)
|
|
128
128
|
npx -y @yottameta/yotta-skills hub config set --hub "D:\my-hub" --move
|
|
129
129
|
npx -y @yottameta/yotta-skills hub config get # 查看生效位置与来源
|
|
130
|
+
npx -y @yottameta/yotta-skills hub config rollback # 回滚预览(位置回退,用当前内容)
|
|
131
|
+
npx -y @yottameta/yotta-skills hub config rollback --yes # 执行回滚(反向迁移同内核)
|
|
130
132
|
npx -y @yottameta/yotta-skills hub config clear # 清除覆盖
|
|
133
|
+
npx -y @yottameta/yotta-skills hub cleanup-backups --yes # 清理历史备份 / 暂存残留(回收站 7 天)
|
|
131
134
|
```
|
|
132
135
|
|
|
133
136
|
**元阁独立安装(把管理引擎装成独立 CLI,不依赖任何智能体)**:
|
|
@@ -311,7 +314,8 @@ Hub。默认范围为已核实宿主 ∪ 本技能链接台账;`--agent` / `--
|
|
|
311
314
|
| `--inventory` | 盘点本机已装技能:扫描技能目录生成/更新注册表(自包含,不依赖元技能);`--json` 输出 JSON、`--project` 附扫项目级目录 |
|
|
312
315
|
| `--reindex` | 重扫注册表:扫描技能目录并增量合并变化(install / update 完成后 CLI 自动重扫;也可在会话开工等时机手动运行);`--json` 输出 JSON、`--project` 附扫项目级目录;`--rescan` 同义 |
|
|
313
316
|
| `hub hosts` | 发现本机已装智能体与技能目录(文件系统 + 环境变量;不读元忆 / 注册表);按已核实 / 自动发现 / 桥接分类;`--json` 输出 JSON |
|
|
314
|
-
| `hub config get \| set --hub <path> [--move] \| clear` | Hub 位置持久化(`config.json`;优先级 `--hub` > `YOTTA_SKILLS_HUB` > config > 默认);`--move` = 复制校验 → 重链 → 旧 Hub 入回收站(7 天),fail-closed;`where` / 面板「Hub
|
|
317
|
+
| `hub config get \| set --hub <path> [--move [--clean-residue]] \| rollback [--yes] \| clear` | Hub 位置持久化(`config.json`;优先级 `--hub` > `YOTTA_SKILLS_HUB` > config > 默认);`--move` = 复制校验 → 重链 → 旧 Hub 入回收站(7 天),fail-closed;`lastMigration` 记录最近迁移,`rollback` = 位置回退(用当前内容,非快照恢复);仅切换指针且旧 Hub 非空时记录「未迁移」供面板一键迁移;`where` / 面板「Hub 位置 + 最近一次迁移」显示来源与回滚入口 |
|
|
318
|
+
| `hub cleanup-backups [--yes] [--include-discovered]` | 清理宿主 / Hub 里的 `.yottaskills-backup-*` / `.yottaskills-staging` 等历史残留(默认预览;`--yes` 移入回收站保留 7 天);`hub doctor` 只读报告 `backup_residue` 警告 |
|
|
315
319
|
| `hub install [skill...]` | 把元技能安装到 Hub 真源(清单 27 + 特殊家族 5;特殊家族跟随各自 npm latest;默认 `~/.yottaskills/hub`;`--hub` / `YOTTA_SKILLS_HUB` 覆盖) |
|
|
316
320
|
| `hub update` | 更新 Hub 内的元技能;非元技能不参与(来源无统一安装源,用户自行处理) |
|
|
317
321
|
| `hub adopt --scan` | 只读预演:扫描各宿主现有技能,输出收编候选、多副本冲突、已在 Hub 状态 |
|
|
@@ -214,6 +214,15 @@
|
|
|
214
214
|
<button class="btn small" id="hubConfigClearBtn" hidden>清除覆盖</button>
|
|
215
215
|
</div>
|
|
216
216
|
</div>
|
|
217
|
+
<div class="section" id="ovSwitchBanner" hidden></div>
|
|
218
|
+
<div class="section panel" id="ovMigration" hidden>
|
|
219
|
+
<h3>最近一次迁移</h3>
|
|
220
|
+
<div id="ovMigrationBody"></div>
|
|
221
|
+
<div class="toolbar section">
|
|
222
|
+
<button class="btn small danger" id="migrationRollbackBtn">回滚到原位置</button>
|
|
223
|
+
<button class="btn small" id="copyMigrationTrash">复制回收站路径</button>
|
|
224
|
+
</div>
|
|
225
|
+
</div>
|
|
217
226
|
</section>
|
|
218
227
|
|
|
219
228
|
<section class="view" id="view-hosts">
|
|
@@ -493,27 +502,198 @@
|
|
|
493
502
|
if (hubInfo.restartRequired) metaParts.push('配置已指向 ' + (hubInfo.effectiveNow || '') + ',重启 view 后生效');
|
|
494
503
|
$('#ovHubMeta').textContent = metaParts.join(' · ');
|
|
495
504
|
$('#hubConfigClearBtn').hidden = !hubInfo.configured;
|
|
505
|
+
renderMigrationBlock(d);
|
|
506
|
+
}
|
|
507
|
+
|
|
508
|
+
/* 0.29.5 S1/S2:迁移向导(迁移 / 仅切换)+ 最近迁移 + 一键回滚(与 CLI 同内核) */
|
|
509
|
+
function renderMigrationBlock(d) {
|
|
510
|
+
var mig = (d.migration && d.migration.last) || null;
|
|
511
|
+
var pending = (d.migration && d.migration.switchPending) || null;
|
|
512
|
+
var banner = $('#ovSwitchBanner');
|
|
513
|
+
if (pending) {
|
|
514
|
+
banner.hidden = false;
|
|
515
|
+
banner.innerHTML = '<div class="banner danger"><svg class="sv" viewBox="0 0 24 24"><path d="M12 9v4"/><path d="M12 17h.01"/><path d="M10.3 3.9 1.8 18a2 2 0 0 0 1.7 3h17a2 2 0 0 0 1.7-3L13.7 3.9a2 2 0 0 0-3.4 0z"/></svg>' +
|
|
516
|
+
'<div><div class="banner-title">旧 Hub 还有 ' + esc(pending.skills) + ' 个技能未迁移</div>' +
|
|
517
|
+
'<div class="banner-body">旧位置 <span class="mono">' + esc(pending.from) + '</span> · 当前位置 <span class="mono">' + esc(d.hubDir) + '</span></div>' +
|
|
518
|
+
'<div class="toolbar section"><button class="btn small primary" id="switchMigrateNow">一键迁移到当前位置</button>' +
|
|
519
|
+
'<button class="btn small" id="switchMigrateOpen">迁移到其它位置…</button></div></div></div>';
|
|
520
|
+
$('#switchMigrateNow').onclick = function () {
|
|
521
|
+
confirmModal('迁移旧 Hub 到当前位置?',
|
|
522
|
+
'<p class="label">将把 <span class="mono">' + esc(pending.from) + '</span>(' + esc(pending.skills) + ' 个技能)复制到 <span class="mono">' + esc(d.hubDir) + '</span>,逐技能校验后切换配置、重链全部宿主,旧 Hub 入回收站(7 天可回滚)。</p>',
|
|
523
|
+
'开始迁移', false, async function () {
|
|
524
|
+
await executeHubMigration({ hub: d.hubDir, mode: 'move' }, { from: pending.from });
|
|
525
|
+
});
|
|
526
|
+
};
|
|
527
|
+
$('#switchMigrateOpen').onclick = function () { openHubConfigWizard(); };
|
|
528
|
+
} else {
|
|
529
|
+
banner.hidden = true;
|
|
530
|
+
banner.innerHTML = '';
|
|
531
|
+
}
|
|
532
|
+
var box = $('#ovMigration');
|
|
533
|
+
if (!mig) {
|
|
534
|
+
box.hidden = true;
|
|
535
|
+
return;
|
|
536
|
+
}
|
|
537
|
+
box.hidden = false;
|
|
538
|
+
var kind = mig.kind === 'rollback' ? '回滚' : '迁移';
|
|
539
|
+
var lines = [
|
|
540
|
+
'最近一次' + kind + ':' + esc(mig.from) + ' → ' + esc(mig.to),
|
|
541
|
+
'时间 ' + esc(fmtTime(mig.at)) + ' · 技能 ' + esc(mig.verifiedSkills) + ' · 重链 ' + esc(mig.relinkDirs) + ' 个宿主目录',
|
|
542
|
+
];
|
|
543
|
+
if (mig.trashedTo) lines.push('回收站 ' + esc(mig.trashedTo) + '(剩余 ' + esc(mig.daysLeft) + ' 天)');
|
|
544
|
+
if (mig.incomplete > 0) lines.push('⚠ 重链未全部完成(' + esc(mig.incomplete) + ' 项);旧 Hub 保留在 ' + esc(mig.oldHubKept || mig.from));
|
|
545
|
+
if (mig.trashError) lines.push('⚠ 旧 Hub 移入回收站失败(已保留原位):' + esc(mig.trashError));
|
|
546
|
+
$('#ovMigrationBody').innerHTML = '<div class="faint">' + lines.join('<br>') + '</div>';
|
|
547
|
+
$('#copyMigrationTrash').hidden = !mig.trashedTo;
|
|
548
|
+
$('#copyMigrationTrash').onclick = function () { copyText(mig.trashedTo); };
|
|
549
|
+
$('#migrationRollbackBtn').onclick = rollbackFlow;
|
|
550
|
+
}
|
|
551
|
+
|
|
552
|
+
function rewireModalButtons(opts) {
|
|
553
|
+
var ok = $('#modal').querySelector('[data-modal="ok"]');
|
|
554
|
+
var cancel = $('#modal').querySelector('[data-modal="cancel"]');
|
|
555
|
+
if (ok && opts.okText) { ok.textContent = opts.okText; ok.disabled = false; ok.onclick = opts.onOk || closeModal; }
|
|
556
|
+
if (ok && opts.hideOk) ok.hidden = true;
|
|
557
|
+
if (cancel && opts.cancelText) { cancel.textContent = opts.cancelText; cancel.onclick = opts.onCancel || closeModal; }
|
|
558
|
+
}
|
|
559
|
+
|
|
560
|
+
function showMigrationResult(result) {
|
|
561
|
+
var move = result.move || {};
|
|
562
|
+
var done = !move.incomplete && !move.trashError;
|
|
563
|
+
$('#modalBody').innerHTML =
|
|
564
|
+
'<div class="result"><span class="badge ' + (done ? 'ok' : 'danger') + '">' + (done ? '迁移完成' : '部分完成') + '</span><span>' + esc(result.previous || '') + ' → ' + esc(result.hub || '') + '</span></div>' +
|
|
565
|
+
'<div class="result"><span class="badge ok">校验</span><span>' + esc(move.verifiedSkills || 0) + ' 个技能复制校验通过</span></div>' +
|
|
566
|
+
'<div class="result"><span class="badge ok">重链</span><span>' + esc(move.relinkDirs || 0) + ' 个宿主目录</span></div>' +
|
|
567
|
+
(move.trashedTo ? '<div class="result"><span class="badge ok">旧 Hub</span><span class="mono">' + esc(move.trashedTo) + '</span><span>已入回收站(7 天可恢复)</span></div>' : '') +
|
|
568
|
+
(move.incomplete > 0 ? '<div class="result"><span class="badge danger">重链未全部完成</span><span>旧 Hub 保留在 <span class="mono">' + esc(move.oldHubKept || move.from || '') + '</span>;重跑:yotta-skills hub link --all --force</span></div>' : '') +
|
|
569
|
+
(move.trashError ? '<div class="result"><span class="badge warn">回收站</span><span>旧 Hub 移入回收站失败(已保留原位):' + esc(move.trashError) + '</span></div>' : '') +
|
|
570
|
+
'<div class="note">新位置从下次面板加载开始生效(已在运行的 view 需重启)。</div>';
|
|
571
|
+
rewireModalButtons({
|
|
572
|
+
okText: '关闭',
|
|
573
|
+
onOk: closeModal,
|
|
574
|
+
cancelText: '刷新面板',
|
|
575
|
+
onCancel: function () { closeModal(); loadOverview(); },
|
|
576
|
+
});
|
|
577
|
+
}
|
|
578
|
+
|
|
579
|
+
function showSwitchResult(result) {
|
|
580
|
+
$('#modalBody').innerHTML =
|
|
581
|
+
'<div class="result"><span class="badge ok">已切换</span><span>' + esc(result.previous || '') + ' → ' + esc(result.hub || '') + '</span></div>' +
|
|
582
|
+
((result.oldSkills || 0) > 0
|
|
583
|
+
? '<div class="result"><span class="badge warn">未迁移</span><span>旧 Hub 还有 ' + esc(result.oldSkills) + ' 个技能未迁移;概览会持续提示,可一键迁移。</span></div>'
|
|
584
|
+
: '') +
|
|
585
|
+
'<div class="note">只切换了指针,没有移动任何文件;新位置从下次面板加载开始生效。</div>';
|
|
586
|
+
rewireModalButtons({
|
|
587
|
+
okText: '关闭',
|
|
588
|
+
onOk: closeModal,
|
|
589
|
+
cancelText: '刷新面板',
|
|
590
|
+
onCancel: function () { closeModal(); loadOverview(); },
|
|
591
|
+
});
|
|
592
|
+
}
|
|
593
|
+
|
|
594
|
+
async function executeHubMigration(body, opts) {
|
|
595
|
+
opts = opts || {};
|
|
596
|
+
var seq = showLoad(body.mode === 'switch' ? '正在切换 Hub 位置…' : '正在迁移 Hub(复制 → 校验 → 重链 → 回收站)…');
|
|
597
|
+
var payload = Object.assign({ confirm: 'hub-config' }, body);
|
|
598
|
+
if (opts.from) payload = Object.assign(payload, { from: opts.from });
|
|
599
|
+
var r = await api('/api/hub/config', { body: payload });
|
|
600
|
+
hideLoad(seq);
|
|
601
|
+
if (r.error) {
|
|
602
|
+
if (r.code === 'target-residue') return wizardResiduePrompt(body, r.error, opts);
|
|
603
|
+
if (opts.inModal) {
|
|
604
|
+
$('#hubWizardResult').innerHTML = '<div class="result"><span class="badge danger">失败</span><span>' + esc(r.error) + '</span></div>';
|
|
605
|
+
} else {
|
|
606
|
+
toast(r.error);
|
|
607
|
+
}
|
|
608
|
+
return null;
|
|
609
|
+
}
|
|
610
|
+
if (opts.inModal) {
|
|
611
|
+
if (body.mode === 'switch') showSwitchResult(r);
|
|
612
|
+
else showMigrationResult(r);
|
|
613
|
+
}
|
|
614
|
+
else {
|
|
615
|
+
closeModal();
|
|
616
|
+
toast('迁移完成:校验 ' + ((r.move && r.move.verifiedSkills) || 0) + ' 个技能 · 重链 ' + ((r.move && r.move.relinkDirs) || 0) + ' 个目录' + (r.move && r.move.incomplete ? '(重链未全部完成,见概览提示)' : ''));
|
|
617
|
+
loadOverview();
|
|
618
|
+
}
|
|
619
|
+
return r;
|
|
496
620
|
}
|
|
497
621
|
|
|
498
|
-
|
|
499
|
-
var
|
|
500
|
-
|
|
622
|
+
function wizardResiduePrompt(body, error, opts) {
|
|
623
|
+
var host = opts && opts.inModal ? $('#hubWizardResult') : $('#modalBody');
|
|
624
|
+
if (!host) { toast(error); return; }
|
|
625
|
+
host.innerHTML = '<div class="result"><span class="badge warn">残留</span><span>' + esc(error) + '</span></div>' +
|
|
626
|
+
'<button class="btn small primary" id="hubResidueClean">清理残留并迁移</button>';
|
|
627
|
+
$('#hubResidueClean').onclick = async function () {
|
|
628
|
+
var seq = showLoad('正在清理残留并迁移…');
|
|
629
|
+
var payload = Object.assign({ confirm: 'hub-config', cleanResidue: true }, body);
|
|
630
|
+
if (opts && opts.from) payload.from = opts.from;
|
|
631
|
+
var r = await api('/api/hub/config', { body: payload });
|
|
632
|
+
hideLoad(seq);
|
|
633
|
+
if (r.error) { toast(r.error); return; }
|
|
634
|
+
if (opts && opts.inModal) showMigrationResult(r);
|
|
635
|
+
else { closeModal(); toast('迁移完成'); loadOverview(); }
|
|
636
|
+
};
|
|
637
|
+
}
|
|
638
|
+
|
|
639
|
+
function openHubConfigWizard() {
|
|
640
|
+
var d = state.overview || {};
|
|
641
|
+
var current = d.hubDir || '';
|
|
642
|
+
var skillCount = (d.status && d.status.summary && d.status.summary.skills) || 0;
|
|
643
|
+
var body = '<p class="label">写入 config.json 持久覆盖(优先级低于 --hub 参数 / YOTTA_SKILLS_HUB)。</p>' +
|
|
501
644
|
'<label class="label">新的 Hub 目录</label>' +
|
|
502
645
|
'<input id="hubNewPath" placeholder="D:\\my-hub" value="">' +
|
|
503
|
-
'<div class="note mono">当前: ' + esc(current) + '
|
|
646
|
+
'<div class="note mono">当前: ' + esc(current) + '(' + esc(skillCount) + ' 个技能)</div>' +
|
|
647
|
+
'<div class="section"><label class="label"><input type="radio" name="hubMode" value="move" checked style="width:auto"> 迁移到新位置(推荐)</label>' +
|
|
648
|
+
'<div class="faint">复制 → 逐技能校验 → 切换配置 → 全宿主重链 → 旧 Hub 入回收站(7 天可回滚)。</div>' +
|
|
649
|
+
'<label class="label section"><input type="radio" name="hubMode" value="switch" style="width:auto"> 仅切换位置(保留旧内容)</label>' +
|
|
650
|
+
'<div class="faint">只改指针,不移动任何文件;旧 Hub 内容保留,概览会提示未迁移。</div></div>' +
|
|
651
|
+
'<div id="hubWizardResult" class="section"></div>';
|
|
504
652
|
openModal('设置 Hub 位置', body, {
|
|
505
|
-
okText: '
|
|
653
|
+
okText: '执行',
|
|
506
654
|
onOk: async function () {
|
|
507
655
|
var value = ($('#hubNewPath').value || '').trim();
|
|
508
656
|
if (!value) { toast('先填写新的 Hub 目录'); return; }
|
|
509
|
-
var
|
|
510
|
-
if (
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
657
|
+
var mode = document.querySelector('input[name="hubMode"]:checked').value;
|
|
658
|
+
if (mode === 'switch' && skillCount > 0) {
|
|
659
|
+
$('#hubWizardResult').innerHTML = '<div class="result"><span class="badge warn">确认</span><span>旧 Hub 还有 ' + esc(skillCount) + ' 个技能未迁移。仅切换位置不会移动它们。</span></div>' +
|
|
660
|
+
'<button class="btn small danger" id="hubSwitchConfirm">确认仅切换</button>';
|
|
661
|
+
$('#hubSwitchConfirm').onclick = function () { executeHubMigration({ hub: value, mode: 'switch' }, { inModal: true }); };
|
|
662
|
+
return;
|
|
663
|
+
}
|
|
664
|
+
await executeHubMigration({ hub: value, mode: mode }, { inModal: true });
|
|
514
665
|
},
|
|
515
666
|
});
|
|
516
|
-
}
|
|
667
|
+
}
|
|
668
|
+
|
|
669
|
+
function rollbackFlow() {
|
|
670
|
+
api('/api/hub/config').then(function (d) {
|
|
671
|
+
if (d.error) { toast(d.error); return; }
|
|
672
|
+
var rb = d.rollback || {};
|
|
673
|
+
if (!rb.lastMigration) { toast('没有可回滚的迁移记录'); return; }
|
|
674
|
+
if (!rb.ok) {
|
|
675
|
+
confirmModal('回滚被阻断', '<p class="label">' + esc((rb.blocked || []).join(';') || rb.error || '') + '</p><div class="note">回滚 fail-closed(不合并);处理阻断项后重试。</div>', null, true, null);
|
|
676
|
+
return;
|
|
677
|
+
}
|
|
678
|
+
confirmModal('回滚 Hub 位置?',
|
|
679
|
+
'<p class="label">将把当前 Hub 内容复制回原位置:<span class="mono">' + esc(rb.current) + '</span> → <span class="mono">' + esc(rb.target) + '</span>(复制校验 → 切换 → 重链 → 当前 Hub 入回收站 7 天)。</p>' +
|
|
680
|
+
'<div class="note">位置回退(用当前内容),不是恢复旧快照。</div>',
|
|
681
|
+
'执行回滚', true, async function () {
|
|
682
|
+
var seq = showLoad('正在回滚 Hub 位置(复制 → 校验 → 重链 → 回收站)…');
|
|
683
|
+
var r = await api('/api/hub/config', { body: { rollback: true, confirm: 'hub-config' } });
|
|
684
|
+
hideLoad(seq);
|
|
685
|
+
if (r.error) {
|
|
686
|
+
if (r.code === 'target-residue') { wizardResiduePrompt({ rollback: true }, r.error, {}); return; }
|
|
687
|
+
toast(r.error); return;
|
|
688
|
+
}
|
|
689
|
+
closeModal();
|
|
690
|
+
toast('回滚完成:' + ((r.move && r.move.verifiedSkills) || 0) + ' 个技能已回到 ' + (r.hub || ''));
|
|
691
|
+
loadOverview();
|
|
692
|
+
});
|
|
693
|
+
});
|
|
694
|
+
}
|
|
695
|
+
|
|
696
|
+
$('#hubConfigBtn').onclick = openHubConfigWizard;
|
|
517
697
|
$('#hubConfigClearBtn').onclick = function () {
|
|
518
698
|
confirmModal('清除 Hub 位置覆盖?', '<p class="label">将删除 config.json 中的 Hub 覆盖,恢复 --hub > 环境变量 > 默认位置的解析。不会移动任何文件。</p>', '清除覆盖', true, async function () {
|
|
519
699
|
var r = await api('/api/hub/config', { body: { clear: true, confirm: 'hub-config' } });
|