@upx-us/shield 0.7.13 → 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 -229
- 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 +66 -10
- package/openclaw.plugin.json +3 -3
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -39,6 +39,7 @@ exports.maskPluginConfigForLogs = maskPluginConfigForLogs;
|
|
|
39
39
|
exports.createSingleflightRunner = createSingleflightRunner;
|
|
40
40
|
exports.createStartGuard = createStartGuard;
|
|
41
41
|
exports.getStatusWarnings = getStatusWarnings;
|
|
42
|
+
exports._resetForTesting = _resetForTesting;
|
|
42
43
|
const config_1 = require("./src/config");
|
|
43
44
|
const log_1 = require("./src/log");
|
|
44
45
|
const log = __importStar(require("./src/log"));
|
|
@@ -231,6 +232,61 @@ function flushAllTimeStats() {
|
|
|
231
232
|
}
|
|
232
233
|
let _stateDirty = true;
|
|
233
234
|
function markStateDirty() { _stateDirty = true; }
|
|
235
|
+
function createInitialState() {
|
|
236
|
+
return {
|
|
237
|
+
activated: false,
|
|
238
|
+
running: false,
|
|
239
|
+
startedAt: 0,
|
|
240
|
+
lastPollAt: 0,
|
|
241
|
+
lastSuccessfulPollAt: 0,
|
|
242
|
+
eventsProcessed: 0,
|
|
243
|
+
quarantineCount: 0,
|
|
244
|
+
consecutiveFailures: 0,
|
|
245
|
+
telemetryConsecutiveFailures: 0,
|
|
246
|
+
instanceId: '',
|
|
247
|
+
lastCaptureAt: 0,
|
|
248
|
+
captureSeenSinceLastSync: false,
|
|
249
|
+
lastSync: null,
|
|
250
|
+
lastTelemetryAt: 0,
|
|
251
|
+
lastSuccessfulTelemetryAt: 0,
|
|
252
|
+
lastTelemetryError: null,
|
|
253
|
+
lastPollError: null,
|
|
254
|
+
lastLifecyclePhase: 'idle',
|
|
255
|
+
lastStopReason: 'none',
|
|
256
|
+
lastError: null,
|
|
257
|
+
lastStartupCheckpoint: null,
|
|
258
|
+
sessionDirCount: 0,
|
|
259
|
+
eventsRetainedForRetry: 0,
|
|
260
|
+
lastDeliveryIssue: null,
|
|
261
|
+
lastDeliveryIssueAt: 0,
|
|
262
|
+
};
|
|
263
|
+
}
|
|
264
|
+
function setLifecyclePhase(phase, checkpoint) {
|
|
265
|
+
state.lastLifecyclePhase = phase;
|
|
266
|
+
if (checkpoint !== undefined)
|
|
267
|
+
state.lastStartupCheckpoint = checkpoint;
|
|
268
|
+
markStateDirty();
|
|
269
|
+
}
|
|
270
|
+
function setLastError(message) {
|
|
271
|
+
state.lastError = message;
|
|
272
|
+
markStateDirty();
|
|
273
|
+
}
|
|
274
|
+
function setStopReason(reason) {
|
|
275
|
+
state.lastStopReason = reason;
|
|
276
|
+
markStateDirty();
|
|
277
|
+
}
|
|
278
|
+
function hasMeaningfulRuntimeState(snapshot) {
|
|
279
|
+
return Boolean(snapshot.running ||
|
|
280
|
+
snapshot.startedAt ||
|
|
281
|
+
snapshot.lastPollAt ||
|
|
282
|
+
snapshot.lastSuccessfulPollAt ||
|
|
283
|
+
snapshot.eventsProcessed ||
|
|
284
|
+
snapshot.quarantineCount ||
|
|
285
|
+
snapshot.lastTelemetryAt ||
|
|
286
|
+
snapshot.lastSuccessfulTelemetryAt ||
|
|
287
|
+
snapshot.lastStartupCheckpoint ||
|
|
288
|
+
snapshot.instanceId);
|
|
289
|
+
}
|
|
234
290
|
function persistState(extra = {}) {
|
|
235
291
|
flushAllTimeStats();
|
|
236
292
|
if (!_stateDirty)
|
|
@@ -264,15 +320,27 @@ function persistState(extra = {}) {
|
|
|
264
320
|
function readPersistedState() {
|
|
265
321
|
try {
|
|
266
322
|
const d = (0, safe_io_1.readJsonSafe)(STATUS_FILE, null, 'status');
|
|
267
|
-
if (!d)
|
|
268
|
-
|
|
323
|
+
if (!d) {
|
|
324
|
+
if (hasMeaningfulRuntimeState(state)) {
|
|
325
|
+
return { data: state, source: 'runtime', message: null };
|
|
326
|
+
}
|
|
327
|
+
return { data: null, source: 'missing', message: 'No persisted Shield runtime state found yet.' };
|
|
328
|
+
}
|
|
269
329
|
const age = Date.now() - (Number(d.updatedAt) || 0);
|
|
270
|
-
if (age > 10 * 60 * 1000)
|
|
271
|
-
return
|
|
272
|
-
|
|
330
|
+
if (age > 10 * 60 * 1000) {
|
|
331
|
+
return {
|
|
332
|
+
data: d,
|
|
333
|
+
source: 'stale',
|
|
334
|
+
message: `Last persisted Shield state is stale (${Math.floor(age / 60_000)}m old).`,
|
|
335
|
+
};
|
|
336
|
+
}
|
|
337
|
+
return { data: d, source: 'persisted', message: null };
|
|
273
338
|
}
|
|
274
339
|
catch {
|
|
275
|
-
|
|
340
|
+
if (hasMeaningfulRuntimeState(state)) {
|
|
341
|
+
return { data: state, source: 'runtime', message: null };
|
|
342
|
+
}
|
|
343
|
+
return { data: null, source: 'missing', message: 'Shield status state could not be read.' };
|
|
276
344
|
}
|
|
277
345
|
}
|
|
278
346
|
const STALE_POLL_WARN_MS = 2 * 60 * 1000;
|
|
@@ -304,19 +372,7 @@ function getStatusWarnings(input) {
|
|
|
304
372
|
}
|
|
305
373
|
return warnings;
|
|
306
374
|
}
|
|
307
|
-
const state =
|
|
308
|
-
activated: false,
|
|
309
|
-
running: false,
|
|
310
|
-
startedAt: 0,
|
|
311
|
-
lastPollAt: 0,
|
|
312
|
-
eventsProcessed: 0,
|
|
313
|
-
quarantineCount: 0,
|
|
314
|
-
consecutiveFailures: 0,
|
|
315
|
-
instanceId: '',
|
|
316
|
-
lastCaptureAt: 0,
|
|
317
|
-
captureSeenSinceLastSync: false,
|
|
318
|
-
lastSync: null,
|
|
319
|
-
};
|
|
375
|
+
const state = createInitialState();
|
|
320
376
|
let firstEventDelivered = false;
|
|
321
377
|
let teardownPreviousRuntime = null;
|
|
322
378
|
let pendingTeardown = null;
|
|
@@ -330,6 +386,26 @@ function getBackoffInterval(baseMs) {
|
|
|
330
386
|
const backoff = baseMs * Math.pow(2, Math.min(state.consecutiveFailures, 10));
|
|
331
387
|
return Math.min(backoff, MAX_BACKOFF_MS);
|
|
332
388
|
}
|
|
389
|
+
function describeSendFailure(kind, statusCode, body) {
|
|
390
|
+
switch (kind) {
|
|
391
|
+
case 'missing_credentials':
|
|
392
|
+
return 'Missing Shield credentials';
|
|
393
|
+
case 'pending_namespace':
|
|
394
|
+
return 'Namespace allocation pending';
|
|
395
|
+
case 'needs_registration':
|
|
396
|
+
return 'Instance registration invalidated by platform';
|
|
397
|
+
case 'quota_exceeded':
|
|
398
|
+
return 'Subscription inactive or quota exhausted';
|
|
399
|
+
case 'circuit_breaker':
|
|
400
|
+
return 'Circuit breaker opened after repeated batch failures';
|
|
401
|
+
case 'network_error':
|
|
402
|
+
return `Network error while sending events${body ? `: ${body}` : ''}`;
|
|
403
|
+
case 'http_error':
|
|
404
|
+
return `HTTP ${statusCode ?? 0} while sending events${body ? `: ${body}` : ''}`;
|
|
405
|
+
default:
|
|
406
|
+
return body || `HTTP ${statusCode ?? 0} while sending events`;
|
|
407
|
+
}
|
|
408
|
+
}
|
|
333
409
|
function printNotActivatedStatus() {
|
|
334
410
|
console.log(`OpenClaw Shield — v${version_1.VERSION}`);
|
|
335
411
|
console.log('');
|
|
@@ -344,42 +420,54 @@ function printNotActivatedStatus() {
|
|
|
344
420
|
console.log(' Get your key at: https://uss.upx.com → APPS → OpenClaw Shield');
|
|
345
421
|
}
|
|
346
422
|
function printActivatedStatus() {
|
|
347
|
-
const
|
|
423
|
+
const snapshot = readPersistedState();
|
|
424
|
+
const s = snapshot.data;
|
|
425
|
+
const fmtTime = (ms) => ms < 60_000 ? `${Math.round(ms / 1000)}s ago`
|
|
426
|
+
: ms < 3_600_000 ? `${Math.floor(ms / 60_000)}m ago`
|
|
427
|
+
: `${(ms / 3_600_000).toFixed(1)}h ago`;
|
|
428
|
+
if (!s) {
|
|
429
|
+
console.log(`OpenClaw Shield — v${version_1.VERSION}`);
|
|
430
|
+
console.log('');
|
|
431
|
+
console.log('── Plugin Health ─────────────────────────────');
|
|
432
|
+
console.log(' Connection: ⚠️ Status unavailable');
|
|
433
|
+
console.log(` Version: ${version_1.VERSION}`);
|
|
434
|
+
console.log(` Warning: ${snapshot.message ?? 'Shield has not persisted runtime state yet.'}`);
|
|
435
|
+
console.log(' - This usually means the gateway has not started the plugin yet, or state persistence is unavailable.');
|
|
436
|
+
console.log(' - If this persists, run: openclaw gateway restart');
|
|
437
|
+
return;
|
|
438
|
+
}
|
|
348
439
|
const isRunning = Boolean(s.running);
|
|
349
440
|
const ageMs = s.updatedAt ? Date.now() - s.updatedAt : null;
|
|
350
|
-
const ageLabel = ageMs != null
|
|
351
|
-
? (ageMs < 60_000 ? `${Math.round(ageMs / 1000)}s ago`
|
|
352
|
-
: ageMs < 3_600_000 ? `${Math.floor(ageMs / 60_000)}m ago`
|
|
353
|
-
: `${(ageMs / 3_600_000).toFixed(1)}h ago`)
|
|
354
|
-
: '';
|
|
441
|
+
const ageLabel = ageMs != null ? fmtTime(ageMs) : '';
|
|
355
442
|
const lastPollMs = s.lastPollAt ? Date.now() - s.lastPollAt : null;
|
|
356
|
-
const lastPollLabel = s.lastPollAt
|
|
357
|
-
? (lastPollMs < 60_000 ? `${Math.round(lastPollMs / 1000)}s ago`
|
|
358
|
-
: lastPollMs < 3_600_000 ? `${Math.floor(lastPollMs / 60_000)}m ago`
|
|
359
|
-
: `${(lastPollMs / 3_600_000).toFixed(1)}h ago`)
|
|
360
|
-
: 'never';
|
|
443
|
+
const lastPollLabel = s.lastPollAt ? fmtTime(lastPollMs) : 'never';
|
|
361
444
|
const instanceId = s.instanceId;
|
|
362
445
|
const shortId = instanceId ? `${instanceId.slice(0, 8)}…` : '';
|
|
446
|
+
const lifecyclePhase = s.lastLifecyclePhase ?? 'unknown';
|
|
447
|
+
const stopReason = s.lastStopReason ?? 'unknown';
|
|
363
448
|
console.log(`OpenClaw Shield — v${s.version ?? version_1.VERSION}${ageLabel ? ` (${ageLabel})` : ''}`);
|
|
364
449
|
console.log('');
|
|
365
450
|
console.log('── Plugin Health ─────────────────────────────');
|
|
366
|
-
console.log(` Connection: ${isRunning ? '✅ Connected' : '❌ Disconnected'}`);
|
|
451
|
+
console.log(` Connection: ${isRunning ? '✅ Connected' : snapshot.source === 'stale' ? '⚠️ Status stale' : '❌ Disconnected'}`);
|
|
367
452
|
console.log(` Version: ${s.version ?? version_1.VERSION}`);
|
|
368
453
|
if (shortId)
|
|
369
454
|
console.log(` Instance: ${shortId}`);
|
|
370
455
|
console.log(` Last poll: ${lastPollLabel}`);
|
|
371
456
|
const lastCaptureMs = s.lastCaptureAt ? Date.now() - s.lastCaptureAt : null;
|
|
372
|
-
const lastCaptureLabel = s.lastCaptureAt
|
|
373
|
-
? (lastCaptureMs < 60_000 ? `${Math.round(lastCaptureMs / 1000)}s ago`
|
|
374
|
-
: lastCaptureMs < 3_600_000 ? `${Math.floor(lastCaptureMs / 60_000)}m ago`
|
|
375
|
-
: `${(lastCaptureMs / 3_600_000).toFixed(1)}h ago`)
|
|
376
|
-
: null;
|
|
457
|
+
const lastCaptureLabel = s.lastCaptureAt ? fmtTime(lastCaptureMs) : null;
|
|
377
458
|
if (lastCaptureLabel)
|
|
378
459
|
console.log(` Last capture: ${lastCaptureLabel}`);
|
|
460
|
+
const updateState = (0, updater_1.loadUpdateState)();
|
|
379
461
|
const allTime = (s.allTime ?? readAllTimeStats());
|
|
380
462
|
console.log(` Events sent: ${allTime.eventsProcessed.toLocaleString()} (all-time)`);
|
|
381
463
|
console.log(` Quarantine: ${allTime.quarantineCount.toLocaleString()} (all-time)`);
|
|
382
|
-
console.log(` Failures: ${s.consecutiveFailures ?? 0} (
|
|
464
|
+
console.log(` Failures: ${s.consecutiveFailures ?? 0} (poll)`);
|
|
465
|
+
if (s.telemetryConsecutiveFailures != null) {
|
|
466
|
+
console.log(` Telemetry: ${s.telemetryConsecutiveFailures ?? 0} (consecutive failures)`);
|
|
467
|
+
}
|
|
468
|
+
console.log(` Phase: ${lifecyclePhase}`);
|
|
469
|
+
if (stopReason && stopReason !== 'none')
|
|
470
|
+
console.log(` Stop reason: ${stopReason}`);
|
|
383
471
|
if (s.pid) {
|
|
384
472
|
let pidAlive = false;
|
|
385
473
|
try {
|
|
@@ -389,6 +477,28 @@ function printActivatedStatus() {
|
|
|
389
477
|
catch { }
|
|
390
478
|
console.log(` Daemon PID: ${s.pid}${pidAlive ? '' : ' ⚠️ stale (process not running)'}`);
|
|
391
479
|
}
|
|
480
|
+
if (snapshot.message) {
|
|
481
|
+
console.log(` Status note: ${snapshot.message}`);
|
|
482
|
+
}
|
|
483
|
+
if (s.lastStartupCheckpoint) {
|
|
484
|
+
console.log(` Checkpoint: ${s.lastStartupCheckpoint}`);
|
|
485
|
+
}
|
|
486
|
+
if (s.lastError) {
|
|
487
|
+
console.log(` Last error: ${s.lastError}`);
|
|
488
|
+
}
|
|
489
|
+
if (updateState.pendingRestart || updateState.lastFailureStage || updateState.rollbackPending) {
|
|
490
|
+
console.log(` Update: ${updateState.pendingRestart ? 'pending restart' : updateState.rollbackPending ? 'rollback needed' : 'attention required'}`);
|
|
491
|
+
if (updateState.lastFailureStage)
|
|
492
|
+
console.log(` Update stage: ${updateState.lastFailureStage}`);
|
|
493
|
+
if (updateState.lastError)
|
|
494
|
+
console.log(` Update error: ${updateState.lastError}`);
|
|
495
|
+
}
|
|
496
|
+
if (s.lastDeliveryIssue) {
|
|
497
|
+
console.log(` Delivery: ${s.lastDeliveryIssue}`);
|
|
498
|
+
}
|
|
499
|
+
if (s.eventsRetainedForRetry && Number(s.eventsRetainedForRetry) > 0) {
|
|
500
|
+
console.log(` Retry backlog:${String(s.eventsRetainedForRetry).padStart(3)} event(s) retained for retry`);
|
|
501
|
+
}
|
|
392
502
|
const statusWarnings = getStatusWarnings({
|
|
393
503
|
running: isRunning,
|
|
394
504
|
lastPollAt: s.lastPollAt ?? null,
|
|
@@ -397,11 +507,21 @@ function printActivatedStatus() {
|
|
|
397
507
|
captureSeenSinceLastSync: Boolean(s.captureSeenSinceLastSync ?? false),
|
|
398
508
|
consecutiveFailures: s.consecutiveFailures ?? 0,
|
|
399
509
|
});
|
|
400
|
-
|
|
510
|
+
const telemetryWarnings = [];
|
|
511
|
+
if (s.telemetryConsecutiveFailures && Number(s.telemetryConsecutiveFailures) >= CONSECUTIVE_FAILURES_WARN) {
|
|
512
|
+
telemetryWarnings.push(`Instance telemetry is failing (${s.telemetryConsecutiveFailures} consecutive attempts), but capture remains active.`);
|
|
513
|
+
}
|
|
514
|
+
if (s.lastTelemetryError) {
|
|
515
|
+
telemetryWarnings.push(`Last telemetry error: ${s.lastTelemetryError}`);
|
|
516
|
+
}
|
|
517
|
+
if (statusWarnings.length > 0 || telemetryWarnings.length > 0 || snapshot.source === 'stale') {
|
|
401
518
|
console.log(' Warning: ⚠️ Capture health degraded');
|
|
402
|
-
for (const warning of statusWarnings) {
|
|
519
|
+
for (const warning of [...statusWarnings, ...telemetryWarnings]) {
|
|
403
520
|
console.log(` - ${warning}`);
|
|
404
521
|
}
|
|
522
|
+
if (snapshot.source === 'stale') {
|
|
523
|
+
console.log(' - Persisted status is stale; the gateway may have restarted or status writes may be blocked.');
|
|
524
|
+
}
|
|
405
525
|
console.log(' - If this persists, run: openclaw gateway restart');
|
|
406
526
|
}
|
|
407
527
|
const startedAt = s.startedAt;
|
|
@@ -422,9 +542,6 @@ function printActivatedStatus() {
|
|
|
422
542
|
const filled = Math.max(1, Math.round((count / max) * BAR_MAX));
|
|
423
543
|
return BAR_CHARS.slice(0, filled);
|
|
424
544
|
};
|
|
425
|
-
const fmtTime = (ms) => ms < 60_000 ? `${Math.round(ms / 1000)}s ago`
|
|
426
|
-
: ms < 3_600_000 ? `${Math.floor(ms / 60_000)}m ago`
|
|
427
|
-
: `${(ms / 3_600_000).toFixed(1)}h ago`;
|
|
428
545
|
const lastSync = s.lastSync;
|
|
429
546
|
console.log('');
|
|
430
547
|
if (lastSync && lastSync.at) {
|
|
@@ -437,7 +554,7 @@ function printActivatedStatus() {
|
|
|
437
554
|
}
|
|
438
555
|
else {
|
|
439
556
|
console.log('📡 Last sync');
|
|
440
|
-
console.log('
|
|
557
|
+
console.log(` ${snapshot.source === 'stale' ? 'Last known sync is unavailable because persisted state is stale.' : 'No sync yet. Bridge will send on the next poll cycle.'}`);
|
|
441
558
|
}
|
|
442
559
|
const counters = (s.counters ?? {});
|
|
443
560
|
const sessionEvents = counters.totalEvents ?? 0;
|
|
@@ -472,6 +589,16 @@ function printActivatedStatus() {
|
|
|
472
589
|
console.log(' (original values never stored or transmitted)');
|
|
473
590
|
}
|
|
474
591
|
}
|
|
592
|
+
function _resetForTesting() {
|
|
593
|
+
Object.assign(state, createInitialState());
|
|
594
|
+
_allTimeStats = null;
|
|
595
|
+
_allTimeStatsDirty = false;
|
|
596
|
+
_stateDirty = true;
|
|
597
|
+
firstEventDelivered = false;
|
|
598
|
+
teardownPreviousRuntime = null;
|
|
599
|
+
pendingTeardown = null;
|
|
600
|
+
serviceStartFn = null;
|
|
601
|
+
}
|
|
475
602
|
exports.default = {
|
|
476
603
|
id: 'shield',
|
|
477
604
|
name: 'OpenClaw Shield',
|
|
@@ -561,6 +688,8 @@ exports.default = {
|
|
|
561
688
|
const markStopped = opts?.markStopped ?? true;
|
|
562
689
|
if (markStopped) {
|
|
563
690
|
state.running = false;
|
|
691
|
+
state.lastLifecyclePhase = opts?.finalPhase ?? 'stopped';
|
|
692
|
+
state.lastStopReason = opts?.stopReason ?? 'unknown';
|
|
564
693
|
markStateDirty();
|
|
565
694
|
persistState();
|
|
566
695
|
}
|
|
@@ -569,7 +698,7 @@ exports.default = {
|
|
|
569
698
|
}
|
|
570
699
|
if (opts?.flushRedactor) {
|
|
571
700
|
try {
|
|
572
|
-
const { flush: flushRedactor } =
|
|
701
|
+
const { flush: flushRedactor } = require('./src/redactor');
|
|
573
702
|
flushRedactor();
|
|
574
703
|
}
|
|
575
704
|
catch { }
|
|
@@ -580,6 +709,17 @@ exports.default = {
|
|
|
580
709
|
.catch((err) => log.warn('shield', `Runtime cleanup before re-register failed: ${err instanceof Error ? err.message : String(err)}`));
|
|
581
710
|
}
|
|
582
711
|
teardownPreviousRuntime = () => cleanupRuntime({ markStopped: true, resetGuard: true, flushRedactor: false });
|
|
712
|
+
const deactivateForRegistrationInvalidation = async (checkpoint, message) => {
|
|
713
|
+
setLifecyclePhase('deactivated', checkpoint);
|
|
714
|
+
setStopReason('registration_invalidated');
|
|
715
|
+
setLastError(message);
|
|
716
|
+
await cleanupRuntime({
|
|
717
|
+
markStopped: true,
|
|
718
|
+
stopReason: 'registration_invalidated',
|
|
719
|
+
finalPhase: 'deactivated',
|
|
720
|
+
flushRedactor: true,
|
|
721
|
+
});
|
|
722
|
+
};
|
|
583
723
|
const serviceDefinition = {
|
|
584
724
|
id: 'shield-monitor',
|
|
585
725
|
async start() {
|
|
@@ -594,6 +734,11 @@ exports.default = {
|
|
|
594
734
|
try {
|
|
595
735
|
await cleanupRuntime({ markStopped: false, resetGuard: false, flushRedactor: false });
|
|
596
736
|
const activeGeneration = ++runtimeGeneration;
|
|
737
|
+
Object.assign(state, createInitialState());
|
|
738
|
+
setLastError(null);
|
|
739
|
+
setStopReason('none');
|
|
740
|
+
setLifecyclePhase('starting', 'checkpoint:1');
|
|
741
|
+
persistState();
|
|
597
742
|
log.info('shield', `[checkpoint:1] Service start() entered — generation=${activeGeneration}`);
|
|
598
743
|
let credentials = (0, config_1.loadCredentials)();
|
|
599
744
|
let validCreds = hasValidCredentials(credentials);
|
|
@@ -603,6 +748,10 @@ exports.default = {
|
|
|
603
748
|
const autoCreds = await performAutoRegistration(installationKey);
|
|
604
749
|
if (!autoCreds) {
|
|
605
750
|
log.error('shield', 'Activation failed. Verify your Installation Key and try again.');
|
|
751
|
+
setLifecyclePhase('startup_failed', 'checkpoint:activation_failed');
|
|
752
|
+
setStopReason('activation_failed');
|
|
753
|
+
setLastError('Activation failed. Verify your Installation Key and try again.');
|
|
754
|
+
persistState();
|
|
606
755
|
startGuard.endFailure();
|
|
607
756
|
return;
|
|
608
757
|
}
|
|
@@ -617,11 +766,17 @@ exports.default = {
|
|
|
617
766
|
log.warn('shield', ' Activate via CLI: openclaw shield activate <YOUR_KEY>');
|
|
618
767
|
log.warn('shield', ' Or set plugins.entries.shield.config.installationKey in openclaw.json and restart.');
|
|
619
768
|
log.warn('shield', ' Get your key at: https://uss.upx.com → APPS → OpenClaw Shield');
|
|
769
|
+
setLifecyclePhase('stopped', 'checkpoint:not_activated');
|
|
770
|
+
setStopReason('not_activated');
|
|
771
|
+
setLastError('Shield is not activated.');
|
|
772
|
+
persistState();
|
|
620
773
|
startGuard.endFailure();
|
|
621
774
|
return;
|
|
622
775
|
}
|
|
623
776
|
state.activated = true;
|
|
624
777
|
state.startedAt = Date.now();
|
|
778
|
+
setLastError(null);
|
|
779
|
+
setLifecyclePhase('starting', 'checkpoint:3');
|
|
625
780
|
const config = (0, config_1.loadConfig)({
|
|
626
781
|
credentials,
|
|
627
782
|
dryRun: dryRunVal,
|
|
@@ -630,25 +785,33 @@ exports.default = {
|
|
|
630
785
|
collectHostMetrics: hostMetricsVal,
|
|
631
786
|
});
|
|
632
787
|
state.instanceId = config.credentials.instanceId ?? '';
|
|
788
|
+
state.sessionDirCount = config.sessionDirs.length;
|
|
789
|
+
if (config.sessionDirs.length === 0) {
|
|
790
|
+
state.lastDeliveryIssue = 'No OpenClaw session directories discovered yet — Shield is loaded but has no event sources.';
|
|
791
|
+
state.lastDeliveryIssueAt = Date.now();
|
|
792
|
+
}
|
|
633
793
|
const persistedStats = readAllTimeStats();
|
|
634
794
|
if (persistedStats.lastSync)
|
|
635
795
|
state.lastSync = persistedStats.lastSync;
|
|
636
796
|
log.info('shield', `[checkpoint:3] Config loaded — sessionDirs=${config.sessionDirs.length} poll=${config.pollIntervalMs}ms dryRun=${config.dryRun}`);
|
|
637
797
|
log.info('shield', `Starting Shield v${version_1.VERSION} (poll: ${config.pollIntervalMs}ms, dryRun: ${config.dryRun})`);
|
|
798
|
+
persistState();
|
|
638
799
|
(0, exclusions_1.initExclusions)((0, path_1.join)((0, os_1.homedir)(), '.openclaw', 'shield', 'data'));
|
|
639
800
|
log.info('shield', '[checkpoint:4] Exclusions initialized');
|
|
640
801
|
(0, case_monitor_1.initCaseMonitor)((0, path_1.join)((0, os_1.homedir)(), '.openclaw', 'shield', 'data'));
|
|
641
802
|
log.info('shield', '[checkpoint:5] Case monitor initialized');
|
|
803
|
+
state.lastStartupCheckpoint = 'checkpoint:5';
|
|
804
|
+
persistState();
|
|
642
805
|
if (config.localEventBuffer) {
|
|
643
806
|
(0, event_store_1.initEventStore)((0, path_1.join)((0, os_1.homedir)(), '.openclaw', 'shield', 'data'), { maxEvents: config.localEventLimit });
|
|
644
807
|
}
|
|
645
808
|
const runtime = api.runtime;
|
|
646
|
-
|
|
647
|
-
|
|
809
|
+
const openclawConfig = api.config;
|
|
810
|
+
if (typeof runtime?.system?.enqueueSystemEvent === 'function' && typeof runtime?.system?.requestHeartbeatNow === 'function') {
|
|
648
811
|
(0, case_monitor_1.setCaseNotificationDispatcher)({
|
|
649
812
|
enqueueSystemEvent: runtime.system.enqueueSystemEvent,
|
|
650
813
|
requestHeartbeatNow: runtime.system.requestHeartbeatNow,
|
|
651
|
-
agentId:
|
|
814
|
+
agentId: openclawConfig?.agents?.default ?? 'main',
|
|
652
815
|
});
|
|
653
816
|
}
|
|
654
817
|
else {
|
|
@@ -656,7 +819,6 @@ exports.default = {
|
|
|
656
819
|
}
|
|
657
820
|
try {
|
|
658
821
|
const channels = runtime?.channel;
|
|
659
|
-
const cfg = api.config;
|
|
660
822
|
const channelCandidates = [
|
|
661
823
|
{ name: 'telegram', sendFn: channels?.telegram?.sendMessageTelegram, configPath: 'telegram' },
|
|
662
824
|
{ name: 'discord', sendFn: channels?.discord?.sendMessageDiscord, configPath: 'discord' },
|
|
@@ -667,7 +829,7 @@ exports.default = {
|
|
|
667
829
|
for (const candidate of channelCandidates) {
|
|
668
830
|
if (typeof candidate.sendFn !== 'function')
|
|
669
831
|
continue;
|
|
670
|
-
const channelCfg =
|
|
832
|
+
const channelCfg = openclawConfig?.channels?.[candidate.configPath];
|
|
671
833
|
if (!channelCfg?.enabled && channelCfg?.enabled !== undefined)
|
|
672
834
|
continue;
|
|
673
835
|
let targetId = null;
|
|
@@ -694,6 +856,9 @@ exports.default = {
|
|
|
694
856
|
break;
|
|
695
857
|
}
|
|
696
858
|
}
|
|
859
|
+
if (!channels) {
|
|
860
|
+
log.debug('case-monitor', 'Direct send runtime channels unavailable — direct message notifications disabled');
|
|
861
|
+
}
|
|
697
862
|
}
|
|
698
863
|
catch (err) {
|
|
699
864
|
log.debug('case-monitor', `Direct send setup failed (non-fatal): ${err instanceof Error ? err.message : String(err)}`);
|
|
@@ -702,7 +867,7 @@ exports.default = {
|
|
|
702
867
|
const updateState = (0, updater_1.loadUpdateState)();
|
|
703
868
|
const previousVersion = updateState.rollbackVersion ?? null;
|
|
704
869
|
const startReason = previousVersion ? 'update' : 'normal';
|
|
705
|
-
const { reportLifecycleEvent } =
|
|
870
|
+
const { reportLifecycleEvent } = require('./src/sender');
|
|
706
871
|
void reportLifecycleEvent('plugin_started', {
|
|
707
872
|
version: version_1.VERSION,
|
|
708
873
|
reason: startReason,
|
|
@@ -712,14 +877,21 @@ exports.default = {
|
|
|
712
877
|
catch { }
|
|
713
878
|
const autoUpdateMode = pluginConfig.autoUpdate ?? true;
|
|
714
879
|
const _bootState = (0, updater_1.loadUpdateState)();
|
|
880
|
+
state.lastStartupCheckpoint = 'checkpoint:6';
|
|
881
|
+
persistState();
|
|
715
882
|
log.info('shield', `[checkpoint:6] Pre-update-check — autoUpdate=${autoUpdateMode} pendingRestart=${_bootState.pendingRestart} updateAvailable=${_bootState.updateAvailable} latestVersion=${_bootState.latestVersion}`);
|
|
716
883
|
log.info('updater', `Startup update check (autoUpdate=${autoUpdateMode}, current=${version_1.VERSION}, pendingRestart=${_bootState.pendingRestart})`);
|
|
717
884
|
const startupUpdate = (0, updater_1.performAutoUpdate)(autoUpdateMode, _bootState.pendingRestart ? undefined : 0);
|
|
885
|
+
state.lastStartupCheckpoint = 'checkpoint:7';
|
|
886
|
+
persistState();
|
|
718
887
|
log.info('shield', `[checkpoint:7] Update check done — action=${startupUpdate.action}`);
|
|
719
888
|
if (startupUpdate.action === 'updated') {
|
|
720
889
|
log.info('updater', startupUpdate.message);
|
|
721
890
|
const restarted = (0, updater_1.requestGatewayRestart)();
|
|
722
891
|
if (restarted) {
|
|
892
|
+
setLifecyclePhase('stopped', 'checkpoint:update_restart');
|
|
893
|
+
setStopReason('update_restart');
|
|
894
|
+
persistState();
|
|
723
895
|
startGuard.endFailure();
|
|
724
896
|
return;
|
|
725
897
|
}
|
|
@@ -736,21 +908,22 @@ exports.default = {
|
|
|
736
908
|
}
|
|
737
909
|
try {
|
|
738
910
|
const inv = (0, inventory_1.collectInventory)();
|
|
739
|
-
const { setCachedInventory } =
|
|
911
|
+
const { setCachedInventory } = require('./src/inventory');
|
|
740
912
|
setCachedInventory(inv);
|
|
741
913
|
}
|
|
742
914
|
catch (err) {
|
|
743
915
|
log.warn('shield', `Inventory collection failed (non-fatal): ${err instanceof Error ? err.message : String(err)}`);
|
|
744
916
|
}
|
|
745
|
-
const { fetchNewEntries, commitCursors } =
|
|
746
|
-
const { transformEntries, generateHostTelemetry, resolveOpenClawVersion, resolveAgentLabel } =
|
|
747
|
-
const { sendEvents, reportInstance } =
|
|
748
|
-
const { init: initRedactor, flush: flushRedactor, redactEvent } =
|
|
749
|
-
const { validate } =
|
|
917
|
+
const { fetchNewEntries, commitCursors } = require('./src/fetcher');
|
|
918
|
+
const { transformEntries, generateHostTelemetry, resolveOpenClawVersion, resolveAgentLabel } = require('./src/transformer');
|
|
919
|
+
const { sendEvents, reportInstance } = require('./src/sender');
|
|
920
|
+
const { init: initRedactor, flush: flushRedactor, redactEvent } = require('./src/redactor');
|
|
921
|
+
const { validate } = require('./src/validator');
|
|
750
922
|
log.info('shield', '[checkpoint:8] Dynamic imports loaded');
|
|
751
923
|
if (config.redactionEnabled)
|
|
752
924
|
initRedactor();
|
|
753
925
|
state.running = true;
|
|
926
|
+
setLifecyclePhase('running', 'checkpoint:9');
|
|
754
927
|
persistState();
|
|
755
928
|
log.info('shield', '[checkpoint:9] state.running=true — entering poll loop');
|
|
756
929
|
const runTelemetry = async () => {
|
|
@@ -779,19 +952,31 @@ exports.default = {
|
|
|
779
952
|
const result = await reportInstance(instancePayload, config.credentials);
|
|
780
953
|
if (activeGeneration !== runtimeGeneration)
|
|
781
954
|
return;
|
|
955
|
+
state.lastTelemetryAt = Date.now();
|
|
782
956
|
log.info('shield', `Instance report → Platform: success=${result.ok}`);
|
|
783
957
|
if (result.ok) {
|
|
784
|
-
state.
|
|
958
|
+
state.telemetryConsecutiveFailures = 0;
|
|
959
|
+
state.lastSuccessfulTelemetryAt = Date.now();
|
|
960
|
+
state.lastTelemetryError = null;
|
|
961
|
+
if (state.lastLifecyclePhase === 'degraded' && state.running) {
|
|
962
|
+
state.lastLifecyclePhase = 'running';
|
|
963
|
+
}
|
|
785
964
|
}
|
|
786
965
|
else {
|
|
787
|
-
state.
|
|
788
|
-
|
|
789
|
-
|
|
790
|
-
|
|
791
|
-
|
|
792
|
-
|
|
966
|
+
state.telemetryConsecutiveFailures++;
|
|
967
|
+
state.lastTelemetryError = result.error ? String(result.error).slice(0, 200) : `HTTP ${result.statusCode ?? 0}`;
|
|
968
|
+
if (result.needsRegistration) {
|
|
969
|
+
log.error('shield', 'Instance report says this Shield instance is no longer registered — deactivating monitoring.');
|
|
970
|
+
await deactivateForRegistrationInvalidation('checkpoint:telemetry_registration_invalid', 'Instance registration invalidated by platform.');
|
|
971
|
+
return;
|
|
972
|
+
}
|
|
973
|
+
if (state.telemetryConsecutiveFailures >= MAX_REGISTRATION_FAILURES) {
|
|
974
|
+
log.error('shield', `Instance telemetry failed ${state.telemetryConsecutiveFailures} consecutive times — capture remains active but health is degraded.`);
|
|
975
|
+
setLifecyclePhase('degraded');
|
|
793
976
|
}
|
|
794
977
|
}
|
|
978
|
+
markStateDirty();
|
|
979
|
+
persistState();
|
|
795
980
|
};
|
|
796
981
|
const runTelemetrySingleflight = createSingleflightRunner(runTelemetry);
|
|
797
982
|
log.info('shield', '[checkpoint:9a] Firing initial telemetry');
|
|
@@ -828,7 +1013,10 @@ exports.default = {
|
|
|
828
1013
|
if (entries.length === 0) {
|
|
829
1014
|
commitCursors(config, []);
|
|
830
1015
|
state.consecutiveFailures = 0;
|
|
1016
|
+
state.lastPollError = null;
|
|
831
1017
|
state.lastPollAt = Date.now();
|
|
1018
|
+
state.lastSuccessfulPollAt = state.lastPollAt;
|
|
1019
|
+
state.eventsRetainedForRetry = 0;
|
|
832
1020
|
markStateDirty();
|
|
833
1021
|
persistState();
|
|
834
1022
|
const idlePlatformConfig = { apiUrl: config.credentials.apiUrl, instanceId: config.credentials.instanceId, hmacSecret: config.credentials.hmacSecret };
|
|
@@ -858,8 +1046,7 @@ exports.default = {
|
|
|
858
1046
|
const needsReg = results.some(r => r.needsRegistration);
|
|
859
1047
|
if (needsReg) {
|
|
860
1048
|
log.error('shield', 'Instance not registered on platform — Shield deactivated.');
|
|
861
|
-
|
|
862
|
-
markStateDirty();
|
|
1049
|
+
await deactivateForRegistrationInvalidation('checkpoint:poll_registration_invalid', 'Instance registration invalidated by platform.');
|
|
863
1050
|
return;
|
|
864
1051
|
}
|
|
865
1052
|
const pending = results.find(r => r.pendingNamespace);
|
|
@@ -867,6 +1054,10 @@ exports.default = {
|
|
|
867
1054
|
const waitMs = Math.min(pending.retryAfterMs ?? 300_000, MAX_BACKOFF_MS);
|
|
868
1055
|
log.warn('shield', `Namespace allocation in progress — holding events, backing off ${Math.round(waitMs / 1000)}s`);
|
|
869
1056
|
state.lastPollAt = Date.now();
|
|
1057
|
+
state.lastPollError = null;
|
|
1058
|
+
state.lastDeliveryIssue = describeSendFailure(pending.failureKind, pending.statusCode, pending.body);
|
|
1059
|
+
state.lastDeliveryIssueAt = Date.now();
|
|
1060
|
+
state.eventsRetainedForRetry = pending.eventCount;
|
|
870
1061
|
markStateDirty();
|
|
871
1062
|
persistState();
|
|
872
1063
|
await new Promise(r => setTimeout(r, waitMs));
|
|
@@ -875,7 +1066,9 @@ exports.default = {
|
|
|
875
1066
|
return;
|
|
876
1067
|
}
|
|
877
1068
|
const accepted = results.reduce((sum, r) => sum + (r.success ? r.eventCount : 0), 0);
|
|
878
|
-
|
|
1069
|
+
const allBatchesSucceeded = results.length > 0 && results.every(r => r.success);
|
|
1070
|
+
const firstFailure = results.find(r => !r.success);
|
|
1071
|
+
if (allBatchesSucceeded && accepted > 0) {
|
|
879
1072
|
(0, case_monitor_1.notifyCaseMonitorActivity)();
|
|
880
1073
|
if (!firstEventDelivered) {
|
|
881
1074
|
firstEventDelivered = true;
|
|
@@ -885,6 +1078,14 @@ exports.default = {
|
|
|
885
1078
|
flushRedactor();
|
|
886
1079
|
state.eventsProcessed += accepted;
|
|
887
1080
|
state.consecutiveFailures = 0;
|
|
1081
|
+
state.lastPollError = null;
|
|
1082
|
+
state.lastDeliveryIssue = null;
|
|
1083
|
+
state.lastDeliveryIssueAt = 0;
|
|
1084
|
+
state.eventsRetainedForRetry = 0;
|
|
1085
|
+
state.lastSuccessfulPollAt = Date.now();
|
|
1086
|
+
if (state.lastLifecyclePhase === 'degraded') {
|
|
1087
|
+
state.lastLifecyclePhase = 'running';
|
|
1088
|
+
}
|
|
888
1089
|
markStateDirty();
|
|
889
1090
|
const syncEventTypes = {};
|
|
890
1091
|
for (const env of envelopes) {
|
|
@@ -908,8 +1109,30 @@ exports.default = {
|
|
|
908
1109
|
(0, event_store_1.appendEvents)(summaries);
|
|
909
1110
|
}
|
|
910
1111
|
}
|
|
1112
|
+
else if (accepted > 0) {
|
|
1113
|
+
state.consecutiveFailures++;
|
|
1114
|
+
state.lastPollError = 'Partial delivery detected — retaining cursor position to retry all events safely.';
|
|
1115
|
+
state.lastDeliveryIssue = firstFailure
|
|
1116
|
+
? `${describeSendFailure(firstFailure.failureKind, firstFailure.statusCode, firstFailure.body)}. Retrying entire poll window without advancing cursors.`
|
|
1117
|
+
: 'Partial delivery detected — retrying without advancing cursors.';
|
|
1118
|
+
state.lastDeliveryIssueAt = Date.now();
|
|
1119
|
+
state.eventsRetainedForRetry = envelopes.length;
|
|
1120
|
+
setLifecyclePhase('degraded');
|
|
1121
|
+
log.warn('shield', 'Partial delivery detected — not advancing cursors so events can be retried safely. Some already-accepted events may be resent.');
|
|
1122
|
+
markStateDirty();
|
|
1123
|
+
}
|
|
911
1124
|
else {
|
|
912
1125
|
state.consecutiveFailures++;
|
|
1126
|
+
state.lastPollError = firstFailure
|
|
1127
|
+
? describeSendFailure(firstFailure.failureKind, firstFailure.statusCode, firstFailure.body)
|
|
1128
|
+
: 'Event delivery failed';
|
|
1129
|
+
state.lastDeliveryIssue = state.lastPollError;
|
|
1130
|
+
state.lastDeliveryIssueAt = Date.now();
|
|
1131
|
+
state.eventsRetainedForRetry = envelopes.length;
|
|
1132
|
+
state.lastLifecyclePhase = 'degraded';
|
|
1133
|
+
if (firstFailure?.failureKind === 'quota_exceeded') {
|
|
1134
|
+
setLifecyclePhase('degraded');
|
|
1135
|
+
}
|
|
913
1136
|
markStateDirty();
|
|
914
1137
|
}
|
|
915
1138
|
state.lastPollAt = Date.now();
|
|
@@ -924,8 +1147,13 @@ exports.default = {
|
|
|
924
1147
|
if (activeGeneration !== runtimeGeneration)
|
|
925
1148
|
return;
|
|
926
1149
|
state.consecutiveFailures++;
|
|
1150
|
+
state.lastPollError = err instanceof Error ? err.message : String(err);
|
|
1151
|
+
state.lastDeliveryIssue = state.lastPollError;
|
|
1152
|
+
state.lastDeliveryIssueAt = Date.now();
|
|
1153
|
+
state.lastLifecyclePhase = 'degraded';
|
|
927
1154
|
markStateDirty();
|
|
928
1155
|
log.error('shield', `Poll error: ${err instanceof Error ? err.message : String(err)}`);
|
|
1156
|
+
persistState();
|
|
929
1157
|
}
|
|
930
1158
|
};
|
|
931
1159
|
const runPollSingleflight = createSingleflightRunner(poll);
|
|
@@ -957,7 +1185,8 @@ exports.default = {
|
|
|
957
1185
|
if (!state.running)
|
|
958
1186
|
return;
|
|
959
1187
|
log.info('shield', '[checkpoint:SIGTERM] SIGTERM received — shutting down');
|
|
960
|
-
|
|
1188
|
+
setLifecyclePhase('stopping', 'checkpoint:SIGTERM');
|
|
1189
|
+
await cleanupRuntime({ markStopped: true, resetGuard: true, flushRedactor: true, stopReason: 'signal' });
|
|
961
1190
|
log.info('shield', 'Service stopped (signal)');
|
|
962
1191
|
};
|
|
963
1192
|
process.once('SIGTERM', onSignalHandler);
|
|
@@ -966,13 +1195,18 @@ exports.default = {
|
|
|
966
1195
|
startGuard.endSuccess();
|
|
967
1196
|
}
|
|
968
1197
|
catch (err) {
|
|
1198
|
+
setLifecyclePhase('startup_failed');
|
|
1199
|
+
setStopReason('startup_failed');
|
|
1200
|
+
setLastError(err instanceof Error ? err.message : String(err));
|
|
1201
|
+
persistState();
|
|
969
1202
|
startGuard.endFailure();
|
|
970
1203
|
throw err;
|
|
971
1204
|
}
|
|
972
1205
|
},
|
|
973
1206
|
async stop() {
|
|
974
1207
|
const wasRunning = state.running;
|
|
975
|
-
|
|
1208
|
+
setLifecyclePhase('stopping');
|
|
1209
|
+
await cleanupRuntime({ markStopped: true, resetGuard: true, flushRedactor: true, stopReason: 'service_stop' });
|
|
976
1210
|
if (wasRunning)
|
|
977
1211
|
log.info('shield', 'Service stopped');
|
|
978
1212
|
},
|
|
@@ -984,6 +1218,7 @@ exports.default = {
|
|
|
984
1218
|
const activated = state.activated || hasValidCredentials(creds);
|
|
985
1219
|
const caseStatus = (0, case_monitor_1.getCaseMonitorStatus)();
|
|
986
1220
|
const monitorHealth = (0, case_monitor_1.getMonitorHealth)();
|
|
1221
|
+
const updateState = (0, updater_1.loadUpdateState)();
|
|
987
1222
|
const caseMonitorDisplay = monitorHealth.status === 'degraded'
|
|
988
1223
|
? `⚠️ Case Monitor: DEGRADED — ${monitorHealth.consecutiveFailures} consecutive failures since ${monitorHealth.degradedSinceMs ? new Date(monitorHealth.degradedSinceMs).toISOString() : 'unknown'}. Last error: ${monitorHealth.lastErrorMessage}`
|
|
989
1224
|
: `✅ Case Monitor: ok`;
|
|
@@ -1005,18 +1240,47 @@ exports.default = {
|
|
|
1005
1240
|
}
|
|
1006
1241
|
}
|
|
1007
1242
|
const hasSecret = !!creds?.hmacSecret && !PLACEHOLDER_VALUES.has((creds.hmacSecret || '').trim().toLowerCase());
|
|
1008
|
-
const stateField = hasSecret
|
|
1243
|
+
const stateField = !hasSecret
|
|
1244
|
+
? 'unconfigured'
|
|
1245
|
+
: !activated
|
|
1246
|
+
? 'pending'
|
|
1247
|
+
: state.running
|
|
1248
|
+
? (state.lastLifecyclePhase === 'degraded' ? 'degraded' : 'connected')
|
|
1249
|
+
: state.lastLifecyclePhase;
|
|
1009
1250
|
respond(true, {
|
|
1010
1251
|
activated,
|
|
1011
1252
|
state: stateField,
|
|
1012
1253
|
running: state.running,
|
|
1013
1254
|
lastPollAt: state.lastPollAt,
|
|
1255
|
+
lastSuccessfulPollAt: state.lastSuccessfulPollAt,
|
|
1014
1256
|
lastCaptureAt: state.lastCaptureAt,
|
|
1015
1257
|
captureSeenSinceLastSync: state.captureSeenSinceLastSync,
|
|
1016
1258
|
eventsProcessed: state.eventsProcessed,
|
|
1017
1259
|
quarantineCount: state.quarantineCount,
|
|
1018
1260
|
consecutiveFailures: state.consecutiveFailures,
|
|
1261
|
+
telemetryConsecutiveFailures: state.telemetryConsecutiveFailures,
|
|
1262
|
+
lastTelemetryAt: state.lastTelemetryAt,
|
|
1263
|
+
lastSuccessfulTelemetryAt: state.lastSuccessfulTelemetryAt,
|
|
1264
|
+
lastTelemetryError: state.lastTelemetryError,
|
|
1265
|
+
lastPollError: state.lastPollError,
|
|
1266
|
+
lastLifecyclePhase: state.lastLifecyclePhase,
|
|
1267
|
+
lastStopReason: state.lastStopReason,
|
|
1268
|
+
lastError: state.lastError,
|
|
1269
|
+
lastStartupCheckpoint: state.lastStartupCheckpoint,
|
|
1270
|
+
sessionDirCount: state.sessionDirCount,
|
|
1271
|
+
eventsRetainedForRetry: state.eventsRetainedForRetry,
|
|
1272
|
+
lastDeliveryIssue: state.lastDeliveryIssue,
|
|
1273
|
+
lastDeliveryIssueAt: state.lastDeliveryIssueAt,
|
|
1019
1274
|
version: version_1.VERSION,
|
|
1275
|
+
update: {
|
|
1276
|
+
pendingRestart: updateState.pendingRestart,
|
|
1277
|
+
restartAttempts: updateState.restartAttempts,
|
|
1278
|
+
latestVersion: updateState.latestVersion,
|
|
1279
|
+
updateAvailable: updateState.updateAvailable,
|
|
1280
|
+
lastError: updateState.lastError,
|
|
1281
|
+
lastFailureStage: updateState.lastFailureStage,
|
|
1282
|
+
rollbackPending: updateState.rollbackPending,
|
|
1283
|
+
},
|
|
1020
1284
|
caseMonitor: {
|
|
1021
1285
|
intervalMs: caseStatus.intervalMs,
|
|
1022
1286
|
nextCheckInMs: caseStatus.nextCheckIn,
|
|
@@ -1071,34 +1335,39 @@ exports.default = {
|
|
|
1071
1335
|
hmacSecret: rpcCreds?.hmacSecret || '',
|
|
1072
1336
|
};
|
|
1073
1337
|
(0, rpc_1.registerAllRpcs)(api, platformApiConfig);
|
|
1074
|
-
api.registerCommand
|
|
1075
|
-
|
|
1076
|
-
|
|
1077
|
-
|
|
1078
|
-
|
|
1079
|
-
|
|
1080
|
-
|
|
1081
|
-
|
|
1082
|
-
|
|
1083
|
-
|
|
1084
|
-
|
|
1085
|
-
|
|
1086
|
-
const
|
|
1087
|
-
|
|
1088
|
-
|
|
1089
|
-
|
|
1090
|
-
|
|
1091
|
-
|
|
1092
|
-
|
|
1093
|
-
|
|
1094
|
-
|
|
1095
|
-
|
|
1096
|
-
|
|
1097
|
-
|
|
1098
|
-
|
|
1099
|
-
|
|
1100
|
-
|
|
1101
|
-
|
|
1338
|
+
if (typeof api.registerCommand === 'function') {
|
|
1339
|
+
api.registerCommand({
|
|
1340
|
+
name: 'shieldcases',
|
|
1341
|
+
description: 'Show pending Shield security cases',
|
|
1342
|
+
requireAuth: true,
|
|
1343
|
+
handler: () => {
|
|
1344
|
+
const { getPendingCases } = require('./src/case-monitor');
|
|
1345
|
+
const cases = getPendingCases();
|
|
1346
|
+
if (cases.length === 0) {
|
|
1347
|
+
return { text: '✅ No pending Shield security cases.' };
|
|
1348
|
+
}
|
|
1349
|
+
const severityEmoji = { CRITICAL: '🔴', HIGH: '🟠', MEDIUM: '🟡', LOW: '🔵' };
|
|
1350
|
+
const lines = cases.map((c) => {
|
|
1351
|
+
const emoji = severityEmoji[c.severity] || '⚠️';
|
|
1352
|
+
const age = Math.floor((Date.now() - new Date(c.created_at).getTime()) / 60000);
|
|
1353
|
+
const ageStr = age < 60 ? `${age}m` : `${Math.floor(age / 60)}h`;
|
|
1354
|
+
return `${emoji} **${c.rule_title}** (${c.severity || 'unknown'}) — ${c.event_count} events — ${ageStr} ago`;
|
|
1355
|
+
});
|
|
1356
|
+
return {
|
|
1357
|
+
text: [
|
|
1358
|
+
`🛡️ **Shield — ${cases.length} Pending Case${cases.length > 1 ? 's' : ''}**`,
|
|
1359
|
+
'',
|
|
1360
|
+
...lines,
|
|
1361
|
+
'',
|
|
1362
|
+
'Use `openclaw shield cases show <id>` for details.',
|
|
1363
|
+
].join('\n'),
|
|
1364
|
+
};
|
|
1365
|
+
},
|
|
1366
|
+
});
|
|
1367
|
+
}
|
|
1368
|
+
else {
|
|
1369
|
+
log.debug('shield', 'registerCommand not available — auto-reply command /shieldcases disabled on this OpenClaw runtime');
|
|
1370
|
+
}
|
|
1102
1371
|
api.registerCli(({ program }) => {
|
|
1103
1372
|
const shield = program.command('shield');
|
|
1104
1373
|
(0, cli_cases_1.registerCasesCli)(shield);
|
|
@@ -1237,10 +1506,9 @@ exports.default = {
|
|
|
1237
1506
|
step('Stale updateAvailable cleared');
|
|
1238
1507
|
}
|
|
1239
1508
|
step('Resetting session state...');
|
|
1240
|
-
state
|
|
1241
|
-
state.
|
|
1242
|
-
state.
|
|
1243
|
-
state.captureSeenSinceLastSync = false;
|
|
1509
|
+
Object.assign(state, createInitialState());
|
|
1510
|
+
state.lastLifecyclePhase = 'stopping';
|
|
1511
|
+
state.lastStopReason = 'manual_restart';
|
|
1244
1512
|
markStateDirty();
|
|
1245
1513
|
persistState();
|
|
1246
1514
|
step('State reset');
|
|
@@ -1332,7 +1600,7 @@ exports.default = {
|
|
|
1332
1600
|
console.log(` agent:${agentHash} workspace:${wsHash} identity=${identityLabel} bootstrapped=${bootstrapLabel}`);
|
|
1333
1601
|
}
|
|
1334
1602
|
try {
|
|
1335
|
-
const { initVault, getAllMappings } =
|
|
1603
|
+
const { initVault, getAllMappings } = require('./src/redactor/vault');
|
|
1336
1604
|
initVault();
|
|
1337
1605
|
const mappings = getAllMappings();
|
|
1338
1606
|
const tokens = Object.keys(mappings);
|