@upx-us/shield 0.7.10 → 0.7.12

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -4,6 +4,22 @@ All notable changes to this project will be documented in this file.
4
4
 
5
5
  ---
6
6
 
7
+ ## [0.7.12] — 2026-03-16
8
+
9
+ ### Fixed
10
+
11
+ - **Plugin showing permanently "Disconnected" after gateway hot-reload** — when the OpenClaw gateway reloaded the plugin module, a race condition between teardown and startup caused new startup attempts to silently abort. The previous instance's timers kept running but were invisible to the status command, resulting in permanent "Disconnected" status and missed telemetry cycles. The teardown is now properly awaited before startup proceeds.
12
+
13
+ ---
14
+
15
+ ## [0.7.11] — 2026-03-16
16
+
17
+ ### Added
18
+
19
+ - **Debug logging mode** (`debugLog: true`) — writes timestamped logs to `~/.openclaw/shield/logs/shield-debug.log`. Captures the full startup sequence with numbered checkpoints (1–11 + SIGTERM) so disconnection issues can be pinpointed exactly. Off by default — safe on all existing installs.
20
+
21
+ ---
22
+
7
23
  ## [0.7.10] — 2026-03-16
8
24
 
9
25
  ### Added
package/dist/index.js CHANGED
@@ -319,6 +319,7 @@ const state = {
319
319
  };
320
320
  let firstEventDelivered = false;
321
321
  let teardownPreviousRuntime = null;
322
+ let pendingTeardown = null;
322
323
  let serviceStartFn = null;
323
324
  const MAX_BACKOFF_MS = 5 * 60 * 1000;
324
325
  const TELEMETRY_INTERVAL_MS = 5 * 60 * 1000;
@@ -488,6 +489,38 @@ exports.default = {
488
489
  (0, log_1.setAdapter)(gatewayAdapter);
489
490
  const pluginConfig = (api.pluginConfig ?? {});
490
491
  log.debug('shield', 'Plugin config received', maskPluginConfigForLogs(pluginConfig));
492
+ const debugLogEnabled = pluginConfig.debugLog === true;
493
+ if (debugLogEnabled) {
494
+ try {
495
+ const { appendFileSync, mkdirSync } = require('fs');
496
+ const { join: pathJoin } = require('path');
497
+ const debugLogDir = pathJoin((0, os_1.homedir)(), '.openclaw', 'shield', 'logs');
498
+ const debugLogPath = pathJoin(debugLogDir, 'shield-debug.log');
499
+ mkdirSync(debugLogDir, { recursive: true });
500
+ const ts = () => new Date().toISOString();
501
+ const writeLine = (level, tag, msg, extra) => {
502
+ try {
503
+ const line = `${ts()} [${level.padEnd(5)}] [${tag}] ${msg}${extra !== undefined ? ' ' + JSON.stringify(extra) : ''}\n`;
504
+ appendFileSync(debugLogPath, line);
505
+ }
506
+ catch { }
507
+ };
508
+ const teeAdapter = {
509
+ debug(tag, msg, data) { gatewayAdapter.debug(tag, msg, data); writeLine('DEBUG', tag, msg, data); },
510
+ info(tag, msg) { gatewayAdapter.info(tag, msg); writeLine('INFO', tag, msg); },
511
+ warn(tag, msg) { gatewayAdapter.warn(tag, msg); writeLine('WARN', tag, msg); },
512
+ error(tag, msg, err) { gatewayAdapter.error(tag, msg, err); writeLine('ERROR', tag, msg, err ? String(err) : undefined); },
513
+ };
514
+ (0, log_1.setAdapter)(teeAdapter);
515
+ appendFileSync(debugLogPath, `\n${'─'.repeat(80)}\n` +
516
+ `${ts()} [SHIELD DEBUG SESSION START] v${version_1.VERSION}\n` +
517
+ `${'─'.repeat(80)}\n`);
518
+ log.info('shield', `Debug logging active → ${debugLogPath}`);
519
+ }
520
+ catch (err) {
521
+ log.warn('shield', `Failed to init debug log file: ${err instanceof Error ? err.message : String(err)}`);
522
+ }
523
+ }
491
524
  if (pluginConfig.enabled === false) {
492
525
  log.info('shield', 'Monitoring disabled via config (enabled: false)');
493
526
  return;
@@ -543,13 +576,17 @@ exports.default = {
543
576
  }
544
577
  };
545
578
  if (teardownPreviousRuntime) {
546
- void teardownPreviousRuntime()
579
+ pendingTeardown = teardownPreviousRuntime()
547
580
  .catch((err) => log.warn('shield', `Runtime cleanup before re-register failed: ${err instanceof Error ? err.message : String(err)}`));
548
581
  }
549
582
  teardownPreviousRuntime = () => cleanupRuntime({ markStopped: true, resetGuard: true, flushRedactor: false });
550
583
  const serviceDefinition = {
551
584
  id: 'shield-monitor',
552
585
  async start() {
586
+ if (pendingTeardown) {
587
+ await pendingTeardown.catch(() => { });
588
+ pendingTeardown = null;
589
+ }
553
590
  if (!startGuard.begin()) {
554
591
  log.debug('shield', 'Start requested while service is already started or in progress');
555
592
  return;
@@ -557,8 +594,10 @@ exports.default = {
557
594
  try {
558
595
  await cleanupRuntime({ markStopped: false, resetGuard: false, flushRedactor: false });
559
596
  const activeGeneration = ++runtimeGeneration;
597
+ log.info('shield', `[checkpoint:1] Service start() entered — generation=${activeGeneration}`);
560
598
  let credentials = (0, config_1.loadCredentials)();
561
599
  let validCreds = hasValidCredentials(credentials);
600
+ log.info('shield', `[checkpoint:2] Credentials loaded — valid=${validCreds} instanceId=${credentials?.instanceId ? credentials.instanceId.slice(0, 8) + '…' : 'missing'}`);
562
601
  if (!validCreds && installationKey) {
563
602
  log.info('shield', 'Installation key found — activating Shield (first-time setup)...');
564
603
  const autoCreds = await performAutoRegistration(installationKey);
@@ -594,9 +633,12 @@ exports.default = {
594
633
  const persistedStats = readAllTimeStats();
595
634
  if (persistedStats.lastSync)
596
635
  state.lastSync = persistedStats.lastSync;
636
+ log.info('shield', `[checkpoint:3] Config loaded — sessionDirs=${config.sessionDirs.length} poll=${config.pollIntervalMs}ms dryRun=${config.dryRun}`);
597
637
  log.info('shield', `Starting Shield v${version_1.VERSION} (poll: ${config.pollIntervalMs}ms, dryRun: ${config.dryRun})`);
598
638
  (0, exclusions_1.initExclusions)((0, path_1.join)((0, os_1.homedir)(), '.openclaw', 'shield', 'data'));
639
+ log.info('shield', '[checkpoint:4] Exclusions initialized');
599
640
  (0, case_monitor_1.initCaseMonitor)((0, path_1.join)((0, os_1.homedir)(), '.openclaw', 'shield', 'data'));
641
+ log.info('shield', '[checkpoint:5] Case monitor initialized');
600
642
  if (config.localEventBuffer) {
601
643
  (0, event_store_1.initEventStore)((0, path_1.join)((0, os_1.homedir)(), '.openclaw', 'shield', 'data'), { maxEvents: config.localEventLimit });
602
644
  }
@@ -670,8 +712,10 @@ exports.default = {
670
712
  catch { }
671
713
  const autoUpdateMode = pluginConfig.autoUpdate ?? true;
672
714
  const _bootState = (0, updater_1.loadUpdateState)();
715
+ log.info('shield', `[checkpoint:6] Pre-update-check — autoUpdate=${autoUpdateMode} pendingRestart=${_bootState.pendingRestart} updateAvailable=${_bootState.updateAvailable} latestVersion=${_bootState.latestVersion}`);
673
716
  log.info('updater', `Startup update check (autoUpdate=${autoUpdateMode}, current=${version_1.VERSION}, pendingRestart=${_bootState.pendingRestart})`);
674
717
  const startupUpdate = (0, updater_1.performAutoUpdate)(autoUpdateMode, _bootState.pendingRestart ? undefined : 0);
718
+ log.info('shield', `[checkpoint:7] Update check done — action=${startupUpdate.action}`);
675
719
  if (startupUpdate.action === 'updated') {
676
720
  log.info('updater', startupUpdate.message);
677
721
  const restarted = (0, updater_1.requestGatewayRestart)();
@@ -703,10 +747,12 @@ exports.default = {
703
747
  const { sendEvents, reportInstance } = await Promise.resolve().then(() => __importStar(require('./src/sender')));
704
748
  const { init: initRedactor, flush: flushRedactor, redactEvent } = await Promise.resolve().then(() => __importStar(require('./src/redactor')));
705
749
  const { validate } = await Promise.resolve().then(() => __importStar(require('./src/validator')));
750
+ log.info('shield', '[checkpoint:8] Dynamic imports loaded');
706
751
  if (config.redactionEnabled)
707
752
  initRedactor();
708
753
  state.running = true;
709
754
  persistState();
755
+ log.info('shield', '[checkpoint:9] state.running=true — entering poll loop');
710
756
  const runTelemetry = async () => {
711
757
  if (!state.running || activeGeneration !== runtimeGeneration)
712
758
  return;
@@ -748,6 +794,7 @@ exports.default = {
748
794
  }
749
795
  };
750
796
  const runTelemetrySingleflight = createSingleflightRunner(runTelemetry);
797
+ log.info('shield', '[checkpoint:9a] Firing initial telemetry');
751
798
  runTelemetrySingleflight().catch((err) => log.error('shield', `Telemetry error: ${err instanceof Error ? err.message : String(err)}`));
752
799
  telemetryHandle = setInterval(() => {
753
800
  if (activeGeneration !== runtimeGeneration || !state.running)
@@ -904,15 +951,18 @@ exports.default = {
904
951
  });
905
952
  }, interval);
906
953
  };
954
+ log.info('shield', '[checkpoint:10] First poll scheduled');
907
955
  schedulePoll();
908
956
  onSignalHandler = async () => {
909
957
  if (!state.running)
910
958
  return;
959
+ log.info('shield', '[checkpoint:SIGTERM] SIGTERM received — shutting down');
911
960
  await cleanupRuntime({ markStopped: true, resetGuard: true, flushRedactor: true });
912
961
  log.info('shield', 'Service stopped (signal)');
913
962
  };
914
963
  process.once('SIGTERM', onSignalHandler);
915
964
  process.once('SIGINT', onSignalHandler);
965
+ log.info('shield', '[checkpoint:11] Startup complete — service running');
916
966
  startGuard.endSuccess();
917
967
  }
918
968
  catch (err) {
@@ -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.7.10",
5
+ "version": "0.7.12",
6
6
  "skills": [
7
7
  "./skills"
8
8
  ],
@@ -48,6 +48,11 @@
48
48
  ],
49
49
  "default": true,
50
50
  "description": "Auto-update mode: true (auto-update patch versions), false (disabled), or 'notify-only' (log available updates without installing)."
51
+ },
52
+ "debugLog": {
53
+ "type": "boolean",
54
+ "default": false,
55
+ "description": "Enable verbose debug logging to ~/.openclaw/shield/logs/shield-debug.log. Use only when diagnosing connectivity issues."
51
56
  }
52
57
  }
53
58
  },
@@ -74,6 +79,10 @@
74
79
  "autoUpdate": {
75
80
  "label": "Auto-update mode",
76
81
  "description": "true = auto-install patch updates, 'notify-only' = log only, false = disabled"
82
+ },
83
+ "debugLog": {
84
+ "label": "Debug logging",
85
+ "description": "Writes detailed startup and lifecycle logs to a file for support diagnosis"
77
86
  }
78
87
  },
79
88
  "clawhub": {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@upx-us/shield",
3
- "version": "0.7.10",
3
+ "version": "0.7.12",
4
4
  "description": "Security monitoring plugin for OpenClaw agents — streams enriched security events to the Shield detection platform",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",