@upx-us/shield 0.7.8 → 0.7.10

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,32 @@ All notable changes to this project will be documented in this file.
4
4
 
5
5
  ---
6
6
 
7
+ ## [0.7.10] — 2026-03-16
8
+
9
+ ### Added
10
+
11
+ - **`openclaw shield restart` command** — restarts Shield monitoring without a full gateway restart. Checks credentials, clears any stale update state, resets the session, and cycles the runtime. Use `--verbose` for step-by-step diagnostic output.
12
+
13
+ ### Fixed
14
+
15
+ - **Plugin no longer silently stalls when telemetry consistently fails** — after 10 consecutive failed `reportInstance` calls, the plugin deactivates with a clear log message instead of running indefinitely with no output.
16
+ - **Update cycle cleanup after successful auto-update** — the periodic update check now properly stops all activity after calling a successful gateway restart, preventing a window where both the old and new instance could be active simultaneously.
17
+
18
+ ### Changed
19
+
20
+ - **Standalone `shield-bridge` binary removed** — the plugin now runs exclusively as an OpenClaw gateway plugin. Running `npx @upx-us/shield` directly shows a clear message with installation instructions. This eliminates a source of divergent behavior that caused the v0.7.7 incident.
21
+ - `bridge` terminology removed from all customer-visible log output and internal comments.
22
+
23
+ ---
24
+
25
+ ## [0.7.9] — 2026-03-16
26
+
27
+ ### Fixed
28
+
29
+ - **Bug causing the plugin to repeatedly reinstall itself to the same version** — After a self-update, the plugin could enter a loop installing the already-installed version (e.g. 0.7.8 → 0.7.8) because the cached update state was not cleared correctly. The updater now verifies that the candidate version is strictly newer than the running version before proceeding with any install.
30
+
31
+ ---
32
+
7
33
  ## [0.7.8] — 2026-03-16
8
34
 
9
35
  ### Fixed
package/dist/index.js CHANGED
@@ -319,8 +319,10 @@ const state = {
319
319
  };
320
320
  let firstEventDelivered = false;
321
321
  let teardownPreviousRuntime = null;
322
+ let serviceStartFn = null;
322
323
  const MAX_BACKOFF_MS = 5 * 60 * 1000;
323
324
  const TELEMETRY_INTERVAL_MS = 5 * 60 * 1000;
325
+ const MAX_REGISTRATION_FAILURES = 10;
324
326
  function getBackoffInterval(baseMs) {
325
327
  if (state.consecutiveFailures === 0)
326
328
  return baseMs;
@@ -545,7 +547,7 @@ exports.default = {
545
547
  .catch((err) => log.warn('shield', `Runtime cleanup before re-register failed: ${err instanceof Error ? err.message : String(err)}`));
546
548
  }
547
549
  teardownPreviousRuntime = () => cleanupRuntime({ markStopped: true, resetGuard: true, flushRedactor: false });
548
- api.registerService({
550
+ const serviceDefinition = {
549
551
  id: 'shield-monitor',
550
552
  async start() {
551
553
  if (!startGuard.begin()) {
@@ -565,7 +567,7 @@ exports.default = {
565
567
  startGuard.endFailure();
566
568
  return;
567
569
  }
568
- log.info('shield', '✅ Shield activated! Starting monitoring bridge...');
570
+ log.info('shield', '✅ Shield activated! Starting Shield monitoring...');
569
571
  log.info('shield', ` Credentials saved to ${config_1.SHIELD_CONFIG_PATH}`);
570
572
  log.info('shield', ' Tip: you can remove installationKey from config after first activation.');
571
573
  credentials = autoCreds;
@@ -592,7 +594,7 @@ exports.default = {
592
594
  const persistedStats = readAllTimeStats();
593
595
  if (persistedStats.lastSync)
594
596
  state.lastSync = persistedStats.lastSync;
595
- log.info('shield', `Starting monitoring bridge v${version_1.VERSION} (poll: ${config.pollIntervalMs}ms, dryRun: ${config.dryRun})`);
597
+ log.info('shield', `Starting Shield v${version_1.VERSION} (poll: ${config.pollIntervalMs}ms, dryRun: ${config.dryRun})`);
596
598
  (0, exclusions_1.initExclusions)((0, path_1.join)((0, os_1.homedir)(), '.openclaw', 'shield', 'data'));
597
599
  (0, case_monitor_1.initCaseMonitor)((0, path_1.join)((0, os_1.homedir)(), '.openclaw', 'shield', 'data'));
598
600
  if (config.localEventBuffer) {
@@ -732,6 +734,18 @@ exports.default = {
732
734
  if (activeGeneration !== runtimeGeneration)
733
735
  return;
734
736
  log.info('shield', `Instance report → Platform: success=${result.ok}`);
737
+ if (result.ok) {
738
+ state.consecutiveFailures = 0;
739
+ }
740
+ else {
741
+ state.consecutiveFailures++;
742
+ if (state.consecutiveFailures >= MAX_REGISTRATION_FAILURES) {
743
+ log.error('shield', `Instance report failed ${state.consecutiveFailures} consecutive times — Shield deactivated. Re-run: openclaw shield activate <KEY>`);
744
+ state.running = false;
745
+ markStateDirty();
746
+ persistState();
747
+ }
748
+ }
735
749
  };
736
750
  const runTelemetrySingleflight = createSingleflightRunner(runTelemetry);
737
751
  runTelemetrySingleflight().catch((err) => log.error('shield', `Telemetry error: ${err instanceof Error ? err.message : String(err)}`));
@@ -744,9 +758,12 @@ exports.default = {
744
758
  if (updateResult.action !== 'none') {
745
759
  log.info('updater', updateResult.message);
746
760
  if (updateResult.action === 'updated') {
747
- if (!(0, updater_1.requestGatewayRestart)()) {
748
- log.warn('updater', 'Restart not available — new version loads on next manual restart');
761
+ const restarted = (0, updater_1.requestGatewayRestart)();
762
+ if (restarted) {
763
+ cleanupRuntime({ markStopped: true, flushRedactor: true });
764
+ return;
749
765
  }
766
+ log.warn('updater', 'Restart not available — new version loads on next manual restart');
750
767
  }
751
768
  }
752
769
  }
@@ -909,7 +926,9 @@ exports.default = {
909
926
  if (wasRunning)
910
927
  log.info('shield', 'Service stopped');
911
928
  },
912
- });
929
+ };
930
+ serviceStartFn = serviceDefinition.start.bind(serviceDefinition);
931
+ api.registerService(serviceDefinition);
913
932
  api.registerGatewayMethod('shield.status', ({ respond }) => {
914
933
  const creds = (0, config_1.loadCredentials)();
915
934
  const activated = state.activated || hasValidCredentials(creds);
@@ -1114,13 +1133,108 @@ exports.default = {
1114
1133
  .description('Trigger an immediate poll cycle')
1115
1134
  .action(async () => {
1116
1135
  if (!pollFn) {
1117
- console.error('Bridge not started');
1136
+ console.error('Shield is not running');
1118
1137
  return;
1119
1138
  }
1120
1139
  console.log('Flushing...');
1121
1140
  await pollFn();
1122
1141
  console.log('Done');
1123
1142
  });
1143
+ shield.command('restart')
1144
+ .description('Restart Shield monitoring (use when status shows Disconnected)')
1145
+ .option('--verbose', 'Show detailed diagnostic output during restart')
1146
+ .action(async (opts) => {
1147
+ const verbose = opts.verbose ?? false;
1148
+ const step = (msg) => { if (verbose)
1149
+ console.log(` [restart] ${msg}`); };
1150
+ console.log('🛡️ Restarting Shield monitoring...');
1151
+ console.log('');
1152
+ step('Reading current state...');
1153
+ const wasRunning = state.running;
1154
+ const wasActivated = state.activated;
1155
+ const failures = state.consecutiveFailures ?? 0;
1156
+ const lastPollAgo = state.lastPollAt
1157
+ ? `${Math.round((Date.now() - state.lastPollAt) / 1000)}s ago`
1158
+ : 'never';
1159
+ console.log(` Was running: ${wasRunning ? 'yes' : 'no'}`);
1160
+ console.log(` Activated: ${wasActivated ? 'yes' : 'no'}`);
1161
+ console.log(` Last poll: ${lastPollAgo}`);
1162
+ console.log(` Failures: ${failures}`);
1163
+ console.log('');
1164
+ step('Checking credentials...');
1165
+ const creds = (0, config_1.loadCredentials)();
1166
+ if (!hasValidCredentials(creds)) {
1167
+ console.error('❌ Cannot restart — Shield is not activated.');
1168
+ console.error(' Run: openclaw shield activate <YOUR_KEY>');
1169
+ return;
1170
+ }
1171
+ step(`Credentials OK (instance: ${(creds.instanceId || '').slice(0, 8)}…)`);
1172
+ step('Checking update state...');
1173
+ const updateState = (0, updater_1.loadUpdateState)();
1174
+ if (updateState.pendingRestart) {
1175
+ console.warn(' ⚠️ update-state.json has pendingRestart:true — clearing before restart');
1176
+ updateState.pendingRestart = false;
1177
+ updateState.restartAttempts = 0;
1178
+ const { saveUpdateState } = require('./src/updater');
1179
+ saveUpdateState(updateState);
1180
+ step('pendingRestart cleared');
1181
+ }
1182
+ if (updateState.updateAvailable && updateState.latestVersion === updateState.currentVersion) {
1183
+ console.warn(' ⚠️ Stale updateAvailable flag detected — clearing');
1184
+ updateState.updateAvailable = false;
1185
+ const { saveUpdateState } = require('./src/updater');
1186
+ saveUpdateState(updateState);
1187
+ step('Stale updateAvailable cleared');
1188
+ }
1189
+ step('Resetting session state...');
1190
+ state.running = false;
1191
+ state.consecutiveFailures = 0;
1192
+ state.lastPollAt = 0;
1193
+ state.captureSeenSinceLastSync = false;
1194
+ markStateDirty();
1195
+ persistState();
1196
+ step('State reset');
1197
+ step('Stopping current runtime...');
1198
+ if (typeof teardownPreviousRuntime === 'function') {
1199
+ await teardownPreviousRuntime();
1200
+ step('Runtime torn down');
1201
+ }
1202
+ else {
1203
+ step('No active runtime to tear down');
1204
+ }
1205
+ console.log(' Restarting...');
1206
+ step('Calling service start...');
1207
+ try {
1208
+ if (!serviceStartFn) {
1209
+ console.error('❌ Service not initialized — try: openclaw gateway restart');
1210
+ return;
1211
+ }
1212
+ await serviceStartFn();
1213
+ await new Promise(r => setTimeout(r, 2000));
1214
+ const nowRunning = state.running;
1215
+ const nowLastPoll = state.lastPollAt
1216
+ ? `${Math.round((Date.now() - state.lastPollAt) / 1000)}s ago`
1217
+ : 'not yet';
1218
+ console.log('');
1219
+ if (nowRunning) {
1220
+ console.log(`✅ Shield restarted successfully.`);
1221
+ console.log(` Running: yes | Last poll: ${nowLastPoll} | Failures: ${state.consecutiveFailures}`);
1222
+ }
1223
+ else {
1224
+ console.log('⚠️ Shield started but running flag is not set yet.');
1225
+ console.log(` Last poll: ${nowLastPoll} | Failures: ${state.consecutiveFailures}`);
1226
+ console.log(' Run `openclaw shield status` in a few seconds to confirm.');
1227
+ }
1228
+ }
1229
+ catch (err) {
1230
+ console.error('');
1231
+ console.error(`❌ Restart failed: ${err instanceof Error ? err.message : String(err)}`);
1232
+ if (verbose) {
1233
+ console.error(err instanceof Error ? err.stack : '');
1234
+ }
1235
+ console.error(' If this persists, run: openclaw gateway restart');
1236
+ }
1237
+ });
1124
1238
  shield.command('activate')
1125
1239
  .description('Activate Shield with an Installation Key')
1126
1240
  .argument('<key>', 'Installation Key from the Shield portal')
@@ -1 +0,0 @@
1
- export {};
package/dist/src/index.js CHANGED
@@ -1,348 +1,13 @@
1
1
  "use strict";
2
- var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
- if (k2 === undefined) k2 = k;
4
- var desc = Object.getOwnPropertyDescriptor(m, k);
5
- if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
- desc = { enumerable: true, get: function() { return m[k]; } };
7
- }
8
- Object.defineProperty(o, k2, desc);
9
- }) : (function(o, m, k, k2) {
10
- if (k2 === undefined) k2 = k;
11
- o[k2] = m[k];
12
- }));
13
- var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
- Object.defineProperty(o, "default", { enumerable: true, value: v });
15
- }) : function(o, v) {
16
- o["default"] = v;
17
- });
18
- var __importStar = (this && this.__importStar) || (function () {
19
- var ownKeys = function(o) {
20
- ownKeys = Object.getOwnPropertyNames || function (o) {
21
- var ar = [];
22
- for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
23
- return ar;
24
- };
25
- return ownKeys(o);
26
- };
27
- return function (mod) {
28
- if (mod && mod.__esModule) return mod;
29
- var result = {};
30
- if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
31
- __setModuleDefault(result, mod);
32
- return result;
33
- };
34
- })();
35
- Object.defineProperty(exports, "__esModule", { value: true });
36
- const os = __importStar(require("os"));
37
- const path = __importStar(require("path"));
38
- const updater_1 = require("./updater");
39
- const config_1 = require("./config");
40
- const log = __importStar(require("./log"));
41
- const fetcher_1 = require("./fetcher");
42
- const transformer_1 = require("./transformer");
43
- const sender_1 = require("./sender");
44
- const redactor_1 = require("./redactor");
45
- const validator_1 = require("./validator");
46
- const fs_1 = require("fs");
47
- const version_1 = require("./version");
48
- const event_store_1 = require("./event-store");
49
- const case_monitor_1 = require("./case-monitor");
50
- const exclusions_1 = require("./exclusions");
51
- const SHIELD_DATA_DIR = path.join(os.homedir(), '.openclaw', 'shield', 'data');
52
- function extractEventDetails(event) {
53
- const path = event.target?.file_path || event.target?.file?.path || event.target?.process?.command_line;
54
- const url = event.network?.http?.url || event.target?.url || event.url;
55
- if (path)
56
- return path.length > 100 ? path.slice(0, 97) + '…' : path;
57
- if (url)
58
- return url.length > 100 ? url.slice(0, 97) + '…' : url;
59
- return undefined;
60
- }
61
- let running = true;
62
- let lastTelemetryAt = 0;
63
- let consecutiveFailures = 0;
64
- let registrationOk = false;
65
- const TELEMETRY_INTERVAL_MS = 5 * 60 * 1000;
66
- const MAX_BACKOFF_MS = 5 * 60 * 1000;
67
- const MAX_REGISTRATION_FAILURES = 10;
68
- function getBackoffInterval(baseMs) {
69
- if (consecutiveFailures === 0)
70
- return baseMs;
71
- const backoff = baseMs * Math.pow(2, Math.min(consecutiveFailures, 10));
72
- return Math.min(backoff, MAX_BACKOFF_MS);
73
- }
74
- async function poll() {
75
- const config = (0, config_1.loadConfig)();
76
- if (config.redactionEnabled) {
77
- (0, redactor_1.init)();
78
- }
79
- (0, exclusions_1.initExclusions)(SHIELD_DATA_DIR);
80
- (0, case_monitor_1.initCaseMonitor)(SHIELD_DATA_DIR);
81
- if (config.localEventBuffer) {
82
- (0, event_store_1.initEventStore)(SHIELD_DATA_DIR, { maxEvents: config.localEventLimit });
83
- }
84
- log.info('bridge', `Starting — dryRun=${config.dryRun} poll=${config.pollIntervalMs}ms maxEvents=${config.maxEvents || 'unlimited'} redaction=${config.redactionEnabled} logLevel=${process.env.LOG_LEVEL || 'info'}`);
85
- const _rawEnvStartup = process.env.SHIELD_AUTO_UPDATE;
86
- const autoUpdateMode = _rawEnvStartup === 'false' ? false : _rawEnvStartup ?? true;
87
- log.info('updater', `Startup update check (autoUpdate=${autoUpdateMode}, current=${version_1.VERSION})`);
88
- const _bootState = (0, updater_1.loadUpdateState)();
89
- const startupUpdate = (0, updater_1.performAutoUpdate)(autoUpdateMode, _bootState.pendingRestart ? undefined : 0);
90
- if (startupUpdate.action === "none") {
91
- log.info("updater", `Up to date (${version_1.VERSION})`);
92
- }
93
- else if (startupUpdate.action === "updated") {
94
- log.info("updater", startupUpdate.message);
95
- const restarted = (0, updater_1.requestGatewayRestart)();
96
- if (restarted)
97
- return;
98
- log.warn("updater", "Gateway restart failed — continuing with current version. Restart manually to load the update.");
99
- }
100
- else if (startupUpdate.action === "notify") {
101
- log.info("updater", startupUpdate.message);
102
- }
103
- else if (startupUpdate.action === "rollback" || startupUpdate.action === "error") {
104
- log.warn("updater", startupUpdate.message);
105
- }
106
- while (running) {
107
- try {
108
- let entries = await (0, fetcher_1.fetchNewEntries)(config);
109
- const now = Date.now();
110
- if (now - lastTelemetryAt >= TELEMETRY_INTERVAL_MS) {
111
- const hostSnapshot = config.collectHostMetrics ? (0, transformer_1.generateHostTelemetry)() : null;
112
- const hostMeta = hostSnapshot?.event?.tool_metadata;
113
- const agentId = process.env.OPENCLAW_AGENT_ID || 'main';
114
- const instancePayload = {
115
- machine: {
116
- hostname: config.hostname,
117
- os: process.platform,
118
- arch: process.arch,
119
- node_version: process.version,
120
- public_ip: (0, transformer_1.getCachedPublicIp)() ?? '',
121
- },
122
- software: {
123
- plugin_version: (0, version_1.getVersion)(),
124
- openclaw_version: (0, transformer_1.resolveOpenClawVersion)(),
125
- agent_label: (0, transformer_1.resolveAgentLabel)(agentId),
126
- instance_name: (0, transformer_1.resolveAgentLabel)(agentId) || config.hostname,
127
- ...(hostMeta && {
128
- gateway_bind: hostMeta['openclaw.gateway_bind'],
129
- webhook_configured: hostMeta['openclaw.webhook_configured'],
130
- browser_auth_required: hostMeta['openclaw.browser_auth_required'],
131
- }),
132
- },
133
- };
134
- if (!(0, transformer_1.getCachedPublicIp)()) {
135
- log.warn('bridge', 'Public IP not yet resolved — sending empty public_ip so platform can enrich server-side');
136
- }
137
- const result = await (0, sender_1.reportInstance)(instancePayload, config.credentials);
138
- log.info('bridge', `Instance report → Platform: success=${result.ok}`);
139
- if (result.ok) {
140
- registrationOk = true;
141
- lastTelemetryAt = now;
142
- if (result.score) {
143
- log.info('bridge', `Protection score: ${result.score.badge} ${result.score.total}/100 (${result.score.grade})`);
144
- if (result.score.recommendations?.length) {
145
- for (const rec of result.score.recommendations) {
146
- log.warn('bridge', `⚠ ${rec}`);
147
- }
148
- }
149
- }
150
- }
151
- else if (!registrationOk) {
152
- consecutiveFailures++;
153
- if (consecutiveFailures >= MAX_REGISTRATION_FAILURES) {
154
- log.error('bridge', `reportInstance failed ${consecutiveFailures} consecutive times — instance not recognized. Re-run setup wizard. Exiting.`);
155
- process.exit(1);
156
- }
157
- log.warn('bridge', `reportInstance failed (attempt ${consecutiveFailures}/${MAX_REGISTRATION_FAILURES}) — skipping events this cycle (platform may still be syncing)`);
158
- continue;
159
- }
160
- }
161
- const _rawEnvLoop = process.env.SHIELD_AUTO_UPDATE;
162
- const autoUpdateMode = _rawEnvLoop === 'false' ? false : _rawEnvLoop ?? true;
163
- const updateResult = (0, updater_1.performAutoUpdate)(autoUpdateMode);
164
- if (updateResult.action !== "none") {
165
- if (updateResult.action === "notify") {
166
- log.info("updater", updateResult.message);
167
- }
168
- else if (updateResult.action === "updated") {
169
- log.info("updater", updateResult.message);
170
- const restarted = (0, updater_1.requestGatewayRestart)();
171
- if (restarted) {
172
- running = false;
173
- break;
174
- }
175
- log.warn("updater", "Gateway restart failed — continuing with current version. Restart manually to load the update.");
176
- }
177
- else if (updateResult.action === "rollback") {
178
- log.warn("updater", updateResult.message);
179
- }
180
- else if (updateResult.action === "error") {
181
- log.error("updater", updateResult.message);
182
- }
183
- }
184
- if (entries.length > 0) {
185
- let envelopes = (0, transformer_1.transformEntries)(entries, config.sessionDirs);
186
- const { valid: validEnvelopes, quarantined } = (0, validator_1.validate)(envelopes.map(e => e.event));
187
- if (quarantined > 0) {
188
- log.warn('bridge', `${quarantined} events quarantined (see ~/.openclaw/shield/data/quarantine.jsonl)`);
189
- }
190
- envelopes = envelopes.filter(e => validEnvelopes.includes(e.event));
191
- if (config.maxEvents > 0 && envelopes.length > config.maxEvents) {
192
- log.info('bridge', `Limiting to ${config.maxEvents} of ${envelopes.length} events (maxEvents cap)`);
193
- envelopes = envelopes.slice(0, config.maxEvents);
194
- }
195
- if (config.redactionEnabled) {
196
- envelopes = envelopes.map(env => {
197
- const redacted = (0, redactor_1.redactEvent)(env);
198
- log.debug('redactor', `tool=${env.event.tool_name} session=${env.event.session_id}`, { event: redacted.event });
199
- return redacted;
200
- });
201
- }
202
- log.info('bridge', `${entries.length} entries → ${envelopes.length} envelopes`);
203
- const results = await (0, sender_1.sendEvents)(envelopes, config);
204
- for (const r of results) {
205
- if (r.success) {
206
- log.info('bridge', `Result: success=${r.success} status=${r.statusCode} events=${r.eventCount}`);
207
- }
208
- else {
209
- log.error('bridge', `Result: FAILED status=${r.statusCode} events=${r.eventCount} body=${r.body?.slice(0, 200)}`);
210
- }
211
- }
212
- if (config.localEventBuffer && results.some(r => r.success)) {
213
- const summaries = envelopes.map(env => ({
214
- ts: env.event.timestamp,
215
- type: env.event.event_type || 'UNKNOWN',
216
- tool: env.event.tool_name || 'unknown',
217
- summary: env.event.tool_metadata?.["openclaw.display_summary"] || env.event.target?.command_line || env.event.tool_name || 'event',
218
- session: env.event.session_id || '?',
219
- model: env.event.model || '?',
220
- redacted: !!env.source?.plugin?.redaction_applied,
221
- details: extractEventDetails(env.event),
222
- trigger_type: env.event.tool_metadata?.['trigger.type'] ?? undefined,
223
- }));
224
- (0, event_store_1.appendEvents)(summaries);
225
- }
226
- if (results.some(r => r.needsRegistration)) {
227
- consecutiveFailures++;
228
- registrationOk = false;
229
- lastTelemetryAt = 0;
230
- if (consecutiveFailures >= MAX_REGISTRATION_FAILURES) {
231
- log.error('bridge', `Instance not recognized by platform after ${consecutiveFailures} attempts. Re-run the setup wizard. Exiting.`);
232
- process.exit(1);
233
- }
234
- log.warn('bridge', `Instance not registered on platform (attempt ${consecutiveFailures}/${MAX_REGISTRATION_FAILURES}) — will re-register on next cycle`);
235
- continue;
236
- }
237
- const pendingResult = results.find(r => r.pendingNamespace);
238
- if (pendingResult) {
239
- const retryAfterMs = Math.min(pendingResult.retryAfterMs ?? 300_000, MAX_BACKOFF_MS);
240
- log.warn('bridge', `Namespace pending — holding cursors, retrying in ${Math.round(retryAfterMs / 1000)}s`);
241
- await new Promise(r => setTimeout(r, retryAfterMs));
242
- continue;
243
- }
244
- const allSuccess = results.every(r => r.success);
245
- if (allSuccess) {
246
- (0, fetcher_1.commitCursors)(config, entries);
247
- if (config.redactionEnabled)
248
- (0, redactor_1.flush)();
249
- log.info('bridge', 'Cursors committed');
250
- consecutiveFailures = 0;
251
- }
252
- else {
253
- consecutiveFailures++;
254
- log.warn('bridge', 'Some batches failed — cursors NOT updated (will retry next poll)');
255
- }
256
- if (config.maxEvents > 0) {
257
- log.info('bridge', 'Test mode complete, exiting');
258
- running = false;
259
- break;
260
- }
261
- }
262
- else {
263
- (0, fetcher_1.commitCursors)(config, []);
264
- consecutiveFailures = 0;
265
- }
266
- }
267
- catch (err) {
268
- consecutiveFailures++;
269
- log.error('bridge', 'Poll error', err);
270
- }
271
- try {
272
- const creds = (0, config_1.loadCredentials)();
273
- const platformConfig = {
274
- apiUrl: config.platformApiUrl ?? null,
275
- instanceId: creds?.instanceId || '',
276
- hmacSecret: creds?.hmacSecret || '',
277
- };
278
- await (0, case_monitor_1.checkForNewCases)(platformConfig);
279
- }
280
- catch (err) {
281
- log.debug('case-monitor', `Check error: ${err instanceof Error ? err.message : String(err)}`);
282
- }
283
- if (!running)
284
- break;
285
- const interval = getBackoffInterval(config.pollIntervalMs);
286
- if (interval !== config.pollIntervalMs) {
287
- log.warn('bridge', `Backing off: next poll in ${Math.round(interval / 1000)}s (${consecutiveFailures} consecutive failures)`);
288
- }
289
- await new Promise(r => setTimeout(r, interval));
290
- }
291
- if (config.redactionEnabled)
292
- (0, redactor_1.flush)();
293
- console.log('[Shield Bridge] Stopped');
294
- }
295
- function checkConfiguration() {
296
- if (!(0, fs_1.existsSync)(config_1.SHIELD_CONFIG_PATH)) {
297
- const { SHIELD_API_URL, SHIELD_INSTANCE_ID, SHIELD_HMAC_SECRET, SHIELD_FINGERPRINT, SHIELD_SECRET } = process.env;
298
- if (SHIELD_API_URL && (SHIELD_INSTANCE_ID || SHIELD_FINGERPRINT) && (SHIELD_HMAC_SECRET || SHIELD_SECRET))
299
- return true;
300
- console.log('🛡️ OpenClaw Shield — First Time Setup Required');
301
- console.log('=================================================\n');
302
- console.log(`No config found at ${config_1.SHIELD_CONFIG_PATH}`);
303
- console.log('Run the setup wizard to connect to your Shield API:\n');
304
- console.log(' npx shield-setup\n');
305
- return false;
306
- }
307
- (0, config_1.injectConfigEnv)();
308
- const creds = (0, config_1.loadCredentials)();
309
- if (!creds.apiUrl || !creds.instanceId || !creds.hmacSecret) {
310
- console.error('❌ Shield config is incomplete. Missing apiUrl, instanceId, or hmacSecret.');
311
- console.error(` Config file: ${config_1.SHIELD_CONFIG_PATH}`);
312
- console.error(' Run: npx shield-setup');
313
- return false;
314
- }
315
- return true;
316
- }
317
- if (!checkConfiguration()) {
318
- process.exit(1);
319
- }
320
- const config = (0, config_1.loadConfig)();
321
- process.on('SIGINT', () => {
322
- console.log('\n[Shield Bridge] Shutting down...');
323
- if (config.redactionEnabled)
324
- (0, redactor_1.flush)();
325
- running = false;
326
- });
327
- process.on('SIGTERM', () => {
328
- console.log('\n[Shield Bridge] Received SIGTERM, shutting down gracefully...');
329
- if (config.redactionEnabled)
330
- (0, redactor_1.flush)();
331
- running = false;
332
- });
333
- poll().catch((err) => {
334
- const msg = err instanceof Error ? err.message : String(err);
335
- console.error('[Shield Bridge] Fatal error:', msg);
336
- if (msg.includes('ENOTFOUND') || msg.includes('ECONNREFUSED')) {
337
- console.error('\n💡 This looks like a network connectivity issue.');
338
- console.error(' - Check your Shield API URL in the configuration');
339
- console.error(' - Verify your internet connection');
340
- console.error(' - Run: npm run setup -- --verify');
341
- }
342
- else if (msg.includes('401') || msg.includes('403')) {
343
- console.error('\n💡 This looks like an authentication issue.');
344
- console.error(' - Verify your Shield API credentials');
345
- console.error(' - Run: npm run setup');
346
- }
347
- process.exit(1);
348
- });
2
+ console.error('');
3
+ console.error('OpenClaw Shield is an OpenClaw plugin — it runs inside the OpenClaw gateway,');
4
+ console.error('not as a standalone process.');
5
+ console.error('');
6
+ console.error('To get started:');
7
+ console.error(' 1. Install OpenClaw https://openclaw.ai');
8
+ console.error(' 2. Install the plugin openclaw plugins install @upx-us/shield');
9
+ console.error(' 3. Activate openclaw shield activate <YOUR_KEY>');
10
+ console.error('');
11
+ console.error('Get your key at: https://www.upx.com/en/lp/openclaw-shield-upx');
12
+ console.error('');
13
+ process.exit(1);
@@ -150,7 +150,7 @@ function checkForUpdate(overrideInterval) {
150
150
  const now = Date.now();
151
151
  const interval = overrideInterval ?? CHECK_INTERVAL_MS;
152
152
  if (now - state.lastCheckAt < Math.max(interval, MIN_CHECK_INTERVAL_MS)) {
153
- if (state.updateAvailable && state.latestVersion) {
153
+ if (state.updateAvailable && state.latestVersion && isNewerVersion(version_1.VERSION, state.latestVersion)) {
154
154
  const classification = classifyUpdate(version_1.VERSION, state.latestVersion);
155
155
  return {
156
156
  updateAvailable: true,
@@ -515,6 +515,13 @@ function performAutoUpdate(mode, checkIntervalMs) {
515
515
  const check = checkForUpdate(checkIntervalMs);
516
516
  if (!check || !check.updateAvailable)
517
517
  return noOp;
518
+ if (!isNewerVersion(version_1.VERSION, check.latestVersion)) {
519
+ log.info('updater', `Already on ${version_1.VERSION} — clearing stale updateAvailable flag`);
520
+ const st = loadUpdateState();
521
+ st.updateAvailable = false;
522
+ saveUpdateState(st);
523
+ return noOp;
524
+ }
518
525
  if (mode === 'notify-only') {
519
526
  const kind = check.isPatch ? 'patch' : check.isMinor ? 'minor' : 'major';
520
527
  return {
@@ -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.8",
5
+ "version": "0.7.10",
6
6
  "skills": [
7
7
  "./skills"
8
8
  ],
package/package.json CHANGED
@@ -1,11 +1,10 @@
1
1
  {
2
2
  "name": "@upx-us/shield",
3
- "version": "0.7.8",
3
+ "version": "0.7.10",
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",
7
7
  "bin": {
8
- "shield-bridge": "dist/src/index.js",
9
8
  "shield-setup": "dist/src/setup.js"
10
9
  },
11
10
  "files": [