prae-cli 0.10.0 → 0.10.2

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.
@@ -1,8 +1,7 @@
1
- import { a as withinAny, r as hostOf, t as evaluate } from "./evaluate-DhoGQw0Z.js";
2
- import { n as cite, t as FRAMEWORKS } from "./frameworks-BgZHGKiH.js";
3
- import { t as parsePack } from "./pack-h5n43S0F.js";
4
- import { a as verifyEntries, b as seal, g as GENESIS, h as verifySignature, l as verifyBytes, n as Ledger, r as readEntries, t as verifyGrowing, u as fingerprint, v as chain } from "./src-Cy-Acrgj.js";
5
- import { a as renderPacks, c as renderNav, i as renderOverview, l as renderShell, n as renderApprovals, o as CONTROLS, r as renderCompliance, s as esc, t as navFor, u as VERDICT_LABEL$1 } from "./views-CUcixte0.js";
1
+ import { c as withinAny, i as evaluate, n as FRAMEWORKS, o as hostOf, r as cite, t as parsePack } from "./src-CZYvvEn5.js";
2
+ import { a as verifyEntries, b as seal, g as GENESIS, h as verifySignature, l as verifyBytes, n as Ledger, r as readEntries, t as verifyGrowing, u as fingerprint, v as chain } from "./src-CuiWC80o.js";
3
+ import { t as capabilityOf } from "./facts-DkTRO2p0.js";
4
+ import { a as renderPacks, c as renderNav, i as renderOverview, l as renderShell, n as renderApprovals, o as CONTROLS, r as renderCompliance, s as esc, t as navFor, u as VERDICT_LABEL$1 } from "./views-endiZu1Z.js";
6
5
  import { createRequire } from "node:module";
7
6
  import { closeSync, existsSync, mkdirSync, openSync, readFileSync, readSync, readdirSync, statSync, watch, writeFileSync } from "node:fs";
8
7
  import { mkdir, readFile, writeFile } from "node:fs/promises";
@@ -866,6 +865,327 @@ function readRedteam(dir, files) {
866
865
  };
867
866
  }
868
867
 
868
+ //#endregion
869
+ //#region ../controls/src/inspect.ts
870
+ /**
871
+ * How a rule is evaluated, one label per matcher field it uses. Every one
872
+ * of them runs in the PRAE kernel's deterministic matcher; these are the
873
+ * only engines the rule model has. There is no semantic model and no
874
+ * syntax tree behind any rule: `command` is a lexical decomposition.
875
+ */
876
+ const ENGINES = {
877
+ tool: "Tool / capability match",
878
+ capability: "Tool / capability match",
879
+ plugin: "Tool / capability match",
880
+ agent: "Agent match",
881
+ path: "Path glob match",
882
+ host: "Host list match",
883
+ arg: "Argument value match",
884
+ count: "Session call count",
885
+ command: "Command decomposition (lexical)",
886
+ secrets: "Content inspection (regex + entropy)"
887
+ };
888
+ function enginesOf(rule) {
889
+ const keys = Object.keys(rule.when);
890
+ if (keys.length === 0) return ["Every call (no matcher)"];
891
+ return [...new Set(keys.map((k) => ENGINES[k] ?? `Unknown matcher: ${k}`))];
892
+ }
893
+ const list = (v) => v === void 0 ? [] : typeof v === "string" ? [v] : v;
894
+ const quoted = (v) => v.map((x) => `\`${String(x)}\``).join(", ");
895
+ /** The plain-language reading, or undefined when it cannot be given reliably. */
896
+ function logicOf(rule) {
897
+ const w = rule.when;
898
+ if (Object.keys(w).some((k) => ENGINES[k] === void 0)) return void 0;
899
+ const subjects = [];
900
+ if (w.capability !== void 0) subjects.push(`calls with capability ${quoted(list(w.capability))}`);
901
+ if (w.tool !== void 0) subjects.push(`calls to ${quoted(list(w.tool))}`);
902
+ if (w.plugin !== void 0) subjects.push(`tools registered by ${quoted(list(w.plugin))}`);
903
+ const evaluates = subjects.length === 0 ? "Every tool call" : `${subjects.join(" that are also ")}`.replace(/^./, (c$1) => c$1.toUpperCase());
904
+ const targets = [];
905
+ if (w.path !== void 0) targets.push(w.path.arg === void 0 ? "the path argument (the first conventional one present)" : `the path in argument \`${w.path.arg}\``);
906
+ if (w.host !== void 0) targets.push(w.host.arg === void 0 ? "the URL argument's host" : `the host of argument \`${w.host.arg}\``);
907
+ if (w.command !== void 0) targets.push("the shell command line, read lexically");
908
+ if (w.arg !== void 0) targets.push(`argument${w.arg.length === 1 ? "" : "s"} ${quoted(w.arg.map((a) => a.name))}`);
909
+ if (w.count !== void 0) targets.push("this session's count of calls with the same capability");
910
+ if (w.secrets !== void 0) targets.push("findings the content inspection attached to the call");
911
+ if (w.agent !== void 0) targets.push("the agent making the call");
912
+ const target = targets.length === 0 ? "The call as a whole" : targets.join("; ");
913
+ const condition = [];
914
+ if (w.agent !== void 0) condition.push(`the agent is ${quoted(list(w.agent))}`);
915
+ if (w.path?.inside !== void 0) condition.push(`the path is under ${quoted(w.path.inside)}`);
916
+ if (w.path?.outside !== void 0) condition.push(`the path is outside all of ${quoted(w.path.outside)}`);
917
+ if (w.host?.in !== void 0) condition.push(`the host is one of ${quoted(w.host.in)}`);
918
+ if (w.host?.notIn !== void 0) condition.push(`the host is not one of ${quoted(w.host.notIn)}`);
919
+ for (const a of w.arg ?? []) {
920
+ if (a.present !== void 0) condition.push(`\`${a.name}\` is ${a.present ? "present" : "absent"}`);
921
+ if (a.above !== void 0) condition.push(`\`${a.name}\` is above ${a.above}`);
922
+ if (a.below !== void 0) condition.push(`\`${a.name}\` is below ${a.below}`);
923
+ if (a.equals !== void 0) condition.push(`\`${a.name}\` equals \`${String(a.equals)}\``);
924
+ if (a.oneOf !== void 0) condition.push(`\`${a.name}\` is one of ${quoted(a.oneOf)}`);
925
+ }
926
+ if (w.count !== void 0) condition.push(`more than ${w.count.above} such calls have already run in this session`);
927
+ if (w.secrets !== void 0) {
928
+ const n = w.secrets.atLeast ?? 1;
929
+ condition.push(`${n === 1 ? "a finding" : `at least ${n} findings`}${w.secrets.types === void 0 ? "" : ` of type ${quoted(w.secrets.types)}`}${w.secrets.minConfidence === void 0 ? "" : ` with confidence ≥ ${w.secrets.minConfidence}`}`);
930
+ }
931
+ const c = w.command;
932
+ if (c !== void 0) {
933
+ if (c.name !== void 0) condition.push(`the program (or a wrapper it runs through) is ${quoted(list(c.name))}`);
934
+ const must = list(c.token).filter((t) => !t.startsWith("!"));
935
+ const never = list(c.token).filter((t) => t.startsWith("!")).map((t) => t.slice(1));
936
+ if (must.length > 0) condition.push(`it carries all of ${quoted(must)}`);
937
+ if (never.length > 0) condition.push(`it carries none of ${quoted(never)}`);
938
+ if (c.anyToken !== void 0) condition.push(`it carries any of ${quoted(list(c.anyToken))}`);
939
+ if (c.pipedTo !== void 0) condition.push(`it pipes into ${quoted(list(c.pipedTo))}`);
940
+ if (c.pipeline !== void 0) condition.push(`its pipeline has the shape ${quoted(list(c.pipeline))}`);
941
+ }
942
+ if (condition.length === 0) condition.push("always: no further condition");
943
+ const e = rule.effect;
944
+ return {
945
+ evaluates,
946
+ target,
947
+ condition,
948
+ action: e.kind === "deny" ? "Refuses the call before it runs." : e.kind === "ask" ? "Holds the call until a person allows or refuses it." : e.kind === "warn" ? "Lets the call run and records a warning." : e.kind === "allow" ? "Allows the call and names this rule in the evidence; an allow never overrides another rule's refusal." : `Rewrites ${quoted(Object.keys(e.set))} before the call runs.`
949
+ };
950
+ }
951
+ const isEvent = (e) => e.tool.startsWith("prae:") || e.tool === "system-prompt";
952
+ /**
953
+ * Per pack name, every row in which a gate showed it in force — its
954
+ * activation or reload row, or a decision attributed to it — and its
955
+ * removal rows, in ledger order.
956
+ */
957
+ function packActivity(entries) {
958
+ const out = /* @__PURE__ */ new Map();
959
+ const add = (name, seen) => {
960
+ const list$1 = out.get(name);
961
+ if (list$1 === void 0) out.set(name, [seen]);
962
+ else list$1.push(seen);
963
+ };
964
+ for (const e of entries) if (e.tool === "prae:pack") {
965
+ if (e.rule === "pack.activated" || e.rule === "pack.reloaded") add(e.pack.name, {
966
+ row: e,
967
+ kind: "in-force"
968
+ });
969
+ else if (e.rule === "pack.removed") add(e.pack.name, {
970
+ row: e,
971
+ kind: "removed"
972
+ });
973
+ } else if (!isEvent(e) && e.pack !== void 0) add(e.pack.name, {
974
+ row: e,
975
+ kind: "in-force"
976
+ });
977
+ return out;
978
+ }
979
+ /** Whether a gate confirmed this exact definition of a pack in force. */
980
+ function activityOf(pack, activity, failed) {
981
+ if (failed !== void 0) return {
982
+ state: "failed",
983
+ note: `A gate refused this definition: ${failed.why}`,
984
+ seq: failed.seq,
985
+ at: failed.at,
986
+ sessions: []
987
+ };
988
+ const rows = activity.get(pack.name) ?? [];
989
+ const same = (r) => r.pack.version === pack.version && (pack.sha256 === void 0 || r.pack.sha256 === void 0 || r.pack.sha256 === pack.sha256);
990
+ const last = rows.at(-1);
991
+ if (last?.kind === "removed") return {
992
+ state: "inactive",
993
+ note: `A gate sealed this pack removed (row #${last.row.seq}); its rules no longer apply there.`,
994
+ seq: last.row.seq,
995
+ at: last.row.at,
996
+ sessions: []
997
+ };
998
+ const confirming = rows.filter((s) => s.kind === "in-force" && same(s.row));
999
+ const newest = confirming.at(-1);
1000
+ if (newest !== void 0) return {
1001
+ state: "active",
1002
+ note: `A gate showed ${pack.name} ${pack.version} in force at row #${newest.row.seq}. That is the last confirmation; PRAE has no heartbeat that says a gate is running now.`,
1003
+ seq: newest.row.seq,
1004
+ at: newest.row.at,
1005
+ sessions: [...new Set(confirming.flatMap((s) => s.row.session === void 0 ? [] : [s.row.session]))]
1006
+ };
1007
+ const other = rows.at(-1);
1008
+ return {
1009
+ state: "unconfirmed",
1010
+ note: other !== void 0 ? `Verification required: the gate last showed ${pack.name} ${other.row.pack.version} in force (row #${other.row.seq}); this configured definition has not been confirmed by any gate.` : "Verification required: configured, but no gate has sealed this pack in force in this record yet.",
1011
+ sessions: []
1012
+ };
1013
+ }
1014
+ /**
1015
+ * The rows that can be evidence: every row except the console's own
1016
+ * deployment rows. Placing a pack is an operation, not something the agent
1017
+ * did or a gate decided, so it never counts toward a control — not as a
1018
+ * recorded event, and never as a refusal.
1019
+ */
1020
+ function evidenceRows(entries) {
1021
+ return entries.filter((e) => e.tool !== "prae:deploy");
1022
+ }
1023
+ /** How long a written pack waits for a gate before it needs verification. */
1024
+ const CONFIRM_WINDOW_MS = 2e4;
1025
+ /**
1026
+ * Each deployment's lifecycle, read from the ledger alone: the console's
1027
+ * `deploy.requested` row, then what a gate sealed after it. ACTIVE only on
1028
+ * a gate's activation or reload row carrying the deployed file's exact
1029
+ * sha256; FAILED on the console's own failure row or a gate refusing the
1030
+ * file; INACTIVE when a gate later sealed it removed. With no gate row,
1031
+ * APPLYING for a short window and then VERIFICATION_REQUIRED — the file is
1032
+ * configured, and nothing has shown it enforced.
1033
+ */
1034
+ function deploymentsOf(entries, now = Date.now()) {
1035
+ const out = [];
1036
+ const requests = entries.filter((e) => e.tool === "prae:deploy" && e.deploy?.action === "requested");
1037
+ for (const r of requests) {
1038
+ const d = r.deploy;
1039
+ if (d === void 0 || r.pack.sha256 === void 0) continue;
1040
+ const sha = r.pack.sha256;
1041
+ const after = entries.filter((e) => e.seq > r.seq);
1042
+ const base = {
1043
+ operation: d.operation,
1044
+ pack: r.pack.name,
1045
+ version: r.pack.version,
1046
+ sha256: sha,
1047
+ file: d.file,
1048
+ target: d.target,
1049
+ environment: d.environment,
1050
+ rules: d.rules,
1051
+ controls: d.controls,
1052
+ ...d.operator === void 0 ? {} : { operator: d.operator },
1053
+ requestedAt: r.at,
1054
+ seq: r.seq
1055
+ };
1056
+ const failedRow = after.find((e) => e.tool === "prae:deploy" && e.deploy?.action === "failed" && e.deploy.operation === d.operation);
1057
+ if (failedRow !== void 0) {
1058
+ out.push({
1059
+ ...base,
1060
+ state: "FAILED",
1061
+ failure: "operation",
1062
+ note: `Not deployed: ${failedRow.deploy?.error ?? "the operation failed"}. Nothing was placed where a gate reads, so coverage is unchanged.`,
1063
+ runtime: {
1064
+ seq: failedRow.seq,
1065
+ at: failedRow.at
1066
+ },
1067
+ sessions: []
1068
+ });
1069
+ continue;
1070
+ }
1071
+ const confirms = after.filter((e) => e.tool === "prae:pack" && (e.rule === "pack.activated" || e.rule === "pack.reloaded") && e.pack.sha256 === sha);
1072
+ const refused = after.find((e) => e.tool === "prae:pack" && e.rule === "pack.rejected" && e.subject === d.file);
1073
+ const removed = after.find((e) => e.tool === "prae:pack" && e.rule === "pack.removed" && e.pack.sha256 === sha);
1074
+ const sessions = [...new Set(confirms.flatMap((e) => e.session === void 0 ? [] : [e.session]))];
1075
+ const at = (e) => ({
1076
+ seq: e.seq,
1077
+ at: e.at,
1078
+ ...e.session === void 0 ? {} : { session: e.session }
1079
+ });
1080
+ if (refused !== void 0) {
1081
+ out.push({
1082
+ ...base,
1083
+ state: "FAILED",
1084
+ failure: "runtime",
1085
+ note: `A gate refused the pack (row #${refused.seq}): ${(refused.reason ?? "").replace(/^pack rejected, not loaded: /, "")}. The gate kept the policy it had; this pack does not count toward coverage.`,
1086
+ runtime: at(refused),
1087
+ sessions
1088
+ });
1089
+ continue;
1090
+ }
1091
+ if (removed !== void 0) {
1092
+ out.push({
1093
+ ...base,
1094
+ state: "INACTIVE",
1095
+ note: `A gate sealed the pack removed (row #${removed.seq}); its rules no longer apply there.`,
1096
+ runtime: at(removed),
1097
+ sessions
1098
+ });
1099
+ continue;
1100
+ }
1101
+ const first = confirms[0];
1102
+ if (first !== void 0) {
1103
+ out.push({
1104
+ ...base,
1105
+ state: "ACTIVE",
1106
+ note: `A gate sealed this exact pack (sha256 ${sha.slice(0, 12)}) in force at row #${first.seq}${sessions.length > 0 ? ` · ${sessions.length} session${sessions.length === 1 ? "" : "s"}` : ""}.`,
1107
+ runtime: at(first),
1108
+ sessions
1109
+ });
1110
+ continue;
1111
+ }
1112
+ const waiting = now - Date.parse(r.at) < CONFIRM_WINDOW_MS;
1113
+ out.push({
1114
+ ...base,
1115
+ state: waiting ? "APPLYING" : "VERIFICATION_REQUIRED",
1116
+ note: waiting ? "Written to the workspace pack directory; waiting for a gate to seal it in force." : "Written and configured, but no gate has sealed it in force: no gate watching this workspace is running. A gate mounted with this workspace confirms it when it starts.",
1117
+ sessions
1118
+ });
1119
+ }
1120
+ return out;
1121
+ }
1122
+ function describeRule(rule) {
1123
+ const logic = logicOf(rule);
1124
+ return {
1125
+ id: rule.id,
1126
+ ...rule.title === void 0 ? {} : { title: rule.title },
1127
+ ...rule.description === void 0 ? {} : { description: rule.description },
1128
+ effect: rule.effect.kind,
1129
+ because: rule.effect.because,
1130
+ engines: enginesOf(rule),
1131
+ ...logic === void 0 ? {} : { logic },
1132
+ definition: JSON.stringify({
1133
+ when: rule.when,
1134
+ effect: rule.effect
1135
+ }, null, 2),
1136
+ cites: Object.entries(rule.frameworks ?? {}).flatMap(([f, cs]) => cs.map((c) => `${f}:${c}`))
1137
+ };
1138
+ }
1139
+ function ruleViews(pack, origins, entries, admitted, environment, failedPacks = /* @__PURE__ */ new Map()) {
1140
+ const activity = packActivity(entries);
1141
+ const byRule = /* @__PURE__ */ new Map();
1142
+ for (const e of admitted) {
1143
+ if (e.rule === void 0 || isEvent(e)) continue;
1144
+ const rows = byRule.get(e.rule);
1145
+ if (rows === void 0) byRule.set(e.rule, [e]);
1146
+ else rows.push(e);
1147
+ }
1148
+ const out = {};
1149
+ for (const rule of pack?.rules ?? []) {
1150
+ const ref = origins.get(rule.id) ?? {
1151
+ name: pack?.pack ?? "unknown",
1152
+ version: pack?.version ?? ""
1153
+ };
1154
+ const rows = byRule.get(rule.id) ?? [];
1155
+ const last = rows.at(-1);
1156
+ out[rule.id] = {
1157
+ ...describeRule(rule),
1158
+ pack: ref,
1159
+ scope: {
1160
+ agents: rule.when.agent === void 0 ? "all" : list(rule.when.agent),
1161
+ environment
1162
+ },
1163
+ active: activityOf(ref, activity, ref.sha256 === void 0 ? void 0 : failedPacks.get(ref.sha256)),
1164
+ evidence: {
1165
+ count: rows.length,
1166
+ ...last === void 0 ? {} : { last: {
1167
+ seq: last.seq,
1168
+ at: last.at
1169
+ } }
1170
+ }
1171
+ };
1172
+ }
1173
+ return out;
1174
+ }
1175
+ /**
1176
+ * Deployed packs a gate refused, by sha256 — the latest deployment of each
1177
+ * definition decides — kept out of coverage: the gate does not enforce them.
1178
+ */
1179
+ function refusedDeployments(deployments) {
1180
+ const latest = /* @__PURE__ */ new Map();
1181
+ for (const d of deployments) latest.set(d.sha256, d);
1182
+ return new Map([...latest.values()].filter((d) => d.failure === "runtime").map((d) => [d.sha256, {
1183
+ seq: d.runtime?.seq ?? d.seq,
1184
+ at: d.runtime?.at ?? d.requestedAt,
1185
+ why: d.note
1186
+ }]));
1187
+ }
1188
+
869
1189
  //#endregion
870
1190
  //#region ../controls/src/scope.ts
871
1191
  const ROW_LIMIT = 50;
@@ -943,10 +1263,11 @@ function catalogOf(file) {
943
1263
  };
944
1264
  frameworks.set(framework.id, f);
945
1265
  }
1266
+ const name = control.name ?? FRAMEWORKS[framework.id]?.controls[id]?.name;
946
1267
  f.controls.push({
947
1268
  key,
948
1269
  id,
949
- ...control.name === void 0 ? {} : { name: control.name },
1270
+ ...name === void 0 ? {} : { name },
950
1271
  asks: control.asks
951
1272
  });
952
1273
  };
@@ -1040,6 +1361,11 @@ function inPeriod(at, period, now) {
1040
1361
  if (period.kind === "days") return t >= now - period.days * 864e5;
1041
1362
  return t >= Date.parse(period.from) && t < Date.parse(period.to) + 864e5;
1042
1363
  }
1364
+ /** The rows a scope admits: its agents, its sessions, its period. */
1365
+ function admit(entries, scope, agent, now = Date.now()) {
1366
+ const agentOf$1 = (e) => e.agent ?? agent;
1367
+ return entries.filter((e) => (scope.agents === "all" || scope.agents.includes(agentOf$1(e))) && (scope.sessions === "all" || e.session !== void 0 && scope.sessions.includes(e.session)) && inPeriod(e.at, scope.period, now));
1368
+ }
1043
1369
  /**
1044
1370
  * Assess the scoped controls against the record. The rows are narrowed to
1045
1371
  * the scope's agents, sessions and period before any control reads them;
@@ -1049,7 +1375,7 @@ function inPeriod(at, period, now) {
1049
1375
  function assess(data, scope, catalog, options) {
1050
1376
  const now = options.now ?? Date.now();
1051
1377
  const agentOf$1 = (e) => e.agent ?? options.agent;
1052
- const admitted = data.entries.filter((e) => (scope.agents === "all" || scope.agents.includes(agentOf$1(e))) && (scope.sessions === "all" || e.session !== void 0 && scope.sessions.includes(e.session)) && inPeriod(e.at, scope.period, now));
1378
+ const admitted = admit(data.entries, scope, options.agent, now);
1053
1379
  const scoped = {
1054
1380
  ...data,
1055
1381
  entries: admitted
@@ -1196,17 +1522,39 @@ function agentOf(dir) {
1196
1522
  } catch {}
1197
1523
  return "this agent";
1198
1524
  }
1199
- function assessCompliance(record, data) {
1525
+ /** What a record's compliance is assessed against: its catalog and scope. */
1526
+ function complianceContext(record) {
1200
1527
  const { catalog, catalogError } = catalogFor(record.dir);
1201
1528
  return {
1202
- ...assess(data, scopeFor(record.dir, catalog), catalog, {
1529
+ catalog,
1530
+ ...catalogError === void 0 ? {} : { catalogError },
1531
+ scope: scopeFor(record.dir, catalog),
1532
+ options: {
1203
1533
  agent: agentOf(record.dir),
1204
1534
  environment: basename(resolve(record.dir))
1205
- }),
1535
+ }
1536
+ };
1537
+ }
1538
+ function assessCompliance(record, data) {
1539
+ const { catalog, catalogError, scope, options } = complianceContext(record);
1540
+ return {
1541
+ ...assess(data, scope, catalog, options),
1206
1542
  saved: existsSync(resolve(record.dir, SCOPE_FILE)),
1207
1543
  ...catalogError === void 0 ? {} : { catalogError }
1208
1544
  };
1209
1545
  }
1546
+ /**
1547
+ * The Rule Inspector's rules (PRA-195): every rule in force, with where it
1548
+ * came from, how it is evaluated, whether a gate confirmed its pack in
1549
+ * force, and its sealed decisions within the scope. Read-only.
1550
+ */
1551
+ function inspectorOf(record, data, scope, origins, deployments, refused) {
1552
+ const agent = agentOf(record.dir);
1553
+ return {
1554
+ rules: ruleViews(data.pack, origins, data.entries, admit(data.entries, scope, agent), basename(resolve(record.dir)), refused),
1555
+ deployments
1556
+ };
1557
+ }
1210
1558
 
1211
1559
  //#endregion
1212
1560
  //#region src/serve.ts
@@ -1395,15 +1743,30 @@ function complianceOf(record) {
1395
1743
  problems: grown.problems
1396
1744
  };
1397
1745
  const { set } = packSetOf(record);
1398
- const pack = set.packs.length > 0 ? set.effective : void 0;
1746
+ const deployments = deploymentsOf(entries);
1747
+ const refused = refusedDeployments(deployments);
1748
+ const kept = set.packs.filter((p) => !refused.has(p.sha256));
1749
+ const pack = kept.length === 0 ? void 0 : kept.length === set.packs.length ? set.effective : {
1750
+ ...set.effective,
1751
+ rules: kept.flatMap((p) => p.pack.rules)
1752
+ };
1399
1753
  const data = {
1400
1754
  ...pack === void 0 ? {} : { pack },
1401
- entries,
1755
+ entries: evidenceRows(entries),
1402
1756
  verification,
1403
1757
  generatedAt: (/* @__PURE__ */ new Date()).toISOString()
1404
1758
  };
1759
+ const assessment = assessCompliance(record, data);
1760
+ const origins = new Map(set.packs.flatMap((p) => p.pack.rules.map((r) => [r.id, {
1761
+ name: p.pack.pack,
1762
+ version: p.pack.version,
1763
+ sha256: p.sha256,
1764
+ origin: p.origin,
1765
+ file: p.file.split("/").at(-1) ?? p.file
1766
+ }])));
1405
1767
  return {
1406
- assessment: assessCompliance(record, data),
1768
+ assessment,
1769
+ inspector: inspectorOf(record, data, assessment.scope, origins, deployments, refused),
1407
1770
  controls: CONTROLS.map((control) => {
1408
1771
  const found = Object.entries(FRAMEWORKS).find(([, f]) => control.id.startsWith(`${f.short} · `));
1409
1772
  const code = found === void 0 ? void 0 : control.id.split(" · ")[1];
@@ -1822,7 +2185,7 @@ async function check(path, argv = []) {
1822
2185
  * from the findings, so there is nothing here to be careful about printing.
1823
2186
  */
1824
2187
  async function scanFile(path, packPath, json) {
1825
- const { scan } = await import("./src-DMoYsdfo.js");
2188
+ const { scan } = await import("./src-DIuEg7gi.js");
1826
2189
  let content;
1827
2190
  try {
1828
2191
  content = await readFile(path, "utf8");
@@ -1862,13 +2225,13 @@ async function scanFile(path, packPath, json) {
1862
2225
  * not come up inspecting nothing.
1863
2226
  */
1864
2227
  async function mcp(root, packPath, ledgerPath) {
1865
- const { serveMcp } = await import("./mcp-D521EoP6.js");
2228
+ const { serveMcp } = await import("./mcp-C2gadkb2.js");
1866
2229
  const { resolve: resolve$1 } = await import("node:path");
1867
2230
  const bundled = [join(import.meta.dirname, "secret-catcher.prae.json"), join(import.meta.dirname, "..", "..", "..", "examples", "secret-catcher.prae.json")].find((candidate) => existsSync(candidate));
1868
2231
  const chosen = packPath ?? bundled;
1869
2232
  if (chosen === void 0) return fail("mcp needs --pack <file>; no bundled secret-catcher found");
1870
2233
  const pack = await loadPack(chosen);
1871
- const { Ledger: Ledger$1 } = await import("./src-wG36kT6K.js");
2234
+ const { Ledger: Ledger$1 } = await import("./src-2iJy5MZc.js");
1872
2235
  const ledger = ledgerPath === void 0 ? void 0 : Ledger$1.open(ledgerPath);
1873
2236
  console.error(`prae mcp: serving ${resolve$1(root)} under ${pack.pack} ${pack.version}` + (ledgerPath === void 0 ? " (no ledger)" : ` · ledger ${ledgerPath}`));
1874
2237
  await serveMcp({
@@ -1894,8 +2257,7 @@ function parseArgs(pairs) {
1894
2257
  }
1895
2258
  async function explain(path, tool, pairs) {
1896
2259
  const pack = await loadPack(path);
1897
- const { capabilityForTool } = await import("capmark");
1898
- const capability = capabilityForTool(tool)?.id;
2260
+ const capability = capabilityOf(tool);
1899
2261
  const decision = evaluate(pack, {
1900
2262
  tool,
1901
2263
  ...capability === void 0 ? {} : { capability },
@@ -1920,7 +2282,7 @@ async function explain(path, tool, pairs) {
1920
2282
  * a key that arrived beside it is checked against nothing.
1921
2283
  */
1922
2284
  async function keygen(dir) {
1923
- const { generateKeypair, fingerprint: fingerprint$1 } = await import("./src-wG36kT6K.js");
2285
+ const { generateKeypair, fingerprint: fingerprint$1 } = await import("./src-2iJy5MZc.js");
1924
2286
  const { writeFileSync: writeFileSync$1 } = await import("node:fs");
1925
2287
  const keys = generateKeypair();
1926
2288
  const priv = join(dir, "prae-signing.pem");
@@ -1943,7 +2305,7 @@ async function keygen(dir) {
1943
2305
  async function keygenSealed(role, dir) {
1944
2306
  if (role !== "operator" && role !== "policy" && role !== "ledger") fail("--role is operator, policy or ledger");
1945
2307
  const stem = role === "ledger" ? "prae-signing" : role;
1946
- const { sealedKeypair, fingerprint: fingerprint$1 } = await import("./src-wG36kT6K.js");
2308
+ const { sealedKeypair, fingerprint: fingerprint$1 } = await import("./src-2iJy5MZc.js");
1947
2309
  const { writeFileSync: writeFileSync$1 } = await import("node:fs");
1948
2310
  const keys = sealedKeypair(process.env.PRAE_KEY_PASSPHRASE ?? await askHidden("passphrase: "));
1949
2311
  const pub = join(dir, `${stem}.pub.pem`);
@@ -1956,7 +2318,7 @@ async function keygenSealed(role, dir) {
1956
2318
  }
1957
2319
  /** A detached signature over a pack's bytes (PRA-107). */
1958
2320
  async function signPack(packPath, keyPath) {
1959
- const { signBytes, unsealPrivateKey, fingerprint: fingerprint$1 } = await import("./src-wG36kT6K.js");
2321
+ const { signBytes, unsealPrivateKey, fingerprint: fingerprint$1 } = await import("./src-2iJy5MZc.js");
1960
2322
  const { createPublicKey: createPublicKey$1 } = await import("node:crypto");
1961
2323
  const bytes = readFileSync(packPath);
1962
2324
  const parsed$1 = parsePack(bytes.toString("utf8"));
@@ -2001,7 +2363,7 @@ function askHidden(prompt) {
2001
2363
  });
2002
2364
  }
2003
2365
  async function receipt(path, seq, keyPath, out) {
2004
- const { Ledger: Ledger$1, makeReceipt, fingerprint: fingerprint$1 } = await import("./src-wG36kT6K.js");
2366
+ const { Ledger: Ledger$1, makeReceipt, fingerprint: fingerprint$1 } = await import("./src-2iJy5MZc.js");
2005
2367
  const { readFileSync: readFileSync$1 } = await import("node:fs");
2006
2368
  const { createPublicKey: createPublicKey$1 } = await import("node:crypto");
2007
2369
  const privateKey = readFileSync$1(keyPath, "utf8");
@@ -2031,7 +2393,7 @@ async function receipt(path, seq, keyPath, out) {
2031
2393
  * file and sets the exit code, so scripts can branch on it.
2032
2394
  */
2033
2395
  async function verifyReceiptCmd(path) {
2034
- const { checkReceipt } = await import("./receipt-check-CUuEjGHL.js");
2396
+ const { checkReceipt } = await import("./receipt-check-BtAJqyrX.js");
2035
2397
  let parsed$1;
2036
2398
  try {
2037
2399
  parsed$1 = JSON.parse(await readFile(path, "utf8"));
@@ -2062,7 +2424,7 @@ async function test(suitePath, packOverride, asJson) {
2062
2424
  } catch (error) {
2063
2425
  return fail(`${suitePath} is not valid JSON: ${error instanceof Error ? error.message : error}`);
2064
2426
  }
2065
- const { parseSuite, renderSuite, runSuite } = await import("./test-run-C8dHjIE2.js");
2427
+ const { parseSuite, renderSuite, runSuite } = await import("./test-run-DCprBW44.js");
2066
2428
  const suite = parseSuite(parsed$1);
2067
2429
  if (!suite.ok) {
2068
2430
  console.error(`prae: ${suitePath} is not a valid test suite`);
@@ -2072,8 +2434,7 @@ async function test(suitePath, packOverride, asJson) {
2072
2434
  const chosen = packOverride ?? (suite.suite.pack === void 0 ? void 0 : resolve(dirname(suitePath), suite.suite.pack));
2073
2435
  if (chosen === void 0) return fail("test needs a pack: set `pack` in the suite or pass --pack");
2074
2436
  const pack = await loadPack(chosen);
2075
- const { capabilityForTool } = await import("capmark");
2076
- const { scan } = await import("./src-DMoYsdfo.js");
2437
+ const { scan } = await import("./src-DIuEg7gi.js");
2077
2438
  const inspect = pack.rules.some((rule) => rule.when.secrets !== void 0) ? (path) => {
2078
2439
  try {
2079
2440
  return scan(readFileSync(path, "utf8"));
@@ -2082,7 +2443,7 @@ async function test(suitePath, packOverride, asJson) {
2082
2443
  }
2083
2444
  } : void 0;
2084
2445
  const result = runSuite(pack, suite.suite, {
2085
- capabilityFor: (tool) => capabilityForTool(tool)?.id,
2446
+ capabilityFor: capabilityOf,
2086
2447
  ...inspect === void 0 ? {} : { inspect }
2087
2448
  });
2088
2449
  if (asJson) console.log(JSON.stringify(result, null, 2));
@@ -2102,14 +2463,14 @@ async function verify$1(path, keyPath, checkpointPath) {
2102
2463
  const last = entries.at(-1);
2103
2464
  console.log(`${result.entries} entries · chain intact${last === void 0 ? "" : ` · seal ${seal(last)}`}`);
2104
2465
  if (checkpointPath !== void 0) {
2105
- const { checkCheckpoint } = await import("./checkpoint-BqJ8xwky.js");
2466
+ const { checkCheckpoint } = await import("./checkpoint-D7d6ZGXR.js");
2106
2467
  const publicKey = keyPath === void 0 ? void 0 : readFileSync(keyPath, "utf8");
2107
2468
  const verdict = checkCheckpoint(entries, JSON.parse(readFileSync(checkpointPath, "utf8")), publicKey);
2108
2469
  for (const line of verdict.lines) (verdict.ok ? console.log : console.error)(`checkpoint: ${line}`);
2109
2470
  if (!verdict.ok) process.exitCode = 1;
2110
2471
  }
2111
2472
  if (keyPath !== void 0) {
2112
- const { verifySignature: verifySignature$1, fingerprint: fingerprint$1 } = await import("./src-wG36kT6K.js");
2473
+ const { verifySignature: verifySignature$1, fingerprint: fingerprint$1 } = await import("./src-2iJy5MZc.js");
2113
2474
  const { readFileSync: readFileSync$1 } = await import("node:fs");
2114
2475
  const publicKey = readFileSync$1(keyPath, "utf8");
2115
2476
  let signed = 0;
@@ -2187,7 +2548,7 @@ async function report(path, out, packPath) {
2187
2548
  * that step is a proposal a human approves, and it starts from this list.
2188
2549
  */
2189
2550
  async function instructions(from, asJson) {
2190
- const { ingest } = await import("./src-CDYyZyeU.js");
2551
+ const { ingest } = await import("./src-DxJIOaP5.js");
2191
2552
  const result = ingest(from);
2192
2553
  if (asJson) {
2193
2554
  console.log(JSON.stringify(result, null, 2));
@@ -2223,7 +2584,7 @@ async function instructions(from, asJson) {
2223
2584
  * on purpose rather than something that happened to their policy.
2224
2585
  */
2225
2586
  async function propose(from, options) {
2226
- const [{ ingest }, { extract }] = await Promise.all([import("./src-CDYyZyeU.js"), import("./src-BlmsTGdY.js")]);
2587
+ const [{ ingest }, { extract }] = await Promise.all([import("./src-DxJIOaP5.js"), import("./src-DV83J9G2.js")]);
2227
2588
  const read = ingest(from);
2228
2589
  if (read.files.length === 0) {
2229
2590
  console.log(`No instruction files found at or above ${from}.`);
@@ -2283,9 +2644,9 @@ async function consoleOut(path, argv) {
2283
2644
  const instructionsDir = flag(argv, "instructions");
2284
2645
  if (instructionsDir !== void 0) {
2285
2646
  const [{ ingest }, { extract }, { renderInstructions }] = await Promise.all([
2286
- import("./src-CDYyZyeU.js"),
2287
- import("./src-BlmsTGdY.js"),
2288
- import("./instructions-DihPiiC2.js")
2647
+ import("./src-DxJIOaP5.js"),
2648
+ import("./src-DV83J9G2.js"),
2649
+ import("./instructions-Bcs2n2O6.js")
2289
2650
  ]);
2290
2651
  const read = ingest(instructionsDir);
2291
2652
  instructionsView = renderInstructions(data, read, extract(read.statements));
@@ -2333,11 +2694,11 @@ function bundledBaseline() {
2333
2694
  async function init(argv) {
2334
2695
  const packPath = flag(argv, "pack") ?? bundledBaseline();
2335
2696
  if (packPath === void 0) fail("init needs --pack <file>; no bundled baseline found");
2697
+ const pack = await loadPack(packPath);
2336
2698
  if (flag(argv, "pack") === void 0) {
2337
- console.log("No --pack given; mounting the built-in baseline (8 rules).");
2699
+ console.log(`No --pack given; mounting the built-in baseline (${pack.rules.length} rules).`);
2338
2700
  console.log(`See it with: prae check ${packPath}\n`);
2339
2701
  }
2340
- const pack = await loadPack(packPath);
2341
2702
  const source = await readFile(packPath, "utf8");
2342
2703
  const link = argv.includes("--link");
2343
2704
  const builtin = link ? { packFile: resolve(packPath) } : { pack: source };
@@ -2418,7 +2779,7 @@ async function chainMatrix(path, argv) {
2418
2779
  const catalogue = guardiansPath === void 0 ? [] : JSON.parse(readFileSync(guardiansPath, "utf8")).guardians;
2419
2780
  const guardianOf = (rule) => rule === void 0 ? void 0 : catalogue.find((g) => g.rules.includes(rule) || (g.armed ?? []).includes(rule))?.name;
2420
2781
  const keyPath = flag(argv, "key");
2421
- const { verifySignature: verifySignature$1 } = await import("./src-wG36kT6K.js");
2782
+ const { verifySignature: verifySignature$1 } = await import("./src-2iJy5MZc.js");
2422
2783
  const publicKey = keyPath === void 0 ? void 0 : readFileSync(keyPath, "utf8");
2423
2784
  const matrix = integrityMatrix(entries, pack, guardianOf).map((m) => {
2424
2785
  const entry = entries.find((e) => e.seq === m.seq);
@@ -2449,7 +2810,7 @@ async function chainMatrix(path, argv) {
2449
2810
  if (!chainOk || sigFailed) process.exitCode = 1;
2450
2811
  }
2451
2812
  async function checkpoint(path, keyPath, out) {
2452
- const { makeCheckpoint } = await import("./checkpoint-BqJ8xwky.js");
2813
+ const { makeCheckpoint } = await import("./checkpoint-D7d6ZGXR.js");
2453
2814
  let ledger;
2454
2815
  try {
2455
2816
  ledger = Ledger.open(path);
@@ -2470,7 +2831,7 @@ async function checkpoint(path, keyPath, out) {
2470
2831
  }
2471
2832
  }
2472
2833
  async function exportLedger(path, options) {
2473
- const { toOcsf, ndjson, sendToHec } = await import("./ocsf-DF9cMiQJ.js");
2834
+ const { toOcsf, ndjson, sendToHec } = await import("./ocsf-DwExfPqP.js");
2474
2835
  const { createRequire: createRequire$1 } = await import("node:module");
2475
2836
  const { version } = createRequire$1(import.meta.url)("../package.json");
2476
2837
  let ledger;
@@ -2601,7 +2962,7 @@ async function main(argv) {
2601
2962
  }
2602
2963
  case "init":
2603
2964
  if (rest[0] === "claude-code") {
2604
- const { initClaudeCode, stableNode } = await import("./init-DRd-IRR6.js");
2965
+ const { initClaudeCode, stableNode } = await import("./init-DvzCuemh.js");
2605
2966
  try {
2606
2967
  return initClaudeCode(rest.slice(1), {
2607
2968
  cwd: process.cwd(),
@@ -2616,7 +2977,7 @@ async function main(argv) {
2616
2977
  return init(rest);
2617
2978
  case "hook": {
2618
2979
  if (rest[0] !== "claude-code") fail("hook takes claude-code");
2619
- const { runHook } = await import("./hook-Gll2spTP.js");
2980
+ const { runHook } = await import("./hook-C7LGEIs2.js");
2620
2981
  return runHook(rest.slice(1));
2621
2982
  }
2622
2983
  case "--help":
@@ -0,0 +1,7 @@
1
+ import "./src-CZYvvEn5.js";
2
+ import "./src-CuiWC80o.js";
3
+ import { t as main } from "./cli-BTVEk0Fh.js";
4
+ import "./facts-DkTRO2p0.js";
5
+ import "./views-endiZu1Z.js";
6
+
7
+ export { main };