@pwddd/skills-scanner 2.0.0 → 2.1.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/index.ts CHANGED
@@ -568,27 +568,78 @@ export default function register(api: any) {
568
568
 
569
569
  const lines = [
570
570
  "📋 *Skills Scanner 状态*",
571
+ `API 服务地址:${apiUrl}`,
571
572
  `Python 依赖:${isVenvReady() ? "✅ 就绪" : "❌ 未就绪"}`,
572
573
  `Python 路径:${VENV_PYTHON}`,
573
574
  `scan.py 路径:${SCAN_SCRIPT}`,
574
575
  `安装前扫描:${preInstallScan === "on" ? `✅ 监听中(${onUnsafe})` : "❌ 已禁用"}`,
576
+ `扫描策略:${policy}`,
577
+ `LLM 分析:${useLLM ? "✅ 启用" : "❌ 禁用"}`,
578
+ `行为分析:${behavioral ? "✅ 启用" : "❌ 禁用"}`,
575
579
  `上次扫描:${state.lastScanAt ? new Date(state.lastScanAt).toLocaleString("zh-CN") : "从未"}`,
576
580
  `上次问题 Skills:${state.lastUnsafeSkills?.length ? state.lastUnsafeSkills.join(", ") : "无"}`,
577
581
  `扫描目录:\n${scanDirs.map(d => ` • ${d}`).join("\n")}`,
578
582
  ];
579
583
 
580
- // 添加依赖检查
581
- try {
582
- const versionCheck = execSync(`"${VENV_PYTHON}" -c "import skill_scanner; print(skill_scanner.__version__)"`, { encoding: 'utf-8' });
583
- lines.push(`cisco-ai-skill-scanner 版本:${versionCheck.trim()}`);
584
- } catch (err: any) {
585
- lines.push(`❌ 依赖检查失败:${err.message}`);
586
-
587
- // 尝试列出已安装的包
584
+ // API 健康检查
585
+ if (isVenvReady()) {
586
+ lines.push("", "🔍 *API 服务检查*");
588
587
  try {
589
- const pipList = execSync(`"${VENV_PYTHON}" -m pip list`, { encoding: 'utf-8' });
590
- lines.push("", "已安装的包:", "```", pipList.trim(), "```");
591
- } catch {}
588
+ // 调用 scan.py health 命令
589
+ const cmd = `"${VENV_PYTHON}" "${SCAN_SCRIPT}" --api-url "${apiUrl}" health`;
590
+
591
+ // 清除代理环境变量
592
+ const env = { ...process.env };
593
+ delete env.http_proxy;
594
+ delete env.https_proxy;
595
+ delete env.HTTP_PROXY;
596
+ delete env.HTTPS_PROXY;
597
+ delete env.all_proxy;
598
+ delete env.ALL_PROXY;
599
+
600
+ const { stdout, stderr } = await execAsync(cmd, { timeout: 5000, env });
601
+ const output = (stdout + stderr).trim();
602
+
603
+ if (output.includes("✓") || output.includes("正常")) {
604
+ lines.push(`API 服务:✅ 正常`);
605
+
606
+ // 尝试解析可用的分析器信息
607
+ if (output.includes("analyzers_available")) {
608
+ try {
609
+ // 从输出中提取 JSON(如果有的话)
610
+ const jsonMatch = output.match(/\{[\s\S]*\}/);
611
+ if (jsonMatch) {
612
+ const healthData = JSON.parse(jsonMatch[0]);
613
+ if (healthData.analyzers_available) {
614
+ lines.push(`可用分析器:${healthData.analyzers_available.join(", ")}`);
615
+ }
616
+ if (healthData.version) {
617
+ lines.push(`API 版本:${healthData.version}`);
618
+ }
619
+ }
620
+ } catch {}
621
+ }
622
+ } else {
623
+ lines.push(`API 服务:❌ 不可用`);
624
+ lines.push(`响应:${output}`);
625
+ }
626
+ } catch (err: any) {
627
+ lines.push(`API 服务:❌ 连接失败`);
628
+ const errorMsg = err.message || err.toString();
629
+ if (errorMsg.includes("ECONNREFUSED") || errorMsg.includes("无法连接")) {
630
+ lines.push(`错误:无法连接到 ${apiUrl}`);
631
+ } else {
632
+ lines.push(`错误:${errorMsg}`);
633
+ }
634
+ lines.push("", "💡 请确保 skill-scanner-api 服务正在运行:");
635
+ lines.push("```");
636
+ lines.push("skill-scanner-api");
637
+ lines.push("# 或指定端口");
638
+ lines.push("skill-scanner-api --port 8080");
639
+ lines.push("```");
640
+ }
641
+ } else {
642
+ lines.push("", "⚠️ Python 依赖未就绪,无法检查 API 服务");
592
643
  }
593
644
 
594
645
  if (alerts.length > 0) {
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@pwddd/skills-scanner",
3
- "version": "2.0.0",
4
- "description": "OpenClaw Plugin:Skills 安全扫描、安装前拦截、安全日报",
3
+ "version": "2.1.0",
4
+ "description": "OpenClaw Plugin:Skills 安全扫描、安装前拦截、安全日报(HTTP API 客户端模式)",
5
5
  "main": "index.ts",
6
6
  "files": [
7
7
  "index.ts",
@@ -12,7 +12,7 @@
12
12
  "openclaw": {
13
13
  "extensions": ["./index.ts"]
14
14
  },
15
- "keywords": ["openclaw", "openclaw-plugin", "security", "skill-scanner"],
15
+ "keywords": ["openclaw", "openclaw-plugin", "security", "skill-scanner", "http-client"],
16
16
  "license": "MIT",
17
17
  "publishConfig": {
18
18
  "access": "public"
@@ -57,13 +57,30 @@ class SkillScannerClient:
57
57
  self.base_url = base_url.rstrip('/')
58
58
  self.session = requests.Session()
59
59
 
60
- def health_check(self) -> bool:
61
- """健康检查"""
60
+ def health_check(self) -> Dict[str, Any]:
61
+ """健康检查,返回详细信息"""
62
62
  try:
63
63
  response = self.session.get(f"{self.base_url}/health", timeout=5)
64
- return response.status_code == 200
65
- except Exception:
66
- return False
64
+ response.raise_for_status()
65
+ return {
66
+ 'status': 'healthy',
67
+ 'data': response.json()
68
+ }
69
+ except requests.exceptions.ConnectionError:
70
+ return {
71
+ 'status': 'unreachable',
72
+ 'error': f'无法连接到 {self.base_url}'
73
+ }
74
+ except requests.exceptions.Timeout:
75
+ return {
76
+ 'status': 'timeout',
77
+ 'error': '请求超时'
78
+ }
79
+ except Exception as e:
80
+ return {
81
+ 'status': 'error',
82
+ 'error': str(e)
83
+ }
67
84
 
68
85
  def scan_upload(
69
86
  self,
@@ -368,11 +385,27 @@ def main():
368
385
 
369
386
  try:
370
387
  if args.command == 'health':
371
- if client.health_check():
388
+ health_result = client.health_check()
389
+
390
+ if health_result['status'] == 'healthy':
372
391
  print(GREEN("✓") + " API 服务正常")
392
+
393
+ # 显示详细信息
394
+ data = health_result.get('data', {})
395
+ if data:
396
+ print(f" 版本: {data.get('version', 'Unknown')}")
397
+ analyzers = data.get('analyzers_available', [])
398
+ if analyzers:
399
+ print(f" 可用分析器: {', '.join(analyzers)}")
400
+
401
+ # 输出 JSON 供程序解析
402
+ print(json.dumps(data))
403
+
373
404
  sys.exit(0)
374
405
  else:
375
406
  print(RED("✗") + f" API 服务不可用: {args.api_url}")
407
+ error = health_result.get('error', '未知错误')
408
+ print(f" 错误: {error}")
376
409
  sys.exit(1)
377
410
 
378
411
  elif args.command == 'scan':