@pwddd/skills-scanner 3.0.11 → 3.0.13

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
@@ -171,7 +171,8 @@ export default function register(api: OpenClawPluginApi) {
171
171
  // Register cron job (only in Gateway mode)
172
172
  const isGatewayMode = !!(api as any).runtime;
173
173
  if (isGatewayMode) {
174
- await ensureCronJob(api.logger);
174
+ const runtime = (api as any).runtime;
175
+ await ensureCronJob(api.logger, runtime);
175
176
  }
176
177
 
177
178
  api.logger.info("[skills-scanner] ─────────────────────────────────────");
@@ -396,6 +397,3 @@ export default function register(api: OpenClawPluginApi) {
396
397
 
397
398
  api.logger.info("[skills-scanner] ✅ Plugin registered");
398
399
  }
399
-
400
-
401
-
@@ -2,7 +2,7 @@
2
2
  "id": "skills-scanner",
3
3
  "name": "Skills Scanner",
4
4
  "description": "Security scanner for OpenClaw Skills to detect potential threats",
5
- "version": "3.0.11",
5
+ "version": "3.0.13",
6
6
  "author": "pwddd",
7
7
  "skills": ["./skills"],
8
8
  "configSchema": {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pwddd/skills-scanner",
3
- "version": "3.0.11",
3
+ "version": "3.0.13",
4
4
  "description": "OpenClaw Skills security scanner plugin - detect malicious code, data exfiltration, and prompt injection",
5
5
  "type": "module",
6
6
  "main": "./index.ts",
package/src/commands.ts CHANGED
@@ -225,7 +225,7 @@ export function createCommandHandlers(
225
225
  }
226
226
 
227
227
  saveState({ ...state, cronJobId: undefined });
228
- await ensureCronJob(logger);
228
+ await ensureCronJob(logger, undefined);
229
229
 
230
230
  const newState = loadState() as any;
231
231
  if (newState.cronJobId) {
package/src/cron.ts CHANGED
@@ -9,16 +9,123 @@ const CRON_JOB_NAME = "skills-daily-report";
9
9
  const CRON_SCHEDULE = "0 8 * * *";
10
10
  const CRON_TIMEZONE = "Asia/Shanghai";
11
11
 
12
- export async function ensureCronJob(logger: any): Promise<void> {
13
- const state = loadState() as any;
12
+ /**
13
+ * Detect the correct OpenClaw command (openclaw vs npx openclaw)
14
+ */
15
+ function getOpenClawCommand(): string {
16
+ // 1. Check environment variable
17
+ if (process.env.OPENCLAW_CLI_PATH) {
18
+ return process.env.OPENCLAW_CLI_PATH;
19
+ }
14
20
 
15
- logger.info("[skills-scanner] ─────────────────────────────────────");
16
- logger.info("[skills-scanner] 🕐 Checking cron job...");
21
+ // 2. Check if running via npx
22
+ const argv1 = process.argv[1];
23
+ if (argv1?.includes("npx") || argv1?.includes("_npx")) {
24
+ return "npx openclaw";
25
+ }
26
+
27
+ if (process.env.npm_execpath?.includes("npx")) {
28
+ return "npx openclaw";
29
+ }
30
+
31
+ // 3. Try global openclaw command
32
+ try {
33
+ execSync("openclaw --version", {
34
+ encoding: "utf-8",
35
+ timeout: 3000,
36
+ stdio: "pipe"
37
+ });
38
+ return "openclaw";
39
+ } catch {
40
+ // openclaw command not available
41
+ }
42
+
43
+ // 4. Try npx as fallback
44
+ try {
45
+ execSync("npx openclaw --version", {
46
+ encoding: "utf-8",
47
+ timeout: 5000,
48
+ stdio: "pipe"
49
+ });
50
+ return "npx openclaw";
51
+ } catch {
52
+ // npx also not available
53
+ }
54
+
55
+ // 5. Default to openclaw (will fail with clear error)
56
+ return "openclaw";
57
+ }
58
+
59
+ /**
60
+ * Create cron job via Gateway API (most reliable)
61
+ */
62
+ async function ensureCronJobViaAPI(runtime: any, logger: any): Promise<boolean> {
63
+ if (!runtime?.cron) {
64
+ logger.debug("[skills-scanner] Cron API not available");
65
+ return false;
66
+ }
67
+
68
+ try {
69
+ const state = loadState() as any;
70
+
71
+ // Check if job already exists
72
+ const jobs = await runtime.cron.list({ includeDisabled: true });
73
+ const existing = jobs.find((j: any) => j.name === CRON_JOB_NAME);
74
+
75
+ if (existing) {
76
+ logger.info(`[skills-scanner] ✅ Job already exists: ${existing.id}`);
77
+ if (state.cronJobId !== existing.id) {
78
+ saveState({ ...state, cronJobId: existing.id });
79
+ }
80
+ return true;
81
+ }
82
+
83
+ // Create new job
84
+ logger.info("[skills-scanner] 📝 Creating cron job via API...");
85
+ const job = await runtime.cron.add({
86
+ name: CRON_JOB_NAME,
87
+ enabled: true,
88
+ schedule: {
89
+ kind: "cron",
90
+ expr: CRON_SCHEDULE,
91
+ tz: CRON_TIMEZONE
92
+ },
93
+ payload: {
94
+ kind: "agentTurn",
95
+ message: "Please run /skills-scanner scan --report and send results to this channel"
96
+ },
97
+ delivery: {
98
+ mode: "announce",
99
+ channel: "last"
100
+ }
101
+ });
102
+
103
+ saveState({ ...state, cronJobId: job.id });
104
+ logger.info(`[skills-scanner] ✅ Job created successfully via API: ${job.id}`);
105
+ logger.info(
106
+ `[skills-scanner] 📅 Schedule: Daily at ${CRON_SCHEDULE.split(" ")[1]}:${CRON_SCHEDULE.split(" ")[0]} (${CRON_TIMEZONE})`
107
+ );
108
+ logger.info("[skills-scanner] 📬 Reports will be delivered to the last active channel");
109
+ return true;
110
+ } catch (err: any) {
111
+ logger.warn(`[skills-scanner] API creation failed: ${err.message}`);
112
+ return false;
113
+ }
114
+ }
115
+
116
+ /**
117
+ * Create cron job via CLI (fallback)
118
+ */
119
+ async function ensureCronJobViaCLI(logger: any): Promise<void> {
120
+ const openclawCmd = getOpenClawCommand();
121
+ logger.info(`[skills-scanner] Using CLI command: ${openclawCmd}`);
122
+
123
+ const state = loadState() as any;
17
124
 
18
125
  try {
19
126
  let jobs: any[] = [];
20
127
  try {
21
- const listResult = execSync("openclaw cron list --format json", {
128
+ const listResult = execSync(`${openclawCmd} cron list --format json`, {
22
129
  encoding: "utf-8",
23
130
  timeout: 5000,
24
131
  });
@@ -26,7 +133,7 @@ export async function ensureCronJob(logger: any): Promise<void> {
26
133
  } catch (listErr: any) {
27
134
  logger.debug("[skills-scanner] JSON format not supported, trying text parsing");
28
135
  try {
29
- const listResult = execSync("openclaw cron list", {
136
+ const listResult = execSync(`${openclawCmd} cron list`, {
30
137
  encoding: "utf-8",
31
138
  timeout: 5000,
32
139
  });
@@ -57,7 +164,7 @@ export async function ensureCronJob(logger: any): Promise<void> {
57
164
  for (let i = 1; i < existingJobs.length; i++) {
58
165
  const jobId = existingJobs[i].id || existingJobs[i].jobId;
59
166
  try {
60
- execSync(`openclaw cron remove ${jobId}`, {
167
+ execSync(`${openclawCmd} cron remove ${jobId}`, {
61
168
  encoding: "utf-8",
62
169
  timeout: 5000,
63
170
  });
@@ -82,7 +189,7 @@ export async function ensureCronJob(logger: any): Promise<void> {
82
189
  if (needsUpdate) {
83
190
  logger.info(`[skills-scanner] 🔄 Job config changed, updating...`);
84
191
  try {
85
- execSync(`openclaw cron remove ${jobId}`, {
192
+ execSync(`${openclawCmd} cron remove ${jobId}`, {
86
193
  encoding: "utf-8",
87
194
  timeout: 5000,
88
195
  });
@@ -106,12 +213,12 @@ export async function ensureCronJob(logger: any): Promise<void> {
106
213
  }
107
214
  }
108
215
 
109
- logger.info("[skills-scanner] 📝 Creating cron job...");
216
+ logger.info("[skills-scanner] 📝 Creating cron job via CLI...");
110
217
 
111
218
  // Create cron job with --announce and --channel last
112
219
  // This will deliver to the last place the agent replied
113
220
  const cronCmd = [
114
- "openclaw cron add",
221
+ `${openclawCmd} cron add`,
115
222
  `--name "${CRON_JOB_NAME}"`,
116
223
  `--cron "${CRON_SCHEDULE}"`,
117
224
  `--tz "${CRON_TIMEZONE}"`,
@@ -151,11 +258,11 @@ export async function ensureCronJob(logger: any): Promise<void> {
151
258
  err.message.includes("command not found") ||
152
259
  err.message.includes("ENOENT")
153
260
  ) {
154
- logger.error("[skills-scanner] ❌ openclaw command not found, please check installation");
261
+ logger.error(`[skills-scanner] ❌ ${openclawCmd} command not found, please check installation`);
155
262
  } else {
156
263
  logger.info("[skills-scanner] 💡 Please manually register cron job:");
157
264
  logger.info("[skills-scanner]");
158
- logger.info("[skills-scanner] openclaw cron add \\");
265
+ logger.info(`[skills-scanner] ${openclawCmd} cron add \\`);
159
266
  logger.info(`[skills-scanner] --name "${CRON_JOB_NAME}" \\`);
160
267
  logger.info(`[skills-scanner] --cron "${CRON_SCHEDULE}" \\`);
161
268
  logger.info(`[skills-scanner] --tz "${CRON_TIMEZONE}" \\`);
@@ -172,3 +279,24 @@ export async function ensureCronJob(logger: any): Promise<void> {
172
279
  }
173
280
  }
174
281
  }
282
+
283
+ /**
284
+ * Ensure cron job exists (tries API first, falls back to CLI)
285
+ */
286
+ export async function ensureCronJob(logger: any, runtime?: any): Promise<void> {
287
+ logger.info("[skills-scanner] ─────────────────────────────────────");
288
+ logger.info("[skills-scanner] 🕐 Checking cron job...");
289
+
290
+ // Try API first (most reliable)
291
+ if (runtime) {
292
+ const success = await ensureCronJobViaAPI(runtime, logger);
293
+ if (success) {
294
+ logger.info("[skills-scanner] ✅ Cron job configured via API");
295
+ return;
296
+ }
297
+ logger.info("[skills-scanner] Falling back to CLI method...");
298
+ }
299
+
300
+ // Fallback to CLI
301
+ await ensureCronJobViaCLI(logger);
302
+ }