@goplus/agentguard 1.1.14 → 1.1.18

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.
Files changed (64) hide show
  1. package/README.md +8 -2
  2. package/dist/adapters/openclaw-plugin.d.ts.map +1 -1
  3. package/dist/adapters/openclaw-plugin.js +6 -18
  4. package/dist/adapters/openclaw-plugin.js.map +1 -1
  5. package/dist/cli.js +377 -37
  6. package/dist/cli.js.map +1 -1
  7. package/dist/cloud/client.d.ts +16 -2
  8. package/dist/cloud/client.d.ts.map +1 -1
  9. package/dist/cloud/client.js +56 -9
  10. package/dist/cloud/client.js.map +1 -1
  11. package/dist/cloud/openclaw-notify.d.ts +18 -0
  12. package/dist/cloud/openclaw-notify.d.ts.map +1 -0
  13. package/dist/cloud/openclaw-notify.js +69 -0
  14. package/dist/cloud/openclaw-notify.js.map +1 -0
  15. package/dist/config.d.ts +11 -0
  16. package/dist/config.d.ts.map +1 -1
  17. package/dist/config.js +41 -0
  18. package/dist/config.js.map +1 -1
  19. package/dist/feed/cron.d.ts +1 -0
  20. package/dist/feed/cron.d.ts.map +1 -1
  21. package/dist/feed/cron.js +309 -14
  22. package/dist/feed/cron.js.map +1 -1
  23. package/dist/feed/selfcheck.d.ts.map +1 -1
  24. package/dist/feed/selfcheck.js +470 -23
  25. package/dist/feed/selfcheck.js.map +1 -1
  26. package/dist/feed/types.d.ts +7 -8
  27. package/dist/feed/types.d.ts.map +1 -1
  28. package/dist/index.d.ts +1 -1
  29. package/dist/index.d.ts.map +1 -1
  30. package/dist/index.js +3 -1
  31. package/dist/index.js.map +1 -1
  32. package/dist/installers.js +25 -1
  33. package/dist/installers.js.map +1 -1
  34. package/dist/runtime/evaluator.d.ts.map +1 -1
  35. package/dist/runtime/evaluator.js +45 -3
  36. package/dist/runtime/evaluator.js.map +1 -1
  37. package/dist/runtime/protect.d.ts.map +1 -1
  38. package/dist/runtime/protect.js +9 -2
  39. package/dist/runtime/protect.js.map +1 -1
  40. package/dist/tests/cli-checkup.test.js +1 -1
  41. package/dist/tests/cli-checkup.test.js.map +1 -1
  42. package/dist/tests/cli-connect.test.d.ts +2 -0
  43. package/dist/tests/cli-connect.test.d.ts.map +1 -0
  44. package/dist/tests/cli-connect.test.js +209 -0
  45. package/dist/tests/cli-connect.test.js.map +1 -0
  46. package/dist/tests/cli-init.test.js +28 -0
  47. package/dist/tests/cli-init.test.js.map +1 -1
  48. package/dist/tests/cli-policy.test.js +72 -0
  49. package/dist/tests/cli-policy.test.js.map +1 -1
  50. package/dist/tests/cli-subscribe.test.js +224 -2
  51. package/dist/tests/cli-subscribe.test.js.map +1 -1
  52. package/dist/tests/feed-cloud.test.js +45 -1
  53. package/dist/tests/feed-cloud.test.js.map +1 -1
  54. package/dist/tests/feed-cron.test.js +270 -10
  55. package/dist/tests/feed-cron.test.js.map +1 -1
  56. package/dist/tests/feed-selfcheck.test.js +61 -13
  57. package/dist/tests/feed-selfcheck.test.js.map +1 -1
  58. package/dist/tests/integration.test.js +41 -9
  59. package/dist/tests/integration.test.js.map +1 -1
  60. package/dist/tests/runtime-cloud.test.js +65 -0
  61. package/dist/tests/runtime-cloud.test.js.map +1 -1
  62. package/openclaw.plugin.json +4 -0
  63. package/package.json +1 -1
  64. package/skills/agentguard/SKILL.md +1 -1
package/dist/cli.js CHANGED
@@ -15,6 +15,8 @@ const installers_js_1 = require("./installers.js");
15
15
  const version_js_1 = require("./version.js");
16
16
  const selfcheck_js_1 = require("./feed/selfcheck.js");
17
17
  const state_js_1 = require("./feed/state.js");
18
+ const client_js_2 = require("./cloud/client.js");
19
+ const openclaw_notify_js_1 = require("./cloud/openclaw-notify.js");
18
20
  const cron_js_1 = require("./feed/cron.js");
19
21
  const SUPPORTED_AGENT_INSTALLERS = ['claude-code', 'codex', 'openclaw', 'hermes', 'qclaw'];
20
22
  const AUTO_AGENT_DETECTION = [
@@ -38,8 +40,10 @@ async function main() {
38
40
  .option('--agent <agent>', 'Install hook/template for claude-code, codex, openclaw, hermes, or qclaw')
39
41
  .option('--cloud <url>', 'AgentGuard Cloud URL to store in local config')
40
42
  .option('--force', 'Overwrite existing hook/template files')
43
+ .option('--no-force', 'Do not overwrite existing hook/template files')
41
44
  .action((options) => {
42
- const config = (0, config_js_1.ensureConfig)();
45
+ const forceTemplates = options.force !== false;
46
+ let config = (0, config_js_1.ensureConfig)();
43
47
  if (options.level) {
44
48
  if (!['strict', 'balanced', 'permissive'].includes(options.level)) {
45
49
  throw new Error('Invalid level. Use strict, balanced, or permissive.');
@@ -57,7 +61,7 @@ async function main() {
57
61
  if (options.agent) {
58
62
  const normalizedAgent = String(options.agent).trim().toLowerCase();
59
63
  if (normalizedAgent === 'auto') {
60
- const results = initAutoAgents(config, Boolean(options.force));
64
+ const results = initAutoAgents(config, forceTemplates);
61
65
  if (results.detected.length === 0) {
62
66
  console.log('No supported agent directories found. Looked for .claude, .openclaw, .hermes, .qclaw, and .codex.');
63
67
  }
@@ -81,7 +85,7 @@ async function main() {
81
85
  config.agentHost = agent;
82
86
  config.agentHosts = appendAgentHost(config.agentHosts, agent);
83
87
  (0, config_js_1.saveConfig)(config);
84
- const result = (0, installers_js_1.installAgentTemplates)(agent, { force: options.force });
88
+ const result = (0, installers_js_1.installAgentTemplates)(agent, { force: forceTemplates });
85
89
  console.log(`Installed ${result.agent} template:`);
86
90
  for (const file of result.files)
87
91
  console.log(`- ${file}`);
@@ -97,7 +101,52 @@ async function main() {
97
101
  .action(async (options) => {
98
102
  const apiKey = options.key || options.apiKey || process.env.AGENTGUARD_API_KEY;
99
103
  if (!apiKey) {
100
- throw new Error('Missing API key. Pass --key, --api-key, or set AGENTGUARD_API_KEY.');
104
+ const config = (0, config_js_1.ensureConfig)();
105
+ if (!isOpenClawAgentConfigured(config)) {
106
+ throw new Error('Missing API key. Pass --key, --api-key, set AGENTGUARD_API_KEY, or run `agentguard init --agent openclaw` before using Agent JWT registration.');
107
+ }
108
+ const cloudUrl = (0, config_js_1.normalizeCloudUrl)(options.cloud || options.url || config.cloudUrl || 'https://agentguard.gopluslabs.io');
109
+ if (config.agentId && config.agentJwt) {
110
+ const existingConfig = { ...config, cloudUrl };
111
+ const client = new client_js_1.AgentGuardCloudClient(existingConfig);
112
+ try {
113
+ const policy = await client.fetchEffectivePolicy();
114
+ const savedConfig = (0, config_js_1.connectAgentJwt)({
115
+ agentId: config.agentId,
116
+ agentJwt: config.agentJwt,
117
+ agentRegisterUrl: config.agentRegisterUrl,
118
+ cloudUrl,
119
+ });
120
+ (0, policy_js_1.saveCachedPolicy)(savedConfig.policyCachePath, policy);
121
+ console.log(`Connected to AgentGuard Cloud (${savedConfig.cloudUrl}).`);
122
+ console.log(`Agent JWT is active for local agent ${savedConfig.agentId}.`);
123
+ console.log(`Cached policy ${policy.policyVersion} at ${savedConfig.policyCachePath}.`);
124
+ return;
125
+ }
126
+ catch (err) {
127
+ if (!(err instanceof client_js_2.CloudRequestError && err.status === 401)) {
128
+ console.log(`Agent JWT is configured for ${cloudUrl}.`);
129
+ console.log(`Could not verify it right now; local protection still works offline. ${err instanceof Error ? err.message : ''}`.trim());
130
+ return;
131
+ }
132
+ }
133
+ }
134
+ const registration = await registerAgentCredential({
135
+ cloudUrl,
136
+ reason: 'connect',
137
+ notifyOpenClaw: true,
138
+ resetExistingJwt: true,
139
+ });
140
+ console.log(`Registered local AgentGuard agent (${registration.config.agentId}).`);
141
+ console.log('Open this link to bind AgentGuard Cloud to your email:');
142
+ console.log(registration.registerUrl);
143
+ if (registration.openClawNotification.notified) {
144
+ console.log('Sent the activation link to the last OpenClaw channel.');
145
+ }
146
+ else if (registration.openClawNotification.reason) {
147
+ console.log(`OpenClaw notification skipped: ${registration.openClawNotification.reason}`);
148
+ }
149
+ return;
101
150
  }
102
151
  const config = (0, config_js_1.connectCloud)({ apiKey, cloudUrl: options.cloud || options.url });
103
152
  const client = new client_js_1.AgentGuardCloudClient(config);
@@ -118,7 +167,7 @@ async function main() {
118
167
  .action(() => {
119
168
  const config = (0, config_js_1.disconnectCloud)();
120
169
  console.log('Disconnected from AgentGuard Cloud.');
121
- console.log('Removed local Cloud API key, connection timestamp, pending event spool, and cached Cloud policy.');
170
+ console.log('Removed local Cloud API key, Agent JWT, connection timestamp, pending event spool, and cached Cloud policy.');
122
171
  console.log(`Local protection remains active using the built-in policy. Audit log: ${config.auditPath}`);
123
172
  });
124
173
  program
@@ -131,6 +180,9 @@ async function main() {
131
180
  console.log(`Protection level: ${config.level}`);
132
181
  console.log(`Cloud URL: ${config.cloudUrl || 'not configured'}`);
133
182
  console.log(`API key: ${(0, config_js_1.maskApiKey)(config.apiKey)}`);
183
+ console.log(`Agent ID: ${config.agentId || 'not configured'}`);
184
+ console.log(`Agent JWT: ${config.agentJwt ? 'configured' : 'not configured'}`);
185
+ console.log(`Agent activation URL: ${config.agentRegisterUrl || 'not configured'}`);
134
186
  console.log(`Agent host: ${config.agentHost || 'not configured'}`);
135
187
  console.log(`Agent hosts: ${config.agentHosts?.join(', ') || 'not configured'}`);
136
188
  console.log(`Policy cache: ${config.policyCachePath}`);
@@ -145,10 +197,10 @@ async function main() {
145
197
  .description('Pull the latest effective runtime policy from AgentGuard Cloud into the local cache')
146
198
  .option('--json', 'Print JSON output')
147
199
  .action(async (options) => {
148
- const config = (0, config_js_1.ensureConfig)();
149
- const client = new client_js_1.AgentGuardCloudClient(config);
200
+ let config = (0, config_js_1.ensureConfig)();
201
+ let client = new client_js_1.AgentGuardCloudClient(config);
150
202
  if (!client.connected) {
151
- const message = 'AgentGuard Cloud is not connected. Run `agentguard connect --key <key>` first.';
203
+ const message = 'AgentGuard Cloud is not connected. Run `agentguard connect` first.';
152
204
  if (options.json) {
153
205
  console.log(JSON.stringify({ success: false, error: message }, null, 2));
154
206
  }
@@ -159,7 +211,16 @@ async function main() {
159
211
  return;
160
212
  }
161
213
  try {
162
- const pulledPolicy = await client.fetchEffectivePolicy();
214
+ const result = await runCloudRequestWithAgentJwtReauth({
215
+ config,
216
+ client,
217
+ reason: 'reauth',
218
+ notifyOpenClaw: true,
219
+ operation: (activeClient) => activeClient.fetchEffectivePolicy(),
220
+ });
221
+ config = result.config;
222
+ client = result.client;
223
+ const pulledPolicy = result.value;
163
224
  (0, policy_js_1.saveCachedPolicy)(config.policyCachePath, pulledPolicy);
164
225
  if (options.json) {
165
226
  console.log(JSON.stringify({
@@ -229,8 +290,8 @@ async function main() {
229
290
  console.log(`✓ Home: ${paths.home}`);
230
291
  console.log(`✓ Config: ${paths.configPath}`);
231
292
  console.log(`✓ Node: ${process.version}`);
232
- if (config.apiKey) {
233
- const client = new client_js_1.AgentGuardCloudClient(config);
293
+ const client = new client_js_1.AgentGuardCloudClient(config);
294
+ if (client.connected) {
234
295
  try {
235
296
  const status = await client.status();
236
297
  const label = status.status || (status.ok ? 'ok' : status.service || 'reachable');
@@ -302,29 +363,143 @@ async function main() {
302
363
  .option('--cron-run', 'Internal: run from the OpenClaw cron prompt without trying to install cron again')
303
364
  .option('--cron-notify-run', 'Internal: run from an OpenClaw cron prompt and print only the notification body or NO_REPLY')
304
365
  .action(async (options) => {
305
- const config = (0, config_js_1.ensureConfig)();
306
- const client = new client_js_1.AgentGuardCloudClient(config);
366
+ let config = (0, config_js_1.ensureConfig)();
367
+ let client = new client_js_1.AgentGuardCloudClient(config);
368
+ const cronAgentHost = resolveCronAgentHost(config);
307
369
  const state = (0, state_js_1.loadFeedState)();
308
370
  const since = options.since;
309
371
  const quiet = Boolean(options.quiet);
310
372
  const cronNotifyRun = Boolean(options.cronNotifyRun);
311
373
  const cronTarget = validateCronTarget(options.cronTarget);
374
+ const cronRunSendsToOpenClaw = Boolean(options.cronRun) && cronAgentHost === 'openclaw';
312
375
  const cronExpression = options.cron && !options.cronRun
313
376
  ? (0, cron_js_1.validateCronExpression)(options.cron)
314
377
  : undefined;
378
+ let registration = null;
379
+ if (!client.connected) {
380
+ if (!isOpenClawAgentConfigured(config)) {
381
+ const message = 'AgentGuard Cloud is not connected. Run `agentguard connect --key <key>` first, or run `agentguard init --agent openclaw` to use Agent JWT registration.';
382
+ if (cronNotifyRun) {
383
+ console.log('NO_REPLY');
384
+ }
385
+ else if (options.json) {
386
+ console.log(JSON.stringify({ success: false, error: message }, null, 2));
387
+ }
388
+ else {
389
+ console.error(message);
390
+ }
391
+ process.exitCode = 1;
392
+ return;
393
+ }
394
+ try {
395
+ registration = await registerAgentCredential({
396
+ cloudUrl: config.cloudUrl,
397
+ reason: 'subscribe',
398
+ notifyOpenClaw: resolveCronAgentHost(config) === 'openclaw',
399
+ });
400
+ config = registration.config;
401
+ client = registration.client;
402
+ }
403
+ catch (err) {
404
+ if (cronNotifyRun) {
405
+ console.log('NO_REPLY');
406
+ process.exitCode = 0;
407
+ return;
408
+ }
409
+ console.error(`! Could not register AgentGuard agent: ${err.message}`);
410
+ process.exitCode = 1;
411
+ return;
412
+ }
413
+ }
414
+ try {
415
+ await client.subscribeFeed();
416
+ }
417
+ catch (err) {
418
+ if (err instanceof client_js_2.CloudRequestError && err.status === 401) {
419
+ if (!isOpenClawAgentConfigured(config)) {
420
+ console.error('! AgentGuard Cloud credential was rejected. Run `agentguard connect --key <key>` again.');
421
+ process.exitCode = 1;
422
+ return;
423
+ }
424
+ try {
425
+ registration = await registerAgentCredential({
426
+ cloudUrl: config.cloudUrl,
427
+ reason: 'subscribe',
428
+ notifyOpenClaw: resolveCronAgentHost(config) === 'openclaw',
429
+ resetExistingJwt: true,
430
+ });
431
+ config = registration.config;
432
+ client = registration.client;
433
+ await client.subscribeFeed();
434
+ }
435
+ catch (retryErr) {
436
+ if (cronNotifyRun) {
437
+ console.log('NO_REPLY');
438
+ process.exitCode = 0;
439
+ return;
440
+ }
441
+ printAgentActivationRequired(registration, retryErr);
442
+ process.exitCode = 1;
443
+ return;
444
+ }
445
+ }
446
+ else {
447
+ if (cronNotifyRun) {
448
+ console.log('NO_REPLY');
449
+ process.exitCode = 0;
450
+ return;
451
+ }
452
+ console.error(`! Could not subscribe to AgentGuard Cloud feed: ${err.message}`);
453
+ process.exitCode = 1;
454
+ return;
455
+ }
456
+ }
457
+ if (registration && !cronNotifyRun && !quiet && !options.json) {
458
+ printAgentRegistrationNotice(registration);
459
+ }
315
460
  let advisories;
316
461
  try {
317
462
  advisories = await client.pullAdvisories(since);
318
463
  }
319
464
  catch (err) {
320
- if (cronNotifyRun) {
321
- console.log('NO_REPLY');
322
- process.exitCode = 0;
465
+ if (err instanceof client_js_2.CloudRequestError && err.status === 401) {
466
+ if (!isOpenClawAgentConfigured(config)) {
467
+ console.error('! AgentGuard Cloud credential was rejected. Run `agentguard connect --key <key>` again.');
468
+ process.exitCode = 1;
469
+ return;
470
+ }
471
+ try {
472
+ registration = await registerAgentCredential({
473
+ cloudUrl: config.cloudUrl,
474
+ reason: 'subscribe',
475
+ notifyOpenClaw: resolveCronAgentHost(config) === 'openclaw',
476
+ resetExistingJwt: true,
477
+ });
478
+ config = registration.config;
479
+ client = registration.client;
480
+ advisories = await client.pullAdvisories(since);
481
+ }
482
+ catch (retryErr) {
483
+ if (cronNotifyRun) {
484
+ console.log('NO_REPLY');
485
+ process.exitCode = 0;
486
+ return;
487
+ }
488
+ printAgentActivationRequired(registration, retryErr);
489
+ process.exitCode = 1;
490
+ return;
491
+ }
492
+ }
493
+ else {
494
+ if (cronNotifyRun) {
495
+ console.log('NO_REPLY');
496
+ process.exitCode = 0;
497
+ return;
498
+ }
499
+ console.error(`! Could not reach AgentGuard Cloud: ${err.message}`);
500
+ process.exitCode = 1;
323
501
  return;
324
502
  }
325
- console.error(`! Could not reach AgentGuard Cloud: ${err.message}`);
326
- process.exitCode = 1;
327
- return;
328
503
  }
329
504
  if (advisories === null) {
330
505
  // 404 — older Cloud build without the feed endpoint. Not an error.
@@ -370,10 +545,20 @@ async function main() {
370
545
  // match, we must NOT mark the advisory seen, otherwise a
371
546
  // transient network blip silently buries a real hit.
372
547
  try {
373
- await client.reportSelfCheck(advisory.id, result.matchedArtifacts, {
374
- elapsedMs: result.elapsedMs,
375
- warnings: result.warnings,
548
+ const reportResult = await runCloudRequestWithAgentJwtReauth({
549
+ config,
550
+ client,
551
+ reason: 'reauth',
552
+ notifyOpenClaw: resolveCronAgentHost(config) === 'openclaw',
553
+ operation: (activeClient) => activeClient.reportSelfCheck(advisory.id, result.matchedArtifacts, {
554
+ elapsedMs: result.elapsedMs,
555
+ warnings: result.warnings,
556
+ }),
376
557
  });
558
+ config = reportResult.config;
559
+ client = reportResult.client;
560
+ if (reportResult.registration)
561
+ registration = reportResult.registration;
377
562
  }
378
563
  catch (err) {
379
564
  console.error(`! Failed to report self-check for ${advisory.id}: ${err.message}`);
@@ -398,13 +583,13 @@ async function main() {
398
583
  newSeenIds.push(advisory.id);
399
584
  }
400
585
  }
401
- if (newSeenIds.length > 0 || foundIds.length > 0) {
402
- (0, state_js_1.saveFeedState)((0, state_js_1.prependFeedStateEntry)(state, {
586
+ const pendingStateEntry = newSeenIds.length > 0 || foundIds.length > 0
587
+ ? {
403
588
  pulledAt,
404
589
  newSeenIds,
405
590
  foundIds,
406
- }));
407
- }
591
+ }
592
+ : null;
408
593
  const totalMatches = results.reduce((acc, r) => acc + r.matchedArtifacts.length, 0);
409
594
  const summary = buildSubscribeSummary({
410
595
  supported: true,
@@ -434,6 +619,33 @@ async function main() {
434
619
  throw err;
435
620
  }
436
621
  }
622
+ if (cronRunSendsToOpenClaw) {
623
+ if (summary.shouldNotify && summary.hardFailures === 0) {
624
+ const body = summary.notification?.body;
625
+ if (!body) {
626
+ console.error('! OpenClaw cron notification was requested, but no notification body was generated.');
627
+ process.exitCode = 1;
628
+ return;
629
+ }
630
+ const notification = await (0, openclaw_notify_js_1.notifyOpenClawMessage)(body, resolveOpenClawGatewayOptionsFromEnv(), {
631
+ idempotencyKeyPrefix: 'agentguard-subscribe',
632
+ });
633
+ if (!notification.notified) {
634
+ console.error(`! Could not send OpenClaw cron notification: ${notification.reason ?? 'Unknown error'}`);
635
+ process.exitCode = 1;
636
+ return;
637
+ }
638
+ }
639
+ if (pendingStateEntry) {
640
+ (0, state_js_1.saveFeedState)((0, state_js_1.prependFeedStateEntry)(state, pendingStateEntry));
641
+ }
642
+ console.log('NO_REPLY');
643
+ process.exitCode = hardFailures > 0 ? 1 : 0;
644
+ return;
645
+ }
646
+ if (pendingStateEntry) {
647
+ (0, state_js_1.saveFeedState)((0, state_js_1.prependFeedStateEntry)(state, pendingStateEntry));
648
+ }
437
649
  if (cronNotifyRun) {
438
650
  console.log(summary.shouldNotify && summary.hardFailures === 0 ? summary.notification?.body ?? 'NO_REPLY' : 'NO_REPLY');
439
651
  process.exitCode = 0;
@@ -449,10 +661,7 @@ async function main() {
449
661
  }
450
662
  console.log(`Pulled ${advisories.length} advisory record(s); ${fresh.length} new.`);
451
663
  if (!quiet && fresh.length > 0) {
452
- console.log('New threat-feed advisories found. Review and handle them manually:');
453
- for (const advisory of fresh) {
454
- console.log(` - ${advisory.id} [${advisory.severity}] ${advisory.summary}`);
455
- }
664
+ console.log(summary.notification?.body ?? formatNewAdvisoryNotification(fresh));
456
665
  }
457
666
  else if (quiet && fresh.length > 0) {
458
667
  console.log(`Self-check found ${totalMatches} match(es) across the new advisories.`);
@@ -467,8 +676,11 @@ async function main() {
467
676
  }
468
677
  if (summary.cron.result) {
469
678
  const label = summary.cron.result.backend ?? 'cron';
470
- const action = summary.cron.result.created ? `Installed ${label} cron job` : `${label} cron job already exists`;
679
+ const action = summary.cron.result.created ? `Installed ${label} cron job` : `${label} cron job already exists and was left unchanged`;
471
680
  console.log(`${action} "${summary.cron.result.name}" (${summary.cron.result.schedule}, ${summary.cron.result.timezone}).`);
681
+ if (!summary.cron.result.created) {
682
+ console.log('Existing cron jobs are not reconfigured unless --force is passed; rerun with --force to apply the requested quiet/manual mode and schedule.');
683
+ }
472
684
  if (summary.cron.result.backend === 'system') {
473
685
  console.log(`System cron output: ${(0, node_path_1.join)((0, config_js_1.getAgentGuardPaths)().home, 'feed-cron.log')}`);
474
686
  }
@@ -495,7 +707,7 @@ async function main() {
495
707
  .option('--against-advisory <id>', 'Restrict the check to a single advisory id (fetches it from Cloud if needed)')
496
708
  .option('--json', 'Emit machine-readable result')
497
709
  .action(async (options) => {
498
- const config = (0, config_js_1.ensureConfig)();
710
+ let config = (0, config_js_1.ensureConfig)();
499
711
  const advisoryId = options.againstAdvisory;
500
712
  if (!advisoryId) {
501
713
  const report = await runLocalHealthCheckup(config);
@@ -514,9 +726,9 @@ async function main() {
514
726
  process.exitCode = 0;
515
727
  return;
516
728
  }
517
- const client = new client_js_1.AgentGuardCloudClient(config);
729
+ let client = new client_js_1.AgentGuardCloudClient(config);
518
730
  if (!client.connected) {
519
- const message = 'AgentGuard Cloud is not connected. Run `agentguard connect --key <key>` first.';
731
+ const message = 'AgentGuard Cloud is not connected. Run `agentguard connect` first.';
520
732
  if (options.json) {
521
733
  console.log(JSON.stringify({ success: false, error: message }, null, 2));
522
734
  }
@@ -528,10 +740,25 @@ async function main() {
528
740
  }
529
741
  let advisory = null;
530
742
  try {
531
- advisory = await client.getAdvisory(advisoryId);
743
+ const result = await runCloudRequestWithAgentJwtReauth({
744
+ config,
745
+ client,
746
+ reason: 'reauth',
747
+ notifyOpenClaw: true,
748
+ operation: (activeClient) => activeClient.getAdvisory(advisoryId),
749
+ });
750
+ config = result.config;
751
+ client = result.client;
752
+ advisory = result.value;
532
753
  }
533
754
  catch (err) {
534
- console.error(`! Could not reach AgentGuard Cloud: ${err.message}`);
755
+ const message = `Could not reach AgentGuard Cloud: ${err.message}`;
756
+ if (options.json) {
757
+ console.log(JSON.stringify({ success: false, error: message }, null, 2));
758
+ }
759
+ else {
760
+ console.error(`! ${message}`);
761
+ }
535
762
  process.exitCode = 1;
536
763
  return;
537
764
  }
@@ -898,6 +1125,102 @@ function runCommandText(command, args) {
898
1125
  }
899
1126
  });
900
1127
  }
1128
+ async function runCloudRequestWithAgentJwtReauth(options) {
1129
+ try {
1130
+ return {
1131
+ value: await options.operation(options.client),
1132
+ config: options.config,
1133
+ client: options.client,
1134
+ registration: null,
1135
+ };
1136
+ }
1137
+ catch (err) {
1138
+ if (!(err instanceof client_js_2.CloudRequestError && err.status === 401) ||
1139
+ !options.config.agentJwt ||
1140
+ !isOpenClawAgentConfigured(options.config)) {
1141
+ throw err;
1142
+ }
1143
+ const registration = await registerAgentCredential({
1144
+ cloudUrl: options.config.cloudUrl,
1145
+ reason: options.reason,
1146
+ notifyOpenClaw: options.notifyOpenClaw,
1147
+ resetExistingJwt: true,
1148
+ });
1149
+ return {
1150
+ value: await options.operation(registration.client),
1151
+ config: registration.config,
1152
+ client: registration.client,
1153
+ registration,
1154
+ };
1155
+ }
1156
+ }
1157
+ async function registerAgentCredential(options) {
1158
+ if (options.resetExistingJwt) {
1159
+ (0, config_js_1.clearAgentJwt)();
1160
+ }
1161
+ const baseConfig = (0, config_js_1.ensureConfig)();
1162
+ const cloudUrl = (0, config_js_1.normalizeCloudUrl)(options.cloudUrl || baseConfig.cloudUrl || 'https://agentguard.gopluslabs.io');
1163
+ const client = new client_js_1.AgentGuardCloudClient({ ...baseConfig, cloudUrl });
1164
+ const registration = await client.registerAgent({
1165
+ metadata: {
1166
+ agentHost: baseConfig.agentHost,
1167
+ agentHosts: baseConfig.agentHosts,
1168
+ agentVersion: version_js_1.packageVersion,
1169
+ platform: process.platform,
1170
+ arch: process.arch,
1171
+ reason: options.reason,
1172
+ },
1173
+ });
1174
+ const config = (0, config_js_1.connectAgentJwt)({
1175
+ agentId: registration.agentId,
1176
+ agentJwt: registration.jwt,
1177
+ agentRegisterUrl: registration.registerUrl,
1178
+ cloudUrl,
1179
+ });
1180
+ const nextClient = new client_js_1.AgentGuardCloudClient(config);
1181
+ const openClawNotification = options.notifyOpenClaw
1182
+ ? await (0, openclaw_notify_js_1.notifyOpenClawRegistrationLink)(registration.registerUrl, resolveOpenClawGatewayOptionsFromEnv())
1183
+ : { notified: false, reason: 'OpenClaw notification was not requested.' };
1184
+ return {
1185
+ config,
1186
+ client: nextClient,
1187
+ registerUrl: registration.registerUrl,
1188
+ openClawNotification,
1189
+ };
1190
+ }
1191
+ function printAgentRegistrationNotice(registration) {
1192
+ console.log('AgentGuard Cloud activation is ready:');
1193
+ console.log(registration.registerUrl);
1194
+ if (registration.openClawNotification.notified) {
1195
+ console.log('Sent the activation link to the last OpenClaw channel.');
1196
+ }
1197
+ }
1198
+ function printAgentActivationRequired(registration, err) {
1199
+ const message = err instanceof Error ? err.message : String(err);
1200
+ console.error(`! AgentGuard Cloud authorization is not active yet. ${message}`);
1201
+ const registerUrl = registration?.registerUrl || (0, config_js_1.ensureConfig)().agentRegisterUrl;
1202
+ if (registerUrl) {
1203
+ console.error('Open this link to bind this agent to your email, then rerun the command:');
1204
+ console.error(registerUrl);
1205
+ }
1206
+ }
1207
+ function isOpenClawAgentConfigured(config) {
1208
+ return config.agentHost === 'openclaw' || config.agentHosts?.includes('openclaw') === true;
1209
+ }
1210
+ function resolveOpenClawGatewayOptionsFromEnv() {
1211
+ const url = process.env.AGENTGUARD_OPENCLAW_GATEWAY_URL?.trim();
1212
+ const host = process.env.AGENTGUARD_OPENCLAW_GATEWAY_HOST?.trim();
1213
+ const portRaw = process.env.AGENTGUARD_OPENCLAW_GATEWAY_PORT?.trim();
1214
+ const timeoutRaw = process.env.AGENTGUARD_OPENCLAW_GATEWAY_TIMEOUT_MS?.trim();
1215
+ const port = portRaw ? Number(portRaw) : undefined;
1216
+ const timeoutMs = timeoutRaw ? Number(timeoutRaw) : undefined;
1217
+ return {
1218
+ ...(url ? { url } : {}),
1219
+ ...(host ? { host } : {}),
1220
+ ...(Number.isFinite(port) ? { port } : {}),
1221
+ ...(Number.isFinite(timeoutMs) ? { timeoutMs } : {}),
1222
+ };
1223
+ }
901
1224
  function calculateCompositeScore(dimensions) {
902
1225
  const web3Score = dimensions.web3_safety.score;
903
1226
  if (web3Score === null || dimensions.web3_safety.na) {
@@ -1031,13 +1354,30 @@ function formatNewAdvisoryNotification(advisories) {
1031
1354
  const lines = ['AgentGuard found new threat-feed advisories that need manual review:'];
1032
1355
  for (const advisory of advisories.slice(0, 10)) {
1033
1356
  lines.push(`- ${advisory.id} [${advisory.severity}] ${advisory.summary}`);
1357
+ const remediation = formatAdvisoryRemediation(advisory);
1358
+ if (remediation) {
1359
+ lines.push(' Remediation guidance:');
1360
+ for (const line of remediation.split('\n')) {
1361
+ lines.push(` ${line}`);
1362
+ }
1363
+ }
1034
1364
  }
1035
1365
  if (advisories.length > 10) {
1036
1366
  lines.push(`- ... ${advisories.length - 10} more`);
1037
1367
  }
1038
- lines.push('Run `agentguard subscribe --quiet` to execute the local self-check and report matches automatically.');
1039
1368
  return lines.join('\n');
1040
1369
  }
1370
+ function formatAdvisoryRemediation(advisory) {
1371
+ const remediation = advisory.selfCheck?.remediationMd?.trim();
1372
+ if (!remediation)
1373
+ return null;
1374
+ const compact = remediation
1375
+ .replace(/\r\n/g, '\n')
1376
+ .replace(/\n{3,}/g, '\n\n')
1377
+ .trim();
1378
+ const maxLen = 1200;
1379
+ return compact.length > maxLen ? `${compact.slice(0, maxLen).trimEnd()}\n...` : compact;
1380
+ }
1041
1381
  function formatThreatFeedNotification(results) {
1042
1382
  const lines = ['AgentGuard threat-feed self-check found local matches:'];
1043
1383
  for (const result of results) {