@pwddd/skills-scanner 3.0.6 → 3.0.7

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/src/deps.ts CHANGED
@@ -1,112 +1,77 @@
1
- /**
2
- * Dependency management module
3
- */
4
-
5
- import { execSync } from "node:child_process";
6
- import { existsSync, rmSync } from "node:fs";
7
- import { promisify } from "node:util";
8
- import { exec } from "node:child_process";
9
- import { join } from "node:path";
10
-
11
- const execAsync = promisify(exec);
12
-
13
- export function hasUv(): boolean {
14
- try {
15
- execSync("uv --version", { stdio: "ignore" });
16
- return true;
17
- } catch {
18
- return false;
19
- }
20
- }
21
-
22
- export function hasPython(): boolean {
23
- try {
24
- execSync("python3 --version", { stdio: "ignore" });
25
- return true;
26
- } catch {
27
- try {
28
- execSync("python --version", { stdio: "ignore" });
29
- return true;
30
- } catch {
31
- return false;
32
- }
33
- }
34
- }
35
-
36
- export function getPythonCommand(): string {
37
- try {
38
- execSync("python3 --version", { stdio: "ignore" });
39
- return "python3";
40
- } catch {
41
- return "python";
42
- }
43
- }
44
-
45
- export function isVenvReady(venvPython: string): boolean {
46
- if (!existsSync(venvPython)) return false;
47
-
48
- try {
49
- execSync(`"${venvPython}" -c "import requests"`, { stdio: "ignore" });
50
- return true;
51
- } catch {
52
- return false;
53
- }
54
- }
55
-
56
- export async function ensureDeps(
57
- skillDir: string,
58
- venvPython: string,
59
- logger: any
60
- ): Promise<boolean> {
61
- if (isVenvReady(venvPython)) {
62
- logger.info("[skills-scanner] Python dependencies ready (requests installed)");
63
- return true;
64
- }
65
-
66
- const useUv = hasUv();
67
- const usePython = !useUv && hasPython();
68
-
69
- if (!useUv && !usePython) {
70
- logger.error(
71
- "[skills-scanner] Neither uv nor python3 found. Please install one of them:"
72
- );
73
- logger.error("[skills-scanner] - uv: brew install uv or curl -LsSf https://astral.sh/uv/install.sh | sh");
74
- logger.error("[skills-scanner] - python3: brew install python3 or apt-get install python3 python3-venv python3-pip");
75
- return false;
76
- }
77
-
78
- logger.info(`[skills-scanner] Installing Python dependencies using ${useUv ? "uv" : "python3"}...`);
79
-
80
- try {
81
- const venvDir = join(skillDir, ".venv");
82
-
83
- if (existsSync(venvDir)) {
84
- logger.info("[skills-scanner] Cleaning old virtual environment...");
85
- rmSync(venvDir, { recursive: true, force: true });
86
- }
87
-
88
- if (useUv) {
89
- // Use uv (faster)
90
- await execAsync(`uv venv "${venvDir}" --python 3.10`);
91
- logger.info("[skills-scanner] Virtual environment created (uv)");
92
-
93
- logger.info("[skills-scanner] Installing requests...");
94
- await execAsync(`uv pip install --python "${venvPython}" requests>=2.31.0`);
95
- } else {
96
- // Fallback to standard Python venv + pip
97
- const pythonCmd = getPythonCommand();
98
- await execAsync(`${pythonCmd} -m venv "${venvDir}"`);
99
- logger.info("[skills-scanner] Virtual environment created (python -m venv)");
100
-
101
- logger.info("[skills-scanner] Installing requests...");
102
- await execAsync(`"${venvPython}" -m pip install --quiet requests>=2.31.0`);
103
- }
104
-
105
- execSync(`"${venvPython}" -c "import requests"`, { stdio: "ignore" });
106
- logger.info("[skills-scanner] ✅ Dependencies installed successfully");
107
- return true;
108
- } catch (err: any) {
109
- logger.error(`[skills-scanner] ⚠️ Dependency installation failed: ${err.message}`);
110
- return false;
111
- }
112
- }
1
+ /**
2
+ * Dependency management module
3
+ */
4
+
5
+ import { execSync, exec } from "node:child_process";
6
+ import { promisify } from "node:util";
7
+
8
+ const execAsync = promisify(exec);
9
+
10
+ function detectPythonCommand(): string | null {
11
+ try {
12
+ execSync("python3 --version", { stdio: "ignore" });
13
+ return "python3";
14
+ } catch {
15
+ try {
16
+ execSync("python --version", { stdio: "ignore" });
17
+ return "python";
18
+ } catch {
19
+ return null;
20
+ }
21
+ }
22
+ }
23
+
24
+ export const getPythonCommand = (): string | null => detectPythonCommand();
25
+ export const hasPython = (): boolean => detectPythonCommand() !== null;
26
+
27
+ export function isRequestsInstalled(pythonCmd: string | null): boolean {
28
+ if (!pythonCmd) return false;
29
+ try {
30
+ execSync(`${pythonCmd} -c "import requests"`, { stdio: "ignore" });
31
+ return true;
32
+ } catch {
33
+ return false;
34
+ }
35
+ }
36
+
37
+ export const isPythonReady = (pythonCmd: string | null): boolean =>
38
+ hasPython() && isRequestsInstalled(pythonCmd);
39
+
40
+ export async function ensureDeps(pythonCmd: string | null, logger: any): Promise<boolean> {
41
+ const resolvedPython = pythonCmd ?? getPythonCommand();
42
+
43
+ if (!resolvedPython) {
44
+ logger.error("[skills-scanner] Python not found. Please install Python 3.10+:");
45
+ logger.error("[skills-scanner] - macOS: brew install python3");
46
+ logger.error("[skills-scanner] - Linux: apt-get install python3 python3-pip");
47
+ logger.error("[skills-scanner] - Windows: https://www.python.org/downloads/");
48
+ return false;
49
+ }
50
+
51
+ if (isRequestsInstalled(resolvedPython)) {
52
+ logger.info("[skills-scanner] Python dependencies ready (requests installed)");
53
+ return true;
54
+ }
55
+
56
+ logger.info(`[skills-scanner] Installing requests package using ${resolvedPython}...`);
57
+
58
+ try {
59
+ await execAsync(`${resolvedPython} -m pip install --user --quiet "requests>=2.31.0"`, {
60
+ timeout: 120000,
61
+ });
62
+
63
+ if (isRequestsInstalled(resolvedPython)) {
64
+ logger.info("[skills-scanner] Dependencies installed successfully");
65
+ return true;
66
+ } else {
67
+ throw new Error("requests package not found after installation");
68
+ }
69
+ } catch (err: any) {
70
+ logger.error(`[skills-scanner] Dependency installation failed: ${err.message}`);
71
+ logger.error(`[skills-scanner] Please install manually:`);
72
+ logger.error(`[skills-scanner] ${resolvedPython} -m pip install --user requests`);
73
+ logger.error(`[skills-scanner] Or with sudo:`);
74
+ logger.error(`[skills-scanner] sudo ${resolvedPython} -m pip install requests`);
75
+ return false;
76
+ }
77
+ }
package/src/report.ts CHANGED
@@ -15,7 +15,7 @@ export async function buildDailyReport(
15
15
  useLLM: boolean,
16
16
  policy: string,
17
17
  logger: any,
18
- venvPython: string,
18
+ pythonCmd: string | null,
19
19
  scanScript: string
20
20
  ): Promise<string> {
21
21
  const now = new Date();
@@ -41,7 +41,7 @@ export async function buildDailyReport(
41
41
  for (const dir of dirs) {
42
42
  if (!existsSync(dir)) continue;
43
43
  const tmpJson = join(STATE_DIR, `tmp-${Date.now()}.json`);
44
- await runScan(venvPython, scanScript, "batch", dir, {
44
+ await runScan(pythonCmd, scanScript, "batch", dir, {
45
45
  behavioral,
46
46
  recursive: true,
47
47
  jsonOut: tmpJson,
package/src/scanner.ts CHANGED
@@ -9,12 +9,16 @@ import type { ScanOptions, ScanResult } from "./types.js";
9
9
  const execAsync = promisify(exec);
10
10
 
11
11
  export async function runScan(
12
- venvPython: string,
12
+ pythonCmd: string | null,
13
13
  scanScript: string,
14
14
  mode: "scan" | "batch" | "clawhub",
15
15
  target: string,
16
16
  opts: ScanOptions = {}
17
17
  ): Promise<ScanResult> {
18
+ if (!pythonCmd) {
19
+ return { exitCode: 1, output: "Python is not available on this host" };
20
+ }
21
+
18
22
  const args = [mode, target];
19
23
  if (opts.detailed) args.push("--detailed");
20
24
  if (opts.behavioral) args.push("--behavioral");
@@ -24,7 +28,7 @@ export async function runScan(
24
28
  if (opts.jsonOut) args.push("--json", opts.jsonOut);
25
29
  if (opts.apiUrl) args.unshift("--api-url", opts.apiUrl);
26
30
 
27
- const cmd = `"${venvPython}" "${scanScript}" ${args.map((a) => `"${a}"`).join(" ")}`;
31
+ const cmd = `"${pythonCmd}" "${scanScript}" ${args.map((a) => `"${a}"`).join(" ")}`;
28
32
 
29
33
  try {
30
34
  const env = { ...process.env };
package/src/watcher.ts CHANGED
@@ -17,7 +17,7 @@ export async function handleNewSkill(
17
17
  policy: string,
18
18
  notifyFn: (msg: string) => void,
19
19
  logger: any,
20
- venvPython: string,
20
+ pythonCmd: string | null,
21
21
  scanScript: string,
22
22
  quarantineDir: string
23
23
  ): Promise<void> {
@@ -27,7 +27,7 @@ export async function handleNewSkill(
27
27
  logger.info(`[skills-scanner] 🔍 检测到新 Skill,开始安装前扫描: ${name}`);
28
28
  notifyFn(`🔍 检测到新 Skill \`${name}\`,正在安全扫描...`);
29
29
 
30
- const res = await runScan(venvPython, scanScript, "scan", skillPath, {
30
+ const res = await runScan(pythonCmd, scanScript, "scan", skillPath, {
31
31
  behavioral,
32
32
  detailed: true,
33
33
  apiUrl,
@@ -77,7 +77,7 @@ export function startWatcher(
77
77
  policy: string,
78
78
  notifyFn: (msg: string) => void,
79
79
  logger: any,
80
- venvPython: string,
80
+ pythonCmd: string | null,
81
81
  scanScript: string,
82
82
  quarantineDir: string
83
83
  ): () => void {
@@ -105,12 +105,12 @@ export function startWatcher(
105
105
  apiUrl,
106
106
  useLLM,
107
107
  policy,
108
- notifyFn,
109
- logger,
110
- venvPython,
111
- scanScript,
112
- quarantineDir
113
- );
108
+ notifyFn,
109
+ logger,
110
+ pythonCmd,
111
+ scanScript,
112
+ quarantineDir
113
+ );
114
114
  }, 500)
115
115
  );
116
116
  });