opencode-api-security-testing 3.0.2 → 4.0.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.
Files changed (3) hide show
  1. package/package.json +6 -18
  2. package/src/index.ts +84 -277
  3. package/postinstall.mjs +0 -130
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "opencode-api-security-testing",
3
- "version": "3.0.2",
4
- "description": "API Security Testing Plugin for OpenCode - Automated vulnerability scanning and penetration testing",
3
+ "version": "4.0.0",
4
+ "description": "API Security Testing Plugin for OpenCode - Tools for vulnerability scanning",
5
5
  "type": "module",
6
6
  "main": "src/index.ts",
7
7
  "files": [
@@ -9,33 +9,21 @@
9
9
  "agents/",
10
10
  "core/",
11
11
  "references/",
12
- "SKILL.md",
13
- "postinstall.mjs"
12
+ "SKILL.md"
14
13
  ],
15
- "scripts": {
16
- "postinstall": "node postinstall.mjs"
17
- },
18
14
  "keywords": [
19
15
  "opencode",
20
16
  "opencode-plugin",
21
17
  "security",
22
- "api-security",
23
- "pentest",
24
- "vulnerability-scanning"
18
+ "api-security"
25
19
  ],
26
20
  "author": "steveopen1",
27
21
  "license": "MIT",
28
22
  "repository": {
29
23
  "type": "git",
30
- "url": "https://github.com/steveopen1/skill-play"
24
+ "url": "git+https://github.com/steveopen1/skill-play.git"
31
25
  },
32
- "homepage": "https://github.com/steveopen1/skill-play/tree/main/agent-plugins/OPENCODE/api-security-testing",
33
26
  "peerDependencies": {
34
- "@opencode-ai/plugin": "^1.1.19",
35
- "@opencode-ai/sdk": "^1.1.19"
36
- },
37
- "dependencies": {
38
- "@opencode-ai/plugin": "^1.1.19",
39
- "@opencode-ai/sdk": "^1.1.19"
27
+ "@opencode-ai/plugin": "^1.1.19"
40
28
  }
41
29
  }
package/src/index.ts CHANGED
@@ -1,368 +1,175 @@
1
1
  import type { Plugin } from "@opencode-ai/plugin";
2
2
  import { tool } from "@opencode-ai/plugin";
3
- import type { AgentConfig } from "@opencode-ai/sdk";
4
- import { join } from "path";
5
- import { existsSync } from "fs";
6
3
 
7
- const SKILL_DIR = "skills/api-security-testing";
8
- const CORE_DIR = `${SKILL_DIR}/core`;
9
-
10
- function getSkillPath(ctx: { directory: string }): string {
11
- return join(ctx.directory, SKILL_DIR);
12
- }
13
-
14
- function getCorePath(ctx: { directory: string }): string {
15
- return join(ctx.directory, CORE_DIR);
16
- }
17
-
18
- function checkDeps(ctx: { directory: string }): string {
19
- const skillPath = getSkillPath(ctx);
20
- const reqFile = join(skillPath, "requirements.txt");
21
- if (existsSync(reqFile)) {
22
- return `pip install -q -r "${reqFile}" 2>/dev/null; `;
23
- }
24
- return "";
4
+ function getCorePath(directory: string): string {
5
+ return `${directory}/skills/api-security-testing/core`;
25
6
  }
26
7
 
27
- const CYBER_SUPERVISOR_PROMPT = `你是 API 安全测试的**赛博监工**,代号"P9"。
28
-
29
- ## 职责
30
-
31
- 1. **永不停止** - 任何线索都要追到底
32
- 2. **自动化编排** - 不等待用户,主动推进
33
- 3. **智能委派** - 识别任务类型,委派给最合适的子 agent
34
- 4. **压力升级** - 遇到失败自动换方法 (L1-L4)
35
-
36
- ## 可用子 Agent
37
-
38
- | 子 Agent | 职责 |
39
- |---------|------|
40
- | @api-probing-miner | 漏洞挖掘 |
41
- | @api-resource-specialist | 端点发现 |
42
- | @api-vuln-verifier | 漏洞验证 |
43
-
44
- ## 可用工具
45
-
46
- | 工具 | 用途 |
47
- |------|------|
48
- | api_security_scan | 完整扫描 |
49
- | api_fuzz_test | 模糊测试 |
50
- | browser_collect | 浏览器采集 |
51
- | js_parse | JS分析 |
52
- | graphql_test | GraphQL测试 |
53
- | cloud_storage_test | 云存储测试 |
54
- | vuln_verify | 漏洞验证 |
55
- | sqli_test | SQL注入测试 |
56
- | idor_test | IDOR测试 |
57
- | auth_test | 认证测试`;
58
-
59
- const PROBING_MINER_PROMPT = `你是**API漏洞挖掘专家**,专注于发现和验证安全漏洞。
60
-
61
- ## 职责
62
-
63
- 1. **针对性测试** - 根据端点特征选择最佳测试方法
64
- 2. **快速验证** - 确认漏洞存在
65
- 3. **PoC 生成** - 提供可执行的测试命令
66
-
67
- ## 测试方法库
68
-
69
- ### SQL 注入
70
- - 布尔盲注: ' OR 1=1 --
71
- - 联合查询: ' UNION SELECT NULL--
72
- - 错误注入: ' AND 1=CONVERT(int,...)--
73
- - 时间盲注: '; WAITFOR DELAY '00:00:05'--
74
-
75
- ### IDOR
76
- - 替换 ID: /api/user/1 → /api/user/2
77
- - 水平/垂直越权测试
78
-
79
- ### JWT
80
- - 空算法: alg: none
81
- - 密钥混淆: HS256 → HS512`;
82
-
83
- const RESOURCE_SPECIALIST_PROMPT = `你是**API资源探测专家**,专注于发现和采集 API 端点。
84
-
85
- ## 职责
86
-
87
- 1. **全面发现** - 不遗漏任何端点
88
- 2. **动态采集** - 拦截真实请求
89
- 3. **静态分析** - 提取 API 模式
90
-
91
- ## 采集技术
92
-
93
- ### 1. 浏览器动态采集
94
- 使用 browser_collect 拦截 XHR/Fetch 请求
95
-
96
- ### 2. JS 静态分析
97
- 使用 js_parse 解析 JS 文件
98
-
99
- ### 3. 目录探测
100
- 常见路径: /api/v1/*, /graphql, /swagger, /.well-known/*`;
101
-
102
- const VULN_VERIFIER_PROMPT = `你是**漏洞验证专家**,专注于验证和确认安全漏洞。
103
-
104
- ## 职责
105
-
106
- 1. **快速验证** - 确认漏洞是否存在
107
- 2. **风险评估** - 判断实际影响
108
- 3. **PoC 生成** - 提供可执行的证明`;
109
-
110
8
  const ApiSecurityTestingPlugin: Plugin = async (ctx) => {
111
- console.log("[api-security-testing] Plugin loaded");
112
-
113
9
  return {
114
10
  tool: {
115
11
  api_security_scan: tool({
116
- description: "完整 API 安全扫描。参数: target(目标URL), scan_type(full/quick/targeted)",
12
+ description: "完整 API 安全扫描",
117
13
  args: {
118
14
  target: tool.schema.string(),
119
15
  scan_type: tool.schema.enum(["full", "quick", "targeted"]).optional(),
120
16
  },
121
17
  async execute(args, ctx) {
122
- const deps = checkDeps(ctx);
123
- const corePath = getCorePath(ctx);
124
- const cmd = `${deps}python3 -c "
18
+ const corePath = getCorePath(ctx.directory);
19
+ const script = `
125
20
  import sys
126
21
  sys.path.insert(0, '${corePath}')
127
- from deep_api_tester_v55 import DeepAPITesterV55
128
- tester = DeepAPITesterV55(target='${args.target}', headless=True)
129
- results = tester.run_test()
130
- print(results)
131
- "`;
132
- const result = await ctx.$`${cmd}`;
22
+ try:
23
+ from deep_api_tester_v55 import DeepAPITesterV55
24
+ tester = DeepAPITesterV55(target='${args.target}', headless=True)
25
+ print(tester.run_test())
26
+ except Exception as e:
27
+ print(f"Error: {e}")
28
+ `;
29
+ const result = await ctx.$`python3 -c ${script}`;
133
30
  return result.toString();
134
31
  },
135
32
  }),
136
33
 
137
34
  api_fuzz_test: tool({
138
- description: "API 模糊测试。参数: endpoint(端点URL), method(HTTP方法)",
35
+ description: "API 模糊测试",
139
36
  args: {
140
37
  endpoint: tool.schema.string(),
141
38
  method: tool.schema.enum(["GET", "POST", "PUT", "DELETE", "PATCH"]).optional(),
142
39
  },
143
40
  async execute(args, ctx) {
144
- const deps = checkDeps(ctx);
145
- const corePath = getCorePath(ctx);
146
- const cmd = `${deps}python3 -c "
41
+ const corePath = getCorePath(ctx.directory);
42
+ const script = `
147
43
  import sys
148
44
  sys.path.insert(0, '${corePath}')
149
- from api_fuzzer import APIFuzzer
150
- fuzzer = APIFuzzer('${args.endpoint}')
151
- results = fuzzer.fuzz(method='${args.method || 'GET'}')
152
- print(results)
153
- "`;
154
- const result = await ctx.$`${cmd}`;
45
+ try:
46
+ from api_fuzzer import APIFuzzer
47
+ fuzzer = APIFuzzer('${args.endpoint}')
48
+ print(fuzzer.fuzz(method='${args.method || "GET"}'))
49
+ except Exception as e:
50
+ print(f"Error: {e}")
51
+ `;
52
+ const result = await ctx.$`python3 -c ${script}`;
155
53
  return result.toString();
156
54
  },
157
55
  }),
158
56
 
159
57
  vuln_verify: tool({
160
- description: "漏洞验证。参数: vuln_type(漏洞类型), endpoint(端点)",
58
+ description: "漏洞验证",
161
59
  args: {
162
60
  vuln_type: tool.schema.string(),
163
61
  endpoint: tool.schema.string(),
164
62
  },
165
63
  async execute(args, ctx) {
166
- const deps = checkDeps(ctx);
167
- const corePath = getCorePath(ctx);
168
- const cmd = `${deps}python3 -c "
64
+ const corePath = getCorePath(ctx.directory);
65
+ const script = `
169
66
  import sys
170
67
  sys.path.insert(0, '${corePath}')
171
- from verifiers.vuln_verifier import VulnVerifier
172
- verifier = VulnVerifier()
173
- result = verifier.verify('${args.vuln_type}', '${args.endpoint}')
174
- print(result)
175
- "`;
176
- const result = await ctx.$`${cmd}`;
68
+ try:
69
+ from verifiers.vuln_verifier import VulnVerifier
70
+ verifier = VulnVerifier()
71
+ print(verifier.verify('${args.vuln_type}', '${args.endpoint}'))
72
+ except Exception as e:
73
+ print(f"Error: {e}")
74
+ `;
75
+ const result = await ctx.$`python3 -c ${script}`;
177
76
  return result.toString();
178
77
  },
179
78
  }),
180
79
 
181
80
  browser_collect: tool({
182
- description: "浏览器采集动态内容。参数: url(目标URL)",
81
+ description: "浏览器采集动态内容",
183
82
  args: {
184
83
  url: tool.schema.string(),
185
84
  },
186
85
  async execute(args, ctx) {
187
- const deps = checkDeps(ctx);
188
- const corePath = getCorePath(ctx);
189
- const cmd = `${deps}python3 -c "
190
- import sys
191
- sys.path.insert(0, '${corePath}')
192
- from collectors.browser_collect import BrowserCollector
193
- collector = BrowserCollector(headless=True)
194
- endpoints = collector.collect('${args.url}')
195
- print(f'发现 {len(endpoints)} 个端点:')
196
- for ep in endpoints:
197
- print(ep)
198
- "`;
199
- const result = await ctx.$`${cmd}`;
200
- return result.toString();
201
- },
202
- }),
203
-
204
- js_parse: tool({
205
- description: "解析 JavaScript 文件。参数: file_path(文件路径)",
206
- args: {
207
- file_path: tool.schema.string(),
208
- },
209
- async execute(args, ctx) {
210
- const deps = checkDeps(ctx);
211
- const corePath = getCorePath(ctx);
212
- const cmd = `${deps}python3 -c "
86
+ const corePath = getCorePath(ctx.directory);
87
+ const script = `
213
88
  import sys
214
89
  sys.path.insert(0, '${corePath}')
215
- from collectors.js_parser import JSParser
216
- parser = JSParser()
217
- endpoints = parser.parse_file('${args.file_path}')
218
- print(f'从 JS 发现 {len(endpoints)} 个端点')
219
- "`;
220
- const result = await ctx.$`${cmd}`;
90
+ try:
91
+ from collectors.browser_collect import BrowserCollector
92
+ collector = BrowserCollector(headless=True)
93
+ endpoints = collector.collect('${args.url}')
94
+ print(f'发现 {len(endpoints)} 个端点')
95
+ for ep in endpoints:
96
+ print(ep)
97
+ except Exception as e:
98
+ print(f"Error: {e}")
99
+ `;
100
+ const result = await ctx.$`python3 -c ${script}`;
221
101
  return result.toString();
222
102
  },
223
103
  }),
224
104
 
225
105
  graphql_test: tool({
226
- description: "GraphQL 安全测试。参数: endpoint(GraphQL端点)",
227
- args: {
228
- endpoint: tool.schema.string(),
229
- },
230
- async execute(args, ctx) {
231
- const deps = checkDeps(ctx);
232
- const corePath = getCorePath(ctx);
233
- const cmd = `${deps}python3 -c "
234
- import sys
235
- sys.path.insert(0, '${corePath}')
236
- from smart_analyzer import SmartAnalyzer
237
- analyzer = SmartAnalyzer()
238
- result = analyzer.graphql_test('${args.endpoint}')
239
- print(result)
240
- "`;
241
- const result = await ctx.$`${cmd}`;
242
- return result.toString();
243
- },
244
- }),
245
-
246
- cloud_storage_test: tool({
247
- description: "云存储安全测试。参数: bucket_url(存储桶URL)",
248
- args: {
249
- bucket_url: tool.schema.string(),
250
- },
251
- async execute(args, ctx) {
252
- const deps = checkDeps(ctx);
253
- const corePath = getCorePath(ctx);
254
- const cmd = `${deps}python3 -c "
255
- import sys
256
- sys.path.insert(0, '${corePath}')
257
- from cloud_storage_tester import CloudStorageTester
258
- tester = CloudStorageTester()
259
- result = tester.full_test('${args.bucket_url}')
260
- print(result)
261
- "`;
262
- const result = await ctx.$`${cmd}`;
263
- return result.toString();
264
- },
265
- }),
266
-
267
- idor_test: tool({
268
- description: "IDOR 越权测试。参数: endpoint, resource_id",
106
+ description: "GraphQL 安全测试",
269
107
  args: {
270
108
  endpoint: tool.schema.string(),
271
- resource_id: tool.schema.string(),
272
109
  },
273
110
  async execute(args, ctx) {
274
- const deps = checkDeps(ctx);
275
- const corePath = getCorePath(ctx);
276
- const cmd = `${deps}python3 -c "
111
+ const corePath = getCorePath(ctx.directory);
112
+ const script = `
277
113
  import sys
278
114
  sys.path.insert(0, '${corePath}')
279
- from testers.idor_tester import IDORTester
280
- tester = IDORTester()
281
- result = tester.test('${args.endpoint}', '${args.resource_id}')
282
- print(result)
283
- "`;
284
- const result = await ctx.$`${cmd}`;
115
+ try:
116
+ from smart_analyzer import SmartAnalyzer
117
+ analyzer = SmartAnalyzer()
118
+ print(analyzer.graphql_test('${args.endpoint}'))
119
+ except Exception as e:
120
+ print(f"Error: {e}")
121
+ `;
122
+ const result = await ctx.$`python3 -c ${script}`;
285
123
  return result.toString();
286
124
  },
287
125
  }),
288
126
 
289
127
  sqli_test: tool({
290
- description: "SQL 注入测试。参数: endpoint, param",
128
+ description: "SQL 注入测试",
291
129
  args: {
292
130
  endpoint: tool.schema.string(),
293
131
  param: tool.schema.string(),
294
132
  },
295
133
  async execute(args, ctx) {
296
- const deps = checkDeps(ctx);
297
- const corePath = getCorePath(ctx);
298
- const cmd = `${deps}python3 -c "
134
+ const corePath = getCorePath(ctx.directory);
135
+ const script = `
299
136
  import sys
300
137
  sys.path.insert(0, '${corePath}')
301
- from testers.sqli_tester import SQLiTester
302
- tester = SQLiTester()
303
- result = tester.test('${args.endpoint}', '${args.param}')
304
- print(result)
305
- "`;
306
- const result = await ctx.$`${cmd}`;
138
+ try:
139
+ from testers.sqli_tester import SQLiTester
140
+ tester = SQLiTester()
141
+ print(tester.test('${args.endpoint}', '${args.param}'))
142
+ except Exception as e:
143
+ print(f"Error: {e}")
144
+ `;
145
+ const result = await ctx.$`python3 -c ${script}`;
307
146
  return result.toString();
308
147
  },
309
148
  }),
310
149
 
311
- auth_test: tool({
312
- description: "认证安全测试。参数: endpoint",
150
+ idor_test: tool({
151
+ description: "IDOR 越权测试",
313
152
  args: {
314
153
  endpoint: tool.schema.string(),
154
+ resource_id: tool.schema.string(),
315
155
  },
316
156
  async execute(args, ctx) {
317
- const deps = checkDeps(ctx);
318
- const corePath = getCorePath(ctx);
319
- const cmd = `${deps}python3 -c "
157
+ const corePath = getCorePath(ctx.directory);
158
+ const script = `
320
159
  import sys
321
160
  sys.path.insert(0, '${corePath}')
322
- from testers.auth_tester import AuthTester
323
- tester = AuthTester()
324
- result = tester.test('${args.endpoint}')
325
- print(result)
326
- "`;
327
- const result = await ctx.$`${cmd}`;
161
+ try:
162
+ from testers.idor_tester import IDORTester
163
+ tester = IDORTester()
164
+ print(tester.test('${args.endpoint}', '${args.resource_id}'))
165
+ except Exception as e:
166
+ print(f"Error: {e}")
167
+ `;
168
+ const result = await ctx.$`python3 -c ${script}`;
328
169
  return result.toString();
329
170
  },
330
171
  }),
331
172
  },
332
-
333
- config: async (config) => {
334
- if (!config.agent) {
335
- config.agent = {};
336
- }
337
-
338
- const agents = config.agent as Record<string, AgentConfig>;
339
-
340
- agents["api-cyber-supervisor"] = {
341
- description: "API安全测试编排者。协调完整扫描流程,永不停止。",
342
- mode: "primary",
343
- prompt: CYBER_SUPERVISOR_PROMPT,
344
- };
345
-
346
- agents["api-probing-miner"] = {
347
- description: "漏洞挖掘专家。专注发现和验证 API 漏洞。",
348
- mode: "subagent",
349
- prompt: PROBING_MINER_PROMPT,
350
- };
351
-
352
- agents["api-resource-specialist"] = {
353
- description: "资源探测专家。专注采集和发现 API 端点。",
354
- mode: "subagent",
355
- prompt: RESOURCE_SPECIALIST_PROMPT,
356
- };
357
-
358
- agents["api-vuln-verifier"] = {
359
- description: "漏洞验证专家。验证和确认安全漏洞。",
360
- mode: "subagent",
361
- prompt: VULN_VERIFIER_PROMPT,
362
- };
363
-
364
- console.log("[api-security-testing] Agents registered:", Object.keys(agents));
365
- },
366
173
  };
367
174
  };
368
175
 
package/postinstall.mjs DELETED
@@ -1,130 +0,0 @@
1
- #!/usr/bin/env node
2
-
3
- /**
4
- * postinstall.mjs - API Security Testing Plugin
5
- *
6
- * Installs:
7
- * 1. Agents to ~/.config/opencode/agents/
8
- * 2. Skill (SKILL.md) to ~/.config/opencode/skills/api-security-testing/
9
- * 3. References to ~/.config/opencode/skills/api-security-testing/
10
- */
11
-
12
- import { copyFileSync, existsSync, mkdirSync, readdirSync } from "node:fs";
13
- import { join, dirname } from "node:path";
14
- import { fileURLToPath } from "node:url";
15
-
16
- const __filename = fileURLToPath(import.meta.url);
17
- const __dirname = dirname(__filename);
18
-
19
- function getOpencodeBaseDir() {
20
- const home = process.env.HOME || process.env.USERPROFILE || "/root";
21
- return join(home, ".config", "opencode");
22
- }
23
-
24
- function main() {
25
- const packageRoot = __dirname;
26
- const agentsSourceDir = join(packageRoot, "agents");
27
- const agentsTargetDir = join(getOpencodeBaseDir(), "agents");
28
- const skillTargetDir = join(getOpencodeBaseDir(), "skills", "api-security-testing");
29
-
30
- console.log("[api-security-testing] Installing...");
31
- console.log(` Package root: ${packageRoot}`);
32
- console.log(` Target base: ${getOpencodeBaseDir()}`);
33
-
34
- let successCount = 0;
35
- let totalFiles = 0;
36
-
37
- // 1. Install agents
38
- console.log("\n[1/3] Installing agents...");
39
- if (existsSync(agentsSourceDir)) {
40
- const agentFiles = readdirSync(agentsSourceDir).filter(f => f.endsWith(".md"));
41
- totalFiles += agentFiles.length;
42
-
43
- if (!existsSync(agentsTargetDir)) {
44
- mkdirSync(agentsTargetDir, { recursive: true });
45
- }
46
-
47
- for (const file of agentFiles) {
48
- const sourcePath = join(agentsSourceDir, file);
49
- const targetPath = join(agentsTargetDir, file);
50
- try {
51
- copyFileSync(sourcePath, targetPath);
52
- console.log(` ✓ Installed agent: ${file}`);
53
- successCount++;
54
- } catch (err) {
55
- console.error(` ✗ Failed: ${file} - ${err.message}`);
56
- }
57
- }
58
- } else {
59
- console.error(" ✗ agents/ directory not found");
60
- }
61
-
62
- // 2. Install SKILL.md
63
- console.log("\n[2/3] Installing skill...");
64
- const skillSource = join(packageRoot, "SKILL.md");
65
- const skillTarget = join(skillTargetDir, "SKILL.md");
66
- totalFiles++;
67
-
68
- if (existsSync(skillSource)) {
69
- if (!existsSync(skillTargetDir)) {
70
- mkdirSync(skillTargetDir, { recursive: true });
71
- }
72
- try {
73
- copyFileSync(skillSource, skillTarget);
74
- console.log(` ✓ Installed: SKILL.md`);
75
- successCount++;
76
- } catch (err) {
77
- console.error(` ✗ Failed: SKILL.md - ${err.message}`);
78
- }
79
- } else {
80
- console.error(" ✗ SKILL.md not found");
81
- }
82
-
83
- // 3. Install references
84
- console.log("\n[3/3] Installing references...");
85
- const refsSourceDir = join(packageRoot, "references");
86
- const refsTargetDir = join(skillTargetDir, "references");
87
-
88
- if (existsSync(refsSourceDir)) {
89
- if (!existsSync(refsTargetDir)) {
90
- mkdirSync(refsTargetDir, { recursive: true });
91
- }
92
-
93
- const refFiles = readdirSync(refsSourceDir, { recursive: true }).filter(f => typeof f === "string");
94
- totalFiles += refFiles.length;
95
-
96
- for (const file of refFiles) {
97
- const sourcePath = join(refsSourceDir, file);
98
- const targetPath = join(refsTargetDir, file);
99
- const targetFileDir = join(refsTargetDir, file).replace(/\/[^\/]+$/, "");
100
- if (!existsSync(targetFileDir)) {
101
- mkdirSync(targetFileDir, { recursive: true });
102
- }
103
- try {
104
- copyFileSync(sourcePath, targetPath);
105
- console.log(` ✓ Installed: references/${file}`);
106
- successCount++;
107
- } catch (err) {
108
- console.error(` ✗ Failed: references/${file} - ${err.message}`);
109
- }
110
- }
111
- } else {
112
- console.log(" (references/ not found, skipping)");
113
- }
114
-
115
- // Summary
116
- console.log("\n========================================");
117
- if (successCount === totalFiles) {
118
- console.log(`✓ Successfully installed ${successCount} file(s)`);
119
- console.log(`\nAgent location: ${agentsTargetDir}`);
120
- console.log(`Skill location: ${skillTargetDir}`);
121
- console.log("\nTo use:");
122
- console.log(" @api-cyber-supervisor - Start security testing");
123
- console.log(" skill({ name: \"api-security-testing\" }) - Load skill");
124
- } else {
125
- console.log(`⚠ Partially installed: ${successCount}/${totalFiles}`);
126
- process.exit(1);
127
- }
128
- }
129
-
130
- main();