@skillfm/local 2.1.1 → 2.1.2
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/dist/guard/bin.js +0 -0
- package/dist/harness/kernels/deny-pipeline.d.ts +5 -0
- package/dist/harness/kernels/deny-pipeline.d.ts.map +1 -1
- package/dist/harness/kernels/deny-pipeline.js +27 -3
- package/dist/harness/kernels/deny-pipeline.js.map +1 -1
- package/dist/index.js +22 -2
- package/dist/index.js.map +1 -1
- package/dist/mcp-stdio/bin.js +0 -0
- package/dist/skill-md/writer.d.ts +7 -0
- package/dist/skill-md/writer.d.ts.map +1 -1
- package/dist/skill-md/writer.js +55 -0
- package/dist/skill-md/writer.js.map +1 -1
- package/package.json +2 -1
- package/dist/agent-hints 2.js +0 -102
- package/dist/agent-hints.d 2.ts +0 -25
- package/dist/agent-hints.d.ts 2.map +0 -1
- package/dist/agent-hints.js 2.map +0 -1
- package/dist/doctor 2.js +0 -456
- package/dist/doctor.d 2.ts +0 -56
- package/dist/doctor.d.ts 2.map +0 -1
- package/dist/doctor.js 2.map +0 -1
- package/dist/mcp-stdio 2/api-client.d.ts +0 -28
- package/dist/mcp-stdio 2/api-client.d.ts.map +0 -1
- package/dist/mcp-stdio 2/api-client.js +0 -123
- package/dist/mcp-stdio 2/api-client.js.map +0 -1
- package/dist/mcp-stdio 2/bin.d.ts +0 -3
- package/dist/mcp-stdio 2/bin.d.ts.map +0 -1
- package/dist/mcp-stdio 2/bin.js +0 -18
- package/dist/mcp-stdio 2/bin.js.map +0 -1
- package/dist/mcp-stdio 2/config.d.ts +0 -6
- package/dist/mcp-stdio 2/config.d.ts.map +0 -1
- package/dist/mcp-stdio 2/config.js +0 -26
- package/dist/mcp-stdio 2/config.js.map +0 -1
- package/dist/mcp-stdio 2/render-flow.d.ts +0 -53
- package/dist/mcp-stdio 2/render-flow.d.ts.map +0 -1
- package/dist/mcp-stdio 2/render-flow.js +0 -70
- package/dist/mcp-stdio 2/render-flow.js.map +0 -1
- package/dist/mcp-stdio 2/request-context.d.ts +0 -30
- package/dist/mcp-stdio 2/request-context.d.ts.map +0 -1
- package/dist/mcp-stdio 2/request-context.js +0 -78
- package/dist/mcp-stdio 2/request-context.js.map +0 -1
- package/dist/mcp-stdio 2/server.d.ts +0 -19
- package/dist/mcp-stdio 2/server.d.ts.map +0 -1
- package/dist/mcp-stdio 2/server.js +0 -651
- package/dist/mcp-stdio 2/server.js.map +0 -1
- package/dist/soul 2.js +0 -439
- package/dist/soul-security 2.js +0 -197
- package/dist/soul-security.d 2.ts +0 -76
- package/dist/soul-security.d.ts 2.map +0 -1
- package/dist/soul-security.js 2.map +0 -1
- package/dist/soul.d 2.ts +0 -135
- package/dist/soul.d.ts 2.map +0 -1
- package/dist/soul.js 2.map +0 -1
package/dist/agent-hints 2.js
DELETED
|
@@ -1,102 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Agent Hint — sidecar → agent 的指令文案 (双语并列)
|
|
3
|
-
*
|
|
4
|
-
* 场景: 用户跑 `npx -y @skillfm/local@latest start` 或 POST /activate/poll
|
|
5
|
-
* 成功后, sidecar 返回的 JSON 里带 `hint_for_agent`, agent 读后决定下一步动作 +
|
|
6
|
-
* 对用户说什么.
|
|
7
|
-
*
|
|
8
|
-
* 设计 (2026-04-19 方案 v2):
|
|
9
|
-
* 首次安装的 hint 文本量不大, 直接**中英并列**. agent 会根据用户发来的消息
|
|
10
|
-
* 语言自动选对应语言回复 — 不需要服务端 detectLang, 更鲁棒
|
|
11
|
-
* (不依赖 env / Intl / locale 判断, 不会踩 macOS LANG=en_US 但系统中文 的坑).
|
|
12
|
-
*
|
|
13
|
-
* 每条 hint 尾部明示: [Reply in the user's language | 请用用户所用的语言回复].
|
|
14
|
-
*/
|
|
15
|
-
const LANG_INSTRUCTION = '[Reply in the user\'s language | 请用用户所用的语言回复用户]';
|
|
16
|
-
// ── activation_success ────────────────────────────────────────────────
|
|
17
|
-
const ACTIVATION_SUCCESS_ZH = [
|
|
18
|
-
'[中文] 激活成功!brain_key 已保存到 ~/.skillfm/config.json。停止轮询。',
|
|
19
|
-
'⚠️ 必做下一步:在用户当前 workspace 执行命令',
|
|
20
|
-
'`npx -y -p @skillfm/local skillfm-local init --v3`',
|
|
21
|
-
'完成项目级三层防御安装(写 SKILL.md priming + 自动检测 harness 写 PreToolUse hook + MCP 服务注册)。',
|
|
22
|
-
'该命令幂等 — 已安装过会自动 skip,不会破坏用户文件。',
|
|
23
|
-
'然后用温暖的语气告诉用户:「SkillFM 激活成功!🎉 SkillFM BSO 三层防御已就绪」',
|
|
24
|
-
'再调用 GET /skills 发现可用技能,',
|
|
25
|
-
'以对话的方式介绍技能列表 — 用大白话解释每个技能能做什么,并基于上下文推荐一个用户可能想先试的。',
|
|
26
|
-
].join(' ');
|
|
27
|
-
const ACTIVATION_SUCCESS_EN = [
|
|
28
|
-
'[English] Activation successful! brain_key saved to ~/.skillfm/config.json.',
|
|
29
|
-
'Stop polling.',
|
|
30
|
-
'⚠️ REQUIRED next step: in the user\'s current workspace, run',
|
|
31
|
-
'`npx -y -p @skillfm/local skillfm-local init --v3`',
|
|
32
|
-
'to complete project-level three-layer defense installation',
|
|
33
|
-
'(SKILL.md priming + auto-detect harness PreToolUse hook + MCP server registration).',
|
|
34
|
-
'This command is idempotent — already-installed files will be skipped, never overwritten.',
|
|
35
|
-
'Then tell the user warmly: "SkillFM activated! 🎉 BSO three-layer defense is ready"',
|
|
36
|
-
'and call GET /skills to discover available skills.',
|
|
37
|
-
'Present skills in a friendly conversational way — explain each in plain language',
|
|
38
|
-
'and suggest one the user might want to try first.',
|
|
39
|
-
].join(' ');
|
|
40
|
-
// ── running_activated ─────────────────────────────────────────────────
|
|
41
|
-
function runningActivatedZh(url) {
|
|
42
|
-
return [
|
|
43
|
-
`[中文] skillfm-local 正在 ${url} 运行。brain key 已加载 — SkillFM 已激活。`,
|
|
44
|
-
'⚠️ 检查项目级初始化:如果用户当前 workspace 没有 SKILL.md 文件,请先在 workspace 执行',
|
|
45
|
-
'`npx -y -p @skillfm/local skillfm-local init --v3`',
|
|
46
|
-
'完成三层防御安装(SKILL.md + harness hooks + MCP 注册),该命令幂等可重复跑。',
|
|
47
|
-
'然后告诉用户「SkillFM 已就绪!」, 调用',
|
|
48
|
-
`GET ${url}/skills 发现可用技能。`,
|
|
49
|
-
'以对话的方式介绍 — 用大白话解释每个技能能做什么, 推荐一个可以试试的。',
|
|
50
|
-
].join(' ');
|
|
51
|
-
}
|
|
52
|
-
function runningActivatedEn(url) {
|
|
53
|
-
return [
|
|
54
|
-
`[English] skillfm-local is running at ${url}. Brain key loaded — SkillFM is already activated.`,
|
|
55
|
-
'⚠️ Check project-level init: if the user\'s current workspace has no SKILL.md file,',
|
|
56
|
-
'first run `npx -y -p @skillfm/local skillfm-local init --v3` in the workspace',
|
|
57
|
-
'to complete three-layer defense installation (SKILL.md + harness hooks + MCP registration).',
|
|
58
|
-
'This command is idempotent and safe to re-run.',
|
|
59
|
-
`Then tell the user "SkillFM is ready!" and call GET ${url}/skills to discover`,
|
|
60
|
-
'available skills. Present them conversationally — explain what each skill does',
|
|
61
|
-
'in plain language and suggest one to try.',
|
|
62
|
-
].join(' ');
|
|
63
|
-
}
|
|
64
|
-
// ── running_not_activated ─────────────────────────────────────────────
|
|
65
|
-
function runningNotActivatedZh(url) {
|
|
66
|
-
return [
|
|
67
|
-
`[中文] skillfm-local 正在 ${url} 运行。尚未激活。`,
|
|
68
|
-
`下一步: POST ${url}/activate/start (无请求体) 启动 OAuth 设备流。`,
|
|
69
|
-
'它会返回一个验证链接 — 用友好的语气告诉用户:',
|
|
70
|
-
'「我需要你打开一个链接来完成 SkillFM 激活, 只需要几秒钟:」然后展示链接。',
|
|
71
|
-
`之后每 5 秒轮询 POST ${url}/activate/poll 直到激活。`,
|
|
72
|
-
'激活成功后会收到 hint 提示再跑 init --v3 完成项目级安装。',
|
|
73
|
-
'不要问用户是否想激活 — 直接做, 这是必需步骤。',
|
|
74
|
-
].join(' ');
|
|
75
|
-
}
|
|
76
|
-
function runningNotActivatedEn(url) {
|
|
77
|
-
return [
|
|
78
|
-
`[English] skillfm-local is running at ${url}. Not yet activated.`,
|
|
79
|
-
`Next step: POST ${url}/activate/start (no body) to start the OAuth device flow.`,
|
|
80
|
-
'It returns a verification URL — tell the user in a friendly way:',
|
|
81
|
-
'"I need you to open a link to complete SkillFM activation, just a few seconds:"',
|
|
82
|
-
`then show the URL. After that, poll POST ${url}/activate/poll every 5 seconds`,
|
|
83
|
-
'until activated. After activation, you will get a hint to run init --v3 for project-level installation.',
|
|
84
|
-
'Do NOT ask the user if they want to activate — just do it, it is required.',
|
|
85
|
-
].join(' ');
|
|
86
|
-
}
|
|
87
|
-
// ── public API ────────────────────────────────────────────────────────
|
|
88
|
-
/**
|
|
89
|
-
* 返回指定 key 的双语并列 hint. Agent 按用户消息语言自动匹配回复.
|
|
90
|
-
*/
|
|
91
|
-
export function agentHint(key, vars = {}) {
|
|
92
|
-
const url = vars.url ?? '';
|
|
93
|
-
switch (key) {
|
|
94
|
-
case 'activation_success':
|
|
95
|
-
return [ACTIVATION_SUCCESS_ZH, ACTIVATION_SUCCESS_EN, LANG_INSTRUCTION].join('\n\n');
|
|
96
|
-
case 'running_activated':
|
|
97
|
-
return [runningActivatedZh(url), runningActivatedEn(url), LANG_INSTRUCTION].join('\n\n');
|
|
98
|
-
case 'running_not_activated':
|
|
99
|
-
return [runningNotActivatedZh(url), runningNotActivatedEn(url), LANG_INSTRUCTION].join('\n\n');
|
|
100
|
-
}
|
|
101
|
-
}
|
|
102
|
-
//# sourceMappingURL=agent-hints.js.map
|
package/dist/agent-hints.d 2.ts
DELETED
|
@@ -1,25 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Agent Hint — sidecar → agent 的指令文案 (双语并列)
|
|
3
|
-
*
|
|
4
|
-
* 场景: 用户跑 `npx -y @skillfm/local@latest start` 或 POST /activate/poll
|
|
5
|
-
* 成功后, sidecar 返回的 JSON 里带 `hint_for_agent`, agent 读后决定下一步动作 +
|
|
6
|
-
* 对用户说什么.
|
|
7
|
-
*
|
|
8
|
-
* 设计 (2026-04-19 方案 v2):
|
|
9
|
-
* 首次安装的 hint 文本量不大, 直接**中英并列**. agent 会根据用户发来的消息
|
|
10
|
-
* 语言自动选对应语言回复 — 不需要服务端 detectLang, 更鲁棒
|
|
11
|
-
* (不依赖 env / Intl / locale 判断, 不会踩 macOS LANG=en_US 但系统中文 的坑).
|
|
12
|
-
*
|
|
13
|
-
* 每条 hint 尾部明示: [Reply in the user's language | 请用用户所用的语言回复].
|
|
14
|
-
*/
|
|
15
|
-
export type AgentHintKey = 'activation_success' | 'running_activated' | 'running_not_activated';
|
|
16
|
-
interface AgentHintVars {
|
|
17
|
-
/** sidecar 运行地址 (running_* 用) */
|
|
18
|
-
url?: string;
|
|
19
|
-
}
|
|
20
|
-
/**
|
|
21
|
-
* 返回指定 key 的双语并列 hint. Agent 按用户消息语言自动匹配回复.
|
|
22
|
-
*/
|
|
23
|
-
export declare function agentHint(key: AgentHintKey, vars?: AgentHintVars): string;
|
|
24
|
-
export {};
|
|
25
|
-
//# sourceMappingURL=agent-hints.d.ts.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"agent-hints.d.ts","sourceRoot":"","sources":["../src/agent-hints.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AAEH,MAAM,MAAM,YAAY,GACpB,oBAAoB,GACpB,mBAAmB,GACnB,uBAAuB,CAAC;AAE5B,UAAU,aAAa;IACrB,iCAAiC;IACjC,GAAG,CAAC,EAAE,MAAM,CAAC;CACd;AAuFD;;GAEG;AACH,wBAAgB,SAAS,CAAC,GAAG,EAAE,YAAY,EAAE,IAAI,GAAE,aAAkB,GAAG,MAAM,CAY7E"}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"agent-hints.js","sourceRoot":"","sources":["../src/agent-hints.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AAYH,MAAM,gBAAgB,GACpB,iDAAiD,CAAC;AAEpD,yEAAyE;AAEzE,MAAM,qBAAqB,GAAG;IAC5B,uDAAuD;IACvD,+BAA+B;IAC/B,oDAAoD;IACpD,8EAA8E;IAC9E,gCAAgC;IAChC,oDAAoD;IACpD,yBAAyB;IACzB,mDAAmD;CACpD,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;AAEZ,MAAM,qBAAqB,GAAG;IAC5B,6EAA6E;IAC7E,eAAe;IACf,8DAA8D;IAC9D,oDAAoD;IACpD,4DAA4D;IAC5D,qFAAqF;IACrF,0FAA0F;IAC1F,qFAAqF;IACrF,oDAAoD;IACpD,kFAAkF;IAClF,mDAAmD;CACpD,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;AAEZ,yEAAyE;AAEzE,SAAS,kBAAkB,CAAC,GAAW;IACrC,OAAO;QACL,yBAAyB,GAAG,kCAAkC;QAC9D,8DAA8D;QAC9D,oDAAoD;QACpD,wDAAwD;QACxD,0BAA0B;QAC1B,OAAO,GAAG,iBAAiB;QAC3B,uCAAuC;KACxC,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;AACd,CAAC;AAED,SAAS,kBAAkB,CAAC,GAAW;IACrC,OAAO;QACL,yCAAyC,GAAG,oDAAoD;QAChG,qFAAqF;QACrF,+EAA+E;QAC/E,6FAA6F;QAC7F,gDAAgD;QAChD,uDAAuD,GAAG,qBAAqB;QAC/E,gFAAgF;QAChF,2CAA2C;KAC5C,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;AACd,CAAC;AAED,yEAAyE;AAEzE,SAAS,qBAAqB,CAAC,GAAW;IACxC,OAAO;QACL,yBAAyB,GAAG,WAAW;QACvC,aAAa,GAAG,sCAAsC;QACtD,0BAA0B;QAC1B,4CAA4C;QAC5C,kBAAkB,GAAG,sBAAsB;QAC3C,uCAAuC;QACvC,2BAA2B;KAC5B,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;AACd,CAAC;AAED,SAAS,qBAAqB,CAAC,GAAW;IACxC,OAAO;QACL,yCAAyC,GAAG,sBAAsB;QAClE,mBAAmB,GAAG,2DAA2D;QACjF,kEAAkE;QAClE,iFAAiF;QACjF,4CAA4C,GAAG,gCAAgC;QAC/E,yGAAyG;QACzG,4EAA4E;KAC7E,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;AACd,CAAC;AAED,yEAAyE;AAEzE;;GAEG;AACH,MAAM,UAAU,SAAS,CAAC,GAAiB,EAAE,OAAsB,EAAE;IACnE,MAAM,GAAG,GAAG,IAAI,CAAC,GAAG,IAAI,EAAE,CAAC;IAC3B,QAAQ,GAAG,EAAE,CAAC;QACZ,KAAK,oBAAoB;YACvB,OAAO,CAAC,qBAAqB,EAAE,qBAAqB,EAAE,gBAAgB,CAAC,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;QACvF,KAAK,mBAAmB;YACtB,OAAO,CAAC,kBAAkB,CAAC,GAAG,CAAC,EAAE,kBAAkB,CAAC,GAAG,CAAC,EAAE,gBAAgB,CAAC,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;QAC3F,KAAK,uBAAuB;YAC1B,OAAO,CAAC,qBAAqB,CAAC,GAAG,CAAC,EAAE,qBAAqB,CAAC,GAAG,CAAC,EAAE,gBAAgB,CAAC,CAAC,IAAI,CACpF,MAAM,CACP,CAAC;IACN,CAAC;AACH,CAAC"}
|
package/dist/doctor 2.js
DELETED
|
@@ -1,456 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* BSO M9-5 — `skillfm doctor` 诊断命令
|
|
3
|
-
*
|
|
4
|
-
* Refs: docs/prd/PRD-BSO-M9-HARNESS-HOOKS.md §3.6
|
|
5
|
-
*
|
|
6
|
-
* 三层自检:
|
|
7
|
-
* L1 LLM 层:priming block 是否已注入到目标文件
|
|
8
|
-
* L2 harness 层:hooks / MCP 配置是否已注册
|
|
9
|
-
* L3 sidecar 层:sidecar 是否在跑、guard 端点是否可达
|
|
10
|
-
*
|
|
11
|
-
* 输出:人类可读(中文),每项 ✅ / ⚠️ / ❌。
|
|
12
|
-
*/
|
|
13
|
-
import { existsSync, readFileSync } from 'node:fs';
|
|
14
|
-
import { detectHarness } from './harness/detector.js';
|
|
15
|
-
import { pathsFor, capabilitiesFor, SKILLFM_GUARD_COMMAND } from './harness/templates.js';
|
|
16
|
-
import { PRIMING_BEGIN } from './harness/priming.js';
|
|
17
|
-
import { readSidecarEndpoint } from './guard/sidecar-client.js';
|
|
18
|
-
function symbol(s) {
|
|
19
|
-
return s === 'ok' ? '✅' : s === 'warn' ? '⚠️ ' : '❌';
|
|
20
|
-
}
|
|
21
|
-
// ============================================================================
|
|
22
|
-
// L1: LLM priming 层
|
|
23
|
-
// ============================================================================
|
|
24
|
-
function checkL1Priming(harness, cwd) {
|
|
25
|
-
const paths = pathsFor(harness, cwd);
|
|
26
|
-
const items = [];
|
|
27
|
-
try {
|
|
28
|
-
if (!existsSync(paths.primingFile)) {
|
|
29
|
-
items.push({
|
|
30
|
-
layer: 'L1',
|
|
31
|
-
status: 'fail',
|
|
32
|
-
title: 'LLM priming 文件不存在',
|
|
33
|
-
detail: paths.primingFile,
|
|
34
|
-
fix: `运行 \`skillfm init --harness=${harness}\``,
|
|
35
|
-
});
|
|
36
|
-
return items;
|
|
37
|
-
}
|
|
38
|
-
const content = readFileSync(paths.primingFile, 'utf-8');
|
|
39
|
-
if (!content.includes(PRIMING_BEGIN)) {
|
|
40
|
-
items.push({
|
|
41
|
-
layer: 'L1',
|
|
42
|
-
status: 'warn',
|
|
43
|
-
title: 'LLM priming 文件存在但未安装 SkillFM block',
|
|
44
|
-
detail: paths.primingFile,
|
|
45
|
-
fix: `运行 \`skillfm init --harness=${harness}\``,
|
|
46
|
-
});
|
|
47
|
-
}
|
|
48
|
-
else {
|
|
49
|
-
items.push({
|
|
50
|
-
layer: 'L1',
|
|
51
|
-
status: 'ok',
|
|
52
|
-
title: 'LLM priming 已安装',
|
|
53
|
-
detail: paths.primingFile,
|
|
54
|
-
});
|
|
55
|
-
}
|
|
56
|
-
}
|
|
57
|
-
catch (err) {
|
|
58
|
-
items.push({
|
|
59
|
-
layer: 'L1',
|
|
60
|
-
status: 'fail',
|
|
61
|
-
title: 'LLM priming 文件检查失败',
|
|
62
|
-
detail: err.message,
|
|
63
|
-
});
|
|
64
|
-
}
|
|
65
|
-
return items;
|
|
66
|
-
}
|
|
67
|
-
// ============================================================================
|
|
68
|
-
// L2: harness 层
|
|
69
|
-
// ============================================================================
|
|
70
|
-
function checkL2Harness(harness, cwd) {
|
|
71
|
-
const paths = pathsFor(harness, cwd);
|
|
72
|
-
const caps = capabilitiesFor(harness);
|
|
73
|
-
const items = [];
|
|
74
|
-
if (harness === 'unknown') {
|
|
75
|
-
items.push({
|
|
76
|
-
layer: 'L2',
|
|
77
|
-
status: 'warn',
|
|
78
|
-
title: 'harness 未识别',
|
|
79
|
-
detail: '无法自动写入 harness 层配置;L1 + L3 仍然可用',
|
|
80
|
-
fix: '运行 `skillfm init --harness=<claude-code|openclaw|cursor|aider|windsurf>`',
|
|
81
|
-
});
|
|
82
|
-
return items;
|
|
83
|
-
}
|
|
84
|
-
if (!caps.preToolUseHook) {
|
|
85
|
-
items.push({
|
|
86
|
-
layer: 'L2',
|
|
87
|
-
status: 'warn',
|
|
88
|
-
title: `${harness}: PreToolUse hook 未支持`,
|
|
89
|
-
detail: caps.fallback || '退化到 MCP description + sidecar 9 红线兜底',
|
|
90
|
-
fix: caps.hookResearched ? undefined : `等待 M9-6 调研结论后升级模板`,
|
|
91
|
-
});
|
|
92
|
-
return items;
|
|
93
|
-
}
|
|
94
|
-
if (!paths.harnessConfigFile) {
|
|
95
|
-
items.push({
|
|
96
|
-
layer: 'L2',
|
|
97
|
-
status: 'fail',
|
|
98
|
-
title: `${harness}: 无 harness 配置文件目标路径`,
|
|
99
|
-
detail: '内部错误,请报 bug',
|
|
100
|
-
});
|
|
101
|
-
return items;
|
|
102
|
-
}
|
|
103
|
-
if (!existsSync(paths.harnessConfigFile)) {
|
|
104
|
-
items.push({
|
|
105
|
-
layer: 'L2',
|
|
106
|
-
status: 'fail',
|
|
107
|
-
title: `${harness}: 配置文件不存在`,
|
|
108
|
-
detail: paths.harnessConfigFile,
|
|
109
|
-
fix: `运行 \`skillfm init --harness=${harness}\``,
|
|
110
|
-
});
|
|
111
|
-
return items;
|
|
112
|
-
}
|
|
113
|
-
try {
|
|
114
|
-
const content = readFileSync(paths.harnessConfigFile, 'utf-8');
|
|
115
|
-
if (!content.includes(SKILLFM_GUARD_COMMAND)) {
|
|
116
|
-
items.push({
|
|
117
|
-
layer: 'L2',
|
|
118
|
-
status: 'warn',
|
|
119
|
-
title: `${harness}: hook 未注册 skillfm-guard`,
|
|
120
|
-
detail: paths.harnessConfigFile,
|
|
121
|
-
fix: `运行 \`skillfm init --harness=${harness}\``,
|
|
122
|
-
});
|
|
123
|
-
}
|
|
124
|
-
else {
|
|
125
|
-
items.push({
|
|
126
|
-
layer: 'L2',
|
|
127
|
-
status: 'ok',
|
|
128
|
-
title: `${harness}: hook 已注册`,
|
|
129
|
-
detail: paths.harnessConfigFile,
|
|
130
|
-
});
|
|
131
|
-
}
|
|
132
|
-
}
|
|
133
|
-
catch (err) {
|
|
134
|
-
items.push({
|
|
135
|
-
layer: 'L2',
|
|
136
|
-
status: 'fail',
|
|
137
|
-
title: `${harness}: 配置文件读取失败`,
|
|
138
|
-
detail: err.message,
|
|
139
|
-
});
|
|
140
|
-
}
|
|
141
|
-
// skillfm-guard 可执行性(简单 existsSync 在 PATH 中不好查;退而求其次检测包内路径)
|
|
142
|
-
// 让 skillfm doctor 读 ~/.skillfm/local.json 的 url 来验证 sidecar 可达即可;
|
|
143
|
-
// guard 可执行由 install 流程保证,此处信息级。
|
|
144
|
-
return items;
|
|
145
|
-
}
|
|
146
|
-
// ============================================================================
|
|
147
|
-
// L3: sidecar + guard endpoint
|
|
148
|
-
// ============================================================================
|
|
149
|
-
async function checkL3Sidecar() {
|
|
150
|
-
const items = [];
|
|
151
|
-
const endpoint = readSidecarEndpoint();
|
|
152
|
-
if (!endpoint) {
|
|
153
|
-
items.push({
|
|
154
|
-
layer: 'L3',
|
|
155
|
-
status: 'fail',
|
|
156
|
-
title: 'sidecar 未运行',
|
|
157
|
-
detail: '找不到 ~/.skillfm/local.json;sidecar 未启动',
|
|
158
|
-
fix: '运行 `skillfm start`',
|
|
159
|
-
});
|
|
160
|
-
return items;
|
|
161
|
-
}
|
|
162
|
-
try {
|
|
163
|
-
const controller = new AbortController();
|
|
164
|
-
const timer = setTimeout(() => controller.abort(), 1000);
|
|
165
|
-
const res = await fetch(`${endpoint.url}/health`, { signal: controller.signal });
|
|
166
|
-
clearTimeout(timer);
|
|
167
|
-
if (res.ok) {
|
|
168
|
-
items.push({
|
|
169
|
-
layer: 'L3',
|
|
170
|
-
status: 'ok',
|
|
171
|
-
title: 'sidecar /health 正常',
|
|
172
|
-
detail: endpoint.url,
|
|
173
|
-
});
|
|
174
|
-
}
|
|
175
|
-
else {
|
|
176
|
-
items.push({
|
|
177
|
-
layer: 'L3',
|
|
178
|
-
status: 'warn',
|
|
179
|
-
title: `sidecar /health 返回 ${res.status}`,
|
|
180
|
-
detail: endpoint.url,
|
|
181
|
-
});
|
|
182
|
-
}
|
|
183
|
-
}
|
|
184
|
-
catch (err) {
|
|
185
|
-
items.push({
|
|
186
|
-
layer: 'L3',
|
|
187
|
-
status: 'fail',
|
|
188
|
-
title: 'sidecar 不可达',
|
|
189
|
-
detail: `${endpoint.url}: ${err.message}`,
|
|
190
|
-
fix: '`skillfm restart`',
|
|
191
|
-
});
|
|
192
|
-
return items;
|
|
193
|
-
}
|
|
194
|
-
try {
|
|
195
|
-
const controller = new AbortController();
|
|
196
|
-
const timer = setTimeout(() => controller.abort(), 1000);
|
|
197
|
-
const res = await fetch(`${endpoint.url}/internal/guard/snapshot`, {
|
|
198
|
-
signal: controller.signal,
|
|
199
|
-
});
|
|
200
|
-
clearTimeout(timer);
|
|
201
|
-
if (res.ok) {
|
|
202
|
-
const body = await res.json().catch(() => ({}));
|
|
203
|
-
const sessCount = body?.snapshot?.sessions?.length ?? 0;
|
|
204
|
-
const lastBrainRunAt = body?.snapshot?.last_brain_run_at ?? null;
|
|
205
|
-
items.push({
|
|
206
|
-
layer: 'L3',
|
|
207
|
-
status: 'ok',
|
|
208
|
-
title: 'guard 端点响应正常',
|
|
209
|
-
detail: `sessions=${sessCount}, last_brain_run_at=${lastBrainRunAt ? new Date(lastBrainRunAt).toISOString() : 'null'}`,
|
|
210
|
-
});
|
|
211
|
-
}
|
|
212
|
-
else {
|
|
213
|
-
items.push({
|
|
214
|
-
layer: 'L3',
|
|
215
|
-
status: 'warn',
|
|
216
|
-
title: `guard 端点返回 ${res.status}`,
|
|
217
|
-
detail: `${endpoint.url}/internal/guard/snapshot`,
|
|
218
|
-
});
|
|
219
|
-
}
|
|
220
|
-
}
|
|
221
|
-
catch (err) {
|
|
222
|
-
items.push({
|
|
223
|
-
layer: 'L3',
|
|
224
|
-
status: 'warn',
|
|
225
|
-
title: 'guard 端点超时',
|
|
226
|
-
detail: err.message,
|
|
227
|
-
});
|
|
228
|
-
}
|
|
229
|
-
return items;
|
|
230
|
-
}
|
|
231
|
-
export async function runDoctor(cwd = process.cwd()) {
|
|
232
|
-
const detected = detectHarness({ cwd });
|
|
233
|
-
const items = [
|
|
234
|
-
...checkL1Priming(detected.harness, cwd),
|
|
235
|
-
...checkL2Harness(detected.harness, cwd),
|
|
236
|
-
...(await checkL3Sidecar()),
|
|
237
|
-
];
|
|
238
|
-
return {
|
|
239
|
-
harness: detected.harness,
|
|
240
|
-
detect_source: detected.source,
|
|
241
|
-
detect_reason: detected.reason,
|
|
242
|
-
items,
|
|
243
|
-
};
|
|
244
|
-
}
|
|
245
|
-
export function renderDoctorReport(r) {
|
|
246
|
-
const lines = [];
|
|
247
|
-
lines.push(`SkillFM doctor — harness=${r.harness} (detected via ${r.detect_source}: ${r.detect_reason})`);
|
|
248
|
-
lines.push('');
|
|
249
|
-
for (const layer of ['L1', 'L2', 'L3']) {
|
|
250
|
-
const layerItems = r.items.filter((i) => i.layer === layer);
|
|
251
|
-
if (layerItems.length === 0)
|
|
252
|
-
continue;
|
|
253
|
-
lines.push(`${layer === 'L1' ? 'L1 LLM 层' : layer === 'L2' ? 'L2 harness 层' : 'L3 sidecar 层'}`);
|
|
254
|
-
for (const it of layerItems) {
|
|
255
|
-
lines.push(` ${symbol(it.status)} ${it.title}`);
|
|
256
|
-
lines.push(` ${it.detail}`);
|
|
257
|
-
if (it.fix)
|
|
258
|
-
lines.push(` 修复: ${it.fix}`);
|
|
259
|
-
}
|
|
260
|
-
lines.push('');
|
|
261
|
-
}
|
|
262
|
-
const hasFail = r.items.some((i) => i.status === 'fail');
|
|
263
|
-
const hasWarn = r.items.some((i) => i.status === 'warn');
|
|
264
|
-
if (hasFail)
|
|
265
|
-
lines.push('❌ 发现 fail 项,上面列出了修复建议');
|
|
266
|
-
else if (hasWarn)
|
|
267
|
-
lines.push('⚠️ 有 warn 项,查看详情决定是否修复');
|
|
268
|
-
else
|
|
269
|
-
lines.push('✅ 所有检查通过');
|
|
270
|
-
return lines.join('\n');
|
|
271
|
-
}
|
|
272
|
-
// ============================================================================
|
|
273
|
-
// v0.3 三层抽象架构 doctor
|
|
274
|
-
// ============================================================================
|
|
275
|
-
import { existsSync as _existsSync, readFileSync as _readFileSync } from 'node:fs';
|
|
276
|
-
import { join as _join } from 'node:path';
|
|
277
|
-
import { detectPrimaryHost, diagnoseAll } from './harness/kernels/registry.js';
|
|
278
|
-
import { SKILL_MD_BEGIN } from './skill-md/template.js';
|
|
279
|
-
function checkLayer1SkillMd(cwd) {
|
|
280
|
-
const items = [];
|
|
281
|
-
const filePath = _join(cwd, 'SKILL.md');
|
|
282
|
-
if (!_existsSync(filePath)) {
|
|
283
|
-
items.push({
|
|
284
|
-
status: 'warn',
|
|
285
|
-
title: 'SKILL.md 不存在(项目级)',
|
|
286
|
-
detail: filePath,
|
|
287
|
-
fix: '运行 `skillfm init --v3`',
|
|
288
|
-
});
|
|
289
|
-
return items;
|
|
290
|
-
}
|
|
291
|
-
try {
|
|
292
|
-
const raw = _readFileSync(filePath, 'utf-8');
|
|
293
|
-
if (raw.includes('skillfm-bso-priming') || raw.includes(SKILL_MD_BEGIN)) {
|
|
294
|
-
items.push({
|
|
295
|
-
status: 'ok',
|
|
296
|
-
title: 'SKILL.md 已安装 SkillFM BSO priming',
|
|
297
|
-
detail: filePath,
|
|
298
|
-
});
|
|
299
|
-
}
|
|
300
|
-
else {
|
|
301
|
-
items.push({
|
|
302
|
-
status: 'warn',
|
|
303
|
-
title: 'SKILL.md 存在但未含 SkillFM block',
|
|
304
|
-
detail: filePath,
|
|
305
|
-
fix: '运行 `skillfm init --v3`',
|
|
306
|
-
});
|
|
307
|
-
}
|
|
308
|
-
// Anthropic Skills 兼容性快检:frontmatter 必须以 --- 开头
|
|
309
|
-
if (!raw.startsWith('---') && !raw.includes('\n---')) {
|
|
310
|
-
items.push({
|
|
311
|
-
status: 'warn',
|
|
312
|
-
title: 'SKILL.md 缺 Anthropic Skills frontmatter',
|
|
313
|
-
detail: '应以 YAML --- 开头',
|
|
314
|
-
});
|
|
315
|
-
}
|
|
316
|
-
else {
|
|
317
|
-
items.push({
|
|
318
|
-
status: 'ok',
|
|
319
|
-
title: 'SKILL.md 含 Anthropic Skills 兼容 frontmatter',
|
|
320
|
-
detail: filePath,
|
|
321
|
-
});
|
|
322
|
-
}
|
|
323
|
-
}
|
|
324
|
-
catch (err) {
|
|
325
|
-
items.push({
|
|
326
|
-
status: 'fail',
|
|
327
|
-
title: 'SKILL.md 读取失败',
|
|
328
|
-
detail: err.message,
|
|
329
|
-
});
|
|
330
|
-
}
|
|
331
|
-
return items;
|
|
332
|
-
}
|
|
333
|
-
export async function runDoctorV3(cwd = process.cwd()) {
|
|
334
|
-
const host = detectPrimaryHost();
|
|
335
|
-
const diag = await diagnoseAll(host, cwd);
|
|
336
|
-
const layer1 = checkLayer1SkillMd(cwd);
|
|
337
|
-
return {
|
|
338
|
-
architecture: 'v3-three-layer',
|
|
339
|
-
host,
|
|
340
|
-
layer1: { name: 'SKILL.md 标准化', items: layer1 },
|
|
341
|
-
layer2: { name: 'MCP Protocol-Reject', diagnose: diag.layer2 },
|
|
342
|
-
layer3: { name: '机制内核 adapter', diagnoses: diag.layer3 },
|
|
343
|
-
};
|
|
344
|
-
}
|
|
345
|
-
function levelBadge(level) {
|
|
346
|
-
switch (level) {
|
|
347
|
-
case 'strong':
|
|
348
|
-
return '✅ 强制';
|
|
349
|
-
case 'protocol-fallback':
|
|
350
|
-
return '✅ 协议级';
|
|
351
|
-
case 'soft':
|
|
352
|
-
return '⚠️ 软层';
|
|
353
|
-
case 'absent':
|
|
354
|
-
return '❌ 缺失';
|
|
355
|
-
}
|
|
356
|
-
}
|
|
357
|
-
export function renderDoctorV3Report(r) {
|
|
358
|
-
const lines = [];
|
|
359
|
-
lines.push(`SkillFM doctor (v0.3 三层抽象架构)`);
|
|
360
|
-
lines.push(`检测到 host: ${r.host.host} (${r.host.source}${r.host.reason ? ': ' + r.host.reason : ''})`);
|
|
361
|
-
lines.push('');
|
|
362
|
-
// Layer 1
|
|
363
|
-
lines.push(`Layer 1 — ${r.layer1.name}`);
|
|
364
|
-
for (const it of r.layer1.items) {
|
|
365
|
-
lines.push(` ${it.status === 'ok' ? '✅' : it.status === 'warn' ? '⚠️ ' : '❌'} ${it.title}`);
|
|
366
|
-
if (it.detail)
|
|
367
|
-
lines.push(` ${it.detail}`);
|
|
368
|
-
if (it.fix)
|
|
369
|
-
lines.push(` 修复: ${it.fix}`);
|
|
370
|
-
}
|
|
371
|
-
lines.push('');
|
|
372
|
-
// Layer 2
|
|
373
|
-
lines.push(`Layer 2 — ${r.layer2.name} ${levelBadge(r.layer2.diagnose.level)}`);
|
|
374
|
-
for (const c of r.layer2.diagnose.checks) {
|
|
375
|
-
lines.push(` ${c.ok ? '✅' : '❌'} ${c.name}${c.detail ? ' — ' + c.detail : ''}`);
|
|
376
|
-
}
|
|
377
|
-
lines.push('');
|
|
378
|
-
// Layer 3
|
|
379
|
-
lines.push(`Layer 3 — ${r.layer3.name}`);
|
|
380
|
-
if (r.layer3.diagnoses.length === 0) {
|
|
381
|
-
lines.push(` ⚠️ 当前 host 无匹配 kernel;仅 Layer 1+2 生效`);
|
|
382
|
-
}
|
|
383
|
-
for (const d of r.layer3.diagnoses) {
|
|
384
|
-
lines.push(` [${d.kernel}] host=${d.host} ${levelBadge(d.level)}`);
|
|
385
|
-
for (const c of d.checks) {
|
|
386
|
-
lines.push(` ${c.ok ? '✅' : '❌'} ${c.name}${c.detail ? ' — ' + c.detail : ''}`);
|
|
387
|
-
}
|
|
388
|
-
}
|
|
389
|
-
lines.push('');
|
|
390
|
-
// 综合判断
|
|
391
|
-
const allOk = r.layer1.items.every((i) => i.status === 'ok') &&
|
|
392
|
-
r.layer2.diagnose.level !== 'absent' &&
|
|
393
|
-
r.layer3.diagnoses.every((d) => d.level !== 'absent');
|
|
394
|
-
if (allOk) {
|
|
395
|
-
lines.push('✅ 三层全部就绪');
|
|
396
|
-
}
|
|
397
|
-
else {
|
|
398
|
-
lines.push('⚠️ 存在缺失层,详情见上');
|
|
399
|
-
}
|
|
400
|
-
lines.push('');
|
|
401
|
-
lines.push('兜底(与三层正交):');
|
|
402
|
-
lines.push(' ✅ 9 安全红线(S1-S9)→ sidecar 协议契约层');
|
|
403
|
-
lines.push(' ✅ brain 仲裁 → ScriptCard 契约校验');
|
|
404
|
-
return lines.join('\n');
|
|
405
|
-
}
|
|
406
|
-
// ============================================================================
|
|
407
|
-
// --disable-hooks 降级预览(不真删 hook,仅模拟"hook 失效后的强制力评估")
|
|
408
|
-
// ============================================================================
|
|
409
|
-
export function renderDisableHooksReport(r) {
|
|
410
|
-
const lines = [];
|
|
411
|
-
lines.push(`SkillFM doctor (--disable-hooks 降级预览模式)`);
|
|
412
|
-
lines.push(`检测到 host: ${r.host.host}`);
|
|
413
|
-
lines.push('');
|
|
414
|
-
lines.push('⚠️ 本模式仅模拟 Layer 3 hook 被禁用后的强制力评估,不实际修改文件。');
|
|
415
|
-
lines.push(' 要真正卸载 hook → 走 `skillfm init --uninstall` 或手动从 ~/.skillfm/backups/ 恢复。');
|
|
416
|
-
lines.push('');
|
|
417
|
-
// 假设 Layer 3 失效后,剩余防御
|
|
418
|
-
lines.push('降级后的强制力栈(自上而下):');
|
|
419
|
-
lines.push('');
|
|
420
|
-
// L1
|
|
421
|
-
const layer1Ok = r.layer1.items.some((i) => i.status === 'ok' && i.title.includes('SkillFM BSO'));
|
|
422
|
-
lines.push(` Layer 1 — SKILL.md priming ${layer1Ok ? '✅ 软层 70-95%' : '❌ 缺失(init --v3 安装)'}`);
|
|
423
|
-
// L2
|
|
424
|
-
const layer2Strong = r.layer2.diagnose.level === 'strong';
|
|
425
|
-
const layer2Ok = r.layer2.diagnose.level !== 'absent';
|
|
426
|
-
lines.push(` Layer 2 — MCP Protocol-Reject ${layer2Strong ? '✅ 协议级强制' : layer2Ok ? '⚠️ 软层' : '❌ 缺失(启 sidecar)'}`);
|
|
427
|
-
// L3 (假设 disabled)
|
|
428
|
-
lines.push(` Layer 3 — harness hook 🚫 (--disable-hooks 模式:模拟禁用)`);
|
|
429
|
-
lines.push('');
|
|
430
|
-
// 9 红线
|
|
431
|
-
lines.push('协议层兜底(始终生效,与三层正交):');
|
|
432
|
-
lines.push(' ✅ 9 安全红线(S1-S9)→ sidecar 判定层');
|
|
433
|
-
lines.push(' ✅ brain 仲裁 → ScriptCard 契约 + must_do 顺序校验');
|
|
434
|
-
lines.push('');
|
|
435
|
-
// 综合评估
|
|
436
|
-
if (layer2Strong && layer1Ok) {
|
|
437
|
-
lines.push('💡 综合评估:');
|
|
438
|
-
lines.push(' Layer 3 即使禁用,Layer 2 (MCP 协议级强制 + URLElicitationRequiredError)');
|
|
439
|
-
lines.push(' 会继续在所有 MCP-compliant client 上拦截破坏性操作。');
|
|
440
|
-
lines.push(' 宿主非 MCP 路径(如 agent 直接调 OpenClaw 内置 Write)→ 落到 9 红线兜底。');
|
|
441
|
-
}
|
|
442
|
-
else if (layer2Ok) {
|
|
443
|
-
lines.push('⚠️ 综合评估:');
|
|
444
|
-
lines.push(' Layer 3 禁用 + Layer 2 仅软层(output_version<v2)→ MCP host 仅 isError 注入回 LLM,');
|
|
445
|
-
lines.push(' 依赖 LLM 自觉。建议升级 sidecar 输出层到 v2 (URLElicitationRequiredError)。');
|
|
446
|
-
}
|
|
447
|
-
else {
|
|
448
|
-
lines.push('❌ 综合评估:');
|
|
449
|
-
lines.push(' Layer 2 缺失 + Layer 3 禁用 → 仅 9 红线 + LLM priming 兜底。');
|
|
450
|
-
lines.push(' 强制力急剧下降,建议立即恢复 Layer 3 或启 sidecar。');
|
|
451
|
-
}
|
|
452
|
-
lines.push('');
|
|
453
|
-
lines.push(`恢复正常模式:去掉 --disable-hooks 重跑 \`skillfm doctor\``);
|
|
454
|
-
return lines.join('\n');
|
|
455
|
-
}
|
|
456
|
-
//# sourceMappingURL=doctor.js.map
|
package/dist/doctor.d 2.ts
DELETED
|
@@ -1,56 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* BSO M9-5 — `skillfm doctor` 诊断命令
|
|
3
|
-
*
|
|
4
|
-
* Refs: docs/prd/PRD-BSO-M9-HARNESS-HOOKS.md §3.6
|
|
5
|
-
*
|
|
6
|
-
* 三层自检:
|
|
7
|
-
* L1 LLM 层:priming block 是否已注入到目标文件
|
|
8
|
-
* L2 harness 层:hooks / MCP 配置是否已注册
|
|
9
|
-
* L3 sidecar 层:sidecar 是否在跑、guard 端点是否可达
|
|
10
|
-
*
|
|
11
|
-
* 输出:人类可读(中文),每项 ✅ / ⚠️ / ❌。
|
|
12
|
-
*/
|
|
13
|
-
import { type Harness } from './harness/detector.js';
|
|
14
|
-
interface CheckItem {
|
|
15
|
-
layer: 'L1' | 'L2' | 'L3';
|
|
16
|
-
status: 'ok' | 'warn' | 'fail';
|
|
17
|
-
title: string;
|
|
18
|
-
detail: string;
|
|
19
|
-
fix?: string;
|
|
20
|
-
}
|
|
21
|
-
export interface DoctorReport {
|
|
22
|
-
harness: Harness;
|
|
23
|
-
detect_source: string;
|
|
24
|
-
detect_reason: string;
|
|
25
|
-
items: CheckItem[];
|
|
26
|
-
}
|
|
27
|
-
export declare function runDoctor(cwd?: string): Promise<DoctorReport>;
|
|
28
|
-
export declare function renderDoctorReport(r: DoctorReport): string;
|
|
29
|
-
import { detectPrimaryHost } from './harness/kernels/registry.js';
|
|
30
|
-
import type { DiagnoseResult } from './harness/kernels/types.js';
|
|
31
|
-
export interface DoctorV3Report {
|
|
32
|
-
architecture: 'v3-three-layer';
|
|
33
|
-
host: ReturnType<typeof detectPrimaryHost>;
|
|
34
|
-
layer1: {
|
|
35
|
-
name: 'SKILL.md 标准化';
|
|
36
|
-
items: Array<{
|
|
37
|
-
status: 'ok' | 'warn' | 'fail';
|
|
38
|
-
title: string;
|
|
39
|
-
detail?: string;
|
|
40
|
-
fix?: string;
|
|
41
|
-
}>;
|
|
42
|
-
};
|
|
43
|
-
layer2: {
|
|
44
|
-
name: 'MCP Protocol-Reject';
|
|
45
|
-
diagnose: DiagnoseResult;
|
|
46
|
-
};
|
|
47
|
-
layer3: {
|
|
48
|
-
name: '机制内核 adapter';
|
|
49
|
-
diagnoses: DiagnoseResult[];
|
|
50
|
-
};
|
|
51
|
-
}
|
|
52
|
-
export declare function runDoctorV3(cwd?: string): Promise<DoctorV3Report>;
|
|
53
|
-
export declare function renderDoctorV3Report(r: DoctorV3Report): string;
|
|
54
|
-
export declare function renderDisableHooksReport(r: DoctorV3Report): string;
|
|
55
|
-
export {};
|
|
56
|
-
//# sourceMappingURL=doctor.d.ts.map
|
package/dist/doctor.d.ts 2.map
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"doctor.d.ts","sourceRoot":"","sources":["../src/doctor.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAGH,OAAO,EAAiB,KAAK,OAAO,EAAE,MAAM,uBAAuB,CAAC;AAKpE,UAAU,SAAS;IACjB,KAAK,EAAE,IAAI,GAAG,IAAI,GAAG,IAAI,CAAC;IAC1B,MAAM,EAAE,IAAI,GAAG,MAAM,GAAG,MAAM,CAAC;IAC/B,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,MAAM,CAAC;IACf,GAAG,CAAC,EAAE,MAAM,CAAC;CACd;AAqOD,MAAM,WAAW,YAAY;IAC3B,OAAO,EAAE,OAAO,CAAC;IACjB,aAAa,EAAE,MAAM,CAAC;IACtB,aAAa,EAAE,MAAM,CAAC;IACtB,KAAK,EAAE,SAAS,EAAE,CAAC;CACpB;AAED,wBAAsB,SAAS,CAAC,GAAG,GAAE,MAAsB,GAAG,OAAO,CAAC,YAAY,CAAC,CAalF;AAED,wBAAgB,kBAAkB,CAAC,CAAC,EAAE,YAAY,GAAG,MAAM,CAqB1D;AAQD,OAAO,EAAE,iBAAiB,EAAe,MAAM,+BAA+B,CAAC;AAE/E,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,4BAA4B,CAAC;AAEjE,MAAM,WAAW,cAAc;IAC7B,YAAY,EAAE,gBAAgB,CAAC;IAC/B,IAAI,EAAE,UAAU,CAAC,OAAO,iBAAiB,CAAC,CAAC;IAC3C,MAAM,EAAE;QACN,IAAI,EAAE,cAAc,CAAC;QACrB,KAAK,EAAE,KAAK,CAAC;YAAE,MAAM,EAAE,IAAI,GAAG,MAAM,GAAG,MAAM,CAAC;YAAC,KAAK,EAAE,MAAM,CAAC;YAAC,MAAM,CAAC,EAAE,MAAM,CAAC;YAAC,GAAG,CAAC,EAAE,MAAM,CAAA;SAAE,CAAC,CAAC;KAChG,CAAC;IACF,MAAM,EAAE;QACN,IAAI,EAAE,qBAAqB,CAAC;QAC5B,QAAQ,EAAE,cAAc,CAAC;KAC1B,CAAC;IACF,MAAM,EAAE;QACN,IAAI,EAAE,cAAc,CAAC;QACrB,SAAS,EAAE,cAAc,EAAE,CAAC;KAC7B,CAAC;CACH;AAsDD,wBAAsB,WAAW,CAAC,GAAG,GAAE,MAAsB,GAAG,OAAO,CAAC,cAAc,CAAC,CAWtF;AAeD,wBAAgB,oBAAoB,CAAC,CAAC,EAAE,cAAc,GAAG,MAAM,CAkD9D;AAMD,wBAAgB,wBAAwB,CAAC,CAAC,EAAE,cAAc,GAAG,MAAM,CA0DlE"}
|