@yottameta/yotta-skills 0.19.12 → 0.19.14
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/CHANGELOG.md +31 -0
- package/SKILL.md +9 -5
- package/bin/yotta-skills.js +27 -3
- package/lib/trusted-verifier.js +156 -0
- package/lib/verify-gate.js +22 -10
- package/package.json +1 -1
- package/references/skill-list.md +3 -3
- package/scripts/yotta-skills-mcp.py +1 -1
- package/skill-manifest.json +1 -1
- package/skills.json +4 -4
package/CHANGELOG.md
CHANGED
|
@@ -1,3 +1,34 @@
|
|
|
1
|
+
## v0.19.14 (2026-09-21)
|
|
2
|
+
|
|
3
|
+
**清单同步:元忆 0.16.2(首启修复)**
|
|
4
|
+
|
|
5
|
+
- `skills.json` / `references/skill-list.md` 的 yotta-memory 版本 0.16.1 → 0.16.2,`updated` → 2026-09-21。
|
|
6
|
+
- 同步内容:元忆 0.16.2 首启修复——空加密库 `view` 恢复钥匙解锁、非 TTY `--password-stdin`、`--recovery-key-out`、缺 `agent-key` 降级未授权、空明文 `migrate`、doctor 分档与 `view` 端口健康检查。
|
|
7
|
+
- 安装器 / 路由 / hook 契约零变更;本版仅为清单与版本同步。
|
|
8
|
+
|
|
9
|
+
## v0.19.13 (2026-09-19)
|
|
10
|
+
|
|
11
|
+
**安全修复:校验器(元信)发现改为「受信安装记录 + 身份 + 摘要」三重绑定。**
|
|
12
|
+
|
|
13
|
+
- 背景(平台扫描发现,T07 工具劫持):旧实现通过本地技能注册表里 `slug=yotta-verify` 的
|
|
14
|
+
`source_dirs` 发现扫描引擎,而注册表身份来自被扫描技能自己 `SKILL.md` frontmatter 的 `name`。
|
|
15
|
+
任意目录只要自称 `yotta-verify` 并提供 `scripts/yotta_verify.py`,就会被当作引擎执行——
|
|
16
|
+
既可直接执行任意代码,也能伪造 `SAFE TO INSTALL` 放行后续安装。
|
|
17
|
+
- 修复:
|
|
18
|
+
- 新增 `lib/trusted-verifier.js`:受信记录 `~/.yottaskills/trusted-verifier.json`
|
|
19
|
+
(与注册表同目录,支持 `YOTTA_SKILLS_REGISTRY_FILE` 多 agent 隔离),只在安装管线
|
|
20
|
+
校验通过、元信安装 / 更新成功后写入。
|
|
21
|
+
- `findVerifier` 只认两类来源:用户显式指定(`--verify` / `YOTTA_SKILLS_VERIFY`)与
|
|
22
|
+
受信记录;候选须通过「路径 realpath 无符号链接跳转 + 包身份(slug / package / trust /
|
|
23
|
+
SKILL.md 与 manifest 版本一致)+ 引擎 SHA-256 与记录一致」。
|
|
24
|
+
- 任一环节不满足即 fail-closed:不再回退到注册表 / 目标目录里的同名引擎,改走自举安装
|
|
25
|
+
(从 npm 重装一份干净的元信,装完立即写记录)。
|
|
26
|
+
- 自举安装后新增身份 / 摘要复核,未通过不返回引擎。
|
|
27
|
+
- 测试:新增 `test/trusted-verifier.test.js` 8 项(含 T07 回归、摘要不符、身份不符、版本不符、
|
|
28
|
+
裸引擎不被采用、记录读写往返);测试夹具 `test/helpers/fake-npm.js` 按真实元信包形态
|
|
29
|
+
生成 manifest;`node --test "test/*.test.js"` 180/180。
|
|
30
|
+
- 清单同步:`skills.json` 元信 0.3.1 → 0.3.2(`references/skill-list.md` 同步)。
|
|
31
|
+
|
|
1
32
|
## v0.19.12 (2026-09-19)
|
|
2
33
|
|
|
3
34
|
同步家族维护批次清单版本:元安全 0.2.6 / 元盾 0.1.4 / 元察 0.2.9 / 元析 0.1.7 / 元安 0.2.4 / 元审 0.2.5。
|
package/SKILL.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: yotta-skills
|
|
3
|
-
version: 0.19.
|
|
3
|
+
version: 0.19.14
|
|
4
4
|
description: 元阁 -- 元阁全家技能的总编排策划 + 编排路由 + 一键安装器 + 技能盘点 + 运行时 hook 适配。路由层:--route / route_request 按需求摘要给出候选组合、调用顺序、角色、置信度、依据、已装/缺失状态与安装命令,只建议不自动安装;非元阁家族已装技能按 frontmatter description 机械匹配作并列候选(标注来源与未扫描状态,只读不自动调用);策划层:按场景给出「该组合哪几个元技能、组合强在哪、怎么组合使用(安装与调用均由用户确认后执行)」;安装层:一条命令把 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,不常驻,未加载降级 CLI)。支持 --list 清单 / --route 路由 / install / update / update --check(只读检查)/ update --check --scheduled(后台周检)/ update --auto(家族自动更新)/ hook 适配 / --inventory / --reindex / --dry-run 预览 / --pin(默认)/ --range。触发:需要批量安装或更新元阁全家技能、按场景组合多个元技能、路由或判断该用哪些技能、盘点或查看本机已装技能、重扫技能注册表、给某个智能体或目录一次性铺齐 yotta-* 技能、评估宿主 hook 能力、预览安装清单、锁版本安装、或用户说 元阁/装全家/一次装齐/yotta-skills/install-all/更新全家/检查更新/自动更新/hook 适配/该用哪个技能/路由技能/盘点技能/查看已装技能 等。边界(Do NOT trigger):只做「组合策划 + 静态路由建议 + 清单 + 下载 + 落位 + 汇总 + 盘点 + re-index + hook 适配」,不含技能本体、不做技能内容开发、不 -g 污染全局、不自动安装缺失技能、不静默写宿主配置或全局记忆;家族安装先自举或调用元信装前门禁,DO NOT INSTALL 阻断,非元阁家族包不自动安装。
|
|
5
5
|
license: MIT
|
|
6
6
|
metadata:
|
|
@@ -341,10 +341,14 @@ npx -y @yottameta/yotta-skills --route "检查代码质量,别糊弄" --json
|
|
|
341
341
|
- `INSTALL WITH CAUTION` / `REVIEW REQUIRED`:继续安装,但显示风险并写入证据;
|
|
342
342
|
- `DO NOT INSTALL` 或扫描失败:阻断,不替换旧版本。
|
|
343
343
|
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
344
|
+
引擎查找顺序(v0.19.13 收紧):`--verify` 指定路径 → 环境变量 `YOTTA_SKILLS_VERIFY` →
|
|
345
|
+
**受信安装记录**(`~/.yottaskills/trusted-verifier.json`,由安装管线在元信安装 / 更新成功后写入,
|
|
346
|
+
记录包身份 + 引擎路径 + SHA-256)。候选引擎必须同时满足:路径不经符号链接跳转、
|
|
347
|
+
包内 `SKILL.md` 与 `skill-manifest.json` 身份一致(slug / package / trust / 版本)、
|
|
348
|
+
引擎摘要与记录一致;任一不满足即 fail-closed,改走自举安装。
|
|
349
|
+
不再按本地注册表里 `name: yotta-verify` 的目录发现引擎。
|
|
350
|
+
`--skip-scan` 只保留为人工应急路径,使用时输出 `explicit-unverified` 并写入
|
|
351
|
+
`~/.yottaskills/install-log.jsonl`;`update --auto` 不会使用该开关。
|
|
348
352
|
|
|
349
353
|
## 支持智能体
|
|
350
354
|
|
package/bin/yotta-skills.js
CHANGED
|
@@ -29,6 +29,7 @@ const http = require('http');
|
|
|
29
29
|
const https = require('https');
|
|
30
30
|
const evidenceLib = require('../lib/install-evidence');
|
|
31
31
|
const gateLib = require('../lib/verify-gate');
|
|
32
|
+
const trustedVerifierLib = require('../lib/trusted-verifier');
|
|
32
33
|
const healthLib = require('../lib/install-health');
|
|
33
34
|
const lifecycleLib = require('../lib/install-lifecycle');
|
|
34
35
|
const snapshotLib = require('../lib/install-snapshot');
|
|
@@ -37,7 +38,7 @@ const hookAdapterLib = require('../lib/hook-adapter');
|
|
|
37
38
|
const { createInstaller, isSafeTarEntry } = require('../lib/install-pipeline');
|
|
38
39
|
|
|
39
40
|
const PKG_ROOT = path.join(__dirname, '..');
|
|
40
|
-
let VERSION = '0.19.
|
|
41
|
+
let VERSION = '0.19.14';
|
|
41
42
|
try { VERSION = require(path.join(PKG_ROOT, 'package.json')).version; } catch (_) { /* keep fallback */ }
|
|
42
43
|
|
|
43
44
|
function loadManifest() {
|
|
@@ -542,8 +543,26 @@ function detectProjectDir() {
|
|
|
542
543
|
|
|
543
544
|
// ── 元信 scan 与安装门禁 ──────────────────────────────────────────────────
|
|
544
545
|
function findVerifyEngine(dest, opts) {
|
|
545
|
-
|
|
546
|
-
|
|
546
|
+
// v0.19.13:不再用注册表(身份来自被扫描技能自己的 frontmatter)发现校验器,
|
|
547
|
+
// 只认安装管线写入的受信记录(路径 + 摘要双绑定)。
|
|
548
|
+
return gateLib.findVerifier({ dest, opts, trustedVerifier: trustedVerifierLib.loadRecord() });
|
|
549
|
+
}
|
|
550
|
+
|
|
551
|
+
/** 安装 / 更新元信成功后写入受信记录(身份 + 摘要),后续门禁只认这份记录。 */
|
|
552
|
+
function recordTrustedVerifier(dest) {
|
|
553
|
+
const engine = path.join(dest, 'yotta-verify', 'scripts', 'yotta_verify.py');
|
|
554
|
+
const check = trustedVerifierLib.verifyEngine(engine, { requireRecord: false });
|
|
555
|
+
if (!check.ok) return null;
|
|
556
|
+
const record = {
|
|
557
|
+
slug: check.info.slug,
|
|
558
|
+
package: check.info.package,
|
|
559
|
+
version: check.info.version,
|
|
560
|
+
path: check.path,
|
|
561
|
+
sha256: check.info.sha256,
|
|
562
|
+
recordedAt: new Date().toISOString(),
|
|
563
|
+
};
|
|
564
|
+
trustedVerifierLib.saveRecord(record);
|
|
565
|
+
return record;
|
|
547
566
|
}
|
|
548
567
|
|
|
549
568
|
function findPython(opts) {
|
|
@@ -678,6 +697,9 @@ function ensureGate(context) {
|
|
|
678
697
|
if (result.status !== 'ok') return { ok: false, error: '元信自举失败: ' + result.note };
|
|
679
698
|
const installedEngine = path.join(dest, 'yotta-verify', 'scripts', 'yotta_verify.py');
|
|
680
699
|
if (!fs.existsSync(installedEngine)) return { ok: false, error: '元信自举后未找到安装引擎' };
|
|
700
|
+
if (!recordTrustedVerifier(dest)) {
|
|
701
|
+
return { ok: false, error: '元信自举后身份 / 摘要校验未通过(fail-closed,不执行该引擎)' };
|
|
702
|
+
}
|
|
681
703
|
return { ok: true, engine: installedEngine, mode: 'trusted-bootstrap' };
|
|
682
704
|
} finally {
|
|
683
705
|
if (tmp) fs.rmSync(tmp, { recursive: true, force: true });
|
|
@@ -708,6 +730,7 @@ function runInstall(opts, dest) {
|
|
|
708
730
|
if (r.gate && r.gate.mode === 'explicit-unverified') {
|
|
709
731
|
out(' ⚠ ' + s.slug.padEnd(22) + '未执行装前扫描(explicit-unverified)');
|
|
710
732
|
}
|
|
733
|
+
if (s.slug === 'yotta-verify') recordTrustedVerifier(dest);
|
|
711
734
|
out(' ✔ ' + s.slug.padEnd(22) + s.name + ' -> ' + (r.version || '?'));
|
|
712
735
|
}
|
|
713
736
|
else if (r.status === 'skip') out(' - ' + s.slug.padEnd(22) + s.name + ' (' + r.note + ',v' + r.version + ')');
|
|
@@ -744,6 +767,7 @@ function runUpdate(opts, dest) {
|
|
|
744
767
|
if (r.gate && r.gate.mode === 'explicit-unverified') {
|
|
745
768
|
out(' ⚠ ' + s.slug.padEnd(22) + '未执行装前扫描(explicit-unverified)');
|
|
746
769
|
}
|
|
770
|
+
if (s.slug === 'yotta-verify') recordTrustedVerifier(dest);
|
|
747
771
|
out(' ✔ ' + s.slug.padEnd(22) + s.name + ' -> ' + (r.version || '?') + (existing ? '(原 v' + existing + ')' : '(新装)'));
|
|
748
772
|
}
|
|
749
773
|
else if (r.status === 'skip') out(' - ' + s.slug.padEnd(22) + s.name + ' (' + r.note + ')');
|
|
@@ -0,0 +1,156 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
/**
|
|
3
|
+
* yotta-skills 受信校验器(元信)记录与身份校验(v0.19.13)。
|
|
4
|
+
*
|
|
5
|
+
* 背景:旧实现用「本地技能注册表里 slug=yotta-verify 的 source_dirs」发现校验器,
|
|
6
|
+
* 而注册表身份来自被扫描技能自己 SKILL.md frontmatter 的 `name` —— 任意目录只要自称
|
|
7
|
+
* `yotta-verify` 并提供 `scripts/yotta_verify.py`,就会被当作扫描引擎执行
|
|
8
|
+
* (可执行任意代码,并可伪造 `SAFE TO INSTALL` 结论)。
|
|
9
|
+
*
|
|
10
|
+
* 新模型:
|
|
11
|
+
* 1. 只有「安装管线校验通过后写入的安装记录」才算受信来源(路径 + SHA-256 双绑定);
|
|
12
|
+
* 2. 记录缺失 / 摘要不符 / 身份不符 / 路径经符号链接跳转 → 一律 fail-closed,
|
|
13
|
+
* 交由上层走自举安装(从 npm 重新装一份干净的元信);
|
|
14
|
+
* 3. 用户显式指定(`--verify` / `YOTTA_SKILLS_VERIFY`)保留为人工可信路径,不参与本校验。
|
|
15
|
+
*
|
|
16
|
+
* 零依赖:只用 Node.js 标准库。
|
|
17
|
+
*/
|
|
18
|
+
const crypto = require('crypto');
|
|
19
|
+
const fs = require('fs');
|
|
20
|
+
const os = require('os');
|
|
21
|
+
const path = require('path');
|
|
22
|
+
|
|
23
|
+
const RECORD_FILE_NAME = 'trusted-verifier.json';
|
|
24
|
+
const REGISTRY_DIR_NAME = '.yottaskills';
|
|
25
|
+
const EXPECTED = {
|
|
26
|
+
slug: 'yotta-verify',
|
|
27
|
+
pkg: '@yottameta/yotta-verify',
|
|
28
|
+
trust: 'yottameta',
|
|
29
|
+
};
|
|
30
|
+
|
|
31
|
+
/** 安装记录路径:与注册表同目录(支持 YOTTA_SKILLS_REGISTRY_FILE 多 agent 隔离)。 */
|
|
32
|
+
function recordPath(registryFile) {
|
|
33
|
+
if (registryFile) return path.join(path.dirname(path.resolve(registryFile)), RECORD_FILE_NAME);
|
|
34
|
+
const override = String(process.env.YOTTA_SKILLS_REGISTRY_FILE || '').trim();
|
|
35
|
+
if (override) return path.join(path.dirname(path.resolve(override)), RECORD_FILE_NAME);
|
|
36
|
+
return path.join(os.homedir(), REGISTRY_DIR_NAME, RECORD_FILE_NAME);
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
function sha256File(file) {
|
|
40
|
+
return crypto.createHash('sha256').update(fs.readFileSync(file)).digest('hex');
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
function loadRecord(registryFile) {
|
|
44
|
+
try {
|
|
45
|
+
const value = JSON.parse(fs.readFileSync(recordPath(registryFile), 'utf8'));
|
|
46
|
+
return (value && typeof value === 'object') ? value : null;
|
|
47
|
+
} catch (_) {
|
|
48
|
+
return null;
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
function saveRecord(record, registryFile) {
|
|
53
|
+
const file = recordPath(registryFile);
|
|
54
|
+
fs.mkdirSync(path.dirname(file), { recursive: true });
|
|
55
|
+
fs.writeFileSync(file, JSON.stringify(record, null, 2) + '\n', 'utf8');
|
|
56
|
+
return file;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
function frontmatterValue(skillFile, key) {
|
|
60
|
+
let text;
|
|
61
|
+
try {
|
|
62
|
+
text = fs.readFileSync(skillFile, 'utf8');
|
|
63
|
+
} catch (_) {
|
|
64
|
+
return null;
|
|
65
|
+
}
|
|
66
|
+
const m = text.match(/^---\r?\n([\s\S]*?)\r?\n---/);
|
|
67
|
+
if (!m) return null;
|
|
68
|
+
const re = new RegExp('^' + key + ':\\s*(.+?)\\s*$', 'm');
|
|
69
|
+
const hit = m[1].match(re);
|
|
70
|
+
return hit ? hit[1].replace(/^["']|["']$/g, '') : null;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
/**
|
|
74
|
+
* 检查候选引擎是否「长得像」一份元信包(不做信任判定):
|
|
75
|
+
* 路径不能经符号链接跳转;包内必须有 SKILL.md + skill-manifest.json,
|
|
76
|
+
* 且 slug / package / trust / 版本与 SKILL.md 一致。
|
|
77
|
+
*/
|
|
78
|
+
function inspectPackage(enginePath) {
|
|
79
|
+
if (!enginePath) return { ok: false, reason: '未指定引擎路径' };
|
|
80
|
+
const resolved = path.resolve(String(enginePath));
|
|
81
|
+
let real;
|
|
82
|
+
try {
|
|
83
|
+
if (!fs.statSync(resolved).isFile()) return { ok: false, reason: '引擎路径不是文件' };
|
|
84
|
+
real = fs.realpathSync(resolved);
|
|
85
|
+
} catch (_) {
|
|
86
|
+
return { ok: false, reason: '引擎文件不存在' };
|
|
87
|
+
}
|
|
88
|
+
if (real !== resolved) return { ok: false, reason: '引擎路径经符号链接跳转,拒绝' };
|
|
89
|
+
|
|
90
|
+
const skillDir = path.dirname(path.dirname(resolved));
|
|
91
|
+
const skillFile = path.join(skillDir, 'SKILL.md');
|
|
92
|
+
const manifestFile = path.join(skillDir, 'skill-manifest.json');
|
|
93
|
+
if (!fs.existsSync(skillFile)) return { ok: false, reason: '缺少 SKILL.md' };
|
|
94
|
+
if (!fs.existsSync(manifestFile)) return { ok: false, reason: '缺少 skill-manifest.json' };
|
|
95
|
+
|
|
96
|
+
const skillName = frontmatterValue(skillFile, 'name');
|
|
97
|
+
if (skillName !== EXPECTED.slug) {
|
|
98
|
+
return { ok: false, reason: 'SKILL.md name 不是 ' + EXPECTED.slug };
|
|
99
|
+
}
|
|
100
|
+
let manifest;
|
|
101
|
+
try {
|
|
102
|
+
manifest = JSON.parse(fs.readFileSync(manifestFile, 'utf8'));
|
|
103
|
+
} catch (_) {
|
|
104
|
+
return { ok: false, reason: 'skill-manifest.json 解析失败' };
|
|
105
|
+
}
|
|
106
|
+
if (manifest.slug !== EXPECTED.slug) return { ok: false, reason: 'manifest slug 不符' };
|
|
107
|
+
if (manifest.package !== EXPECTED.pkg) return { ok: false, reason: 'manifest package 不符' };
|
|
108
|
+
if (manifest.trust !== EXPECTED.trust) return { ok: false, reason: 'manifest trust 不符' };
|
|
109
|
+
const skillVersion = frontmatterValue(skillFile, 'version');
|
|
110
|
+
if (!skillVersion || manifest.version !== skillVersion) {
|
|
111
|
+
return { ok: false, reason: 'manifest 与 SKILL.md 版本不一致' };
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
return {
|
|
115
|
+
ok: true,
|
|
116
|
+
path: resolved,
|
|
117
|
+
reason: null,
|
|
118
|
+
info: {
|
|
119
|
+
slug: manifest.slug,
|
|
120
|
+
package: manifest.package,
|
|
121
|
+
version: manifest.version,
|
|
122
|
+
skillDir: skillDir,
|
|
123
|
+
sha256: sha256File(resolved),
|
|
124
|
+
},
|
|
125
|
+
};
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
/** 身份 + 摘要双绑定校验:options.record 为安装记录(缺失即 fail-closed)。 */
|
|
129
|
+
function verifyEngine(enginePath, options) {
|
|
130
|
+
const opts = options || {};
|
|
131
|
+
const inspected = inspectPackage(enginePath);
|
|
132
|
+
if (!inspected.ok) return inspected;
|
|
133
|
+
const record = opts.record;
|
|
134
|
+
if (opts.requireRecord === false) return inspected;
|
|
135
|
+
if (!record || !record.path || !record.sha256) {
|
|
136
|
+
return { ok: false, reason: '缺少受信安装记录(fail-closed)' };
|
|
137
|
+
}
|
|
138
|
+
if (path.resolve(String(record.path)) !== inspected.path) {
|
|
139
|
+
return { ok: false, reason: '安装记录路径与候选引擎不一致' };
|
|
140
|
+
}
|
|
141
|
+
if (String(record.sha256).toLowerCase() !== inspected.info.sha256.toLowerCase()) {
|
|
142
|
+
return { ok: false, reason: '引擎摘要与安装记录不一致' };
|
|
143
|
+
}
|
|
144
|
+
return inspected;
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
module.exports = {
|
|
148
|
+
RECORD_FILE_NAME,
|
|
149
|
+
EXPECTED,
|
|
150
|
+
recordPath,
|
|
151
|
+
sha256File,
|
|
152
|
+
loadRecord,
|
|
153
|
+
saveRecord,
|
|
154
|
+
inspectPackage,
|
|
155
|
+
verifyEngine,
|
|
156
|
+
};
|
package/lib/verify-gate.js
CHANGED
|
@@ -1,35 +1,46 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
const fs = require('fs');
|
|
3
3
|
const path = require('path');
|
|
4
|
+
const trusted = require('./trusted-verifier');
|
|
4
5
|
|
|
5
6
|
const SAFE = 'SAFE TO INSTALL';
|
|
6
7
|
const CAUTION = 'INSTALL WITH CAUTION';
|
|
7
8
|
const REVIEW = 'REVIEW REQUIRED';
|
|
8
9
|
const BLOCK = 'DO NOT INSTALL';
|
|
9
10
|
|
|
10
|
-
|
|
11
|
+
/** 用户显式指定的引擎(人工可信,不做身份判定)。 */
|
|
12
|
+
function explicitCandidates(opts) {
|
|
11
13
|
const candidates = [];
|
|
12
14
|
if (opts && opts.verify) candidates.push(path.resolve(opts.verify));
|
|
13
15
|
if (process.env.YOTTA_SKILLS_VERIFY) candidates.push(path.resolve(process.env.YOTTA_SKILLS_VERIFY));
|
|
14
|
-
if (dest) candidates.push(path.join(dest, 'yotta-verify', 'scripts', 'yotta_verify.py'));
|
|
15
16
|
return candidates;
|
|
16
17
|
}
|
|
17
18
|
|
|
19
|
+
/**
|
|
20
|
+
* 定位元信扫描引擎(v0.19.13 收紧)。
|
|
21
|
+
*
|
|
22
|
+
* 旧实现会读本地注册表里 slug=yotta-verify 的 source_dirs,而注册表身份来自被扫描技能
|
|
23
|
+
* 自己 SKILL.md frontmatter 的 name —— 伪造目录即可劫持校验器(执行任意代码 + 伪造
|
|
24
|
+
* SAFE 结论)。现只认两类来源:
|
|
25
|
+
* ① 用户显式指定:--verify / YOTTA_SKILLS_VERIFY;
|
|
26
|
+
* ② 安装管线写入的受信记录:包身份(slug / package / trust / 版本)+ 路径 realpath
|
|
27
|
+
* + 引擎 SHA-256 与记录一致。
|
|
28
|
+
* 其余一律返回 null(fail-closed),由上层走自举安装。
|
|
29
|
+
*/
|
|
18
30
|
function findVerifier(context) {
|
|
19
|
-
const
|
|
20
|
-
const
|
|
21
|
-
if (registry && registry.skills && registry.skills['yotta-verify']) {
|
|
22
|
-
for (const dir of registry.skills['yotta-verify'].source_dirs || []) {
|
|
23
|
-
candidates.push(path.join(dir, 'scripts', 'yotta_verify.py'));
|
|
24
|
-
}
|
|
25
|
-
}
|
|
26
|
-
for (const candidate of candidates) {
|
|
31
|
+
const ctx = context || {};
|
|
32
|
+
for (const candidate of explicitCandidates(ctx.opts || {})) {
|
|
27
33
|
try {
|
|
28
34
|
if (fs.statSync(candidate).isFile()) return candidate;
|
|
29
35
|
} catch (_) {
|
|
30
36
|
// Try the next candidate.
|
|
31
37
|
}
|
|
32
38
|
}
|
|
39
|
+
const record = ctx.trustedVerifier;
|
|
40
|
+
if (record && record.path) {
|
|
41
|
+
const check = trusted.verifyEngine(record.path, { record });
|
|
42
|
+
if (check.ok) return check.path;
|
|
43
|
+
}
|
|
33
44
|
return null;
|
|
34
45
|
}
|
|
35
46
|
|
|
@@ -74,6 +85,7 @@ module.exports = {
|
|
|
74
85
|
CAUTION,
|
|
75
86
|
REVIEW,
|
|
76
87
|
BLOCK,
|
|
88
|
+
explicitCandidates,
|
|
77
89
|
findVerifier,
|
|
78
90
|
parseScanOutput,
|
|
79
91
|
evaluateVerdict,
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@yottameta/yotta-skills",
|
|
3
|
-
"version": "0.19.
|
|
3
|
+
"version": "0.19.14",
|
|
4
4
|
"description": "YuanGe (元阁) - orchestration routing, inventory, and one-command installer for the YottaMeta skill family (zero dependencies, Node.js only).",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"keywords": [
|
package/references/skill-list.md
CHANGED
|
@@ -8,12 +8,12 @@
|
|
|
8
8
|
| `yotta-anti-shallow` | 元谨 | `@yottameta/yotta-anti-shallow` | 1.4.0 | 防 AI 敷衍规则引擎(深入分析/根因追溯时激活) |
|
|
9
9
|
| `yotta-code-quality` | 元质 | `@yottameta/yotta-code-quality` | 0.4.1 | 结对式代码质量评审(十二类腐化风险 + 0-100 健康分) |
|
|
10
10
|
| `yotta-workflow` | 元序 | `@yottameta/yotta-workflow` | 0.4.1 | 跨会话/跨项目通用工作流协议(状态就近存) |
|
|
11
|
-
| `yotta-memory` | 元忆 | `@yottameta/yotta-memory` | 0.16.
|
|
11
|
+
| `yotta-memory` | 元忆 | `@yottameta/yotta-memory` | 0.16.2 | 有权限边界的文件式智能体记忆(FACT/PREF/BOUND/COMMIT) |
|
|
12
12
|
| `yotta-learn` | 元习 | `@yottameta/yotta-learn` | 0.2.1 | 学习沉淀 CLI:错误/纠正/洞见沉淀为 .learnings/ 条目 |
|
|
13
13
|
| `yotta-security-audit` | 元安 | `@yottameta/yotta-security-audit` | 0.2.4 | 安全扫描引擎:13 类检测器 + 系统安全基线 |
|
|
14
14
|
| `yotta-vetter` | 元审 | `@yottameta/yotta-vetter` | 0.2.5 | 安全审查协议:四阶段 review + SAFE TO INSTALL 判定 |
|
|
15
15
|
| `yotta-recon` | 元析 | `@yottameta/yotta-recon` | 0.1.7 | 跨智能体网络侦察:零依赖端口/服务/版本指纹探测 |
|
|
16
|
-
| `yotta-guardian` | 元盾 | `@yottameta/yotta-guardian` | 0.1.
|
|
16
|
+
| `yotta-guardian` | 元盾 | `@yottameta/yotta-guardian` | 0.1.5 | 跨智能体危险调用拦截护栏:确定性规则 + 可插拔意图验证 |
|
|
17
17
|
| `yotta-humanize` | 元真 | `@yottameta/yotta-humanize` | 0.2.1 | 去 AI 味中文写作编辑:检测器引擎 |
|
|
18
18
|
| `yotta-logs` | 元史 | `@yottameta/yotta-logs` | 0.3.1 | 跨智能体历史会话 / 记忆日志检索 |
|
|
19
19
|
| `yotta-security-testing` | 元测 | `@yottameta/yotta-security-testing` | 0.3.1 | 有纪律的 AI 安全测试方法论 + Scope Guard 五道防线 |
|
|
@@ -26,7 +26,7 @@
|
|
|
26
26
|
| `yotta-chain` | 元链 | `@yottameta/yotta-chain` | 0.1.4 | 供应链依赖校验引擎 |
|
|
27
27
|
| `yotta-triage` | 元鉴 | `@yottameta/yotta-triage` | 0.1.3 | 恶意样本静态初筛引擎(哈希/熵/字符串/PE-ELF) |
|
|
28
28
|
| `yotta-prompt` | 元引 | `@yottameta/yotta-prompt` | 0.2.2 | 意图澄清 + 生态入口(常驻注入,map 串联元阁全家) |
|
|
29
|
-
| `yotta-verify` | 元信 | `@yottameta/yotta-verify` | 0.3.
|
|
29
|
+
| `yotta-verify` | 元信 | `@yottameta/yotta-verify` | 0.3.2 | 装前安全扫描器 + audited 徽章(prompt injection + 危险模式) |
|
|
30
30
|
|
|
31
31
|
## 家族分布
|
|
32
32
|
|
package/skill-manifest.json
CHANGED
package/skills.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"manifestVersion": 1,
|
|
3
|
-
"updated": "2026-09-
|
|
3
|
+
"updated": "2026-09-21",
|
|
4
4
|
"note": "清单同步要求:每次技能发布新版本后,按 YottaSkills 技能登记表更新本文件(登记表为内部台账,不随包分发)。",
|
|
5
5
|
"skills": [
|
|
6
6
|
{
|
|
@@ -28,7 +28,7 @@
|
|
|
28
28
|
"slug": "yotta-memory",
|
|
29
29
|
"name": "元忆",
|
|
30
30
|
"pkg": "@yottameta/yotta-memory",
|
|
31
|
-
"version": "0.16.
|
|
31
|
+
"version": "0.16.2",
|
|
32
32
|
"desc": "有权限边界的文件式智能体记忆(FACT/PREF/BOUND/COMMIT)"
|
|
33
33
|
},
|
|
34
34
|
{
|
|
@@ -63,7 +63,7 @@
|
|
|
63
63
|
"slug": "yotta-guardian",
|
|
64
64
|
"name": "元盾",
|
|
65
65
|
"pkg": "@yottameta/yotta-guardian",
|
|
66
|
-
"version": "0.1.
|
|
66
|
+
"version": "0.1.5",
|
|
67
67
|
"desc": "跨智能体危险调用拦截护栏:确定性规则 + 可插拔意图验证"
|
|
68
68
|
},
|
|
69
69
|
{
|
|
@@ -154,7 +154,7 @@
|
|
|
154
154
|
"slug": "yotta-verify",
|
|
155
155
|
"name": "元信",
|
|
156
156
|
"pkg": "@yottameta/yotta-verify",
|
|
157
|
-
"version": "0.3.
|
|
157
|
+
"version": "0.3.2",
|
|
158
158
|
"desc": "装前安全扫描器 + audited 徽章(prompt injection + 危险模式)"
|
|
159
159
|
}
|
|
160
160
|
]
|