qlogicagent 0.2.1 → 0.4.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/README.md +45 -45
- package/package.json +56 -42
- package/dist/agent/agent.d.ts +0 -43
- package/dist/agent/agent.js +0 -113
- package/dist/agent/tool-loop.d.ts +0 -64
- package/dist/agent/tool-loop.js +0 -575
- package/dist/agent/types.d.ts +0 -175
- package/dist/agent/types.js +0 -14
- package/dist/cli/main.d.ts +0 -11
- package/dist/cli/main.js +0 -23
- package/dist/cli/stdio-server.d.ts +0 -45
- package/dist/cli/stdio-server.js +0 -463
- package/dist/config/config.d.ts +0 -17
- package/dist/config/config.js +0 -21
- package/dist/contracts/hooks.d.ts +0 -120
- package/dist/contracts/hooks.js +0 -7
- package/dist/contracts/index.d.ts +0 -10
- package/dist/contracts/index.js +0 -10
- package/dist/contracts/planner.d.ts +0 -35
- package/dist/contracts/planner.js +0 -2
- package/dist/contracts/skill-candidate.d.ts +0 -63
- package/dist/contracts/skill-candidate.js +0 -195
- package/dist/contracts/todo.d.ts +0 -14
- package/dist/contracts/todo.js +0 -9
- package/dist/index.d.ts +0 -13
- package/dist/index.js +0 -15
- package/dist/llm/builtin-providers.d.ts +0 -10
- package/dist/llm/builtin-providers.js +0 -531
- package/dist/llm/index.d.ts +0 -15
- package/dist/llm/index.js +0 -14
- package/dist/llm/llm-client.d.ts +0 -43
- package/dist/llm/llm-client.js +0 -67
- package/dist/llm/model-catalog.d.ts +0 -53
- package/dist/llm/model-catalog.js +0 -191
- package/dist/llm/provider-def.d.ts +0 -59
- package/dist/llm/provider-def.js +0 -12
- package/dist/llm/provider-registry.d.ts +0 -54
- package/dist/llm/provider-registry.js +0 -147
- package/dist/llm/transport.d.ts +0 -62
- package/dist/llm/transport.js +0 -27
- package/dist/llm/transports/anthropic-messages.d.ts +0 -31
- package/dist/llm/transports/anthropic-messages.js +0 -293
- package/dist/llm/transports/openai-chat.d.ts +0 -36
- package/dist/llm/transports/openai-chat.js +0 -165
- package/dist/orchestration/agent-registry.d.ts +0 -41
- package/dist/orchestration/agent-registry.js +0 -116
- package/dist/orchestration/approval-aware-tool-plan.d.ts +0 -32
- package/dist/orchestration/approval-aware-tool-plan.js +0 -87
- package/dist/orchestration/context-compression.d.ts +0 -220
- package/dist/orchestration/context-compression.js +0 -583
- package/dist/orchestration/conversation-repair.d.ts +0 -61
- package/dist/orchestration/conversation-repair.js +0 -429
- package/dist/orchestration/curator-scheduler.d.ts +0 -119
- package/dist/orchestration/curator-scheduler.js +0 -135
- package/dist/orchestration/embedded-failover-policy.d.ts +0 -110
- package/dist/orchestration/embedded-failover-policy.js +0 -168
- package/dist/orchestration/error-classification.d.ts +0 -12
- package/dist/orchestration/error-classification.js +0 -77
- package/dist/orchestration/failover-classification.d.ts +0 -8
- package/dist/orchestration/failover-classification.js +0 -381
- package/dist/orchestration/failover-error.d.ts +0 -33
- package/dist/orchestration/failover-error.js +0 -198
- package/dist/orchestration/fork-subagent.d.ts +0 -100
- package/dist/orchestration/fork-subagent.js +0 -98
- package/dist/orchestration/index.d.ts +0 -120
- package/dist/orchestration/index.js +0 -267
- package/dist/orchestration/memory-flush-policy.d.ts +0 -57
- package/dist/orchestration/memory-flush-policy.js +0 -85
- package/dist/orchestration/memory-provider.d.ts +0 -14
- package/dist/orchestration/memory-provider.js +0 -2
- package/dist/orchestration/parallel-tool-calls.d.ts +0 -41
- package/dist/orchestration/parallel-tool-calls.js +0 -59
- package/dist/orchestration/prompt-cache-strategy.d.ts +0 -126
- package/dist/orchestration/prompt-cache-strategy.js +0 -228
- package/dist/orchestration/reactive-compact.d.ts +0 -73
- package/dist/orchestration/reactive-compact.js +0 -78
- package/dist/orchestration/retry-loop.d.ts +0 -22
- package/dist/orchestration/retry-loop.js +0 -24
- package/dist/orchestration/skill-candidate.d.ts +0 -52
- package/dist/orchestration/skill-candidate.js +0 -141
- package/dist/orchestration/skill-consolidation.d.ts +0 -123
- package/dist/orchestration/skill-consolidation.js +0 -220
- package/dist/orchestration/skill-improvement.d.ts +0 -59
- package/dist/orchestration/skill-improvement.js +0 -66
- package/dist/orchestration/skill-similarity.d.ts +0 -98
- package/dist/orchestration/skill-similarity.js +0 -131
- package/dist/orchestration/streaming-tool-executor.d.ts +0 -73
- package/dist/orchestration/streaming-tool-executor.js +0 -96
- package/dist/orchestration/team-orchestration.d.ts +0 -195
- package/dist/orchestration/team-orchestration.js +0 -369
- package/dist/orchestration/team-tool-loop-wiring.d.ts +0 -92
- package/dist/orchestration/team-tool-loop-wiring.js +0 -147
- package/dist/orchestration/tool-choice-policy.d.ts +0 -54
- package/dist/orchestration/tool-choice-policy.js +0 -164
- package/dist/orchestration/tool-loop-state.d.ts +0 -50
- package/dist/orchestration/tool-loop-state.js +0 -133
- package/dist/orchestration/tool-schema.d.ts +0 -39
- package/dist/orchestration/tool-schema.js +0 -297
- package/dist/orchestration/transcript-repair.d.ts +0 -42
- package/dist/orchestration/transcript-repair.js +0 -426
- package/dist/orchestration/turn-loop-guard.d.ts +0 -86
- package/dist/orchestration/turn-loop-guard.js +0 -92
- package/dist/orchestration/web-browser-policy.d.ts +0 -17
- package/dist/orchestration/web-browser-policy.js +0 -39
- package/dist/runtime/context-compression.d.ts +0 -61
- package/dist/runtime/context-compression.js +0 -274
- package/dist/runtime/hook-registry.d.ts +0 -12
- package/dist/runtime/hook-registry.js +0 -53
- package/dist/runtime/memory-hooks.d.ts +0 -23
- package/dist/runtime/memory-hooks.js +0 -65
- package/dist/runtime/tool-eligibility.d.ts +0 -59
- package/dist/runtime/tool-eligibility.js +0 -111
- package/dist/skills/index.d.ts +0 -108
- package/dist/skills/index.js +0 -82
- package/dist/skills/memory-extractor.d.ts +0 -64
- package/dist/skills/memory-extractor.js +0 -173
- package/dist/skills/memory-query-tool.d.ts +0 -43
- package/dist/skills/memory-query-tool.js +0 -127
- package/dist/skills/memory-store.d.ts +0 -66
- package/dist/skills/memory-store.js +0 -228
- package/dist/skills/memory-tool.d.ts +0 -67
- package/dist/skills/memory-tool.js +0 -192
- package/dist/skills/portable-tool.d.ts +0 -71
- package/dist/skills/portable-tool.js +0 -14
- package/dist/skills/qmemory-adapter.d.ts +0 -52
- package/dist/skills/qmemory-adapter.js +0 -165
- package/dist/skills/skill-frontmatter.d.ts +0 -19
- package/dist/skills/skill-frontmatter.js +0 -344
- package/dist/skills/skill-guard.d.ts +0 -23
- package/dist/skills/skill-guard.js +0 -229
- package/dist/skills/skill-loader.d.ts +0 -16
- package/dist/skills/skill-loader.js +0 -303
- package/dist/skills/skill-source.d.ts +0 -119
- package/dist/skills/skill-source.js +0 -126
- package/dist/skills/skill-types.d.ts +0 -199
- package/dist/skills/skill-types.js +0 -6
- package/dist/skills/think-tool.d.ts +0 -16
- package/dist/skills/think-tool.js +0 -59
- package/dist/skills/todo-tool.d.ts +0 -63
- package/dist/skills/todo-tool.js +0 -114
- package/dist/skills/tools/agent-tool.d.ts +0 -91
- package/dist/skills/tools/agent-tool.js +0 -142
- package/dist/skills/tools/apply-patch-tool.d.ts +0 -29
- package/dist/skills/tools/apply-patch-tool.js +0 -184
- package/dist/skills/tools/ask-user-tool.d.ts +0 -80
- package/dist/skills/tools/ask-user-tool.js +0 -121
- package/dist/skills/tools/brief-tool.d.ts +0 -74
- package/dist/skills/tools/brief-tool.js +0 -95
- package/dist/skills/tools/browser-tool.d.ts +0 -114
- package/dist/skills/tools/browser-tool.js +0 -155
- package/dist/skills/tools/checkpoint-tool.d.ts +0 -66
- package/dist/skills/tools/checkpoint-tool.js +0 -102
- package/dist/skills/tools/config-tool.d.ts +0 -63
- package/dist/skills/tools/config-tool.js +0 -143
- package/dist/skills/tools/cron-tool.d.ts +0 -116
- package/dist/skills/tools/cron-tool.js +0 -175
- package/dist/skills/tools/edit-tool.d.ts +0 -43
- package/dist/skills/tools/edit-tool.js +0 -70
- package/dist/skills/tools/exec-tool.d.ts +0 -102
- package/dist/skills/tools/exec-tool.js +0 -133
- package/dist/skills/tools/image-generate-tool.d.ts +0 -62
- package/dist/skills/tools/image-generate-tool.js +0 -67
- package/dist/skills/tools/instructions-tool.d.ts +0 -103
- package/dist/skills/tools/instructions-tool.js +0 -187
- package/dist/skills/tools/lsp-tool.d.ts +0 -153
- package/dist/skills/tools/lsp-tool.js +0 -227
- package/dist/skills/tools/mcp-client-types.d.ts +0 -269
- package/dist/skills/tools/mcp-client-types.js +0 -53
- package/dist/skills/tools/mcp-tool.d.ts +0 -249
- package/dist/skills/tools/mcp-tool.js +0 -503
- package/dist/skills/tools/memory-tool.d.ts +0 -74
- package/dist/skills/tools/memory-tool.js +0 -88
- package/dist/skills/tools/monitor-tool.d.ts +0 -113
- package/dist/skills/tools/monitor-tool.js +0 -131
- package/dist/skills/tools/music-generate-tool.d.ts +0 -55
- package/dist/skills/tools/music-generate-tool.js +0 -62
- package/dist/skills/tools/notify-tool.d.ts +0 -53
- package/dist/skills/tools/notify-tool.js +0 -62
- package/dist/skills/tools/patch-tool.d.ts +0 -45
- package/dist/skills/tools/patch-tool.js +0 -505
- package/dist/skills/tools/pdf-tool.d.ts +0 -66
- package/dist/skills/tools/pdf-tool.js +0 -88
- package/dist/skills/tools/plan-mode-tool.d.ts +0 -59
- package/dist/skills/tools/plan-mode-tool.js +0 -122
- package/dist/skills/tools/read-tool.d.ts +0 -51
- package/dist/skills/tools/read-tool.js +0 -84
- package/dist/skills/tools/repl-tool.d.ts +0 -70
- package/dist/skills/tools/repl-tool.js +0 -69
- package/dist/skills/tools/search-tool.d.ts +0 -112
- package/dist/skills/tools/search-tool.js +0 -225
- package/dist/skills/tools/send-message-tool.d.ts +0 -51
- package/dist/skills/tools/send-message-tool.js +0 -76
- package/dist/skills/tools/skill-list-tool.d.ts +0 -33
- package/dist/skills/tools/skill-list-tool.js +0 -54
- package/dist/skills/tools/skill-manage-tool.d.ts +0 -73
- package/dist/skills/tools/skill-manage-tool.js +0 -153
- package/dist/skills/tools/skill-view-tool.d.ts +0 -37
- package/dist/skills/tools/skill-view-tool.js +0 -72
- package/dist/skills/tools/sleep-tool.d.ts +0 -49
- package/dist/skills/tools/sleep-tool.js +0 -81
- package/dist/skills/tools/structured-output-tool.d.ts +0 -116
- package/dist/skills/tools/structured-output-tool.js +0 -176
- package/dist/skills/tools/task-tool.d.ts +0 -104
- package/dist/skills/tools/task-tool.js +0 -161
- package/dist/skills/tools/team-tool.d.ts +0 -89
- package/dist/skills/tools/team-tool.js +0 -105
- package/dist/skills/tools/tool-search-tool.d.ts +0 -51
- package/dist/skills/tools/tool-search-tool.js +0 -110
- package/dist/skills/tools/tts-tool.d.ts +0 -38
- package/dist/skills/tools/tts-tool.js +0 -45
- package/dist/skills/tools/video-edit-tool.d.ts +0 -69
- package/dist/skills/tools/video-edit-tool.js +0 -74
- package/dist/skills/tools/video-generate-tool.d.ts +0 -62
- package/dist/skills/tools/video-generate-tool.js +0 -66
- package/dist/skills/tools/video-merge-tool.d.ts +0 -105
- package/dist/skills/tools/video-merge-tool.js +0 -92
- package/dist/skills/tools/video-upscale-tool.d.ts +0 -45
- package/dist/skills/tools/video-upscale-tool.js +0 -52
- package/dist/skills/tools/web-fetch-tool.d.ts +0 -78
- package/dist/skills/tools/web-fetch-tool.js +0 -92
- package/dist/skills/tools/web-search-tool.d.ts +0 -57
- package/dist/skills/tools/web-search-tool.js +0 -86
- package/dist/skills/tools/worktree-tool.d.ts +0 -69
- package/dist/skills/tools/worktree-tool.js +0 -147
- package/dist/skills/tools/write-tool.d.ts +0 -45
- package/dist/skills/tools/write-tool.js +0 -81
|
@@ -1,229 +0,0 @@
|
|
|
1
|
-
// ---------------------------------------------------------------------------
|
|
2
|
-
// Skill security scanner — portable, zero external dependencies.
|
|
3
|
-
// Mirrors openclaw/src/security/skill-scanner.ts rules but uses DI deps
|
|
4
|
-
// instead of Node.js fs/path directly.
|
|
5
|
-
// ---------------------------------------------------------------------------
|
|
6
|
-
const LINE_RULES = [
|
|
7
|
-
{
|
|
8
|
-
ruleId: "dangerous-exec",
|
|
9
|
-
severity: "critical",
|
|
10
|
-
message: "Shell command execution detected (child_process)",
|
|
11
|
-
pattern: /\b(exec|execSync|spawn|spawnSync|execFile|execFileSync)\s*\(/,
|
|
12
|
-
requiresContext: /child_process/,
|
|
13
|
-
},
|
|
14
|
-
{
|
|
15
|
-
ruleId: "dynamic-code-execution",
|
|
16
|
-
severity: "critical",
|
|
17
|
-
message: "Dynamic code execution detected",
|
|
18
|
-
pattern: /\beval\s*\(|new\s+Function\s*\(/,
|
|
19
|
-
},
|
|
20
|
-
{
|
|
21
|
-
ruleId: "crypto-mining",
|
|
22
|
-
severity: "critical",
|
|
23
|
-
message: "Possible crypto-mining reference detected",
|
|
24
|
-
pattern: /stratum\+tcp|stratum\+ssl|coinhive|cryptonight|xmrig/i,
|
|
25
|
-
},
|
|
26
|
-
{
|
|
27
|
-
ruleId: "suspicious-network",
|
|
28
|
-
severity: "warn",
|
|
29
|
-
message: "WebSocket connection to non-standard port",
|
|
30
|
-
pattern: /new\s+WebSocket\s*\(\s*["']wss?:\/\/[^"']*:(\d+)/,
|
|
31
|
-
},
|
|
32
|
-
];
|
|
33
|
-
const STANDARD_PORTS = new Set([80, 443, 8080, 8443, 3000]);
|
|
34
|
-
const SOURCE_RULES = [
|
|
35
|
-
{
|
|
36
|
-
ruleId: "potential-exfiltration",
|
|
37
|
-
severity: "warn",
|
|
38
|
-
message: "File read combined with network send — possible data exfiltration",
|
|
39
|
-
pattern: /readFileSync|readFile/,
|
|
40
|
-
requiresContext: /\bfetch\b|\bpost\b|http\.request/i,
|
|
41
|
-
},
|
|
42
|
-
{
|
|
43
|
-
ruleId: "obfuscated-code",
|
|
44
|
-
severity: "warn",
|
|
45
|
-
message: "Hex-encoded string sequence detected (possible obfuscation)",
|
|
46
|
-
pattern: /(\\x[0-9a-fA-F]{2}){6,}/,
|
|
47
|
-
},
|
|
48
|
-
{
|
|
49
|
-
ruleId: "obfuscated-code",
|
|
50
|
-
severity: "warn",
|
|
51
|
-
message: "Large base64 payload with decode call detected (possible obfuscation)",
|
|
52
|
-
pattern: /(?:atob|Buffer\.from)\s*\(\s*["'][A-Za-z0-9+/=]{200,}["']/,
|
|
53
|
-
},
|
|
54
|
-
{
|
|
55
|
-
ruleId: "env-harvesting",
|
|
56
|
-
severity: "critical",
|
|
57
|
-
message: "Environment variable access combined with network send — possible credential harvesting",
|
|
58
|
-
pattern: /process\.env/,
|
|
59
|
-
requiresContext: /\bfetch\b|\bpost\b|http\.request/i,
|
|
60
|
-
},
|
|
61
|
-
];
|
|
62
|
-
// ---------------------------------------------------------------------------
|
|
63
|
-
// Scannable extensions
|
|
64
|
-
// ---------------------------------------------------------------------------
|
|
65
|
-
const SCANNABLE_EXTENSIONS = new Set([
|
|
66
|
-
".js", ".ts", ".mjs", ".cjs", ".mts", ".cts", ".jsx", ".tsx",
|
|
67
|
-
]);
|
|
68
|
-
const DEFAULT_MAX_SCAN_FILES = 500;
|
|
69
|
-
const DEFAULT_MAX_FILE_BYTES = 1024 * 1024;
|
|
70
|
-
// ---------------------------------------------------------------------------
|
|
71
|
-
// Core scanner (operates on source text — no FS dependency)
|
|
72
|
-
// ---------------------------------------------------------------------------
|
|
73
|
-
function truncateEvidence(evidence, maxLen = 120) {
|
|
74
|
-
return evidence.length <= maxLen ? evidence : `${evidence.slice(0, maxLen)}…`;
|
|
75
|
-
}
|
|
76
|
-
/**
|
|
77
|
-
* Scan a source string for security findings.
|
|
78
|
-
* This is a pure function — no file I/O.
|
|
79
|
-
*/
|
|
80
|
-
export function scanSource(source, filePath) {
|
|
81
|
-
const findings = [];
|
|
82
|
-
const lines = source.split("\n");
|
|
83
|
-
const matchedLineRules = new Set();
|
|
84
|
-
// Line rules
|
|
85
|
-
for (const rule of LINE_RULES) {
|
|
86
|
-
if (matchedLineRules.has(rule.ruleId))
|
|
87
|
-
continue;
|
|
88
|
-
if (rule.requiresContext && !rule.requiresContext.test(source))
|
|
89
|
-
continue;
|
|
90
|
-
for (let i = 0; i < lines.length; i++) {
|
|
91
|
-
const line = lines[i];
|
|
92
|
-
const match = rule.pattern.exec(line);
|
|
93
|
-
if (!match)
|
|
94
|
-
continue;
|
|
95
|
-
// Special: suspicious-network checks port number
|
|
96
|
-
if (rule.ruleId === "suspicious-network") {
|
|
97
|
-
const port = parseInt(match[1], 10);
|
|
98
|
-
if (STANDARD_PORTS.has(port))
|
|
99
|
-
continue;
|
|
100
|
-
}
|
|
101
|
-
findings.push({
|
|
102
|
-
ruleId: rule.ruleId,
|
|
103
|
-
severity: rule.severity,
|
|
104
|
-
file: filePath,
|
|
105
|
-
line: i + 1,
|
|
106
|
-
message: rule.message,
|
|
107
|
-
evidence: truncateEvidence(line.trim()),
|
|
108
|
-
});
|
|
109
|
-
matchedLineRules.add(rule.ruleId);
|
|
110
|
-
break; // one finding per line-rule per file
|
|
111
|
-
}
|
|
112
|
-
}
|
|
113
|
-
// Source rules
|
|
114
|
-
const matchedSourceRules = new Set();
|
|
115
|
-
for (const rule of SOURCE_RULES) {
|
|
116
|
-
const ruleKey = `${rule.ruleId}::${rule.message}`;
|
|
117
|
-
if (matchedSourceRules.has(ruleKey))
|
|
118
|
-
continue;
|
|
119
|
-
if (!rule.pattern.test(source))
|
|
120
|
-
continue;
|
|
121
|
-
if (rule.requiresContext && !rule.requiresContext.test(source))
|
|
122
|
-
continue;
|
|
123
|
-
let matchLine = 0;
|
|
124
|
-
let matchEvidence = "";
|
|
125
|
-
for (let i = 0; i < lines.length; i++) {
|
|
126
|
-
if (rule.pattern.test(lines[i])) {
|
|
127
|
-
matchLine = i + 1;
|
|
128
|
-
matchEvidence = lines[i].trim();
|
|
129
|
-
break;
|
|
130
|
-
}
|
|
131
|
-
}
|
|
132
|
-
if (matchLine === 0) {
|
|
133
|
-
matchLine = 1;
|
|
134
|
-
matchEvidence = source.slice(0, 120);
|
|
135
|
-
}
|
|
136
|
-
findings.push({
|
|
137
|
-
ruleId: rule.ruleId,
|
|
138
|
-
severity: rule.severity,
|
|
139
|
-
file: filePath,
|
|
140
|
-
line: matchLine,
|
|
141
|
-
message: rule.message,
|
|
142
|
-
evidence: truncateEvidence(matchEvidence),
|
|
143
|
-
});
|
|
144
|
-
matchedSourceRules.add(ruleKey);
|
|
145
|
-
}
|
|
146
|
-
return findings;
|
|
147
|
-
}
|
|
148
|
-
async function walkDirWithLimit(deps, dirPath, maxFiles) {
|
|
149
|
-
const files = [];
|
|
150
|
-
const stack = [dirPath];
|
|
151
|
-
while (stack.length > 0 && files.length < maxFiles) {
|
|
152
|
-
const currentDir = stack.pop();
|
|
153
|
-
if (!currentDir)
|
|
154
|
-
break;
|
|
155
|
-
let entries;
|
|
156
|
-
try {
|
|
157
|
-
entries = deps.fs.readdirSync(currentDir, { withFileTypes: true });
|
|
158
|
-
}
|
|
159
|
-
catch {
|
|
160
|
-
continue;
|
|
161
|
-
}
|
|
162
|
-
for (const entry of entries) {
|
|
163
|
-
if (files.length >= maxFiles)
|
|
164
|
-
break;
|
|
165
|
-
if (entry.name.startsWith(".") || entry.name === "node_modules")
|
|
166
|
-
continue;
|
|
167
|
-
const fullPath = deps.path.join(currentDir, entry.name);
|
|
168
|
-
if (entry.isDirectory()) {
|
|
169
|
-
stack.push(fullPath);
|
|
170
|
-
}
|
|
171
|
-
else if (SCANNABLE_EXTENSIONS.has(deps.path.extname(entry.name).toLowerCase())) {
|
|
172
|
-
files.push(fullPath);
|
|
173
|
-
}
|
|
174
|
-
}
|
|
175
|
-
}
|
|
176
|
-
return files;
|
|
177
|
-
}
|
|
178
|
-
/**
|
|
179
|
-
* Scan an entire skill directory for security issues.
|
|
180
|
-
* Returns a summary with per-file findings.
|
|
181
|
-
*/
|
|
182
|
-
export async function scanSkillDirectory(deps, skillDir, opts) {
|
|
183
|
-
const maxFiles = Math.max(1, opts?.maxFiles ?? DEFAULT_MAX_SCAN_FILES);
|
|
184
|
-
const maxFileBytes = Math.max(1, opts?.maxFileBytes ?? DEFAULT_MAX_FILE_BYTES);
|
|
185
|
-
const files = opts?.includeFiles && opts.includeFiles.length > 0
|
|
186
|
-
? opts.includeFiles
|
|
187
|
-
: await walkDirWithLimit(deps, skillDir, maxFiles);
|
|
188
|
-
const allFindings = [];
|
|
189
|
-
let scannedFiles = 0;
|
|
190
|
-
for (const filePath of files) {
|
|
191
|
-
try {
|
|
192
|
-
const stat = deps.fs.statSync(filePath);
|
|
193
|
-
if (stat.size > maxFileBytes)
|
|
194
|
-
continue;
|
|
195
|
-
}
|
|
196
|
-
catch {
|
|
197
|
-
continue;
|
|
198
|
-
}
|
|
199
|
-
let source;
|
|
200
|
-
try {
|
|
201
|
-
source = await deps.fs.readFile(filePath, "utf-8");
|
|
202
|
-
}
|
|
203
|
-
catch {
|
|
204
|
-
continue;
|
|
205
|
-
}
|
|
206
|
-
scannedFiles += 1;
|
|
207
|
-
const findings = scanSource(source, filePath);
|
|
208
|
-
allFindings.push(...findings);
|
|
209
|
-
}
|
|
210
|
-
return {
|
|
211
|
-
scannedFiles,
|
|
212
|
-
critical: allFindings.filter((f) => f.severity === "critical").length,
|
|
213
|
-
warn: allFindings.filter((f) => f.severity === "warn").length,
|
|
214
|
-
info: allFindings.filter((f) => f.severity === "info").length,
|
|
215
|
-
findings: allFindings,
|
|
216
|
-
};
|
|
217
|
-
}
|
|
218
|
-
/**
|
|
219
|
-
* Quick check: does this source have any critical findings?
|
|
220
|
-
*/
|
|
221
|
-
export function hasCriticalFindings(source, filePath) {
|
|
222
|
-
return scanSource(source, filePath).some((f) => f.severity === "critical");
|
|
223
|
-
}
|
|
224
|
-
/**
|
|
225
|
-
* Check if a file extension is scannable by the guard.
|
|
226
|
-
*/
|
|
227
|
-
export function isScannable(filePath, pathDeps) {
|
|
228
|
-
return SCANNABLE_EXTENSIONS.has(pathDeps.extname(filePath).toLowerCase());
|
|
229
|
-
}
|
|
@@ -1,16 +0,0 @@
|
|
|
1
|
-
import type { SkillEntry, SkillLoadLimits, SkillLoaderDeps, SkillSnapshot } from "./skill-types.js";
|
|
2
|
-
/**
|
|
3
|
-
* Discover all skills from configured search paths, applying precedence merge
|
|
4
|
-
* and size/count limits. Returns fully parsed `SkillEntry` objects.
|
|
5
|
-
*/
|
|
6
|
-
export declare function loadSkillEntries(deps: SkillLoaderDeps): SkillEntry[];
|
|
7
|
-
/**
|
|
8
|
-
* Filter skill entries by a name-based filter list.
|
|
9
|
-
* If filter is undefined or empty, returns all entries.
|
|
10
|
-
*/
|
|
11
|
-
export declare function filterSkillEntries(entries: SkillEntry[], skillFilter?: string[]): SkillEntry[];
|
|
12
|
-
/**
|
|
13
|
-
* Build a system-prompt-embeddable skill snapshot.
|
|
14
|
-
* Applies count and character limits from the provided config.
|
|
15
|
-
*/
|
|
16
|
-
export declare function buildSkillSnapshot(entries: SkillEntry[], limits?: SkillLoadLimits, skillFilter?: string[]): SkillSnapshot;
|
|
@@ -1,303 +0,0 @@
|
|
|
1
|
-
// ---------------------------------------------------------------------------
|
|
2
|
-
// Skill discovery & loading — portable, DI-based.
|
|
3
|
-
// Mirrors openclaw/src/agents/skills/workspace.ts logic but decoupled from
|
|
4
|
-
// Node.js fs/path via SkillLoaderDeps.
|
|
5
|
-
// ---------------------------------------------------------------------------
|
|
6
|
-
import { parseFrontmatter, resolveSkillMetadata, resolveSkillInvocationPolicy } from "./skill-frontmatter.js";
|
|
7
|
-
// ---------------------------------------------------------------------------
|
|
8
|
-
// Default limits (match openclaw upstream)
|
|
9
|
-
// ---------------------------------------------------------------------------
|
|
10
|
-
const DEFAULT_LIMITS = {
|
|
11
|
-
maxCandidatesPerRoot: 300,
|
|
12
|
-
maxSkillsLoadedPerSource: 200,
|
|
13
|
-
maxSkillsInPrompt: 150,
|
|
14
|
-
maxSkillsPromptChars: 30_000,
|
|
15
|
-
maxSkillFileBytes: 256_000,
|
|
16
|
-
};
|
|
17
|
-
function resolveLimits(input) {
|
|
18
|
-
return {
|
|
19
|
-
maxCandidatesPerRoot: input?.maxCandidatesPerRoot ?? DEFAULT_LIMITS.maxCandidatesPerRoot,
|
|
20
|
-
maxSkillsLoadedPerSource: input?.maxSkillsLoadedPerSource ?? DEFAULT_LIMITS.maxSkillsLoadedPerSource,
|
|
21
|
-
maxSkillsInPrompt: input?.maxSkillsInPrompt ?? DEFAULT_LIMITS.maxSkillsInPrompt,
|
|
22
|
-
maxSkillsPromptChars: input?.maxSkillsPromptChars ?? DEFAULT_LIMITS.maxSkillsPromptChars,
|
|
23
|
-
maxSkillFileBytes: input?.maxSkillFileBytes ?? DEFAULT_LIMITS.maxSkillFileBytes,
|
|
24
|
-
};
|
|
25
|
-
}
|
|
26
|
-
// ---------------------------------------------------------------------------
|
|
27
|
-
// Path containment guard
|
|
28
|
-
// ---------------------------------------------------------------------------
|
|
29
|
-
function isPathInside(parentRealPath, childRealPath, sep) {
|
|
30
|
-
const normalizedParent = parentRealPath.endsWith(sep) ? parentRealPath : parentRealPath + sep;
|
|
31
|
-
return childRealPath === parentRealPath || childRealPath.startsWith(normalizedParent);
|
|
32
|
-
}
|
|
33
|
-
function resolveContainedPath(deps, rootRealPath, candidatePath) {
|
|
34
|
-
try {
|
|
35
|
-
const real = deps.fs.realpathSync(candidatePath);
|
|
36
|
-
return isPathInside(rootRealPath, real, deps.path.sep) ? real : null;
|
|
37
|
-
}
|
|
38
|
-
catch {
|
|
39
|
-
return null;
|
|
40
|
-
}
|
|
41
|
-
}
|
|
42
|
-
// ---------------------------------------------------------------------------
|
|
43
|
-
// Directory listing helpers
|
|
44
|
-
// ---------------------------------------------------------------------------
|
|
45
|
-
function listChildDirectories(deps, dir) {
|
|
46
|
-
try {
|
|
47
|
-
const entries = deps.fs.readdirSync(dir, { withFileTypes: true });
|
|
48
|
-
const dirs = [];
|
|
49
|
-
for (const entry of entries) {
|
|
50
|
-
if (entry.name.startsWith(".") || entry.name === "node_modules")
|
|
51
|
-
continue;
|
|
52
|
-
if (entry.isDirectory()) {
|
|
53
|
-
dirs.push(entry.name);
|
|
54
|
-
continue;
|
|
55
|
-
}
|
|
56
|
-
if (entry.isSymbolicLink()) {
|
|
57
|
-
try {
|
|
58
|
-
const fullPath = deps.path.join(dir, entry.name);
|
|
59
|
-
if (deps.fs.statSync(fullPath).isDirectory()) {
|
|
60
|
-
dirs.push(entry.name);
|
|
61
|
-
}
|
|
62
|
-
}
|
|
63
|
-
catch {
|
|
64
|
-
// broken symlink — skip
|
|
65
|
-
}
|
|
66
|
-
}
|
|
67
|
-
}
|
|
68
|
-
return dirs;
|
|
69
|
-
}
|
|
70
|
-
catch {
|
|
71
|
-
return [];
|
|
72
|
-
}
|
|
73
|
-
}
|
|
74
|
-
/**
|
|
75
|
-
* Detect nested `skills/` subfolder. If `dir/skills/<x>/SKILL.md` exists,
|
|
76
|
-
* treat `dir/skills/` as the real root.
|
|
77
|
-
*/
|
|
78
|
-
function resolveNestedSkillsRoot(deps, dir, maxEntriesToScan) {
|
|
79
|
-
const nested = deps.path.join(dir, "skills");
|
|
80
|
-
try {
|
|
81
|
-
if (!deps.fs.existsSync(nested) || !deps.fs.statSync(nested).isDirectory()) {
|
|
82
|
-
return dir;
|
|
83
|
-
}
|
|
84
|
-
}
|
|
85
|
-
catch {
|
|
86
|
-
return dir;
|
|
87
|
-
}
|
|
88
|
-
const nestedDirs = listChildDirectories(deps, nested);
|
|
89
|
-
const toScan = nestedDirs.slice(0, Math.min(nestedDirs.length, maxEntriesToScan));
|
|
90
|
-
for (const name of toScan) {
|
|
91
|
-
const skillMd = deps.path.join(nested, name, "SKILL.md");
|
|
92
|
-
if (deps.fs.existsSync(skillMd)) {
|
|
93
|
-
return nested;
|
|
94
|
-
}
|
|
95
|
-
}
|
|
96
|
-
return dir;
|
|
97
|
-
}
|
|
98
|
-
// ---------------------------------------------------------------------------
|
|
99
|
-
// Single-source skill loading
|
|
100
|
-
// ---------------------------------------------------------------------------
|
|
101
|
-
function loadSkillsFromSourceDir(deps, dir, source, limits) {
|
|
102
|
-
const rootDir = deps.path.resolve(dir);
|
|
103
|
-
let rootRealPath;
|
|
104
|
-
try {
|
|
105
|
-
rootRealPath = deps.fs.realpathSync(rootDir);
|
|
106
|
-
}
|
|
107
|
-
catch {
|
|
108
|
-
return [];
|
|
109
|
-
}
|
|
110
|
-
const baseDir = resolveNestedSkillsRoot(deps, rootDir, limits.maxCandidatesPerRoot);
|
|
111
|
-
const baseDirRealPath = resolveContainedPath(deps, rootRealPath, baseDir);
|
|
112
|
-
if (!baseDirRealPath)
|
|
113
|
-
return [];
|
|
114
|
-
// Case 1: root itself is a single skill
|
|
115
|
-
const rootSkillMd = deps.path.join(baseDir, "SKILL.md");
|
|
116
|
-
if (deps.fs.existsSync(rootSkillMd)) {
|
|
117
|
-
if (!resolveContainedPath(deps, baseDirRealPath, rootSkillMd))
|
|
118
|
-
return [];
|
|
119
|
-
try {
|
|
120
|
-
const size = deps.fs.statSync(rootSkillMd).size;
|
|
121
|
-
if (size > limits.maxSkillFileBytes)
|
|
122
|
-
return [];
|
|
123
|
-
}
|
|
124
|
-
catch {
|
|
125
|
-
return [];
|
|
126
|
-
}
|
|
127
|
-
return [buildWorkspaceSkill(deps, rootSkillMd, baseDir, source)].filter((s) => s !== null);
|
|
128
|
-
}
|
|
129
|
-
// Case 2: enumerate child directories for `<child>/SKILL.md`
|
|
130
|
-
const childDirs = listChildDirectories(deps, baseDir);
|
|
131
|
-
const maxCandidates = Math.max(0, limits.maxSkillsLoadedPerSource);
|
|
132
|
-
const limitedChildren = childDirs.slice().sort().slice(0, maxCandidates);
|
|
133
|
-
const skills = [];
|
|
134
|
-
for (const name of limitedChildren) {
|
|
135
|
-
const skillDir = deps.path.join(baseDir, name);
|
|
136
|
-
if (!resolveContainedPath(deps, baseDirRealPath, skillDir))
|
|
137
|
-
continue;
|
|
138
|
-
const skillMd = deps.path.join(skillDir, "SKILL.md");
|
|
139
|
-
if (!deps.fs.existsSync(skillMd))
|
|
140
|
-
continue;
|
|
141
|
-
if (!resolveContainedPath(deps, baseDirRealPath, skillMd))
|
|
142
|
-
continue;
|
|
143
|
-
try {
|
|
144
|
-
const size = deps.fs.statSync(skillMd).size;
|
|
145
|
-
if (size > limits.maxSkillFileBytes)
|
|
146
|
-
continue;
|
|
147
|
-
}
|
|
148
|
-
catch {
|
|
149
|
-
continue;
|
|
150
|
-
}
|
|
151
|
-
const skill = buildWorkspaceSkill(deps, skillMd, skillDir, source);
|
|
152
|
-
if (skill)
|
|
153
|
-
skills.push(skill);
|
|
154
|
-
if (skills.length >= limits.maxSkillsLoadedPerSource)
|
|
155
|
-
break;
|
|
156
|
-
}
|
|
157
|
-
return skills;
|
|
158
|
-
}
|
|
159
|
-
function buildWorkspaceSkill(deps, filePath, baseDir, source) {
|
|
160
|
-
try {
|
|
161
|
-
const content = deps.fs.readFileSync(filePath, "utf-8");
|
|
162
|
-
const fm = parseFrontmatter(content);
|
|
163
|
-
const name = fm["name"] ?? deps.path.resolve(baseDir).split(deps.path.sep).pop() ?? "unknown";
|
|
164
|
-
return {
|
|
165
|
-
name,
|
|
166
|
-
description: fm["description"],
|
|
167
|
-
source,
|
|
168
|
-
filePath: deps.path.resolve(filePath),
|
|
169
|
-
baseDir: deps.path.resolve(baseDir),
|
|
170
|
-
primaryEnv: fm["primaryEnv"] ?? fm["primary-env"],
|
|
171
|
-
requiredEnv: fm["required-env"]?.split(",").map((s) => s.trim()).filter(Boolean),
|
|
172
|
-
};
|
|
173
|
-
}
|
|
174
|
-
catch {
|
|
175
|
-
return null;
|
|
176
|
-
}
|
|
177
|
-
}
|
|
178
|
-
// ---------------------------------------------------------------------------
|
|
179
|
-
// Public API
|
|
180
|
-
// ---------------------------------------------------------------------------
|
|
181
|
-
/**
|
|
182
|
-
* Discover all skills from configured search paths, applying precedence merge
|
|
183
|
-
* and size/count limits. Returns fully parsed `SkillEntry` objects.
|
|
184
|
-
*/
|
|
185
|
-
export function loadSkillEntries(deps) {
|
|
186
|
-
const limits = resolveLimits(deps.limits);
|
|
187
|
-
const { searchPaths } = deps;
|
|
188
|
-
const sources = [];
|
|
189
|
-
// Order: extra < bundled < managed < personalAgents < projectAgents < workspace
|
|
190
|
-
if (searchPaths.extra) {
|
|
191
|
-
for (const dir of searchPaths.extra) {
|
|
192
|
-
sources.push({ dir, source: "extra" });
|
|
193
|
-
}
|
|
194
|
-
}
|
|
195
|
-
if (searchPaths.bundled) {
|
|
196
|
-
sources.push({ dir: searchPaths.bundled, source: "bundled" });
|
|
197
|
-
}
|
|
198
|
-
if (searchPaths.managed) {
|
|
199
|
-
sources.push({ dir: searchPaths.managed, source: "managed" });
|
|
200
|
-
}
|
|
201
|
-
if (searchPaths.personalAgents) {
|
|
202
|
-
sources.push({ dir: searchPaths.personalAgents, source: "agents-personal" });
|
|
203
|
-
}
|
|
204
|
-
if (searchPaths.projectAgents) {
|
|
205
|
-
sources.push({ dir: searchPaths.projectAgents, source: "agents-project" });
|
|
206
|
-
}
|
|
207
|
-
if (searchPaths.workspace) {
|
|
208
|
-
sources.push({ dir: searchPaths.workspace, source: "workspace" });
|
|
209
|
-
}
|
|
210
|
-
// Merge with later sources overriding earlier ones (by name)
|
|
211
|
-
const merged = new Map();
|
|
212
|
-
for (const src of sources) {
|
|
213
|
-
const skills = loadSkillsFromSourceDir(deps, src.dir, src.source, limits);
|
|
214
|
-
for (const skill of skills) {
|
|
215
|
-
merged.set(skill.name, skill);
|
|
216
|
-
}
|
|
217
|
-
}
|
|
218
|
-
// Build entries with parsed metadata
|
|
219
|
-
const entries = [];
|
|
220
|
-
for (const skill of merged.values()) {
|
|
221
|
-
let frontmatter = parseFrontmatter("");
|
|
222
|
-
try {
|
|
223
|
-
const raw = deps.fs.readFileSync(skill.filePath, "utf-8");
|
|
224
|
-
frontmatter = parseFrontmatter(raw);
|
|
225
|
-
}
|
|
226
|
-
catch {
|
|
227
|
-
// ignore malformed
|
|
228
|
-
}
|
|
229
|
-
entries.push({
|
|
230
|
-
skill,
|
|
231
|
-
frontmatter,
|
|
232
|
-
metadata: resolveSkillMetadata(frontmatter),
|
|
233
|
-
invocation: resolveSkillInvocationPolicy(frontmatter),
|
|
234
|
-
});
|
|
235
|
-
}
|
|
236
|
-
return entries;
|
|
237
|
-
}
|
|
238
|
-
/**
|
|
239
|
-
* Filter skill entries by a name-based filter list.
|
|
240
|
-
* If filter is undefined or empty, returns all entries.
|
|
241
|
-
*/
|
|
242
|
-
export function filterSkillEntries(entries, skillFilter) {
|
|
243
|
-
if (!skillFilter || skillFilter.length === 0)
|
|
244
|
-
return entries;
|
|
245
|
-
const normalized = new Set(skillFilter.map((s) => s.trim().toLowerCase()).filter(Boolean));
|
|
246
|
-
if (normalized.size === 0)
|
|
247
|
-
return [];
|
|
248
|
-
return entries.filter((e) => normalized.has(e.skill.name.toLowerCase()));
|
|
249
|
-
}
|
|
250
|
-
/**
|
|
251
|
-
* Build a system-prompt-embeddable skill snapshot.
|
|
252
|
-
* Applies count and character limits from the provided config.
|
|
253
|
-
*/
|
|
254
|
-
export function buildSkillSnapshot(entries, limits, skillFilter) {
|
|
255
|
-
const resolved = resolveLimits(limits);
|
|
256
|
-
const filtered = filterSkillEntries(entries, skillFilter);
|
|
257
|
-
// Truncate by count first
|
|
258
|
-
let promptSkills = filtered.slice(0, Math.max(0, resolved.maxSkillsInPrompt));
|
|
259
|
-
// Format function for prompt
|
|
260
|
-
const formatForPrompt = (skills) => {
|
|
261
|
-
if (skills.length === 0)
|
|
262
|
-
return "";
|
|
263
|
-
const lines = [];
|
|
264
|
-
for (const entry of skills) {
|
|
265
|
-
const s = entry.skill;
|
|
266
|
-
const meta = entry.metadata;
|
|
267
|
-
const desc = s.description ? ` — ${s.description}` : "";
|
|
268
|
-
const env = s.primaryEnv ? ` [${s.primaryEnv}]` : "";
|
|
269
|
-
const emoji = meta?.emoji ? `${meta.emoji} ` : "";
|
|
270
|
-
lines.push(`- ${emoji}**${s.name}**${env}${desc}`);
|
|
271
|
-
// Compact path with ~ prefix
|
|
272
|
-
lines.push(` File: ${s.filePath}`);
|
|
273
|
-
}
|
|
274
|
-
return lines.join("\n");
|
|
275
|
-
};
|
|
276
|
-
// Truncate by character budget via binary search
|
|
277
|
-
let prompt = formatForPrompt(promptSkills);
|
|
278
|
-
if (prompt.length > resolved.maxSkillsPromptChars) {
|
|
279
|
-
let lo = 0;
|
|
280
|
-
let hi = promptSkills.length;
|
|
281
|
-
while (lo < hi) {
|
|
282
|
-
const mid = Math.ceil((lo + hi) / 2);
|
|
283
|
-
const candidate = formatForPrompt(promptSkills.slice(0, mid));
|
|
284
|
-
if (candidate.length <= resolved.maxSkillsPromptChars) {
|
|
285
|
-
lo = mid;
|
|
286
|
-
}
|
|
287
|
-
else {
|
|
288
|
-
hi = mid - 1;
|
|
289
|
-
}
|
|
290
|
-
}
|
|
291
|
-
promptSkills = promptSkills.slice(0, lo);
|
|
292
|
-
prompt = formatForPrompt(promptSkills);
|
|
293
|
-
}
|
|
294
|
-
return {
|
|
295
|
-
prompt,
|
|
296
|
-
skills: promptSkills.map((e) => ({
|
|
297
|
-
name: e.skill.name,
|
|
298
|
-
primaryEnv: e.skill.primaryEnv,
|
|
299
|
-
requiredEnv: e.skill.requiredEnv,
|
|
300
|
-
})),
|
|
301
|
-
skillFilter,
|
|
302
|
-
};
|
|
303
|
-
}
|
|
@@ -1,119 +0,0 @@
|
|
|
1
|
-
import type { SkillScanSummary } from "./skill-types.js";
|
|
2
|
-
/**
|
|
3
|
-
* Trust level for a skill based on its origin.
|
|
4
|
-
* Higher trust = fewer restrictions.
|
|
5
|
-
*/
|
|
6
|
-
export type TrustLevel = "builtin" | "verified" | "community" | "unknown";
|
|
7
|
-
/**
|
|
8
|
-
* Result of a skill security scan decision.
|
|
9
|
-
*/
|
|
10
|
-
export type ScanVerdict = "pass" | "quarantine" | "reject";
|
|
11
|
-
/**
|
|
12
|
-
* A resolved skill source descriptor.
|
|
13
|
-
*/
|
|
14
|
-
export interface SkillSourceDescriptor {
|
|
15
|
-
/** Source type. */
|
|
16
|
-
kind: "bundled" | "github" | "npm" | "local" | "tap";
|
|
17
|
-
/** Human-readable identifier, e.g. "github:user/repo" or "npm:@scope/pkg". */
|
|
18
|
-
identifier: string;
|
|
19
|
-
/** Trust level derived from source type + verification status. */
|
|
20
|
-
trust: TrustLevel;
|
|
21
|
-
/** Version constraint (semver or tag). */
|
|
22
|
-
version?: string;
|
|
23
|
-
}
|
|
24
|
-
/**
|
|
25
|
-
* Result of fetching a skill from a remote source.
|
|
26
|
-
*/
|
|
27
|
-
export interface SkillFetchResult {
|
|
28
|
-
/** Local directory where fetched content was placed. */
|
|
29
|
-
quarantineDir: string;
|
|
30
|
-
/** Manifest metadata if found. */
|
|
31
|
-
name?: string;
|
|
32
|
-
version?: string;
|
|
33
|
-
}
|
|
34
|
-
/**
|
|
35
|
-
* Dependencies for the skill fetch step.
|
|
36
|
-
* Consumers inject actual HTTP/git/npm implementations.
|
|
37
|
-
*/
|
|
38
|
-
export interface SkillFetchDeps {
|
|
39
|
-
/**
|
|
40
|
-
* Download / clone / extract a skill from its source into a quarantine dir.
|
|
41
|
-
* Must NOT place content in the final install location.
|
|
42
|
-
*/
|
|
43
|
-
fetch(source: SkillSourceDescriptor, quarantineDir: string): Promise<SkillFetchResult>;
|
|
44
|
-
}
|
|
45
|
-
/**
|
|
46
|
-
* Dependencies for the skill storage step.
|
|
47
|
-
* Manages the local skill directory and registry.
|
|
48
|
-
*/
|
|
49
|
-
export interface SkillStorageDeps {
|
|
50
|
-
/** Root directory for installed skills (e.g. ~/.xiaozhiclaw/skills/). */
|
|
51
|
-
skillsDir: string;
|
|
52
|
-
/** Registry subdirectory (e.g. ~/.xiaozhiclaw/skills/.registry/). */
|
|
53
|
-
registryDir: string;
|
|
54
|
-
/** Move content from quarantine to final location. */
|
|
55
|
-
moveToInstallDir(quarantineDir: string, skillName: string): Promise<string>;
|
|
56
|
-
/** Remove an installed skill directory. */
|
|
57
|
-
removeSkill(skillName: string): Promise<void>;
|
|
58
|
-
/** Read the registry lock file. */
|
|
59
|
-
readLockfile(): Promise<SkillLockfile>;
|
|
60
|
-
/** Write the registry lock file. */
|
|
61
|
-
writeLockfile(lockfile: SkillLockfile): Promise<void>;
|
|
62
|
-
}
|
|
63
|
-
/**
|
|
64
|
-
* Single entry in the skill lockfile.
|
|
65
|
-
*/
|
|
66
|
-
export interface SkillLockEntry {
|
|
67
|
-
name: string;
|
|
68
|
-
source: SkillSourceDescriptor;
|
|
69
|
-
installedAt: string;
|
|
70
|
-
scanVerdict: ScanVerdict;
|
|
71
|
-
scanSummary?: SkillScanSummary;
|
|
72
|
-
/** Integrity hash of the SKILL.md content (sha256). */
|
|
73
|
-
integrity?: string;
|
|
74
|
-
}
|
|
75
|
-
/**
|
|
76
|
-
* Complete lockfile for all installed skills.
|
|
77
|
-
*/
|
|
78
|
-
export interface SkillLockfile {
|
|
79
|
-
version: 1;
|
|
80
|
-
entries: Record<string, SkillLockEntry>;
|
|
81
|
-
}
|
|
82
|
-
/**
|
|
83
|
-
* Result of a skill install operation.
|
|
84
|
-
*/
|
|
85
|
-
export interface SkillInstallResult {
|
|
86
|
-
success: boolean;
|
|
87
|
-
name: string;
|
|
88
|
-
installDir?: string;
|
|
89
|
-
verdict: ScanVerdict;
|
|
90
|
-
scanSummary?: SkillScanSummary;
|
|
91
|
-
error?: string;
|
|
92
|
-
}
|
|
93
|
-
/**
|
|
94
|
-
* Orchestrate the full install pipeline:
|
|
95
|
-
* Fetch → Quarantine → Scan → Validate → Move → Lock
|
|
96
|
-
*
|
|
97
|
-
* This function is pure orchestration — actual I/O is delegated to deps.
|
|
98
|
-
*/
|
|
99
|
-
export declare function installSkill(params: {
|
|
100
|
-
source: SkillSourceDescriptor;
|
|
101
|
-
fetchDeps: SkillFetchDeps;
|
|
102
|
-
storageDeps: SkillStorageDeps;
|
|
103
|
-
scanSkillDir: (dir: string) => Promise<SkillScanSummary>;
|
|
104
|
-
quarantineDir: string;
|
|
105
|
-
}): Promise<SkillInstallResult>;
|
|
106
|
-
/**
|
|
107
|
-
* Uninstall a skill: remove from disk + update lockfile.
|
|
108
|
-
*/
|
|
109
|
-
export declare function uninstallSkill(params: {
|
|
110
|
-
skillName: string;
|
|
111
|
-
storageDeps: SkillStorageDeps;
|
|
112
|
-
}): Promise<{
|
|
113
|
-
success: boolean;
|
|
114
|
-
error?: string;
|
|
115
|
-
}>;
|
|
116
|
-
/**
|
|
117
|
-
* Create an empty lockfile.
|
|
118
|
-
*/
|
|
119
|
-
export declare function createEmptyLockfile(): SkillLockfile;
|