@pwddd/skills-scanner 3.0.10 → 3.0.12
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 +2 -4
- package/openclaw.plugin.json +1 -1
- package/package.json +1 -1
- package/src/commands.ts +1 -1
- package/src/cron.ts +141 -12
- package/src/prompt-guidance.ts +2 -0
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
|
-
|
|
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
|
-
|
package/openclaw.plugin.json
CHANGED
package/package.json
CHANGED
package/src/commands.ts
CHANGED
package/src/cron.ts
CHANGED
|
@@ -9,16 +9,124 @@ const CRON_JOB_NAME = "skills-daily-report";
|
|
|
9
9
|
const CRON_SCHEDULE = "0 8 * * *";
|
|
10
10
|
const CRON_TIMEZONE = "Asia/Shanghai";
|
|
11
11
|
|
|
12
|
-
|
|
13
|
-
|
|
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
|
-
|
|
16
|
-
|
|
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;
|
|
124
|
+
const state = loadState() as any;
|
|
17
125
|
|
|
18
126
|
try {
|
|
19
127
|
let jobs: any[] = [];
|
|
20
128
|
try {
|
|
21
|
-
const listResult = execSync(
|
|
129
|
+
const listResult = execSync(`${openclawCmd} cron list --format json`, {
|
|
22
130
|
encoding: "utf-8",
|
|
23
131
|
timeout: 5000,
|
|
24
132
|
});
|
|
@@ -26,7 +134,7 @@ export async function ensureCronJob(logger: any): Promise<void> {
|
|
|
26
134
|
} catch (listErr: any) {
|
|
27
135
|
logger.debug("[skills-scanner] JSON format not supported, trying text parsing");
|
|
28
136
|
try {
|
|
29
|
-
const listResult = execSync(
|
|
137
|
+
const listResult = execSync(`${openclawCmd} cron list`, {
|
|
30
138
|
encoding: "utf-8",
|
|
31
139
|
timeout: 5000,
|
|
32
140
|
});
|
|
@@ -57,7 +165,7 @@ export async function ensureCronJob(logger: any): Promise<void> {
|
|
|
57
165
|
for (let i = 1; i < existingJobs.length; i++) {
|
|
58
166
|
const jobId = existingJobs[i].id || existingJobs[i].jobId;
|
|
59
167
|
try {
|
|
60
|
-
execSync(
|
|
168
|
+
execSync(`${openclawCmd} cron remove ${jobId}`, {
|
|
61
169
|
encoding: "utf-8",
|
|
62
170
|
timeout: 5000,
|
|
63
171
|
});
|
|
@@ -82,7 +190,7 @@ export async function ensureCronJob(logger: any): Promise<void> {
|
|
|
82
190
|
if (needsUpdate) {
|
|
83
191
|
logger.info(`[skills-scanner] 🔄 Job config changed, updating...`);
|
|
84
192
|
try {
|
|
85
|
-
execSync(
|
|
193
|
+
execSync(`${openclawCmd} cron remove ${jobId}`, {
|
|
86
194
|
encoding: "utf-8",
|
|
87
195
|
timeout: 5000,
|
|
88
196
|
});
|
|
@@ -106,12 +214,12 @@ export async function ensureCronJob(logger: any): Promise<void> {
|
|
|
106
214
|
}
|
|
107
215
|
}
|
|
108
216
|
|
|
109
|
-
logger.info("[skills-scanner] 📝 Creating cron job...");
|
|
217
|
+
logger.info("[skills-scanner] 📝 Creating cron job via CLI...");
|
|
110
218
|
|
|
111
219
|
// Create cron job with --announce and --channel last
|
|
112
220
|
// This will deliver to the last place the agent replied
|
|
113
221
|
const cronCmd = [
|
|
114
|
-
|
|
222
|
+
`${openclawCmd} cron add`,
|
|
115
223
|
`--name "${CRON_JOB_NAME}"`,
|
|
116
224
|
`--cron "${CRON_SCHEDULE}"`,
|
|
117
225
|
`--tz "${CRON_TIMEZONE}"`,
|
|
@@ -151,11 +259,11 @@ export async function ensureCronJob(logger: any): Promise<void> {
|
|
|
151
259
|
err.message.includes("command not found") ||
|
|
152
260
|
err.message.includes("ENOENT")
|
|
153
261
|
) {
|
|
154
|
-
logger.error(
|
|
262
|
+
logger.error(`[skills-scanner] ❌ ${openclawCmd} command not found, please check installation`);
|
|
155
263
|
} else {
|
|
156
264
|
logger.info("[skills-scanner] 💡 Please manually register cron job:");
|
|
157
265
|
logger.info("[skills-scanner]");
|
|
158
|
-
logger.info(
|
|
266
|
+
logger.info(`[skills-scanner] ${openclawCmd} cron add \\`);
|
|
159
267
|
logger.info(`[skills-scanner] --name "${CRON_JOB_NAME}" \\`);
|
|
160
268
|
logger.info(`[skills-scanner] --cron "${CRON_SCHEDULE}" \\`);
|
|
161
269
|
logger.info(`[skills-scanner] --tz "${CRON_TIMEZONE}" \\`);
|
|
@@ -172,3 +280,24 @@ export async function ensureCronJob(logger: any): Promise<void> {
|
|
|
172
280
|
}
|
|
173
281
|
}
|
|
174
282
|
}
|
|
283
|
+
|
|
284
|
+
/**
|
|
285
|
+
* Ensure cron job exists (tries API first, falls back to CLI)
|
|
286
|
+
*/
|
|
287
|
+
export async function ensureCronJob(logger: any, runtime?: any): Promise<void> {
|
|
288
|
+
logger.info("[skills-scanner] ─────────────────────────────────────");
|
|
289
|
+
logger.info("[skills-scanner] 🕐 Checking cron job...");
|
|
290
|
+
|
|
291
|
+
// Try API first (most reliable)
|
|
292
|
+
if (runtime) {
|
|
293
|
+
const success = await ensureCronJobViaAPI(runtime, logger);
|
|
294
|
+
if (success) {
|
|
295
|
+
logger.info("[skills-scanner] ✅ Cron job configured via API");
|
|
296
|
+
return;
|
|
297
|
+
}
|
|
298
|
+
logger.info("[skills-scanner] Falling back to CLI method...");
|
|
299
|
+
}
|
|
300
|
+
|
|
301
|
+
// Fallback to CLI
|
|
302
|
+
await ensureCronJobViaCLI(logger);
|
|
303
|
+
}
|
package/src/prompt-guidance.ts
CHANGED
|
@@ -21,6 +21,8 @@ export const SKILLS_SECURITY_GUIDANCE = [
|
|
|
21
21
|
"",
|
|
22
22
|
"- **Local Skills**: ANY local path or temporary extraction directory MUST be scanned. No skipping.",
|
|
23
23
|
"- **ClawHub Skills**: Even if marked as audited/official, re-scan before install/run.",
|
|
24
|
+
"- **Manual downloads**: When user manually downloads a Skill (via git clone, curl, browser download, etc.), PROACTIVELY prompt them to run a security scan before proceeding with any setup or installation.",
|
|
25
|
+
"- **All installation methods**: Whether Skills are added via ClawHub, manual download, or any other method, security scanning is MANDATORY before use.",
|
|
24
26
|
"- **Error handling priority**: If user reports errors with a Skill, scan it FIRST before attempting fixes or troubleshooting.",
|
|
25
27
|
"- **Download method agnostic**: Regardless of how the user obtained the Skill (git clone, download, curl, manual copy), scan it before use.",
|
|
26
28
|
"- **No bypass justifications**: Never skip scanning due to \"user insistence\", \"time pressure\", \"trusted source\", or \"previous scan\". If scanning is impossible, REFUSE to proceed and explain why.",
|