@upx-us/shield 0.3.16 → 0.4.36

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.
@@ -0,0 +1,477 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || (function () {
19
+ var ownKeys = function(o) {
20
+ ownKeys = Object.getOwnPropertyNames || function (o) {
21
+ var ar = [];
22
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
23
+ return ar;
24
+ };
25
+ return ownKeys(o);
26
+ };
27
+ return function (mod) {
28
+ if (mod && mod.__esModule) return mod;
29
+ var result = {};
30
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
31
+ __setModuleDefault(result, mod);
32
+ return result;
33
+ };
34
+ })();
35
+ Object.defineProperty(exports, "__esModule", { value: true });
36
+ exports.parseSemVer = parseSemVer;
37
+ exports.isNewerVersion = isNewerVersion;
38
+ exports.classifyUpdate = classifyUpdate;
39
+ exports.loadUpdateState = loadUpdateState;
40
+ exports.saveUpdateState = saveUpdateState;
41
+ exports.checkNpmVersion = checkNpmVersion;
42
+ exports.checkForUpdate = checkForUpdate;
43
+ exports.backupCurrentVersion = backupCurrentVersion;
44
+ exports.restoreFromBackup = restoreFromBackup;
45
+ exports.downloadAndInstall = downloadAndInstall;
46
+ exports.performAutoUpdate = performAutoUpdate;
47
+ exports.requestGatewayRestart = requestGatewayRestart;
48
+ const child_process_1 = require("child_process");
49
+ const fs_1 = require("fs");
50
+ const safe_io_1 = require("./safe-io");
51
+ const path_1 = require("path");
52
+ const os_1 = require("os");
53
+ const crypto_1 = require("crypto");
54
+ const log = __importStar(require("./log"));
55
+ const version_1 = require("./version");
56
+ const PACKAGE_NAME = '@upx-us/shield';
57
+ const CHECK_INTERVAL_MS = 6 * 60 * 60 * 1000;
58
+ const MIN_CHECK_INTERVAL_MS = 60 * 1000;
59
+ const PLUGIN_DIR = (0, path_1.join)((0, os_1.homedir)(), '.openclaw', 'extensions', 'shield');
60
+ const BACKUP_DIR = (0, path_1.join)((0, os_1.homedir)(), '.openclaw', 'shield', 'data', 'backup');
61
+ const UPDATE_STATE_FILE = (0, path_1.join)((0, os_1.homedir)(), '.openclaw', 'shield', 'data', 'update-state.json');
62
+ const MAX_BACKUPS = 2;
63
+ function parseSemVer(version) {
64
+ const match = version.match(/^(\d+)\.(\d+)\.(\d+)(?:-(.+))?$/);
65
+ if (!match)
66
+ return null;
67
+ return {
68
+ major: parseInt(match[1], 10),
69
+ minor: parseInt(match[2], 10),
70
+ patch: parseInt(match[3], 10),
71
+ prerelease: match[4] || null,
72
+ };
73
+ }
74
+ function isNewerVersion(current, candidate) {
75
+ const cur = parseSemVer(current);
76
+ const cand = parseSemVer(candidate);
77
+ if (!cur || !cand)
78
+ return false;
79
+ if (cand.major !== cur.major)
80
+ return cand.major > cur.major;
81
+ if (cand.minor !== cur.minor)
82
+ return cand.minor > cur.minor;
83
+ if (cand.patch !== cur.patch)
84
+ return cand.patch > cur.patch;
85
+ if (cur.prerelease && !cand.prerelease)
86
+ return true;
87
+ return false;
88
+ }
89
+ function classifyUpdate(current, candidate) {
90
+ const cur = parseSemVer(current);
91
+ const cand = parseSemVer(candidate);
92
+ if (!cur || !cand)
93
+ return { isPatch: false, isMinor: false, isMajor: false };
94
+ return {
95
+ isMajor: cand.major > cur.major,
96
+ isMinor: cand.major === cur.major && cand.minor > cur.minor,
97
+ isPatch: cand.major === cur.major && cand.minor === cur.minor && cand.patch > cur.patch,
98
+ };
99
+ }
100
+ function ensureDir(dir) {
101
+ if (!(0, fs_1.existsSync)(dir))
102
+ (0, fs_1.mkdirSync)(dir, { recursive: true });
103
+ }
104
+ function loadUpdateState() {
105
+ const defaults = {
106
+ lastCheckAt: 0,
107
+ lastUpdateAt: 0,
108
+ currentVersion: version_1.VERSION,
109
+ latestVersion: null,
110
+ updateAvailable: false,
111
+ lastError: null,
112
+ rollbackVersion: null,
113
+ consecutiveFailures: 0,
114
+ };
115
+ if (!(0, fs_1.existsSync)(UPDATE_STATE_FILE))
116
+ return defaults;
117
+ const loaded = (0, safe_io_1.readJsonSafe)(UPDATE_STATE_FILE, {}, 'update-state');
118
+ return { ...defaults, ...loaded };
119
+ }
120
+ function saveUpdateState(state) {
121
+ ensureDir((0, path_1.dirname)(UPDATE_STATE_FILE));
122
+ (0, safe_io_1.writeJsonSafe)(UPDATE_STATE_FILE, state);
123
+ }
124
+ function checkNpmVersion() {
125
+ try {
126
+ const result = (0, child_process_1.execSync)(`npm view ${PACKAGE_NAME} version`, {
127
+ encoding: 'utf-8',
128
+ timeout: 15_000,
129
+ stdio: ['pipe', 'pipe', 'pipe'],
130
+ }).trim();
131
+ if (!parseSemVer(result)) {
132
+ log.warn('updater', `npm returned invalid version: ${result}`);
133
+ return null;
134
+ }
135
+ return result;
136
+ }
137
+ catch (err) {
138
+ log.warn('updater', `Failed to check npm: ${err instanceof Error ? err.message : String(err)}`);
139
+ return null;
140
+ }
141
+ }
142
+ function checkForUpdate(overrideInterval) {
143
+ const state = loadUpdateState();
144
+ const now = Date.now();
145
+ const interval = overrideInterval ?? CHECK_INTERVAL_MS;
146
+ if (now - state.lastCheckAt < Math.max(interval, MIN_CHECK_INTERVAL_MS)) {
147
+ if (state.updateAvailable && state.latestVersion) {
148
+ const classification = classifyUpdate(version_1.VERSION, state.latestVersion);
149
+ return {
150
+ updateAvailable: true,
151
+ currentVersion: version_1.VERSION,
152
+ latestVersion: state.latestVersion,
153
+ ...classification,
154
+ };
155
+ }
156
+ return null;
157
+ }
158
+ log.info('updater', `Checking npm for updates (current: ${version_1.VERSION})...`);
159
+ const latestVersion = checkNpmVersion();
160
+ state.lastCheckAt = now;
161
+ state.currentVersion = version_1.VERSION;
162
+ if (!latestVersion) {
163
+ state.lastError = 'Failed to query npm registry';
164
+ saveUpdateState(state);
165
+ return null;
166
+ }
167
+ state.latestVersion = latestVersion;
168
+ if (isNewerVersion(version_1.VERSION, latestVersion)) {
169
+ state.updateAvailable = true;
170
+ state.lastError = null;
171
+ saveUpdateState(state);
172
+ const classification = classifyUpdate(version_1.VERSION, latestVersion);
173
+ log.info('updater', `Update available: ${version_1.VERSION} → ${latestVersion} (${classification.isPatch ? 'patch' : classification.isMinor ? 'minor' : 'major'})`);
174
+ return {
175
+ updateAvailable: true,
176
+ currentVersion: version_1.VERSION,
177
+ latestVersion,
178
+ ...classification,
179
+ };
180
+ }
181
+ state.updateAvailable = false;
182
+ state.lastError = null;
183
+ saveUpdateState(state);
184
+ return null;
185
+ }
186
+ function backupCurrentVersion() {
187
+ if (!(0, fs_1.existsSync)(PLUGIN_DIR))
188
+ return null;
189
+ ensureDir(BACKUP_DIR);
190
+ const backupName = `shield-${version_1.VERSION}-${Date.now()}`;
191
+ const backupPath = (0, path_1.join)(BACKUP_DIR, backupName);
192
+ try {
193
+ (0, fs_1.mkdirSync)(backupPath, { recursive: true });
194
+ const filesToBackup = ['package.json', 'openclaw.plugin.json', 'LICENSE', 'README.md'];
195
+ for (const f of filesToBackup) {
196
+ const src = (0, path_1.join)(PLUGIN_DIR, f);
197
+ if ((0, fs_1.existsSync)(src))
198
+ (0, fs_1.copyFileSync)(src, (0, path_1.join)(backupPath, f));
199
+ }
200
+ const distSrc = (0, path_1.join)(PLUGIN_DIR, 'dist');
201
+ if ((0, fs_1.existsSync)(distSrc)) {
202
+ copyRecursive(distSrc, (0, path_1.join)(backupPath, 'dist'));
203
+ }
204
+ const skillsSrc = (0, path_1.join)(PLUGIN_DIR, 'skills');
205
+ if ((0, fs_1.existsSync)(skillsSrc)) {
206
+ copyRecursive(skillsSrc, (0, path_1.join)(backupPath, 'skills'));
207
+ }
208
+ pruneBackups();
209
+ log.info('updater', `Backup created: ${backupName}`);
210
+ return backupPath;
211
+ }
212
+ catch (err) {
213
+ log.error('updater', `Backup failed: ${err instanceof Error ? err.message : String(err)}`);
214
+ return null;
215
+ }
216
+ }
217
+ function copyRecursive(src, dest) {
218
+ (0, fs_1.mkdirSync)(dest, { recursive: true });
219
+ for (const entry of (0, fs_1.readdirSync)(src, { withFileTypes: true })) {
220
+ const srcPath = (0, path_1.join)(src, entry.name);
221
+ const destPath = (0, path_1.join)(dest, entry.name);
222
+ if (entry.isDirectory()) {
223
+ copyRecursive(srcPath, destPath);
224
+ }
225
+ else {
226
+ (0, fs_1.copyFileSync)(srcPath, destPath);
227
+ }
228
+ }
229
+ }
230
+ function pruneBackups() {
231
+ if (!(0, fs_1.existsSync)(BACKUP_DIR))
232
+ return;
233
+ const backups = (0, fs_1.readdirSync)(BACKUP_DIR)
234
+ .filter(d => d.startsWith('shield-'))
235
+ .map(d => ({ name: d, path: (0, path_1.join)(BACKUP_DIR, d), stat: (0, fs_1.statSync)((0, path_1.join)(BACKUP_DIR, d)) }))
236
+ .sort((a, b) => b.stat.mtimeMs - a.stat.mtimeMs);
237
+ for (const old of backups.slice(MAX_BACKUPS)) {
238
+ try {
239
+ (0, fs_1.rmSync)(old.path, { recursive: true, force: true });
240
+ log.info('updater', `Pruned old backup: ${old.name}`);
241
+ }
242
+ catch { }
243
+ }
244
+ }
245
+ function restoreFromBackup(backupPath) {
246
+ if (!(0, fs_1.existsSync)(backupPath)) {
247
+ log.error('updater', `Backup not found: ${backupPath}`);
248
+ return false;
249
+ }
250
+ try {
251
+ const backupPkg = (0, path_1.join)(backupPath, 'package.json');
252
+ if (!(0, fs_1.existsSync)(backupPkg)) {
253
+ log.error('updater', 'Backup is corrupt — no package.json');
254
+ return false;
255
+ }
256
+ for (const entry of (0, fs_1.readdirSync)(PLUGIN_DIR)) {
257
+ if (entry === 'node_modules')
258
+ continue;
259
+ const fullPath = (0, path_1.join)(PLUGIN_DIR, entry);
260
+ (0, fs_1.rmSync)(fullPath, { recursive: true, force: true });
261
+ }
262
+ copyRecursive(backupPath, PLUGIN_DIR);
263
+ const restoredVersion = JSON.parse((0, fs_1.readFileSync)(backupPkg, 'utf-8')).version;
264
+ log.info('updater', `Restored from backup: ${restoredVersion}`);
265
+ return true;
266
+ }
267
+ catch (err) {
268
+ log.error('updater', `Restore failed: ${err instanceof Error ? err.message : String(err)}`);
269
+ return false;
270
+ }
271
+ }
272
+ function updateOpenClawPluginMetadata(newVersion, shasum) {
273
+ const configPath = (0, path_1.join)((0, os_1.homedir)(), '.openclaw', 'openclaw.json');
274
+ try {
275
+ if (!(0, fs_1.existsSync)(configPath))
276
+ return;
277
+ const config = JSON.parse((0, fs_1.readFileSync)(configPath, 'utf-8'));
278
+ if (!config.plugins?.installs?.shield)
279
+ return;
280
+ const install = config.plugins.installs.shield;
281
+ install.spec = `${PACKAGE_NAME}@${newVersion}`;
282
+ install.version = newVersion;
283
+ install.resolvedVersion = newVersion;
284
+ install.resolvedSpec = `${PACKAGE_NAME}@${newVersion}`;
285
+ delete install.integrity;
286
+ if (shasum) {
287
+ install.shasum = shasum;
288
+ }
289
+ else {
290
+ delete install.shasum;
291
+ }
292
+ install.installedAt = new Date().toISOString();
293
+ (0, safe_io_1.writeJsonSafe)(configPath, config);
294
+ log.info('updater', `Updated openclaw.json plugin metadata → ${newVersion}`);
295
+ }
296
+ catch (err) {
297
+ log.warn('updater', `Failed to update openclaw.json metadata: ${err instanceof Error ? err.message : String(err)}`);
298
+ }
299
+ }
300
+ function downloadAndInstall(targetVersion) {
301
+ const tmpDir = (0, path_1.join)((0, os_1.homedir)(), '.openclaw', 'shield', 'data', 'tmp-update');
302
+ try {
303
+ if ((0, fs_1.existsSync)(tmpDir))
304
+ (0, fs_1.rmSync)(tmpDir, { recursive: true, force: true });
305
+ (0, fs_1.mkdirSync)(tmpDir, { recursive: true });
306
+ log.info('updater', `Downloading ${PACKAGE_NAME}@${targetVersion}...`);
307
+ (0, child_process_1.execSync)(`npm pack ${PACKAGE_NAME}@${targetVersion}`, {
308
+ cwd: tmpDir,
309
+ encoding: 'utf-8',
310
+ timeout: 60_000,
311
+ stdio: ['pipe', 'pipe', 'pipe'],
312
+ });
313
+ const tarballs = (0, fs_1.readdirSync)(tmpDir).filter(f => f.endsWith('.tgz'));
314
+ if (tarballs.length === 0) {
315
+ log.error('updater', 'npm pack produced no tarball');
316
+ return false;
317
+ }
318
+ const tarball = (0, path_1.join)(tmpDir, tarballs[0]);
319
+ const tarballShasum = (0, crypto_1.createHash)('sha1').update((0, fs_1.readFileSync)(tarball)).digest('hex');
320
+ const stagingDir = (0, path_1.join)(tmpDir, 'staging');
321
+ (0, fs_1.mkdirSync)(stagingDir, { recursive: true });
322
+ (0, child_process_1.execSync)(`tar xzf "${tarball}" -C "${stagingDir}" --strip-components=1`, {
323
+ timeout: 30_000,
324
+ stdio: ['pipe', 'pipe', 'pipe'],
325
+ });
326
+ const pkgPath = (0, path_1.join)(stagingDir, 'package.json');
327
+ if (!(0, fs_1.existsSync)(pkgPath)) {
328
+ log.error('updater', 'Downloaded package has no package.json');
329
+ return false;
330
+ }
331
+ const pkg = JSON.parse((0, fs_1.readFileSync)(pkgPath, 'utf-8'));
332
+ if (pkg.version !== targetVersion) {
333
+ log.error('updater', `Version mismatch: expected ${targetVersion}, got ${pkg.version}`);
334
+ return false;
335
+ }
336
+ const pluginJsonPath = (0, path_1.join)(stagingDir, 'openclaw.plugin.json');
337
+ if ((0, fs_1.existsSync)(pluginJsonPath)) {
338
+ const pluginJson = JSON.parse((0, fs_1.readFileSync)(pluginJsonPath, 'utf-8'));
339
+ if (pluginJson.version !== targetVersion) {
340
+ log.error('updater', `Plugin manifest version mismatch: expected ${targetVersion}, got ${pluginJson.version}`);
341
+ return false;
342
+ }
343
+ }
344
+ if (!(0, fs_1.existsSync)((0, path_1.join)(stagingDir, 'dist', 'index.js'))) {
345
+ log.error('updater', 'Downloaded package missing dist/index.js');
346
+ return false;
347
+ }
348
+ for (const entry of (0, fs_1.readdirSync)(PLUGIN_DIR)) {
349
+ if (entry === 'node_modules')
350
+ continue;
351
+ (0, fs_1.rmSync)((0, path_1.join)(PLUGIN_DIR, entry), { recursive: true, force: true });
352
+ }
353
+ copyRecursive(stagingDir, PLUGIN_DIR);
354
+ updateOpenClawPluginMetadata(targetVersion, tarballShasum);
355
+ log.info('updater', `Installed ${PACKAGE_NAME}@${targetVersion} successfully`);
356
+ return true;
357
+ }
358
+ catch (err) {
359
+ log.error('updater', `Download/install failed: ${err instanceof Error ? err.message : String(err)}`);
360
+ return false;
361
+ }
362
+ finally {
363
+ try {
364
+ (0, fs_1.rmSync)(tmpDir, { recursive: true, force: true });
365
+ }
366
+ catch { }
367
+ }
368
+ }
369
+ function performAutoUpdate(mode, checkIntervalMs) {
370
+ const noOp = { action: 'none', fromVersion: version_1.VERSION, toVersion: null, message: '', requiresRestart: false };
371
+ if (mode === false)
372
+ return noOp;
373
+ const check = checkForUpdate(checkIntervalMs);
374
+ if (!check || !check.updateAvailable)
375
+ return noOp;
376
+ if (mode === 'notify-only') {
377
+ const kind = check.isPatch ? 'patch' : check.isMinor ? 'minor' : 'major';
378
+ return {
379
+ action: 'notify',
380
+ fromVersion: version_1.VERSION,
381
+ toVersion: check.latestVersion,
382
+ message: `Shield update available: ${version_1.VERSION} → ${check.latestVersion} (${kind}). Set autoUpdate: true to enable automatic updates.`,
383
+ requiresRestart: false,
384
+ };
385
+ }
386
+ if (check.isMajor) {
387
+ return {
388
+ action: 'notify',
389
+ fromVersion: version_1.VERSION,
390
+ toVersion: check.latestVersion,
391
+ message: `Shield major update available: ${version_1.VERSION} → ${check.latestVersion}. Major updates require manual installation. Run: openclaw plugins update shield`,
392
+ requiresRestart: false,
393
+ };
394
+ }
395
+ const state = loadUpdateState();
396
+ log.info('updater', `Auto-updating: ${version_1.VERSION} → ${check.latestVersion}`);
397
+ const backupPath = backupCurrentVersion();
398
+ if (!backupPath) {
399
+ state.consecutiveFailures++;
400
+ state.lastError = 'Backup failed — update aborted';
401
+ saveUpdateState(state);
402
+ return {
403
+ action: 'error',
404
+ fromVersion: version_1.VERSION,
405
+ toVersion: check.latestVersion,
406
+ message: 'Auto-update aborted: failed to backup current version',
407
+ requiresRestart: false,
408
+ };
409
+ }
410
+ const installed = downloadAndInstall(check.latestVersion);
411
+ if (!installed) {
412
+ log.warn('updater', 'Install failed — rolling back...');
413
+ const restored = restoreFromBackup(backupPath);
414
+ state.consecutiveFailures++;
415
+ state.lastError = 'Install failed — rolled back';
416
+ saveUpdateState(state);
417
+ return {
418
+ action: restored ? 'rollback' : 'error',
419
+ fromVersion: version_1.VERSION,
420
+ toVersion: check.latestVersion,
421
+ message: restored
422
+ ? `Auto-update to ${check.latestVersion} failed — rolled back to ${version_1.VERSION}`
423
+ : `Auto-update to ${check.latestVersion} failed and rollback failed! Manual intervention needed.`,
424
+ requiresRestart: !restored,
425
+ };
426
+ }
427
+ try {
428
+ const newPkg = JSON.parse((0, fs_1.readFileSync)((0, path_1.join)(PLUGIN_DIR, 'package.json'), 'utf-8'));
429
+ if (newPkg.version !== check.latestVersion) {
430
+ throw new Error(`Post-install version mismatch: ${newPkg.version} !== ${check.latestVersion}`);
431
+ }
432
+ }
433
+ catch (err) {
434
+ log.error('updater', `Post-install validation failed: ${err instanceof Error ? err.message : String(err)}`);
435
+ log.warn('updater', 'Rolling back...');
436
+ restoreFromBackup(backupPath);
437
+ state.consecutiveFailures++;
438
+ state.lastError = 'Post-install validation failed — rolled back';
439
+ saveUpdateState(state);
440
+ return {
441
+ action: 'rollback',
442
+ fromVersion: version_1.VERSION,
443
+ toVersion: check.latestVersion,
444
+ message: `Auto-update to ${check.latestVersion} failed validation — rolled back to ${version_1.VERSION}`,
445
+ requiresRestart: false,
446
+ };
447
+ }
448
+ state.lastUpdateAt = Date.now();
449
+ state.updateAvailable = false;
450
+ state.rollbackVersion = version_1.VERSION;
451
+ state.currentVersion = check.latestVersion;
452
+ state.consecutiveFailures = 0;
453
+ state.lastError = null;
454
+ saveUpdateState(state);
455
+ log.info('updater', `✅ Auto-updated: ${version_1.VERSION} → ${check.latestVersion}. Gateway restart required to load new version.`);
456
+ return {
457
+ action: 'updated',
458
+ fromVersion: version_1.VERSION,
459
+ toVersion: check.latestVersion,
460
+ message: `Shield auto-updated: ${version_1.VERSION} → ${check.latestVersion}. Gateway restart required.`,
461
+ requiresRestart: true,
462
+ };
463
+ }
464
+ function requestGatewayRestart() {
465
+ try {
466
+ (0, child_process_1.execSync)('openclaw gateway restart', {
467
+ timeout: 30_000,
468
+ stdio: ['pipe', 'pipe', 'pipe'],
469
+ });
470
+ log.info('updater', 'Gateway restart requested');
471
+ return true;
472
+ }
473
+ catch (err) {
474
+ log.warn('updater', `Gateway restart failed: ${err instanceof Error ? err.message : String(err)}`);
475
+ return false;
476
+ }
477
+ }
@@ -1,60 +1,84 @@
1
1
  {
2
- "id": "shield",
3
- "name": "OpenClaw Shield",
4
- "description": "Real-time security monitoring streams enriched, redacted security events to the Shield detection platform.",
5
- "version": "0.3.16",
6
- "skills": [
7
- "./skills"
8
- ],
9
- "configSchema": {
10
- "type": "object",
11
- "additionalProperties": false,
12
- "properties": {
13
- "installationKey": {
14
- "type": "string",
15
- "description": "One-time installation key from the Shield portal. The plugin uses this to register the instance and obtain credentials automatically. Can be removed from config after first activation."
16
- },
17
- "enabled": {
18
- "type": "boolean",
19
- "default": true
20
- },
21
- "dryRun": {
22
- "type": "boolean",
23
- "default": false
24
- },
25
- "redactionEnabled": {
26
- "type": "boolean",
27
- "default": true
28
- },
29
- "pollIntervalMs": {
30
- "type": "number",
31
- "default": 30000
32
- },
33
- "collectHostMetrics": {
34
- "type": "boolean",
35
- "default": false
36
- }
37
- }
2
+ "id": "shield",
3
+ "name": "OpenClaw Shield",
4
+ "description": "Real-time security monitoring \u2014 streams enriched, redacted security events to the Shield detection platform.",
5
+ "version": "0.4.36",
6
+ "skills": [
7
+ "./skills"
8
+ ],
9
+ "configSchema": {
10
+ "type": "object",
11
+ "additionalProperties": false,
12
+ "properties": {
13
+ "installationKey": {
14
+ "type": "string",
15
+ "description": "One-time installation key from the Shield portal. The plugin uses this to register the instance and obtain credentials automatically. Can be removed from config after first activation."
16
+ },
17
+ "enabled": {
18
+ "type": "boolean",
19
+ "default": true
20
+ },
21
+ "dryRun": {
22
+ "type": "boolean",
23
+ "default": false
24
+ },
25
+ "redactionEnabled": {
26
+ "type": "boolean",
27
+ "default": true
28
+ },
29
+ "pollIntervalMs": {
30
+ "type": "number",
31
+ "default": 30000
32
+ },
33
+ "collectHostMetrics": {
34
+ "type": "boolean",
35
+ "default": false
36
+ },
37
+ "autoUpdate": {
38
+ "oneOf": [
39
+ {
40
+ "type": "boolean"
41
+ },
42
+ {
43
+ "type": "string",
44
+ "enum": [
45
+ "notify-only"
46
+ ]
47
+ }
48
+ ],
49
+ "default": true,
50
+ "description": "Auto-update mode: true (auto-update patch versions), false (disabled), or 'notify-only' (log available updates without installing)."
51
+ }
52
+ }
53
+ },
54
+ "uiHints": {
55
+ "installationKey": {
56
+ "label": "Installation Key",
57
+ "description": "One-time key from the Shield portal (https://uss.upx.com). Required for first-time activation only."
58
+ },
59
+ "enabled": {
60
+ "label": "Enable security monitoring"
61
+ },
62
+ "dryRun": {
63
+ "label": "Dry run (log events locally, do not transmit)"
64
+ },
65
+ "redactionEnabled": {
66
+ "label": "Redact sensitive values before transmitting"
67
+ },
68
+ "pollIntervalMs": {
69
+ "label": "Polling interval (milliseconds)"
70
+ },
71
+ "collectHostMetrics": {
72
+ "label": "Collect host telemetry metrics"
38
73
  },
39
- "uiHints": {
40
- "installationKey": {
41
- "label": "Installation Key",
42
- "description": "One-time key from the Shield portal (https://uss.upx.com). Required for first-time activation only."
43
- },
44
- "enabled": {
45
- "label": "Enable security monitoring"
46
- },
47
- "dryRun": {
48
- "label": "Dry run (log events locally, do not transmit)"
49
- },
50
- "redactionEnabled": {
51
- "label": "Redact sensitive values before transmitting"
52
- },
53
- "pollIntervalMs": {
54
- "label": "Polling interval (milliseconds)"
55
- },
56
- "collectHostMetrics": {
57
- "label": "Collect host telemetry metrics"
58
- }
74
+ "autoUpdate": {
75
+ "label": "Auto-update mode",
76
+ "description": "true = auto-install patch updates, 'notify-only' = log only, false = disabled"
59
77
  }
60
- }
78
+ },
79
+ "clawhub": {
80
+ "slug": "openclaw-shield-upx",
81
+ "skillVersion": "1.0.2",
82
+ "note": "ClawHub auto-increments on publish. Update this after each clawhub submission."
83
+ }
84
+ }