calllint 1.1.0 → 1.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (2) hide show
  1. package/dist/index.js +2787 -252
  2. package/package.json +5 -1
package/dist/index.js CHANGED
@@ -1,7 +1,7 @@
1
1
  #!/usr/bin/env node
2
2
 
3
3
  // src/index.ts
4
- import { readFileSync as readFileSync17 } from "node:fs";
4
+ import { readFileSync as readFileSync20 } from "node:fs";
5
5
  import { execFileSync } from "node:child_process";
6
6
 
7
7
  // src/args.ts
@@ -69,6 +69,8 @@ COMMANDS
69
69
  scan --agent <type> Discover and scan a specific agent (cursor, claude-code, claude-desktop, vscode, windsurf)
70
70
  action inspect <f> Preflight a planned external action (calllint.action.v0)
71
71
  inbox inspect <f> Preflight a normalized agent inbox event
72
+ evidence import <f> Import a third-party scanner report as evidence (no re-scoring)
73
+ trust prepare <target> Read-only Trust Gateway preview: resolve to a digest-pinned identity
72
74
  diagnostics [target] Emit editor/agent-host diagnostics JSON (calllint.diagnostics.v0)
73
75
  baseline [target] Record the approved risk surface as a baseline
74
76
  approve Record the repo-wide capability surface as approved state (L4)
@@ -104,6 +106,10 @@ SCAN OPTIONS
104
106
  --badge Emit a shields.io endpoint badge JSON (SAFE/REVIEW/UNKNOWN/BLOCK)
105
107
  --receipt Generate a cryptographically-verifiable audit receipt (for CI/compliance)
106
108
  --receipt-out <f> Receipt output path (default: calllint-receipt.json)
109
+ --evidence <file> Attach an external scanner report (e.g. SkillSpector JSON/SARIF)
110
+ as supporting evidence; shown side-by-side in a joint Trust
111
+ Packet. Never re-scored, never changes the CallLint verdict.
112
+ --evidence-format <fmt> Force evidence format (json|sarif); default auto-detect
107
113
  --policy <file> Use a policy file (default: built-in defaults)
108
114
  --stdin Read config JSON from stdin
109
115
  --ci Exit non-zero per policy (BLOCK=30, UNKNOWN=20, REVIEW=10 if enabled)
@@ -128,6 +134,7 @@ EXAMPLES
128
134
  calllint scan .cursor/mcp.json --markdown
129
135
  calllint scan .cursor/mcp.json --badge > calllint-badge.json
130
136
  calllint scan .cursor/mcp.json --receipt # Generate audit receipt
137
+ calllint scan .cursor/mcp.json --evidence skillspector-report.json # Joint Trust Packet
131
138
  calllint receipt verify calllint-receipt.json # Verify receipt integrity
132
139
  calllint action inspect payment.json
133
140
  calllint inbox inspect gmail-reply.normalized.json
@@ -286,11 +293,11 @@ function applyPolicy(rawVerdict, serverName, blockingFindings, policy, now) {
286
293
  if (rawVerdict !== "BLOCK") {
287
294
  return { verdict: rawVerdict, changed: false };
288
295
  }
289
- const override = policy.overrides.find(
296
+ const override2 = policy.overrides.find(
290
297
  (o) => o.target === serverName && isOverrideActive(o, now)
291
298
  );
292
- if (!override) return { verdict: rawVerdict, changed: false };
293
- const allowed = new Set(override.allow ?? []);
299
+ if (!override2) return { verdict: rawVerdict, changed: false };
300
+ const allowed = new Set(override2.allow ?? []);
294
301
  const blockingSymbols = new Set(
295
302
  blockingFindings.filter((f) => f.blocker).map((f) => f.symbol)
296
303
  );
@@ -299,7 +306,7 @@ function applyPolicy(rawVerdict, serverName, blockingFindings, policy, now) {
299
306
  return {
300
307
  verdict: "REVIEW",
301
308
  changed: true,
302
- note: `Policy decision: override for "${serverName}" (expires ${override.expiresAt}${override.owner ? `, owner: ${override.owner}` : ""}) \u2014 ${override.reason}`
309
+ note: `Policy decision: override for "${serverName}" (expires ${override2.expiresAt}${override2.owner ? `, owner: ${override2.owner}` : ""}) \u2014 ${override2.reason}`
303
310
  };
304
311
  }
305
312
  function shouldFailCi(verdict, policy) {
@@ -307,17 +314,6 @@ function shouldFailCi(verdict, policy) {
307
314
  return policy.ci.failOn.includes(verdict);
308
315
  }
309
316
 
310
- // ../../packages/core/src/options.ts
311
- function resolveScanOptions(opts) {
312
- return {
313
- policy: opts?.policy ?? defaultPolicy(),
314
- now: opts?.now ?? 0,
315
- generatedAt: opts?.generatedAt ?? "1970-01-01T00:00:00.000Z",
316
- extraFindings: opts?.extraFindings ?? {},
317
- surfaces: opts?.surfaces ?? []
318
- };
319
- }
320
-
321
317
  // ../../packages/types/src/verdict.ts
322
318
  var VERDICT_SEVERITY = {
323
319
  SAFE: 0,
@@ -492,6 +488,210 @@ var VERDICT_NEXT_ACTION = {
492
488
  UNKNOWN: "gather_more_evidence"
493
489
  };
494
490
 
491
+ // ../../packages/types/src/authority.ts
492
+ var AUTHORITY_SCHEMA_VERSION = "calllint.authority.v0";
493
+
494
+ // ../../packages/types/src/trustDecision.ts
495
+ var DECISION_SCHEMA_VERSION = "calllint.decision.v0";
496
+
497
+ // ../../packages/types/src/applyResult.ts
498
+ var APPLY_RESULT_SCHEMA = "calllint.apply-result.v1";
499
+
500
+ // ../../packages/types/src/trustGateway.ts
501
+ var TRUST_PREPARATION_SCHEMA = "calllint.trust-preparation.v0";
502
+
503
+ // ../../packages/fingerprint/src/hashJson.ts
504
+ import { createHash } from "node:crypto";
505
+ function sha256(input) {
506
+ return "sha256:" + createHash("sha256").update(input, "utf8").digest("hex");
507
+ }
508
+ function stableStringify(value) {
509
+ return JSON.stringify(sortValue(value));
510
+ }
511
+ function sortValue(value) {
512
+ if (Array.isArray(value)) return value.map(sortValue);
513
+ if (value && typeof value === "object") {
514
+ const out = {};
515
+ for (const key of Object.keys(value).sort()) {
516
+ out[key] = sortValue(value[key]);
517
+ }
518
+ return out;
519
+ }
520
+ return value;
521
+ }
522
+ function hashJson(value) {
523
+ return sha256(stableStringify(value));
524
+ }
525
+
526
+ // ../../packages/fingerprint/src/computeFingerprints.ts
527
+ function computeFingerprints(input) {
528
+ const { server, binding, symbols, findingIds } = input;
529
+ const targetSpec = {
530
+ command: binding.declaredCommand,
531
+ args: binding.declaredArgs,
532
+ envKeys: [...server.envKeys].sort(),
533
+ remoteUrl: binding.remoteUrl
534
+ };
535
+ const packageSpec = binding.packageName !== void 0 ? `${binding.packageName}@${binding.packageVersionSpec ?? ""}` : void 0;
536
+ const riskSurface = {
537
+ symbols: [...symbols].sort(),
538
+ findingIds: [...findingIds].sort()
539
+ };
540
+ const sourceText = server.instructions ?? (server.providedTools.length > 0 ? JSON.stringify(server.providedTools) : void 0);
541
+ const toolMetadata = server.providedTools.length > 0 ? server.providedTools : void 0;
542
+ const fp = {
543
+ configHash: hashJson(server.raw),
544
+ targetSpecHash: hashJson(targetSpec),
545
+ riskSurfaceHash: hashJson(riskSurface)
546
+ };
547
+ if (packageSpec !== void 0) fp.packageSpecHash = sha256(packageSpec);
548
+ if (sourceText !== void 0) fp.sourceHash = sha256(sourceText);
549
+ if (toolMetadata !== void 0) fp.toolMetadataHash = hashJson(toolMetadata);
550
+ return fp;
551
+ }
552
+
553
+ // ../../packages/policy/src/decideOverAuthority.ts
554
+ function reasonCodeFor(c) {
555
+ switch (c.pattern) {
556
+ case "privilege-escalation":
557
+ case "auto-exec-bypass":
558
+ return "SHELL_OR_DOCKER_EXECUTION";
559
+ case "sensitive-file-read":
560
+ return "SECRET_IN_WORKSPACE_CONFIG";
561
+ case "data-exfil":
562
+ return "UNKNOWN_REMOTE";
563
+ case "hidden-override":
564
+ return "PROMPT_METADATA_INSTRUCTION";
565
+ case "messaging-financial":
566
+ return c.resource === "financial" ? "MONEY_OR_PAYMENT_CAPABILITY" : "MESSAGING_OR_EMAIL_SEND";
567
+ default:
568
+ break;
569
+ }
570
+ if (c.resource === "financial") return "MONEY_OR_PAYMENT_CAPABILITY";
571
+ if (c.resource === "message") return "MESSAGING_OR_EMAIL_SEND";
572
+ if (c.resource === "secret") return "SECRET_IN_WORKSPACE_CONFIG";
573
+ if (c.resource === "network") return "UNKNOWN_REMOTE";
574
+ if (c.resource === "process") return "SHELL_OR_DOCKER_EXECUTION";
575
+ if (c.resource === "filesystem") return "BROAD_FILESYSTEM_ACCESS";
576
+ return "EXTERNAL_MUTATION_UNKNOWN";
577
+ }
578
+ function baseVerdict(c) {
579
+ switch (c.approvalRequirement) {
580
+ case "block":
581
+ return "BLOCK";
582
+ case "review":
583
+ return "REVIEW";
584
+ default:
585
+ return "SAFE";
586
+ }
587
+ }
588
+ function policyFloor(code, policy) {
589
+ const d = policy.defaults;
590
+ const knob = {
591
+ UNKNOWN_REMOTE: d.unknownSource,
592
+ UNPINNED_PACKAGE: d.unpinnedPackage,
593
+ BROAD_FILESYSTEM_ACCESS: d.broadFilesystemAccess,
594
+ SHELL_OR_DOCKER_EXECUTION: d.arbitraryCommandExecution,
595
+ PROMPT_METADATA_INSTRUCTION: d.promptPoisoning,
596
+ EXTERNAL_MUTATION_UNKNOWN: d.externalMutation,
597
+ MONEY_OR_PAYMENT_CAPABILITY: d.financialAction
598
+ }[code];
599
+ if (knob === "deny") return "BLOCK";
600
+ if (knob === "warn") return "REVIEW";
601
+ return "SAFE";
602
+ }
603
+ function moreSevere(a, b) {
604
+ return VERDICT_SEVERITY[a] >= VERDICT_SEVERITY[b] ? a : b;
605
+ }
606
+ function evidenceFloor(evidence) {
607
+ let verdict = "SAFE";
608
+ let note = null;
609
+ for (const e of evidence) {
610
+ if (e.completeness === "degraded" || e.completeness === "failed") {
611
+ if (VERDICT_SEVERITY.UNKNOWN > VERDICT_SEVERITY[verdict]) {
612
+ verdict = "UNKNOWN";
613
+ note = `external evidence from ${e.provider} is ${e.completeness} \u2014 cannot yield SAFE`;
614
+ }
615
+ } else if (e.completeness === "partial") {
616
+ if (VERDICT_SEVERITY.REVIEW > VERDICT_SEVERITY[verdict]) {
617
+ verdict = "REVIEW";
618
+ note = `external evidence from ${e.provider} is partial`;
619
+ }
620
+ }
621
+ }
622
+ return { verdict, note };
623
+ }
624
+ function decideOverAuthority(input) {
625
+ const { authority, policy } = input;
626
+ const evidence = input.evidence ?? [];
627
+ const reasons = authority.capabilities.map((c) => {
628
+ const code = reasonCodeFor(c);
629
+ const contributes = moreSevere(baseVerdict(c), policyFloor(code, policy));
630
+ return { code, evidenceSource: c.evidenceSource, contributes };
631
+ });
632
+ const unknowns = [...authority.unknowns];
633
+ const contributions = reasons.map((r) => r.contributes);
634
+ if (authority.subject.artifactDigest === null) {
635
+ contributions.push("UNKNOWN");
636
+ unknowns.push("decision made over an unpinned artifact (no digest)");
637
+ }
638
+ if (authority.completeness === "partial") {
639
+ contributions.push("UNKNOWN");
640
+ unknowns.push("authority manifest is partial \u2014 capabilities may be under-counted");
641
+ }
642
+ const ev = evidenceFloor(evidence);
643
+ if (ev.verdict !== "SAFE") {
644
+ contributions.push(ev.verdict);
645
+ if (ev.note) unknowns.push(ev.note);
646
+ }
647
+ const verdict = mostSevereVerdict(contributions);
648
+ const order = (c) => REASON_CODES.indexOf(c);
649
+ const sortedReasons = dedupeReasons(reasons).sort(
650
+ (a, b) => order(a.code) - order(b.code) || cmp(a.evidenceSource, b.evidenceSource) || VERDICT_SEVERITY[b.contributes] - VERDICT_SEVERITY[a.contributes]
651
+ );
652
+ const completeness = authority.completeness === "partial" || unknowns.length > 0 || ev.verdict === "UNKNOWN" ? "partial" : "complete";
653
+ const evidenceDigests = [...new Set(evidence.map((e) => e.rawReportDigest))].sort();
654
+ const sealed = {
655
+ schema: DECISION_SCHEMA_VERSION,
656
+ artifactDigest: authority.subject.artifactDigest,
657
+ authorityDigest: authority.digest,
658
+ policyDigest: hashJson(policy),
659
+ evidenceDigests,
660
+ verdict,
661
+ reasons: sortedReasons,
662
+ requiredApprovals: [...authority.approval.required].sort(),
663
+ unknowns: [...new Set(unknowns)].sort(),
664
+ completeness
665
+ };
666
+ return { ...sealed, digest: hashJson(sealed) };
667
+ }
668
+ function dedupeReasons(reasons) {
669
+ const byKey = /* @__PURE__ */ new Map();
670
+ for (const r of reasons) {
671
+ const k = `${r.code}|${r.evidenceSource}`;
672
+ const prev = byKey.get(k);
673
+ if (!prev || VERDICT_SEVERITY[r.contributes] > VERDICT_SEVERITY[prev.contributes]) {
674
+ byKey.set(k, r);
675
+ }
676
+ }
677
+ return [...byKey.values()];
678
+ }
679
+ function cmp(a, b) {
680
+ return a < b ? -1 : a > b ? 1 : 0;
681
+ }
682
+
683
+ // ../../packages/core/src/options.ts
684
+ function resolveScanOptions(opts) {
685
+ return {
686
+ policy: opts?.policy ?? defaultPolicy(),
687
+ now: opts?.now ?? 0,
688
+ generatedAt: opts?.generatedAt ?? "1970-01-01T00:00:00.000Z",
689
+ extraFindings: opts?.extraFindings ?? {},
690
+ surfaces: opts?.surfaces ?? [],
691
+ evidence: opts?.evidence ?? []
692
+ };
693
+ }
694
+
495
695
  // ../../packages/resolver/src/npmSpec.ts
496
696
  var PACKAGE_RUNNERS = /* @__PURE__ */ new Set(["npx", "pnpm", "yarn", "bunx", "uvx", "pipx"]);
497
697
  var SHELL_COMMANDS = /* @__PURE__ */ new Set([
@@ -612,6 +812,58 @@ function resolveRuntimeBinding(server) {
612
812
  };
613
813
  }
614
814
 
815
+ // ../../packages/resolver/src/resolveArtifactIdentity.ts
816
+ function digestTree(entries) {
817
+ const sorted = [...entries].sort((a, b) => a.path < b.path ? -1 : a.path > b.path ? 1 : 0);
818
+ const canonical = sorted.map((e) => `${e.path}\0${e.content.length}\0${e.content}`).join("\n");
819
+ return sha256(canonical);
820
+ }
821
+ var LOCAL_TYPES = /* @__PURE__ */ new Set(["dir", "file", "mcp-config"]);
822
+ function resolveArtifactIdentity(input) {
823
+ const reasons = [...input.resolutionReasons ?? []];
824
+ let digest = null;
825
+ if (input.entries && input.entries.length > 0) {
826
+ digest = digestTree(input.entries);
827
+ } else if (typeof input.content === "string") {
828
+ digest = sha256(input.content);
829
+ } else {
830
+ reasons.push("no fetched bytes available to digest");
831
+ }
832
+ let resolvedRef = input.resolvedRef ?? null;
833
+ if (!resolvedRef) {
834
+ if (LOCAL_TYPES.has(input.sourceType) && digest) {
835
+ resolvedRef = `content:${digest}`;
836
+ } else {
837
+ resolvedRef = null;
838
+ if (input.sourceType === "git" || input.sourceType === "npm") {
839
+ reasons.push(
840
+ `remote ${input.sourceType} target could not be pinned to an immutable ref (offline or unresolved)`
841
+ );
842
+ }
843
+ }
844
+ }
845
+ let resolution;
846
+ if (resolvedRef && digest) {
847
+ resolution = "resolved";
848
+ } else if (digest || resolvedRef) {
849
+ resolution = "partial";
850
+ } else {
851
+ resolution = "unresolved";
852
+ }
853
+ const identity = {
854
+ schema: "calllint.artifact.v1",
855
+ sourceType: input.sourceType,
856
+ source: input.source,
857
+ requestedRef: input.requestedRef ?? null,
858
+ resolvedRef,
859
+ digest,
860
+ resolvedAt: input.resolvedAt,
861
+ resolution
862
+ };
863
+ if (reasons.length > 0) identity.resolutionReasons = reasons;
864
+ return identity;
865
+ }
866
+
615
867
  // ../../packages/static-analyzer/src/detectors/unpinnedPackage.ts
616
868
  function detectUnpinnedPackage(ctx) {
617
869
  const { binding } = ctx;
@@ -1611,6 +1863,301 @@ function analyzeDocumentSurfaces(surfaces) {
1611
1863
  ];
1612
1864
  }
1613
1865
 
1866
+ // ../../packages/static-analyzer/src/instructionAuthority.ts
1867
+ var RULES = [
1868
+ // 1. privilege-escalation → execute × process (irreversible, must be approved)
1869
+ esc(/\bsudo\s+\S/),
1870
+ esc(/\brun(?:ning)?\s+as\s+(?:root|administrator|admin|superuser|super\s?user)\b/),
1871
+ esc(/\bas\s+root\b/),
1872
+ esc(/\b(?:disable|bypass|turn\s+off)\s+(?:the\s+)?sandbox\b/),
1873
+ esc(/\bescalate\s+privileg/),
1874
+ esc(/\bwith\s+root\s+privileg/),
1875
+ // 2. auto-exec-bypass → execute × process (removing the approval gate is the grant)
1876
+ bypass(/\bwithout\s+(?:asking|confirmation|prompting|permission|approval)\b/),
1877
+ bypass(/\bauto[-\s]?(?:approve|approving|run|execute|confirm)\b/),
1878
+ bypass(/\b(?:run|execute)\s+automatically\b/),
1879
+ bypass(/\bautomatically\s+(?:run|execute|approve)\b/),
1880
+ bypass(/\bskip\s+(?:the\s+)?(?:confirmation|approval|prompt)\b/),
1881
+ bypass(/\bdon['’]?t\s+ask\b/),
1882
+ bypass(/\bdo\s+not\s+ask\b/),
1883
+ bypass(/\bno\s+confirmation\s+(?:needed|required)\b/),
1884
+ // 3. sensitive-file-read → read × secret (concrete paths are near-zero FP)
1885
+ secretRead(/(?:^|[\s"'`(/~])\.ssh\b/, "high"),
1886
+ secretRead(/\bid_(?:rsa|ed25519|ecdsa|dsa)\b/, "high"),
1887
+ secretRead(/(?:^|[\s"'`(/])\.env\b/, "high"),
1888
+ secretRead(/\.aws[/\\]credentials\b/, "high"),
1889
+ secretRead(/\baws\s+credentials\b/, "high"),
1890
+ secretRead(/\.git-credentials\b/, "high"),
1891
+ secretRead(/\/etc\/(?:passwd|shadow)\b/, "high"),
1892
+ secretRead(/\bprivate\s+key\b/, "medium"),
1893
+ secretRead(/\bread\b[^.\n]{0,30}\b(?:secret|credential|api[-\s]?key|password)s?\b/, "medium"),
1894
+ // 4. data-exfil → send × network (destination pulled off the line)
1895
+ exfil(/\bexfiltrat/, "high"),
1896
+ exfil(/\b(?:send|upload|post|transmit)\b[^.\n]{0,50}\bto\s+https?:\/\/\S+/, "high"),
1897
+ exfil(/\b(?:send|upload|post)\b[^.\n]{0,30}\b(?:the\s+)?(?:contents?|file|data|output|workspace)\b[^.\n]{0,30}\bto\b/, "medium"),
1898
+ // 5a. messaging-financial (messaging) → send × message
1899
+ messaging(/\bsend\b[^.\n]{0,20}\b(?:an?\s+)?(?:e[-\s]?mail|message|sms|text)\b/),
1900
+ messaging(/\bpost\b[^.\n]{0,20}\bto\s+(?:slack|discord|teams|telegram)\b/),
1901
+ // 5b. messaging-financial (financial) → spend × financial (irreversible, must be approved)
1902
+ financial(/\bmake\b[^.\n]{0,20}\ba?\s*payment\b/),
1903
+ financial(/\btransfer\b[^.\n]{0,20}\bfunds?\b/),
1904
+ financial(/\b(?:charge|bill)\b[^.\n]{0,20}\b(?:the\s+)?(?:card|account|customer)\b/),
1905
+ financial(/\bwire\b[^.\n]{0,20}\b(?:money|funds|payment)\b/),
1906
+ financial(/\bsend\b[^.\n]{0,20}\b(?:money|funds|payment)\b/),
1907
+ // 6. hidden-override (phrase half) → mutate × agent. The obfuscated-character
1908
+ // half is handled separately via findHiddenContent (see extractLine).
1909
+ override(/\bignore\s+(?:all\s+)?(?:previous|prior|the\s+above)\s+instructions\b/),
1910
+ override(/\bdisregard\s+(?:all\s+)?(?:previous|prior)\s+instructions\b/),
1911
+ override(/\boverride\s+(?:the\s+)?system\s+prompt\b/),
1912
+ override(/\bdo\s+not\s+(?:tell|inform|notify)\s+the\s+user\b/),
1913
+ override(/\bwithout\s+(?:telling|informing)\s+the\s+user\b/)
1914
+ ];
1915
+ function ci(re) {
1916
+ return re.flags.includes("i") ? re : new RegExp(re.source, re.flags + "i");
1917
+ }
1918
+ function esc(rawTest) {
1919
+ const test = ci(rawTest);
1920
+ return {
1921
+ test,
1922
+ pattern: "privilege-escalation",
1923
+ action: "execute",
1924
+ resource: "process",
1925
+ mutability: "mutating",
1926
+ reversibility: "irreversible",
1927
+ approvalRequirement: "block",
1928
+ confidence: "high"
1929
+ };
1930
+ }
1931
+ function bypass(rawTest) {
1932
+ const test = ci(rawTest);
1933
+ return {
1934
+ test,
1935
+ pattern: "auto-exec-bypass",
1936
+ action: "execute",
1937
+ resource: "process",
1938
+ mutability: "mutating",
1939
+ reversibility: "irreversible",
1940
+ approvalRequirement: "block",
1941
+ confidence: "high"
1942
+ };
1943
+ }
1944
+ function secretRead(rawTest, confidence) {
1945
+ const test = ci(rawTest);
1946
+ return {
1947
+ test,
1948
+ pattern: "sensitive-file-read",
1949
+ action: "read",
1950
+ resource: "secret",
1951
+ mutability: "read-only",
1952
+ reversibility: "n/a",
1953
+ approvalRequirement: "review",
1954
+ confidence
1955
+ };
1956
+ }
1957
+ function exfil(rawTest, confidence) {
1958
+ const test = ci(rawTest);
1959
+ return {
1960
+ test,
1961
+ pattern: "data-exfil",
1962
+ action: "send",
1963
+ resource: "network",
1964
+ mutability: "mutating",
1965
+ reversibility: "irreversible",
1966
+ approvalRequirement: "block",
1967
+ confidence,
1968
+ extractDestination: true
1969
+ };
1970
+ }
1971
+ function messaging(rawTest) {
1972
+ const test = ci(rawTest);
1973
+ return {
1974
+ test,
1975
+ pattern: "messaging-financial",
1976
+ action: "send",
1977
+ resource: "message",
1978
+ mutability: "mutating",
1979
+ reversibility: "irreversible",
1980
+ approvalRequirement: "review",
1981
+ confidence: "high"
1982
+ };
1983
+ }
1984
+ function financial(rawTest) {
1985
+ const test = ci(rawTest);
1986
+ return {
1987
+ test,
1988
+ pattern: "messaging-financial",
1989
+ action: "spend",
1990
+ resource: "financial",
1991
+ mutability: "mutating",
1992
+ reversibility: "irreversible",
1993
+ approvalRequirement: "block",
1994
+ confidence: "high"
1995
+ };
1996
+ }
1997
+ function override(rawTest) {
1998
+ const test = ci(rawTest);
1999
+ return {
2000
+ test,
2001
+ pattern: "hidden-override",
2002
+ action: "mutate",
2003
+ resource: "agent",
2004
+ mutability: "mutating",
2005
+ reversibility: "irreversible",
2006
+ approvalRequirement: "block",
2007
+ confidence: "medium"
2008
+ };
2009
+ }
2010
+ function extractHost(line) {
2011
+ const m = line.match(/https?:\/\/([^\s/"')]+)/i);
2012
+ return m ? m[1] : null;
2013
+ }
2014
+ function capabilityFrom(t, evidenceSource, line) {
2015
+ return {
2016
+ action: t.action,
2017
+ resource: t.resource,
2018
+ scope: null,
2019
+ destination: t.extractDestination ? extractHost(line) : null,
2020
+ mutability: t.mutability,
2021
+ reversibility: t.reversibility,
2022
+ monetaryLimit: null,
2023
+ approvalRequirement: t.approvalRequirement,
2024
+ evidenceSource,
2025
+ confidence: t.confidence,
2026
+ completeness: "complete",
2027
+ pattern: t.pattern
2028
+ };
2029
+ }
2030
+ function keyOf(c) {
2031
+ return [c.pattern, c.action, c.resource, c.destination ?? "", c.evidenceSource].join("|");
2032
+ }
2033
+ function extractInstructionAuthority(surfaces) {
2034
+ const seen = /* @__PURE__ */ new Map();
2035
+ for (const surface of surfaces) {
2036
+ const lines = surface.text.split(/\r?\n/);
2037
+ for (let i = 0; i < lines.length; i++) {
2038
+ const line = lines[i];
2039
+ const evidenceSource = `${surface.path}:${i + 1}`;
2040
+ for (const rule2 of RULES) {
2041
+ if (rule2.test.test(line)) {
2042
+ const cap = capabilityFrom(rule2, evidenceSource, line);
2043
+ const k = keyOf(cap);
2044
+ if (!seen.has(k)) seen.set(k, cap);
2045
+ }
2046
+ }
2047
+ const smuggling = findHiddenContent(line).filter(
2048
+ (c) => c !== "embedded HTML/XML comment"
2049
+ );
2050
+ if (smuggling.length > 0) {
2051
+ const cap = {
2052
+ action: "mutate",
2053
+ resource: "agent",
2054
+ scope: null,
2055
+ destination: null,
2056
+ mutability: "mutating",
2057
+ reversibility: "irreversible",
2058
+ monetaryLimit: null,
2059
+ approvalRequirement: "block",
2060
+ evidenceSource,
2061
+ confidence: "high",
2062
+ // structural, near-zero FP
2063
+ completeness: "complete",
2064
+ pattern: "hidden-override"
2065
+ };
2066
+ const k = keyOf(cap);
2067
+ if (!seen.has(k)) seen.set(k, cap);
2068
+ }
2069
+ }
2070
+ }
2071
+ return sortCapabilities([...seen.values()]);
2072
+ }
2073
+ function sortCapabilities(caps) {
2074
+ return caps.sort((a, b) => {
2075
+ return cmp2(a.pattern ?? "", b.pattern ?? "") || cmp2(a.evidenceSource, b.evidenceSource) || cmp2(a.action, b.action) || cmp2(a.resource, b.resource) || cmp2(a.destination ?? "", b.destination ?? "");
2076
+ });
2077
+ }
2078
+ function cmp2(a, b) {
2079
+ return a < b ? -1 : a > b ? 1 : 0;
2080
+ }
2081
+
2082
+ // ../../packages/static-analyzer/src/configAuthority.ts
2083
+ var SECRET_HINTS2 = [
2084
+ "TOKEN",
2085
+ "SECRET",
2086
+ "PASSWORD",
2087
+ "PASSWD",
2088
+ "API_KEY",
2089
+ "APIKEY",
2090
+ "ACCESS_KEY",
2091
+ "PRIVATE_KEY",
2092
+ "CREDENTIAL",
2093
+ "AUTH",
2094
+ "SESSION"
2095
+ ];
2096
+ function looksSecret2(key) {
2097
+ const upper = key.toUpperCase();
2098
+ return SECRET_HINTS2.some((h2) => upper.includes(h2));
2099
+ }
2100
+ function hostOf2(url) {
2101
+ try {
2102
+ return new URL(url).host || url;
2103
+ } catch {
2104
+ return url;
2105
+ }
2106
+ }
2107
+ function deriveConfigCapabilities(server) {
2108
+ const caps = [];
2109
+ if (server.command) {
2110
+ caps.push({
2111
+ action: "execute",
2112
+ resource: "process",
2113
+ scope: server.command,
2114
+ destination: null,
2115
+ mutability: "mutating",
2116
+ reversibility: "irreversible",
2117
+ monetaryLimit: null,
2118
+ // Running a local process is expected for a stdio server; the policy, not
2119
+ // the manifest, decides whether that warrants review. Mark it review-worthy
2120
+ // only where the command itself is unknown — here just record it as routine.
2121
+ approvalRequirement: "none",
2122
+ evidenceSource: "server.command",
2123
+ confidence: "high",
2124
+ completeness: "complete"
2125
+ });
2126
+ }
2127
+ if (server.url) {
2128
+ caps.push({
2129
+ action: "connect",
2130
+ resource: "network",
2131
+ scope: hostOf2(server.url),
2132
+ destination: hostOf2(server.url),
2133
+ mutability: "mutating",
2134
+ reversibility: "irreversible",
2135
+ monetaryLimit: null,
2136
+ approvalRequirement: "review",
2137
+ evidenceSource: "server.url",
2138
+ confidence: "high",
2139
+ completeness: "complete"
2140
+ });
2141
+ }
2142
+ for (const key of server.envKeys) {
2143
+ if (!looksSecret2(key)) continue;
2144
+ caps.push({
2145
+ action: "read",
2146
+ resource: "secret",
2147
+ scope: key,
2148
+ destination: null,
2149
+ mutability: "read-only",
2150
+ reversibility: "n/a",
2151
+ monetaryLimit: null,
2152
+ approvalRequirement: "review",
2153
+ evidenceSource: `server.env.${key}`,
2154
+ confidence: "medium",
2155
+ completeness: "complete"
2156
+ });
2157
+ }
2158
+ return sortCapabilities(caps);
2159
+ }
2160
+
1614
2161
  // ../../packages/risk-engine/src/computeRiskClass.ts
1615
2162
  function computeRiskClass(findings, binding) {
1616
2163
  const classes = findings.map((f) => f.riskClass);
@@ -1734,56 +2281,6 @@ function assessServer(findings, binding) {
1734
2281
  };
1735
2282
  }
1736
2283
 
1737
- // ../../packages/fingerprint/src/hashJson.ts
1738
- import { createHash } from "node:crypto";
1739
- function sha256(input) {
1740
- return "sha256:" + createHash("sha256").update(input, "utf8").digest("hex");
1741
- }
1742
- function stableStringify(value) {
1743
- return JSON.stringify(sortValue(value));
1744
- }
1745
- function sortValue(value) {
1746
- if (Array.isArray(value)) return value.map(sortValue);
1747
- if (value && typeof value === "object") {
1748
- const out = {};
1749
- for (const key of Object.keys(value).sort()) {
1750
- out[key] = sortValue(value[key]);
1751
- }
1752
- return out;
1753
- }
1754
- return value;
1755
- }
1756
- function hashJson(value) {
1757
- return sha256(stableStringify(value));
1758
- }
1759
-
1760
- // ../../packages/fingerprint/src/computeFingerprints.ts
1761
- function computeFingerprints(input) {
1762
- const { server, binding, symbols, findingIds } = input;
1763
- const targetSpec = {
1764
- command: binding.declaredCommand,
1765
- args: binding.declaredArgs,
1766
- envKeys: [...server.envKeys].sort(),
1767
- remoteUrl: binding.remoteUrl
1768
- };
1769
- const packageSpec = binding.packageName !== void 0 ? `${binding.packageName}@${binding.packageVersionSpec ?? ""}` : void 0;
1770
- const riskSurface = {
1771
- symbols: [...symbols].sort(),
1772
- findingIds: [...findingIds].sort()
1773
- };
1774
- const sourceText = server.instructions ?? (server.providedTools.length > 0 ? JSON.stringify(server.providedTools) : void 0);
1775
- const toolMetadata = server.providedTools.length > 0 ? server.providedTools : void 0;
1776
- const fp = {
1777
- configHash: hashJson(server.raw),
1778
- targetSpecHash: hashJson(targetSpec),
1779
- riskSurfaceHash: hashJson(riskSurface)
1780
- };
1781
- if (packageSpec !== void 0) fp.packageSpecHash = sha256(packageSpec);
1782
- if (sourceText !== void 0) fp.sourceHash = sha256(sourceText);
1783
- if (toolMetadata !== void 0) fp.toolMetadataHash = hashJson(toolMetadata);
1784
- return fp;
1785
- }
1786
-
1787
2284
  // ../../packages/core/src/summarize.ts
1788
2285
  function summarize(name, verdict, a, policyApplied) {
1789
2286
  const symbolText = a.symbols.length > 0 ? a.symbols.map((s) => RISK_SYMBOL_LABEL[s]).join(", ") : "no risk surface observed";
@@ -1997,16 +2494,16 @@ function readString(c) {
1997
2494
  while (c.i < c.text.length) {
1998
2495
  const ch = advance(c);
1999
2496
  if (ch === "\\") {
2000
- const esc2 = advance(c);
2001
- if (esc2 === "n") out += "\n";
2002
- else if (esc2 === "t") out += " ";
2003
- else if (esc2 === "r") out += "\r";
2004
- else if (esc2 === "u") {
2497
+ const esc3 = advance(c);
2498
+ if (esc3 === "n") out += "\n";
2499
+ else if (esc3 === "t") out += " ";
2500
+ else if (esc3 === "r") out += "\r";
2501
+ else if (esc3 === "u") {
2005
2502
  let hex = "";
2006
2503
  for (let k = 0; k < 4; k++) hex += advance(c);
2007
2504
  const code = Number.parseInt(hex, 16);
2008
2505
  out += Number.isNaN(code) ? "" : String.fromCharCode(code);
2009
- } else out += esc2;
2506
+ } else out += esc3;
2010
2507
  } else if (ch === '"') {
2011
2508
  break;
2012
2509
  } else {
@@ -2179,7 +2676,7 @@ function enrichEvidencePositions(reports, positions) {
2179
2676
  }
2180
2677
 
2181
2678
  // ../../packages/core/src/scanConfig.ts
2182
- function aggregate(configPath, reports, generatedAt) {
2679
+ function aggregate(configPath, reports, generatedAt, evidence) {
2183
2680
  const counts = { SAFE: 0, REVIEW: 0, BLOCK: 0, UNKNOWN: 0 };
2184
2681
  for (const r of reports) counts[r.verdict]++;
2185
2682
  const verdict = reports.length === 0 ? "UNKNOWN" : mostSevereVerdict(reports.map((r) => r.verdict));
@@ -2192,18 +2689,23 @@ function aggregate(configPath, reports, generatedAt) {
2192
2689
  counts,
2193
2690
  reports,
2194
2691
  diagnostics: [],
2195
- generatedAt
2692
+ generatedAt,
2693
+ // Additive projection (ADR 0034). Attached only when the CLI imported evidence
2694
+ // via `scan --evidence`; omitted otherwise so default scan output is
2695
+ // byte-identical (the offline corpus never attaches evidence). Never re-scored:
2696
+ // the `verdict` above is unaffected by these findings.
2697
+ ...evidence.length > 0 ? { evidence } : {}
2196
2698
  };
2197
2699
  }
2198
2700
  function scanParsed(parsed, opts) {
2199
- const { generatedAt, surfaces } = resolveScanOptions(opts);
2701
+ const { generatedAt, surfaces, evidence } = resolveScanOptions(opts);
2200
2702
  const reports = parsed.servers.map(
2201
2703
  (server) => scanServer({ server, targetKind: parsed.kind }, opts)
2202
2704
  );
2203
2705
  enrichEvidencePositions(reports, parsed.positions);
2204
2706
  const surfaceReport = scanDocumentSurfaces(surfaces, parsed.configPath, generatedAt);
2205
2707
  if (surfaceReport) reports.push(surfaceReport);
2206
- return aggregate(parsed.configPath, reports, generatedAt);
2708
+ return aggregate(parsed.configPath, reports, generatedAt, evidence);
2207
2709
  }
2208
2710
  function scanConfigText(text, configPath, opts) {
2209
2711
  return scanParsed(parseConfigText(text, configPath), opts);
@@ -3138,32 +3640,221 @@ function verifyReceipt(input) {
3138
3640
  return { valid: errors.length === 0, errors, signed };
3139
3641
  }
3140
3642
 
3141
- // ../../packages/report-renderer/src/style.ts
3142
- var DEFAULT_STYLE = { emoji: true };
3143
- var NO_EMOJI_STYLE = { emoji: false };
3144
- function verdictTag(verdict, style) {
3145
- return style.emoji ? VERDICT_CLI_SYMBOL[verdict] : VERDICT_TEXT_SYMBOL[verdict];
3146
- }
3147
- function symbolTag(symbol, style) {
3148
- return style.emoji ? `${RISK_SYMBOL_EMOJI[symbol]} ${symbol}` : symbol;
3149
- }
3150
- function symbolList(symbols, style) {
3151
- if (symbols.length === 0) return "\u2014";
3152
- return symbols.map((s) => symbolTag(s, style)).join(" ");
3153
- }
3154
-
3155
- // ../../packages/report-renderer/src/renderJson.ts
3156
- function renderJson(summary) {
3157
- return JSON.stringify(summary, null, 2);
3643
+ // ../../packages/core/src/gateway/prepare.ts
3644
+ var COMPLETENESS_RANK = {
3645
+ complete: 0,
3646
+ partial: 1,
3647
+ degraded: 2,
3648
+ failed: 3
3649
+ };
3650
+ function worstCompleteness(evidence) {
3651
+ let worst = "complete";
3652
+ for (const e of evidence) {
3653
+ if (COMPLETENESS_RANK[e.completeness] > COMPLETENESS_RANK[worst]) worst = e.completeness;
3654
+ }
3655
+ return worst;
3158
3656
  }
3159
-
3160
- // ../../packages/report-renderer/src/renderTerminal.ts
3161
- function renderFindingLine(f) {
3162
- const lines = [];
3163
- const flag = f.blocker ? "[BLOCKER] " : "";
3164
- lines.push(` \u2022 ${flag}${f.title} (${f.id}, ${f.mode.toLowerCase()}, confidence ${f.confidence})`);
3165
- if (f.evidence.length > 0) {
3166
- const e = f.evidence[0];
3657
+ function prepare(input) {
3658
+ const { artifact, preparedAt } = input;
3659
+ const evidence = input.evidence ?? [];
3660
+ const notes = [];
3661
+ let state;
3662
+ if (artifact.resolution === "resolved") {
3663
+ if (evidence.length === 0) {
3664
+ state = "PLAN_READY";
3665
+ notes.push("no external evidence attached (optional); decision will rely on CallLint's own analysis");
3666
+ } else {
3667
+ const worst = worstCompleteness(evidence);
3668
+ switch (worst) {
3669
+ case "complete":
3670
+ state = "PLAN_READY";
3671
+ break;
3672
+ case "partial":
3673
+ state = "EVIDENCE_PARTIAL";
3674
+ notes.push("attached evidence is partial \u2014 gaps remain; not a clean pass");
3675
+ break;
3676
+ default:
3677
+ state = "EVIDENCE_FAILED";
3678
+ notes.push(
3679
+ "attached evidence is degraded or failed \u2014 fail-closed; a degraded external scan never reads as a pass"
3680
+ );
3681
+ break;
3682
+ }
3683
+ }
3684
+ } else if (artifact.resolution === "partial") {
3685
+ state = "FETCH_REJECTED";
3686
+ notes.push(
3687
+ "artifact could not be fully pinned (missing immutable ref or bytes); not a verified target"
3688
+ );
3689
+ } else {
3690
+ state = "RESOLUTION_FAILED";
3691
+ notes.push("artifact could not be resolved to an immutable, digested identity");
3692
+ }
3693
+ for (const r of artifact.resolutionReasons ?? []) notes.push(r);
3694
+ for (const e of evidence) {
3695
+ for (const reason of e.degradedReasons) {
3696
+ notes.push(`evidence[${e.provider}]: ${reason}`);
3697
+ }
3698
+ }
3699
+ const authority = input.authority ?? null;
3700
+ if (authority) {
3701
+ if (state === "PLAN_READY") state = "AUTHORITY_NORMALIZED";
3702
+ const caps = authority.capabilities.length;
3703
+ notes.push(
3704
+ caps === 0 ? "authority normalized: no elevated capabilities detected" : `authority normalized: ${caps} capabilit${caps === 1 ? "y" : "ies"}${authority.approval.required.length > 0 ? `, approvals required: ${authority.approval.required.join(", ")}` : ""}`
3705
+ );
3706
+ if (authority.completeness === "partial") {
3707
+ notes.push("authority manifest is partial \u2014 some sources were not fully normalized");
3708
+ }
3709
+ }
3710
+ const decision = input.decision ?? null;
3711
+ if (decision) {
3712
+ if (state === "AUTHORITY_NORMALIZED") {
3713
+ state = decision.verdict === "UNKNOWN" ? "POLICY_UNKNOWN" : "DECIDED";
3714
+ }
3715
+ notes.push(
3716
+ `policy decision: ${decision.verdict}` + (decision.reasons.length > 0 ? ` (${[...new Set(decision.reasons.map((r) => r.code))].join(", ")})` : "")
3717
+ );
3718
+ if (decision.verdict === "UNKNOWN") {
3719
+ notes.push("verdict UNKNOWN \u2014 insufficient evidence; fail-closed, never a pass");
3720
+ }
3721
+ }
3722
+ const plan = input.plan ?? null;
3723
+ if (plan) {
3724
+ if (state === "DECIDED") {
3725
+ state = "PLAN_READY";
3726
+ notes.push(
3727
+ `install plan computed for host "${plan.host}" (tier ${plan.tier}): ${plan.operations.length} operation(s), ${plan.rollback.length} rollback op(s); verdict ${decision?.verdict ?? "?"} \u2014 NOT applied (apply is a separate approved step)`
3728
+ );
3729
+ if (decision && decision.verdict !== "SAFE") {
3730
+ notes.push(
3731
+ `verdict ${decision.verdict} \u2014 applying this plan would require an explicit, digest-bound approval`
3732
+ );
3733
+ }
3734
+ } else {
3735
+ notes.push("install plan present but the gateway did not reach a confident decision \u2014 plan not activated");
3736
+ }
3737
+ }
3738
+ return {
3739
+ schema: TRUST_PREPARATION_SCHEMA,
3740
+ artifact,
3741
+ evidence: input.evidence ? [...evidence] : null,
3742
+ authority,
3743
+ decision,
3744
+ plan,
3745
+ state,
3746
+ notes,
3747
+ preparedAt
3748
+ };
3749
+ }
3750
+ function prepareExitCode(prep) {
3751
+ switch (prep.state) {
3752
+ case "PLAN_READY":
3753
+ return prep.decision?.verdict === "REVIEW" ? 10 : prep.decision?.verdict === "BLOCK" || prep.decision?.verdict === "UNKNOWN" ? 20 : 0;
3754
+ case "AUTHORITY_NORMALIZED":
3755
+ return 0;
3756
+ case "DECIDED":
3757
+ return prep.decision?.verdict === "REVIEW" ? 10 : prep.decision?.verdict === "BLOCK" ? 20 : 0;
3758
+ case "FETCH_REJECTED":
3759
+ case "EVIDENCE_PARTIAL":
3760
+ return 10;
3761
+ default:
3762
+ return 20;
3763
+ }
3764
+ }
3765
+
3766
+ // ../../packages/core/src/gateway/authority.ts
3767
+ function approvalLabel(c) {
3768
+ if (c.approvalRequirement === "none") return null;
3769
+ switch (c.pattern) {
3770
+ case "privilege-escalation":
3771
+ return "privilege-escalation";
3772
+ case "auto-exec-bypass":
3773
+ return "unattended-execution";
3774
+ case "data-exfil":
3775
+ return "data-exfiltration";
3776
+ case "hidden-override":
3777
+ return "instruction-override";
3778
+ case "sensitive-file-read":
3779
+ return "secret-read";
3780
+ case "messaging-financial":
3781
+ return c.resource === "financial" ? "financial-action" : "external-messaging";
3782
+ default:
3783
+ break;
3784
+ }
3785
+ if (c.action === "connect" && c.resource === "network") return "external-network-access";
3786
+ if (c.action === "read" && c.resource === "secret") return "secret-read";
3787
+ if (c.action === "execute" && c.resource === "process") return "process-exec";
3788
+ return "review";
3789
+ }
3790
+ function deriveLimits(caps) {
3791
+ let spendPerCall = null;
3792
+ for (const c of caps) {
3793
+ if (c.action === "spend" && typeof c.monetaryLimit === "number") {
3794
+ spendPerCall = spendPerCall === null ? c.monetaryLimit : Math.min(spendPerCall, c.monetaryLimit);
3795
+ }
3796
+ }
3797
+ return { spendPerCall, spendTotal: null };
3798
+ }
3799
+ function buildAuthorityManifest(input) {
3800
+ const servers = input.servers ?? [];
3801
+ const surfaces = input.surfaces ?? [];
3802
+ const configCaps = servers.flatMap((s) => deriveConfigCapabilities(s));
3803
+ const instructionCaps = extractInstructionAuthority(surfaces);
3804
+ const capabilities = sortCapabilities([...configCaps, ...instructionCaps]);
3805
+ const unknowns = [];
3806
+ if (input.artifactDigest === null) {
3807
+ unknowns.push(
3808
+ "artifact did not resolve to a digest \u2014 authority is over an unpinned target"
3809
+ );
3810
+ }
3811
+ for (const s of surfaces) {
3812
+ if (s.truncated) {
3813
+ unknowns.push(`surface truncated at size cap \u2014 authority past the cap is unread: ${s.path}`);
3814
+ }
3815
+ }
3816
+ unknowns.sort();
3817
+ const required = [...new Set(capabilities.map(approvalLabel).filter((l) => l !== null))].sort();
3818
+ const anyPartial = capabilities.some((c) => c.completeness === "partial");
3819
+ const completeness = anyPartial || unknowns.length > 0 ? "partial" : "complete";
3820
+ const sealed = {
3821
+ schema: AUTHORITY_SCHEMA_VERSION,
3822
+ subject: { artifactDigest: input.artifactDigest },
3823
+ capabilities,
3824
+ limits: deriveLimits(capabilities),
3825
+ approval: { required },
3826
+ unknowns,
3827
+ completeness
3828
+ };
3829
+ return { ...sealed, digest: hashJson(sealed) };
3830
+ }
3831
+
3832
+ // ../../packages/report-renderer/src/style.ts
3833
+ var DEFAULT_STYLE = { emoji: true };
3834
+ var NO_EMOJI_STYLE = { emoji: false };
3835
+ function verdictTag(verdict, style) {
3836
+ return style.emoji ? VERDICT_CLI_SYMBOL[verdict] : VERDICT_TEXT_SYMBOL[verdict];
3837
+ }
3838
+ function symbolTag(symbol, style) {
3839
+ return style.emoji ? `${RISK_SYMBOL_EMOJI[symbol]} ${symbol}` : symbol;
3840
+ }
3841
+ function symbolList(symbols, style) {
3842
+ if (symbols.length === 0) return "\u2014";
3843
+ return symbols.map((s) => symbolTag(s, style)).join(" ");
3844
+ }
3845
+
3846
+ // ../../packages/report-renderer/src/renderJson.ts
3847
+ function renderJson(summary) {
3848
+ return JSON.stringify(summary, null, 2);
3849
+ }
3850
+
3851
+ // ../../packages/report-renderer/src/renderTerminal.ts
3852
+ function renderFindingLine(f) {
3853
+ const lines = [];
3854
+ const flag = f.blocker ? "[BLOCKER] " : "";
3855
+ lines.push(` \u2022 ${flag}${f.title} (${f.id}, ${f.mode.toLowerCase()}, confidence ${f.confidence})`);
3856
+ if (f.evidence.length > 0) {
3857
+ const e = f.evidence[0];
3167
3858
  const ev = e.value ?? e.snippet ?? e.key ?? e.path ?? "";
3168
3859
  lines.push(` evidence: ${e.key ?? e.type}${ev ? ` = ${ev}` : ""}`);
3169
3860
  }
@@ -3641,7 +4332,7 @@ function renderDiagnostics(summary) {
3641
4332
  var LOGO_REPORT_BASE64 = "iVBORw0KGgoAAAANSUhEUgAAADAAAAAwCAYAAABXAvmHAAAACXBIWXMAAAPoAAAD6AG1e1JrAAAKdElEQVRo3u1aeXAT1xlf35cs2bJ8yJYtbGxjG+rgkJCjdaBMSYBAhstAhwSYZDJ105CGnBwNSUlKmibNJDR0GJrJQELb1C2kgQ6dtJwFAgFyTAB7tVpprdvyjW3Zkvb4+r2nAzVtZ/qHhO2ZLrOjZd/bx/f73u87Hwzz/+u/X8AwSeSezABSgJmVNikFJ7+m3PINnLq8NfbdpKAN+f1So8kTVAarNbfM9YWqpDB2bFJo36Iu32tP14EDb4umYh9510YpNYFBoHCp5JfTGTc5GA103vU9qbN5gWTHZ0tB1WYydhLnTDgQRKCTYeHZwurHhLRC4HVTZD/LKX7eqpiLqmQhTQfmgsofRUC8yDDJE0H2pFhacAVVzwtIGXNOsTxy6qwS7LRDwCLAyJlPFbNKL9sQBKub8sI3QY+jiwxp0W5o0Jq0Vb91JuUBX2CURk6eUZQxPwgzm8E64w6QfT4EcV7hdZWSMxnnaCvbWH2tLrxOctt4eiizvnYpn1NmcTLZ0Nl4t0hoIw8Ng+2ehWBKzgdTSj7Yvn0vSIPXwc/xSmfTd0Qy15xTKphLqlvGxR44pjqD05St4LJLT9iIsabrlK4nt0iKKIG/gwNhxl3A4m6YMouAyyqmQKwNs8HfzoIiy+B9drskZOgUG6MGNlt/ukNTtlJgjJkRWiZM8IjhsZqKSkd26ag7txycyx6Sxr6+KgNeA3v3AZdXAaaMQmBRONs9C8A+bwk+5wKXWQycphz697xHpsLYlQ7ZuWKd5FYZwJZTOnxFa6yPUCrhQepqwxwVarjdOWsukUUeu/g52Ocuhg4mh1KGRYpY62+HgLUTgjYHCA130HcsjpE5tuaFMPrZZUDUsvPO+fhOY/bMfzAnocGOGNpVhkmnQapyRjHL5PW65iwC0TeqWOtvQyHSwZxrAKHudvBu2gqixwsKoiO36O2G7mdeAKF+NnA4h8y11MwEyR9QXN8lwNU9jurZBqoc/DcSZtQR7VhLan9mYzLAu/WnstjTA3y+EThVKZiYPOh99ZeUIooogixJoRufydX72lu4E3k4twy9VRUEPV3g3fYyBroM6CyZ9mosTePOffQaq0xq/UY2u/SQBenAl9UqYm8fDOz+DQqeg9ovA1N6IQlW4L/aHgKBwtMbnwNo3GZdVWiOuhy/yYaBXXtA7OsHS9k0wDUVdAqHTGrDRlOO4ftxoVPsAi5V6YnBHAO4NBWSY/5SOSjYqMfh9bVgwgDFZeuRHuWU696Nz4Z34Yb2vU9uRqpkUZpxOaXApRcBX1ILAZaDINqK/d7lijvPKA2gUbtVZefiYtCxAFDIY47iGhj78usA0ajv9DmwVjZSo+VyysCcpUffrgdTqo6+l3B3ojaAWrZWN9ExKnxWCZiRRqbUArBMvQV8p85SkL4LlwLOiukITncm7gDY5PwTbkMDKIoieZ/ahtrMoO6SahS1b47eKGCKFoaP/h0i18jx0xgLtFR4c+xc/Jas0cFk4g5tQXc2JnVV34pz884lAID2hAcBoOeQnA+sQaqokMsGYDE1MKFGuQgQNaFRJvT94u0oAPJMYwHSw0xAkLn4DZusprGBjDnvXwXSyEgYgDb+ALhk7fGu8umE05Jr9cOotTSwNy+A7m0vI40QBBEQeW0mAiXlg/exp6MAPPhMhDRrKugcAp5NzYPuLTvAhq64A9Mq18p1IPl8CKApQQBStcc9BgQQDEquNY9AOw73hV2m728nwLVkDfCFU4FL09Ixx+LllP/kcixdDejf0QMVgLmwGsdWwcgnx0Ou9ZXX4RqORQFMRQApCQGgO+Ypa8AdCErOZWvBgh5E7OkNGaoSEpVE3uHDR6HvzXdg8Hd/jBrx4IcHoe+NXTD88VE6h16yEjLw7h7qyQgoaXRUclfeQmzoH3FLKyIg0IN87CmZBuLwsOR+5HEYOnQkJAcJWOQOBqNAIhf5+7+9wz90bjjQkWvooyPg2tAK4uCgRByFKa3gaNwARMK6Ob14X7fGCGJvr+h94nnwPPpEFIASFiTi+0mAE3v7QwCIlvv76Ts5Zh6N0MFQjPC0PgUejB3Brm7Jie4VM9j347kDtGrC7sIrXiwVx65cE/t/tZfy1nfsVEhoFGT0/EXoeW47OBauBB557Fr7aNQG3A8/DpYpjeBY1ALdOIfMVXAXqP0cO03X6seIPnrlmuQk9bOmfEes8uLSZbAXVa/txEg6+Ps/yaNnL1BD9az7AYjuLnAtexDQRsIZpwbHMsFxf0sUgHPFQ/guhXoelmSsGLnJN2JXN7jXt9K1fGc+xbUPSS70UILOuCZuvaTINrprmuo5JjtI0gQ0XgUbVmh800CovRWFyqL+ncN8KOTX1eBGcFE3ijbTQaqwPGM08NGUu3om5lR1YM4n1OxTvEgljCFjjsoZjXE34qstLel8Uv4XHqysJL9fdi1eTYXgsopionEJBUGEJVSJXCRWkDqAi43auZTrdA0X8UC4pnvGncAzmq/a2tpSEtKs6sw17PRgkPKdvyhdR9dI6MDjTtBELnKHE7rBd/dHAQwe+AO+09A0+l/SDnVo7lDbRzBy4RLSB8tTjWFn3Pj/TRo5jQ0zeSY74EHeYt6iWGuaaEJGEjMiUGyiNvrZ51EA/mtsKN3OihGegMFvrUhBspZ7XSuYmWy/raJuVkJKy2gxk1lyyIUC+M0WaeDd9yn/I9QgGiYFfGdTM8iBQDQOEJ9P0g5TeBdCFCqn3w7iGmMmXnJhEtiZrT+YsLIyohGLvr7ZwqhE16r1GJFkxd58HzVaCgK1TCjRu+O1G0Eu7OtJJCZj1F7wJt/Ysd0CkqQ4V64HsqZJX9Oc0MI+srCQa9jrRm0OHfmrGOgwoUBYTpKuQwYWKUVVtBt3oyKjDQsQnS7gS2swmSsM0U2Fu3ilHa4fPioS7pM1b1pXQjA2lAipOqvDUA+BLq98/YMPqUZNaOD9b+8JaZ9QCHtAhELkOdp2SSlAKqlg8L0DEPR2yzZcA9fi2yvq9DelBR9tnxfXLRYYteyYt4SoWOnZvpMGMOL/ScsksgOR9GH08lcYDzbS4qVn80ukpaLY5j2gWBm1xJfULEq49v9TfmTRVm4lkdOz4YeU6N4fb6ZpgQljQ9+bu6NeqP8dLPyx3CRjXeE6AYMbthixKNJWbhmXE5yoUedX/NqDgmCCR5Ob7md+QrVMAtfQwcMwgqUloRfpBXk3PgfhOUE3juPBx56EtVL+1xPItpaWFHSJBzwoEO5AkNLppZ+HmldV36IdOlJxEdqQMe/TRHgV8JryAyfnzCGJ4vidZEZAnGTmpPKaiv0ERJhOysDe/cCma7C8xP7PW9Swla7WTVTzGL0/uDyLnl6O/zEsEYBQABhI4jXGXU4U0HnfckkKBOThv3yCacKfMVzIsmvZWnSXGAfyp+wG3LUJdYYcFibUucazMIFRyY7GuxW/zS4FPF2S/ba5ikAMVjd1W8R+JuJhX1LEO+Hpy1ILo+l1ldaBGzsZPJPbz+qmrposJ5UhF1vaWMum6y5g5L0kGKfXT7bDbkonL54jcNWz1XFPj2/GRQz7xTDXYYIcqU7I/63yT1NyvbeodjVcAAAAAElFTkSuQmCC";
3642
4333
 
3643
4334
  // ../../packages/report-renderer/src/renderHtml.ts
3644
- function esc(value) {
4335
+ function esc2(value) {
3645
4336
  return String(value ?? "").replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;").replace(/"/g, "&quot;").replace(/'/g, "&#39;");
3646
4337
  }
3647
4338
  var VERDICT_COLOR = {
@@ -3651,50 +4342,50 @@ var VERDICT_COLOR = {
3651
4342
  UNKNOWN: "#6e7781"
3652
4343
  };
3653
4344
  function badge(verdict) {
3654
- return `<span class="badge" style="background:${VERDICT_COLOR[verdict]}">${esc(verdict)}</span>`;
4345
+ return `<span class="badge" style="background:${VERDICT_COLOR[verdict]}">${esc2(verdict)}</span>`;
3655
4346
  }
3656
4347
  function findingRow(f) {
3657
4348
  const ev = f.evidence.map((e) => {
3658
- const loc = e.path ? ` (${esc(e.path)}${e.line ? `:${e.line}` : ""})` : "";
4349
+ const loc = e.path ? ` (${esc2(e.path)}${e.line ? `:${e.line}` : ""})` : "";
3659
4350
  const detail = e.value ?? e.snippet ?? "";
3660
- return `<li><code>${esc(e.type)}</code> ${esc(e.key ?? "")}${detail ? ` = ${esc(detail)}` : ""}${loc}</li>`;
4351
+ return `<li><code>${esc2(e.type)}</code> ${esc2(e.key ?? "")}${detail ? ` = ${esc2(detail)}` : ""}${loc}</li>`;
3661
4352
  }).join("");
3662
4353
  return `
3663
- <tr class="sev-${esc(f.severity)}">
3664
- <td>${f.blocker ? "\u26D4 " : ""}${esc(f.title)}</td>
3665
- <td><code>${esc(f.id)}</code></td>
3666
- <td>${esc(RISK_SYMBOL_LABEL[f.symbol])} <small>(${esc(f.symbol)})</small></td>
3667
- <td>${esc(f.riskClass)}</td>
3668
- <td>${esc(f.severity)}</td>
3669
- <td>${esc(f.mode)}</td>
4354
+ <tr class="sev-${esc2(f.severity)}">
4355
+ <td>${f.blocker ? "\u26D4 " : ""}${esc2(f.title)}</td>
4356
+ <td><code>${esc2(f.id)}</code></td>
4357
+ <td>${esc2(RISK_SYMBOL_LABEL[f.symbol])} <small>(${esc2(f.symbol)})</small></td>
4358
+ <td>${esc2(f.riskClass)}</td>
4359
+ <td>${esc2(f.severity)}</td>
4360
+ <td>${esc2(f.mode)}</td>
3670
4361
  <td>
3671
- <div class="impact">${esc(f.impact)}</div>
3672
- <div class="fix"><strong>Fix:</strong> ${esc(f.fix)}</div>
4362
+ <div class="impact">${esc2(f.impact)}</div>
4363
+ <div class="fix"><strong>Fix:</strong> ${esc2(f.fix)}</div>
3673
4364
  ${f.evidence.length ? `<ul class="evidence">${ev}</ul>` : ""}
3674
- ${f.falsePositiveNote ? `<div class="fp"><em>${esc(f.falsePositiveNote)}</em></div>` : ""}
4365
+ ${f.falsePositiveNote ? `<div class="fp"><em>${esc2(f.falsePositiveNote)}</em></div>` : ""}
3675
4366
  </td>
3676
4367
  </tr>`;
3677
4368
  }
3678
4369
  function serverCard(r) {
3679
- const symbols = r.symbols.map((s) => `<span class="sym">${esc(RISK_SYMBOL_LABEL[s])}</span>`).join(" ");
4370
+ const symbols = r.symbols.map((s) => `<span class="sym">${esc2(RISK_SYMBOL_LABEL[s])}</span>`).join(" ");
3680
4371
  const findings = r.findings.length ? `<table class="findings">
3681
4372
  <thead><tr><th>Finding</th><th>Rule</th><th>Symbol</th><th>Class</th><th>Severity</th><th>Mode</th><th>Detail</th></tr></thead>
3682
4373
  <tbody>${r.findings.map(findingRow).join("")}</tbody>
3683
4374
  </table>` : `<p class="none">No findings.</p>`;
3684
4375
  return `
3685
4376
  <section class="server">
3686
- <h2>${badge(r.verdict)} ${esc(r.target.name)}</h2>
4377
+ <h2>${badge(r.verdict)} ${esc2(r.target.name)}</h2>
3687
4378
  <p class="meta">
3688
- Class <strong>${esc(r.riskClass)}</strong> ${esc(RISK_CLASS_LABEL[r.riskClass])}
3689
- \xB7 confidence ${esc(r.confidence)}
3690
- \xB7 reproducibility ${esc(r.reproducibility.level)}
4379
+ Class <strong>${esc2(r.riskClass)}</strong> ${esc2(RISK_CLASS_LABEL[r.riskClass])}
4380
+ \xB7 confidence ${esc2(r.confidence)}
4381
+ \xB7 reproducibility ${esc2(r.reproducibility.level)}
3691
4382
  </p>
3692
4383
  <p class="symbols">${symbols || '<span class="sym none">no risk symbols</span>'}</p>
3693
- <p class="summary">${esc(r.summary)}</p>
4384
+ <p class="summary">${esc2(r.summary)}</p>
3694
4385
  ${findings}
3695
- <p class="policy">autonomous use: <strong>${esc(r.policy.autonomousUse)}</strong>
3696
- \xB7 manual approval: <strong>${esc(r.policy.manualApproval)}</strong>
3697
- \xB7 sandbox: <strong>${esc(r.policy.sandbox)}</strong></p>
4386
+ <p class="policy">autonomous use: <strong>${esc2(r.policy.autonomousUse)}</strong>
4387
+ \xB7 manual approval: <strong>${esc2(r.policy.manualApproval)}</strong>
4388
+ \xB7 sandbox: <strong>${esc2(r.policy.sandbox)}</strong></p>
3698
4389
  </section>`;
3699
4390
  }
3700
4391
  var LOGO_SRC = `data:image/png;base64,${LOGO_REPORT_BASE64}`;
@@ -3737,7 +4428,7 @@ function renderHtml(summary) {
3737
4428
  <head>
3738
4429
  <meta charset="utf-8">
3739
4430
  <meta name="viewport" content="width=device-width, initial-scale=1">
3740
- <title>CallLint report \u2014 ${esc(summary.configPath)}</title>
4431
+ <title>CallLint report \u2014 ${esc2(summary.configPath)}</title>
3741
4432
  <style>${STYLE}</style>
3742
4433
  </head>
3743
4434
  <body>
@@ -3746,7 +4437,7 @@ function renderHtml(summary) {
3746
4437
  <img class="brand-mark" src="${LOGO_SRC}" width="40" height="40" alt="CallLint">
3747
4438
  <div>
3748
4439
  <h1>CallLint report ${badge(summary.verdict)}</h1>
3749
- <div class="sub">${esc(summary.configPath)} \xB7 generated ${esc(summary.generatedAt)}</div>
4440
+ <div class="sub">${esc2(summary.configPath)} \xB7 generated ${esc2(summary.generatedAt)}</div>
3750
4441
  </div>
3751
4442
  </div>
3752
4443
  </header>
@@ -3762,6 +4453,275 @@ function renderHtml(summary) {
3762
4453
  </html>`;
3763
4454
  }
3764
4455
 
4456
+ // ../../packages/report-renderer/src/renderTrustPacket.ts
4457
+ var COMPLETENESS_HINT = {
4458
+ complete: "complete",
4459
+ partial: "partial (incomplete \u2014 treat as inconclusive)",
4460
+ degraded: "degraded (not a pass)",
4461
+ failed: "failed (not a pass)"
4462
+ };
4463
+ function topProviderSeverity(ev) {
4464
+ const sevs = ev.findings.map(
4465
+ (f) => f && typeof f === "object" && "providerSeverity" in f ? String(f.providerSeverity) : void 0
4466
+ ).filter((s) => Boolean(s));
4467
+ return sevs[0];
4468
+ }
4469
+ function whyTheyDiffer(ev, authorityVerdict) {
4470
+ if (ev.completeness === "failed" || ev.completeness === "degraded") {
4471
+ return "Why they differ: the content scan is " + COMPLETENESS_HINT[ev.completeness] + ", so it carries no weight here; CallLint's authority verdict stands on its own evidence.";
4472
+ }
4473
+ if (ev.findings.length === 0 && authorityVerdict !== "SAFE") {
4474
+ return "Why they differ: the content scan found nothing malicious, but CallLint judges the granted authority itself too broad \u2014 clean code can still request unsafe capabilities.";
4475
+ }
4476
+ return "Why they differ: the two tools answer different questions \u2014 content risk (the scanner) vs. whether the requested authority is acceptable (CallLint). Neither overrides the other.";
4477
+ }
4478
+ function renderTrustPacket(summary, toolVersion, style = DEFAULT_STYLE) {
4479
+ const evidence = summary.evidence;
4480
+ if (!evidence || evidence.length === 0) return "";
4481
+ const lines = ["", "Joint Trust Packet", "\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500"];
4482
+ lines.push("Content scan");
4483
+ for (const ev of evidence) {
4484
+ const sev = topProviderSeverity(ev);
4485
+ const findingsLabel = ev.findings.length === 0 ? "no findings" : `${ev.findings.length} finding${ev.findings.length === 1 ? "" : "s"}` + (sev ? ` (top severity: ${sev})` : "");
4486
+ lines.push(
4487
+ ` ${ev.provider} ${ev.providerVersion} scanMode: ${ev.scanMode} completeness: ${COMPLETENESS_HINT[ev.completeness]}`
4488
+ );
4489
+ lines.push(` ${findingsLabel}`);
4490
+ lines.push(` raw report digest: ${ev.rawReportDigest}`);
4491
+ for (const reason of ev.degradedReasons) {
4492
+ lines.push(` degraded: ${reason}`);
4493
+ }
4494
+ }
4495
+ lines.push("Authority scan");
4496
+ lines.push(
4497
+ ` CallLint ${toolVersion} ${verdictTag(summary.verdict, style)} (${summary.publicVerdictLabel})`
4498
+ );
4499
+ lines.push(whyTheyDiffer(evidence[0], summary.verdict));
4500
+ return lines.join("\n");
4501
+ }
4502
+
4503
+ // ../../packages/evidence/src/types.ts
4504
+ var EVIDENCE_SCHEMA_VERSION = "calllint.evidence-provider.v0";
4505
+
4506
+ // ../../packages/evidence/src/providers/skillspector.ts
4507
+ function pinnedVersion(raw) {
4508
+ const tool = raw.tool;
4509
+ const commit = typeof raw.commit === "string" && raw.commit || tool && typeof tool.commit === "string" && tool.commit || "";
4510
+ if (commit) return `git:${commit}`;
4511
+ const version = typeof raw.version === "string" && raw.version || tool && typeof tool.version === "string" && tool.version || "";
4512
+ return version || void 0;
4513
+ }
4514
+ function parseSkillSpectorJson(parsed) {
4515
+ const raw = asObject(parsed, "SkillSpector JSON root");
4516
+ const degradedReasons = [];
4517
+ const usedLlm = raw.llm_used === true || raw.llmUsed === true;
4518
+ const scanMode = usedLlm ? "llm" : "static";
4519
+ const rawFindings = arrayOf(raw.findings) ?? arrayOf(raw.results) ?? [];
4520
+ const findings = rawFindings.map((f) => {
4521
+ const o = asObject(f, "SkillSpector finding");
4522
+ return {
4523
+ providerRuleId: String(o.rule_id ?? o.ruleId ?? o.id ?? "unknown"),
4524
+ providerSeverity: String(o.severity ?? o.level ?? "unknown"),
4525
+ message: typeof o.message === "string" ? o.message : void 0,
4526
+ locations: extractLocations(o)
4527
+ };
4528
+ });
4529
+ let completenessHint;
4530
+ const status = String(raw.status ?? raw.scan_status ?? "").toLowerCase();
4531
+ if (status && status !== "complete" && status !== "completed" && status !== "success") {
4532
+ degradedReasons.push(`SkillSpector reported status "${status}"`);
4533
+ completenessHint = status === "partial" ? "partial" : "degraded";
4534
+ }
4535
+ if (raw.partial === true) {
4536
+ degradedReasons.push("SkillSpector reported a partial scan");
4537
+ completenessHint = completenessHint ?? "partial";
4538
+ }
4539
+ if (raw.degraded === true) {
4540
+ degradedReasons.push("SkillSpector reported a degraded scan");
4541
+ completenessHint = "degraded";
4542
+ }
4543
+ const coverage = (arrayOf(raw.categories) ?? arrayOf(raw.coverage) ?? []).map((c) => String(c));
4544
+ return {
4545
+ provider: "skillspector",
4546
+ providerVersion: pinnedVersion(raw),
4547
+ scanMode,
4548
+ coverage,
4549
+ findings,
4550
+ degradedReasons,
4551
+ completenessHint
4552
+ };
4553
+ }
4554
+ function parseSkillSpectorSarif(parsed) {
4555
+ const raw = asObject(parsed, "SARIF root");
4556
+ const runs = arrayOf(raw.runs) ?? [];
4557
+ if (runs.length === 0) {
4558
+ return {
4559
+ provider: "skillspector",
4560
+ findings: [],
4561
+ degradedReasons: ["SARIF had no runs"]
4562
+ };
4563
+ }
4564
+ const degradedReasons = [];
4565
+ const findings = [];
4566
+ let providerVersion;
4567
+ const coverage = [];
4568
+ for (const runUnknown of runs) {
4569
+ const run2 = asObject(runUnknown, "SARIF run");
4570
+ const tool = asObject(run2.tool ?? {}, "SARIF tool");
4571
+ const driver = asObject(tool.driver ?? {}, "SARIF tool.driver");
4572
+ if (!providerVersion) {
4573
+ const v = typeof driver.semanticVersion === "string" && driver.semanticVersion || typeof driver.version === "string" && driver.version || "";
4574
+ if (v) providerVersion = v;
4575
+ }
4576
+ const results = arrayOf(run2.results) ?? [];
4577
+ for (const resUnknown of results) {
4578
+ const res = asObject(resUnknown, "SARIF result");
4579
+ findings.push({
4580
+ providerRuleId: String(res.ruleId ?? "unknown"),
4581
+ providerSeverity: String(res.level ?? "warning"),
4582
+ message: extractSarifMessage(res),
4583
+ locations: extractSarifLocations(res)
4584
+ });
4585
+ }
4586
+ }
4587
+ degradedReasons.push("SARIF import: provider-specific fields may be lost vs JSON form");
4588
+ return {
4589
+ provider: "skillspector",
4590
+ providerVersion,
4591
+ scanMode: "static",
4592
+ coverage,
4593
+ findings,
4594
+ degradedReasons
4595
+ };
4596
+ }
4597
+ function asObject(v, what) {
4598
+ if (v && typeof v === "object" && !Array.isArray(v)) return v;
4599
+ throw new Error(`expected object for ${what}`);
4600
+ }
4601
+ function arrayOf(v) {
4602
+ return Array.isArray(v) ? v : void 0;
4603
+ }
4604
+ function extractLocations(o) {
4605
+ const loc = o.location ?? o.locations ?? o.path;
4606
+ if (typeof loc === "string") return [loc];
4607
+ if (Array.isArray(loc)) return loc.map((x) => String(x));
4608
+ return void 0;
4609
+ }
4610
+ function extractSarifMessage(res) {
4611
+ const msg = res.message;
4612
+ if (msg && typeof msg.text === "string") return msg.text;
4613
+ return void 0;
4614
+ }
4615
+ function extractSarifLocations(res) {
4616
+ const locs = arrayOf(res.locations);
4617
+ if (!locs) return void 0;
4618
+ const out = [];
4619
+ for (const l of locs) {
4620
+ const lo = l;
4621
+ const phys = lo?.physicalLocation;
4622
+ const art = phys?.artifactLocation;
4623
+ const uri = art && typeof art.uri === "string" ? art.uri : void 0;
4624
+ const region = phys?.region;
4625
+ const line = region && typeof region.startLine === "number" ? region.startLine : void 0;
4626
+ if (uri) out.push(line ? `${uri}:${line}` : uri);
4627
+ }
4628
+ return out.length > 0 ? out : void 0;
4629
+ }
4630
+
4631
+ // ../../packages/evidence/src/importEvidence.ts
4632
+ var ZERO_DIGEST = `sha256:${"0".repeat(64)}`;
4633
+ function detectProvider(raw, explicit) {
4634
+ if (explicit) return explicit;
4635
+ const r = raw;
4636
+ if (!r) return "unknown";
4637
+ const topTool = r.tool;
4638
+ const jsonName = typeof r.scanner === "string" && r.scanner || topTool && typeof topTool.name === "string" && topTool.name || "";
4639
+ let sarifName = "";
4640
+ const runs = r.runs;
4641
+ if (Array.isArray(runs) && runs.length > 0) {
4642
+ const run2 = runs[0];
4643
+ const tool = run2?.tool;
4644
+ const driver = tool?.driver;
4645
+ if (driver && typeof driver.name === "string") sarifName = driver.name;
4646
+ }
4647
+ if (/skillspector/i.test(String(jsonName)) || /skillspector/i.test(sarifName)) {
4648
+ return "skillspector";
4649
+ }
4650
+ return "unknown";
4651
+ }
4652
+ function importEvidence(rawText, opts = {}) {
4653
+ const format = opts.format ?? (looksLikeSarif(rawText) ? "sarif" : "json");
4654
+ const rawReportDigest = sha256(rawText);
4655
+ let parsed;
4656
+ try {
4657
+ parsed = JSON.parse(rawText);
4658
+ } catch {
4659
+ return failClosed(opts.provider ?? "unknown", rawReportDigest, [
4660
+ `report is not valid JSON (format=${format})`
4661
+ ]);
4662
+ }
4663
+ const provider = detectProvider(parsed, opts.provider);
4664
+ let result;
4665
+ try {
4666
+ if (provider === "skillspector") {
4667
+ result = format === "sarif" ? parseSkillSpectorSarif(parsed) : parseSkillSpectorJson(parsed);
4668
+ } else {
4669
+ return failClosed(provider, rawReportDigest, [
4670
+ `no adapter for provider "${provider}"; evidence not interpreted`
4671
+ ]);
4672
+ }
4673
+ } catch (err2) {
4674
+ return failClosed(provider, rawReportDigest, [
4675
+ `adapter error: ${err2.message}`
4676
+ ]);
4677
+ }
4678
+ return finalizeEnvelope(result, rawReportDigest);
4679
+ }
4680
+ function failClosed(provider, rawReportDigest, reasons) {
4681
+ return {
4682
+ schema_version: EVIDENCE_SCHEMA_VERSION,
4683
+ provider: provider || "unknown",
4684
+ providerVersion: "unknown",
4685
+ artifactDigest: ZERO_DIGEST,
4686
+ scanMode: "static",
4687
+ coverage: [],
4688
+ completeness: "failed",
4689
+ findings: [],
4690
+ rawReportDigest,
4691
+ degradedReasons: reasons
4692
+ };
4693
+ }
4694
+ function finalizeEnvelope(r, rawReportDigest) {
4695
+ const degradedReasons = [...r.degradedReasons ?? []];
4696
+ const rank = { complete: 0, partial: 1, degraded: 2, failed: 3 };
4697
+ const rankToCompleteness = ["complete", "partial", "degraded", "failed"];
4698
+ let level = rank[r.completenessHint ?? (degradedReasons.length > 0 ? "degraded" : "complete")];
4699
+ let providerVersion = r.providerVersion?.trim() || "";
4700
+ if (!providerVersion) {
4701
+ providerVersion = "unknown";
4702
+ degradedReasons.push("provider version not pinned (no release/commit reported)");
4703
+ level = Math.max(level, rank.degraded);
4704
+ }
4705
+ const completeness = rankToCompleteness[level];
4706
+ return {
4707
+ schema_version: EVIDENCE_SCHEMA_VERSION,
4708
+ provider: r.provider,
4709
+ providerVersion,
4710
+ artifactDigest: r.artifactDigest ?? ZERO_DIGEST,
4711
+ scanMode: r.scanMode ?? "static",
4712
+ coverage: r.coverage ?? [],
4713
+ completeness,
4714
+ findings: r.findings,
4715
+ rawReportDigest,
4716
+ startedAt: r.startedAt,
4717
+ finishedAt: r.finishedAt,
4718
+ degradedReasons
4719
+ };
4720
+ }
4721
+ function looksLikeSarif(text) {
4722
+ return /"runs"\s*:/.test(text) && /sarif/i.test(text);
4723
+ }
4724
+
3765
4725
  // src/exitCode.ts
3766
4726
  function exitCodeFor(summary, policy) {
3767
4727
  const verdict = summary.verdict;
@@ -4350,6 +5310,15 @@ function scanCommand(args, deps) {
4350
5310
  function scanOneConfig(text, configPath, policy, args, deps, allowReceipt = true) {
4351
5311
  const surfaceDir = flagStr(args.flags, "surface-dir");
4352
5312
  const surfaces = surfaceDir ? readDocumentSurfaces(resolve3(deps.cwd, surfaceDir)) : void 0;
5313
+ const evidenceFile = flagStr(args.flags, "evidence");
5314
+ let evidence;
5315
+ if (evidenceFile) {
5316
+ const loaded = loadEvidence(evidenceFile, args, deps);
5317
+ if ("error" in loaded) {
5318
+ return { stdout: "", stderr: loaded.error, exitCode: loaded.exitCode };
5319
+ }
5320
+ evidence = [loaded];
5321
+ }
4353
5322
  let summary;
4354
5323
  try {
4355
5324
  summary = scanConfigText(text, configPath, {
@@ -4357,7 +5326,8 @@ function scanOneConfig(text, configPath, policy, args, deps, allowReceipt = true
4357
5326
  now: deps.now,
4358
5327
  generatedAt: deps.generatedAt,
4359
5328
  extraFindings: deps.online?.extraFindings,
4360
- surfaces
5329
+ surfaces,
5330
+ evidence
4361
5331
  });
4362
5332
  } catch (err2) {
4363
5333
  if (err2 instanceof ConfigParseError) {
@@ -4375,7 +5345,7 @@ function scanOneConfig(text, configPath, policy, args, deps, allowReceipt = true
4375
5345
  } catch {
4376
5346
  }
4377
5347
  }
4378
- const stdout = renderSummary(summary, args);
5348
+ const stdout = renderSummary(summary, args, deps.toolVersion);
4379
5349
  if (allowReceipt && flagBool(args.flags, "receipt")) {
4380
5350
  const err2 = writeReceiptFile(summary, text, configPath, policy, args, deps);
4381
5351
  if (err2) return { stdout: "", stderr: err2, exitCode: EXIT.ERROR };
@@ -4408,15 +5378,31 @@ function writeReceiptFile(summary, text, configPath, policy, args, deps) {
4408
5378
  }
4409
5379
  return void 0;
4410
5380
  }
4411
- function renderSummary(summary, args) {
5381
+ function loadEvidence(file, args, deps) {
5382
+ let rawText;
5383
+ try {
5384
+ rawText = readFileSync12(resolve3(deps.cwd, file), "utf8");
5385
+ } catch (err2) {
5386
+ const e = err2;
5387
+ return {
5388
+ error: e.code === "ENOENT" ? `Evidence file not found: ${file}` : e.message,
5389
+ exitCode: EXIT.USAGE
5390
+ };
5391
+ }
5392
+ const fmt = flagStr(args.flags, "evidence-format");
5393
+ const format = fmt === "sarif" ? "sarif" : fmt === "json" ? "json" : void 0;
5394
+ return importEvidence(rawText, { format });
5395
+ }
5396
+ function renderSummary(summary, args, toolVersion) {
4412
5397
  const style = flagBool(args.flags, "no-emoji") ? NO_EMOJI_STYLE : DEFAULT_STYLE;
4413
5398
  if (flagBool(args.flags, "json")) return renderJson(summary);
4414
5399
  if (flagBool(args.flags, "sarif")) return renderSarif(summary);
4415
5400
  if (flagBool(args.flags, "markdown")) return renderMarkdown(summary);
4416
5401
  if (flagBool(args.flags, "badge")) return renderBadge(summary);
4417
5402
  if (flagBool(args.flags, "html")) return renderHtml(summary);
4418
- if (flagBool(args.flags, "compact")) return renderCompact(summary, style);
4419
- return renderTerminal(summary, style);
5403
+ const base = flagBool(args.flags, "compact") ? renderCompact(summary, style) : renderTerminal(summary, style);
5404
+ const packet = renderTrustPacket(summary, toolVersion ?? "0.0.0-dev", style);
5405
+ return packet ? base + "\n" + packet : base;
4420
5406
  }
4421
5407
  function scanChangedCommand(args, deps) {
4422
5408
  const policyPath = flagStr(args.flags, "policy");
@@ -6819,127 +7805,1676 @@ function formatAgentType(agentType) {
6819
7805
  }
6820
7806
  }
6821
7807
 
6822
- // src/run.ts
6823
- function run(argv, deps) {
6824
- const args = parseArgs(argv);
6825
- const cmd = args.command;
6826
- if (!cmd || cmd === "help" || cmd === "--help" || cmd === "-h") {
6827
- return helpCommand();
7808
+ // src/commands/evidence.ts
7809
+ import { readFileSync as readFileSync16 } from "node:fs";
7810
+ import { resolve as resolve8 } from "node:path";
7811
+ function evidenceCommand(args, deps) {
7812
+ const subcommand = args.positionals[0];
7813
+ if (!subcommand || subcommand === "help") {
7814
+ return { stdout: evidenceHelp(), stderr: "", exitCode: 0 };
6828
7815
  }
6829
- switch (cmd) {
6830
- case "check":
6831
- return checkCommand(args, {
6832
- cwd: deps.cwd,
6833
- readStdin: deps.readStdin,
6834
- now: deps.now,
6835
- generatedAt: deps.generatedAt
6836
- });
6837
- case "scan-all":
6838
- return scanAllCommand(args, {
6839
- cwd: deps.cwd,
6840
- now: deps.now,
6841
- generatedAt: deps.generatedAt
6842
- });
6843
- case "scan":
6844
- return scanCommand(args, {
6845
- cwd: deps.cwd,
6846
- readStdin: deps.readStdin,
6847
- now: deps.now,
6848
- generatedAt: deps.generatedAt,
6849
- writeCacheFile: deps.writeCacheFile,
6850
- online: deps.online,
6851
- getChangedFilesDiff: deps.getChangedFilesDiff,
6852
- toolVersion: deps.toolVersion
6853
- });
6854
- case "diagnostics":
6855
- return diagnosticsCommand(args, {
6856
- cwd: deps.cwd,
6857
- readStdin: deps.readStdin,
6858
- now: deps.now,
6859
- generatedAt: deps.generatedAt,
6860
- online: deps.online
6861
- });
6862
- case "baseline":
6863
- return baselineCommand(args, {
6864
- cwd: deps.cwd,
6865
- readStdin: deps.readStdin,
6866
- generatedAt: deps.generatedAt,
6867
- writeBaselineFile: deps.writeCacheFile,
6868
- online: deps.online
6869
- });
6870
- case "verify":
6871
- return verifyCommand(args, {
6872
- cwd: deps.cwd,
6873
- readStdin: deps.readStdin,
6874
- now: deps.now,
6875
- generatedAt: deps.generatedAt,
6876
- writeBaselineFile: deps.writeCacheFile,
6877
- online: deps.online
6878
- });
6879
- case "approve":
6880
- return approveCommand(args, {
6881
- cwd: deps.cwd,
6882
- now: deps.now,
6883
- generatedAt: deps.generatedAt,
6884
- writeFile: deps.writeCacheFile
6885
- });
6886
- case "explain":
6887
- return explainCommand(args, { cwd: deps.cwd });
6888
- case "receipt":
6889
- return receiptCommand(args, { cwd: deps.cwd });
6890
- case "action":
6891
- return actionCommand(args, { cwd: deps.cwd, toolVersion: deps.toolVersion, generatedAt: deps.generatedAt });
6892
- case "inbox":
6893
- return inboxCommand(args, { cwd: deps.cwd, toolVersion: deps.toolVersion, generatedAt: deps.generatedAt });
6894
- case "inventory":
6895
- return inventoryCommand(args, { cwd: deps.cwd });
6896
- case "gen-rule":
6897
- return genRuleCommand(args, { cwd: deps.cwd });
6898
- case "policy":
6899
- return policyCommand(args, { cwd: deps.cwd });
6900
- default:
6901
- return {
6902
- stdout: "",
6903
- stderr: `Unknown command: ${cmd}
6904
- Run \`calllint help\`.`,
6905
- exitCode: 2
6906
- };
7816
+ if (subcommand === "import") {
7817
+ return evidenceImport(args, deps);
6907
7818
  }
7819
+ return {
7820
+ stdout: "",
7821
+ stderr: `Unknown evidence subcommand: ${subcommand}
7822
+ Run \`calllint evidence help\`.`,
7823
+ exitCode: 2
7824
+ };
6908
7825
  }
6909
-
6910
- // ../../packages/online/src/npm.ts
6911
- function isRecord3(v) {
6912
- return typeof v === "object" && v !== null && !Array.isArray(v);
6913
- }
6914
- function parseSpec(packageSpec) {
6915
- if (packageSpec.startsWith("@")) {
6916
- const slash = packageSpec.indexOf("/");
6917
- const at2 = packageSpec.indexOf("@", slash);
6918
- if (at2 === -1) return { name: packageSpec };
6919
- return { name: packageSpec.slice(0, at2), version: packageSpec.slice(at2 + 1) || void 0 };
7826
+ function evidenceImport(args, deps) {
7827
+ const file = args.positionals[1];
7828
+ if (!file) {
7829
+ return {
7830
+ stdout: "",
7831
+ stderr: "Error: Missing report file\nUsage: calllint evidence import <report.json|.sarif> [--provider <p>] [--format json|sarif]",
7832
+ exitCode: 2
7833
+ };
6920
7834
  }
6921
- const at = packageSpec.indexOf("@");
6922
- if (at <= 0) return { name: packageSpec };
6923
- return { name: packageSpec.slice(0, at), version: packageSpec.slice(at + 1) || void 0 };
6924
- }
6925
- var INSTALL_SCRIPT_KEYS = ["preinstall", "install", "postinstall"];
6926
- async function fetchNpmFacts(packageSpec, fetchJson) {
6927
- const { name, version } = parseSpec(packageSpec);
6928
- const url = `https://registry.npmjs.org/${name.replace(/\//g, "%2f")}`;
6929
- let doc;
7835
+ let rawText;
6930
7836
  try {
6931
- doc = await fetchJson(url);
6932
- } catch {
6933
- return { name, versionExists: false, installScripts: [] };
6934
- }
6935
- if (!isRecord3(doc)) return { name, versionExists: false, installScripts: [] };
6936
- const distTags = isRecord3(doc["dist-tags"]) ? doc["dist-tags"] : {};
6937
- const latestVersion = typeof distTags.latest === "string" ? distTags.latest : void 0;
6938
- const versions = isRecord3(doc.versions) ? doc.versions : {};
6939
- const isFloating = !version || version === "latest" || /[\^~><*]/.test(version);
6940
- const resolvedVersion = isFloating ? latestVersion : version;
6941
- const versionDoc = resolvedVersion && isRecord3(versions[resolvedVersion]) ? versions[resolvedVersion] : void 0;
6942
- if (!versionDoc) {
7837
+ rawText = readFileSync16(resolve8(deps.cwd, file), "utf-8");
7838
+ } catch (error) {
7839
+ const err2 = error;
7840
+ const stderr = err2.code === "ENOENT" ? `Error: File not found: ${file}` : `Error: ${err2.message}`;
7841
+ return { stdout: "", stderr, exitCode: 2 };
7842
+ }
7843
+ const provider = args.flags["provider"];
7844
+ const formatFlag = args.flags["format"];
7845
+ const format = formatFlag === "sarif" ? "sarif" : formatFlag === "json" ? "json" : void 0;
7846
+ const envelope = importEvidence(rawText, { provider, format });
7847
+ const exitCode = envelope.completeness === "complete" ? 0 : envelope.completeness === "partial" ? 10 : 20;
7848
+ if (args.flags["json"]) {
7849
+ return { stdout: JSON.stringify(envelope, null, 2), stderr: "", exitCode };
7850
+ }
7851
+ const badge2 = envelope.completeness === "complete" ? "\u2713" : envelope.completeness === "partial" ? "~" : "\u25C7";
7852
+ let stdout = `
7853
+ CallLint evidence import
7854
+ `;
7855
+ stdout += `provider: ${envelope.provider} (${envelope.providerVersion})
7856
+ `;
7857
+ stdout += `scan mode: ${envelope.scanMode}
7858
+ `;
7859
+ stdout += `completeness: ${badge2} ${envelope.completeness}
7860
+ `;
7861
+ stdout += `findings: ${envelope.findings.length} (provider-native, not re-scored)
7862
+ `;
7863
+ stdout += `raw digest: ${envelope.rawReportDigest}
7864
+ `;
7865
+ if (envelope.degradedReasons.length > 0) {
7866
+ stdout += `
7867
+ degraded/failed reasons:
7868
+ `;
7869
+ for (const r of envelope.degradedReasons) stdout += ` \u2022 ${r}
7870
+ `;
7871
+ }
7872
+ stdout += `
7873
+ Note: external evidence is recorded, not converted into a CallLint verdict.
7874
+ `;
7875
+ stdout += `A degraded or failed external scan is never treated as a pass.
7876
+ `;
7877
+ return { stdout, stderr: "", exitCode };
7878
+ }
7879
+ function evidenceHelp() {
7880
+ return `
7881
+ calllint evidence \u2014 Import third-party scanner evidence (no re-scoring)
7882
+
7883
+ USAGE
7884
+ calllint evidence import <report.json|.sarif> [options]
7885
+
7886
+ DESCRIPTION
7887
+ Parse a third-party scanner report (e.g. NVIDIA SkillSpector) into a normalized
7888
+ envelope (calllint.evidence-provider.v0). CallLint records the external evidence
7889
+ with provenance \u2014 provider, pinned version, scan mode, coverage, completeness,
7890
+ raw-report digest \u2014 WITHOUT re-scoring or renaming the provider's own findings.
7891
+
7892
+ Aggregate, don't impersonate: an external SAFE never upgrades a CallLint verdict,
7893
+ and a degraded / failed / malformed scan fails closed (never reads as a pass).
7894
+
7895
+ OPTIONS
7896
+ --provider <name> Force the provider adapter (default: auto-detect). e.g. skillspector
7897
+ --format json|sarif Force the input format (default: auto-detect)
7898
+ --json Emit the raw envelope as JSON
7899
+
7900
+ EXIT CODES
7901
+ 0 evidence complete
7902
+ 10 evidence partial (review the gaps)
7903
+ 20 evidence degraded/failed/malformed (fail-closed \u2014 not a pass)
7904
+ 2 usage error / file not found
7905
+
7906
+ EXAMPLES
7907
+ calllint evidence import skillspector-report.json
7908
+ calllint evidence import skillspector.sarif --format sarif --json
7909
+
7910
+ SEE ALSO
7911
+ ADR 0034 \u2014 Evidence Provider Envelope
7912
+ docs/new7-packet-a-evidence.md \u2014 file-level execution plan
7913
+ `;
7914
+ }
7915
+
7916
+ // src/commands/trust.ts
7917
+ import { existsSync as existsSync11, mkdirSync as mkdirSync5, readdirSync as readdirSync2, readFileSync as readFileSync18, statSync as statSync4, writeFileSync as writeFileSync10 } from "node:fs";
7918
+ import { basename as basename4, join as join16, resolve as resolvePath4 } from "node:path";
7919
+ import { homedir, userInfo } from "node:os";
7920
+
7921
+ // ../../packages/install-planner/src/buildPlan.ts
7922
+ function ptr(segment) {
7923
+ return segment.replace(/~/g, "~0").replace(/\//g, "~1");
7924
+ }
7925
+ function buildServerOps(ctx) {
7926
+ const forward = [];
7927
+ const inverse = [];
7928
+ const cfg = ctx.currentConfig ?? null;
7929
+ const hasContainer = !!cfg && typeof cfg === "object" && !!cfg.mcpServers;
7930
+ if (!hasContainer) {
7931
+ forward.push({ op: "add", path: "/mcpServers", value: {} });
7932
+ inverse.unshift({ op: "remove", path: "/mcpServers" });
7933
+ }
7934
+ for (const s of ctx.servers) {
7935
+ const path = `/mcpServers/${ptr(s.name)}`;
7936
+ const prior = hasContainer && cfg?.mcpServers ? cfg.mcpServers[s.name] : void 0;
7937
+ forward.push({ op: "add", path, value: s.entry });
7938
+ if (prior === void 0) {
7939
+ if (hasContainer) inverse.unshift({ op: "remove", path });
7940
+ } else {
7941
+ inverse.unshift({ op: "replace", path, value: prior });
7942
+ }
7943
+ }
7944
+ const op = {
7945
+ type: "json-patch",
7946
+ target: ctx.configPath,
7947
+ preconditionDigest: ctx.configDigest,
7948
+ patch: forward
7949
+ };
7950
+ const rb = {
7951
+ type: "json-patch",
7952
+ target: ctx.configPath,
7953
+ preconditionDigest: ctx.configDigest,
7954
+ patch: inverse
7955
+ };
7956
+ return { operations: [op], rollback: inverse.length > 0 ? [rb] : [] };
7957
+ }
7958
+ function buildInstallPlan(ctx, upstream) {
7959
+ const { operations, rollback } = buildServerOps(ctx);
7960
+ const idempotencyKey = hashJson({
7961
+ host: ctx.host,
7962
+ operations
7963
+ });
7964
+ const planId = hashJson({
7965
+ artifactDigest: upstream.artifactDigest,
7966
+ authorityDigest: upstream.authority.digest,
7967
+ decisionDigest: upstream.decision.digest,
7968
+ host: ctx.host,
7969
+ operations
7970
+ }).slice("sha256:".length, "sha256:".length + 16);
7971
+ const sealed = {
7972
+ schema: "calllint.install-plan.v1",
7973
+ planId,
7974
+ artifactDigest: upstream.artifactDigest,
7975
+ authorityDigest: upstream.authority.digest,
7976
+ decisionDigest: upstream.decision.digest,
7977
+ policyDigest: upstream.decision.policyDigest,
7978
+ host: ctx.host,
7979
+ tier: ctx.tier,
7980
+ operations,
7981
+ rollback,
7982
+ backup: { path: ctx.backupPath },
7983
+ idempotencyKey,
7984
+ expiresAt: ctx.expiresAt
7985
+ };
7986
+ return { ...sealed, planDigest: hashJson(sealed) };
7987
+ }
7988
+ function verifyPlanDigest(plan) {
7989
+ const { planDigest, ...rest } = plan;
7990
+ return planDigest === hashJson(rest);
7991
+ }
7992
+
7993
+ // ../../packages/install-planner/src/validate.ts
7994
+ function validatePlan(plan) {
7995
+ const errors = [];
7996
+ if (plan.schema !== "calllint.install-plan.v1") {
7997
+ errors.push(`unexpected schema: ${String(plan.schema)}`);
7998
+ }
7999
+ if (!verifyPlanDigest(plan)) {
8000
+ errors.push("planDigest does not match plan contents (tampered or stale)");
8001
+ }
8002
+ if (plan.operations.length === 0) {
8003
+ errors.push("plan has no operations (nothing to install)");
8004
+ }
8005
+ for (const op of [...plan.operations, ...plan.rollback]) {
8006
+ if (op.type !== "json-patch") {
8007
+ errors.push(`operation is not json-patch: ${String(op.type)}`);
8008
+ }
8009
+ if (!op.preconditionDigest) {
8010
+ errors.push(`operation on ${op.target} has no preconditionDigest`);
8011
+ }
8012
+ for (const p of op.patch) {
8013
+ if (!p.path.startsWith("/")) {
8014
+ errors.push(`patch path must be an absolute JSON-Pointer: ${p.path}`);
8015
+ }
8016
+ }
8017
+ }
8018
+ return { ok: errors.length === 0, errors };
8019
+ }
8020
+
8021
+ // ../../packages/install-planner/src/jsonPatch.ts
8022
+ var JsonPatchError = class extends Error {
8023
+ constructor(message) {
8024
+ super(message);
8025
+ this.name = "JsonPatchError";
8026
+ }
8027
+ };
8028
+ function clone(v) {
8029
+ return v === void 0 ? v : JSON.parse(JSON.stringify(v));
8030
+ }
8031
+ function parsePointer(pointer) {
8032
+ if (pointer === "") return [];
8033
+ if (!pointer.startsWith("/")) throw new JsonPatchError(`invalid JSON-Pointer: ${pointer}`);
8034
+ return pointer.slice(1).split("/").map((t) => t.replace(/~1/g, "/").replace(/~0/g, "~"));
8035
+ }
8036
+ function isContainer(v) {
8037
+ return v !== null && typeof v === "object";
8038
+ }
8039
+ function resolveParent(doc, tokens) {
8040
+ let node = doc;
8041
+ for (let i = 0; i < tokens.length - 1; i++) {
8042
+ if (!isContainer(node)) throw new JsonPatchError(`path traverses a non-container at "${tokens[i]}"`);
8043
+ node = Array.isArray(node) ? node[Number(tokens[i])] : node[tokens[i]];
8044
+ }
8045
+ if (!isContainer(node)) throw new JsonPatchError("path parent is not a container");
8046
+ return { parent: node, key: tokens[tokens.length - 1] };
8047
+ }
8048
+ function getAt(doc, tokens) {
8049
+ let node = doc;
8050
+ for (const t of tokens) {
8051
+ if (!isContainer(node)) throw new JsonPatchError(`cannot read "${t}" of a non-container`);
8052
+ node = Array.isArray(node) ? node[t === "-" ? node.length : Number(t)] : node[t];
8053
+ }
8054
+ return node;
8055
+ }
8056
+ function addAt(parent, key, value) {
8057
+ if (Array.isArray(parent)) {
8058
+ const idx = key === "-" ? parent.length : Number(key);
8059
+ if (Number.isNaN(idx) || idx < 0 || idx > parent.length) throw new JsonPatchError(`array index out of range: ${key}`);
8060
+ parent.splice(idx, 0, value);
8061
+ } else {
8062
+ parent[key] = value;
8063
+ }
8064
+ }
8065
+ function removeAt(parent, key) {
8066
+ if (Array.isArray(parent)) {
8067
+ const idx = Number(key);
8068
+ if (Number.isNaN(idx) || idx < 0 || idx >= parent.length) throw new JsonPatchError(`array index out of range: ${key}`);
8069
+ parent.splice(idx, 1);
8070
+ } else {
8071
+ if (!(key in parent)) throw new JsonPatchError(`cannot remove missing key: ${key}`);
8072
+ delete parent[key];
8073
+ }
8074
+ }
8075
+ function applyJsonPatch(doc, patch) {
8076
+ let result = clone(doc);
8077
+ for (const op of patch) {
8078
+ const tokens = parsePointer(op.path);
8079
+ if (op.op === "test") {
8080
+ const actual = getAt(result, tokens);
8081
+ if (JSON.stringify(actual) !== JSON.stringify(op.value)) {
8082
+ throw new JsonPatchError(`test failed at ${op.path}`);
8083
+ }
8084
+ continue;
8085
+ }
8086
+ if (tokens.length === 0) {
8087
+ if (op.op === "add" || op.op === "replace") {
8088
+ result = clone(op.value);
8089
+ continue;
8090
+ }
8091
+ throw new JsonPatchError(`op "${op.op}" not allowed on document root`);
8092
+ }
8093
+ switch (op.op) {
8094
+ case "add": {
8095
+ const { parent, key } = resolveParent(result, tokens);
8096
+ addAt(parent, key, clone(op.value));
8097
+ break;
8098
+ }
8099
+ case "replace": {
8100
+ const { parent, key } = resolveParent(result, tokens);
8101
+ if (Array.isArray(parent)) {
8102
+ const idx = Number(key);
8103
+ if (Number.isNaN(idx) || idx < 0 || idx >= parent.length) throw new JsonPatchError(`replace index out of range: ${key}`);
8104
+ parent[idx] = clone(op.value);
8105
+ } else {
8106
+ if (!(key in parent)) throw new JsonPatchError(`cannot replace missing key: ${key}`);
8107
+ parent[key] = clone(op.value);
8108
+ }
8109
+ break;
8110
+ }
8111
+ case "remove": {
8112
+ const { parent, key } = resolveParent(result, tokens);
8113
+ removeAt(parent, key);
8114
+ break;
8115
+ }
8116
+ case "move":
8117
+ case "copy": {
8118
+ if (op.from === void 0) throw new JsonPatchError(`"${op.op}" requires "from"`);
8119
+ const fromTokens = parsePointer(op.from);
8120
+ const moved = clone(getAt(result, fromTokens));
8121
+ if (moved === void 0) throw new JsonPatchError(`"${op.op}" source missing: ${op.from}`);
8122
+ if (op.op === "move") {
8123
+ const src = resolveParent(result, fromTokens);
8124
+ removeAt(src.parent, src.key);
8125
+ }
8126
+ const { parent, key } = resolveParent(result, tokens);
8127
+ addAt(parent, key, moved);
8128
+ break;
8129
+ }
8130
+ default:
8131
+ throw new JsonPatchError(`unsupported op: ${op.op}`);
8132
+ }
8133
+ }
8134
+ return result;
8135
+ }
8136
+
8137
+ // ../../packages/install-planner/src/applyEngine.ts
8138
+ function digestBytes(bytes) {
8139
+ return hashJson(bytes);
8140
+ }
8141
+ function detectIndent(bytes) {
8142
+ const m = bytes.match(/\n([ \t]+)"/);
8143
+ if (!m) return 2;
8144
+ return m[1].includes(" ") ? " " : m[1].length;
8145
+ }
8146
+ function serializeLike(original, value) {
8147
+ const indent = original === null ? 2 : detectIndent(original);
8148
+ const body = JSON.stringify(value, null, indent);
8149
+ const trailing = original === null || original.endsWith("\n") ? "\n" : "";
8150
+ return body + trailing;
8151
+ }
8152
+ function sameConfig(a, b) {
8153
+ return stableStringify(a) === stableStringify(b);
8154
+ }
8155
+ function mk(plan, opts, state, outcome, before, extra, notes) {
8156
+ return {
8157
+ schema: APPLY_RESULT_SCHEMA,
8158
+ state,
8159
+ outcome,
8160
+ planId: plan.planId,
8161
+ planDigest: plan.planDigest,
8162
+ host: plan.host,
8163
+ configPath: opts.configPath,
8164
+ configDigestBefore: before,
8165
+ configDigestAfter: null,
8166
+ backupPath: null,
8167
+ rolledBack: false,
8168
+ notes,
8169
+ appliedAt: opts.now,
8170
+ ...extra
8171
+ };
8172
+ }
8173
+ function applyPlan(opts) {
8174
+ const { plan, fs } = opts;
8175
+ const notes = [];
8176
+ const v = validatePlan(plan);
8177
+ if (!v.ok) {
8178
+ return mk(plan, opts, "PLAN_STALE", "stale", "absent", {}, [...notes, ...v.errors.map((e) => `invalid plan: ${e}`)]);
8179
+ }
8180
+ if (!verifyPlanDigest(plan)) {
8181
+ return mk(plan, opts, "PLAN_STALE", "stale", "absent", {}, [...notes, "plan digest does not seal its contents \u2014 tampered"]);
8182
+ }
8183
+ if (opts.approvalDigest !== plan.planDigest) {
8184
+ return mk(plan, opts, "PLAN_STALE", "stale", "absent", {}, [
8185
+ ...notes,
8186
+ `approval digest does not match plan digest (approved ${opts.approvalDigest.slice(0, 16)}\u2026, plan ${plan.planDigest.slice(0, 16)}\u2026)`
8187
+ ]);
8188
+ }
8189
+ if (opts.now > plan.expiresAt) {
8190
+ return mk(plan, opts, "PLAN_STALE", "stale", "absent", {}, [...notes, `plan expired at ${plan.expiresAt} (now ${opts.now})`]);
8191
+ }
8192
+ if (plan.tier !== "A") {
8193
+ return mk(plan, opts, "PLAN_STALE", "stale", "absent", {}, [...notes, `host "${plan.host}" is tier ${plan.tier} \u2014 not approved for apply`]);
8194
+ }
8195
+ const op = plan.operations[0];
8196
+ if (!op) {
8197
+ return mk(plan, opts, "VERIFIED", "already_applied", "absent", { configDigestAfter: null }, [...notes, "plan has no operations \u2014 nothing to apply"]);
8198
+ }
8199
+ const exists = fs.exists(opts.configPath);
8200
+ const currentBytes = exists ? fs.readFile(opts.configPath) : null;
8201
+ const before = currentBytes === null ? "absent" : digestBytes(currentBytes);
8202
+ const currentConfig = currentBytes === null ? null : safeParse(currentBytes);
8203
+ if (currentBytes !== null && currentConfig === PARSE_FAILED) {
8204
+ return mk(plan, opts, "APPLY_CONFLICT", "conflict", before, {}, [...notes, "current config is not valid JSON \u2014 refusing to overwrite"]);
8205
+ }
8206
+ let next;
8207
+ try {
8208
+ next = applyJsonPatch(currentConfig ?? {}, op.patch);
8209
+ } catch (e) {
8210
+ const msg = e instanceof JsonPatchError ? e.message : String(e);
8211
+ return mk(plan, opts, "APPLY_CONFLICT", "conflict", before, {}, [...notes, `patch does not apply to current config: ${msg}`]);
8212
+ }
8213
+ if (currentConfig !== null && sameConfig(next, currentConfig)) {
8214
+ notes.push("plan already in effect \u2014 no change needed (idempotent)");
8215
+ return mk(plan, opts, "VERIFIED", "already_applied", before, { configDigestAfter: before === "absent" ? null : before }, notes);
8216
+ }
8217
+ if (op.preconditionDigest !== before) {
8218
+ return mk(plan, opts, "APPLY_CONFLICT", "conflict", before, {}, [
8219
+ ...notes,
8220
+ `target config changed since planning (precondition ${op.preconditionDigest.slice(0, 16)}\u2026, now ${before === "absent" ? "absent" : before.slice(0, 16) + "\u2026"}) \u2192 not auto-merging`
8221
+ ]);
8222
+ }
8223
+ fs.ensureDir(opts.lockPath);
8224
+ if (!fs.acquireLock(opts.lockPath)) {
8225
+ return mk(plan, opts, "APPLY_CONFLICT", "conflict", before, {}, [...notes, "another apply holds the config lock \u2014 try again"]);
8226
+ }
8227
+ try {
8228
+ return writeVerifyRollback(opts, op.patch, currentBytes, before, next, notes);
8229
+ } finally {
8230
+ fs.remove(opts.lockPath);
8231
+ }
8232
+ }
8233
+ var PARSE_FAILED = Symbol("parse-failed");
8234
+ function safeParse(bytes) {
8235
+ try {
8236
+ return JSON.parse(bytes);
8237
+ } catch {
8238
+ return PARSE_FAILED;
8239
+ }
8240
+ }
8241
+ function writeVerifyRollback(opts, patch, currentBytes, before, next, notes) {
8242
+ const { plan, fs } = opts;
8243
+ const nextBytes = serializeLike(currentBytes, next);
8244
+ const after = digestBytes(nextBytes);
8245
+ let backupPath = null;
8246
+ if (currentBytes !== null) {
8247
+ fs.ensureDir(opts.backupPath);
8248
+ fs.writeFile(opts.backupPath, currentBytes);
8249
+ fs.fsync(opts.backupPath);
8250
+ backupPath = opts.backupPath;
8251
+ notes.push(`backed up original config \u2192 ${opts.backupPath}`);
8252
+ }
8253
+ const tmp = opts.configPath + ".calllint-tmp";
8254
+ fs.ensureDir(opts.configPath);
8255
+ fs.writeFile(tmp, nextBytes);
8256
+ fs.fsync(tmp);
8257
+ fs.rename(tmp, opts.configPath);
8258
+ notes.push("config written atomically (temp \u2192 fsync \u2192 rename)");
8259
+ const verifyOk = verify2(fs, opts.configPath, patch, next);
8260
+ if (verifyOk) {
8261
+ notes.push("post-apply verify OK \u2014 resulting config re-parses and matches the plan");
8262
+ return mk(plan, opts, "VERIFIED", "applied", before, { configDigestAfter: after, backupPath }, notes);
8263
+ }
8264
+ notes.push("post-apply verify FAILED \u2014 attempting rollback to the original config");
8265
+ if (currentBytes === null) {
8266
+ fs.remove(opts.configPath);
8267
+ const restored = !fs.exists(opts.configPath);
8268
+ return mk(plan, opts, restored ? "VERIFICATION_FAILED" : "ROLLBACK_REQUIRED", restored ? "rolled_back" : "rollback_failed", before, { configDigestAfter: null, backupPath, rolledBack: restored }, [
8269
+ ...notes,
8270
+ restored ? "rollback OK \u2014 created file removed, original absence restored" : "rollback FAILED \u2014 file still present; manual intervention required"
8271
+ ]);
8272
+ }
8273
+ const rtmp = opts.configPath + ".calllint-rollback-tmp";
8274
+ fs.writeFile(rtmp, currentBytes);
8275
+ fs.fsync(rtmp);
8276
+ fs.rename(rtmp, opts.configPath);
8277
+ const restoredDigest = digestBytes(fs.readFile(opts.configPath));
8278
+ const rolledBack = restoredDigest === before;
8279
+ return mk(plan, opts, rolledBack ? "VERIFICATION_FAILED" : "ROLLBACK_REQUIRED", rolledBack ? "rolled_back" : "rollback_failed", before, { configDigestAfter: null, backupPath, rolledBack }, [
8280
+ ...notes,
8281
+ rolledBack ? "rollback OK \u2014 original config restored (digest matches)" : "rollback FAILED \u2014 restored digest does not match original; manual intervention required"
8282
+ ]);
8283
+ }
8284
+ function verify2(fs, path, patch, expected) {
8285
+ if (!fs.exists(path)) return false;
8286
+ const parsed = safeParse(fs.readFile(path));
8287
+ if (parsed === PARSE_FAILED) return false;
8288
+ if (stableStringify(parsed) !== stableStringify(expected)) return false;
8289
+ return true;
8290
+ }
8291
+
8292
+ // ../../packages/install-planner/src/nodeFsPort.ts
8293
+ import {
8294
+ existsSync as existsSync10,
8295
+ readFileSync as readFileSync17,
8296
+ writeFileSync as writeFileSync9,
8297
+ renameSync,
8298
+ rmSync,
8299
+ mkdirSync as mkdirSync4,
8300
+ openSync,
8301
+ closeSync,
8302
+ fsyncSync
8303
+ } from "node:fs";
8304
+ import { dirname as dirname4 } from "node:path";
8305
+ function nodeFsPort() {
8306
+ return {
8307
+ exists: (p) => existsSync10(p),
8308
+ readFile: (p) => readFileSync17(p, "utf8"),
8309
+ writeFile: (p, data) => writeFileSync9(p, data, "utf8"),
8310
+ fsync: (p) => {
8311
+ try {
8312
+ const fd = openSync(p, "r+");
8313
+ try {
8314
+ fsyncSync(fd);
8315
+ } finally {
8316
+ closeSync(fd);
8317
+ }
8318
+ } catch {
8319
+ }
8320
+ },
8321
+ rename: (from, to) => renameSync(from, to),
8322
+ remove: (p) => rmSync(p, { force: true }),
8323
+ ensureDir: (p) => mkdirSync4(dirname4(p), { recursive: true }),
8324
+ acquireLock: (p) => {
8325
+ try {
8326
+ const fd = openSync(p, "wx");
8327
+ closeSync(fd);
8328
+ return true;
8329
+ } catch {
8330
+ return false;
8331
+ }
8332
+ }
8333
+ };
8334
+ }
8335
+
8336
+ // ../../packages/install-planner/src/pathSafety.ts
8337
+ import { isAbsolute as isAbsolute2, resolve as resolve9 } from "node:path";
8338
+ var PathSafetyError = class extends Error {
8339
+ constructor(message) {
8340
+ super(message);
8341
+ this.name = "PathSafetyError";
8342
+ }
8343
+ };
8344
+ function expandHome(p, home) {
8345
+ if (p.includes("\0")) throw new PathSafetyError("path contains a NUL byte");
8346
+ let expanded = p;
8347
+ if (p === "~") expanded = home;
8348
+ else if (p.startsWith("~/") || p.startsWith("~\\")) expanded = home + p.slice(1);
8349
+ else if (p.startsWith("~")) throw new PathSafetyError(`refusing to expand "~" username form: ${p}`);
8350
+ return expanded;
8351
+ }
8352
+ function safeConfigPath(rawPath, opts) {
8353
+ const expanded = expandHome(rawPath, opts.home);
8354
+ const abs = isAbsolute2(expanded) ? expanded : resolve9(opts.cwd, expanded);
8355
+ if (abs.includes("\0")) throw new PathSafetyError("resolved path contains a NUL byte");
8356
+ if (!isAbsolute2(abs)) throw new PathSafetyError(`could not resolve to an absolute path: ${rawPath}`);
8357
+ return abs;
8358
+ }
8359
+
8360
+ // ../../packages/install-planner/src/decisionReceipt.ts
8361
+ function resultFor(outcome) {
8362
+ if (outcome === "applied" || outcome === "already_applied") return "applied";
8363
+ if (outcome === "rolled_back") return "rolled-back";
8364
+ return "prepared-only";
8365
+ }
8366
+ function deriveReceiptId(installPlanDigest, approvedAt) {
8367
+ const h2 = sha256(`${installPlanDigest}|${approvedAt}`).slice(7);
8368
+ const b64 = Buffer.from(h2.slice(0, 32), "hex").toString("base64").replace(/\+/g, "-").replace(/\//g, "_").replace(/=+$/, "");
8369
+ return `clrec_${b64}`;
8370
+ }
8371
+ function sortedUnique(xs) {
8372
+ return [...new Set(xs)].sort();
8373
+ }
8374
+ function buildDecisionReceipt(result, plan, ctx) {
8375
+ return {
8376
+ schema: "calllint.receipt.v1",
8377
+ receiptId: deriveReceiptId(plan.planDigest, ctx.approvedAt),
8378
+ artifactDigest: plan.artifactDigest ?? null,
8379
+ evidenceDigests: sortedUnique(ctx.evidenceDigests ?? []),
8380
+ authorityDigest: plan.authorityDigest,
8381
+ policyDigest: plan.policyDigest,
8382
+ decisionDigest: plan.decisionDigest,
8383
+ installPlanDigest: plan.planDigest,
8384
+ approval: {
8385
+ type: "local-human",
8386
+ approvedAt: ctx.approvedAt,
8387
+ approver: ctx.approver,
8388
+ approvedDigest: plan.planDigest
8389
+ },
8390
+ result: resultFor(result.outcome),
8391
+ host: plan.host,
8392
+ configPath: result.configPath,
8393
+ configDigestBefore: result.configDigestBefore,
8394
+ configDigestAfter: result.configDigestAfter,
8395
+ policyVersion: ctx.policyVersion ?? null,
8396
+ scannerVersion: ctx.scannerVersion,
8397
+ exceptionReason: ctx.exceptionReason ?? null,
8398
+ expiration: plan.expiresAt,
8399
+ supersedes: ctx.supersedes ?? null,
8400
+ revocation: null,
8401
+ signature: null
8402
+ };
8403
+ }
8404
+ function signDecisionReceipt(receipt, keypair) {
8405
+ const { signature: _drop, ...body } = receipt;
8406
+ const sig = signReceipt(body, keypair);
8407
+ return { ...receipt, signature: { ...sig, algorithm: "ed25519" } };
8408
+ }
8409
+
8410
+ // ../../packages/install-planner/src/verifyDecisionReceipt.ts
8411
+ var SHA256 = /^sha256:[0-9a-f]{64}$/;
8412
+ var RECEIPT_ID = /^clrec_[A-Za-z0-9_-]+$/;
8413
+ var RESULTS = /* @__PURE__ */ new Set(["applied", "rolled-back", "prepared-only"]);
8414
+ function isObj(v) {
8415
+ return typeof v === "object" && v !== null && !Array.isArray(v);
8416
+ }
8417
+ function isSha(v) {
8418
+ return typeof v === "string" && SHA256.test(v);
8419
+ }
8420
+ function isShaOrNull(v) {
8421
+ return v === null || isSha(v);
8422
+ }
8423
+ function verifyDecisionReceipt(input, opts) {
8424
+ const errors = [];
8425
+ const push = (m) => errors.push(m);
8426
+ const fail = (msg) => ({
8427
+ valid: false,
8428
+ errors: [msg],
8429
+ signed: false,
8430
+ expired: false,
8431
+ tampered: false
8432
+ });
8433
+ if (!isObj(input)) return fail("receipt is not a JSON object");
8434
+ const r = input;
8435
+ if (r.schema !== "calllint.receipt.v1") push('schema must be "calllint.receipt.v1"');
8436
+ if (typeof r.receiptId !== "string" || !RECEIPT_ID.test(r.receiptId)) push("receiptId must match /^clrec_/");
8437
+ if (!isShaOrNull(r.artifactDigest)) push("artifactDigest must be sha256 or null");
8438
+ if (!Array.isArray(r.evidenceDigests) || !r.evidenceDigests.every(isSha)) push("evidenceDigests must be an array of sha256");
8439
+ for (const k of ["authorityDigest", "policyDigest", "decisionDigest", "installPlanDigest"]) {
8440
+ if (!isSha(r[k])) push(`${k} must be sha256`);
8441
+ }
8442
+ if (typeof r.result !== "string" || !RESULTS.has(r.result)) push("result must be applied|rolled-back|prepared-only");
8443
+ if (typeof r.host !== "string" || r.host.length === 0) push("host must be a non-empty string");
8444
+ if (typeof r.configPath !== "string" || r.configPath.length === 0) push("configPath must be a non-empty string");
8445
+ if (!(r.configDigestBefore === "absent" || isSha(r.configDigestBefore))) push('configDigestBefore must be sha256 or "absent"');
8446
+ if (!isShaOrNull(r.configDigestAfter)) push("configDigestAfter must be sha256 or null");
8447
+ if (typeof r.scannerVersion !== "string" || r.scannerVersion.length === 0) push("scannerVersion must be a non-empty string");
8448
+ if (typeof r.expiration !== "string" || Number.isNaN(Date.parse(r.expiration))) push("expiration must be ISO-8601");
8449
+ if (!isObj(r.approval)) {
8450
+ push("approval must be an object");
8451
+ } else {
8452
+ const a = r.approval;
8453
+ if (a.type !== "local-human") push('approval.type must be "local-human"');
8454
+ if (typeof a.approvedAt !== "string" || Number.isNaN(Date.parse(a.approvedAt))) push("approval.approvedAt must be ISO-8601");
8455
+ if (!(a.approver === null || typeof a.approver === "string")) push("approval.approver must be string|null");
8456
+ if (!isSha(a.approvedDigest)) push("approval.approvedDigest must be sha256");
8457
+ else if (isSha(r.installPlanDigest) && a.approvedDigest !== r.installPlanDigest) {
8458
+ push("approval.approvedDigest must equal installPlanDigest (approval binding broken)");
8459
+ }
8460
+ }
8461
+ let signed = false;
8462
+ let tampered = false;
8463
+ if (r.signature != null) {
8464
+ signed = true;
8465
+ const sig = r.signature;
8466
+ if (sig.algorithm !== "ed25519" || typeof sig.key_id !== "string" || typeof sig.value !== "string") {
8467
+ push("signature, when present, must have ed25519 algorithm + string key_id + value");
8468
+ } else if (opts.publicKey !== void 0) {
8469
+ const res = verifyReceipt2(r, opts.publicKey);
8470
+ if (!res.valid) {
8471
+ tampered = true;
8472
+ push(`signature verification failed: ${res.error ?? "invalid signature"}`);
8473
+ }
8474
+ }
8475
+ }
8476
+ const expired = typeof r.expiration === "string" && !Number.isNaN(Date.parse(r.expiration)) ? Date.parse(opts.now) > Date.parse(r.expiration) : false;
8477
+ return { valid: errors.length === 0, errors, signed, expired, tampered };
8478
+ }
8479
+
8480
+ // ../../packages/install-planner/src/adapters/claudeCode.ts
8481
+ var CLAUDE_CODE_HOST_ID = "claude-code";
8482
+ var CLAUDE_CODE_TIER = "A";
8483
+ var claudeCodeAdapter = {
8484
+ id: CLAUDE_CODE_HOST_ID,
8485
+ tier: CLAUDE_CODE_TIER,
8486
+ createPlan(ctx, upstream) {
8487
+ return buildInstallPlan({ ...ctx, host: CLAUDE_CODE_HOST_ID, tier: CLAUDE_CODE_TIER }, upstream);
8488
+ },
8489
+ validatePlan(plan) {
8490
+ return validatePlan(plan);
8491
+ },
8492
+ applyPlan(plan, ctx) {
8493
+ return applyPlan({
8494
+ plan,
8495
+ approvalDigest: ctx.approvalDigest,
8496
+ configPath: ctx.configPath,
8497
+ backupPath: ctx.backupPath,
8498
+ lockPath: ctx.lockPath,
8499
+ fs: ctx.fs,
8500
+ now: ctx.now
8501
+ });
8502
+ }
8503
+ };
8504
+ function claudeCodeServerEntry(server) {
8505
+ const entry = {};
8506
+ if (server.url) {
8507
+ entry["url"] = server.url;
8508
+ } else if (server.command) {
8509
+ entry["command"] = server.command;
8510
+ entry["args"] = server.args ?? [];
8511
+ }
8512
+ if (server.envKeys && server.envKeys.length > 0) {
8513
+ const env = {};
8514
+ for (const k of [...server.envKeys].sort()) env[k] = "";
8515
+ entry["env"] = env;
8516
+ }
8517
+ return entry;
8518
+ }
8519
+
8520
+ // ../../packages/install-planner/src/index.ts
8521
+ var HOST_ADAPTERS = {
8522
+ [claudeCodeAdapter.id]: claudeCodeAdapter
8523
+ };
8524
+ function getHostAdapter(host) {
8525
+ return HOST_ADAPTERS[host] ?? null;
8526
+ }
8527
+
8528
+ // src/commands/trust.ts
8529
+ function trustCommand(args, deps) {
8530
+ const subcommand = args.positionals[0];
8531
+ if (!subcommand || subcommand === "help") {
8532
+ return { stdout: trustHelp(), stderr: "", exitCode: EXIT.OK };
8533
+ }
8534
+ if (subcommand === "prepare") return trustPrepare(args, deps);
8535
+ if (subcommand === "show") return trustShow(args, deps);
8536
+ if (subcommand === "explain") return trustExplain(args, deps);
8537
+ if (subcommand === "apply") return trustApply(args, deps);
8538
+ if (subcommand === "verify") return trustVerify(args, deps);
8539
+ return {
8540
+ stdout: "",
8541
+ stderr: `Unknown trust subcommand: ${subcommand}
8542
+ Run \`calllint trust help\`.`,
8543
+ exitCode: EXIT.USAGE
8544
+ };
8545
+ }
8546
+ var MAX_FILE_BYTES2 = 5 * 1024 * 1024;
8547
+ var MAX_DIR_ENTRIES = 2e3;
8548
+ var MAX_DIR_DEPTH = 8;
8549
+ var SKIP_DIRS2 = /* @__PURE__ */ new Set([
8550
+ "node_modules",
8551
+ ".git",
8552
+ "dist",
8553
+ "build",
8554
+ ".next",
8555
+ "coverage",
8556
+ ".turbo"
8557
+ ]);
8558
+ function loadArtifactInput(target, deps) {
8559
+ const spec = parseTargetSpec(target);
8560
+ if (spec.kind === "npm") {
8561
+ return {
8562
+ sourceType: "npm",
8563
+ source: target,
8564
+ requestedRef: spec.packageSpec?.includes("@") ? spec.packageSpec.slice(spec.packageSpec.lastIndexOf("@") + 1) : null,
8565
+ resolutionReasons: [
8566
+ "npm target requires network to pin an exact published version (offline default)"
8567
+ ],
8568
+ resolvedAt: deps.generatedAt
8569
+ };
8570
+ }
8571
+ if (spec.kind === "github") {
8572
+ return {
8573
+ sourceType: "git",
8574
+ source: target,
8575
+ requestedRef: spec.ref ?? null,
8576
+ resolutionReasons: [
8577
+ "git target requires network to pin an immutable commit (offline default)"
8578
+ ],
8579
+ resolvedAt: deps.generatedAt
8580
+ };
8581
+ }
8582
+ const abs = resolvePath4(deps.cwd, target);
8583
+ if (!existsSync11(abs)) {
8584
+ return { error: `Target not found: ${target}`, exitCode: EXIT.USAGE };
8585
+ }
8586
+ let stat;
8587
+ try {
8588
+ stat = statSync4(abs);
8589
+ } catch (err2) {
8590
+ return { error: `Cannot stat target: ${err2.message}`, exitCode: EXIT.USAGE };
8591
+ }
8592
+ if (stat.isDirectory()) {
8593
+ const entries = readDirEntries(abs);
8594
+ return {
8595
+ sourceType: "dir",
8596
+ source: target,
8597
+ requestedRef: null,
8598
+ entries,
8599
+ resolutionReasons: entries.length === 0 ? ["directory has no readable files to digest"] : [],
8600
+ resolvedAt: deps.generatedAt
8601
+ };
8602
+ }
8603
+ const name = basename4(abs).toLowerCase();
8604
+ const sourceType = name.endsWith(".json") && (name.includes("mcp") || name.includes("settings")) ? "mcp-config" : "file";
8605
+ let content;
8606
+ try {
8607
+ content = readFileSync18(abs, "utf8").slice(0, MAX_FILE_BYTES2);
8608
+ } catch (err2) {
8609
+ return { error: `Cannot read target: ${err2.message}`, exitCode: EXIT.USAGE };
8610
+ }
8611
+ return {
8612
+ sourceType,
8613
+ source: target,
8614
+ requestedRef: null,
8615
+ content,
8616
+ resolvedAt: deps.generatedAt
8617
+ };
8618
+ }
8619
+ function readDirEntries(absDir) {
8620
+ const entries = [];
8621
+ function walk(dir, depth) {
8622
+ if (depth > MAX_DIR_DEPTH || entries.length >= MAX_DIR_ENTRIES) return;
8623
+ let dirents;
8624
+ try {
8625
+ dirents = readdirSync2(dir, { withFileTypes: true, encoding: "utf8" });
8626
+ } catch {
8627
+ return;
8628
+ }
8629
+ dirents.sort((a, b) => a.name < b.name ? -1 : a.name > b.name ? 1 : 0);
8630
+ for (const e of dirents) {
8631
+ if (entries.length >= MAX_DIR_ENTRIES) return;
8632
+ if (e.isSymbolicLink()) continue;
8633
+ const full = join16(dir, e.name);
8634
+ if (e.isDirectory()) {
8635
+ if (SKIP_DIRS2.has(e.name)) continue;
8636
+ walk(full, depth + 1);
8637
+ } else if (e.isFile()) {
8638
+ let text;
8639
+ try {
8640
+ text = readFileSync18(full, "utf8").slice(0, MAX_FILE_BYTES2);
8641
+ } catch {
8642
+ continue;
8643
+ }
8644
+ const rel = full.slice(absDir.length).replace(/^[/\\]/, "").replace(/\\/g, "/");
8645
+ entries.push({ path: rel, content: text });
8646
+ }
8647
+ }
8648
+ }
8649
+ walk(absDir, 0);
8650
+ return entries;
8651
+ }
8652
+ var INSTRUCTION_SURFACES = [
8653
+ { match: (r) => r === "SKILL.md", kind: "skill" },
8654
+ { match: (r) => r === "AGENTS.md" || r === "CLAUDE.md", kind: "agents" },
8655
+ { match: (r) => r === "README.md", kind: "readme" },
8656
+ { match: (r) => r.startsWith(".cursor/rules/") && r.endsWith(".md"), kind: "agents" }
8657
+ ];
8658
+ function surfaceKindFor(rel) {
8659
+ for (const s of INSTRUCTION_SURFACES) if (s.match(rel)) return s.kind;
8660
+ return null;
8661
+ }
8662
+ function buildAuthorityForTarget(input, artifactDigest) {
8663
+ const servers = [];
8664
+ const surfaces = [];
8665
+ if (input.sourceType === "mcp-config" && typeof input.content === "string") {
8666
+ try {
8667
+ servers.push(...parseConfigText(input.content, input.source).servers);
8668
+ } catch {
8669
+ }
8670
+ }
8671
+ if (input.sourceType === "dir" && input.entries) {
8672
+ for (const e of input.entries) {
8673
+ const kind = surfaceKindFor(e.path);
8674
+ if (kind) {
8675
+ surfaces.push({
8676
+ path: e.path,
8677
+ kind,
8678
+ text: e.content,
8679
+ truncated: e.content.length >= MAX_FILE_BYTES2
8680
+ });
8681
+ }
8682
+ }
8683
+ } else if (input.sourceType === "file" && typeof input.content === "string") {
8684
+ const rel = basename4(input.source);
8685
+ const kind = surfaceKindFor(rel) ?? (rel.toLowerCase().endsWith(".md") ? "readme" : null);
8686
+ if (kind) {
8687
+ surfaces.push({
8688
+ path: rel,
8689
+ kind,
8690
+ text: input.content,
8691
+ truncated: input.content.length >= MAX_FILE_BYTES2
8692
+ });
8693
+ }
8694
+ }
8695
+ return buildAuthorityManifest({ artifactDigest, servers, surfaces });
8696
+ }
8697
+ function defaultHostConfigPath(host) {
8698
+ if (host === CLAUDE_CODE_HOST_ID) return join16(homedir(), ".claude.json");
8699
+ return null;
8700
+ }
8701
+ function plannedServersFor(input, host) {
8702
+ if (input.sourceType !== "mcp-config" || typeof input.content !== "string") return [];
8703
+ let servers;
8704
+ try {
8705
+ servers = parseConfigText(input.content, input.source).servers;
8706
+ } catch {
8707
+ return [];
8708
+ }
8709
+ return servers.map((s) => ({
8710
+ name: s.name,
8711
+ entry: claudeCodeServerEntry({ command: s.command, args: s.args, url: s.url, envKeys: s.envKeys })
8712
+ }));
8713
+ }
8714
+ function buildPlanForHost(host, input, artifactDigest, authority, decision, deps, configPathOverride) {
8715
+ const adapter = getHostAdapter(host);
8716
+ if (!adapter) {
8717
+ return { error: `Unknown host "${host}". Known hosts: ${CLAUDE_CODE_HOST_ID}`, exitCode: EXIT.USAGE };
8718
+ }
8719
+ const servers = plannedServersFor(input, host);
8720
+ if (servers.length === 0) return null;
8721
+ const configPath = configPathOverride ? resolvePath4(deps.cwd, configPathOverride) : defaultHostConfigPath(host);
8722
+ if (!configPath) {
8723
+ return { error: `No default config path known for host "${host}"; pass --host-config <path>`, exitCode: EXIT.USAGE };
8724
+ }
8725
+ let currentConfig = null;
8726
+ let configDigest = "absent";
8727
+ if (existsSync11(configPath)) {
8728
+ try {
8729
+ const bytes = readFileSync18(configPath, "utf8");
8730
+ configDigest = hashJson(bytes);
8731
+ currentConfig = JSON.parse(bytes);
8732
+ } catch {
8733
+ return { error: `Host config exists but is not readable/valid JSON: ${configPath}`, exitCode: EXIT.ERROR };
8734
+ }
8735
+ }
8736
+ const backupPath = `${configPath}.calllint-backup`;
8737
+ const ctx = {
8738
+ host,
8739
+ tier: adapter.tier,
8740
+ configPath,
8741
+ configDigest,
8742
+ currentConfig,
8743
+ servers,
8744
+ backupPath,
8745
+ expiresAt: planExpiry(deps.generatedAt)
8746
+ };
8747
+ const plan = adapter.createPlan(ctx, { artifactDigest, authority, decision });
8748
+ const check = adapter.validatePlan(plan);
8749
+ if (!check.ok) {
8750
+ return { error: `Generated plan failed validation: ${check.errors.join("; ")}`, exitCode: EXIT.ERROR };
8751
+ }
8752
+ return plan;
8753
+ }
8754
+ function planExpiry(generatedAt) {
8755
+ const t = Date.parse(generatedAt);
8756
+ if (Number.isNaN(t)) return generatedAt;
8757
+ return new Date(t + 60 * 60 * 1e3).toISOString();
8758
+ }
8759
+ function writePlanFile(plan, deps) {
8760
+ const dir = join16(deps.cwd, ".calllint", "plans");
8761
+ mkdirSync5(dir, { recursive: true });
8762
+ const file = join16(dir, `${plan.planId}.json`);
8763
+ writeFileSync10(file, JSON.stringify(plan, null, 2) + "\n", "utf8");
8764
+ return file;
8765
+ }
8766
+ function loadEvidence2(file, deps, format, provider) {
8767
+ let rawText;
8768
+ try {
8769
+ rawText = readFileSync18(resolvePath4(deps.cwd, file), "utf8");
8770
+ } catch (err2) {
8771
+ const e = err2;
8772
+ return {
8773
+ error: e.code === "ENOENT" ? `Evidence file not found: ${file}` : e.message,
8774
+ exitCode: EXIT.USAGE
8775
+ };
8776
+ }
8777
+ return importEvidence(rawText, { provider, format });
8778
+ }
8779
+ function trustPrepare(args, deps) {
8780
+ const target = args.positionals[1];
8781
+ if (!target) {
8782
+ return {
8783
+ stdout: "",
8784
+ stderr: "Error: Missing target\nUsage: calllint trust prepare <git-url|dir|SKILL.md|mcp.json> [--evidence <file>] [--json]",
8785
+ exitCode: EXIT.USAGE
8786
+ };
8787
+ }
8788
+ const input = loadArtifactInput(target, deps);
8789
+ if ("error" in input) {
8790
+ return { stdout: "", stderr: `Error: ${input.error}`, exitCode: input.exitCode };
8791
+ }
8792
+ if (flagBool(args.flags, "with-skillspector")) {
8793
+ return {
8794
+ stdout: "",
8795
+ stderr: "Error: --with-skillspector (pinned runner) is not wired yet.\nRun SkillSpector yourself, then attach its report:\n calllint trust prepare " + target + " --evidence skillspector-report.json",
8796
+ exitCode: EXIT.USAGE
8797
+ };
8798
+ }
8799
+ const evidenceFile = typeof args.flags["evidence"] === "string" ? args.flags["evidence"] : void 0;
8800
+ const formatFlag = args.flags["format"];
8801
+ const format = formatFlag === "sarif" ? "sarif" : formatFlag === "json" ? "json" : void 0;
8802
+ const providerFlag = typeof args.flags["provider"] === "string" ? args.flags["provider"] : void 0;
8803
+ let evidence;
8804
+ if (evidenceFile) {
8805
+ const imported = loadEvidence2(evidenceFile, deps, format, providerFlag);
8806
+ if ("error" in imported) {
8807
+ return { stdout: "", stderr: `Error: ${imported.error}`, exitCode: imported.exitCode };
8808
+ }
8809
+ evidence = [imported];
8810
+ }
8811
+ const artifact = resolveArtifactIdentity(input);
8812
+ const authority = buildAuthorityForTarget(input, artifact.digest ?? null);
8813
+ const policyPath = flagStr(args.flags, "policy");
8814
+ const policy = loadPolicyOrDefault(policyPath ? resolvePath4(deps.cwd, policyPath) : void 0);
8815
+ const decision = artifact.resolution === "resolved" ? decideOverAuthority({ authority, evidence, policy }) : void 0;
8816
+ let plan;
8817
+ const hostFlag = flagStr(args.flags, "host");
8818
+ if (hostFlag && decision && decision.verdict !== "UNKNOWN") {
8819
+ const hostConfig = flagStr(args.flags, "host-config");
8820
+ const built = buildPlanForHost(hostFlag, input, artifact.digest ?? null, authority, decision, deps, hostConfig);
8821
+ if (built && "error" in built) {
8822
+ return { stdout: "", stderr: `Error: ${built.error}`, exitCode: built.exitCode };
8823
+ }
8824
+ plan = built ?? void 0;
8825
+ }
8826
+ const preparation = prepare({
8827
+ artifact,
8828
+ evidence,
8829
+ authority,
8830
+ decision,
8831
+ plan,
8832
+ preparedAt: deps.generatedAt
8833
+ });
8834
+ const exitCode = prepareExitCode(preparation);
8835
+ let planNote = "";
8836
+ if (preparation.plan && flagBool(args.flags, "write-plan")) {
8837
+ const file = writePlanFile(preparation.plan, deps);
8838
+ planNote = `
8839
+ plan written: ${file}
8840
+ `;
8841
+ }
8842
+ if (flagBool(args.flags, "json")) {
8843
+ return { stdout: JSON.stringify(preparation, null, 2), stderr: "", exitCode };
8844
+ }
8845
+ return { stdout: renderPreparation(preparation) + planNote, stderr: "", exitCode };
8846
+ }
8847
+ function loadPreparation(args, deps) {
8848
+ const file = args.positionals[1];
8849
+ if (!file) {
8850
+ return { error: "Missing preparation file", exitCode: EXIT.USAGE };
8851
+ }
8852
+ let raw;
8853
+ try {
8854
+ raw = readFileSync18(join16(deps.cwd, file), "utf8");
8855
+ } catch (err2) {
8856
+ const e = err2;
8857
+ return {
8858
+ error: e.code === "ENOENT" ? `File not found: ${file}` : e.message,
8859
+ exitCode: EXIT.USAGE
8860
+ };
8861
+ }
8862
+ let parsed;
8863
+ try {
8864
+ parsed = JSON.parse(raw);
8865
+ } catch {
8866
+ return { error: `Not valid JSON: ${file}`, exitCode: EXIT.ERROR };
8867
+ }
8868
+ if (!parsed || typeof parsed !== "object" || parsed.schema !== "calllint.trust-preparation.v0") {
8869
+ return { error: `Not a calllint.trust-preparation.v0 document: ${file}`, exitCode: EXIT.ERROR };
8870
+ }
8871
+ return parsed;
8872
+ }
8873
+ function trustShow(args, deps) {
8874
+ const prep = loadPreparation(args, deps);
8875
+ if ("error" in prep) return { stdout: "", stderr: `Error: ${prep.error}`, exitCode: prep.exitCode };
8876
+ if (flagBool(args.flags, "json")) {
8877
+ return { stdout: JSON.stringify(prep, null, 2), stderr: "", exitCode: EXIT.OK };
8878
+ }
8879
+ return { stdout: renderPreparation(prep), stderr: "", exitCode: EXIT.OK };
8880
+ }
8881
+ function trustExplain(args, deps) {
8882
+ const prep = loadPreparation(args, deps);
8883
+ if ("error" in prep) return { stdout: "", stderr: `Error: ${prep.error}`, exitCode: prep.exitCode };
8884
+ return { stdout: renderExplanation(prep), stderr: "", exitCode: EXIT.OK };
8885
+ }
8886
+ function receiptId(planDigest, now) {
8887
+ return "clrec_" + hashJson({ planDigest, now }).slice("sha256:".length, "sha256:".length + 16);
8888
+ }
8889
+ function applyExitCode(r) {
8890
+ if (r.outcome === "applied") return EXIT.OK;
8891
+ if (r.outcome === "already_applied") return EXIT.REVIEW;
8892
+ return EXIT.UNKNOWN;
8893
+ }
8894
+ function trustApply(args, deps) {
8895
+ const planFile = flagStr(args.flags, "plan");
8896
+ const approve = flagStr(args.flags, "approve");
8897
+ if (!planFile) {
8898
+ return usageErr("Missing --plan <file>\nUsage: calllint trust apply --plan <plan.json> --approve <plan-digest>");
8899
+ }
8900
+ if (!approve) {
8901
+ return usageErr("Missing --approve <plan-digest>\nApproval must name the exact plan digest you reviewed (see `trust prepare`).");
8902
+ }
8903
+ let plan;
8904
+ try {
8905
+ plan = JSON.parse(readFileSync18(resolvePath4(deps.cwd, planFile), "utf8"));
8906
+ } catch (err2) {
8907
+ const e = err2;
8908
+ return { stdout: "", stderr: e.code === "ENOENT" ? `Plan file not found: ${planFile}` : `Plan file is not valid JSON: ${e.message}`, exitCode: EXIT.ERROR };
8909
+ }
8910
+ if (plan?.schema !== "calllint.install-plan.v1") {
8911
+ return { stdout: "", stderr: `Not a calllint.install-plan.v1 document: ${planFile}`, exitCode: EXIT.ERROR };
8912
+ }
8913
+ const adapter = getHostAdapter(plan.host);
8914
+ if (!adapter) return usageErr(`Unknown host "${plan.host}". Known hosts: ${CLAUDE_CODE_HOST_ID}`);
8915
+ if (!adapter.applyPlan) {
8916
+ return usageErr(`Host "${plan.host}" is tier ${adapter.tier} \u2014 plan-only; copy the patch or use a Tier-A host to apply.`);
8917
+ }
8918
+ const rawTarget = plan.operations[0]?.target;
8919
+ if (!rawTarget) return usageErr("Plan has no operations to apply.");
8920
+ let configPath;
8921
+ try {
8922
+ configPath = safeConfigPath(rawTarget, { cwd: deps.cwd, home: homedir() });
8923
+ } catch (err2) {
8924
+ if (err2 instanceof PathSafetyError) return { stdout: "", stderr: `Unsafe target path in plan: ${err2.message}`, exitCode: EXIT.ERROR };
8925
+ throw err2;
8926
+ }
8927
+ const rid = receiptId(plan.planDigest, deps.generatedAt);
8928
+ const backupPath = `${configPath}.calllint-backup-${rid}`;
8929
+ const lockPath = join16(deps.cwd, ".calllint", "locks", `${hashJson(configPath).slice("sha256:".length, "sha256:".length + 16)}.lock`);
8930
+ const result = adapter.applyPlan(plan, {
8931
+ approvalDigest: approve,
8932
+ configPath,
8933
+ backupPath,
8934
+ lockPath,
8935
+ fs: nodeFsPort(),
8936
+ now: deps.generatedAt
8937
+ });
8938
+ let receiptNote = "";
8939
+ const receiptOut = flagStr(args.flags, "receipt");
8940
+ if (receiptOut) {
8941
+ const err2 = emitReceipt(result, plan, args, deps, receiptOut);
8942
+ if (err2) return err2;
8943
+ receiptNote = `
8944
+ receipt: ${resolvePath4(deps.cwd, receiptOut)}
8945
+ `;
8946
+ }
8947
+ if (flagBool(args.flags, "json")) {
8948
+ return { stdout: JSON.stringify(result, null, 2), stderr: "", exitCode: applyExitCode(result) };
8949
+ }
8950
+ return { stdout: renderApplyResult(result) + receiptNote, stderr: "", exitCode: applyExitCode(result) };
8951
+ }
8952
+ function emitReceipt(result, plan, args, deps, outFile) {
8953
+ const approver = flagStr(args.flags, "approver") ?? (userInfo().username || null);
8954
+ let receipt = buildDecisionReceipt(result, plan, {
8955
+ approvedAt: deps.generatedAt,
8956
+ approver,
8957
+ scannerVersion: deps.toolVersion ?? "0.0.0-dev",
8958
+ policyVersion: null
8959
+ });
8960
+ const keyFile = flagStr(args.flags, "key");
8961
+ if (flagBool(args.flags, "sign") || keyFile) {
8962
+ if (!keyFile) return usageErr("--sign requires --key <keyfile> (a local ed25519 keypair from `receipt keygen`)");
8963
+ try {
8964
+ const kp = importKeypair(JSON.parse(readFileSync18(resolvePath4(deps.cwd, keyFile), "utf8")));
8965
+ receipt = signDecisionReceipt(receipt, kp);
8966
+ } catch (err2) {
8967
+ return { stdout: "", stderr: `Cannot load signing key: ${err2.message}`, exitCode: EXIT.ERROR };
8968
+ }
8969
+ }
8970
+ try {
8971
+ writeFileSync10(resolvePath4(deps.cwd, outFile), JSON.stringify(receipt, null, 2) + "\n");
8972
+ } catch (err2) {
8973
+ return { stdout: "", stderr: `Cannot write receipt: ${err2.message}`, exitCode: EXIT.ERROR };
8974
+ }
8975
+ return null;
8976
+ }
8977
+ function trustVerify(args, deps) {
8978
+ const file = args.positionals[1];
8979
+ if (!file) return usageErr("Usage: calllint trust verify <receipt.json> [--public-key <keyfile>]");
8980
+ let parsed;
8981
+ try {
8982
+ parsed = JSON.parse(readFileSync18(resolvePath4(deps.cwd, file), "utf8"));
8983
+ } catch (err2) {
8984
+ const e = err2;
8985
+ return { stdout: "", stderr: e.code === "ENOENT" ? `Receipt file not found: ${file}` : `Receipt is not valid JSON: ${e.message}`, exitCode: EXIT.ERROR };
8986
+ }
8987
+ let publicKey;
8988
+ const pkFile = flagStr(args.flags, "public-key");
8989
+ if (pkFile) {
8990
+ try {
8991
+ const j = JSON.parse(readFileSync18(resolvePath4(deps.cwd, pkFile), "utf8"));
8992
+ publicKey = j.public_key ?? j.publicKey;
8993
+ if (typeof publicKey !== "string") throw new Error("no public_key field");
8994
+ } catch (err2) {
8995
+ return { stdout: "", stderr: `Cannot load public key: ${err2.message}`, exitCode: EXIT.ERROR };
8996
+ }
8997
+ }
8998
+ const res = verifyDecisionReceipt(parsed, { now: deps.generatedAt, publicKey });
8999
+ if (flagBool(args.flags, "json")) {
9000
+ return { stdout: JSON.stringify(res, null, 2), stderr: "", exitCode: res.valid ? EXIT.OK : 1 };
9001
+ }
9002
+ let out = `
9003
+ CallLint trust verify
9004
+ `;
9005
+ out += `receipt: ${resolvePath4(deps.cwd, file)}
9006
+ `;
9007
+ out += `structure: ${res.valid ? "\u2705 valid" : "\u26D4 invalid"}
9008
+ `;
9009
+ out += `signed: ${res.signed ? publicKey ? res.tampered ? "\u26D4 signature INVALID" : "\u2705 signature verified" : "\u25C7 present (no --public-key; not checked)" : "\u25C7 unsigned"}
9010
+ `;
9011
+ out += `expiry: ${res.expired ? "\u26A0 EXPIRED" : "\u2705 within validity window"}
9012
+ `;
9013
+ if (res.errors.length > 0) {
9014
+ out += `
9015
+ errors:
9016
+ `;
9017
+ for (const e of res.errors) out += ` \u2022 ${e}
9018
+ `;
9019
+ }
9020
+ return { stdout: out, stderr: "", exitCode: res.valid ? EXIT.OK : 1 };
9021
+ }
9022
+ function usageErr(msg) {
9023
+ return { stdout: "", stderr: `Error: ${msg}`, exitCode: EXIT.USAGE };
9024
+ }
9025
+ var STATE_BADGE = {
9026
+ PLAN_READY: "\u25C7 prepared (read-only)",
9027
+ AUTHORITY_NORMALIZED: "\u25C7 authority normalized (read-only)",
9028
+ DECIDED: "\u25C7 decided (read-only)",
9029
+ FETCH_REJECTED: "\u26A0 not a verified target",
9030
+ RESOLUTION_FAILED: "\u26D4 could not resolve",
9031
+ EVIDENCE_PARTIAL: "\u26A0 evidence partial",
9032
+ EVIDENCE_FAILED: "\u26D4 evidence failed",
9033
+ POLICY_UNKNOWN: "\u25C7 policy unknown (insufficient evidence)",
9034
+ VERIFIED: "\u2705 applied + verified",
9035
+ APPLY_CONFLICT: "\u26D4 config conflict",
9036
+ PLAN_STALE: "\u26D4 plan stale",
9037
+ VERIFICATION_FAILED: "\u21A9 verify failed \u2014 rolled back",
9038
+ ROLLBACK_REQUIRED: "\u{1F6A8} rollback required"
9039
+ };
9040
+ var VERDICT_SYMBOL = {
9041
+ SAFE: "\u{1F6E1} SAFE",
9042
+ REVIEW: "\u26A0 REVIEW",
9043
+ BLOCK: "\u26D4 BLOCK",
9044
+ UNKNOWN: "\u25C7 UNKNOWN"
9045
+ };
9046
+ function renderPreparation(p) {
9047
+ const a = p.artifact;
9048
+ let out = `
9049
+ CallLint trust prepare (read-only)
9050
+ `;
9051
+ out += `target: ${a.source}
9052
+ `;
9053
+ out += `source type: ${a.sourceType}
9054
+ `;
9055
+ out += `requested: ${a.requestedRef ?? "(none)"}
9056
+ `;
9057
+ out += `resolved ref: ${a.resolvedRef ?? "(unresolved)"}
9058
+ `;
9059
+ out += `digest: ${a.digest ?? "(none)"}
9060
+ `;
9061
+ out += `resolution: ${a.resolution}
9062
+ `;
9063
+ out += `state: ${STATE_BADGE[p.state] ?? p.state}
9064
+ `;
9065
+ if (p.evidence === null) {
9066
+ out += `evidence: (none attached \u2014 pass --evidence <file>)
9067
+ `;
9068
+ } else {
9069
+ out += `evidence: ${p.evidence.length} provider(s)
9070
+ `;
9071
+ for (const e of p.evidence) {
9072
+ out += ` \u2022 ${e.provider} (${e.providerVersion}) \u2014 ${e.scanMode}, ${e.completeness}, ${e.findings.length} finding(s), not re-scored
9073
+ `;
9074
+ }
9075
+ }
9076
+ if (p.authority === null) {
9077
+ out += `authority: (not normalized \u2014 G3)
9078
+ `;
9079
+ } else {
9080
+ const caps = p.authority.capabilities;
9081
+ out += `authority: ${caps.length} capabilit${caps.length === 1 ? "y" : "ies"}`;
9082
+ out += p.authority.completeness === "partial" ? " (partial)\n" : "\n";
9083
+ for (const c of caps) {
9084
+ const dst = c.destination ? ` \u2192 ${c.destination}` : "";
9085
+ const appr = c.approvalRequirement === "none" ? "" : ` [${c.approvalRequirement}]`;
9086
+ out += ` \u2022 ${c.action} ${c.resource}${dst}${appr} (${c.evidenceSource})
9087
+ `;
9088
+ }
9089
+ if (p.authority.approval.required.length > 0) {
9090
+ out += ` approvals required: ${p.authority.approval.required.join(", ")}
9091
+ `;
9092
+ }
9093
+ }
9094
+ if (p.decision === null) {
9095
+ out += `decision: (not decided \u2014 G4)
9096
+ `;
9097
+ } else {
9098
+ const d = p.decision;
9099
+ out += `decision: ${VERDICT_SYMBOL[d.verdict] ?? d.verdict}`;
9100
+ out += d.completeness === "partial" ? " (over partial evidence)\n" : "\n";
9101
+ for (const r of d.reasons) {
9102
+ out += ` \u2022 ${r.code} \u2192 ${r.contributes} (${r.evidenceSource})
9103
+ `;
9104
+ }
9105
+ if (d.requiredApprovals.length > 0) {
9106
+ out += ` approvals required: ${d.requiredApprovals.join(", ")}
9107
+ `;
9108
+ }
9109
+ }
9110
+ if (p.plan === null) {
9111
+ out += `plan: (none \u2014 pass --host <id> for an install plan)
9112
+ `;
9113
+ } else {
9114
+ const pl = p.plan;
9115
+ out += `plan: host "${pl.host}" (tier ${pl.tier}) \u2014 ${pl.operations.length} op(s), ${pl.rollback.length} rollback op(s)
9116
+ `;
9117
+ out += ` plan id: ${pl.planId}
9118
+ `;
9119
+ out += ` plan digest:${pl.planDigest}
9120
+ `;
9121
+ out += ` expires: ${pl.expiresAt}
9122
+ `;
9123
+ out += ` NOT applied \u2014 review, then: calllint trust apply --plan <file> --approve ${pl.planDigest}
9124
+ `;
9125
+ }
9126
+ if (p.notes.length > 0) {
9127
+ out += `
9128
+ notes:
9129
+ `;
9130
+ for (const n of p.notes) out += ` \u2022 ${n}
9131
+ `;
9132
+ }
9133
+ out += `
9134
+ This is the READ-ONLY half of the Trust Gateway. It touched no live config
9135
+ `;
9136
+ out += `and never executed the target. An unresolved target is never a pass.
9137
+ `;
9138
+ return out;
9139
+ }
9140
+ function renderExplanation(p) {
9141
+ let out = `
9142
+ CallLint trust explain
9143
+ `;
9144
+ out += `state: ${p.state}
9145
+
9146
+ `;
9147
+ switch (p.state) {
9148
+ case "AUTHORITY_NORMALIZED": {
9149
+ const caps = p.authority?.capabilities.length ?? 0;
9150
+ out += `The artifact resolved to an immutable, digested identity
9151
+ `;
9152
+ out += `(${p.artifact.resolvedRef}) and its authority was normalized into a
9153
+ `;
9154
+ out += `manifest of ${caps} capabilit${caps === 1 ? "y" : "ies"}, each pinned to the evidence byte
9155
+ `;
9156
+ out += `that granted it. This is an inventory, not a verdict \u2014 the deterministic
9157
+ `;
9158
+ out += `decision (G4) will bind this manifest's digest. UNKNOWN is never SAFE.
9159
+ `;
9160
+ break;
9161
+ }
9162
+ case "DECIDED": {
9163
+ const d = p.decision;
9164
+ out += `The artifact resolved and its authority was normalized, then the
9165
+ `;
9166
+ out += `deterministic policy decided ${d?.verdict ?? "?"} over the manifest \u2014
9167
+ `;
9168
+ out += `binding the artifact, authority, and policy digests. The verdict comes
9169
+ `;
9170
+ out += `from normalized authority + policy, never from a scanner: external
9171
+ `;
9172
+ out += `evidence can add reasons or lower confidence, but never sets it alone.
9173
+ `;
9174
+ break;
9175
+ }
9176
+ case "POLICY_UNKNOWN":
9177
+ out += `The policy could not reach a confident verdict \u2014 authority or evidence
9178
+ `;
9179
+ out += `was incomplete, so the decision is UNKNOWN. Insufficient evidence is
9180
+ `;
9181
+ out += `fail-closed: UNKNOWN outranks REVIEW and never reads as a pass.
9182
+ `;
9183
+ break;
9184
+ case "PLAN_READY":
9185
+ out += `The artifact resolved to an immutable, digested identity
9186
+ `;
9187
+ out += `(${p.artifact.resolvedRef}). Downstream evidence, authority, and a
9188
+ `;
9189
+ out += `deterministic decision will bind this digest as Phase G lands.
9190
+ `;
9191
+ break;
9192
+ case "FETCH_REJECTED":
9193
+ out += `The artifact could not be fully pinned \u2014 either an immutable ref or
9194
+ `;
9195
+ out += `the bytes to digest were missing. It is NOT a verified target and the
9196
+ `;
9197
+ out += `gateway will not advance to a plan.
9198
+ `;
9199
+ break;
9200
+ case "RESOLUTION_FAILED":
9201
+ out += `The target could not be resolved to an immutable, digested identity.
9202
+ `;
9203
+ out += `Nothing can be evaluated. UNKNOWN is never SAFE.
9204
+ `;
9205
+ break;
9206
+ default:
9207
+ out += `The read-only preparation stopped in a state that does not read as a
9208
+ `;
9209
+ out += `pass. See the notes for why.
9210
+ `;
9211
+ }
9212
+ if (p.notes.length > 0) {
9213
+ out += `
9214
+ why:
9215
+ `;
9216
+ for (const n of p.notes) out += ` \u2022 ${n}
9217
+ `;
9218
+ }
9219
+ return out;
9220
+ }
9221
+ var APPLY_BADGE = {
9222
+ applied: "\u2705 applied + verified",
9223
+ already_applied: "\u25C7 already applied (no change)",
9224
+ stale: "\u26D4 plan stale \u2014 not applied",
9225
+ conflict: "\u26D4 config conflict \u2014 not applied",
9226
+ rolled_back: "\u21A9 verify failed \u2014 rolled back to original",
9227
+ rollback_failed: "\u{1F6A8} rollback FAILED \u2014 manual intervention required"
9228
+ };
9229
+ function renderApplyResult(r) {
9230
+ let out = `
9231
+ CallLint trust apply
9232
+ `;
9233
+ out += `host: ${r.host}
9234
+ `;
9235
+ out += `config: ${r.configPath}
9236
+ `;
9237
+ out += `plan id: ${r.planId}
9238
+ `;
9239
+ out += `plan digest: ${r.planDigest}
9240
+ `;
9241
+ out += `state: ${r.state}
9242
+ `;
9243
+ out += `outcome: ${APPLY_BADGE[r.outcome] ?? r.outcome}
9244
+ `;
9245
+ out += `before: ${r.configDigestBefore}
9246
+ `;
9247
+ out += `after: ${r.configDigestAfter ?? "(unchanged / not written)"}
9248
+ `;
9249
+ if (r.backupPath) out += `backup: ${r.backupPath}
9250
+ `;
9251
+ if (r.notes.length > 0) {
9252
+ out += `
9253
+ notes:
9254
+ `;
9255
+ for (const n of r.notes) out += ` \u2022 ${n}
9256
+ `;
9257
+ }
9258
+ if (r.outcome === "applied") {
9259
+ out += `
9260
+ The config was written atomically and re-verified. To undo, restore the
9261
+ `;
9262
+ out += `backup above (the plan also carries typed rollback operations).
9263
+ `;
9264
+ } else if (r.outcome === "rollback_failed") {
9265
+ out += `
9266
+ The write could not be verified AND the automatic rollback failed. Your
9267
+ `;
9268
+ out += `original config is preserved at the backup path \u2014 restore it by hand.
9269
+ `;
9270
+ }
9271
+ return out;
9272
+ }
9273
+ function trustHelp() {
9274
+ return `
9275
+ calllint trust \u2014 Automated Trust Gateway (prepare \u2192 approve \u2192 apply \u2192 verify)
9276
+
9277
+ USAGE
9278
+ calllint trust prepare <target> [--evidence <file>] [--host <id>] [--json]
9279
+ calllint trust show <preparation.json> Human summary of a preparation
9280
+ calllint trust explain <preparation.json> Why this state / these notes
9281
+ calllint trust apply --plan <p> --approve <plan-digest> Apply an approved plan
9282
+ calllint trust verify <receipt.json> [--public-key <k>] Verify a decision receipt
9283
+
9284
+ DESCRIPTION
9285
+ \`trust prepare\` resolves a target (a directory, SKILL.md, mcp.json, or a
9286
+ remote git/npm ref) to an immutable, digest-pinned Artifact Identity
9287
+ (calllint.artifact.v1) and produces a READ-ONLY preview. It touches no live
9288
+ config and NEVER executes the target \u2014 it only reads bytes to digest them.
9289
+
9290
+ Attach a third-party scanner report with --evidence <file>: it is imported
9291
+ provenance-preserved and NEVER re-scored. Degraded or failed evidence can only
9292
+ tighten the preparation (fail-closed) \u2014 a degraded external scan never reads
9293
+ as a pass. An external SAFE never upgrades a CallLint verdict.
9294
+
9295
+ Remote git/npm targets need network to pin an immutable ref; offline they
9296
+ degrade explicitly (never a silent pass). Authority, Decision, and Install
9297
+ Plan slots are populated by later Phase-G steps.
9298
+
9299
+ OPTIONS (prepare)
9300
+ --evidence <file> Attach a third-party scanner report (JSON or SARIF)
9301
+ --format json|sarif Force the evidence format (default: auto-detect)
9302
+ --provider <name> Force the evidence provider adapter (default: auto-detect)
9303
+ --policy <file> Use a policy file for the decision (default: built-in defaults)
9304
+ --host <id> Build an install plan for a host (G5: claude-code). Reads
9305
+ the host config READ-ONLY; never applies. Plan is emitted
9306
+ only for a non-blocking decision (SAFE/REVIEW).
9307
+ --host-config <path> Override the host config path (default: ~/.claude.json)
9308
+ --write-plan Persist the plan to .calllint/plans/<plan-id>.json
9309
+ --no-llm Default posture: no LLM in the verdict path (accepted, no-op)
9310
+ --json Emit the raw calllint.trust-preparation.v0 document
9311
+
9312
+ OPTIONS (apply)
9313
+ --plan <file> The install plan to apply (from prepare --write-plan)
9314
+ --approve <digest> The exact plan digest you reviewed \u2014 binds the approval.
9315
+ A mismatch, a tampered plan, or an expired plan is refused
9316
+ (PLAN_STALE) before any write. Apply re-checks the target's
9317
+ precondition digest (drift \u2192 APPLY_CONFLICT), writes
9318
+ atomically with a backup, re-verifies, and rolls back on
9319
+ failure. Re-applying the same plan is a no-op.
9320
+ --receipt <file> After apply, write a calllint.receipt.v1 decision receipt
9321
+ (durable proof of the six-digest chain + approval + result)
9322
+ --sign --key <file> Sign the receipt with a local ed25519 keypair (\`receipt keygen\`)
9323
+ --approver <name> Attribution for the receipt (default: OS user)
9324
+ --json Emit the raw calllint.apply-result.v1 document
9325
+
9326
+ OPTIONS (verify)
9327
+ --public-key <file> Public key JSON to check the receipt's ed25519 signature.
9328
+ Without it, a signature is shape-checked but not verified.
9329
+ verify is READ-ONLY: it never re-judges, re-scans, or writes.
9330
+ --json Emit the raw verification result
9331
+
9332
+ TARGETS
9333
+ ./path/to/dir a local directory (tree-digested, read-only)
9334
+ ./SKILL.md a single file
9335
+ ./.cursor/mcp.json an MCP config
9336
+ npm:<pkg>[@version] an npm package (needs network to pin \u2014 offline: unresolved)
9337
+ github:<owner/repo>[@ref] a git repo (needs network to pin \u2014 offline: unresolved)
9338
+
9339
+ EXIT CODES
9340
+ 0 decision SAFE (or resolved read-only preview with no blocking authority)
9341
+ 10 decision REVIEW / target not fully pinned / evidence partial
9342
+ 20 decision BLOCK or UNKNOWN / unresolved / fail-closed (never a pass)
9343
+ 2 usage error / target not found
9344
+ 3 malformed preparation document
9345
+
9346
+ SEE ALSO
9347
+ ADR 0035 \u2014 Automated Trust Gateway & Authority Manifest
9348
+ ADR 0036 \u2014 Install Plan & Approval Binding
9349
+ ADR 0037 \u2014 Host Adapter Safety Contract
9350
+ `;
9351
+ }
9352
+
9353
+ // src/run.ts
9354
+ function run(argv, deps) {
9355
+ const args = parseArgs(argv);
9356
+ const cmd = args.command;
9357
+ if (!cmd || cmd === "help" || cmd === "--help" || cmd === "-h") {
9358
+ return helpCommand();
9359
+ }
9360
+ switch (cmd) {
9361
+ case "check":
9362
+ return checkCommand(args, {
9363
+ cwd: deps.cwd,
9364
+ readStdin: deps.readStdin,
9365
+ now: deps.now,
9366
+ generatedAt: deps.generatedAt
9367
+ });
9368
+ case "scan-all":
9369
+ return scanAllCommand(args, {
9370
+ cwd: deps.cwd,
9371
+ now: deps.now,
9372
+ generatedAt: deps.generatedAt
9373
+ });
9374
+ case "scan":
9375
+ return scanCommand(args, {
9376
+ cwd: deps.cwd,
9377
+ readStdin: deps.readStdin,
9378
+ now: deps.now,
9379
+ generatedAt: deps.generatedAt,
9380
+ writeCacheFile: deps.writeCacheFile,
9381
+ online: deps.online,
9382
+ getChangedFilesDiff: deps.getChangedFilesDiff,
9383
+ toolVersion: deps.toolVersion
9384
+ });
9385
+ case "diagnostics":
9386
+ return diagnosticsCommand(args, {
9387
+ cwd: deps.cwd,
9388
+ readStdin: deps.readStdin,
9389
+ now: deps.now,
9390
+ generatedAt: deps.generatedAt,
9391
+ online: deps.online
9392
+ });
9393
+ case "baseline":
9394
+ return baselineCommand(args, {
9395
+ cwd: deps.cwd,
9396
+ readStdin: deps.readStdin,
9397
+ generatedAt: deps.generatedAt,
9398
+ writeBaselineFile: deps.writeCacheFile,
9399
+ online: deps.online
9400
+ });
9401
+ case "verify":
9402
+ return verifyCommand(args, {
9403
+ cwd: deps.cwd,
9404
+ readStdin: deps.readStdin,
9405
+ now: deps.now,
9406
+ generatedAt: deps.generatedAt,
9407
+ writeBaselineFile: deps.writeCacheFile,
9408
+ online: deps.online
9409
+ });
9410
+ case "approve":
9411
+ return approveCommand(args, {
9412
+ cwd: deps.cwd,
9413
+ now: deps.now,
9414
+ generatedAt: deps.generatedAt,
9415
+ writeFile: deps.writeCacheFile
9416
+ });
9417
+ case "explain":
9418
+ return explainCommand(args, { cwd: deps.cwd });
9419
+ case "receipt":
9420
+ return receiptCommand(args, { cwd: deps.cwd });
9421
+ case "action":
9422
+ return actionCommand(args, { cwd: deps.cwd, toolVersion: deps.toolVersion, generatedAt: deps.generatedAt });
9423
+ case "inbox":
9424
+ return inboxCommand(args, { cwd: deps.cwd, toolVersion: deps.toolVersion, generatedAt: deps.generatedAt });
9425
+ case "inventory":
9426
+ return inventoryCommand(args, { cwd: deps.cwd });
9427
+ case "evidence":
9428
+ return evidenceCommand(args, { cwd: deps.cwd });
9429
+ case "trust":
9430
+ return trustCommand(args, { cwd: deps.cwd, generatedAt: deps.generatedAt, toolVersion: deps.toolVersion });
9431
+ case "gen-rule":
9432
+ return genRuleCommand(args, { cwd: deps.cwd });
9433
+ case "policy":
9434
+ return policyCommand(args, { cwd: deps.cwd });
9435
+ default:
9436
+ return {
9437
+ stdout: "",
9438
+ stderr: `Unknown command: ${cmd}
9439
+ Run \`calllint help\`.`,
9440
+ exitCode: 2
9441
+ };
9442
+ }
9443
+ }
9444
+
9445
+ // ../../packages/online/src/npm.ts
9446
+ function isRecord3(v) {
9447
+ return typeof v === "object" && v !== null && !Array.isArray(v);
9448
+ }
9449
+ function parseSpec(packageSpec) {
9450
+ if (packageSpec.startsWith("@")) {
9451
+ const slash = packageSpec.indexOf("/");
9452
+ const at2 = packageSpec.indexOf("@", slash);
9453
+ if (at2 === -1) return { name: packageSpec };
9454
+ return { name: packageSpec.slice(0, at2), version: packageSpec.slice(at2 + 1) || void 0 };
9455
+ }
9456
+ const at = packageSpec.indexOf("@");
9457
+ if (at <= 0) return { name: packageSpec };
9458
+ return { name: packageSpec.slice(0, at), version: packageSpec.slice(at + 1) || void 0 };
9459
+ }
9460
+ var INSTALL_SCRIPT_KEYS = ["preinstall", "install", "postinstall"];
9461
+ async function fetchNpmFacts(packageSpec, fetchJson) {
9462
+ const { name, version } = parseSpec(packageSpec);
9463
+ const url = `https://registry.npmjs.org/${name.replace(/\//g, "%2f")}`;
9464
+ let doc;
9465
+ try {
9466
+ doc = await fetchJson(url);
9467
+ } catch {
9468
+ return { name, versionExists: false, installScripts: [] };
9469
+ }
9470
+ if (!isRecord3(doc)) return { name, versionExists: false, installScripts: [] };
9471
+ const distTags = isRecord3(doc["dist-tags"]) ? doc["dist-tags"] : {};
9472
+ const latestVersion = typeof distTags.latest === "string" ? distTags.latest : void 0;
9473
+ const versions = isRecord3(doc.versions) ? doc.versions : {};
9474
+ const isFloating = !version || version === "latest" || /[\^~><*]/.test(version);
9475
+ const resolvedVersion = isFloating ? latestVersion : version;
9476
+ const versionDoc = resolvedVersion && isRecord3(versions[resolvedVersion]) ? versions[resolvedVersion] : void 0;
9477
+ if (!versionDoc) {
6943
9478
  return { name, versionExists: false, installScripts: [], latestVersion, resolvedVersion };
6944
9479
  }
6945
9480
  const scripts = isRecord3(versionDoc.scripts) ? versionDoc.scripts : {};
@@ -7166,13 +9701,13 @@ async function breathe(argv, deps = {}) {
7166
9701
  }
7167
9702
 
7168
9703
  // src/version.ts
7169
- import { readFileSync as readFileSync16 } from "node:fs";
9704
+ import { readFileSync as readFileSync19 } from "node:fs";
7170
9705
  import { fileURLToPath } from "node:url";
7171
- import { dirname as dirname4, join as join16 } from "node:path";
9706
+ import { dirname as dirname6, join as join17 } from "node:path";
7172
9707
  function resolveToolVersion() {
7173
9708
  try {
7174
- const pkgPath = join16(dirname4(fileURLToPath(import.meta.url)), "..", "package.json");
7175
- const pkg = JSON.parse(readFileSync16(pkgPath, "utf8"));
9709
+ const pkgPath = join17(dirname6(fileURLToPath(import.meta.url)), "..", "package.json");
9710
+ const pkg = JSON.parse(readFileSync19(pkgPath, "utf8"));
7176
9711
  return typeof pkg.version === "string" && pkg.version.length > 0 ? pkg.version : "unknown";
7177
9712
  } catch {
7178
9713
  return "unknown";
@@ -7182,7 +9717,7 @@ function resolveToolVersion() {
7182
9717
  // src/index.ts
7183
9718
  function readStdin() {
7184
9719
  try {
7185
- return readFileSync17(0, "utf8");
9720
+ return readFileSync20(0, "utf8");
7186
9721
  } catch {
7187
9722
  return "";
7188
9723
  }