@peac/cli 0.12.10 → 0.12.11
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/doctor.d.ts +22 -0
- package/dist/commands/doctor.d.ts.map +1 -0
- package/dist/index.cjs +208 -3
- package/dist/index.cjs.map +1 -1
- package/dist/index.mjs +208 -3
- package/dist/index.mjs.map +1 -1
- package/package.json +7 -7
package/dist/index.mjs
CHANGED
|
@@ -12,6 +12,7 @@ import { createHash } from 'crypto';
|
|
|
12
12
|
import { computeReceiptRef, validateWorkflowContextOrdered, isValidPurposeToken, isCanonicalPurpose, validateObligationsExtension, validateInteractionOrdered, validateDisputeAttestation, validateAttributionAttestation, validateAgentIdentityAttestation, ReceiptClaimsSchema, parseReceiptClaims } from '@peac/schema';
|
|
13
13
|
import { parse } from '@peac/disc';
|
|
14
14
|
import { readDisputeBundle } from '@peac/audit';
|
|
15
|
+
import { createRequire } from 'module';
|
|
15
16
|
|
|
16
17
|
function getGlobalOptions(cmd) {
|
|
17
18
|
const parent = cmd.parent;
|
|
@@ -565,9 +566,9 @@ policy.command("show-profile").description("Show details of a specific policy pr
|
|
|
565
566
|
function sha256(content) {
|
|
566
567
|
return createHash("sha256").update(content, "utf8").digest("hex");
|
|
567
568
|
}
|
|
568
|
-
function zodPathToJsonPointer(
|
|
569
|
-
if (
|
|
570
|
-
return "/" +
|
|
569
|
+
function zodPathToJsonPointer(path9) {
|
|
570
|
+
if (path9.length === 0) return "";
|
|
571
|
+
return "/" + path9.map((segment) => {
|
|
571
572
|
const str = String(segment);
|
|
572
573
|
return str.replace(/~/g, "~0").replace(/\//g, "~1");
|
|
573
574
|
}).join("/");
|
|
@@ -2368,6 +2369,209 @@ function reconcileCommand() {
|
|
|
2368
2369
|
);
|
|
2369
2370
|
return reconcile;
|
|
2370
2371
|
}
|
|
2372
|
+
var requireFrom = createRequire(path6.join(process.cwd(), "package.json"));
|
|
2373
|
+
function paint(status, msg) {
|
|
2374
|
+
const labels = {
|
|
2375
|
+
green: "PASS",
|
|
2376
|
+
yellow: "WARN",
|
|
2377
|
+
red: "FAIL"
|
|
2378
|
+
};
|
|
2379
|
+
return `[${labels[status]}] ${msg}`;
|
|
2380
|
+
}
|
|
2381
|
+
function parseMajor(nodeVersion) {
|
|
2382
|
+
const m = /^v(\d+)\./.exec(nodeVersion);
|
|
2383
|
+
return m ? parseInt(m[1], 10) : 0;
|
|
2384
|
+
}
|
|
2385
|
+
function checkNodeVersion() {
|
|
2386
|
+
const major = parseMajor(process.version);
|
|
2387
|
+
if (major >= 22) {
|
|
2388
|
+
return {
|
|
2389
|
+
name: "Node version",
|
|
2390
|
+
status: "green",
|
|
2391
|
+
detail: `${process.version} (>= 22 required)`
|
|
2392
|
+
};
|
|
2393
|
+
}
|
|
2394
|
+
return {
|
|
2395
|
+
name: "Node version",
|
|
2396
|
+
status: "red",
|
|
2397
|
+
detail: `${process.version} is below the required Node >= 22`
|
|
2398
|
+
};
|
|
2399
|
+
}
|
|
2400
|
+
function checkPackageResolution() {
|
|
2401
|
+
const required = ["@peac/protocol", "@peac/crypto", "@peac/schema", "@peac/kernel"];
|
|
2402
|
+
const missing = [];
|
|
2403
|
+
for (const pkg of required) {
|
|
2404
|
+
try {
|
|
2405
|
+
requireFrom.resolve(pkg);
|
|
2406
|
+
} catch {
|
|
2407
|
+
missing.push(pkg);
|
|
2408
|
+
}
|
|
2409
|
+
}
|
|
2410
|
+
if (missing.length === 0) {
|
|
2411
|
+
return {
|
|
2412
|
+
name: "Package resolution",
|
|
2413
|
+
status: "green",
|
|
2414
|
+
detail: `${required.length} required packages resolvable`
|
|
2415
|
+
};
|
|
2416
|
+
}
|
|
2417
|
+
return {
|
|
2418
|
+
name: "Package resolution",
|
|
2419
|
+
status: "red",
|
|
2420
|
+
detail: `${missing.length} missing: ${missing.join(", ")}`
|
|
2421
|
+
};
|
|
2422
|
+
}
|
|
2423
|
+
function checkKeyMaterial(cwd) {
|
|
2424
|
+
const env = process.env.PEAC_ISSUER_KEY || "";
|
|
2425
|
+
if (env) {
|
|
2426
|
+
if (env.startsWith("env:") || env.startsWith("file:")) {
|
|
2427
|
+
return {
|
|
2428
|
+
name: "Issuer key material",
|
|
2429
|
+
status: "green",
|
|
2430
|
+
detail: `PEAC_ISSUER_KEY set via ${env.split(":")[0]}:... scheme`
|
|
2431
|
+
};
|
|
2432
|
+
}
|
|
2433
|
+
return {
|
|
2434
|
+
name: "Issuer key material",
|
|
2435
|
+
status: "yellow",
|
|
2436
|
+
detail: "PEAC_ISSUER_KEY set but not using env: or file: scheme"
|
|
2437
|
+
};
|
|
2438
|
+
}
|
|
2439
|
+
const candidatePaths = [
|
|
2440
|
+
path6.join(cwd, "issuer.jwk.json"),
|
|
2441
|
+
path6.join(cwd, ".peac/issuer.jwk.json")
|
|
2442
|
+
];
|
|
2443
|
+
for (const p of candidatePaths) {
|
|
2444
|
+
if (fs6.existsSync(p)) {
|
|
2445
|
+
return {
|
|
2446
|
+
name: "Issuer key material",
|
|
2447
|
+
status: "yellow",
|
|
2448
|
+
detail: `found ${path6.relative(cwd, p)}; prefer PEAC_ISSUER_KEY env var`
|
|
2449
|
+
};
|
|
2450
|
+
}
|
|
2451
|
+
}
|
|
2452
|
+
return {
|
|
2453
|
+
name: "Issuer key material",
|
|
2454
|
+
status: "yellow",
|
|
2455
|
+
detail: "not found (issuance disabled; read-only tools still work)"
|
|
2456
|
+
};
|
|
2457
|
+
}
|
|
2458
|
+
function checkPeacTxt(cwd) {
|
|
2459
|
+
const p = path6.join(cwd, ".well-known/peac.txt");
|
|
2460
|
+
if (fs6.existsSync(p)) {
|
|
2461
|
+
return {
|
|
2462
|
+
name: "peac.txt presence",
|
|
2463
|
+
status: "green",
|
|
2464
|
+
detail: `.well-known/peac.txt present at ${p}`
|
|
2465
|
+
};
|
|
2466
|
+
}
|
|
2467
|
+
return {
|
|
2468
|
+
name: "peac.txt presence",
|
|
2469
|
+
status: "yellow",
|
|
2470
|
+
detail: "no .well-known/peac.txt (discovery-layer check; optional for tool callers)"
|
|
2471
|
+
};
|
|
2472
|
+
}
|
|
2473
|
+
async function checkOnlineJwks(issuerUrl, timeoutMs) {
|
|
2474
|
+
const ctrl = new AbortController();
|
|
2475
|
+
const timer = setTimeout(() => ctrl.abort(), timeoutMs);
|
|
2476
|
+
try {
|
|
2477
|
+
const cfgUrl = new URL("/.well-known/peac-issuer.json", issuerUrl).toString();
|
|
2478
|
+
const res = await fetch(cfgUrl, { signal: ctrl.signal });
|
|
2479
|
+
if (res.status !== 200) {
|
|
2480
|
+
return {
|
|
2481
|
+
name: "Issuer configuration",
|
|
2482
|
+
status: "red",
|
|
2483
|
+
detail: `${cfgUrl} returned ${res.status}`
|
|
2484
|
+
};
|
|
2485
|
+
}
|
|
2486
|
+
const cfg = await res.json();
|
|
2487
|
+
const jwksUri = cfg.jwks_uri;
|
|
2488
|
+
if (!jwksUri) {
|
|
2489
|
+
return {
|
|
2490
|
+
name: "Issuer configuration",
|
|
2491
|
+
status: "red",
|
|
2492
|
+
detail: `${cfgUrl} missing jwks_uri`
|
|
2493
|
+
};
|
|
2494
|
+
}
|
|
2495
|
+
const jwksRes = await fetch(jwksUri, { signal: ctrl.signal });
|
|
2496
|
+
if (jwksRes.status !== 200) {
|
|
2497
|
+
return {
|
|
2498
|
+
name: "Issuer configuration",
|
|
2499
|
+
status: "red",
|
|
2500
|
+
detail: `jwks_uri ${jwksUri} returned ${jwksRes.status}`
|
|
2501
|
+
};
|
|
2502
|
+
}
|
|
2503
|
+
const jwks = await jwksRes.json();
|
|
2504
|
+
const keyCount = Array.isArray(jwks.keys) ? jwks.keys.length : 0;
|
|
2505
|
+
if (keyCount === 0) {
|
|
2506
|
+
return {
|
|
2507
|
+
name: "Issuer configuration",
|
|
2508
|
+
status: "red",
|
|
2509
|
+
detail: `jwks_uri ${jwksUri} returned empty keys array`
|
|
2510
|
+
};
|
|
2511
|
+
}
|
|
2512
|
+
return {
|
|
2513
|
+
name: "Issuer configuration",
|
|
2514
|
+
status: "green",
|
|
2515
|
+
detail: `${cfgUrl} + jwks_uri resolvable with ${keyCount} key(s)`
|
|
2516
|
+
};
|
|
2517
|
+
} catch (err) {
|
|
2518
|
+
return {
|
|
2519
|
+
name: "Issuer configuration",
|
|
2520
|
+
status: "red",
|
|
2521
|
+
detail: `fetch failed: ${err instanceof Error ? err.message : String(err)}`
|
|
2522
|
+
};
|
|
2523
|
+
} finally {
|
|
2524
|
+
clearTimeout(timer);
|
|
2525
|
+
}
|
|
2526
|
+
}
|
|
2527
|
+
async function runDoctor(options) {
|
|
2528
|
+
const results = [];
|
|
2529
|
+
results.push(checkNodeVersion());
|
|
2530
|
+
results.push(checkPackageResolution());
|
|
2531
|
+
results.push(checkKeyMaterial(options.cwd));
|
|
2532
|
+
results.push(checkPeacTxt(options.cwd));
|
|
2533
|
+
if (options.online) {
|
|
2534
|
+
if (!options.issuer) {
|
|
2535
|
+
results.push({
|
|
2536
|
+
name: "Issuer configuration",
|
|
2537
|
+
status: "yellow",
|
|
2538
|
+
detail: "--online set but --issuer <url> not provided; skipping remote check"
|
|
2539
|
+
});
|
|
2540
|
+
} else {
|
|
2541
|
+
results.push(await checkOnlineJwks(options.issuer, options.timeout));
|
|
2542
|
+
}
|
|
2543
|
+
} else {
|
|
2544
|
+
results.push({
|
|
2545
|
+
name: "Issuer configuration",
|
|
2546
|
+
status: "yellow",
|
|
2547
|
+
detail: "offline mode (default); pass --online --issuer <url> to enable remote check"
|
|
2548
|
+
});
|
|
2549
|
+
}
|
|
2550
|
+
console.log("\npeac doctor");
|
|
2551
|
+
console.log("-----------");
|
|
2552
|
+
for (const r of results) {
|
|
2553
|
+
console.log(paint(r.status, `${r.name}: ${r.detail}`));
|
|
2554
|
+
}
|
|
2555
|
+
const hasRed = results.some((r) => r.status === "red");
|
|
2556
|
+
console.log(hasRed ? "\nFAIL: one or more checks are red." : "\nOK: no red checks.");
|
|
2557
|
+
return hasRed ? 1 : 0;
|
|
2558
|
+
}
|
|
2559
|
+
function doctor(program2) {
|
|
2560
|
+
program2.command("doctor").description("Run PEAC installability diagnostics (offline by default)").option("--online", "enable remote checks (issuer configuration, JWKS)", false).option("--issuer <url>", "issuer URL for --online checks (https://...)").option(
|
|
2561
|
+
"--timeout <ms>",
|
|
2562
|
+
"timeout for remote checks in milliseconds",
|
|
2563
|
+
(v) => parseInt(v, 10),
|
|
2564
|
+
5e3
|
|
2565
|
+
).action(async (opts) => {
|
|
2566
|
+
const exitCode = await runDoctor({
|
|
2567
|
+
online: !!opts.online,
|
|
2568
|
+
issuer: opts.issuer,
|
|
2569
|
+
timeout: opts.timeout,
|
|
2570
|
+
cwd: process.cwd()
|
|
2571
|
+
});
|
|
2572
|
+
process.exit(exitCode);
|
|
2573
|
+
});
|
|
2574
|
+
}
|
|
2371
2575
|
|
|
2372
2576
|
// src/index.ts
|
|
2373
2577
|
var program = new Command();
|
|
@@ -2504,6 +2708,7 @@ program.addCommand(policy);
|
|
|
2504
2708
|
program.addCommand(conformance);
|
|
2505
2709
|
program.addCommand(samples);
|
|
2506
2710
|
program.addCommand(reconcileCommand());
|
|
2711
|
+
doctor(program);
|
|
2507
2712
|
try {
|
|
2508
2713
|
program.parse();
|
|
2509
2714
|
} catch (err) {
|