@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.
- package/README.md +74 -177
- package/SECURITY.md +6 -3
- package/bin/whoisleuth.mjs +2 -1
- package/cli/archive-inspect.mjs +28 -10
- package/cli/arguments.mjs +204 -174
- package/cli/artifact-structure.mjs +837 -163
- package/cli/artifact-verify.mjs +90 -61
- package/cli/bounded-json.mjs +1 -1
- package/cli/bulk-checkpoint.mjs +33 -20
- package/cli/bulk.mjs +9 -2
- package/cli/case-pack.mjs +180 -576
- package/cli/ci-report.mjs +6 -1
- package/cli/collection-preflight.mjs +2 -2
- package/cli/command-catalogue.mjs +12 -3
- package/cli/command-reference.mjs +910 -92
- package/cli/completion.mjs +504 -226
- package/cli/config-profile.mjs +13 -3
- package/cli/discover.mjs +7 -1
- package/cli/discovery-observation-snapshot.mjs +24 -28
- package/cli/discovery-scan-command-runner.mjs +9 -9
- package/cli/discovery-scan.mjs +17 -3
- package/cli/discovery-snapshot.mjs +15 -7
- package/cli/domain-control-monitor.mjs +203 -30
- package/cli/domain-control-observations.mjs +15 -21
- package/cli/errors.mjs +8 -3
- package/cli/evidence-signing.mjs +10 -20
- package/cli/external-observation-mapping.mjs +3 -3
- package/cli/formatters/json.mjs +20 -32
- package/cli/formatters/terminal.mjs +583 -22
- package/cli/input.mjs +7 -2
- package/cli/interactive-launcher.mjs +226 -0
- package/cli/interchange-report.mjs +19 -19
- package/cli/investigation-manifest.mjs +1 -1
- package/cli/investigation-plan.mjs +167 -16
- package/cli/investigation-run.mjs +9 -1
- package/cli/lookalike-calibration.mjs +3 -1
- package/cli/lookup-brief.mjs +13 -8
- package/cli/lookup-browser.mjs +844 -0
- package/cli/lookup-command-runner.mjs +94 -31
- package/cli/lookup-diff.mjs +2 -2
- package/cli/lookup-plan.mjs +1 -0
- package/cli/mail-review.mjs +206 -94
- package/cli/manual.mjs +5 -5
- package/cli/offline-evidence-review.mjs +18 -7
- package/cli/open-asset-model-bridge.mjs +1 -1
- package/cli/output-file.mjs +70 -14
- package/cli/page-compare.mjs +11 -6
- package/cli/progress-events.mjs +2 -1
- package/cli/progress.mjs +3 -3
- package/cli/registry-cohort.mjs +9 -51
- package/cli/registry-doctor.mjs +67 -28
- package/cli/retained-artifact-diff.mjs +7 -8
- package/cli/risk-calibration.mjs +85 -38
- package/cli/runner.mjs +189 -75
- package/cli/saved-lookup.mjs +424 -57
- package/cli/sharing-review.mjs +6 -1
- package/cli/source-reliability.mjs +75 -36
- package/cli/terminal-presentation.mjs +43 -18
- package/docs/cli-reference.md +125 -1708
- package/docs/cli.md +86 -254
- package/frontend/src/lib/analysis/domain-control-manifest-core.js +3 -145
- package/frontend/src/lib/analysis/domain-control-records.js +3 -88
- package/lib/availability.mjs +58 -40
- package/lib/bounded-contract-normalizers.mjs +2 -4
- package/lib/bounded-file.mjs +78 -4
- package/lib/bounded-json.mjs +51 -13
- package/lib/browser-library-profile.mjs +1 -1
- package/lib/classify.mjs +56 -5
- package/lib/client-behavior-profile.mjs +1 -1
- package/lib/credential-surface-profile.mjs +1 -1
- package/lib/cryptographic-assurance.mjs +35 -17
- package/lib/ct-query.mjs +3 -3
- package/lib/ct-search.mjs +37 -21
- package/lib/distributed-operation-budget.mjs +4 -2
- package/lib/dns-delegation-health.mjs +9 -6
- package/lib/dns-intelligence.mjs +1 -1
- package/lib/dnssec-chain-validation.mjs +2 -3
- package/lib/dnssec-evidence-validation.mjs +13 -7
- package/lib/domain-assurance.mjs +12 -12
- package/lib/domain-change-packet.mjs +1 -1
- package/lib/domain-control-flight-recorder.mjs +442 -78
- package/lib/domain-control-manifest.mjs +314 -59
- package/lib/domain-posture-analysis.mjs +5 -2
- package/lib/encrypted-dns-contract.mjs +2 -3
- package/lib/evidence-export.mjs +518 -36
- package/lib/external-intelligence-risk.mjs +26 -10
- package/lib/feature-policy.mjs +1 -1
- package/lib/homepage-metadata-contract.mjs +290 -0
- package/lib/hostname.mjs +1 -1
- package/lib/html-signals.mjs +89 -5
- package/lib/http-intelligence.mjs +331 -2
- package/lib/interchange-fidelity-registry.mjs +28 -26
- package/lib/lookup-cache.mjs +6 -4
- package/lib/lookup.mjs +25 -2
- package/lib/observed-network-context.mjs +1 -1
- package/lib/page-fingerprints.mjs +2 -2
- package/lib/page-role-profile.mjs +1 -1
- package/lib/portable-generator.mjs +8 -0
- package/lib/provider-policy-admission.mjs +3 -1
- package/lib/rdap-bootstrap.mjs +76 -16
- package/lib/rdap-client.mjs +20 -12
- package/lib/rdap-normalization.mjs +306 -75
- package/lib/rdap-transport.mjs +2 -2
- package/lib/rdap-validation.mjs +65 -14
- package/lib/rdap.mjs +2 -2
- package/lib/registrable-domain.mjs +36 -0
- package/lib/registry-insights.mjs +7 -0
- package/lib/response-policy.mjs +27 -4
- package/lib/risk-calibration-summary.mjs +35 -33
- package/lib/risk-scoring.mjs +4 -3
- package/lib/safe-fetch.mjs +62 -40
- package/lib/security-txt.mjs +1 -1
- package/lib/smtp-transport-review.mjs +8 -5
- package/lib/sslbl-intelligence.mjs +6 -8
- package/lib/static-html-analysis.mjs +301 -4
- package/lib/structured-data-identity.mjs +51 -19
- package/lib/threat-intelligence-fixtures.mjs +2 -4
- package/lib/threat-intelligence-manifests.mjs +2 -7
- package/lib/threat-intelligence-runtime.mjs +3 -5
- package/lib/threat-intelligence-types.mjs +3 -0
- package/lib/threatfox-intelligence.mjs +7 -3
- package/lib/tls-intelligence.mjs +4 -4
- package/lib/urlhaus-intelligence.mjs +4 -2
- package/lib/urlscan-intelligence.mjs +6 -5
- package/lib/website-security-posture.mjs +1 -1
- package/lib/website-technology.mjs +1 -1
- package/lib/whois-authority.mjs +43 -8
- package/lib/whois-chain.mjs +25 -3
- package/lib/whois-normalization.mjs +3 -1
- package/lib/whois-transport.mjs +12 -3
- package/lib/zone-intent-review.mjs +1 -1
- package/package.json +12 -4
- package/{frontend/src/lib/analysis/case-brand-profile-references.js → packages/cases/case-brand-profile-references.mjs} +2 -2
- package/{frontend/src/lib/analysis/case-evidence-model.js → packages/cases/case-evidence-model.mjs} +35 -14
- package/{frontend/src/lib/analysis/case-investigation-branch-model.js → packages/cases/case-investigation-branch-model.mjs} +10 -12
- package/packages/cases/case-migration-model.mjs +476 -0
- package/{frontend/src/lib/analysis/case-model.js → packages/cases/case-model.mjs} +3 -3
- package/{frontend/src/lib/analysis/case-record-contracts.js → packages/cases/case-record-contracts.mjs} +3 -21
- package/{frontend/src/lib/analysis/case-record-core.js → packages/cases/case-record-core.mjs} +58 -31
- package/packages/cases/case-record-model.mjs +9 -0
- package/{frontend/src/lib/analysis/case-record-operations.js → packages/cases/case-record-operations.mjs} +72 -29
- package/{frontend/src/lib/analysis/case-report.js → packages/cases/case-report.mjs} +108 -14
- package/packages/cases/case-response-model.mjs +1604 -0
- package/packages/cases/case-response-packet.mjs +1312 -0
- package/{frontend/src/lib/analysis/case-storage-model.js → packages/cases/case-storage-model.mjs} +2 -2
- package/{frontend/src/lib/analysis/http-summary.js → packages/cases/http-summary.mjs} +1 -1
- package/{frontend/src/lib/analysis/comparison-ledger-bulk.js → packages/comparison/comparison-ledger-bulk.mjs} +2 -2
- package/{frontend/src/lib/analysis/comparison-ledger-contract.js → packages/comparison/comparison-ledger-contract.mjs} +4 -4
- package/{frontend/src/lib/analysis/comparison-ledger-json.js → packages/comparison/comparison-ledger-json.mjs} +1 -0
- package/{frontend/src/lib/analysis/comparison-ledger-serialization.js → packages/comparison/comparison-ledger-serialization.mjs} +1 -0
- package/packages/comparison/favicon-similarity.mjs +78 -0
- package/{frontend/src/lib/analysis/page-similarity.js → packages/comparison/page-similarity.mjs} +4 -3
- package/{frontend/src/lib/analysis/relationship-evidence.js → packages/comparison/relationship-evidence.mjs} +6 -7
- package/packages/contracts/analyst-review-state-contract.mjs +61 -0
- package/packages/contracts/analyst-review-state.mjs +50 -0
- package/packages/contracts/case-portability.mjs +1310 -0
- package/packages/contracts/cli-command-catalogue.mjs +2 -0
- package/packages/contracts/cli-lookup.mjs +394 -0
- package/packages/contracts/domain-control-flight-recorder.mjs +461 -0
- package/packages/contracts/domain-control-manifest.mjs +1027 -0
- package/packages/contracts/domain-control-monitor.mjs +310 -0
- package/packages/contracts/domain-control-review.mjs +828 -0
- package/packages/contracts/domain-name.mjs +4 -0
- package/packages/contracts/external-observation-interchange.mjs +224 -0
- package/packages/contracts/extracted-domain-lifecycle.mjs +218 -0
- package/packages/contracts/investigation-portability.mjs +331 -0
- package/packages/contracts/offline-comparison.mjs +160 -0
- package/packages/contracts/risk-calibration.mjs +1288 -0
- package/packages/contracts/schema-compatibility.mjs +71 -0
- package/packages/contracts/schema-lifecycle.mjs +1702 -0
- package/packages/contracts/sslbl-snapshot.mjs +17 -0
- package/packages/contracts/workspace-portability.mjs +1347 -0
- package/{frontend/src/lib/analysis/artifact-integrity.js → packages/evidence/artifact-integrity.mjs} +13 -11
- package/packages/evidence/decision-fact.mjs +735 -0
- package/packages/evidence/domain-control-runtime.mjs +547 -0
- package/packages/evidence/domain-name.mjs +41 -0
- package/packages/evidence/observation.mjs +384 -0
- package/{frontend/src/lib/analysis/external-findings-import.js → packages/interchange/external-findings-import.mjs} +24 -22
- package/packages/interchange/retained-text.mjs +8 -0
- package/{frontend/src/lib/analysis/acquisition-decision-packet.js → packages/investigation/acquisition-decision-packet.mjs} +9 -6
- package/{frontend/src/lib/analysis/bulk-domain-comparison.js → packages/investigation/bulk-domain-comparison.mjs} +5 -6
- package/{frontend/src/lib/analysis/bulk-mail-exposure.js → packages/investigation/bulk-mail-exposure.mjs} +4 -6
- package/{frontend/src/lib/analysis/bulk-review-export.js → packages/investigation/bulk-review-export.mjs} +5 -5
- package/packages/investigation/bulk-review-policy.mjs +1 -0
- package/{frontend/src/lib/analysis/investigation-capsule.js → packages/investigation/investigation-capsule.mjs} +30 -32
- package/packages/investigation/lookup-artefact-inputs.mjs +1 -0
- package/{frontend/src/lib/analysis/lookup-asset-graph.js → packages/investigation/lookup-asset-graph.mjs} +4 -2
- package/{frontend/src/lib/analysis/lookup-claim-passport.js → packages/investigation/lookup-claim-passport.mjs} +6 -10
- package/{frontend/src/lib/analysis/lookup-claim-readiness.js → packages/investigation/lookup-claim-readiness.mjs} +9 -2
- package/packages/investigation/lookup-investigation-brief.mjs +172 -0
- package/packages/monitoring/analyst-review-state.mjs +680 -0
- package/{frontend/src/lib/analysis/brand-profile-model.js → packages/workspace/brand-profile-model.mjs} +70 -47
- package/{frontend/src/lib/analysis/bulk-review-model.js → packages/workspace/bulk-review-model.mjs} +13 -14
- package/{frontend/src/lib/analysis/bulk-session-model.js → packages/workspace/bulk-session-model.mjs} +33 -59
- package/{frontend/src/lib/analysis/bulk-sort.js → packages/workspace/bulk-sort.mjs} +8 -5
- package/{frontend/src/lib/analysis/campaign-model.js → packages/workspace/campaign-model.mjs} +20 -22
- package/{frontend/src/lib/analysis/detection-rule-model.js → packages/workspace/detection-rule-model.mjs} +15 -17
- package/packages/workspace/hostile-input.mjs +172 -0
- package/{frontend/src/lib/analysis/investigation-guide.js → packages/workspace/investigation-guide.mjs} +8 -45
- package/{frontend/src/lib/analysis/investigation-template-model.js → packages/workspace/investigation-template-model.mjs} +18 -21
- package/{frontend/src/lib/analysis/page-baseline.js → packages/workspace/page-baseline.mjs} +6 -13
- package/{frontend/src/lib/analysis/relationship-observation-model.js → packages/workspace/relationship-observation-model.mjs} +13 -17
- package/{frontend/src/lib/analysis/shortlist-model.js → packages/workspace/shortlist-model.mjs} +20 -18
- package/{frontend/src/lib/analysis/watchlist-history.js → packages/workspace/watchlist-history.mjs} +9 -16
- package/{frontend/src/lib/analysis/watchlist-store.js → packages/workspace/watchlist-store.mjs} +24 -17
- package/{frontend/src/lib/analysis/website-snapshot-model.js → packages/workspace/website-snapshot-model.mjs} +13 -13
- package/{frontend/src/lib/analysis/workspace-archive-crypto.js → packages/workspace/workspace-archive-crypto.mjs} +49 -19
- package/{frontend/src/lib/analysis/workspace-archive.js → packages/workspace/workspace-archive.mjs} +110 -89
- package/third-party-notices.txt +14 -14
- package/frontend/src/lib/analysis/bulk-retry-plan.js +0 -79
- package/frontend/src/lib/analysis/bulk-source-coverage.js +0 -76
- package/frontend/src/lib/analysis/case-migration-model.js +0 -305
- package/frontend/src/lib/analysis/case-record-model.js +0 -9
- package/frontend/src/lib/analysis/case-response-model.js +0 -728
- package/frontend/src/lib/analysis/case-response-packet.js +0 -583
- package/frontend/src/lib/analysis/lookup-decision-support.js +0 -542
- package/frontend/src/lib/analysis/lookup-investigation-brief.js +0 -153
- package/frontend/src/lib/analysis/lookup-presentation.js +0 -120
- package/frontend/src/lib/analysis/lookup-response.js +0 -2
- package/frontend/src/lib/analysis/lookup-source-refresh.js +0 -277
- package/frontend/src/lib/analysis/lookup-summary-model.js +0 -398
- package/frontend/src/lib/analysis/scoring.js +0 -55
- package/frontend/src/lib/analysis/utils.js +0 -255
- package/frontend/src/lib/analysis/visualization-bounds.js +0 -26
- package/frontend/src/lib/analysis/visualization-force-graph.js +0 -352
- package/frontend/src/lib/analysis/visualization-matrix.js +0 -131
- package/frontend/src/lib/analysis/visualization-models.js +0 -338
- package/frontend/src/lib/analysis/visualization-time-series.js +0 -185
- package/lib/bounded-json-response.mjs +0 -170
- package/lib/lookup-response-contract.mjs +0 -1034
- package/lib/observation.mjs +0 -115
- /package/{frontend/src/lib/analysis/opaque-reference-id.js → packages/cases/opaque-reference-id.mjs} +0 -0
- /package/{frontend/src/lib/analysis/acquisition-due-diligence.js → packages/investigation/acquisition-due-diligence.mjs} +0 -0
- /package/{frontend/src/lib/analysis/evidence-coverage-ledger.js → packages/investigation/evidence-coverage-ledger.mjs} +0 -0
- /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 {
|
|
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 {
|
|
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
|
|
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:
|
|
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
|
-
?
|
|
504
|
+
? serializeRiskCalibrationReport(buildRiskCalibrationSummaryReport(report))
|
|
409
505
|
: args.output === 'json'
|
|
410
|
-
?
|
|
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,
|
|
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
|
-
?
|
|
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
|
-
|
|
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
|
|
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
|
-
|
|
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 };
|