@upx-us/shield 0.6.4 → 0.6.6
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 +31 -0
- package/dist/index.js +26 -0
- package/dist/src/case-monitor.d.ts +10 -3
- package/dist/src/case-monitor.js +85 -5
- package/dist/src/index.js +4 -2
- package/dist/src/sender.d.ts +1 -0
- package/dist/src/sender.js +14 -0
- package/dist/src/updater.js +46 -0
- package/openclaw.plugin.json +3 -3
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -4,6 +4,37 @@ All notable changes to this project will be documented in this file.
|
|
|
4
4
|
|
|
5
5
|
---
|
|
6
6
|
|
|
7
|
+
## [0.6.6] — 2026-03-10
|
|
8
|
+
|
|
9
|
+
### Added
|
|
10
|
+
- **Plugin lifecycle events** — the plugin now reports key lifecycle signals to the USS platform via the existing `PUT /v1/instance` channel (no new endpoint). Signals are forwarded by Cloud Run as an optional `lifecycle_event` field in the instance telemetry PATCH to the platform. Absent on older plugin versions — fully backward compatible.
|
|
11
|
+
- `plugin_started`: emitted on every successful startup. Includes `version`, `reason` (`'update'` | `'normal'`), and `previous_version` when applicable. Enables the platform to confirm end-to-end update success.
|
|
12
|
+
- `update_restart_failed`: emitted after all gateway restart retries are exhausted. Includes `new_version`, `running_version`, `attempts`, `error`.
|
|
13
|
+
- `plugin_integrity_drift`: emitted when `openclaw.json` version diverges from the installed version after a self-update. Includes `installed_version`, `registered_version`.
|
|
14
|
+
- `update_check_failing`: emitted after 3 consecutive npm registry check failures. Includes `consecutive_failures`, `last_error`, `next_retry_at`.
|
|
15
|
+
|
|
16
|
+
### Changed
|
|
17
|
+
- `reportPluginEvent()` removed from `sender.ts` — replaced by `reportLifecycleEvent()` which reuses the existing `PUT /v1/instance` telemetry channel.
|
|
18
|
+
|
|
19
|
+
### Fixed
|
|
20
|
+
- `SHIELD_AUTO_UPDATE=false` environment variable now correctly disables auto-update (was being read as truthy string `'false'`).
|
|
21
|
+
|
|
22
|
+
---
|
|
23
|
+
|
|
24
|
+
## [0.6.5] — 2026-03-09
|
|
25
|
+
|
|
26
|
+
### Added
|
|
27
|
+
- **Smart degradation detection in `shield status`** — `getMonitorHealth()` now implements a three-gate algorithm to eliminate false positives from transient connectivity blips:
|
|
28
|
+
1. **Count gate**: `consecutiveFailures ≥ 5` (unchanged from v0.6.4)
|
|
29
|
+
2. **Time gate**: first failure in the current streak must be `> 3 minutes` old — a 1-minute connectivity blip (3 failures at 1m intervals) can never trigger DEGRADED
|
|
30
|
+
3. **Sticky recovery**: requires `≥ 2 consecutive successes` to clear DEGRADED — prevents flapping where one success clears it and then it degrades again next cycle
|
|
31
|
+
- **`MonitorHealth` interface** exported from `src/case-monitor.ts` — new fields: `status: 'ok' | 'degraded' | 'unknown'`, `consecutiveSuccesses`, `degradedSinceMs`, `lastCheckMs`, `lastErrorMessage`
|
|
32
|
+
- `status: 'unknown'` before the monitor has completed its first check cycle
|
|
33
|
+
- **`shield.status` RPC** — `caseMonitor` section now includes smart health fields: `status`, `degradedSince` (ISO string), `consecutiveFailures`, `lastError`, `lastCheck` (ISO string), and a pre-formatted `display` string (e.g. `⚠️ Case Monitor: DEGRADED — 7 consecutive failures since 2026-03-09T12:00:00.000Z. Last error: ...` or `✅ Case Monitor: ok`)
|
|
34
|
+
- **7 new tests** covering all degradation scenarios: unknown initial state, transient blip (count gate), time gate, true DEGRADED positive, sticky recovery (1 success not enough), recovery (2 successes clear), and RPC handler shape
|
|
35
|
+
|
|
36
|
+
---
|
|
37
|
+
|
|
7
38
|
## [0.6.4] — 2026-03-09
|
|
8
39
|
|
|
9
40
|
### Fixed
|
package/dist/index.js
CHANGED
|
@@ -648,6 +648,18 @@ exports.default = {
|
|
|
648
648
|
catch (err) {
|
|
649
649
|
log.debug('case-monitor', `Direct send setup failed (non-fatal): ${err instanceof Error ? err.message : String(err)}`);
|
|
650
650
|
}
|
|
651
|
+
try {
|
|
652
|
+
const updateState = (0, updater_1.loadUpdateState)();
|
|
653
|
+
const previousVersion = updateState.rollbackVersion ?? null;
|
|
654
|
+
const startReason = previousVersion ? 'update' : 'normal';
|
|
655
|
+
const { reportLifecycleEvent } = await Promise.resolve().then(() => __importStar(require('./src/sender')));
|
|
656
|
+
void reportLifecycleEvent('plugin_started', {
|
|
657
|
+
version: version_1.VERSION,
|
|
658
|
+
reason: startReason,
|
|
659
|
+
...(previousVersion ? { previous_version: previousVersion } : {}),
|
|
660
|
+
}, credentials);
|
|
661
|
+
}
|
|
662
|
+
catch { }
|
|
651
663
|
const autoUpdateMode = pluginConfig.autoUpdate ?? true;
|
|
652
664
|
log.info('updater', `Startup update check (autoUpdate=${autoUpdateMode}, current=${version_1.VERSION})`);
|
|
653
665
|
const startupUpdate = (0, updater_1.performAutoUpdate)(autoUpdateMode, 0);
|
|
@@ -879,6 +891,10 @@ exports.default = {
|
|
|
879
891
|
const creds = (0, config_1.loadCredentials)();
|
|
880
892
|
const activated = state.activated || hasValidCredentials(creds);
|
|
881
893
|
const caseStatus = (0, case_monitor_1.getCaseMonitorStatus)();
|
|
894
|
+
const monitorHealth = (0, case_monitor_1.getMonitorHealth)();
|
|
895
|
+
const caseMonitorDisplay = monitorHealth.status === 'degraded'
|
|
896
|
+
? `⚠️ Case Monitor: DEGRADED — ${monitorHealth.consecutiveFailures} consecutive failures since ${monitorHealth.degradedSinceMs ? new Date(monitorHealth.degradedSinceMs).toISOString() : 'unknown'}. Last error: ${monitorHealth.lastErrorMessage}`
|
|
897
|
+
: `✅ Case Monitor: ok`;
|
|
882
898
|
respond(true, {
|
|
883
899
|
activated,
|
|
884
900
|
running: state.running,
|
|
@@ -893,6 +909,16 @@ exports.default = {
|
|
|
893
909
|
intervalMs: caseStatus.intervalMs,
|
|
894
910
|
nextCheckInMs: caseStatus.nextCheckIn,
|
|
895
911
|
lastCheckAt: caseStatus.lastCheckAt,
|
|
912
|
+
status: monitorHealth.status,
|
|
913
|
+
degradedSince: monitorHealth.degradedSinceMs
|
|
914
|
+
? new Date(monitorHealth.degradedSinceMs).toISOString()
|
|
915
|
+
: null,
|
|
916
|
+
consecutiveFailures: monitorHealth.consecutiveFailures,
|
|
917
|
+
lastError: monitorHealth.lastErrorMessage,
|
|
918
|
+
lastCheck: monitorHealth.lastCheckMs
|
|
919
|
+
? new Date(monitorHealth.lastCheckMs).toISOString()
|
|
920
|
+
: null,
|
|
921
|
+
display: caseMonitorDisplay,
|
|
896
922
|
},
|
|
897
923
|
});
|
|
898
924
|
});
|
|
@@ -56,10 +56,15 @@ export interface CaseDetail extends CaseSummary {
|
|
|
56
56
|
}
|
|
57
57
|
export declare function initCaseMonitor(dataDir: string): void;
|
|
58
58
|
export declare function notifyCaseMonitorActivity(): void;
|
|
59
|
-
export
|
|
60
|
-
status: 'ok' | 'degraded';
|
|
59
|
+
export interface MonitorHealth {
|
|
60
|
+
status: 'ok' | 'degraded' | 'unknown';
|
|
61
61
|
consecutiveFailures: number;
|
|
62
|
-
|
|
62
|
+
consecutiveSuccesses: number;
|
|
63
|
+
degradedSinceMs: number | null;
|
|
64
|
+
lastCheckMs: number | null;
|
|
65
|
+
lastErrorMessage: string | null;
|
|
66
|
+
}
|
|
67
|
+
export declare function getMonitorHealth(): MonitorHealth;
|
|
63
68
|
export declare function getCaseMonitorStatus(): {
|
|
64
69
|
intervalMs: number;
|
|
65
70
|
nextCheckIn: number;
|
|
@@ -70,4 +75,6 @@ export declare function getPendingCases(): CaseSummary[];
|
|
|
70
75
|
export declare function acknowledgeCases(caseIds: string[]): void;
|
|
71
76
|
export declare function formatCaseNotification(c: CaseSummary | CaseDetail): string;
|
|
72
77
|
export declare function _resetForTesting(): void;
|
|
78
|
+
export declare function _simulateFailuresForTesting(count: number, firstFailureAgeMs: number): void;
|
|
79
|
+
export declare function _simulateSuccessForTesting(): void;
|
|
73
80
|
export {};
|
package/dist/src/case-monitor.js
CHANGED
|
@@ -44,6 +44,8 @@ exports.getPendingCases = getPendingCases;
|
|
|
44
44
|
exports.acknowledgeCases = acknowledgeCases;
|
|
45
45
|
exports.formatCaseNotification = formatCaseNotification;
|
|
46
46
|
exports._resetForTesting = _resetForTesting;
|
|
47
|
+
exports._simulateFailuresForTesting = _simulateFailuresForTesting;
|
|
48
|
+
exports._simulateSuccessForTesting = _simulateSuccessForTesting;
|
|
47
49
|
const safe_io_1 = require("./safe-io");
|
|
48
50
|
const client_1 = require("./rpc/client");
|
|
49
51
|
const exclusions_1 = require("./exclusions");
|
|
@@ -164,7 +166,14 @@ let _lastEventAt = 0;
|
|
|
164
166
|
let _currentIntervalMs = MAX_CHECK_INTERVAL_MS;
|
|
165
167
|
let _exclusionsWarnedOnce = false;
|
|
166
168
|
let _consecutiveCheckFailures = 0;
|
|
169
|
+
let _consecutiveSuccesses = 0;
|
|
170
|
+
let _degradedSinceMs = null;
|
|
171
|
+
let _firstFailureInStreakMs = null;
|
|
172
|
+
let _lastCheckMs = null;
|
|
173
|
+
let _lastErrorMessage = null;
|
|
167
174
|
const DEGRADED_THRESHOLD = 5;
|
|
175
|
+
const DEGRADED_TIME_GATE_MS = 3 * 60 * 1000;
|
|
176
|
+
const RECOVERY_THRESHOLD = 2;
|
|
168
177
|
function computeInterval() {
|
|
169
178
|
if (_lastCheckAt === 0)
|
|
170
179
|
return MIN_CHECK_INTERVAL_MS;
|
|
@@ -186,11 +195,58 @@ function notifyCaseMonitorActivity() {
|
|
|
186
195
|
_lastEventAt = Date.now();
|
|
187
196
|
}
|
|
188
197
|
function getMonitorHealth() {
|
|
198
|
+
const now = Date.now();
|
|
199
|
+
const isDegraded = _consecutiveCheckFailures >= DEGRADED_THRESHOLD &&
|
|
200
|
+
_firstFailureInStreakMs !== null &&
|
|
201
|
+
(now - _firstFailureInStreakMs) >= DEGRADED_TIME_GATE_MS;
|
|
202
|
+
if (isDegraded && _degradedSinceMs === null) {
|
|
203
|
+
_degradedSinceMs = now;
|
|
204
|
+
}
|
|
205
|
+
let status;
|
|
206
|
+
if (_lastCheckMs === null) {
|
|
207
|
+
status = 'unknown';
|
|
208
|
+
}
|
|
209
|
+
else if (isDegraded) {
|
|
210
|
+
status = 'degraded';
|
|
211
|
+
}
|
|
212
|
+
else {
|
|
213
|
+
status = 'ok';
|
|
214
|
+
}
|
|
189
215
|
return {
|
|
190
|
-
status
|
|
216
|
+
status,
|
|
191
217
|
consecutiveFailures: _consecutiveCheckFailures,
|
|
218
|
+
consecutiveSuccesses: _consecutiveSuccesses,
|
|
219
|
+
degradedSinceMs: _degradedSinceMs,
|
|
220
|
+
lastCheckMs: _lastCheckMs,
|
|
221
|
+
lastErrorMessage: _lastErrorMessage,
|
|
192
222
|
};
|
|
193
223
|
}
|
|
224
|
+
function recordCheckFailure(message) {
|
|
225
|
+
const now = Date.now();
|
|
226
|
+
if (_consecutiveCheckFailures === 0) {
|
|
227
|
+
_firstFailureInStreakMs = now;
|
|
228
|
+
}
|
|
229
|
+
_consecutiveCheckFailures++;
|
|
230
|
+
_consecutiveSuccesses = 0;
|
|
231
|
+
_lastCheckMs = now;
|
|
232
|
+
_lastErrorMessage = message;
|
|
233
|
+
if (_degradedSinceMs === null &&
|
|
234
|
+
_consecutiveCheckFailures >= DEGRADED_THRESHOLD &&
|
|
235
|
+
_firstFailureInStreakMs !== null &&
|
|
236
|
+
(now - _firstFailureInStreakMs) >= DEGRADED_TIME_GATE_MS) {
|
|
237
|
+
_degradedSinceMs = now;
|
|
238
|
+
}
|
|
239
|
+
}
|
|
240
|
+
function recordCheckSuccess() {
|
|
241
|
+
_consecutiveSuccesses++;
|
|
242
|
+
_lastCheckMs = Date.now();
|
|
243
|
+
_lastErrorMessage = null;
|
|
244
|
+
if (_consecutiveSuccesses >= RECOVERY_THRESHOLD) {
|
|
245
|
+
_consecutiveCheckFailures = 0;
|
|
246
|
+
_firstFailureInStreakMs = null;
|
|
247
|
+
_degradedSinceMs = null;
|
|
248
|
+
}
|
|
249
|
+
}
|
|
194
250
|
function getCaseMonitorStatus() {
|
|
195
251
|
const interval = computeInterval();
|
|
196
252
|
const nextCheckIn = Math.max(0, (_lastCheckAt + interval) - Date.now());
|
|
@@ -220,7 +276,7 @@ async function checkForNewCases(config) {
|
|
|
220
276
|
const result = await (0, client_1.callPlatformApi)(config, '/v1/agent/cases', params, 'GET');
|
|
221
277
|
if (!result.ok) {
|
|
222
278
|
if (!result.error?.includes('not configured')) {
|
|
223
|
-
|
|
279
|
+
recordCheckFailure(result.error ?? 'unknown error');
|
|
224
280
|
log.warn('case-monitor', `Failed to check cases: ${result.error}`);
|
|
225
281
|
if (_consecutiveCheckFailures >= DEGRADED_THRESHOLD) {
|
|
226
282
|
log.warn('case-monitor', `[case-monitor] DEGRADED: ${_consecutiveCheckFailures} consecutive check failures — notifications may be silently dropped`);
|
|
@@ -260,11 +316,12 @@ async function checkForNewCases(config) {
|
|
|
260
316
|
}
|
|
261
317
|
state.lastCheckAt = new Date().toISOString();
|
|
262
318
|
(0, safe_io_1.writeJsonSafe)(stateFile, state);
|
|
263
|
-
|
|
319
|
+
recordCheckSuccess();
|
|
264
320
|
}
|
|
265
321
|
catch (err) {
|
|
266
|
-
|
|
267
|
-
|
|
322
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
323
|
+
recordCheckFailure(msg);
|
|
324
|
+
log.warn('case-monitor', `Check failed: ${msg}`);
|
|
268
325
|
if (_consecutiveCheckFailures >= DEGRADED_THRESHOLD) {
|
|
269
326
|
log.warn('case-monitor', `[case-monitor] DEGRADED: ${_consecutiveCheckFailures} consecutive check failures — notifications may be silently dropped`);
|
|
270
327
|
}
|
|
@@ -369,6 +426,11 @@ function _resetForTesting() {
|
|
|
369
426
|
_currentIntervalMs = MAX_CHECK_INTERVAL_MS;
|
|
370
427
|
_exclusionsWarnedOnce = false;
|
|
371
428
|
_consecutiveCheckFailures = 0;
|
|
429
|
+
_consecutiveSuccesses = 0;
|
|
430
|
+
_degradedSinceMs = null;
|
|
431
|
+
_firstFailureInStreakMs = null;
|
|
432
|
+
_lastCheckMs = null;
|
|
433
|
+
_lastErrorMessage = null;
|
|
372
434
|
_enqueueSystemEvent = null;
|
|
373
435
|
_requestHeartbeatNow = null;
|
|
374
436
|
_agentId = 'main';
|
|
@@ -376,3 +438,21 @@ function _resetForTesting() {
|
|
|
376
438
|
_directSendTo = null;
|
|
377
439
|
_directSendChannel = null;
|
|
378
440
|
}
|
|
441
|
+
function _simulateFailuresForTesting(count, firstFailureAgeMs) {
|
|
442
|
+
const now = Date.now();
|
|
443
|
+
_firstFailureInStreakMs = now - firstFailureAgeMs;
|
|
444
|
+
_consecutiveCheckFailures = count;
|
|
445
|
+
_consecutiveSuccesses = 0;
|
|
446
|
+
_lastCheckMs = now - firstFailureAgeMs;
|
|
447
|
+
_lastErrorMessage = 'simulated failure';
|
|
448
|
+
}
|
|
449
|
+
function _simulateSuccessForTesting() {
|
|
450
|
+
_consecutiveSuccesses++;
|
|
451
|
+
_lastCheckMs = Date.now();
|
|
452
|
+
_lastErrorMessage = null;
|
|
453
|
+
if (_consecutiveSuccesses >= RECOVERY_THRESHOLD) {
|
|
454
|
+
_consecutiveCheckFailures = 0;
|
|
455
|
+
_firstFailureInStreakMs = null;
|
|
456
|
+
_degradedSinceMs = null;
|
|
457
|
+
}
|
|
458
|
+
}
|
package/dist/src/index.js
CHANGED
|
@@ -73,7 +73,8 @@ async function poll() {
|
|
|
73
73
|
(0, event_store_1.initEventStore)(SHIELD_DATA_DIR, { maxEvents: config.localEventLimit });
|
|
74
74
|
}
|
|
75
75
|
log.info('bridge', `Starting — dryRun=${config.dryRun} poll=${config.pollIntervalMs}ms maxEvents=${config.maxEvents || 'unlimited'} redaction=${config.redactionEnabled} logLevel=${process.env.LOG_LEVEL || 'info'}`);
|
|
76
|
-
const
|
|
76
|
+
const _rawEnvStartup = process.env.SHIELD_AUTO_UPDATE;
|
|
77
|
+
const autoUpdateMode = _rawEnvStartup === 'false' ? false : _rawEnvStartup ?? true;
|
|
77
78
|
log.info('updater', `Startup update check (autoUpdate=${autoUpdateMode}, current=${version_1.VERSION})`);
|
|
78
79
|
const startupUpdate = (0, updater_1.performAutoUpdate)(autoUpdateMode, 0);
|
|
79
80
|
if (startupUpdate.action === "none") {
|
|
@@ -147,7 +148,8 @@ async function poll() {
|
|
|
147
148
|
continue;
|
|
148
149
|
}
|
|
149
150
|
}
|
|
150
|
-
const
|
|
151
|
+
const _rawEnvLoop = process.env.SHIELD_AUTO_UPDATE;
|
|
152
|
+
const autoUpdateMode = _rawEnvLoop === 'false' ? false : _rawEnvLoop ?? true;
|
|
151
153
|
const updateResult = (0, updater_1.performAutoUpdate)(autoUpdateMode);
|
|
152
154
|
if (updateResult.action !== "none") {
|
|
153
155
|
if (updateResult.action === "notify") {
|
package/dist/src/sender.d.ts
CHANGED
|
@@ -24,3 +24,4 @@ export interface ReportInstanceResult {
|
|
|
24
24
|
score?: InstanceScore;
|
|
25
25
|
}
|
|
26
26
|
export declare function reportInstance(payload: Record<string, unknown>, credentials: ShieldCredentials): Promise<ReportInstanceResult>;
|
|
27
|
+
export declare function reportLifecycleEvent(type: 'plugin_started' | 'update_restart_failed' | 'plugin_integrity_drift' | 'update_check_failing', data: Record<string, unknown>, credentials: ShieldCredentials): Promise<void>;
|
package/dist/src/sender.js
CHANGED
|
@@ -37,6 +37,7 @@ exports.CIRCUIT_BREAKER_THRESHOLD = exports.REQUEST_TIMEOUT_MS = void 0;
|
|
|
37
37
|
exports._signRequestWithSecret = _signRequestWithSecret;
|
|
38
38
|
exports.sendEvents = sendEvents;
|
|
39
39
|
exports.reportInstance = reportInstance;
|
|
40
|
+
exports.reportLifecycleEvent = reportLifecycleEvent;
|
|
40
41
|
const crypto_1 = require("crypto");
|
|
41
42
|
const log = __importStar(require("./log"));
|
|
42
43
|
const version_1 = require("./version");
|
|
@@ -233,3 +234,16 @@ async function reportInstance(payload, credentials) {
|
|
|
233
234
|
return { ok: false };
|
|
234
235
|
}
|
|
235
236
|
}
|
|
237
|
+
async function reportLifecycleEvent(type, data, credentials) {
|
|
238
|
+
try {
|
|
239
|
+
await reportInstance({
|
|
240
|
+
lifecycle_event: {
|
|
241
|
+
type,
|
|
242
|
+
timestamp: new Date().toISOString(),
|
|
243
|
+
data,
|
|
244
|
+
},
|
|
245
|
+
}, credentials);
|
|
246
|
+
}
|
|
247
|
+
catch {
|
|
248
|
+
}
|
|
249
|
+
}
|
package/dist/src/updater.js
CHANGED
|
@@ -53,6 +53,8 @@ const os_1 = require("os");
|
|
|
53
53
|
const crypto_1 = require("crypto");
|
|
54
54
|
const log = __importStar(require("./log"));
|
|
55
55
|
const version_1 = require("./version");
|
|
56
|
+
const config_1 = require("./config");
|
|
57
|
+
const sender_1 = require("./sender");
|
|
56
58
|
const PACKAGE_NAME = '@upx-us/shield';
|
|
57
59
|
const CHECK_INTERVAL_MS = 6 * 60 * 60 * 1000;
|
|
58
60
|
const MIN_CHECK_INTERVAL_MS = 60 * 1000;
|
|
@@ -163,13 +165,28 @@ function checkForUpdate(overrideInterval) {
|
|
|
163
165
|
state.currentVersion = version_1.VERSION;
|
|
164
166
|
if (!latestVersion) {
|
|
165
167
|
state.lastError = 'Failed to query npm registry';
|
|
168
|
+
state.consecutiveFailures = (state.consecutiveFailures ?? 0) + 1;
|
|
166
169
|
saveUpdateState(state);
|
|
170
|
+
const FAILURE_THRESHOLD = 3;
|
|
171
|
+
if (state.consecutiveFailures >= FAILURE_THRESHOLD) {
|
|
172
|
+
const nextRetryAt = new Date(now + CHECK_INTERVAL_MS).toISOString();
|
|
173
|
+
try {
|
|
174
|
+
const creds = (0, config_1.loadCredentials)();
|
|
175
|
+
void (0, sender_1.reportLifecycleEvent)('update_check_failing', {
|
|
176
|
+
consecutive_failures: state.consecutiveFailures,
|
|
177
|
+
last_error: state.lastError,
|
|
178
|
+
next_retry_at: nextRetryAt,
|
|
179
|
+
}, creds);
|
|
180
|
+
}
|
|
181
|
+
catch { }
|
|
182
|
+
}
|
|
167
183
|
return null;
|
|
168
184
|
}
|
|
169
185
|
state.latestVersion = latestVersion;
|
|
170
186
|
if (isNewerVersion(version_1.VERSION, latestVersion)) {
|
|
171
187
|
state.updateAvailable = true;
|
|
172
188
|
state.lastError = null;
|
|
189
|
+
state.consecutiveFailures = 0;
|
|
173
190
|
saveUpdateState(state);
|
|
174
191
|
const classification = classifyUpdate(version_1.VERSION, latestVersion);
|
|
175
192
|
log.info('updater', `Update available: ${version_1.VERSION} → ${latestVersion} (${classification.isPatch ? 'patch' : classification.isMinor ? 'minor' : 'major'})`);
|
|
@@ -182,6 +199,7 @@ function checkForUpdate(overrideInterval) {
|
|
|
182
199
|
}
|
|
183
200
|
state.updateAvailable = false;
|
|
184
201
|
state.lastError = null;
|
|
202
|
+
state.consecutiveFailures = 0;
|
|
185
203
|
saveUpdateState(state);
|
|
186
204
|
return null;
|
|
187
205
|
}
|
|
@@ -302,6 +320,24 @@ function updateOpenClawPluginMetadata(newVersion, shasum) {
|
|
|
302
320
|
install.installedAt = new Date().toISOString();
|
|
303
321
|
(0, safe_io_1.writeJsonSafe)(configPath, config);
|
|
304
322
|
log.info('updater', `Updated openclaw.json plugin metadata → ${newVersion}`);
|
|
323
|
+
try {
|
|
324
|
+
const written = JSON.parse((0, fs_1.readFileSync)(configPath, 'utf-8'));
|
|
325
|
+
const installedVersion = written?.plugins?.installs?.shield?.version;
|
|
326
|
+
if (installedVersion !== newVersion) {
|
|
327
|
+
log.warn('updater', `openclaw.json integrity check failed: expected=${newVersion} got=${installedVersion ?? '(missing)'}`);
|
|
328
|
+
try {
|
|
329
|
+
const creds = (0, config_1.loadCredentials)();
|
|
330
|
+
void (0, sender_1.reportLifecycleEvent)('plugin_integrity_drift', {
|
|
331
|
+
expected_version: newVersion,
|
|
332
|
+
installed_version: installedVersion ?? null,
|
|
333
|
+
}, creds);
|
|
334
|
+
}
|
|
335
|
+
catch { }
|
|
336
|
+
}
|
|
337
|
+
}
|
|
338
|
+
catch (verifyErr) {
|
|
339
|
+
log.warn('updater', `openclaw.json read-back failed: ${verifyErr instanceof Error ? verifyErr.message : String(verifyErr)}`);
|
|
340
|
+
}
|
|
305
341
|
}
|
|
306
342
|
catch (err) {
|
|
307
343
|
log.warn('updater', `Failed to update openclaw.json metadata: ${err instanceof Error ? err.message : String(err)}`);
|
|
@@ -386,6 +422,16 @@ function performAutoUpdate(mode, checkIntervalMs) {
|
|
|
386
422
|
const MAX_RESTART_ATTEMPTS = 5;
|
|
387
423
|
if ((state.restartAttempts ?? 0) >= MAX_RESTART_ATTEMPTS) {
|
|
388
424
|
log.warn('updater', `Gateway restart failed ${MAX_RESTART_ATTEMPTS}x after update to ${state.currentVersion} — rolling back`);
|
|
425
|
+
try {
|
|
426
|
+
const creds = (0, config_1.loadCredentials)();
|
|
427
|
+
void (0, sender_1.reportLifecycleEvent)('update_restart_failed', {
|
|
428
|
+
new_version: state.currentVersion,
|
|
429
|
+
running_version: version_1.VERSION,
|
|
430
|
+
attempts: state.restartAttempts ?? MAX_RESTART_ATTEMPTS,
|
|
431
|
+
error: state.lastError ?? 'gateway restart exhausted all retries',
|
|
432
|
+
}, creds);
|
|
433
|
+
}
|
|
434
|
+
catch { }
|
|
389
435
|
const backups = listBackups();
|
|
390
436
|
if (backups.length > 0) {
|
|
391
437
|
restoreFromBackup(backups[0]);
|
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 \u2014 streams enriched, redacted security events to the Shield detection platform.",
|
|
5
|
-
"version": "0.6.
|
|
5
|
+
"version": "0.6.6",
|
|
6
6
|
"skills": [
|
|
7
7
|
"./skills"
|
|
8
8
|
],
|
|
@@ -78,7 +78,7 @@
|
|
|
78
78
|
},
|
|
79
79
|
"clawhub": {
|
|
80
80
|
"slug": "openclaw-shield-upx",
|
|
81
|
-
"skillVersion": "
|
|
81
|
+
"skillVersion": "0.6.5",
|
|
82
82
|
"note": "ClawHub auto-increments on publish. Update this after each clawhub submission."
|
|
83
83
|
}
|
|
84
|
-
}
|
|
84
|
+
}
|
package/package.json
CHANGED