@slicedearth/whoisleuth-cli 1.47.4 → 2.0.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 (235) hide show
  1. package/README.md +74 -177
  2. package/SECURITY.md +6 -3
  3. package/bin/whoisleuth.mjs +2 -1
  4. package/cli/archive-inspect.mjs +28 -10
  5. package/cli/arguments.mjs +204 -174
  6. package/cli/artifact-structure.mjs +837 -163
  7. package/cli/artifact-verify.mjs +90 -61
  8. package/cli/bounded-json.mjs +1 -1
  9. package/cli/bulk-checkpoint.mjs +33 -20
  10. package/cli/bulk.mjs +9 -2
  11. package/cli/case-pack.mjs +180 -576
  12. package/cli/ci-report.mjs +6 -1
  13. package/cli/collection-preflight.mjs +2 -2
  14. package/cli/command-catalogue.mjs +12 -3
  15. package/cli/command-reference.mjs +910 -92
  16. package/cli/completion.mjs +504 -226
  17. package/cli/config-profile.mjs +13 -3
  18. package/cli/discover.mjs +7 -1
  19. package/cli/discovery-observation-snapshot.mjs +24 -28
  20. package/cli/discovery-scan-command-runner.mjs +9 -9
  21. package/cli/discovery-scan.mjs +17 -3
  22. package/cli/discovery-snapshot.mjs +15 -7
  23. package/cli/domain-control-monitor.mjs +203 -30
  24. package/cli/domain-control-observations.mjs +15 -21
  25. package/cli/errors.mjs +8 -3
  26. package/cli/evidence-signing.mjs +10 -20
  27. package/cli/external-observation-mapping.mjs +3 -3
  28. package/cli/formatters/json.mjs +20 -32
  29. package/cli/formatters/terminal.mjs +583 -22
  30. package/cli/input.mjs +7 -2
  31. package/cli/interactive-launcher.mjs +226 -0
  32. package/cli/interchange-report.mjs +19 -19
  33. package/cli/investigation-manifest.mjs +1 -1
  34. package/cli/investigation-plan.mjs +167 -16
  35. package/cli/investigation-run.mjs +9 -1
  36. package/cli/lookalike-calibration.mjs +3 -1
  37. package/cli/lookup-brief.mjs +13 -8
  38. package/cli/lookup-browser.mjs +844 -0
  39. package/cli/lookup-command-runner.mjs +94 -31
  40. package/cli/lookup-diff.mjs +2 -2
  41. package/cli/lookup-plan.mjs +1 -0
  42. package/cli/mail-review.mjs +206 -94
  43. package/cli/manual.mjs +5 -5
  44. package/cli/offline-evidence-review.mjs +18 -7
  45. package/cli/open-asset-model-bridge.mjs +1 -1
  46. package/cli/output-file.mjs +70 -14
  47. package/cli/page-compare.mjs +11 -6
  48. package/cli/progress-events.mjs +2 -1
  49. package/cli/progress.mjs +3 -3
  50. package/cli/registry-cohort.mjs +9 -51
  51. package/cli/registry-doctor.mjs +67 -28
  52. package/cli/retained-artifact-diff.mjs +7 -8
  53. package/cli/risk-calibration.mjs +85 -38
  54. package/cli/runner.mjs +189 -75
  55. package/cli/saved-lookup.mjs +424 -57
  56. package/cli/sharing-review.mjs +6 -1
  57. package/cli/source-reliability.mjs +75 -36
  58. package/cli/terminal-presentation.mjs +43 -18
  59. package/docs/cli-reference.md +125 -1708
  60. package/docs/cli.md +86 -254
  61. package/frontend/src/lib/analysis/domain-control-manifest-core.js +3 -145
  62. package/frontend/src/lib/analysis/domain-control-records.js +3 -88
  63. package/lib/availability.mjs +58 -40
  64. package/lib/bounded-contract-normalizers.mjs +2 -4
  65. package/lib/bounded-file.mjs +78 -4
  66. package/lib/bounded-json.mjs +51 -13
  67. package/lib/browser-library-profile.mjs +1 -1
  68. package/lib/classify.mjs +56 -5
  69. package/lib/client-behavior-profile.mjs +1 -1
  70. package/lib/credential-surface-profile.mjs +1 -1
  71. package/lib/cryptographic-assurance.mjs +35 -17
  72. package/lib/ct-query.mjs +3 -3
  73. package/lib/ct-search.mjs +37 -21
  74. package/lib/distributed-operation-budget.mjs +4 -2
  75. package/lib/dns-delegation-health.mjs +9 -6
  76. package/lib/dns-intelligence.mjs +1 -1
  77. package/lib/dnssec-chain-validation.mjs +2 -3
  78. package/lib/dnssec-evidence-validation.mjs +13 -7
  79. package/lib/domain-assurance.mjs +12 -12
  80. package/lib/domain-change-packet.mjs +1 -1
  81. package/lib/domain-control-flight-recorder.mjs +442 -78
  82. package/lib/domain-control-manifest.mjs +314 -59
  83. package/lib/domain-posture-analysis.mjs +5 -2
  84. package/lib/encrypted-dns-contract.mjs +2 -3
  85. package/lib/evidence-export.mjs +518 -36
  86. package/lib/external-intelligence-risk.mjs +26 -10
  87. package/lib/feature-policy.mjs +1 -1
  88. package/lib/homepage-metadata-contract.mjs +290 -0
  89. package/lib/hostname.mjs +1 -1
  90. package/lib/html-signals.mjs +89 -5
  91. package/lib/http-intelligence.mjs +331 -2
  92. package/lib/interchange-fidelity-registry.mjs +28 -26
  93. package/lib/lookup-cache.mjs +6 -4
  94. package/lib/lookup.mjs +25 -2
  95. package/lib/observed-network-context.mjs +1 -1
  96. package/lib/page-fingerprints.mjs +2 -2
  97. package/lib/page-role-profile.mjs +1 -1
  98. package/lib/portable-generator.mjs +8 -0
  99. package/lib/provider-policy-admission.mjs +3 -1
  100. package/lib/rdap-bootstrap.mjs +76 -16
  101. package/lib/rdap-client.mjs +20 -12
  102. package/lib/rdap-normalization.mjs +306 -75
  103. package/lib/rdap-transport.mjs +2 -2
  104. package/lib/rdap-validation.mjs +65 -14
  105. package/lib/rdap.mjs +2 -2
  106. package/lib/registrable-domain.mjs +36 -0
  107. package/lib/registry-insights.mjs +7 -0
  108. package/lib/response-policy.mjs +27 -4
  109. package/lib/risk-calibration-summary.mjs +35 -33
  110. package/lib/risk-scoring.mjs +4 -3
  111. package/lib/safe-fetch.mjs +62 -40
  112. package/lib/security-txt.mjs +1 -1
  113. package/lib/smtp-transport-review.mjs +8 -5
  114. package/lib/sslbl-intelligence.mjs +6 -8
  115. package/lib/static-html-analysis.mjs +301 -4
  116. package/lib/structured-data-identity.mjs +51 -19
  117. package/lib/threat-intelligence-fixtures.mjs +2 -4
  118. package/lib/threat-intelligence-manifests.mjs +2 -7
  119. package/lib/threat-intelligence-runtime.mjs +3 -5
  120. package/lib/threat-intelligence-types.mjs +3 -0
  121. package/lib/threatfox-intelligence.mjs +7 -3
  122. package/lib/tls-intelligence.mjs +4 -4
  123. package/lib/urlhaus-intelligence.mjs +4 -2
  124. package/lib/urlscan-intelligence.mjs +6 -5
  125. package/lib/website-security-posture.mjs +1 -1
  126. package/lib/website-technology.mjs +1 -1
  127. package/lib/whois-authority.mjs +43 -8
  128. package/lib/whois-chain.mjs +25 -3
  129. package/lib/whois-normalization.mjs +3 -1
  130. package/lib/whois-transport.mjs +12 -3
  131. package/lib/zone-intent-review.mjs +1 -1
  132. package/package.json +12 -4
  133. package/{frontend/src/lib/analysis/case-brand-profile-references.js → packages/cases/case-brand-profile-references.mjs} +2 -2
  134. package/{frontend/src/lib/analysis/case-evidence-model.js → packages/cases/case-evidence-model.mjs} +35 -14
  135. package/{frontend/src/lib/analysis/case-investigation-branch-model.js → packages/cases/case-investigation-branch-model.mjs} +10 -12
  136. package/packages/cases/case-migration-model.mjs +476 -0
  137. package/{frontend/src/lib/analysis/case-model.js → packages/cases/case-model.mjs} +3 -3
  138. package/{frontend/src/lib/analysis/case-record-contracts.js → packages/cases/case-record-contracts.mjs} +3 -21
  139. package/{frontend/src/lib/analysis/case-record-core.js → packages/cases/case-record-core.mjs} +58 -31
  140. package/packages/cases/case-record-model.mjs +9 -0
  141. package/{frontend/src/lib/analysis/case-record-operations.js → packages/cases/case-record-operations.mjs} +72 -29
  142. package/{frontend/src/lib/analysis/case-report.js → packages/cases/case-report.mjs} +108 -14
  143. package/packages/cases/case-response-model.mjs +1604 -0
  144. package/packages/cases/case-response-packet.mjs +1312 -0
  145. package/{frontend/src/lib/analysis/case-storage-model.js → packages/cases/case-storage-model.mjs} +2 -2
  146. package/{frontend/src/lib/analysis/http-summary.js → packages/cases/http-summary.mjs} +1 -1
  147. package/{frontend/src/lib/analysis/comparison-ledger-bulk.js → packages/comparison/comparison-ledger-bulk.mjs} +2 -2
  148. package/{frontend/src/lib/analysis/comparison-ledger-contract.js → packages/comparison/comparison-ledger-contract.mjs} +4 -4
  149. package/{frontend/src/lib/analysis/comparison-ledger-json.js → packages/comparison/comparison-ledger-json.mjs} +1 -0
  150. package/{frontend/src/lib/analysis/comparison-ledger-serialization.js → packages/comparison/comparison-ledger-serialization.mjs} +1 -0
  151. package/packages/comparison/favicon-similarity.mjs +78 -0
  152. package/{frontend/src/lib/analysis/page-similarity.js → packages/comparison/page-similarity.mjs} +4 -3
  153. package/{frontend/src/lib/analysis/relationship-evidence.js → packages/comparison/relationship-evidence.mjs} +6 -7
  154. package/packages/contracts/analyst-review-state-contract.mjs +61 -0
  155. package/packages/contracts/analyst-review-state.mjs +50 -0
  156. package/packages/contracts/case-portability.mjs +1310 -0
  157. package/packages/contracts/cli-command-catalogue.mjs +2 -0
  158. package/packages/contracts/cli-lookup.mjs +394 -0
  159. package/packages/contracts/domain-control-flight-recorder.mjs +461 -0
  160. package/packages/contracts/domain-control-manifest.mjs +1027 -0
  161. package/packages/contracts/domain-control-monitor.mjs +310 -0
  162. package/packages/contracts/domain-control-review.mjs +828 -0
  163. package/packages/contracts/domain-name.mjs +4 -0
  164. package/packages/contracts/external-observation-interchange.mjs +224 -0
  165. package/packages/contracts/extracted-domain-lifecycle.mjs +218 -0
  166. package/packages/contracts/investigation-portability.mjs +331 -0
  167. package/packages/contracts/offline-comparison.mjs +160 -0
  168. package/packages/contracts/risk-calibration.mjs +1288 -0
  169. package/packages/contracts/schema-compatibility.mjs +71 -0
  170. package/packages/contracts/schema-lifecycle.mjs +1702 -0
  171. package/packages/contracts/sslbl-snapshot.mjs +17 -0
  172. package/packages/contracts/workspace-portability.mjs +1347 -0
  173. package/{frontend/src/lib/analysis/artifact-integrity.js → packages/evidence/artifact-integrity.mjs} +13 -11
  174. package/packages/evidence/decision-fact.mjs +735 -0
  175. package/packages/evidence/domain-control-runtime.mjs +547 -0
  176. package/packages/evidence/domain-name.mjs +41 -0
  177. package/packages/evidence/observation.mjs +384 -0
  178. package/{frontend/src/lib/analysis/external-findings-import.js → packages/interchange/external-findings-import.mjs} +24 -22
  179. package/packages/interchange/retained-text.mjs +8 -0
  180. package/{frontend/src/lib/analysis/acquisition-decision-packet.js → packages/investigation/acquisition-decision-packet.mjs} +9 -6
  181. package/{frontend/src/lib/analysis/bulk-domain-comparison.js → packages/investigation/bulk-domain-comparison.mjs} +5 -6
  182. package/{frontend/src/lib/analysis/bulk-mail-exposure.js → packages/investigation/bulk-mail-exposure.mjs} +4 -6
  183. package/{frontend/src/lib/analysis/bulk-review-export.js → packages/investigation/bulk-review-export.mjs} +5 -5
  184. package/packages/investigation/bulk-review-policy.mjs +1 -0
  185. package/{frontend/src/lib/analysis/investigation-capsule.js → packages/investigation/investigation-capsule.mjs} +30 -32
  186. package/packages/investigation/lookup-artefact-inputs.mjs +1 -0
  187. package/{frontend/src/lib/analysis/lookup-asset-graph.js → packages/investigation/lookup-asset-graph.mjs} +4 -2
  188. package/{frontend/src/lib/analysis/lookup-claim-passport.js → packages/investigation/lookup-claim-passport.mjs} +6 -10
  189. package/{frontend/src/lib/analysis/lookup-claim-readiness.js → packages/investigation/lookup-claim-readiness.mjs} +9 -2
  190. package/packages/investigation/lookup-investigation-brief.mjs +172 -0
  191. package/packages/monitoring/analyst-review-state.mjs +680 -0
  192. package/{frontend/src/lib/analysis/brand-profile-model.js → packages/workspace/brand-profile-model.mjs} +70 -47
  193. package/{frontend/src/lib/analysis/bulk-review-model.js → packages/workspace/bulk-review-model.mjs} +13 -14
  194. package/{frontend/src/lib/analysis/bulk-session-model.js → packages/workspace/bulk-session-model.mjs} +33 -59
  195. package/{frontend/src/lib/analysis/bulk-sort.js → packages/workspace/bulk-sort.mjs} +8 -5
  196. package/{frontend/src/lib/analysis/campaign-model.js → packages/workspace/campaign-model.mjs} +20 -22
  197. package/{frontend/src/lib/analysis/detection-rule-model.js → packages/workspace/detection-rule-model.mjs} +15 -17
  198. package/packages/workspace/hostile-input.mjs +172 -0
  199. package/{frontend/src/lib/analysis/investigation-guide.js → packages/workspace/investigation-guide.mjs} +8 -45
  200. package/{frontend/src/lib/analysis/investigation-template-model.js → packages/workspace/investigation-template-model.mjs} +18 -21
  201. package/{frontend/src/lib/analysis/page-baseline.js → packages/workspace/page-baseline.mjs} +6 -13
  202. package/{frontend/src/lib/analysis/relationship-observation-model.js → packages/workspace/relationship-observation-model.mjs} +13 -17
  203. package/{frontend/src/lib/analysis/shortlist-model.js → packages/workspace/shortlist-model.mjs} +20 -18
  204. package/{frontend/src/lib/analysis/watchlist-history.js → packages/workspace/watchlist-history.mjs} +9 -16
  205. package/{frontend/src/lib/analysis/watchlist-store.js → packages/workspace/watchlist-store.mjs} +24 -17
  206. package/{frontend/src/lib/analysis/website-snapshot-model.js → packages/workspace/website-snapshot-model.mjs} +13 -13
  207. package/{frontend/src/lib/analysis/workspace-archive-crypto.js → packages/workspace/workspace-archive-crypto.mjs} +49 -19
  208. package/{frontend/src/lib/analysis/workspace-archive.js → packages/workspace/workspace-archive.mjs} +110 -89
  209. package/third-party-notices.txt +14 -14
  210. package/frontend/src/lib/analysis/bulk-retry-plan.js +0 -79
  211. package/frontend/src/lib/analysis/bulk-source-coverage.js +0 -76
  212. package/frontend/src/lib/analysis/case-migration-model.js +0 -305
  213. package/frontend/src/lib/analysis/case-record-model.js +0 -9
  214. package/frontend/src/lib/analysis/case-response-model.js +0 -728
  215. package/frontend/src/lib/analysis/case-response-packet.js +0 -583
  216. package/frontend/src/lib/analysis/lookup-decision-support.js +0 -542
  217. package/frontend/src/lib/analysis/lookup-investigation-brief.js +0 -153
  218. package/frontend/src/lib/analysis/lookup-presentation.js +0 -120
  219. package/frontend/src/lib/analysis/lookup-response.js +0 -2
  220. package/frontend/src/lib/analysis/lookup-source-refresh.js +0 -277
  221. package/frontend/src/lib/analysis/lookup-summary-model.js +0 -398
  222. package/frontend/src/lib/analysis/scoring.js +0 -55
  223. package/frontend/src/lib/analysis/utils.js +0 -255
  224. package/frontend/src/lib/analysis/visualization-bounds.js +0 -26
  225. package/frontend/src/lib/analysis/visualization-force-graph.js +0 -352
  226. package/frontend/src/lib/analysis/visualization-matrix.js +0 -131
  227. package/frontend/src/lib/analysis/visualization-models.js +0 -338
  228. package/frontend/src/lib/analysis/visualization-time-series.js +0 -185
  229. package/lib/bounded-json-response.mjs +0 -170
  230. package/lib/lookup-response-contract.mjs +0 -1034
  231. package/lib/observation.mjs +0 -115
  232. /package/{frontend/src/lib/analysis/opaque-reference-id.js → packages/cases/opaque-reference-id.mjs} +0 -0
  233. /package/{frontend/src/lib/analysis/acquisition-due-diligence.js → packages/investigation/acquisition-due-diligence.mjs} +0 -0
  234. /package/{frontend/src/lib/analysis/evidence-coverage-ledger.js → packages/investigation/evidence-coverage-ledger.mjs} +0 -0
  235. /package/{frontend/src/lib/analysis/dns-record-normalization.js → packages/workspace/dns-record-normalization.mjs} +0 -0
package/cli/runner.mjs CHANGED
@@ -5,9 +5,9 @@ import { scanBoundedJson } from "../lib/bounded-json.mjs";
5
5
  import { REGISTRY_CAPABILITIES_VERSION, registryCapabilityFor } from "../lib/registry-capabilities.mjs";
6
6
  import { explainRiskScore, explainRiskScoreV6, RISK_MODEL_VERSION, RISK_REVIEW_THRESHOLD } from "../lib/risk-scoring.mjs";
7
7
  import { buildRiskCalibrationSummaryReport } from "../lib/risk-calibration-summary.mjs";
8
- import { CLI_COMMANDS, parseCliArguments } from "./arguments.mjs";
9
- import { buildCliCommandCatalogue, formatCliCommandCatalogue } from "./command-catalogue.mjs";
10
- import { COMMAND_COLLECTION, COMMAND_DETAILS, COMMAND_USAGE, HELP, commandHelp, } from "./command-reference.mjs";
8
+ import { parseCliArguments } from "./arguments.mjs";
9
+ import { buildCliCommandCatalogue, formatCliCommandCatalogue, selectCliCommands } from "./command-catalogue.mjs";
10
+ import { COMMAND_COLLECTION, COMMAND_DETAILS, COMMAND_USAGE, HELP, CLI_COMMANDS, CLI_COMMAND_REGISTRY, commandDefinition, commandHelp, } from "./command-reference.mjs";
11
11
  import { buildShellCompletion } from "./completion.mjs";
12
12
  import { buildDoctorReport, formatDoctorReport } from "./doctor.mjs";
13
13
  import { MAX_COMPARE_INPUT_BYTES, compareLookupDocument, parseCliLookupDocument, } from "./compare.mjs";
@@ -27,7 +27,9 @@ import { buildCliPageComparison, formatCliPageComparison } from "./page-compare.
27
27
  import { MAX_MAIL_REVIEW_INPUT_BYTES, buildCliMailReview, formatCliMailReview, } from "./mail-review.mjs";
28
28
  import { MAX_OFFLINE_EVIDENCE_INPUT_BYTES, buildOfflineEvidenceReview, buildOfflineEvidenceReviewWithLocalResources, formatOfflineEvidenceReview, } from "./offline-evidence-review.mjs";
29
29
  import { DOMAIN_CONTROL_MANIFEST_INPUT_SCHEMA, DOMAIN_CONTROL_REVIEW_INPUT_SCHEMA, buildDomainControlManifest, formatDomainControlResult, reviewDomainControlManifest, } from "../lib/domain-control-manifest.mjs";
30
- import { DOMAIN_CONTROL_FLIGHT_RECORDER_INPUT_SCHEMA, buildDomainControlFlightRecorder, formatDomainControlFlightRecorder, } from "../lib/domain-control-flight-recorder.mjs";
30
+ import { serializeDomainControlManifest } from "../packages/evidence/domain-control-runtime.mjs";
31
+ import { MAX_DOMAIN_CONTROL_REVIEW_COMMAND_BYTES } from "../packages/contracts/domain-control-review.mjs";
32
+ import { DOMAIN_CONTROL_FLIGHT_RECORDER_INPUT_SCHEMA, buildDomainControlFlightRecorder, formatDomainControlFlightRecorder, serializeDomainControlFlightRecorder, } from "../lib/domain-control-flight-recorder.mjs";
31
33
  import { CLI_DOMAIN_CONTROL_REVIEW_INPUT_SCHEMA, MAX_DOMAIN_CONTROL_REVIEW_JSON_DEPTH, MAX_DOMAIN_CONTROL_REVIEW_JSON_KEYS, MAX_DOMAIN_CONTROL_REVIEW_JSON_VALUES, buildCliDomainControlReview, formatCliDomainControlReview, } from "./domain-control-observations.mjs";
32
34
  import { buildCliLookupBrief, formatCliLookupBrief } from "./lookup-brief.mjs";
33
35
  import { buildCliCasePack, formatCliCasePack } from "./case-pack.mjs";
@@ -39,12 +41,13 @@ import { MAX_INVESTIGATION_MANIFEST_ARTIFACT_BYTES, MAX_INVESTIGATION_MANIFEST_T
39
41
  import { MAX_EXTERNAL_OBSERVATION_MAPPING_BYTES, mapExternalObservations, formatExternalObservationMapping, } from "./external-observation-mapping.mjs";
40
42
  import { buildOpenAssetModelBridge, formatOpenAssetModelBridge, } from "./open-asset-model-bridge.mjs";
41
43
  import { MAX_CT_EVENT_INPUT_BYTES, buildCtEventFindings, formatCtEventFindings, } from "./ct-event-intake.mjs";
42
- import { buildInvestigationPlan, formatInvestigationPlan } from "./investigation-plan.mjs";
44
+ import { buildInvestigationPlan, buildWorkflowRecipeCatalogue, formatInvestigationPlan, formatWorkflowRecipeCatalogue, } from "./investigation-plan.mjs";
43
45
  import { MAX_INVESTIGATION_RUN_BYTES, formatInvestigationRun, runInvestigationRecipe } from "./investigation-run.mjs";
44
46
  import { readCliTextInput } from "./input.mjs";
45
47
  import { evaluateCliFailPolicies, formatFailPolicyNotice } from "./fail-policy.mjs";
46
48
  import { formatCliJunit } from "./ci-report.mjs";
47
- import { createBufferedOutput, writePrivateFile } from "./output-file.mjs";
49
+ import { cleanupPendingOutputFiles, createBufferedOutput, writePrivateFile } from "./output-file.mjs";
50
+ import { canLaunchInteractiveCli, launchInteractiveCli, } from "./interactive-launcher.mjs";
48
51
  import { createTerminalProgress } from "./progress.mjs";
49
52
  import { buildRegistrySupportDocument } from "./registry-support.mjs";
50
53
  import { buildRegistryDoctorReport, formatRegistryDoctorReport } from "./registry-doctor.mjs";
@@ -53,7 +56,7 @@ import { buildRegistryFixtureScaffold } from "./registry-fixture-scaffold.mjs";
53
56
  import { MAX_OFFLINE_ARTIFACT_BYTES, MAX_OFFLINE_PASSPHRASE_FILE_BYTES, formatOfflineArtifactVerification, isCompleteOfflineArtifactVerification, verifyOfflineArtifact, } from "./artifact-verify.mjs";
54
57
  import { buildInterchangeFidelityReport, formatInterchangeFidelityReport, } from "./interchange-report.mjs";
55
58
  import { MAX_SOURCE_RELIABILITY_INPUT_BYTES, buildSourceReliabilityReport, formatSourceReliabilityReport, } from "./source-reliability.mjs";
56
- import { MAX_RISK_CALIBRATION_INPUT_BYTES, buildRiskCalibrationReport, parseRiskCalibrationDataset, } from "./risk-calibration.mjs";
59
+ import { MAX_RISK_CALIBRATION_INPUT_BYTES, buildRiskCalibrationReport, parseRiskCalibrationDataset, serializeRiskCalibrationReport, } from "./risk-calibration.mjs";
57
60
  import { MAX_LOOKALIKE_CALIBRATION_BYTES, buildLookalikeCalibration, formatLookalikeCalibration, } from "./lookalike-calibration.mjs";
58
61
  import { MAX_SAVED_LOOKUP_INPUT_BYTES } from "./saved-lookup.mjs";
59
62
  import { presentTerminalOutput, terminalPresentation, } from "./terminal-presentation.mjs";
@@ -75,10 +78,13 @@ function write(stream, value) {
75
78
  if (stream && typeof stream.write === 'function')
76
79
  stream.write(value);
77
80
  }
78
- function formatForTerminal(value, stream, color, environment) {
79
- return presentTerminalOutput(value, terminalPresentation(stream, color, environment));
81
+ function formatForTerminal(value, stream, color, environment, palette) {
82
+ return presentTerminalOutput(value, terminalPresentation(stream, color, environment, palette));
80
83
  }
81
84
  function isCancellation(error, signal) {
85
+ if (error instanceof AggregateError) {
86
+ return error.errors.length > 0 && error.errors.every((item) => isCancellation(item));
87
+ }
82
88
  return signal?.aborted === true
83
89
  || Boolean(error && typeof error === 'object' && 'name' in error && error.name === 'AbortError');
84
90
  }
@@ -92,6 +98,16 @@ function usageEventReason(error) {
92
98
  return 'missing_input';
93
99
  return 'invalid_input';
94
100
  }
101
+ const INLINE_CLI_COMMANDS = Object.freeze([
102
+ 'completion', 'doctor', 'commands', 'manual', 'manifest', 'map-observations',
103
+ 'oam-export', 'ct-intake', 'registry-support', 'registry-doctor',
104
+ 'registry-cohort', 'registry-scaffold', 'risk-calibrate', 'lookalike-calibrate',
105
+ 'verify-artifact', 'interchange-report', 'source-report', 'compare',
106
+ 'page-compare', 'mail-review', 'review-evidence', 'brief', 'case-pack',
107
+ 'domain-control', 'monitor-once', 'assurance', 'change-packet',
108
+ 'sharing-review', 'workflow-plan', 'workflow-run', 'diff', 'reconcile',
109
+ 'timeline', 'export',
110
+ ]);
95
111
  async function runParsedCli(args, dependencies = {}) {
96
112
  const stdout = dependencies.stdout || process.stdout;
97
113
  const stderr = dependencies.stderr || process.stderr;
@@ -100,7 +116,8 @@ async function runParsedCli(args, dependencies = {}) {
100
116
  const eventProgress = { current: null };
101
117
  let failureLabel = 'Lookup';
102
118
  try {
103
- const terminal = (value, color = true) => formatForTerminal(value, stdout, color, environment);
119
+ const palette = args.palette || 'auto';
120
+ const terminal = (value, color = true) => formatForTerminal(value, stdout, color, environment, palette);
104
121
  const beginProgress = (message) => {
105
122
  const terminalOutput = 'output' in args
106
123
  && args.output === 'terminal'
@@ -112,6 +129,7 @@ async function runParsedCli(args, dependencies = {}) {
112
129
  enabled,
113
130
  color: terminalOutput ? args.color : false,
114
131
  environment,
132
+ palette,
115
133
  ...(dependencies.nowMs ? { now: dependencies.nowMs } : {}),
116
134
  });
117
135
  progress.start(message);
@@ -180,13 +198,90 @@ async function runParsedCli(args, dependencies = {}) {
180
198
  write(stdout, `${VERSION}\n`);
181
199
  return EXIT_CODES.SUCCESS;
182
200
  }
201
+ const handlerOwner = commandDefinition(args.action).execution.handlerOwner;
202
+ if (handlerOwner === 'bulk') {
203
+ if (args.action !== 'bulk')
204
+ throw new Error('Bulk command registry ownership is inconsistent.');
205
+ failureLabel = 'Bulk lookup';
206
+ const { runBulkCommand } = await import("./bulk-command-runner.mjs");
207
+ return await runBulkCommand(args, dependencies, commandContext);
208
+ }
209
+ if (handlerOwner === 'discovery') {
210
+ if (args.action !== 'discover')
211
+ throw new Error('Discovery command registry ownership is inconsistent.');
212
+ failureLabel = 'Candidate generation';
213
+ const { runDiscoveryCommand } = await import("./discovery-command-runner.mjs");
214
+ return await runDiscoveryCommand(args, dependencies, commandContext);
215
+ }
216
+ if (handlerOwner === 'discovery_scan') {
217
+ if (args.action !== 'discover-scan')
218
+ throw new Error('Discovery-scan command registry ownership is inconsistent.');
219
+ failureLabel = 'Candidate scan';
220
+ const { runDiscoveryScanCommand } = await import("./discovery-scan-command-runner.mjs");
221
+ return await runDiscoveryScanCommand(args, dependencies, commandContext);
222
+ }
223
+ if (handlerOwner === 'evidence') {
224
+ if (!isEvidenceCommand(args))
225
+ throw new Error('Evidence command registry ownership is inconsistent.');
226
+ failureLabel = evidenceCommandFailureLabel(args.action);
227
+ const evidenceStdout = args.action !== 'sign-artifact' && args.output === 'terminal'
228
+ ? { write: (value) => write(stdout, terminal(value, args.color)) }
229
+ : stdout;
230
+ return await runEvidenceCommand(args, {
231
+ stdout: evidenceStdout,
232
+ stdin: dependencies.stdin || process.stdin,
233
+ readArtifactInput: dependencies.readArtifactInput,
234
+ readPassphraseFile: dependencies.readPassphraseFile,
235
+ readPrivateKeyFile: dependencies.readPrivateKeyFile,
236
+ readPublicKeyFile: dependencies.readPublicKeyFile,
237
+ now: dependencies.now,
238
+ signal: dependencies.signal,
239
+ });
240
+ }
241
+ if (handlerOwner === 'network') {
242
+ if (args.action !== 'ct-search'
243
+ && args.action !== 'posture'
244
+ && args.action !== 'http'
245
+ && args.action !== 'tls'
246
+ && args.action !== 'dnssec-validate'
247
+ && args.action !== 'mail-transport') {
248
+ throw new Error('Network command registry ownership is inconsistent.');
249
+ }
250
+ failureLabel = args.action === 'ct-search'
251
+ ? 'Certificate Transparency search'
252
+ : args.action === 'posture'
253
+ ? 'Domain posture audit'
254
+ : args.action === 'http'
255
+ ? 'HTTP probe'
256
+ : args.action === 'tls'
257
+ ? 'TLS evidence collection'
258
+ : args.action === 'dnssec-validate'
259
+ ? 'DNSSEC chain validation'
260
+ : 'Mail transport review';
261
+ const { runNetworkCommand } = await import("./network-command-runner.mjs");
262
+ return await runNetworkCommand(args, dependencies, commandContext);
263
+ }
264
+ if (handlerOwner === 'lookup') {
265
+ if (args.action !== 'lookup')
266
+ throw new Error('Lookup command registry ownership is inconsistent.');
267
+ const { runLookupCommand } = await import("./lookup-command-runner.mjs");
268
+ return await runLookupCommand(args, dependencies, commandContext);
269
+ }
270
+ if (handlerOwner !== 'inline' || !INLINE_CLI_COMMANDS.includes(args.action)) {
271
+ throw new Error('No CLI execution route is registered for the parsed command.');
272
+ }
183
273
  if (args.action === 'completion') {
184
274
  write(stdout, buildShellCompletion(args.shell));
185
275
  return EXIT_CODES.SUCCESS;
186
276
  }
187
277
  if (args.action === 'commands') {
278
+ const selectedCommands = selectCliCommands(CLI_COMMAND_REGISTRY, {
279
+ common: args.common,
280
+ group: args.group,
281
+ mode: args.mode,
282
+ });
188
283
  const catalogue = buildCliCommandCatalogue({
189
- commands: CLI_COMMANDS,
284
+ commands: selectedCommands,
190
285
  collections: COMMAND_COLLECTION,
191
286
  details: COMMAND_DETAILS,
192
287
  usage: COMMAND_USAGE,
@@ -264,10 +359,11 @@ async function runParsedCli(args, dependencies = {}) {
264
359
  throw new CliUsageError(`${args.action} requires one versioned JSON file or a document on stdin.`);
265
360
  let parsed;
266
361
  try {
362
+ scanBoundedJson(input);
267
363
  parsed = JSON.parse(input);
268
364
  }
269
365
  catch {
270
- throw new CliUsageError(`${mapping ? 'External observation mapping' : 'Open Asset Model bridge'} input is not valid JSON.`);
366
+ throw new CliUsageError(`${mapping ? 'External observation mapping' : 'Open Asset Model bridge'} input is not valid bounded JSON without duplicate keys.`);
271
367
  }
272
368
  let document;
273
369
  try {
@@ -292,7 +388,7 @@ async function runParsedCli(args, dependencies = {}) {
292
388
  version: VERSION,
293
389
  generatedAt: dependencies.now ? dependencies.now() : new Date().toISOString(),
294
390
  network: args.network,
295
- presentation: terminalPresentation(stdout, args.color, environment),
391
+ presentation: terminalPresentation(stdout, args.color, environment, palette),
296
392
  ...(dependencies.resolvePublicAddresses ? { resolveAddresses: dependencies.resolvePublicAddresses } : {}),
297
393
  ...(dependencies.safeFetch ? { fetchHttps: dependencies.safeFetch } : {}),
298
394
  ...(dependencies.whoisQuery ? { queryWhois: dependencies.whoisQuery } : {}),
@@ -405,9 +501,9 @@ async function runParsedCli(args, dependencies = {}) {
405
501
  });
406
502
  if (!args.quiet)
407
503
  write(stdout, args.output === 'summary_json'
408
- ? formatJsonDocument(buildRiskCalibrationSummaryReport(report))
504
+ ? serializeRiskCalibrationReport(buildRiskCalibrationSummaryReport(report))
409
505
  : args.output === 'json'
410
- ? formatJsonDocument(report)
506
+ ? serializeRiskCalibrationReport(report)
411
507
  : terminal(formatTerminalRiskCalibration(report), args.color));
412
508
  return EXIT_CODES.SUCCESS;
413
509
  }
@@ -508,22 +604,6 @@ async function runParsedCli(args, dependencies = {}) {
508
604
  : terminal(formatInterchangeFidelityReport(report), args.color));
509
605
  return EXIT_CODES.SUCCESS;
510
606
  }
511
- if (isEvidenceCommand(args)) {
512
- failureLabel = evidenceCommandFailureLabel(args.action);
513
- const evidenceStdout = args.action !== 'sign-artifact' && args.output === 'terminal'
514
- ? { write: (value) => write(stdout, terminal(value, args.color)) }
515
- : stdout;
516
- return await runEvidenceCommand(args, {
517
- stdout: evidenceStdout,
518
- stdin: dependencies.stdin || process.stdin,
519
- readArtifactInput: dependencies.readArtifactInput,
520
- readPassphraseFile: dependencies.readPassphraseFile,
521
- readPrivateKeyFile: dependencies.readPrivateKeyFile,
522
- readPublicKeyFile: dependencies.readPublicKeyFile,
523
- now: dependencies.now,
524
- signal: dependencies.signal,
525
- });
526
- }
527
607
  if (args.action === 'source-report') {
528
608
  failureLabel = 'Source reliability report';
529
609
  let input;
@@ -691,7 +771,7 @@ async function runParsedCli(args, dependencies = {}) {
691
771
  try {
692
772
  input = dependencies.readArtifactInput
693
773
  ? await dependencies.readArtifactInput(args.source)
694
- : await readInput(args.source, MAX_OFFLINE_EVIDENCE_INPUT_BYTES, 'Domain control input');
774
+ : await readInput(args.source, MAX_DOMAIN_CONTROL_REVIEW_COMMAND_BYTES, 'Domain control input');
695
775
  }
696
776
  catch (error) {
697
777
  if (error instanceof CliUsageError)
@@ -739,7 +819,11 @@ async function runParsedCli(args, dependencies = {}) {
739
819
  : formatDomainControlResult(document);
740
820
  if (!args.quiet)
741
821
  write(stdout, args.output === 'json'
742
- ? formatJsonDocument(document)
822
+ ? schema === DOMAIN_CONTROL_MANIFEST_INPUT_SCHEMA
823
+ ? serializeDomainControlManifest(document)
824
+ : schema === DOMAIN_CONTROL_FLIGHT_RECORDER_INPUT_SCHEMA
825
+ ? serializeDomainControlFlightRecorder(document)
826
+ : formatJsonDocument(document)
743
827
  : terminal(terminalDocument, args.color));
744
828
  return EXIT_CODES.SUCCESS;
745
829
  }
@@ -808,10 +892,11 @@ async function runParsedCli(args, dependencies = {}) {
808
892
  throw new CliUsageError('assurance requires one versioned JSON file or a document on stdin.');
809
893
  let parsed;
810
894
  try {
895
+ scanBoundedJson(input);
811
896
  parsed = JSON.parse(input);
812
897
  }
813
898
  catch {
814
- throw new CliUsageError('Domain assurance input is not valid JSON.');
899
+ throw new CliUsageError('Domain assurance input is not valid bounded JSON without duplicate keys.');
815
900
  }
816
901
  let document;
817
902
  try {
@@ -843,10 +928,11 @@ async function runParsedCli(args, dependencies = {}) {
843
928
  throw new CliUsageError('change-packet requires one versioned JSON file or a document on stdin.');
844
929
  let parsed;
845
930
  try {
931
+ scanBoundedJson(input);
846
932
  parsed = JSON.parse(input);
847
933
  }
848
934
  catch {
849
- throw new CliUsageError('Domain change packet input is not valid JSON.');
935
+ throw new CliUsageError('Domain change packet input is not valid bounded JSON without duplicate keys.');
850
936
  }
851
937
  let document;
852
938
  try {
@@ -898,6 +984,14 @@ async function runParsedCli(args, dependencies = {}) {
898
984
  }
899
985
  if (args.action === 'workflow-plan') {
900
986
  failureLabel = 'Investigation plan';
987
+ if ('discovery' in args) {
988
+ const catalogue = buildWorkflowRecipeCatalogue(args.discovery === 'explain' ? args.recipe : null);
989
+ if (!args.quiet)
990
+ write(stdout, args.output === 'json'
991
+ ? formatJsonDocument(catalogue)
992
+ : terminal(formatWorkflowRecipeCatalogue(catalogue), args.color));
993
+ return EXIT_CODES.SUCCESS;
994
+ }
901
995
  const document = buildInvestigationPlan(args.recipe, args.subject, commandContext.now());
902
996
  if (!args.quiet)
903
997
  write(stdout, args.output === 'json'
@@ -924,6 +1018,7 @@ async function runParsedCli(args, dependencies = {}) {
924
1018
  approveNetwork: args.approveNetwork,
925
1019
  resumeInput,
926
1020
  generatedAt: commandContext.now(),
1021
+ ...(dependencies.signal ? { signal: dependencies.signal } : {}),
927
1022
  execute: async (command, stepArguments) => {
928
1023
  const stepStdout = createBufferedOutput();
929
1024
  const stepStderr = createBufferedOutput();
@@ -1048,21 +1143,6 @@ async function runParsedCli(args, dependencies = {}) {
1048
1143
  write(stdout, output);
1049
1144
  return EXIT_CODES.SUCCESS;
1050
1145
  }
1051
- if (args.action === 'bulk') {
1052
- failureLabel = 'Bulk lookup';
1053
- const { runBulkCommand } = await import("./bulk-command-runner.mjs");
1054
- return await runBulkCommand(args, dependencies, commandContext);
1055
- }
1056
- if (args.action === 'discover') {
1057
- failureLabel = 'Candidate generation';
1058
- const { runDiscoveryCommand } = await import("./discovery-command-runner.mjs");
1059
- return await runDiscoveryCommand(args, dependencies, commandContext);
1060
- }
1061
- if (args.action === 'discover-scan') {
1062
- failureLabel = 'Candidate scan';
1063
- const { runDiscoveryScanCommand } = await import("./discovery-scan-command-runner.mjs");
1064
- return await runDiscoveryScanCommand(args, dependencies, commandContext);
1065
- }
1066
1146
  if (args.action === 'ct-intake') {
1067
1147
  failureLabel = 'Certificate event intake';
1068
1148
  let input;
@@ -1080,10 +1160,11 @@ async function runParsedCli(args, dependencies = {}) {
1080
1160
  throw new CliUsageError('ct-intake requires one versioned JSON file or a document on stdin.');
1081
1161
  let parsed;
1082
1162
  try {
1163
+ scanBoundedJson(input);
1083
1164
  parsed = JSON.parse(input);
1084
1165
  }
1085
1166
  catch {
1086
- throw new CliUsageError('Certificate event input is not valid JSON.');
1167
+ throw new CliUsageError('Certificate event input is not valid bounded JSON without duplicate keys.');
1087
1168
  }
1088
1169
  let document;
1089
1170
  try {
@@ -1098,28 +1179,7 @@ async function runParsedCli(args, dependencies = {}) {
1098
1179
  : terminal(formatCtEventFindings(document), args.color));
1099
1180
  return EXIT_CODES.SUCCESS;
1100
1181
  }
1101
- if (args.action === 'ct-search'
1102
- || args.action === 'posture'
1103
- || args.action === 'http'
1104
- || args.action === 'tls'
1105
- || args.action === 'dnssec-validate'
1106
- || args.action === 'mail-transport') {
1107
- failureLabel = args.action === 'ct-search'
1108
- ? 'Certificate Transparency search'
1109
- : args.action === 'posture'
1110
- ? 'Domain posture audit'
1111
- : args.action === 'http'
1112
- ? 'HTTP probe'
1113
- : args.action === 'tls'
1114
- ? 'TLS intelligence'
1115
- : args.action === 'dnssec-validate'
1116
- ? 'DNSSEC chain validation'
1117
- : 'Mail transport review';
1118
- const { runNetworkCommand } = await import("./network-command-runner.mjs");
1119
- return await runNetworkCommand(args, dependencies, commandContext);
1120
- }
1121
- const { runLookupCommand } = await import("./lookup-command-runner.mjs");
1122
- return await runLookupCommand(args, dependencies, commandContext);
1182
+ throw new Error('No CLI execution route is registered for the parsed command.');
1123
1183
  }
1124
1184
  catch (error) {
1125
1185
  progress?.stop();
@@ -1147,8 +1207,41 @@ async function runParsedCli(args, dependencies = {}) {
1147
1207
  return EXIT_CODES.LOOKUP_FAILED;
1148
1208
  }
1149
1209
  }
1150
- async function runCli(argv, dependencies = {}) {
1210
+ async function runCliCommand(argv, dependencies = {}) {
1211
+ const stdout = dependencies.stdout || process.stdout;
1151
1212
  const stderr = dependencies.stderr || process.stderr;
1213
+ const environment = dependencies.environment || process.env;
1214
+ if (Array.isArray(argv) && argv.length === 0) {
1215
+ const input = (dependencies.stdin || process.stdin);
1216
+ const output = stdout;
1217
+ const supportsInteractiveLaunch = dependencies.canLaunchInteractiveCli || canLaunchInteractiveCli;
1218
+ if (supportsInteractiveLaunch(input, output, environment)) {
1219
+ const launch = dependencies.launchInteractiveCli || launchInteractiveCli;
1220
+ try {
1221
+ const launchedArgv = await launch({
1222
+ input,
1223
+ output,
1224
+ environment,
1225
+ ...(dependencies.signal ? { signal: dependencies.signal } : {}),
1226
+ });
1227
+ if (launchedArgv === null)
1228
+ return EXIT_CODES.SUCCESS;
1229
+ argv = launchedArgv;
1230
+ }
1231
+ catch (error) {
1232
+ if (isCancellation(error, dependencies.signal)) {
1233
+ write(stderr, 'Cancelled by analyst.\n');
1234
+ return EXIT_CODES.CANCELLED;
1235
+ }
1236
+ if (error instanceof CliUsageError) {
1237
+ write(stderr, `Usage error: ${boundedCliErrorMessage(error, 'Invalid interactive selection')}\n`);
1238
+ return EXIT_CODES.USAGE;
1239
+ }
1240
+ write(stderr, `CLI startup failed: ${boundedCliErrorMessage(error, 'Interactive launch failed')}\n`);
1241
+ return EXIT_CODES.INTERNAL_ERROR;
1242
+ }
1243
+ }
1244
+ }
1152
1245
  let args;
1153
1246
  try {
1154
1247
  args = parseCliArguments(argv);
@@ -1180,4 +1273,25 @@ async function runCli(argv, dependencies = {}) {
1180
1273
  return EXIT_CODES.LOOKUP_FAILED;
1181
1274
  }
1182
1275
  }
1183
- export { HELP, MAX_STDIN_BYTES, VERSION, readStdinBounded, runCli };
1276
+ async function runCli(argv, dependencies = {}) {
1277
+ const stderr = dependencies.stderr || process.stderr;
1278
+ try {
1279
+ return await runCliCommand(argv, dependencies);
1280
+ }
1281
+ finally {
1282
+ const cleanup = dependencies.cleanupPendingOutputFiles || cleanupPendingOutputFiles;
1283
+ try {
1284
+ const report = await cleanup();
1285
+ if (report.retainedPublished > 0) {
1286
+ write(stderr, `Output cleanup warning: Published output is intact, but ${report.retainedPublished} linked temporary output ${report.retainedPublished === 1 ? 'file remains' : 'files remain'} in the selected output directory.\n`);
1287
+ }
1288
+ if (report.retainedUnpublished > 0) {
1289
+ write(stderr, `Output cleanup warning: ${report.retainedUnpublished} unpublished temporary output ${report.retainedUnpublished === 1 ? 'file remains' : 'files remain'} in the selected output directory.\n`);
1290
+ }
1291
+ }
1292
+ catch {
1293
+ write(stderr, 'Output cleanup warning: Temporary output cleanup could not be verified.\n');
1294
+ }
1295
+ }
1296
+ }
1297
+ export { HELP, INLINE_CLI_COMMANDS, MAX_STDIN_BYTES, VERSION, readStdinBounded, runCli };