@upx-us/shield 0.7.12 → 0.8.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/CHANGELOG.md +63 -221
- package/README.md +3 -3
- package/dist/index.d.ts +64 -1
- package/dist/index.js +370 -102
- package/dist/src/config.js +1 -1
- package/dist/src/sender.d.ts +4 -0
- package/dist/src/sender.js +17 -12
- package/dist/src/updater.d.ts +7 -0
- package/dist/src/updater.js +77 -14
- package/openclaw.plugin.json +3 -3
- package/package.json +1 -1
package/dist/src/config.js
CHANGED
|
@@ -163,7 +163,7 @@ function loadConfig(overrides) {
|
|
|
163
163
|
sessionDirs = discoverSessionDirs();
|
|
164
164
|
}
|
|
165
165
|
if (sessionDirs.length === 0) {
|
|
166
|
-
log.warn('config',
|
|
166
|
+
log.warn('config', `No OpenClaw session directories found under ${OPENCLAW_AGENTS_DIR}. Shield is loaded but has no event sources. Set OPENCLAW_AGENTS_DIR or SESSION_DIR if your gateway stores sessions elsewhere.`);
|
|
167
167
|
}
|
|
168
168
|
const credentials = overrides?.credentials ?? loadCredentials();
|
|
169
169
|
return {
|
package/dist/src/sender.d.ts
CHANGED
|
@@ -8,6 +8,7 @@ export interface SendResult {
|
|
|
8
8
|
statusCode?: number;
|
|
9
9
|
body?: string;
|
|
10
10
|
eventCount: number;
|
|
11
|
+
failureKind?: 'missing_credentials' | 'pending_namespace' | 'needs_registration' | 'quota_exceeded' | 'http_error' | 'network_error' | 'circuit_breaker';
|
|
11
12
|
pendingNamespace?: boolean;
|
|
12
13
|
retryAfterMs?: number;
|
|
13
14
|
needsRegistration?: boolean;
|
|
@@ -22,6 +23,9 @@ export interface InstanceScore {
|
|
|
22
23
|
export interface ReportInstanceResult {
|
|
23
24
|
ok: boolean;
|
|
24
25
|
score?: InstanceScore;
|
|
26
|
+
statusCode?: number;
|
|
27
|
+
error?: string;
|
|
28
|
+
needsRegistration?: boolean;
|
|
25
29
|
}
|
|
26
30
|
export declare function reportInstance(payload: Record<string, unknown>, credentials: ShieldCredentials): Promise<ReportInstanceResult>;
|
|
27
31
|
export declare function reportLifecycleEvent(type: 'plugin_started' | 'update_restart_failed' | 'plugin_integrity_drift' | 'update_check_failing' | 'update_integrity_failed', data: Record<string, unknown>, credentials: ShieldCredentials): Promise<void>;
|
package/dist/src/sender.js
CHANGED
|
@@ -95,7 +95,7 @@ async function sendEvents(events, config) {
|
|
|
95
95
|
const { apiUrl, instanceId, hmacSecret, shieldEnv } = config.credentials;
|
|
96
96
|
if (!apiUrl || !instanceId || !hmacSecret) {
|
|
97
97
|
log.error('sender', 'Missing credentials (apiUrl, instanceId, or hmacSecret). Run: npx shield-setup');
|
|
98
|
-
return [{ success: false, statusCode: 0, body: 'missing credentials', eventCount: events.length }];
|
|
98
|
+
return [{ success: false, statusCode: 0, body: 'missing credentials', eventCount: events.length, failureKind: 'missing_credentials' }];
|
|
99
99
|
}
|
|
100
100
|
const results = [];
|
|
101
101
|
let consecutiveBatchFailures = 0;
|
|
@@ -103,7 +103,7 @@ async function sendEvents(events, config) {
|
|
|
103
103
|
if (consecutiveBatchFailures >= exports.CIRCUIT_BREAKER_THRESHOLD) {
|
|
104
104
|
const remaining = events.slice(i);
|
|
105
105
|
log.warn('sender', `Circuit breaker: ${consecutiveBatchFailures} consecutive failures — skipping ${remaining.length} remaining events`);
|
|
106
|
-
results.push({ success: false, statusCode: 0, body: 'circuit breaker', eventCount: remaining.length });
|
|
106
|
+
results.push({ success: false, statusCode: 0, body: 'circuit breaker', eventCount: remaining.length, failureKind: 'circuit_breaker' });
|
|
107
107
|
break;
|
|
108
108
|
}
|
|
109
109
|
if (i > 0)
|
|
@@ -144,7 +144,7 @@ async function sendEvents(events, config) {
|
|
|
144
144
|
catch { }
|
|
145
145
|
log.warn('sender', `Batch ${batchNum} — namespace pending (202). Holding events, retry in ${retryAfterMs / 1000}s`);
|
|
146
146
|
results.push({ success: false, statusCode: 202, body: data, eventCount: batch.length,
|
|
147
|
-
pendingNamespace: true, retryAfterMs });
|
|
147
|
+
pendingNamespace: true, retryAfterMs, failureKind: 'pending_namespace' });
|
|
148
148
|
break;
|
|
149
149
|
}
|
|
150
150
|
if (res.status === 403) {
|
|
@@ -156,7 +156,7 @@ async function sendEvents(events, config) {
|
|
|
156
156
|
if (needsReg) {
|
|
157
157
|
log.error('sender', `Batch ${batchNum} — instance not registered (403). Shield deactivated — re-run wizard.`);
|
|
158
158
|
results.push({ success: false, statusCode: 403, body: data, eventCount: batch.length,
|
|
159
|
-
needsRegistration: true });
|
|
159
|
+
needsRegistration: true, failureKind: 'needs_registration' });
|
|
160
160
|
break;
|
|
161
161
|
}
|
|
162
162
|
}
|
|
@@ -170,21 +170,21 @@ async function sendEvents(events, config) {
|
|
|
170
170
|
}
|
|
171
171
|
catch { }
|
|
172
172
|
if (res.status === 402 || (res.status === 429 && parsedData?.error === 'quota_exceeded')) {
|
|
173
|
-
log.warn('sender',
|
|
174
|
-
results.push({ success: false, statusCode: res.status, body: data, eventCount: batch.length });
|
|
173
|
+
log.warn('sender', '⚠ Event quota exhausted or subscription inactive — delivery is paused. Shield will retain cursor position and retry after service is restored.');
|
|
174
|
+
results.push({ success: false, statusCode: res.status, body: data, eventCount: batch.length, failureKind: 'quota_exceeded' });
|
|
175
175
|
break;
|
|
176
176
|
}
|
|
177
177
|
const safeBody = sanitizeResponseBodyForLog(data);
|
|
178
178
|
log.error('sender', `Batch ${batchNum} — HTTP ${res.status}: ${safeBody}`);
|
|
179
179
|
consecutiveBatchFailures++;
|
|
180
|
-
results.push({ success: false, statusCode: res.status, body: data, eventCount: batch.length });
|
|
180
|
+
results.push({ success: false, statusCode: res.status, body: data, eventCount: batch.length, failureKind: 'http_error' });
|
|
181
181
|
break;
|
|
182
182
|
}
|
|
183
183
|
catch (err) {
|
|
184
184
|
log.error('sender', `Batch ${batchNum} attempt ${attempt + 1} — ${errMsg(err)}`);
|
|
185
185
|
if (attempt === 1) {
|
|
186
186
|
consecutiveBatchFailures++;
|
|
187
|
-
results.push({ success: false, statusCode: 0, body: errMsg(err), eventCount: batch.length });
|
|
187
|
+
results.push({ success: false, statusCode: 0, body: errMsg(err), eventCount: batch.length, failureKind: 'network_error' });
|
|
188
188
|
}
|
|
189
189
|
}
|
|
190
190
|
}
|
|
@@ -195,7 +195,7 @@ async function reportInstance(payload, credentials) {
|
|
|
195
195
|
const { apiUrl, instanceId, hmacSecret, shieldEnv } = credentials;
|
|
196
196
|
if (!apiUrl || !instanceId || !hmacSecret) {
|
|
197
197
|
log.warn('sender', 'reportInstance: missing credentials, skipping');
|
|
198
|
-
return { ok: false };
|
|
198
|
+
return { ok: false, statusCode: 0, error: 'missing credentials' };
|
|
199
199
|
}
|
|
200
200
|
const nonce = generateNonce();
|
|
201
201
|
const signature = signRequest(instanceId, nonce, hmacSecret);
|
|
@@ -217,7 +217,12 @@ async function reportInstance(payload, credentials) {
|
|
|
217
217
|
if (!res.ok) {
|
|
218
218
|
const body = await res.text();
|
|
219
219
|
log.warn('sender', `reportInstance HTTP ${res.status}: ${sanitizeResponseBodyForLog(body)}`);
|
|
220
|
-
|
|
220
|
+
let needsRegistration = false;
|
|
221
|
+
try {
|
|
222
|
+
needsRegistration = JSON.parse(body).needs_registration === true;
|
|
223
|
+
}
|
|
224
|
+
catch { }
|
|
225
|
+
return { ok: false, statusCode: res.status, error: body, needsRegistration };
|
|
221
226
|
}
|
|
222
227
|
let score;
|
|
223
228
|
try {
|
|
@@ -227,11 +232,11 @@ async function reportInstance(payload, credentials) {
|
|
|
227
232
|
}
|
|
228
233
|
catch {
|
|
229
234
|
}
|
|
230
|
-
return { ok: true, score };
|
|
235
|
+
return { ok: true, score, statusCode: res.status };
|
|
231
236
|
}
|
|
232
237
|
catch (err) {
|
|
233
238
|
log.warn('sender', `reportInstance error: ${errMsg(err)}`);
|
|
234
|
-
return { ok: false };
|
|
239
|
+
return { ok: false, statusCode: 0, error: errMsg(err) };
|
|
235
240
|
}
|
|
236
241
|
}
|
|
237
242
|
async function reportLifecycleEvent(type, data, credentials) {
|
package/dist/src/updater.d.ts
CHANGED
|
@@ -10,6 +10,9 @@ export interface UpdateState {
|
|
|
10
10
|
consecutiveFailures: number;
|
|
11
11
|
pendingRestart: boolean;
|
|
12
12
|
restartAttempts: number;
|
|
13
|
+
lastFailureStage: string | null;
|
|
14
|
+
lastFailureAt: number;
|
|
15
|
+
rollbackPending: boolean;
|
|
13
16
|
}
|
|
14
17
|
export interface UpdateCheckResult {
|
|
15
18
|
updateAvailable: boolean;
|
|
@@ -33,6 +36,10 @@ export declare function classifyUpdate(current: string, candidate: string): {
|
|
|
33
36
|
isMajor: boolean;
|
|
34
37
|
};
|
|
35
38
|
export declare function loadUpdateState(): UpdateState;
|
|
39
|
+
export declare function preflightAutoUpdateEnvironment(): {
|
|
40
|
+
ok: boolean;
|
|
41
|
+
missing: string[];
|
|
42
|
+
};
|
|
36
43
|
export declare function saveUpdateState(state: UpdateState): void;
|
|
37
44
|
export declare function checkNpmVersion(): string | null;
|
|
38
45
|
export declare function checkForUpdate(overrideInterval?: number): UpdateCheckResult | null;
|
package/dist/src/updater.js
CHANGED
|
@@ -37,6 +37,7 @@ exports.parseSemVer = parseSemVer;
|
|
|
37
37
|
exports.isNewerVersion = isNewerVersion;
|
|
38
38
|
exports.classifyUpdate = classifyUpdate;
|
|
39
39
|
exports.loadUpdateState = loadUpdateState;
|
|
40
|
+
exports.preflightAutoUpdateEnvironment = preflightAutoUpdateEnvironment;
|
|
40
41
|
exports.saveUpdateState = saveUpdateState;
|
|
41
42
|
exports.checkNpmVersion = checkNpmVersion;
|
|
42
43
|
exports.checkForUpdate = checkForUpdate;
|
|
@@ -117,12 +118,43 @@ function loadUpdateState() {
|
|
|
117
118
|
consecutiveFailures: 0,
|
|
118
119
|
pendingRestart: false,
|
|
119
120
|
restartAttempts: 0,
|
|
121
|
+
lastFailureStage: null,
|
|
122
|
+
lastFailureAt: 0,
|
|
123
|
+
rollbackPending: false,
|
|
120
124
|
};
|
|
121
125
|
if (!(0, fs_1.existsSync)(UPDATE_STATE_FILE))
|
|
122
126
|
return defaults;
|
|
123
127
|
const loaded = (0, safe_io_1.readJsonSafe)(UPDATE_STATE_FILE, {}, 'update-state');
|
|
124
128
|
return { ...defaults, ...loaded };
|
|
125
129
|
}
|
|
130
|
+
function recordUpdateFailure(state, stage, error, extra) {
|
|
131
|
+
state.lastFailureStage = stage;
|
|
132
|
+
state.lastFailureAt = Date.now();
|
|
133
|
+
state.lastError = error;
|
|
134
|
+
Object.assign(state, extra ?? {});
|
|
135
|
+
saveUpdateState(state);
|
|
136
|
+
}
|
|
137
|
+
function preflightAutoUpdateEnvironment() {
|
|
138
|
+
const checks = [
|
|
139
|
+
{ name: 'npm', command: 'npm --version' },
|
|
140
|
+
{ name: 'tar', command: 'tar --version' },
|
|
141
|
+
{ name: 'openclaw', command: 'openclaw --version' },
|
|
142
|
+
];
|
|
143
|
+
const missing = [];
|
|
144
|
+
for (const check of checks) {
|
|
145
|
+
try {
|
|
146
|
+
(0, child_process_1.execSync)(check.command, {
|
|
147
|
+
encoding: 'utf-8',
|
|
148
|
+
timeout: 10_000,
|
|
149
|
+
stdio: ['pipe', 'pipe', 'pipe'],
|
|
150
|
+
});
|
|
151
|
+
}
|
|
152
|
+
catch {
|
|
153
|
+
missing.push(check.name);
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
return { ok: missing.length === 0, missing };
|
|
157
|
+
}
|
|
126
158
|
function saveUpdateState(state) {
|
|
127
159
|
ensureDir((0, path_1.dirname)(UPDATE_STATE_FILE));
|
|
128
160
|
(0, safe_io_1.writeJsonSafe)(UPDATE_STATE_FILE, state);
|
|
@@ -166,9 +198,8 @@ function checkForUpdate(overrideInterval) {
|
|
|
166
198
|
state.lastCheckAt = now;
|
|
167
199
|
state.currentVersion = version_1.VERSION;
|
|
168
200
|
if (!latestVersion) {
|
|
169
|
-
state.lastError = 'Failed to query npm registry';
|
|
170
201
|
state.consecutiveFailures = (state.consecutiveFailures ?? 0) + 1;
|
|
171
|
-
|
|
202
|
+
recordUpdateFailure(state, 'check', 'Failed to query npm registry');
|
|
172
203
|
const FAILURE_THRESHOLD = 3;
|
|
173
204
|
if (state.consecutiveFailures >= FAILURE_THRESHOLD) {
|
|
174
205
|
const nextRetryAt = new Date(now + CHECK_INTERVAL_MS).toISOString();
|
|
@@ -189,6 +220,8 @@ function checkForUpdate(overrideInterval) {
|
|
|
189
220
|
state.updateAvailable = true;
|
|
190
221
|
state.lastError = null;
|
|
191
222
|
state.consecutiveFailures = 0;
|
|
223
|
+
state.lastFailureStage = null;
|
|
224
|
+
state.lastFailureAt = 0;
|
|
192
225
|
saveUpdateState(state);
|
|
193
226
|
const classification = classifyUpdate(version_1.VERSION, latestVersion);
|
|
194
227
|
log.info('updater', `Update available: ${version_1.VERSION} → ${latestVersion} (${classification.isPatch ? 'patch' : classification.isMinor ? 'minor' : 'major'})`);
|
|
@@ -202,6 +235,8 @@ function checkForUpdate(overrideInterval) {
|
|
|
202
235
|
state.updateAvailable = false;
|
|
203
236
|
state.lastError = null;
|
|
204
237
|
state.consecutiveFailures = 0;
|
|
238
|
+
state.lastFailureStage = null;
|
|
239
|
+
state.lastFailureAt = 0;
|
|
205
240
|
saveUpdateState(state);
|
|
206
241
|
return null;
|
|
207
242
|
}
|
|
@@ -342,7 +377,7 @@ function restoreFromBackup(backupPath) {
|
|
|
342
377
|
return false;
|
|
343
378
|
}
|
|
344
379
|
}
|
|
345
|
-
function updateOpenClawPluginMetadata(newVersion, shasum) {
|
|
380
|
+
function updateOpenClawPluginMetadata(newVersion, shasum, tarballBuffer) {
|
|
346
381
|
const configPath = (0, path_1.join)((0, os_1.homedir)(), '.openclaw', 'openclaw.json');
|
|
347
382
|
try {
|
|
348
383
|
if (!(0, fs_1.existsSync)(configPath))
|
|
@@ -355,7 +390,13 @@ function updateOpenClawPluginMetadata(newVersion, shasum) {
|
|
|
355
390
|
install.version = newVersion;
|
|
356
391
|
install.resolvedVersion = newVersion;
|
|
357
392
|
install.resolvedSpec = `${PACKAGE_NAME}@${newVersion}`;
|
|
358
|
-
|
|
393
|
+
if (tarballBuffer) {
|
|
394
|
+
const integrityHash = (0, crypto_1.createHash)('sha512').update(tarballBuffer).digest('base64');
|
|
395
|
+
install.integrity = `sha512-${integrityHash}`;
|
|
396
|
+
}
|
|
397
|
+
else {
|
|
398
|
+
delete install.integrity;
|
|
399
|
+
}
|
|
359
400
|
if (shasum) {
|
|
360
401
|
install.shasum = shasum;
|
|
361
402
|
}
|
|
@@ -407,7 +448,8 @@ function downloadAndInstall(targetVersion) {
|
|
|
407
448
|
return false;
|
|
408
449
|
}
|
|
409
450
|
const tarball = (0, path_1.join)(tmpDir, tarballs[0]);
|
|
410
|
-
const
|
|
451
|
+
const tarballBuffer = (0, fs_1.readFileSync)(tarball);
|
|
452
|
+
const tarballShasum = (0, crypto_1.createHash)('sha1').update(tarballBuffer).digest('hex');
|
|
411
453
|
const stagingDir = (0, path_1.join)(tmpDir, 'staging');
|
|
412
454
|
(0, fs_1.mkdirSync)(stagingDir, { recursive: true });
|
|
413
455
|
(0, child_process_1.execSync)(`tar xzf "${tarball}" -C "${stagingDir}" --strip-components=1`, {
|
|
@@ -442,7 +484,7 @@ function downloadAndInstall(targetVersion) {
|
|
|
442
484
|
(0, fs_1.rmSync)((0, path_1.join)(PLUGIN_DIR, entry), { recursive: true, force: true });
|
|
443
485
|
}
|
|
444
486
|
copyRecursive(stagingDir, PLUGIN_DIR);
|
|
445
|
-
updateOpenClawPluginMetadata(targetVersion, tarballShasum);
|
|
487
|
+
updateOpenClawPluginMetadata(targetVersion, tarballShasum, tarballBuffer);
|
|
446
488
|
log.info('updater', `Installed ${PACKAGE_NAME}@${targetVersion} successfully`);
|
|
447
489
|
return true;
|
|
448
490
|
}
|
|
@@ -465,6 +507,8 @@ function performAutoUpdate(mode, checkIntervalMs) {
|
|
|
465
507
|
log.info('updater', `Clearing stale pendingRestart — running version matches stored currentVersion (${version_1.VERSION})`);
|
|
466
508
|
st.pendingRestart = false;
|
|
467
509
|
st.restartAttempts = 0;
|
|
510
|
+
st.rollbackPending = false;
|
|
511
|
+
st.lastFailureStage = null;
|
|
468
512
|
saveUpdateState(st);
|
|
469
513
|
}
|
|
470
514
|
}
|
|
@@ -492,6 +536,7 @@ function performAutoUpdate(mode, checkIntervalMs) {
|
|
|
492
536
|
}
|
|
493
537
|
state.pendingRestart = false;
|
|
494
538
|
state.restartAttempts = 0;
|
|
539
|
+
state.rollbackPending = false;
|
|
495
540
|
saveUpdateState(state);
|
|
496
541
|
}
|
|
497
542
|
else {
|
|
@@ -500,10 +545,14 @@ function performAutoUpdate(mode, checkIntervalMs) {
|
|
|
500
545
|
if (restarted) {
|
|
501
546
|
state.pendingRestart = false;
|
|
502
547
|
state.restartAttempts = 0;
|
|
548
|
+
state.rollbackPending = false;
|
|
549
|
+
state.lastFailureStage = null;
|
|
503
550
|
saveUpdateState(state);
|
|
504
551
|
}
|
|
505
552
|
else {
|
|
506
553
|
state.restartAttempts = (state.restartAttempts ?? 0) + 1;
|
|
554
|
+
state.lastFailureStage = 'restart';
|
|
555
|
+
state.lastFailureAt = Date.now();
|
|
507
556
|
saveUpdateState(state);
|
|
508
557
|
}
|
|
509
558
|
}
|
|
@@ -543,11 +592,22 @@ function performAutoUpdate(mode, checkIntervalMs) {
|
|
|
543
592
|
}
|
|
544
593
|
const state = loadUpdateState();
|
|
545
594
|
log.info('updater', `Auto-updating: ${version_1.VERSION} → ${check.latestVersion}`);
|
|
595
|
+
const preflight = preflightAutoUpdateEnvironment();
|
|
596
|
+
if (!preflight.ok) {
|
|
597
|
+
state.consecutiveFailures++;
|
|
598
|
+
recordUpdateFailure(state, 'preflight', `Missing required commands for auto-update: ${preflight.missing.join(', ')}`);
|
|
599
|
+
return {
|
|
600
|
+
action: 'error',
|
|
601
|
+
fromVersion: version_1.VERSION,
|
|
602
|
+
toVersion: check.latestVersion,
|
|
603
|
+
message: `Auto-update aborted: missing required commands (${preflight.missing.join(', ')})`,
|
|
604
|
+
requiresRestart: false,
|
|
605
|
+
};
|
|
606
|
+
}
|
|
546
607
|
const backupPath = backupCurrentVersion();
|
|
547
608
|
if (!backupPath) {
|
|
548
609
|
state.consecutiveFailures++;
|
|
549
|
-
state
|
|
550
|
-
saveUpdateState(state);
|
|
610
|
+
recordUpdateFailure(state, 'backup', 'Backup failed — update aborted');
|
|
551
611
|
return {
|
|
552
612
|
action: 'error',
|
|
553
613
|
fromVersion: version_1.VERSION,
|
|
@@ -561,8 +621,7 @@ function performAutoUpdate(mode, checkIntervalMs) {
|
|
|
561
621
|
log.warn('updater', 'Install failed — rolling back...');
|
|
562
622
|
const restored = restoreFromBackup(backupPath);
|
|
563
623
|
state.consecutiveFailures++;
|
|
564
|
-
state
|
|
565
|
-
saveUpdateState(state);
|
|
624
|
+
recordUpdateFailure(state, restored ? 'install' : 'rollback', restored ? 'Install failed — rolled back' : 'Install failed and rollback failed', { rollbackPending: !restored });
|
|
566
625
|
return {
|
|
567
626
|
action: restored ? 'rollback' : 'error',
|
|
568
627
|
fromVersion: version_1.VERSION,
|
|
@@ -584,8 +643,7 @@ function performAutoUpdate(mode, checkIntervalMs) {
|
|
|
584
643
|
log.warn('updater', 'Rolling back...');
|
|
585
644
|
restoreFromBackup(backupPath);
|
|
586
645
|
state.consecutiveFailures++;
|
|
587
|
-
state
|
|
588
|
-
saveUpdateState(state);
|
|
646
|
+
recordUpdateFailure(state, 'validation', 'Post-install validation failed — rolled back');
|
|
589
647
|
return {
|
|
590
648
|
action: 'rollback',
|
|
591
649
|
fromVersion: version_1.VERSION,
|
|
@@ -607,8 +665,7 @@ function performAutoUpdate(mode, checkIntervalMs) {
|
|
|
607
665
|
}
|
|
608
666
|
catch { }
|
|
609
667
|
state.consecutiveFailures++;
|
|
610
|
-
state
|
|
611
|
-
saveUpdateState(state);
|
|
668
|
+
recordUpdateFailure(state, 'integrity', 'Integrity check failed — rolled back');
|
|
612
669
|
return noOp;
|
|
613
670
|
}
|
|
614
671
|
const restarted = requestGatewayRestart();
|
|
@@ -619,6 +676,9 @@ function performAutoUpdate(mode, checkIntervalMs) {
|
|
|
619
676
|
state.lastUpdateAt = Date.now();
|
|
620
677
|
state.consecutiveFailures = 0;
|
|
621
678
|
state.lastError = null;
|
|
679
|
+
state.lastFailureStage = 'restart';
|
|
680
|
+
state.lastFailureAt = Date.now();
|
|
681
|
+
state.rollbackPending = false;
|
|
622
682
|
saveUpdateState(state);
|
|
623
683
|
log.warn('updater', `Gateway restart failed after update to ${check.latestVersion} — will retry on next poll cycle.`);
|
|
624
684
|
return {
|
|
@@ -637,6 +697,9 @@ function performAutoUpdate(mode, checkIntervalMs) {
|
|
|
637
697
|
state.currentVersion = check.latestVersion;
|
|
638
698
|
state.consecutiveFailures = 0;
|
|
639
699
|
state.lastError = null;
|
|
700
|
+
state.lastFailureStage = null;
|
|
701
|
+
state.lastFailureAt = 0;
|
|
702
|
+
state.rollbackPending = false;
|
|
640
703
|
saveUpdateState(state);
|
|
641
704
|
log.info('updater', `✅ Auto-updated: ${version_1.VERSION} → ${check.latestVersion}. Gateway restart initiated.`);
|
|
642
705
|
return {
|
package/openclaw.plugin.json
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
"id": "shield",
|
|
3
3
|
"name": "OpenClaw Shield",
|
|
4
4
|
"description": "Real-time security monitoring — streams enriched, redacted security events to the Shield detection platform.",
|
|
5
|
-
"version": "0.
|
|
5
|
+
"version": "0.8.0",
|
|
6
6
|
"skills": [
|
|
7
7
|
"./skills"
|
|
8
8
|
],
|
|
@@ -47,7 +47,7 @@
|
|
|
47
47
|
}
|
|
48
48
|
],
|
|
49
49
|
"default": true,
|
|
50
|
-
"description": "Auto-update mode: true (auto-update patch versions), false (disabled), or 'notify-only' (log available updates without installing)."
|
|
50
|
+
"description": "Auto-update mode: true (auto-update patch and minor versions with rollback safety), false (disabled), or 'notify-only' (log available updates without installing)."
|
|
51
51
|
},
|
|
52
52
|
"debugLog": {
|
|
53
53
|
"type": "boolean",
|
|
@@ -78,7 +78,7 @@
|
|
|
78
78
|
},
|
|
79
79
|
"autoUpdate": {
|
|
80
80
|
"label": "Auto-update mode",
|
|
81
|
-
"description": "true = auto-install patch updates, 'notify-only' = log only, false = disabled"
|
|
81
|
+
"description": "true = auto-install patch and minor updates, 'notify-only' = log only, false = disabled"
|
|
82
82
|
},
|
|
83
83
|
"debugLog": {
|
|
84
84
|
"label": "Debug logging",
|
package/package.json
CHANGED