@goplus/agentguard 1.1.14 → 1.1.20
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/README.md +8 -2
- package/dist/adapters/engine.d.ts.map +1 -1
- package/dist/adapters/engine.js +10 -0
- package/dist/adapters/engine.js.map +1 -1
- package/dist/adapters/openclaw-plugin.d.ts.map +1 -1
- package/dist/adapters/openclaw-plugin.js +6 -18
- package/dist/adapters/openclaw-plugin.js.map +1 -1
- package/dist/cli.js +525 -42
- package/dist/cli.js.map +1 -1
- package/dist/cloud/client.d.ts +16 -2
- package/dist/cloud/client.d.ts.map +1 -1
- package/dist/cloud/client.js +56 -9
- package/dist/cloud/client.js.map +1 -1
- package/dist/cloud/openclaw-notify.d.ts +18 -0
- package/dist/cloud/openclaw-notify.d.ts.map +1 -0
- package/dist/cloud/openclaw-notify.js +69 -0
- package/dist/cloud/openclaw-notify.js.map +1 -0
- package/dist/config.d.ts +14 -0
- package/dist/config.d.ts.map +1 -1
- package/dist/config.js +51 -0
- package/dist/config.js.map +1 -1
- package/dist/feed/cron.d.ts +18 -0
- package/dist/feed/cron.d.ts.map +1 -1
- package/dist/feed/cron.js +499 -25
- package/dist/feed/cron.js.map +1 -1
- package/dist/feed/selfcheck.d.ts.map +1 -1
- package/dist/feed/selfcheck.js +470 -23
- package/dist/feed/selfcheck.js.map +1 -1
- package/dist/feed/types.d.ts +7 -8
- package/dist/feed/types.d.ts.map +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +3 -1
- package/dist/index.js.map +1 -1
- package/dist/installers.d.ts.map +1 -1
- package/dist/installers.js +101 -2
- package/dist/installers.js.map +1 -1
- package/dist/runtime/evaluator.d.ts.map +1 -1
- package/dist/runtime/evaluator.js +45 -3
- package/dist/runtime/evaluator.js.map +1 -1
- package/dist/runtime/protect.d.ts.map +1 -1
- package/dist/runtime/protect.js +15 -2
- package/dist/runtime/protect.js.map +1 -1
- package/dist/runtime/self-command.d.ts +2 -0
- package/dist/runtime/self-command.d.ts.map +1 -0
- package/dist/runtime/self-command.js +73 -0
- package/dist/runtime/self-command.js.map +1 -0
- package/dist/tests/cli-checkup.test.js +1 -1
- package/dist/tests/cli-checkup.test.js.map +1 -1
- package/dist/tests/cli-connect.test.d.ts +2 -0
- package/dist/tests/cli-connect.test.d.ts.map +1 -0
- package/dist/tests/cli-connect.test.js +326 -0
- package/dist/tests/cli-connect.test.js.map +1 -0
- package/dist/tests/cli-init.test.js +141 -0
- package/dist/tests/cli-init.test.js.map +1 -1
- package/dist/tests/cli-policy.test.js +72 -0
- package/dist/tests/cli-policy.test.js.map +1 -1
- package/dist/tests/cli-subscribe.test.js +295 -2
- package/dist/tests/cli-subscribe.test.js.map +1 -1
- package/dist/tests/feed-cloud.test.js +45 -1
- package/dist/tests/feed-cloud.test.js.map +1 -1
- package/dist/tests/feed-cron.test.js +506 -10
- package/dist/tests/feed-cron.test.js.map +1 -1
- package/dist/tests/feed-selfcheck.test.js +61 -13
- package/dist/tests/feed-selfcheck.test.js.map +1 -1
- package/dist/tests/installer.test.js +69 -0
- package/dist/tests/installer.test.js.map +1 -1
- package/dist/tests/integration.test.js +41 -9
- package/dist/tests/integration.test.js.map +1 -1
- package/dist/tests/runtime-cloud.test.js +148 -0
- package/dist/tests/runtime-cloud.test.js.map +1 -1
- package/openclaw.plugin.json +4 -0
- package/package.json +1 -1
- package/skills/agentguard/SKILL.md +11 -5
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
|
|
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,
|
|
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:
|
|
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,60 @@ 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
|
-
|
|
104
|
+
let 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
|
+
config = withDetectedOpenClawAgentHost(config);
|
|
109
|
+
const cloudUrl = (0, config_js_1.normalizeCloudUrl)(options.cloud || options.url || config.cloudUrl || 'https://agentguard.gopluslabs.io');
|
|
110
|
+
if (config.agentId && config.agentJwt) {
|
|
111
|
+
const existingConfig = { ...config, cloudUrl };
|
|
112
|
+
const client = new client_js_1.AgentGuardCloudClient(existingConfig);
|
|
113
|
+
try {
|
|
114
|
+
const policy = await client.fetchEffectivePolicy();
|
|
115
|
+
const savedConfig = (0, config_js_1.connectAgentJwt)({
|
|
116
|
+
agentId: config.agentId,
|
|
117
|
+
agentJwt: config.agentJwt,
|
|
118
|
+
agentRegisterUrl: config.agentRegisterUrl,
|
|
119
|
+
cloudUrl,
|
|
120
|
+
});
|
|
121
|
+
const activeConfig = (0, config_js_1.clearAgentRegisterUrl)(savedConfig);
|
|
122
|
+
(0, policy_js_1.saveCachedPolicy)(activeConfig.policyCachePath, policy);
|
|
123
|
+
console.log(`Connected to AgentGuard Cloud (${activeConfig.cloudUrl}).`);
|
|
124
|
+
console.log(`Agent JWT is active for local agent ${activeConfig.agentId}.`);
|
|
125
|
+
console.log(`Cached policy ${policy.policyVersion} at ${activeConfig.policyCachePath}.`);
|
|
126
|
+
return;
|
|
127
|
+
}
|
|
128
|
+
catch (err) {
|
|
129
|
+
if (!(err instanceof client_js_2.CloudRequestError && err.status === 401)) {
|
|
130
|
+
console.log(`Agent JWT is configured for ${cloudUrl}.`);
|
|
131
|
+
console.log(`Could not verify it right now; local protection still works offline. ${err instanceof Error ? err.message : ''}`.trim());
|
|
132
|
+
return;
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
let registration;
|
|
137
|
+
try {
|
|
138
|
+
registration = await registerAgentCredential({
|
|
139
|
+
cloudUrl,
|
|
140
|
+
reason: 'connect',
|
|
141
|
+
notifyOpenClaw: true,
|
|
142
|
+
resetExistingJwt: true,
|
|
143
|
+
});
|
|
144
|
+
}
|
|
145
|
+
catch (err) {
|
|
146
|
+
throw new Error(`Could not register AgentGuard agent: ${err instanceof Error ? err.message : String(err)}`);
|
|
147
|
+
}
|
|
148
|
+
console.log(`Registered local AgentGuard agent (${registration.config.agentId}).`);
|
|
149
|
+
console.log('Open this link to bind this agent to your account:');
|
|
150
|
+
console.log(registration.registerUrl);
|
|
151
|
+
if (registration.openClawNotification.notified) {
|
|
152
|
+
console.log('Sent the activation link to the last OpenClaw channel.');
|
|
153
|
+
}
|
|
154
|
+
else if (registration.openClawNotification.reason) {
|
|
155
|
+
console.log(`OpenClaw notification skipped: ${registration.openClawNotification.reason}`);
|
|
156
|
+
}
|
|
157
|
+
return;
|
|
101
158
|
}
|
|
102
159
|
const config = (0, config_js_1.connectCloud)({ apiKey, cloudUrl: options.cloud || options.url });
|
|
103
160
|
const client = new client_js_1.AgentGuardCloudClient(config);
|
|
@@ -115,22 +172,30 @@ async function main() {
|
|
|
115
172
|
program
|
|
116
173
|
.command('disconnect')
|
|
117
174
|
.description('Disconnect local AgentGuard from AgentGuard Cloud')
|
|
118
|
-
.action(() => {
|
|
175
|
+
.action(async () => {
|
|
176
|
+
const currentConfig = (0, config_js_1.ensureConfig)();
|
|
177
|
+
const cronRemoval = await (0, cron_js_1.removeThreatFeedCron)({
|
|
178
|
+
name: currentConfig.threatFeedCronName || 'agentguard-threat-feed',
|
|
179
|
+
backend: 'auto',
|
|
180
|
+
agentHost: resolveCronAgentHost(currentConfig),
|
|
181
|
+
agentGuardHome: (0, config_js_1.getAgentGuardPaths)().home,
|
|
182
|
+
});
|
|
119
183
|
const config = (0, config_js_1.disconnectCloud)();
|
|
120
184
|
console.log('Disconnected from AgentGuard Cloud.');
|
|
121
|
-
console.log('Removed local Cloud API key, connection timestamp, pending event spool, and cached Cloud policy.');
|
|
185
|
+
console.log('Removed local Cloud API key, Agent JWT, connection timestamp, pending event spool, and cached Cloud policy.');
|
|
186
|
+
printCronRemovalSummary(cronRemoval);
|
|
122
187
|
console.log(`Local protection remains active using the built-in policy. Audit log: ${config.auditPath}`);
|
|
123
188
|
});
|
|
124
189
|
program
|
|
125
190
|
.command('status')
|
|
126
191
|
.description('Show local and Cloud connection status')
|
|
127
|
-
.action(() => {
|
|
128
|
-
const config = (0, config_js_1.ensureConfig)();
|
|
192
|
+
.action(async () => {
|
|
193
|
+
const config = await refreshAgentAccountBinding((0, config_js_1.ensureConfig)());
|
|
129
194
|
const paths = (0, config_js_1.getAgentGuardPaths)();
|
|
130
195
|
console.log(`Config: ${paths.configPath}`);
|
|
131
196
|
console.log(`Protection level: ${config.level}`);
|
|
132
197
|
console.log(`Cloud URL: ${config.cloudUrl || 'not configured'}`);
|
|
133
|
-
|
|
198
|
+
printCloudAuthStatus(config);
|
|
134
199
|
console.log(`Agent host: ${config.agentHost || 'not configured'}`);
|
|
135
200
|
console.log(`Agent hosts: ${config.agentHosts?.join(', ') || 'not configured'}`);
|
|
136
201
|
console.log(`Policy cache: ${config.policyCachePath}`);
|
|
@@ -145,10 +210,10 @@ async function main() {
|
|
|
145
210
|
.description('Pull the latest effective runtime policy from AgentGuard Cloud into the local cache')
|
|
146
211
|
.option('--json', 'Print JSON output')
|
|
147
212
|
.action(async (options) => {
|
|
148
|
-
|
|
149
|
-
|
|
213
|
+
let config = (0, config_js_1.ensureConfig)();
|
|
214
|
+
let client = new client_js_1.AgentGuardCloudClient(config);
|
|
150
215
|
if (!client.connected) {
|
|
151
|
-
const message = 'AgentGuard Cloud is not connected. Run `agentguard connect
|
|
216
|
+
const message = 'AgentGuard Cloud is not connected. Run `agentguard connect` first.';
|
|
152
217
|
if (options.json) {
|
|
153
218
|
console.log(JSON.stringify({ success: false, error: message }, null, 2));
|
|
154
219
|
}
|
|
@@ -159,7 +224,16 @@ async function main() {
|
|
|
159
224
|
return;
|
|
160
225
|
}
|
|
161
226
|
try {
|
|
162
|
-
const
|
|
227
|
+
const result = await runCloudRequestWithAgentJwtReauth({
|
|
228
|
+
config,
|
|
229
|
+
client,
|
|
230
|
+
reason: 'reauth',
|
|
231
|
+
notifyOpenClaw: true,
|
|
232
|
+
operation: (activeClient) => activeClient.fetchEffectivePolicy(),
|
|
233
|
+
});
|
|
234
|
+
config = result.config;
|
|
235
|
+
client = result.client;
|
|
236
|
+
const pulledPolicy = result.value;
|
|
163
237
|
(0, policy_js_1.saveCachedPolicy)(config.policyCachePath, pulledPolicy);
|
|
164
238
|
if (options.json) {
|
|
165
239
|
console.log(JSON.stringify({
|
|
@@ -229,8 +303,8 @@ async function main() {
|
|
|
229
303
|
console.log(`✓ Home: ${paths.home}`);
|
|
230
304
|
console.log(`✓ Config: ${paths.configPath}`);
|
|
231
305
|
console.log(`✓ Node: ${process.version}`);
|
|
232
|
-
|
|
233
|
-
|
|
306
|
+
const client = new client_js_1.AgentGuardCloudClient(config);
|
|
307
|
+
if (client.connected) {
|
|
234
308
|
try {
|
|
235
309
|
const status = await client.status();
|
|
236
310
|
const label = status.status || (status.ok ? 'ok' : status.service || 'reachable');
|
|
@@ -302,29 +376,154 @@ async function main() {
|
|
|
302
376
|
.option('--cron-run', 'Internal: run from the OpenClaw cron prompt without trying to install cron again')
|
|
303
377
|
.option('--cron-notify-run', 'Internal: run from an OpenClaw cron prompt and print only the notification body or NO_REPLY')
|
|
304
378
|
.action(async (options) => {
|
|
305
|
-
|
|
306
|
-
|
|
379
|
+
let config = (0, config_js_1.ensureConfig)();
|
|
380
|
+
let client = new client_js_1.AgentGuardCloudClient(config);
|
|
381
|
+
const cronAgentHost = resolveCronAgentHost(config);
|
|
307
382
|
const state = (0, state_js_1.loadFeedState)();
|
|
308
383
|
const since = options.since;
|
|
309
384
|
const quiet = Boolean(options.quiet);
|
|
310
385
|
const cronNotifyRun = Boolean(options.cronNotifyRun);
|
|
386
|
+
const cronInternalRun = Boolean(options.cronRun || options.cronNotifyRun);
|
|
311
387
|
const cronTarget = validateCronTarget(options.cronTarget);
|
|
388
|
+
const cronRunSendsToOpenClaw = Boolean(options.cronRun) && cronAgentHost === 'openclaw';
|
|
312
389
|
const cronExpression = options.cron && !options.cronRun
|
|
313
390
|
? (0, cron_js_1.validateCronExpression)(options.cron)
|
|
314
391
|
: undefined;
|
|
392
|
+
let registration = null;
|
|
393
|
+
if (!client.connected) {
|
|
394
|
+
if (!isOpenClawAgentConfigured(config)) {
|
|
395
|
+
const message = 'AgentGuard Cloud is not connected. Run `agentguard connect --key <key>` first, or run `agentguard init --agent openclaw` to use Agent JWT registration.';
|
|
396
|
+
if (cronNotifyRun) {
|
|
397
|
+
console.log('NO_REPLY');
|
|
398
|
+
}
|
|
399
|
+
else if (options.json) {
|
|
400
|
+
console.log(JSON.stringify({ success: false, error: message }, null, 2));
|
|
401
|
+
}
|
|
402
|
+
else {
|
|
403
|
+
console.error(message);
|
|
404
|
+
}
|
|
405
|
+
process.exitCode = 1;
|
|
406
|
+
return;
|
|
407
|
+
}
|
|
408
|
+
try {
|
|
409
|
+
registration = await registerAgentCredential({
|
|
410
|
+
cloudUrl: config.cloudUrl,
|
|
411
|
+
reason: 'subscribe',
|
|
412
|
+
notifyOpenClaw: resolveCronAgentHost(config) === 'openclaw',
|
|
413
|
+
});
|
|
414
|
+
config = registration.config;
|
|
415
|
+
client = registration.client;
|
|
416
|
+
}
|
|
417
|
+
catch (err) {
|
|
418
|
+
if (cronNotifyRun) {
|
|
419
|
+
console.log('NO_REPLY');
|
|
420
|
+
process.exitCode = 0;
|
|
421
|
+
return;
|
|
422
|
+
}
|
|
423
|
+
console.error(`! Could not register AgentGuard agent: ${err.message}`);
|
|
424
|
+
process.exitCode = 1;
|
|
425
|
+
return;
|
|
426
|
+
}
|
|
427
|
+
}
|
|
428
|
+
try {
|
|
429
|
+
await client.subscribeFeed();
|
|
430
|
+
}
|
|
431
|
+
catch (err) {
|
|
432
|
+
if (err instanceof client_js_2.CloudRequestError && err.status === 401) {
|
|
433
|
+
if (cronInternalRun) {
|
|
434
|
+
await printSubscribeConnectRequired(options, cronRunSendsToOpenClaw);
|
|
435
|
+
process.exitCode = 1;
|
|
436
|
+
return;
|
|
437
|
+
}
|
|
438
|
+
if (!isOpenClawAgentConfigured(config)) {
|
|
439
|
+
console.error('! AgentGuard Cloud credential was rejected. Run `agentguard connect --key <key>` again.');
|
|
440
|
+
process.exitCode = 1;
|
|
441
|
+
return;
|
|
442
|
+
}
|
|
443
|
+
try {
|
|
444
|
+
registration = await registerAgentCredential({
|
|
445
|
+
cloudUrl: config.cloudUrl,
|
|
446
|
+
reason: 'subscribe',
|
|
447
|
+
notifyOpenClaw: resolveCronAgentHost(config) === 'openclaw',
|
|
448
|
+
resetExistingJwt: true,
|
|
449
|
+
});
|
|
450
|
+
config = registration.config;
|
|
451
|
+
client = registration.client;
|
|
452
|
+
await client.subscribeFeed();
|
|
453
|
+
}
|
|
454
|
+
catch (retryErr) {
|
|
455
|
+
if (cronNotifyRun) {
|
|
456
|
+
console.log('NO_REPLY');
|
|
457
|
+
process.exitCode = 0;
|
|
458
|
+
return;
|
|
459
|
+
}
|
|
460
|
+
printAgentActivationRequired(registration, retryErr);
|
|
461
|
+
process.exitCode = 1;
|
|
462
|
+
return;
|
|
463
|
+
}
|
|
464
|
+
}
|
|
465
|
+
else {
|
|
466
|
+
if (cronNotifyRun) {
|
|
467
|
+
console.log('NO_REPLY');
|
|
468
|
+
process.exitCode = 0;
|
|
469
|
+
return;
|
|
470
|
+
}
|
|
471
|
+
console.error(`! Could not subscribe to AgentGuard Cloud feed: ${err.message}`);
|
|
472
|
+
process.exitCode = 1;
|
|
473
|
+
return;
|
|
474
|
+
}
|
|
475
|
+
}
|
|
476
|
+
if (registration && !cronNotifyRun && !quiet && !options.json) {
|
|
477
|
+
printAgentRegistrationNotice(registration);
|
|
478
|
+
}
|
|
315
479
|
let advisories;
|
|
316
480
|
try {
|
|
317
481
|
advisories = await client.pullAdvisories(since);
|
|
318
482
|
}
|
|
319
483
|
catch (err) {
|
|
320
|
-
if (
|
|
321
|
-
|
|
322
|
-
|
|
484
|
+
if (err instanceof client_js_2.CloudRequestError && err.status === 401) {
|
|
485
|
+
if (cronInternalRun) {
|
|
486
|
+
await printSubscribeConnectRequired(options, cronRunSendsToOpenClaw);
|
|
487
|
+
process.exitCode = 1;
|
|
488
|
+
return;
|
|
489
|
+
}
|
|
490
|
+
if (!isOpenClawAgentConfigured(config)) {
|
|
491
|
+
console.error('! AgentGuard Cloud credential was rejected. Run `agentguard connect --key <key>` again.');
|
|
492
|
+
process.exitCode = 1;
|
|
493
|
+
return;
|
|
494
|
+
}
|
|
495
|
+
try {
|
|
496
|
+
registration = await registerAgentCredential({
|
|
497
|
+
cloudUrl: config.cloudUrl,
|
|
498
|
+
reason: 'subscribe',
|
|
499
|
+
notifyOpenClaw: resolveCronAgentHost(config) === 'openclaw',
|
|
500
|
+
resetExistingJwt: true,
|
|
501
|
+
});
|
|
502
|
+
config = registration.config;
|
|
503
|
+
client = registration.client;
|
|
504
|
+
advisories = await client.pullAdvisories(since);
|
|
505
|
+
}
|
|
506
|
+
catch (retryErr) {
|
|
507
|
+
if (cronNotifyRun) {
|
|
508
|
+
console.log('NO_REPLY');
|
|
509
|
+
process.exitCode = 0;
|
|
510
|
+
return;
|
|
511
|
+
}
|
|
512
|
+
printAgentActivationRequired(registration, retryErr);
|
|
513
|
+
process.exitCode = 1;
|
|
514
|
+
return;
|
|
515
|
+
}
|
|
516
|
+
}
|
|
517
|
+
else {
|
|
518
|
+
if (cronNotifyRun) {
|
|
519
|
+
console.log('NO_REPLY');
|
|
520
|
+
process.exitCode = 0;
|
|
521
|
+
return;
|
|
522
|
+
}
|
|
523
|
+
console.error(`! Could not reach AgentGuard Cloud: ${err.message}`);
|
|
524
|
+
process.exitCode = 1;
|
|
323
525
|
return;
|
|
324
526
|
}
|
|
325
|
-
console.error(`! Could not reach AgentGuard Cloud: ${err.message}`);
|
|
326
|
-
process.exitCode = 1;
|
|
327
|
-
return;
|
|
328
527
|
}
|
|
329
528
|
if (advisories === null) {
|
|
330
529
|
// 404 — older Cloud build without the feed endpoint. Not an error.
|
|
@@ -370,12 +569,35 @@ async function main() {
|
|
|
370
569
|
// match, we must NOT mark the advisory seen, otherwise a
|
|
371
570
|
// transient network blip silently buries a real hit.
|
|
372
571
|
try {
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
572
|
+
if (cronInternalRun) {
|
|
573
|
+
await client.reportSelfCheck(advisory.id, result.matchedArtifacts, {
|
|
574
|
+
elapsedMs: result.elapsedMs,
|
|
575
|
+
warnings: result.warnings,
|
|
576
|
+
});
|
|
577
|
+
}
|
|
578
|
+
else {
|
|
579
|
+
const reportResult = await runCloudRequestWithAgentJwtReauth({
|
|
580
|
+
config,
|
|
581
|
+
client,
|
|
582
|
+
reason: 'reauth',
|
|
583
|
+
notifyOpenClaw: resolveCronAgentHost(config) === 'openclaw',
|
|
584
|
+
operation: (activeClient) => activeClient.reportSelfCheck(advisory.id, result.matchedArtifacts, {
|
|
585
|
+
elapsedMs: result.elapsedMs,
|
|
586
|
+
warnings: result.warnings,
|
|
587
|
+
}),
|
|
588
|
+
});
|
|
589
|
+
config = reportResult.config;
|
|
590
|
+
client = reportResult.client;
|
|
591
|
+
if (reportResult.registration)
|
|
592
|
+
registration = reportResult.registration;
|
|
593
|
+
}
|
|
377
594
|
}
|
|
378
595
|
catch (err) {
|
|
596
|
+
if (cronInternalRun && err instanceof client_js_2.CloudRequestError && err.status === 401) {
|
|
597
|
+
await printSubscribeConnectRequired(options, cronRunSendsToOpenClaw);
|
|
598
|
+
process.exitCode = 1;
|
|
599
|
+
return;
|
|
600
|
+
}
|
|
379
601
|
console.error(`! Failed to report self-check for ${advisory.id}: ${err.message}`);
|
|
380
602
|
processed = false;
|
|
381
603
|
hardFailures += 1;
|
|
@@ -398,13 +620,13 @@ async function main() {
|
|
|
398
620
|
newSeenIds.push(advisory.id);
|
|
399
621
|
}
|
|
400
622
|
}
|
|
401
|
-
|
|
402
|
-
|
|
623
|
+
const pendingStateEntry = newSeenIds.length > 0 || foundIds.length > 0
|
|
624
|
+
? {
|
|
403
625
|
pulledAt,
|
|
404
626
|
newSeenIds,
|
|
405
627
|
foundIds,
|
|
406
|
-
}
|
|
407
|
-
|
|
628
|
+
}
|
|
629
|
+
: null;
|
|
408
630
|
const totalMatches = results.reduce((acc, r) => acc + r.matchedArtifacts.length, 0);
|
|
409
631
|
const summary = buildSubscribeSummary({
|
|
410
632
|
supported: true,
|
|
@@ -427,6 +649,11 @@ async function main() {
|
|
|
427
649
|
agentHost: resolveCronAgentHost(config),
|
|
428
650
|
agentGuardHome: (0, config_js_1.getAgentGuardPaths)().home,
|
|
429
651
|
});
|
|
652
|
+
(0, config_js_1.saveConfig)({
|
|
653
|
+
...config,
|
|
654
|
+
threatFeedCronName: summary.cron.result.name,
|
|
655
|
+
threatFeedCronInstalledAt: new Date().toISOString(),
|
|
656
|
+
});
|
|
430
657
|
summary.cron.installed = true;
|
|
431
658
|
}
|
|
432
659
|
catch (err) {
|
|
@@ -434,6 +661,33 @@ async function main() {
|
|
|
434
661
|
throw err;
|
|
435
662
|
}
|
|
436
663
|
}
|
|
664
|
+
if (cronRunSendsToOpenClaw) {
|
|
665
|
+
if (summary.shouldNotify && summary.hardFailures === 0) {
|
|
666
|
+
const body = summary.notification?.body;
|
|
667
|
+
if (!body) {
|
|
668
|
+
console.error('! OpenClaw cron notification was requested, but no notification body was generated.');
|
|
669
|
+
process.exitCode = 1;
|
|
670
|
+
return;
|
|
671
|
+
}
|
|
672
|
+
const notification = await (0, openclaw_notify_js_1.notifyOpenClawMessage)(body, resolveOpenClawGatewayOptionsFromEnv(), {
|
|
673
|
+
idempotencyKeyPrefix: 'agentguard-subscribe',
|
|
674
|
+
});
|
|
675
|
+
if (!notification.notified) {
|
|
676
|
+
console.error(`! Could not send OpenClaw cron notification: ${notification.reason ?? 'Unknown error'}`);
|
|
677
|
+
process.exitCode = 1;
|
|
678
|
+
return;
|
|
679
|
+
}
|
|
680
|
+
}
|
|
681
|
+
if (pendingStateEntry) {
|
|
682
|
+
(0, state_js_1.saveFeedState)((0, state_js_1.prependFeedStateEntry)(state, pendingStateEntry));
|
|
683
|
+
}
|
|
684
|
+
console.log('NO_REPLY');
|
|
685
|
+
process.exitCode = hardFailures > 0 ? 1 : 0;
|
|
686
|
+
return;
|
|
687
|
+
}
|
|
688
|
+
if (pendingStateEntry) {
|
|
689
|
+
(0, state_js_1.saveFeedState)((0, state_js_1.prependFeedStateEntry)(state, pendingStateEntry));
|
|
690
|
+
}
|
|
437
691
|
if (cronNotifyRun) {
|
|
438
692
|
console.log(summary.shouldNotify && summary.hardFailures === 0 ? summary.notification?.body ?? 'NO_REPLY' : 'NO_REPLY');
|
|
439
693
|
process.exitCode = 0;
|
|
@@ -449,10 +703,7 @@ async function main() {
|
|
|
449
703
|
}
|
|
450
704
|
console.log(`Pulled ${advisories.length} advisory record(s); ${fresh.length} new.`);
|
|
451
705
|
if (!quiet && fresh.length > 0) {
|
|
452
|
-
console.log(
|
|
453
|
-
for (const advisory of fresh) {
|
|
454
|
-
console.log(` - ${advisory.id} [${advisory.severity}] ${advisory.summary}`);
|
|
455
|
-
}
|
|
706
|
+
console.log(summary.notification?.body ?? formatNewAdvisoryNotification(fresh));
|
|
456
707
|
}
|
|
457
708
|
else if (quiet && fresh.length > 0) {
|
|
458
709
|
console.log(`Self-check found ${totalMatches} match(es) across the new advisories.`);
|
|
@@ -467,8 +718,11 @@ async function main() {
|
|
|
467
718
|
}
|
|
468
719
|
if (summary.cron.result) {
|
|
469
720
|
const label = summary.cron.result.backend ?? 'cron';
|
|
470
|
-
const action = summary.cron.result.created ? `Installed ${label} cron job` : `${label} cron job already exists`;
|
|
721
|
+
const action = summary.cron.result.created ? `Installed ${label} cron job` : `${label} cron job already exists and was left unchanged`;
|
|
471
722
|
console.log(`${action} "${summary.cron.result.name}" (${summary.cron.result.schedule}, ${summary.cron.result.timezone}).`);
|
|
723
|
+
if (!summary.cron.result.created) {
|
|
724
|
+
console.log('Existing cron jobs are not reconfigured unless --force is passed; rerun with --force to apply the requested quiet/manual mode and schedule.');
|
|
725
|
+
}
|
|
472
726
|
if (summary.cron.result.backend === 'system') {
|
|
473
727
|
console.log(`System cron output: ${(0, node_path_1.join)((0, config_js_1.getAgentGuardPaths)().home, 'feed-cron.log')}`);
|
|
474
728
|
}
|
|
@@ -495,7 +749,7 @@ async function main() {
|
|
|
495
749
|
.option('--against-advisory <id>', 'Restrict the check to a single advisory id (fetches it from Cloud if needed)')
|
|
496
750
|
.option('--json', 'Emit machine-readable result')
|
|
497
751
|
.action(async (options) => {
|
|
498
|
-
|
|
752
|
+
let config = (0, config_js_1.ensureConfig)();
|
|
499
753
|
const advisoryId = options.againstAdvisory;
|
|
500
754
|
if (!advisoryId) {
|
|
501
755
|
const report = await runLocalHealthCheckup(config);
|
|
@@ -514,9 +768,9 @@ async function main() {
|
|
|
514
768
|
process.exitCode = 0;
|
|
515
769
|
return;
|
|
516
770
|
}
|
|
517
|
-
|
|
771
|
+
let client = new client_js_1.AgentGuardCloudClient(config);
|
|
518
772
|
if (!client.connected) {
|
|
519
|
-
const message = 'AgentGuard Cloud is not connected. Run `agentguard connect
|
|
773
|
+
const message = 'AgentGuard Cloud is not connected. Run `agentguard connect` first.';
|
|
520
774
|
if (options.json) {
|
|
521
775
|
console.log(JSON.stringify({ success: false, error: message }, null, 2));
|
|
522
776
|
}
|
|
@@ -528,10 +782,25 @@ async function main() {
|
|
|
528
782
|
}
|
|
529
783
|
let advisory = null;
|
|
530
784
|
try {
|
|
531
|
-
|
|
785
|
+
const result = await runCloudRequestWithAgentJwtReauth({
|
|
786
|
+
config,
|
|
787
|
+
client,
|
|
788
|
+
reason: 'reauth',
|
|
789
|
+
notifyOpenClaw: true,
|
|
790
|
+
operation: (activeClient) => activeClient.getAdvisory(advisoryId),
|
|
791
|
+
});
|
|
792
|
+
config = result.config;
|
|
793
|
+
client = result.client;
|
|
794
|
+
advisory = result.value;
|
|
532
795
|
}
|
|
533
796
|
catch (err) {
|
|
534
|
-
|
|
797
|
+
const message = `Could not reach AgentGuard Cloud: ${err.message}`;
|
|
798
|
+
if (options.json) {
|
|
799
|
+
console.log(JSON.stringify({ success: false, error: message }, null, 2));
|
|
800
|
+
}
|
|
801
|
+
else {
|
|
802
|
+
console.error(`! ${message}`);
|
|
803
|
+
}
|
|
535
804
|
process.exitCode = 1;
|
|
536
805
|
return;
|
|
537
806
|
}
|
|
@@ -617,6 +886,85 @@ function printInitGuidanceIfNeeded(config) {
|
|
|
617
886
|
console.log('Required next step:');
|
|
618
887
|
console.log(` ${REQUIRED_INIT_COMMAND}`);
|
|
619
888
|
}
|
|
889
|
+
function printCloudAuthStatus(config) {
|
|
890
|
+
if (config.agentJwt) {
|
|
891
|
+
console.log('Cloud auth: connected via Agent JWT');
|
|
892
|
+
console.log('API key: not used for this connection');
|
|
893
|
+
console.log(`Agent ID: ${config.agentId || 'configured'}`);
|
|
894
|
+
console.log('Agent JWT: configured');
|
|
895
|
+
if (config.agentRegisterUrl) {
|
|
896
|
+
console.log('Agent account: not bound (activation required)');
|
|
897
|
+
console.log(`Agent activation URL: ${config.agentRegisterUrl}`);
|
|
898
|
+
}
|
|
899
|
+
else {
|
|
900
|
+
console.log('Agent account: bound');
|
|
901
|
+
console.log('Agent activation URL: not required');
|
|
902
|
+
}
|
|
903
|
+
return;
|
|
904
|
+
}
|
|
905
|
+
if (config.apiKey) {
|
|
906
|
+
console.log('Cloud auth: connected via API key');
|
|
907
|
+
console.log(`API key: ${(0, config_js_1.maskApiKey)(config.apiKey)}`);
|
|
908
|
+
console.log('Agent JWT: not used for this connection');
|
|
909
|
+
return;
|
|
910
|
+
}
|
|
911
|
+
console.log('Cloud auth: not connected');
|
|
912
|
+
console.log('API key: not configured');
|
|
913
|
+
console.log('Agent JWT: not configured');
|
|
914
|
+
}
|
|
915
|
+
async function printSubscribeConnectRequired(options, notifyOpenClaw) {
|
|
916
|
+
const message = 'AgentGuard Cloud credential was rejected. Run `agentguard connect` again before the next subscribe cron run.';
|
|
917
|
+
if (notifyOpenClaw) {
|
|
918
|
+
const notification = await (0, openclaw_notify_js_1.notifyOpenClawMessage)(message, resolveOpenClawGatewayOptionsFromEnv(), {
|
|
919
|
+
idempotencyKeyPrefix: 'agentguard-subscribe-auth',
|
|
920
|
+
});
|
|
921
|
+
if (notification.notified) {
|
|
922
|
+
console.log('NO_REPLY');
|
|
923
|
+
return;
|
|
924
|
+
}
|
|
925
|
+
console.error(`! Could not send OpenClaw cron auth notification: ${notification.reason ?? 'Unknown error'}`);
|
|
926
|
+
return;
|
|
927
|
+
}
|
|
928
|
+
if (options.cronNotifyRun) {
|
|
929
|
+
console.log(message);
|
|
930
|
+
}
|
|
931
|
+
else if (options.json) {
|
|
932
|
+
console.log(JSON.stringify({ success: false, error: message }, null, 2));
|
|
933
|
+
}
|
|
934
|
+
else {
|
|
935
|
+
console.error(`! ${message}`);
|
|
936
|
+
}
|
|
937
|
+
}
|
|
938
|
+
async function refreshAgentAccountBinding(config) {
|
|
939
|
+
if (!config.agentJwt || !config.agentRegisterUrl)
|
|
940
|
+
return config;
|
|
941
|
+
const client = new client_js_1.AgentGuardCloudClient(config);
|
|
942
|
+
try {
|
|
943
|
+
const policy = await client.fetchEffectivePolicy();
|
|
944
|
+
const activeConfig = (0, config_js_1.clearAgentRegisterUrl)(config);
|
|
945
|
+
(0, policy_js_1.saveCachedPolicy)(activeConfig.policyCachePath, policy);
|
|
946
|
+
return activeConfig;
|
|
947
|
+
}
|
|
948
|
+
catch {
|
|
949
|
+
return config;
|
|
950
|
+
}
|
|
951
|
+
}
|
|
952
|
+
function printCronRemovalSummary(results) {
|
|
953
|
+
const removed = results.filter((result) => result.removed);
|
|
954
|
+
if (removed.length > 0) {
|
|
955
|
+
console.log(`Removed AgentGuard subscribe cron job "${removed[0].name}" from: ${removed.map((result) => result.backend).join(', ')}.`);
|
|
956
|
+
return;
|
|
957
|
+
}
|
|
958
|
+
const errors = results.filter((result) => result.error);
|
|
959
|
+
if (errors.length > 0) {
|
|
960
|
+
console.log('No AgentGuard subscribe cron job was removed; some cron backends were unavailable.');
|
|
961
|
+
for (const result of errors) {
|
|
962
|
+
console.error(`! ${result.backend}: ${result.error}`);
|
|
963
|
+
}
|
|
964
|
+
return;
|
|
965
|
+
}
|
|
966
|
+
console.log('No AgentGuard subscribe cron job was found.');
|
|
967
|
+
}
|
|
620
968
|
function resolveCronAgentHost(config) {
|
|
621
969
|
return config.agentHost ?? config.agentHosts?.[0];
|
|
622
970
|
}
|
|
@@ -898,6 +1246,124 @@ function runCommandText(command, args) {
|
|
|
898
1246
|
}
|
|
899
1247
|
});
|
|
900
1248
|
}
|
|
1249
|
+
async function runCloudRequestWithAgentJwtReauth(options) {
|
|
1250
|
+
try {
|
|
1251
|
+
return {
|
|
1252
|
+
value: await options.operation(options.client),
|
|
1253
|
+
config: options.config,
|
|
1254
|
+
client: options.client,
|
|
1255
|
+
registration: null,
|
|
1256
|
+
};
|
|
1257
|
+
}
|
|
1258
|
+
catch (err) {
|
|
1259
|
+
if (!(err instanceof client_js_2.CloudRequestError && err.status === 401) ||
|
|
1260
|
+
!options.config.agentJwt ||
|
|
1261
|
+
!isOpenClawAgentConfigured(options.config)) {
|
|
1262
|
+
throw err;
|
|
1263
|
+
}
|
|
1264
|
+
const registration = await registerAgentCredential({
|
|
1265
|
+
cloudUrl: options.config.cloudUrl,
|
|
1266
|
+
reason: options.reason,
|
|
1267
|
+
notifyOpenClaw: options.notifyOpenClaw,
|
|
1268
|
+
resetExistingJwt: true,
|
|
1269
|
+
});
|
|
1270
|
+
return {
|
|
1271
|
+
value: await options.operation(registration.client),
|
|
1272
|
+
config: registration.config,
|
|
1273
|
+
client: registration.client,
|
|
1274
|
+
registration,
|
|
1275
|
+
};
|
|
1276
|
+
}
|
|
1277
|
+
}
|
|
1278
|
+
async function registerAgentCredential(options) {
|
|
1279
|
+
if (options.resetExistingJwt) {
|
|
1280
|
+
(0, config_js_1.clearAgentJwt)();
|
|
1281
|
+
}
|
|
1282
|
+
const baseConfig = (0, config_js_1.ensureConfig)();
|
|
1283
|
+
const cloudUrl = (0, config_js_1.normalizeCloudUrl)(options.cloudUrl || baseConfig.cloudUrl || 'https://agentguard.gopluslabs.io');
|
|
1284
|
+
const client = new client_js_1.AgentGuardCloudClient({ ...baseConfig, cloudUrl });
|
|
1285
|
+
const registration = await client.registerAgent({
|
|
1286
|
+
metadata: {
|
|
1287
|
+
agentHost: baseConfig.agentHost,
|
|
1288
|
+
agentHosts: baseConfig.agentHosts,
|
|
1289
|
+
agentVersion: version_js_1.packageVersion,
|
|
1290
|
+
platform: process.platform,
|
|
1291
|
+
arch: process.arch,
|
|
1292
|
+
reason: options.reason,
|
|
1293
|
+
},
|
|
1294
|
+
});
|
|
1295
|
+
const config = (0, config_js_1.connectAgentJwt)({
|
|
1296
|
+
agentId: registration.agentId,
|
|
1297
|
+
agentJwt: registration.jwt,
|
|
1298
|
+
agentRegisterUrl: registration.registerUrl,
|
|
1299
|
+
cloudUrl,
|
|
1300
|
+
});
|
|
1301
|
+
const nextClient = new client_js_1.AgentGuardCloudClient(config);
|
|
1302
|
+
const openClawNotification = options.notifyOpenClaw
|
|
1303
|
+
? await (0, openclaw_notify_js_1.notifyOpenClawRegistrationLink)(registration.registerUrl, resolveOpenClawGatewayOptionsFromEnv())
|
|
1304
|
+
: { notified: false, reason: 'OpenClaw notification was not requested.' };
|
|
1305
|
+
return {
|
|
1306
|
+
config,
|
|
1307
|
+
client: nextClient,
|
|
1308
|
+
registerUrl: registration.registerUrl,
|
|
1309
|
+
openClawNotification,
|
|
1310
|
+
};
|
|
1311
|
+
}
|
|
1312
|
+
function printAgentRegistrationNotice(registration) {
|
|
1313
|
+
console.log('AgentGuard Cloud activation is ready:');
|
|
1314
|
+
console.log(registration.registerUrl);
|
|
1315
|
+
if (registration.openClawNotification.notified) {
|
|
1316
|
+
console.log('Sent the activation link to the last OpenClaw channel.');
|
|
1317
|
+
}
|
|
1318
|
+
}
|
|
1319
|
+
function printAgentActivationRequired(registration, err) {
|
|
1320
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
1321
|
+
console.error(`! AgentGuard Cloud authorization is not active yet. ${message}`);
|
|
1322
|
+
const registerUrl = registration?.registerUrl || (0, config_js_1.ensureConfig)().agentRegisterUrl;
|
|
1323
|
+
if (registerUrl) {
|
|
1324
|
+
console.error('Open this link to bind this agent to your account, then rerun the command:');
|
|
1325
|
+
console.error(registerUrl);
|
|
1326
|
+
}
|
|
1327
|
+
}
|
|
1328
|
+
function isOpenClawAgentConfigured(config) {
|
|
1329
|
+
return config.agentHost === 'openclaw' || config.agentHosts?.includes('openclaw') === true || detectOpenClawRuntime();
|
|
1330
|
+
}
|
|
1331
|
+
function withDetectedOpenClawAgentHost(config) {
|
|
1332
|
+
if (hasSavedAgentHost(config) || !detectOpenClawRuntime())
|
|
1333
|
+
return config;
|
|
1334
|
+
const next = {
|
|
1335
|
+
...config,
|
|
1336
|
+
agentHost: 'openclaw',
|
|
1337
|
+
agentHosts: appendAgentHost(config.agentHosts, 'openclaw'),
|
|
1338
|
+
};
|
|
1339
|
+
(0, config_js_1.saveConfig)(next);
|
|
1340
|
+
return next;
|
|
1341
|
+
}
|
|
1342
|
+
function detectOpenClawRuntime() {
|
|
1343
|
+
const configPath = process.env.OPENCLAW_CONFIG_PATH?.trim();
|
|
1344
|
+
if (configPath && (0, node_fs_1.existsSync)(configPath))
|
|
1345
|
+
return true;
|
|
1346
|
+
const stateDir = process.env.OPENCLAW_STATE_DIR?.trim();
|
|
1347
|
+
if (stateDir && ((0, node_fs_1.existsSync)(stateDir) || (0, node_fs_1.existsSync)((0, node_path_1.join)(stateDir, 'openclaw.json'))))
|
|
1348
|
+
return true;
|
|
1349
|
+
return (0, node_fs_1.existsSync)((0, node_path_1.join)((0, node_os_1.homedir)(), '.openclaw', 'openclaw.json'));
|
|
1350
|
+
}
|
|
1351
|
+
function resolveOpenClawGatewayOptionsFromEnv() {
|
|
1352
|
+
const url = process.env.AGENTGUARD_OPENCLAW_GATEWAY_URL?.trim();
|
|
1353
|
+
const host = process.env.AGENTGUARD_OPENCLAW_GATEWAY_HOST?.trim();
|
|
1354
|
+
const token = process.env.AGENTGUARD_OPENCLAW_GATEWAY_TOKEN?.trim();
|
|
1355
|
+
const portRaw = process.env.AGENTGUARD_OPENCLAW_GATEWAY_PORT?.trim();
|
|
1356
|
+
const timeoutRaw = process.env.AGENTGUARD_OPENCLAW_GATEWAY_TIMEOUT_MS?.trim();
|
|
1357
|
+
const port = portRaw ? Number(portRaw) : undefined;
|
|
1358
|
+
const timeoutMs = timeoutRaw ? Number(timeoutRaw) : undefined;
|
|
1359
|
+
return {
|
|
1360
|
+
...(url ? { url } : {}),
|
|
1361
|
+
...(host ? { host } : {}),
|
|
1362
|
+
...(token ? { token } : {}),
|
|
1363
|
+
...(Number.isFinite(port) ? { port } : {}),
|
|
1364
|
+
...(Number.isFinite(timeoutMs) ? { timeoutMs } : {}),
|
|
1365
|
+
};
|
|
1366
|
+
}
|
|
901
1367
|
function calculateCompositeScore(dimensions) {
|
|
902
1368
|
const web3Score = dimensions.web3_safety.score;
|
|
903
1369
|
if (web3Score === null || dimensions.web3_safety.na) {
|
|
@@ -1031,13 +1497,30 @@ function formatNewAdvisoryNotification(advisories) {
|
|
|
1031
1497
|
const lines = ['AgentGuard found new threat-feed advisories that need manual review:'];
|
|
1032
1498
|
for (const advisory of advisories.slice(0, 10)) {
|
|
1033
1499
|
lines.push(`- ${advisory.id} [${advisory.severity}] ${advisory.summary}`);
|
|
1500
|
+
const remediation = formatAdvisoryRemediation(advisory);
|
|
1501
|
+
if (remediation) {
|
|
1502
|
+
lines.push(' Remediation guidance:');
|
|
1503
|
+
for (const line of remediation.split('\n')) {
|
|
1504
|
+
lines.push(` ${line}`);
|
|
1505
|
+
}
|
|
1506
|
+
}
|
|
1034
1507
|
}
|
|
1035
1508
|
if (advisories.length > 10) {
|
|
1036
1509
|
lines.push(`- ... ${advisories.length - 10} more`);
|
|
1037
1510
|
}
|
|
1038
|
-
lines.push('Run `agentguard subscribe --quiet` to execute the local self-check and report matches automatically.');
|
|
1039
1511
|
return lines.join('\n');
|
|
1040
1512
|
}
|
|
1513
|
+
function formatAdvisoryRemediation(advisory) {
|
|
1514
|
+
const remediation = advisory.selfCheck?.remediationMd?.trim();
|
|
1515
|
+
if (!remediation)
|
|
1516
|
+
return null;
|
|
1517
|
+
const compact = remediation
|
|
1518
|
+
.replace(/\r\n/g, '\n')
|
|
1519
|
+
.replace(/\n{3,}/g, '\n\n')
|
|
1520
|
+
.trim();
|
|
1521
|
+
const maxLen = 1200;
|
|
1522
|
+
return compact.length > maxLen ? `${compact.slice(0, maxLen).trimEnd()}\n...` : compact;
|
|
1523
|
+
}
|
|
1041
1524
|
function formatThreatFeedNotification(results) {
|
|
1042
1525
|
const lines = ['AgentGuard threat-feed self-check found local matches:'];
|
|
1043
1526
|
for (const result of results) {
|