fullcourtdefense-cli 1.15.5 → 1.15.6
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/dist/commands/daemon.js +72 -15
- package/dist/version.json +1 -1
- package/package.json +1 -1
package/dist/commands/daemon.js
CHANGED
|
@@ -266,6 +266,7 @@ async function runDaemon(args, config) {
|
|
|
266
266
|
await (0, mcpGateway_1.protectAllCommand)({ ...args, dryRun: undefined }, config);
|
|
267
267
|
quietUntil = Date.now() + SELF_WRITE_QUIET_MS;
|
|
268
268
|
log('Re-protection pass complete.');
|
|
269
|
+
await uploadLogTail();
|
|
269
270
|
if (!quiet) {
|
|
270
271
|
(0, notify_1.notifyOs)({
|
|
271
272
|
title: 'FullCourtDefense re-protected this machine',
|
|
@@ -329,6 +330,28 @@ async function runDaemon(args, config) {
|
|
|
329
330
|
}
|
|
330
331
|
return targets.length;
|
|
331
332
|
};
|
|
333
|
+
// Ship the REAL daemon log tail to the control plane so the dashboard's
|
|
334
|
+
// machine page can show live progress (protect-all passes, cache clears,
|
|
335
|
+
// discovery sweeps). Best-effort — never blocks or fails the daemon.
|
|
336
|
+
const uploadLogTail = async () => {
|
|
337
|
+
if (!creds.shieldId)
|
|
338
|
+
return;
|
|
339
|
+
try {
|
|
340
|
+
const raw = fs.readFileSync(logFile(), 'utf8');
|
|
341
|
+
const lines = raw.split(/\r?\n/).filter(line => line.trim()).slice(-80);
|
|
342
|
+
const identity = (0, machineIdentity_1.getMachineIdentity)();
|
|
343
|
+
await fetch(`${creds.apiUrl}/api/cli/machines/log`, {
|
|
344
|
+
method: 'POST',
|
|
345
|
+
headers: {
|
|
346
|
+
'Content-Type': 'application/json',
|
|
347
|
+
...(creds.shieldKey ? { 'x-shield-key': creds.shieldKey } : {}),
|
|
348
|
+
},
|
|
349
|
+
body: JSON.stringify({ shieldId: creds.shieldId, machineId: identity.machineId, lines }),
|
|
350
|
+
signal: AbortSignal.timeout(8_000),
|
|
351
|
+
});
|
|
352
|
+
}
|
|
353
|
+
catch { /* log shipping is best-effort */ }
|
|
354
|
+
};
|
|
332
355
|
const reportMachineAction = async (actionId, status, detail) => {
|
|
333
356
|
if (!creds.shieldId)
|
|
334
357
|
return;
|
|
@@ -359,10 +382,16 @@ async function runDaemon(args, config) {
|
|
|
359
382
|
executingActionIds.add(action.id);
|
|
360
383
|
await reportMachineAction(action.id, 'running');
|
|
361
384
|
log(`Remote action started: ${action.type} (${action.id}).`);
|
|
385
|
+
await uploadLogTail();
|
|
362
386
|
try {
|
|
363
387
|
let resultSummary = '';
|
|
364
388
|
if (action.type === 'health_check') {
|
|
389
|
+
log('Health check: verifying daemon + protection surfaces…');
|
|
390
|
+
await uploadLogTail();
|
|
365
391
|
const integrity = (0, integrity_1.getLocalIntegrityReport)({ requireDaemon: true });
|
|
392
|
+
log(integrity.ok
|
|
393
|
+
? `Health check: all protection points healthy (${integrity.protectedMcpConfigs}/${integrity.discoveredMcpConfigs} MCP configs wrapped).`
|
|
394
|
+
: `Health check: issues found — ${integrity.reasons.join(', ')}.`);
|
|
366
395
|
resultSummary = integrity.ok
|
|
367
396
|
? 'Daemon and required AgentGuard protection points are healthy.'
|
|
368
397
|
: `Health check found: ${integrity.reasons.join(', ')}`;
|
|
@@ -374,21 +403,28 @@ async function runDaemon(args, config) {
|
|
|
374
403
|
throw new Error('Shield not configured on this machine.');
|
|
375
404
|
const shieldId = creds.shieldId;
|
|
376
405
|
const identity = (0, machineIdentity_1.getMachineIdentity)();
|
|
377
|
-
|
|
406
|
+
log('Policy refresh: clearing Local Safety snapshot cache…');
|
|
407
|
+
(0, localSafetySnapshot_1.clearLocalSafetySnapshotCache)({
|
|
378
408
|
apiUrl: creds.apiUrl,
|
|
379
409
|
shieldId,
|
|
380
|
-
shieldKey: creds.shieldKey,
|
|
381
410
|
developerName: identity.developerName,
|
|
382
411
|
machineName: identity.hostname,
|
|
383
|
-
force: true,
|
|
384
|
-
ttlMs: 0,
|
|
385
412
|
});
|
|
386
|
-
(
|
|
413
|
+
await uploadLogTail();
|
|
414
|
+
log('Policy refresh: pulling latest policy bundle from control plane…');
|
|
415
|
+
const bundle = await (0, runtimeConfig_1.getRuntimeBundle)({
|
|
387
416
|
apiUrl: creds.apiUrl,
|
|
388
417
|
shieldId,
|
|
418
|
+
shieldKey: creds.shieldKey,
|
|
389
419
|
developerName: identity.developerName,
|
|
390
420
|
machineName: identity.hostname,
|
|
421
|
+
machineId: identity.machineId,
|
|
422
|
+
force: true,
|
|
423
|
+
ttlMs: 0,
|
|
391
424
|
});
|
|
425
|
+
log(bundle.policyHash
|
|
426
|
+
? `Policy refresh: bundle applied (hash ${bundle.policyHash.slice(0, 12)}…, ${bundle.policyCount ?? 0} policies).`
|
|
427
|
+
: 'Policy refresh: bundle applied from control plane.');
|
|
392
428
|
resultSummary = bundle.policyHash
|
|
393
429
|
? `Policy bundle refreshed (hash ${bundle.policyHash.slice(0, 12)}…).`
|
|
394
430
|
: 'Policy bundle refreshed from the control plane.';
|
|
@@ -396,25 +432,41 @@ async function runDaemon(args, config) {
|
|
|
396
432
|
else if (action.type === 'repair_protection') {
|
|
397
433
|
if (suspended)
|
|
398
434
|
throw new Error('Machine is suspended; resume it before repairing protection.');
|
|
435
|
+
log('Repair protection: running protect-all (IDE hooks + MCP gateways)…');
|
|
436
|
+
await uploadLogTail();
|
|
399
437
|
await (0, mcpGateway_1.protectAllCommand)({ ...args, dryRun: undefined }, config);
|
|
438
|
+
log('Repair protection: verifying hooks, gateways and daemon integrity…');
|
|
400
439
|
const verification = (0, integrity_1.getLocalIntegrityReport)({ requireDaemon: true });
|
|
401
440
|
if (!verification.ok) {
|
|
402
441
|
throw new Error(`Repair completed but verification still reports: ${verification.reasons.join(', ')}`);
|
|
403
442
|
}
|
|
443
|
+
log(`Repair protection: verified (${verification.protectedMcpConfigs}/${verification.discoveredMcpConfigs} MCP configs wrapped).`);
|
|
404
444
|
resultSummary = 'AgentGuard hooks, gateways, and protection configuration were repaired and verified.';
|
|
405
445
|
}
|
|
406
446
|
else if (action.type === 'discovery_scan') {
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
447
|
+
log('Discovery scan: starting full surface sweep (MCP + secrets + agent files + posture)…');
|
|
448
|
+
await uploadLogTail();
|
|
449
|
+
const logPump = setInterval(() => { void uploadLogTail(); }, 15_000);
|
|
450
|
+
try {
|
|
451
|
+
const exitCode = await new Promise((resolve, reject) => {
|
|
452
|
+
const child = (0, child_process_1.spawn)(process.execPath, [cliEntry(), 'discover', '--upload', '--surface', 'all', '--silent'], {
|
|
453
|
+
windowsHide: true,
|
|
454
|
+
stdio: 'ignore',
|
|
455
|
+
});
|
|
456
|
+
const timer = setTimeout(() => {
|
|
457
|
+
child.kill();
|
|
458
|
+
reject(new Error('Discovery command timed out after 5 minutes'));
|
|
459
|
+
}, 300_000);
|
|
460
|
+
child.on('error', error => { clearTimeout(timer); reject(error); });
|
|
461
|
+
child.on('close', code => { clearTimeout(timer); resolve(code ?? 1); });
|
|
462
|
+
});
|
|
463
|
+
if (exitCode !== 0)
|
|
464
|
+
throw new Error(`Discovery command failed with exit code ${exitCode}`);
|
|
417
465
|
}
|
|
466
|
+
finally {
|
|
467
|
+
clearInterval(logPump);
|
|
468
|
+
}
|
|
469
|
+
log('Discovery scan: upload complete — dashboard discovery + posture timestamps will refresh.');
|
|
418
470
|
resultSummary = 'Discovery + posture scan completed and uploaded.';
|
|
419
471
|
}
|
|
420
472
|
await reportMachineAction(action.id, 'succeeded', { resultSummary });
|
|
@@ -425,6 +477,10 @@ async function runDaemon(args, config) {
|
|
|
425
477
|
await reportMachineAction(action.id, 'failed', { error: message });
|
|
426
478
|
log(`Remote action failed: ${action.type}: ${message}`);
|
|
427
479
|
}
|
|
480
|
+
finally {
|
|
481
|
+
executingActionIds.delete(action.id);
|
|
482
|
+
await uploadLogTail();
|
|
483
|
+
}
|
|
428
484
|
};
|
|
429
485
|
const pollBundle = async () => {
|
|
430
486
|
if (!creds.shieldId)
|
|
@@ -481,6 +537,7 @@ async function runDaemon(args, config) {
|
|
|
481
537
|
log(`Heartbeat: flushed ${result.accepted} spooled event(s).`);
|
|
482
538
|
if (!integrity.ok)
|
|
483
539
|
log(`Integrity warning: ${integrity.reasons.join(', ')}.`);
|
|
540
|
+
await uploadLogTail();
|
|
484
541
|
}
|
|
485
542
|
catch { /* spool stays on disk for the next tick */ }
|
|
486
543
|
};
|
package/dist/version.json
CHANGED