@slicedearth/whoisleuth-cli 2.3.0 → 2.5.1
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/NOTICE +8 -2
- package/README.md +19 -15
- package/bin/whoisleuth.mjs +8 -1
- package/cli/archive-content-digest.mjs +14 -0
- package/cli/archive-inspect.mjs +20 -53
- package/cli/argument-values.mjs +55 -0
- package/cli/arguments.mjs +24 -408
- package/cli/artifact-validation/case-response.mjs +65 -44
- package/cli/artifact-validation/investigation-capsule.mjs +52 -31
- package/cli/artifact-validation/lookup-evidence.mjs +15 -9
- package/cli/artifact-validation/signed-review.mjs +136 -68
- package/cli/artifact-validation/structure-primitives.mjs +1 -105
- package/cli/artifact-verify.mjs +92 -24
- package/cli/assurance-arguments.mjs +56 -0
- package/cli/assurance-command-definitions.mjs +179 -0
- package/cli/assurance-command-runner.mjs +40 -5
- package/cli/bagit-folder.mjs +57 -0
- package/cli/bagit-review.mjs +23 -0
- package/cli/bounded-json.mjs +1 -1
- package/cli/bulk-checkpoint.mjs +1 -1
- package/cli/bulk-command-runner.mjs +4 -2
- package/cli/bulk-output.mjs +29 -3
- package/cli/bulk.mjs +2 -3
- package/cli/case-command.mjs +212 -0
- package/cli/case-pack.mjs +50 -32
- package/cli/ci-report.mjs +6 -3
- package/cli/collection-arguments.mjs +123 -0
- package/cli/collection-command-definitions.mjs +330 -0
- package/cli/collection-preflight.mjs +8 -1
- package/cli/command-argument-grammar.mjs +8 -206
- package/cli/command-definition.mjs +329 -0
- package/cli/command-reference.mjs +94 -1357
- package/cli/command-seeds.mjs +18 -0
- package/cli/compare.mjs +1 -1
- package/cli/completion.mjs +298 -79
- package/cli/config-profile.mjs +8 -6
- package/cli/ct-event-intake.mjs +16 -16
- package/cli/discover.mjs +5 -4
- package/cli/discovery-observation-snapshot.mjs +46 -18
- package/cli/discovery-scan-command-runner.mjs +5 -3
- package/cli/discovery-scan.mjs +7 -4
- package/cli/discovery-snapshot.mjs +2 -2
- package/cli/discovery-workflow.mjs +5 -5
- package/cli/domain-control-monitor.mjs +50 -20
- package/cli/domain-control-observations.mjs +151 -103
- package/cli/errors.mjs +37 -6
- package/cli/evidence-arguments.mjs +46 -0
- package/cli/evidence-command-definitions.mjs +69 -0
- package/cli/evidence-command-runner.mjs +18 -8
- package/cli/evidence-signing.mjs +5 -4
- package/cli/external-observation-mapping.mjs +9 -10
- package/cli/formatters/terminal-command-formats.mjs +6 -0
- package/cli/formatters/terminal-lookup.mjs +24 -46
- package/cli/formatters/terminal-shared.mjs +5 -3
- package/cli/formatters/terminal-technology.mjs +114 -0
- package/cli/history-arguments.mjs +39 -0
- package/cli/history-command-definitions.mjs +88 -0
- package/cli/indicator-set-command.mjs +33 -0
- package/cli/inline-command-families.mjs +6 -46
- package/cli/inline-command-runner.mjs +4 -31
- package/cli/input.mjs +2 -2
- package/cli/intake-command.mjs +107 -0
- package/cli/interactive-launcher.mjs +2 -160
- package/cli/interchange-report.mjs +8 -10
- package/cli/investigation-artifacts.mjs +176 -0
- package/cli/investigation-checkpoint.mjs +10 -0
- package/cli/investigation-folder.mjs +114 -0
- package/cli/investigation-manifest.mjs +1 -112
- package/cli/investigation-package-review.mjs +103 -0
- package/cli/investigation-plan.mjs +38 -147
- package/cli/investigation-recipes.mjs +177 -0
- package/cli/investigation-run.mjs +256 -35
- package/cli/local-document-checkpoint.mjs +170 -0
- package/cli/local-mmdb-review.mjs +197 -17
- package/cli/lookalike-calibration.mjs +1 -1
- package/cli/lookup-brief.mjs +2 -2
- package/cli/lookup-browser.mjs +5 -12
- package/cli/lookup-command-runner.mjs +33 -39
- package/cli/lookup-diff.mjs +47 -12
- package/cli/lookup-plan.mjs +16 -14
- package/cli/lookup-reconcile.mjs +15 -12
- package/cli/lookup-timeline.mjs +7 -6
- package/cli/mail-header-review.mjs +24 -247
- package/cli/mail-review.mjs +75 -21
- package/cli/manual.mjs +1 -0
- package/cli/network-arguments.mjs +51 -0
- package/cli/network-command-definitions.mjs +130 -0
- package/cli/network-command-runner.mjs +45 -31
- package/cli/offline-evidence-review.mjs +39 -6
- package/cli/output-file.mjs +31 -5
- package/cli/posture.mjs +1 -1
- package/cli/registry-doctor.mjs +3 -1
- package/cli/registry-fixture-scaffold.mjs +1 -1
- package/cli/registry-support.mjs +1 -1
- package/cli/retained-artifact-diff.mjs +10 -9
- package/cli/review-arguments.mjs +131 -0
- package/cli/review-command-definitions.mjs +273 -0
- package/cli/review-command-runner.mjs +104 -149
- package/cli/risk-calibration.mjs +8 -8
- package/cli/runner.mjs +74 -37
- package/cli/saved-lookup.mjs +2 -2
- package/cli/selected-input-worker.mjs +63 -0
- package/cli/sharing-review.mjs +8 -80
- package/cli/signer-trust.mjs +104 -0
- package/cli/source-reliability.mjs +75 -84
- package/cli/support-arguments.mjs +49 -0
- package/cli/support-command-definitions.mjs +209 -0
- package/cli/support-command-runner.mjs +2 -2
- package/cli/terminal-input.mjs +168 -0
- package/cli/workflow-arguments.mjs +86 -0
- package/cli/workflow-command-definitions.mjs +78 -0
- package/cli/workflow-command-runner.mjs +75 -13
- package/docs/cli-reference.md +103 -30
- package/docs/cli.md +510 -25
- package/docs/contextual-reviews.md +155 -0
- package/lib/abort.mjs +4 -1
- package/lib/analyst-taxonomy.mjs +2 -40
- package/lib/application-version.mjs +1 -1
- package/lib/availability.mjs +27 -754
- package/lib/bounded-contract-normalizers.mjs +4 -0
- package/lib/bounded-file.mjs +3 -1
- package/lib/bounded-html-document.mjs +125 -0
- package/lib/bounded-json.mjs +2 -290
- package/lib/browser-library-profile.mjs +99 -130
- package/lib/browser-library-worker.mjs +112 -0
- package/lib/candidate-provenance-bounds.mjs +2 -0
- package/lib/classify.mjs +1 -1
- package/lib/client-behavior-profile.mjs +3 -3
- package/lib/ct-query.mjs +2 -27
- package/lib/ct-response-bounds.mjs +2 -9
- package/lib/ct-search.mjs +89 -46
- package/lib/dmarc-discovery.mjs +62 -0
- package/lib/dns-convergence-review.mjs +21 -8
- package/lib/dns-delegation-health.mjs +36 -22
- package/lib/dns-inheritance-review.mjs +207 -0
- package/lib/dns-intelligence.mjs +44 -29
- package/lib/dns-mx.mjs +1 -1
- package/lib/dns-query-coverage.mjs +40 -0
- package/lib/dnssec-evidence-validation.mjs +11 -3
- package/lib/domain-assurance.mjs +1 -1
- package/lib/domain-change-packet.mjs +18 -14
- package/lib/domain-change-review.mjs +40 -16
- package/lib/domain-control-flight-recorder.mjs +301 -124
- package/lib/domain-control-manifest.mjs +126 -58
- package/lib/domain-posture-analysis.mjs +41 -11
- package/lib/domain-posture-parsers.mjs +88 -6
- package/lib/domain-posture.mjs +120 -32
- package/lib/domain-registration.mjs +392 -0
- package/lib/evidence-export-privacy.mjs +568 -0
- package/lib/evidence-export.mjs +12 -593
- package/lib/evidence-report.mjs +7 -2
- package/lib/external-intelligence-risk.mjs +2 -124
- package/lib/favicon.mjs +117 -41
- package/lib/generated/cisa-kev-catalog.mjs +50 -4
- package/lib/generated/registrar-standing-catalogue.mjs +18 -10
- package/lib/generated/retire-browser-catalog.mjs +2742 -1034
- package/lib/generated/{unicode-confusables-17.mjs → unicode-confusables.mjs} +33 -33
- package/lib/html-signals.mjs +87 -85
- package/lib/http-evidence-bounds.mjs +2 -7
- package/lib/http-intelligence.mjs +52 -27
- package/lib/idn-confusable-policy.mjs +6 -4
- package/lib/idn-confusables.mjs +1 -1
- package/lib/interchange-fidelity-registry.mjs +10 -0
- package/lib/lookup-cache.mjs +34 -11
- package/lib/lookup-child-profile-contract.mjs +24 -15
- package/lib/lookup-contract-primitives.mjs +1 -1
- package/lib/lookup-diagnostics.mjs +2 -1
- package/lib/lookup-network-evidence-bounds.mjs +1 -10
- package/lib/lookup-progress.mjs +79 -13
- package/lib/lookup-response-contract.mjs +1077 -0
- package/lib/lookup-response.mjs +11 -24
- package/lib/lookup-source-progress.mjs +24 -35
- package/lib/lookup.mjs +49 -28
- package/lib/observed-network-context.mjs +43 -27
- package/lib/opportunity-scoring.mjs +2 -107
- package/lib/outbound-identity.mjs +1 -1
- package/lib/outbound-request-bounds.mjs +4 -1
- package/lib/page-fingerprints.mjs +87 -134
- package/lib/page-role-profile.mjs +12 -8
- package/lib/perceptual-hash-comparison.mjs +2 -34
- package/lib/perceptual-hash.mjs +79 -24
- package/lib/portable-generator.mjs +2 -29
- package/lib/project-metadata.mjs +2 -8
- package/lib/rdap-bootstrap.mjs +72 -22
- package/lib/rdap-client.mjs +26 -14
- package/lib/rdap-normalization.mjs +33 -18
- package/lib/rdap-search-workbench.mjs +1 -1
- package/lib/rdap-transport.mjs +47 -20
- package/lib/rdap-types.mjs +2 -1
- package/lib/rdap.mjs +18 -3
- package/lib/registrable-domain.mjs +2 -36
- package/lib/registrar-standing-contract.mjs +2 -1
- package/lib/registrar-standing.mjs +9 -13
- package/lib/registry-capabilities.mjs +7 -3
- package/lib/registry-capability-catalogue.mjs +29 -20
- package/lib/registry-dates.mjs +2 -190
- package/lib/registry-insights.mjs +56 -18
- package/lib/response-policy.mjs +86 -48
- package/lib/risk-calibration-summary.mjs +2 -338
- package/lib/risk-scoring.mjs +2 -345
- package/lib/rpki-evidence.mjs +8 -5
- package/lib/safe-fetch.mjs +54 -47
- package/lib/scoring-evidence-quality.mjs +2 -107
- package/lib/security-txt.mjs +10 -7
- package/lib/semantic-version.mjs +2 -54
- package/lib/service-binding-dns.mjs +35 -0
- package/lib/smtp-transport-review.mjs +75 -34
- package/lib/sslbl-certificates.generated.mjs +141 -138
- package/lib/static-html-analysis.mjs +117 -146
- package/lib/structured-data-identity.mjs +2 -1
- package/lib/technology-markup.mjs +100 -0
- package/lib/threat-intelligence-manifests.mjs +11 -139
- package/lib/threat-intelligence-runtime.mjs +24 -52
- package/lib/threat-intelligence-targets.mjs +6 -34
- package/lib/threat-intelligence-types.mjs +2 -36
- package/lib/typosquat-generator.mjs +29 -16
- package/lib/urlhaus-intelligence.mjs +8 -3
- package/lib/urlscan-intelligence.mjs +1 -1
- package/lib/website-enrichment.mjs +388 -0
- package/lib/website-security-posture.mjs +30 -6
- package/lib/website-technology.mjs +111 -71
- package/lib/whois-authority.mjs +55 -5
- package/lib/whois-chain.mjs +9 -2
- package/lib/whois-common-formats.mjs +5 -5
- package/lib/whois-contracts.mjs +2 -1
- package/lib/whois-dialects.mjs +2 -2
- package/lib/whois-normalization.mjs +3 -1
- package/lib/whois-parser-support.mjs +8 -1
- package/lib/whois-parser.mjs +5 -3
- package/lib/whois-transport.mjs +23 -7
- package/package.json +14 -14
- package/packages/analysis/analyst-taxonomy.mjs +40 -0
- package/packages/analysis/bounded-json.mjs +330 -0
- package/packages/analysis/candidate-provenance-bounds.mjs +4 -0
- package/packages/analysis/cli-argument-grammar.mjs +216 -0
- package/packages/analysis/ct-query.mjs +27 -0
- package/packages/analysis/ct-response-bounds.mjs +9 -0
- package/packages/analysis/external-intelligence-risk.mjs +124 -0
- package/packages/analysis/http-evidence-bounds.mjs +8 -0
- package/packages/analysis/lookup-network-evidence-bounds.mjs +10 -0
- package/packages/analysis/opportunity-scoring.mjs +107 -0
- package/packages/analysis/perceptual-hash-comparison.mjs +34 -0
- package/packages/analysis/portable-generator.mjs +29 -0
- package/packages/analysis/project-metadata.mjs +8 -0
- package/packages/analysis/registrable-domain.mjs +56 -0
- package/packages/analysis/risk-calibration-summary.mjs +352 -0
- package/packages/analysis/risk-scoring.mjs +345 -0
- package/packages/analysis/scoring-evidence-quality.mjs +107 -0
- package/packages/analysis/semantic-version.mjs +54 -0
- package/packages/analysis/threat-intelligence-types.mjs +75 -0
- package/packages/cases/case-attachment-model.mjs +83 -0
- package/packages/cases/case-evidence-links.mjs +135 -0
- package/packages/cases/case-evidence-model.mjs +229 -69
- package/packages/cases/case-evidence-quality.mjs +5 -0
- package/packages/cases/case-export-input.mjs +55 -0
- package/packages/cases/case-incident-context.mjs +52 -0
- package/packages/cases/case-markdown.mjs +11 -0
- package/packages/cases/case-migration-model.mjs +98 -48
- package/packages/cases/case-model.mjs +1 -0
- package/packages/cases/case-recheck-model.mjs +149 -0
- package/packages/cases/case-record-contracts.mjs +1 -1
- package/packages/cases/case-record-core.mjs +4 -3
- package/packages/cases/case-record-operations.mjs +123 -133
- package/packages/cases/case-record-projection.mjs +30 -20
- package/packages/cases/case-record-version-input.mjs +19 -0
- package/packages/cases/case-report.mjs +114 -67
- package/packages/cases/case-requested-evidence.mjs +146 -0
- package/packages/cases/case-response-actions.mjs +756 -0
- package/packages/cases/case-response-model.mjs +49 -1129
- package/packages/cases/case-response-outcomes.mjs +338 -0
- package/packages/cases/case-response-packet-render.mjs +157 -0
- package/packages/cases/case-response-packet-types.mjs +1 -0
- package/packages/cases/case-response-packet-vocabulary.mjs +48 -0
- package/packages/cases/case-response-packet.mjs +58 -593
- package/packages/cases/case-response-records.mjs +93 -0
- package/packages/cases/case-response-review-inputs.mjs +382 -0
- package/packages/cases/case-response-values.mjs +85 -0
- package/packages/cases/case-review-package.mjs +45 -0
- package/packages/cases/case-selection.mjs +46 -0
- package/packages/cases/case-storage-model.mjs +26 -1
- package/packages/cases/case-workflow-metadata.mjs +225 -0
- package/packages/cases/case-workflow-migration.mjs +43 -0
- package/packages/cases/http-summary.mjs +6 -1
- package/packages/cases/response-route-freshness.mjs +16 -0
- package/packages/comparison/capture-context.mjs +61 -0
- package/packages/comparison/comparison-ledger-bulk.mjs +4 -1
- package/packages/comparison/favicon-similarity.mjs +1 -1
- package/packages/comparison/page-similarity.mjs +3 -2
- package/packages/comparison/relationship-evidence.mjs +103 -14
- package/packages/comparison/relationship-provenance.mjs +99 -0
- package/packages/contracts/analyst-interchange.mjs +234 -0
- package/packages/contracts/analyst-review-state-contract.mjs +11 -0
- package/packages/contracts/analyst-review-state.mjs +5 -4
- package/packages/contracts/case-portability.mjs +370 -114
- package/packages/contracts/case-views-contract.mjs +12 -0
- package/packages/contracts/case-views.mjs +26 -0
- package/packages/contracts/cli-command-semantics.mjs +3 -0
- package/packages/contracts/cli-grammar.mjs +6 -0
- package/packages/contracts/cli-lookup-brief.mjs +2 -0
- package/packages/contracts/cli-lookup.mjs +0 -1
- package/packages/contracts/context-review.mjs +24 -0
- package/packages/contracts/document-intake.mjs +11 -0
- package/packages/contracts/domain-control-flight-recorder.mjs +98 -44
- package/packages/contracts/domain-control-manifest.mjs +128 -74
- package/packages/contracts/domain-control-monitor.mjs +59 -45
- package/packages/contracts/domain-control-review.mjs +200 -121
- package/packages/contracts/domain-name.mjs +21 -0
- package/packages/contracts/external-observation-interchange.mjs +2 -1
- package/packages/contracts/extracted-domain-lifecycle.mjs +11 -32
- package/packages/contracts/har-review.mjs +2 -0
- package/packages/contracts/http-url.mjs +2 -0
- package/packages/contracts/identity-events.mjs +4 -0
- package/packages/contracts/investigation-package-limits.mjs +24 -0
- package/packages/contracts/investigation-portability.mjs +56 -27
- package/packages/contracts/investigation-run.mjs +18 -0
- package/packages/contracts/ip-address.mjs +44 -0
- package/packages/contracts/lookup-evidence.mjs +35 -0
- package/packages/contracts/mail-authentication.mjs +5 -0
- package/packages/contracts/message-intake.mjs +31 -0
- package/packages/contracts/offline-comparison.mjs +4 -2
- package/packages/contracts/page-fingerprints.mjs +15 -0
- package/packages/contracts/passive-mail.mjs +10 -0
- package/packages/contracts/risk-calibration.mjs +0 -1
- package/packages/contracts/schema-compatibility.mjs +1 -1
- package/packages/contracts/schema-lifecycle.mjs +83 -153
- package/packages/contracts/selected-file-limits.mjs +6 -0
- package/packages/contracts/vulnerability-identifiers.mjs +5 -0
- package/packages/contracts/web-capture.mjs +10 -0
- package/packages/contracts/workspace-portability.mjs +219 -238
- package/packages/evidence/artifact-integrity.mjs +8 -2
- package/packages/evidence/artifact-structure.mjs +105 -0
- package/packages/evidence/collection-quality.mjs +67 -0
- package/packages/evidence/decision-fact.mjs +4 -1
- package/packages/evidence/dns-query-outcome.mjs +72 -0
- package/packages/evidence/domain-control-runtime.mjs +155 -27
- package/packages/evidence/domain-posture-context.mjs +79 -0
- package/packages/evidence/email-recipient.mjs +18 -0
- package/packages/evidence/image-regions.mjs +56 -0
- package/packages/evidence/latest-observations.mjs +28 -0
- package/packages/evidence/lookup-target.mjs +108 -0
- package/packages/evidence/observation.mjs +18 -1
- package/packages/evidence/passphrase-encryption.mjs +44 -0
- package/packages/evidence/png-pixels.mjs +117 -0
- package/packages/evidence/public-address-policy.mjs +83 -0
- package/packages/evidence/record-identity.mjs +72 -0
- package/packages/evidence/registry-dates.mjs +190 -0
- package/packages/evidence/retained-file.mjs +69 -0
- package/packages/evidence/review-age.mjs +14 -0
- package/packages/interchange/bagit.mjs +342 -0
- package/packages/interchange/bounded-zip-extraction.mjs +278 -0
- package/packages/interchange/crc32.mjs +17 -0
- package/packages/interchange/defensive-indicator-export.mjs +264 -0
- package/packages/interchange/external-findings-import.mjs +93 -61
- package/packages/interchange/managed-indicator-export.mjs +35 -0
- package/packages/interchange/managed-indicator-set.mjs +210 -0
- package/packages/interchange/misp-indicator-export.mjs +93 -0
- package/packages/interchange/sharing-policy.mjs +56 -0
- package/packages/interchange/stix-indicator-export.mjs +129 -0
- package/packages/interchange/web-capture-import.mjs +389 -0
- package/packages/investigation/acquisition-decision-packet.mjs +1 -1
- package/packages/investigation/bulk-mail-exposure.mjs +9 -14
- package/packages/investigation/capture-coverage.mjs +57 -0
- package/packages/investigation/connector-provenance-review.mjs +100 -0
- package/packages/investigation/context-review.mjs +35 -0
- package/packages/investigation/document-intake.mjs +81 -0
- package/packages/investigation/docx-intake.mjs +133 -0
- package/packages/investigation/domain-history-review.mjs +100 -0
- package/packages/investigation/domain-transition-review.mjs +47 -0
- package/packages/investigation/evidence-coverage-ledger.mjs +9 -1
- package/packages/investigation/har-intake.mjs +68 -0
- package/packages/investigation/identity-event-intake.mjs +131 -0
- package/packages/investigation/identity-incident-review.mjs +39 -0
- package/packages/investigation/incident-sequence-review.mjs +58 -0
- package/packages/investigation/intake-report.mjs +19 -0
- package/packages/investigation/investigation-bagit.mjs +14 -0
- package/packages/investigation/investigation-capsule.mjs +33 -5
- package/packages/investigation/investigation-manifest.mjs +200 -0
- package/packages/investigation/investigation-package-crypto.mjs +98 -0
- package/packages/investigation/investigation-package.mjs +200 -0
- package/packages/investigation/link-intake.mjs +179 -0
- package/packages/investigation/lookup-asset-graph-inputs.mjs +38 -0
- package/packages/investigation/lookup-asset-graph.mjs +161 -141
- package/packages/investigation/lookup-claim-readiness.mjs +18 -0
- package/packages/investigation/lookup-investigation-brief.mjs +3 -3
- package/packages/investigation/lookup-task-guidance.mjs +58 -0
- package/packages/investigation/mail-authentication-review.mjs +222 -0
- package/packages/investigation/mail-header-identity.mjs +209 -0
- package/packages/investigation/message-intake.mjs +195 -0
- package/packages/investigation/page-behaviour.mjs +113 -0
- package/packages/investigation/pdf-intake.mjs +155 -0
- package/packages/investigation/platform-continuity-review.mjs +60 -0
- package/packages/investigation/qr-intake.mjs +56 -0
- package/packages/investigation/requested-action-hints.mjs +11 -0
- package/packages/investigation/selected-input-review.mjs +17 -0
- package/packages/investigation/storefront-review.mjs +70 -0
- package/packages/monitoring/analyst-review-state.mjs +38 -90
- package/packages/workspace/brand-profile-model.mjs +160 -22
- package/packages/workspace/bulk-columns.mjs +17 -0
- package/packages/workspace/bulk-review-model.mjs +39 -31
- package/packages/workspace/bulk-session-model.mjs +112 -27
- package/packages/workspace/bulk-sort.mjs +1 -0
- package/packages/workspace/campaign-model.mjs +1 -1
- package/packages/workspace/case-views.mjs +102 -0
- package/packages/workspace/detection-rule-model.mjs +1 -1
- package/packages/workspace/hostile-input.mjs +29 -8
- package/packages/workspace/investigation-guide.mjs +2 -2
- package/packages/workspace/investigation-template-model.mjs +45 -6
- package/packages/workspace/page-baseline.mjs +20 -17
- package/packages/workspace/relationship-observation-model.mjs +54 -35
- package/packages/workspace/shortlist-model.mjs +14 -5
- package/packages/workspace/watchlist-history.mjs +30 -14
- package/packages/workspace/watchlist-store.mjs +24 -6
- package/packages/workspace/website-snapshot-model.mjs +98 -33
- package/packages/workspace/workspace-archive-crypto.mjs +41 -61
- package/packages/workspace/workspace-archive.mjs +90 -25
- package/third-party-notices.txt +577 -17
- package/lib/hostname.mjs +0 -38
- package/packages/investigation/bulk-review-export.mjs +0 -63
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { SUPPORT_COMMAND_DEFINITIONS } from "./support-command-definitions.mjs";
|
|
2
|
+
import { ASSURANCE_COMMAND_DEFINITIONS } from "./assurance-command-definitions.mjs";
|
|
3
|
+
import { COLLECTION_COMMAND_DEFINITIONS } from "./collection-command-definitions.mjs";
|
|
4
|
+
import { NETWORK_COMMAND_DEFINITIONS } from "./network-command-definitions.mjs";
|
|
5
|
+
import { REVIEW_COMMAND_DEFINITIONS } from "./review-command-definitions.mjs";
|
|
6
|
+
import { EVIDENCE_COMMAND_DEFINITIONS } from "./evidence-command-definitions.mjs";
|
|
7
|
+
import { WORKFLOW_COMMAND_DEFINITIONS } from "./workflow-command-definitions.mjs";
|
|
8
|
+
import { HISTORY_COMMAND_DEFINITIONS } from "./history-command-definitions.mjs";
|
|
9
|
+
export const COMMAND_SEEDS = Object.freeze({
|
|
10
|
+
...SUPPORT_COMMAND_DEFINITIONS,
|
|
11
|
+
...ASSURANCE_COMMAND_DEFINITIONS,
|
|
12
|
+
...COLLECTION_COMMAND_DEFINITIONS,
|
|
13
|
+
...NETWORK_COMMAND_DEFINITIONS,
|
|
14
|
+
...REVIEW_COMMAND_DEFINITIONS,
|
|
15
|
+
...EVIDENCE_COMMAND_DEFINITIONS,
|
|
16
|
+
...WORKFLOW_COMMAND_DEFINITIONS,
|
|
17
|
+
...HISTORY_COMMAND_DEFINITIONS,
|
|
18
|
+
});
|
package/cli/compare.mjs
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { CliUsageError } from "./
|
|
1
|
+
import { CliUsageError } from "./errors.mjs";
|
|
2
2
|
import { isRdapRegistryAccessProfile, isWhoisRegistryAccessProfile, } from "./registry-access.mjs";
|
|
3
3
|
import { MAX_SAVED_LOOKUP_INPUT_BYTES, MAX_SAVED_LOOKUP_STRING_LENGTH, SAVED_LOOKUP_SCHEMA, SAVED_LOOKUP_SCHEMA_VERSION, parseSavedLookupDocument, readSavedLookupInputBounded, } from "./saved-lookup.mjs";
|
|
4
4
|
const MAX_COMPARE_INPUT_BYTES = MAX_SAVED_LOOKUP_INPUT_BYTES;
|
package/cli/completion.mjs
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { CLI_COMMAND_REGISTRY, CLI_COMMANDS, COMMAND_DESCRIPTIONS, FILE_POSITIONAL_COMMANDS, commandOptionSpec, commandDefinition, commandPositionalSpecs, metaActionDefinition, } from "./command-reference.mjs";
|
|
2
|
-
|
|
2
|
+
// Emergency bound for one static script; input and execution bounds are independent.
|
|
3
|
+
const MAX_CLI_COMPLETION_BYTES = 1024 * 1024;
|
|
3
4
|
const HELP_META_ACTION = metaActionDefinition('help');
|
|
4
5
|
const VERSION_META_ACTION = metaActionDefinition('version');
|
|
5
6
|
const COMMAND_META_ALIASES = Object.freeze([...HELP_META_ACTION.aliases]);
|
|
@@ -92,10 +93,83 @@ function commandOptionPatterns(kinds) {
|
|
|
92
93
|
return CLI_COMMANDS.flatMap((command) => optionNamesByKind(command, kinds)
|
|
93
94
|
.map((option) => `${command}:${option}`)).join('|');
|
|
94
95
|
}
|
|
96
|
+
function completionExclusions() {
|
|
97
|
+
const exclusions = new Map();
|
|
98
|
+
let changed = false;
|
|
99
|
+
const add = (command, selected, excluded) => {
|
|
100
|
+
const key = `${command}:${selected}`;
|
|
101
|
+
const values = exclusions.get(key) ?? new Set();
|
|
102
|
+
if (!values.has(excluded)) {
|
|
103
|
+
values.add(excluded);
|
|
104
|
+
changed = true;
|
|
105
|
+
}
|
|
106
|
+
exclusions.set(key, values);
|
|
107
|
+
};
|
|
108
|
+
for (const definition of CLI_COMMAND_REGISTRY) {
|
|
109
|
+
for (const constraint of definition.grammar.constraints) {
|
|
110
|
+
if (constraint.kind === 'mutually_exclusive') {
|
|
111
|
+
for (const selected of constraint.options) {
|
|
112
|
+
for (const excluded of constraint.options) {
|
|
113
|
+
if (selected !== excluded)
|
|
114
|
+
add(definition.command, selected, excluded);
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
else if (constraint.kind === 'excludes_all' || constraint.kind === 'value_excludes') {
|
|
119
|
+
const selected = constraint.kind === 'value_excludes'
|
|
120
|
+
? `${constraint.option}=${constraint.value}` : constraint.option;
|
|
121
|
+
for (const excluded of constraint.excludedOptions) {
|
|
122
|
+
add(definition.command, selected, excluded);
|
|
123
|
+
add(definition.command, excluded, selected);
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
do {
|
|
129
|
+
changed = false;
|
|
130
|
+
for (const definition of CLI_COMMAND_REGISTRY) {
|
|
131
|
+
for (const constraint of definition.grammar.constraints) {
|
|
132
|
+
if (constraint.kind !== 'requires_all' && constraint.kind !== 'requires_any')
|
|
133
|
+
continue;
|
|
134
|
+
const required = constraint.requiredOptions;
|
|
135
|
+
for (const [key, blocked] of exclusions) {
|
|
136
|
+
if (!key.startsWith(`${definition.command}:`))
|
|
137
|
+
continue;
|
|
138
|
+
const unavailable = required.map((option) => blocked.has(option));
|
|
139
|
+
if (constraint.kind === 'requires_all' ? unavailable.some(Boolean) : unavailable.every(Boolean)) {
|
|
140
|
+
add(definition.command, key.slice(definition.command.length + 1), constraint.option);
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
const requiredExclusions = required.map((option) => exclusions.get(`${definition.command}:${option}`) ?? new Set());
|
|
144
|
+
for (const blocked of new Set(requiredExclusions.flatMap((values) => [...values]))) {
|
|
145
|
+
if (constraint.kind === 'requires_all' || requiredExclusions.every((values) => values.has(blocked))) {
|
|
146
|
+
add(definition.command, constraint.option, blocked);
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
} while (changed);
|
|
152
|
+
return new Map([...exclusions].map(([key, values]) => [key, [...values]]));
|
|
153
|
+
}
|
|
154
|
+
const COMPLETION_EXCLUSIONS = completionExclusions();
|
|
155
|
+
function groupedCompletionExclusions() {
|
|
156
|
+
const groups = new Map();
|
|
157
|
+
for (const [key, values] of COMPLETION_EXCLUSIONS) {
|
|
158
|
+
const excluded = [...values].sort().join(' ');
|
|
159
|
+
const keys = groups.get(excluded) ?? [];
|
|
160
|
+
keys.push(key);
|
|
161
|
+
groups.set(excluded, keys);
|
|
162
|
+
}
|
|
163
|
+
return [...groups].map(([excluded, keys]) => ({ keys, excluded }));
|
|
164
|
+
}
|
|
165
|
+
function repeatableOptions(command) {
|
|
166
|
+
return commandDefinition(command).grammar.options
|
|
167
|
+
.filter((option) => option.occurrence === 'repeatable').map((option) => option.option);
|
|
168
|
+
}
|
|
95
169
|
function bashCompletion() {
|
|
96
|
-
const cases = CLI_COMMANDS.map((command) => ` ${command}) options="${commandOptions(command)}"; value_options="${optionNamesByKind(command, ['enum', 'file', 'integer', 'policy_list', 'text']).join(' ')}"; file_limit=${maximumFilePositionals(command)} ;;`).join('\n');
|
|
97
|
-
const commandValueCases = COMMAND_VALUE_OPTION_GROUPS.map((group) => ` ${group.commands.map((command) => `${command}:${group.option}`).join('|')})
|
|
98
|
-
const valueCases = Object.entries(VALUE_OPTIONS).map(([option, values]) => ` ${option})
|
|
170
|
+
const cases = CLI_COMMANDS.map((command) => ` ${command}) options="${commandOptions(command)}"; value_options="${optionNamesByKind(command, ['enum', 'file', 'integer', 'policy_list', 'text']).join(' ')}"; repeat_options="${repeatableOptions(command).join(' ')}"; file_limit=${maximumFilePositionals(command)} ;;`).join('\n');
|
|
171
|
+
const commandValueCases = COMMAND_VALUE_OPTION_GROUPS.map((group) => ` ${group.commands.map((command) => `${command}:${group.option}`).join('|')}) _whoisleuth_complete_values "${group.values.join(' ')}"; return ;;`).join('\n');
|
|
172
|
+
const valueCases = Object.entries(VALUE_OPTIONS).map(([option, values]) => ` ${option}) _whoisleuth_complete_values "${values.join(' ')}"; return ;;`).join('\n');
|
|
99
173
|
const integerCases = CLI_COMMAND_REGISTRY.flatMap((definition) => definition.grammar.options
|
|
100
174
|
.filter((option) => option.valueKind === 'integer')
|
|
101
175
|
.map((option) => {
|
|
@@ -120,8 +194,28 @@ _whoisleuth_direct_lookup_target() {
|
|
|
120
194
|
case " ${CLI_COMMANDS.join(' ')} " in *" \${candidate} "*) return 1 ;; esac
|
|
121
195
|
"\${COMP_WORDS[0]}" "\${candidate}" --plan --json >/dev/null 2>&1
|
|
122
196
|
}
|
|
197
|
+
_whoisleuth_complete_files() {
|
|
198
|
+
local candidate
|
|
199
|
+
COMPREPLY=()
|
|
200
|
+
# Readline handles quoting and escapes; preserve each path as one candidate.
|
|
201
|
+
while IFS= read -r candidate; do
|
|
202
|
+
COMPREPLY+=("\${candidate}")
|
|
203
|
+
done < <(compgen -o default -- "\${1}")
|
|
204
|
+
}
|
|
205
|
+
_whoisleuth_exclusions() {
|
|
206
|
+
case "\${1}:\${2}" in
|
|
207
|
+
${groupedCompletionExclusions().map((group) => ` ${group.keys.join('|')}) printf '%s' '${group.excluded}' ;;`).join('\n')}
|
|
208
|
+
esac
|
|
209
|
+
}
|
|
210
|
+
_whoisleuth_complete_values() {
|
|
211
|
+
local candidate
|
|
212
|
+
COMPREPLY=()
|
|
213
|
+
for candidate in $(compgen -W "\${1}" -- "\${current}"); do
|
|
214
|
+
[[ " \${blocked_options} " == *" \${previous}=\${candidate} "* ]] || COMPREPLY+=("\${candidate}")
|
|
215
|
+
done
|
|
216
|
+
}
|
|
123
217
|
_whoisleuth_completion() {
|
|
124
|
-
local current previous command options value_options file_limit positional_count expect_value foreign_option word integer_values seen_options i
|
|
218
|
+
local current previous command options value_options repeat_options file_limit positional_count expect_value foreign_option word integer_values seen_options i ended blocked_options value_option candidate available
|
|
125
219
|
current="\${COMP_WORDS[COMP_CWORD]}"
|
|
126
220
|
previous="\${COMP_WORDS[COMP_CWORD-1]}"
|
|
127
221
|
command="\${COMP_WORDS[1]}"
|
|
@@ -140,18 +234,28 @@ ${cases}
|
|
|
140
234
|
expect_value=0
|
|
141
235
|
foreign_option=0
|
|
142
236
|
seen_options=""
|
|
237
|
+
repeat_options="\${repeat_options:-}"
|
|
238
|
+
blocked_options=""
|
|
239
|
+
ended=0
|
|
240
|
+
value_option=""
|
|
143
241
|
for ((i=2; i<COMP_CWORD; i++)); do
|
|
144
242
|
word="\${COMP_WORDS[i]}"
|
|
145
|
-
if (( expect_value )); then
|
|
243
|
+
if (( expect_value )); then
|
|
244
|
+
blocked_options="\${blocked_options} $(_whoisleuth_exclusions "\${command}" "\${value_option}=\${word}")"
|
|
245
|
+
expect_value=0; continue
|
|
246
|
+
fi
|
|
247
|
+
if (( ended )); then ((positional_count+=1)); continue; fi
|
|
248
|
+
if [[ "\${word}" == -- ]]; then ended=1; continue; fi
|
|
146
249
|
if [[ "\${word}" == -* ]]; then
|
|
147
250
|
if [[ " \${options} " != *" \${word} "* ]]; then foreign_option=1; continue; fi
|
|
148
251
|
seen_options="\${seen_options} \${word}"
|
|
149
|
-
|
|
252
|
+
blocked_options="\${blocked_options} $(_whoisleuth_exclusions "\${command}" "\${word}")"
|
|
253
|
+
if [[ " \${value_options} " == *" \${word} "* ]]; then expect_value=1; value_option="\${word}"; fi
|
|
150
254
|
else
|
|
151
255
|
((positional_count+=1))
|
|
152
256
|
fi
|
|
153
257
|
done
|
|
154
|
-
if [[ " \${options} " == *" \${previous} "* ]]; then
|
|
258
|
+
if (( ! ended && expect_value )) && [[ " \${options} " == *" \${previous} "* ]]; then
|
|
155
259
|
case "\${command}:\${previous}" in
|
|
156
260
|
${commandValueCases}
|
|
157
261
|
esac
|
|
@@ -159,7 +263,7 @@ ${commandValueCases}
|
|
|
159
263
|
${valueCases}
|
|
160
264
|
esac
|
|
161
265
|
case "\${command}:\${previous}" in
|
|
162
|
-
${commandOptionPatterns(['file'])})
|
|
266
|
+
${commandOptionPatterns(['file'])}) _whoisleuth_complete_files "\${current}"; return ;;
|
|
163
267
|
${integerCases}
|
|
164
268
|
${commandOptionPatterns(['text'])}) COMPREPLY=(); return ;;
|
|
165
269
|
esac
|
|
@@ -167,20 +271,29 @@ ${integerCases}
|
|
|
167
271
|
case "\${command}:\${positional_count}" in
|
|
168
272
|
${positionalValueCases}
|
|
169
273
|
esac
|
|
170
|
-
if (( file_limit > positional_count && foreign_option == 0 && expect_value == 0 )) && [[ "\${current}" != -* ]]; then
|
|
171
|
-
|
|
274
|
+
if (( file_limit > positional_count && foreign_option == 0 && expect_value == 0 )) && { (( ended )) || [[ "\${current}" != -* ]]; }; then
|
|
275
|
+
_whoisleuth_complete_files "\${current}"
|
|
172
276
|
return
|
|
173
277
|
fi
|
|
174
|
-
COMPREPLY=(
|
|
278
|
+
COMPREPLY=()
|
|
279
|
+
(( ended || foreign_option || expect_value )) && return
|
|
280
|
+
available=""
|
|
281
|
+
for candidate in \${options}; do
|
|
282
|
+
[[ " \${blocked_options} " == *" \${candidate} "* ]] && continue
|
|
283
|
+
if [[ " \${seen_options} " == *" \${candidate} "* && " \${repeat_options} " != *" \${candidate} "* ]]; then continue; fi
|
|
284
|
+
if (( COMP_CWORD > 2 )) && [[ " ${COMMAND_META_ALIASES.join(' ')} " == *" \${candidate} "* ]]; then continue; fi
|
|
285
|
+
available="\${available} \${candidate}"
|
|
286
|
+
done
|
|
287
|
+
COMPREPLY=( $(compgen -W "\${available}" -- "\${current}") )
|
|
175
288
|
}
|
|
176
|
-
complete -F _whoisleuth_completion whoisleuth
|
|
289
|
+
complete -o filenames -F _whoisleuth_completion whoisleuth
|
|
177
290
|
`;
|
|
178
291
|
}
|
|
179
292
|
function zshCompletion() {
|
|
180
293
|
const commandEntries = CLI_COMMANDS.map((command) => `'${command}:${COMMAND_DESCRIPTIONS[command] || command}'`).join(' ');
|
|
181
|
-
const cases = CLI_COMMANDS.map((command) => ` ${command}) options=(${commandOptions(command)}); value_options=(${optionNamesByKind(command, ['enum', 'file', 'integer', 'policy_list', 'text']).join(' ')}); file_limit=${maximumFilePositionals(command)} ;;`).join('\n');
|
|
182
|
-
const commandValueCases = COMMAND_VALUE_OPTION_GROUPS.map((group) => ` ${group.commands.map((command) => `${command}:${group.option}`).join('|')})
|
|
183
|
-
const valueCases = Object.entries(VALUE_OPTIONS).map(([option, values]) => ` ${option})
|
|
294
|
+
const cases = CLI_COMMANDS.map((command) => ` ${command}) options=(${commandOptions(command)}); value_options=(${optionNamesByKind(command, ['enum', 'file', 'integer', 'policy_list', 'text']).join(' ')}); repeat_options=(${repeatableOptions(command).join(' ')}); file_limit=${maximumFilePositionals(command)} ;;`).join('\n');
|
|
295
|
+
const commandValueCases = COMMAND_VALUE_OPTION_GROUPS.map((group) => ` ${group.commands.map((command) => `${command}:${group.option}`).join('|')}) _whoisleuth_values ${group.values.join(' ')}; return ;;`).join('\n');
|
|
296
|
+
const valueCases = Object.entries(VALUE_OPTIONS).map(([option, values]) => ` ${option}) _whoisleuth_values ${values.join(' ')}; return ;;`).join('\n');
|
|
184
297
|
const integerCases = CLI_COMMAND_REGISTRY.flatMap((definition) => definition.grammar.options
|
|
185
298
|
.filter((option) => option.valueKind === 'integer')
|
|
186
299
|
.map((option) => {
|
|
@@ -204,10 +317,23 @@ _whoisleuth_direct_lookup_target() {
|
|
|
204
317
|
[[ " ${CLI_COMMANDS.join(' ')} " == *" \${candidate} "* ]] && return 1
|
|
205
318
|
"\${words[1]}" "\${candidate}" --plan --json >/dev/null 2>&1
|
|
206
319
|
}
|
|
320
|
+
_whoisleuth_exclusions() {
|
|
321
|
+
case "\${1}:\${2}" in
|
|
322
|
+
${groupedCompletionExclusions().map((group) => ` ${group.keys.join('|')}) print -r -- '${group.excluded}' ;;`).join('\n')}
|
|
323
|
+
esac
|
|
324
|
+
}
|
|
325
|
+
_whoisleuth_values() {
|
|
326
|
+
local candidate
|
|
327
|
+
local -a values
|
|
328
|
+
for candidate in "$@"; do
|
|
329
|
+
[[ " \${blocked_options} " == *" \${previous}=\${candidate} "* ]] || values+=("\${candidate}")
|
|
330
|
+
done
|
|
331
|
+
(( \${#values} )) && compadd -- \${values[@]}
|
|
332
|
+
}
|
|
207
333
|
_whoisleuth() {
|
|
208
|
-
local command previous word
|
|
209
|
-
local -a options commands value_options seen_options
|
|
210
|
-
integer file_limit=0 positional_count=0 expect_value=0 foreign_option=0 i
|
|
334
|
+
local command previous word blocked_options="" value_option="" candidate
|
|
335
|
+
local -a options commands value_options seen_options repeat_options available
|
|
336
|
+
integer file_limit=0 positional_count=0 expect_value=0 foreign_option=0 ended=0 i
|
|
211
337
|
commands=(${commandEntries} ${[
|
|
212
338
|
...HELP_META_ACTION.aliases.map((alias) => `'${alias}:Show help'`),
|
|
213
339
|
...VERSION_META_ACTION.aliases.map((alias) => `'${alias}:Show version'`),
|
|
@@ -227,16 +353,22 @@ ${cases}
|
|
|
227
353
|
esac
|
|
228
354
|
for ((i=3; i<CURRENT; i++)); do
|
|
229
355
|
word="\${words[i]}"
|
|
230
|
-
if (( expect_value )); then
|
|
356
|
+
if (( expect_value )); then
|
|
357
|
+
blocked_options="\${blocked_options} $(_whoisleuth_exclusions "\${command}" "\${value_option}=\${word}")"
|
|
358
|
+
expect_value=0; continue
|
|
359
|
+
fi
|
|
360
|
+
if (( ended )); then ((positional_count+=1)); continue; fi
|
|
361
|
+
if [[ "\${word}" == -- ]]; then ended=1; continue; fi
|
|
231
362
|
if [[ "\${word}" == -* ]]; then
|
|
232
363
|
if [[ " \${options[*]} " != *" \${word} "* ]]; then foreign_option=1; continue; fi
|
|
233
364
|
seen_options+=("\${word}")
|
|
234
|
-
|
|
365
|
+
blocked_options="\${blocked_options} $(_whoisleuth_exclusions "\${command}" "\${word}")"
|
|
366
|
+
if [[ " \${value_options[*]} " == *" \${word} "* ]]; then expect_value=1; value_option="\${word}"; fi
|
|
235
367
|
else
|
|
236
368
|
((positional_count+=1))
|
|
237
369
|
fi
|
|
238
370
|
done
|
|
239
|
-
if [[ " \${options[*]} " == *" \${previous} "* ]]; then
|
|
371
|
+
if (( ! ended && expect_value )) && [[ " \${options[*]} " == *" \${previous} "* ]]; then
|
|
240
372
|
case "\${command}:\${previous}" in
|
|
241
373
|
${commandValueCases}
|
|
242
374
|
esac
|
|
@@ -252,11 +384,18 @@ ${integerCases}
|
|
|
252
384
|
case "\${command}:\${positional_count}" in
|
|
253
385
|
${positionalValueCases}
|
|
254
386
|
esac
|
|
255
|
-
if (( file_limit > positional_count && foreign_option == 0 && expect_value == 0 )) && [[ "\${words[CURRENT]}" != -* ]]; then
|
|
387
|
+
if (( file_limit > positional_count && foreign_option == 0 && expect_value == 0 )) && { (( ended )) || [[ "\${words[CURRENT]}" != -* ]]; }; then
|
|
256
388
|
_files
|
|
257
389
|
return
|
|
258
390
|
fi
|
|
259
|
-
|
|
391
|
+
(( ended || foreign_option || expect_value )) && return
|
|
392
|
+
for candidate in \${options[@]}; do
|
|
393
|
+
[[ " \${blocked_options} " == *" \${candidate} "* ]] && continue
|
|
394
|
+
if [[ " \${seen_options[*]} " == *" \${candidate} "* && " \${repeat_options[*]} " != *" \${candidate} "* ]]; then continue; fi
|
|
395
|
+
if (( CURRENT > 3 )) && [[ " ${COMMAND_META_ALIASES.join(' ')} " == *" \${candidate} "* ]]; then continue; fi
|
|
396
|
+
available+=("\${candidate}")
|
|
397
|
+
done
|
|
398
|
+
(( \${#available} )) && compadd -- \${available[@]}
|
|
260
399
|
}
|
|
261
400
|
if [[ "\${funcstack[1]}" == "_whoisleuth" ]]; then
|
|
262
401
|
_whoisleuth "\$@"
|
|
@@ -269,7 +408,13 @@ function fishCompletion() {
|
|
|
269
408
|
const commandLines = CLI_COMMANDS.map((command) => (`__whoisleuth_add_command '${command}' '${COMMAND_DESCRIPTIONS[command]}'`));
|
|
270
409
|
const optionGroups = new Map();
|
|
271
410
|
for (const definition of CLI_COMMAND_REGISTRY) {
|
|
272
|
-
|
|
411
|
+
const options = [
|
|
412
|
+
...definition.grammar.options,
|
|
413
|
+
...definition.grammar.metaActions.map((id) => ({
|
|
414
|
+
option: longMetaAlias(metaActionDefinition(id)), arity: 0, valueKind: 'flag',
|
|
415
|
+
})),
|
|
416
|
+
];
|
|
417
|
+
for (const option of options) {
|
|
273
418
|
const key = `${option.option}\u0000${option.arity}\u0000${option.valueKind === 'file'}`;
|
|
274
419
|
const group = optionGroups.get(key) ?? {
|
|
275
420
|
option: option.option,
|
|
@@ -284,14 +429,14 @@ function fishCompletion() {
|
|
|
284
429
|
const optionLines = [...optionGroups.values()].map((group) => {
|
|
285
430
|
const condition = `__whoisleuth_command_is ${group.commands.join(' ')}`
|
|
286
431
|
+ (group.commands.includes('lookup') ? '; or __whoisleuth_direct_lookup_target' : '');
|
|
287
|
-
return `complete -c whoisleuth -n '${condition}'${group.option === HELP_LONG_ALIAS ? ` -s ${HELP_SHORT_ALIAS.slice(1)}` : ''} -l ${group.option.slice(2)}${group.arity === 1 ? ' -r' : '
|
|
432
|
+
return `complete -c whoisleuth -n 'begin; ${condition}; end; and __whoisleuth_option_available ${group.option}'${group.option === HELP_LONG_ALIAS ? ` -s ${HELP_SHORT_ALIAS.slice(1)}` : ''} -l ${group.option.slice(2)}${group.arity === 1 ? group.file ? ' -r -F' : ' -r -f' : ''}`;
|
|
288
433
|
});
|
|
289
434
|
const commandValueLines = COMMAND_VALUE_OPTION_GROUPS.flatMap((group) => {
|
|
290
435
|
const lines = [
|
|
291
|
-
`complete -c whoisleuth -n '__whoisleuth_command_is ${group.commands.join(' ')}; and __fish_prev_arg_in ${group.option}' -a '${group.values.join(' ')}'`,
|
|
436
|
+
`complete -c whoisleuth -n '__whoisleuth_command_is ${group.commands.join(' ')}; and __fish_prev_arg_in ${group.option}' -l ${group.option.slice(2)} -r -f -a '(__whoisleuth_values ${group.option} ${group.values.join(' ')})'`,
|
|
292
437
|
];
|
|
293
438
|
if (group.commands.includes('lookup')) {
|
|
294
|
-
lines.push(`complete -c whoisleuth -n '__whoisleuth_direct_lookup_target; and __fish_prev_arg_in ${group.option}' -a '${group.values.join(' ')}'`);
|
|
439
|
+
lines.push(`complete -c whoisleuth -n '__whoisleuth_direct_lookup_target; and __fish_prev_arg_in ${group.option}' -l ${group.option.slice(2)} -r -f -a '(__whoisleuth_values ${group.option} ${group.values.join(' ')})'`);
|
|
295
440
|
}
|
|
296
441
|
return lines;
|
|
297
442
|
});
|
|
@@ -301,10 +446,10 @@ function fishCompletion() {
|
|
|
301
446
|
? `__fish_prev_arg_in ${option}`
|
|
302
447
|
: `__whoisleuth_command_is ${commands.join(' ')}; and __fish_prev_arg_in ${option}`;
|
|
303
448
|
const lines = commands.length > 0
|
|
304
|
-
? [`complete -c whoisleuth -n '${namedCondition}' -a '${values.join(' ')}'`]
|
|
449
|
+
? [`complete -c whoisleuth -n '${namedCondition}' -l ${option.slice(2)} -r -f -a '(__whoisleuth_values ${option} ${values.join(' ')})'`]
|
|
305
450
|
: [];
|
|
306
451
|
if (commands.includes('lookup')) {
|
|
307
|
-
lines.push(`complete -c whoisleuth -n '__whoisleuth_direct_lookup_target; and __fish_prev_arg_in ${option}' -a '${values.join(' ')}'`);
|
|
452
|
+
lines.push(`complete -c whoisleuth -n '__whoisleuth_direct_lookup_target; and __fish_prev_arg_in ${option}' -l ${option.slice(2)} -r -f -a '(__whoisleuth_values ${option} ${values.join(' ')})'`);
|
|
308
453
|
}
|
|
309
454
|
return lines;
|
|
310
455
|
});
|
|
@@ -313,6 +458,7 @@ function fishCompletion() {
|
|
|
313
458
|
.flatMap((option) => option.integerRanges.map((range) => {
|
|
314
459
|
const qualifiers = [
|
|
315
460
|
`__fish_prev_arg_in ${option.option}`,
|
|
461
|
+
`and __whoisleuth_value_expected ${option.option}`,
|
|
316
462
|
`and __whoisleuth_command_is ${definition.command}`,
|
|
317
463
|
...(range.whenOptionPresent
|
|
318
464
|
? [`and __whoisleuth_seen ${definition.command} ${range.whenOptionPresent}`]
|
|
@@ -320,24 +466,14 @@ function fishCompletion() {
|
|
|
320
466
|
? [`and not __whoisleuth_seen ${definition.command} ${option.integerRanges.find((candidate) => candidate.whenOptionPresent)?.whenOptionPresent}`]
|
|
321
467
|
: []),
|
|
322
468
|
].join('; ');
|
|
323
|
-
return `complete -c whoisleuth -n '${qualifiers}' -a '(__whoisleuth_integer_values ${range.minimum} ${range.maximum})'`;
|
|
469
|
+
return `complete -c whoisleuth -n '${qualifiers}' -l ${option.option.slice(2)} -r -f -a '(__whoisleuth_integer_values ${range.minimum} ${range.maximum})'`;
|
|
324
470
|
})));
|
|
325
|
-
const positionalCases =
|
|
326
|
-
...FILE_POSITIONAL_COMMANDS,
|
|
327
|
-
...CLI_COMMANDS.filter((command) => positionalEnumValues(command).length > 0),
|
|
328
|
-
...CLI_COMMAND_REGISTRY.filter((definition) => definition.grammar.options
|
|
329
|
-
.some((option) => option.integerRanges.some((range) => range.whenOptionPresent !== null)))
|
|
330
|
-
.map((definition) => definition.command),
|
|
331
|
-
])].map((command) => {
|
|
471
|
+
const positionalCases = CLI_COMMANDS.map((command) => {
|
|
332
472
|
const definition = commandDefinition(command);
|
|
333
473
|
const commandValueOptions = definition.grammar.options
|
|
334
|
-
.filter((option) => option.
|
|
474
|
+
.filter((option) => option.arity === 1)
|
|
335
475
|
.map((option) => option.option);
|
|
336
|
-
|
|
337
|
-
|| positionalEnumValues(command).length > 0;
|
|
338
|
-
return ` case ${command}\n${positioning
|
|
339
|
-
? ` set options $options ${definition.completion.options.join(' ')}\n`
|
|
340
|
-
: ' set strict 0\n'} set value_options $value_options ${commandValueOptions.join(' ')}\n set limit ${maximumFilePositionals(command)}`;
|
|
476
|
+
return ` case ${command}\n set options ${commandOptions(command)}\n set value_options ${commandValueOptions.join(' ')}\n set repeat_options ${repeatableOptions(command).join(' ')}\n set limit ${maximumFilePositionals(command)}`;
|
|
341
477
|
}).join('\n');
|
|
342
478
|
const positionalEnumLines = CLI_COMMANDS.flatMap((command) => {
|
|
343
479
|
const values = positionalEnumValues(command);
|
|
@@ -383,56 +519,113 @@ function __whoisleuth_integer_values
|
|
|
383
519
|
set value (math $value + 1)
|
|
384
520
|
end
|
|
385
521
|
end
|
|
522
|
+
function __whoisleuth_exclusions
|
|
523
|
+
switch "$argv[1]:$argv[2]"
|
|
524
|
+
${groupedCompletionExclusions().map((group) => ` case ${group.keys.map((key) => `'${key}'`).join(' ')}\n printf '%s\\n' ${group.excluded}`).join('\n')}
|
|
525
|
+
end
|
|
526
|
+
end
|
|
386
527
|
function __whoisleuth_position_state
|
|
387
528
|
set -l expected_command $argv[1]
|
|
388
529
|
set -l words (commandline -opc)
|
|
389
530
|
test (count $words) -ge 2; or return 1
|
|
390
531
|
set -l command $words[2]
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
532
|
+
if not contains -- $command ${CLI_COMMANDS.join(' ')}
|
|
533
|
+
__whoisleuth_direct_lookup_target; or return 1
|
|
534
|
+
set command lookup
|
|
535
|
+
end
|
|
536
|
+
if test -n "$expected_command"
|
|
537
|
+
test "$command" = "$expected_command"; or return 1
|
|
538
|
+
end
|
|
539
|
+
set -l options
|
|
540
|
+
set -l value_options
|
|
541
|
+
set -l repeat_options
|
|
394
542
|
set -l limit 0
|
|
395
|
-
set -l strict 1
|
|
396
543
|
switch $command
|
|
397
544
|
${positionalCases}
|
|
398
545
|
case '*'
|
|
399
546
|
return 1
|
|
400
547
|
end
|
|
401
548
|
set -l expect_value 0
|
|
549
|
+
set -l value_option @none
|
|
550
|
+
set -l ended 0
|
|
402
551
|
set -l positional_count 0
|
|
403
552
|
set -l seen
|
|
553
|
+
set -l blocked
|
|
404
554
|
for word in $words[3..-1]
|
|
405
555
|
if test $expect_value -eq 1
|
|
556
|
+
set -a blocked (__whoisleuth_exclusions $command "$value_option=$word")
|
|
406
557
|
set expect_value 0
|
|
558
|
+
set value_option @none
|
|
559
|
+
continue
|
|
560
|
+
end
|
|
561
|
+
if test $ended -eq 1
|
|
562
|
+
set positional_count (math $positional_count + 1)
|
|
563
|
+
continue
|
|
564
|
+
end
|
|
565
|
+
if test "$word" = --
|
|
566
|
+
set ended 1
|
|
407
567
|
continue
|
|
408
568
|
end
|
|
409
569
|
if string match -q -- '-*' $word
|
|
410
|
-
|
|
411
|
-
contains -- $word $options; or return 1
|
|
412
|
-
end
|
|
570
|
+
contains -- $word $options; or return 1
|
|
413
571
|
set -a seen $word
|
|
414
|
-
|
|
572
|
+
set -a blocked (__whoisleuth_exclusions $command $word)
|
|
573
|
+
if contains -- $word $value_options
|
|
574
|
+
set expect_value 1
|
|
575
|
+
set value_option $word
|
|
576
|
+
end
|
|
415
577
|
else
|
|
416
578
|
set positional_count (math $positional_count + 1)
|
|
417
579
|
end
|
|
418
580
|
end
|
|
419
|
-
|
|
420
|
-
|
|
581
|
+
# Rows: position, file limit, pending option, separator, argument count,
|
|
582
|
+
# supplied options, excluded options/values, and repeatable options.
|
|
583
|
+
printf '%s\\n' $positional_count $limit $value_option $ended (math (count $words) - 2) "seen $seen" "blocked $blocked" "repeat $repeat_options"
|
|
421
584
|
end
|
|
422
585
|
function __whoisleuth_position_is
|
|
423
586
|
set -l state (__whoisleuth_position_state $argv[2]); or return 1
|
|
587
|
+
test "$state[3]" = @none; or return 1
|
|
424
588
|
test $state[1] -eq $argv[1]
|
|
425
589
|
end
|
|
426
590
|
function __whoisleuth_file_position
|
|
427
591
|
set -l words (commandline -opc)
|
|
428
592
|
test (count $words) -ge 2; or return 1
|
|
429
|
-
set -l state (__whoisleuth_position_state
|
|
593
|
+
set -l state (__whoisleuth_position_state); or return 1
|
|
430
594
|
test $state[2] -gt $state[1]; or return 1
|
|
595
|
+
test "$state[3]" = @none; or return 1
|
|
596
|
+
test $state[4] -eq 1; and return 0
|
|
431
597
|
not string match -qr -- ^- (commandline -ct)
|
|
432
598
|
end
|
|
433
599
|
function __whoisleuth_seen
|
|
434
600
|
set -l s (__whoisleuth_position_state $argv[1]); or return 1
|
|
435
|
-
contains -- $argv[2] $s[
|
|
601
|
+
contains -- $argv[2] (string split ' ' -- $s[6])
|
|
602
|
+
end
|
|
603
|
+
function __whoisleuth_option_available
|
|
604
|
+
set -l state (__whoisleuth_position_state); or return 1
|
|
605
|
+
test $state[4] -eq 0; or return 1
|
|
606
|
+
if test "$state[3]" != @none
|
|
607
|
+
test "$state[3]" = "$argv[1]"
|
|
608
|
+
return $status
|
|
609
|
+
end
|
|
610
|
+
contains -- $argv[1] (string split ' ' -- $state[7]); and return 1
|
|
611
|
+
if contains -- $argv[1] (string split ' ' -- $state[6])
|
|
612
|
+
contains -- $argv[1] (string split ' ' -- $state[8]); or return 1
|
|
613
|
+
end
|
|
614
|
+
if test $state[5] -gt 0
|
|
615
|
+
contains -- $argv[1] ${COMMAND_META_ALIASES.join(' ')}; and return 1
|
|
616
|
+
end
|
|
617
|
+
return 0
|
|
618
|
+
end
|
|
619
|
+
function __whoisleuth_value_expected
|
|
620
|
+
set -l state (__whoisleuth_position_state); or return 1
|
|
621
|
+
test $state[4] -eq 0; and test "$state[3]" = "$argv[1]"
|
|
622
|
+
end
|
|
623
|
+
function __whoisleuth_values
|
|
624
|
+
set -l state (__whoisleuth_position_state); or return 1
|
|
625
|
+
test $state[4] -eq 0; and test "$state[3]" = "$argv[1]"; or return 1
|
|
626
|
+
for value in $argv[2..-1]
|
|
627
|
+
contains -- "$argv[1]=$value" (string split ' ' -- $state[7]); or printf '%s\\n' $value
|
|
628
|
+
end
|
|
436
629
|
end
|
|
437
630
|
complete -c whoisleuth -f
|
|
438
631
|
${[HELP_META_ACTION, VERSION_META_ACTION].map((action) => (`complete -c whoisleuth -n '__fish_use_subcommand' -s ${shortMetaAlias(action).slice(1)} -l ${longMetaAlias(action).slice(2)}`)).join('\n')}
|
|
@@ -479,9 +672,27 @@ function powershellCompletion() {
|
|
|
479
672
|
Register-ArgumentCompleter -Native -CommandName whoisleuth -ScriptBlock {
|
|
480
673
|
param($wordToComplete, $commandAst, $cursorPosition)
|
|
481
674
|
$noMatch = { [System.Management.Automation.CompletionResult]::new(' ', 'no matches', 'ParameterValue', 'No completion available') }
|
|
675
|
+
$completeFiles = {
|
|
676
|
+
param($prefix)
|
|
677
|
+
$files = @([System.Management.Automation.CompletionCompleters]::CompleteFilename($prefix))
|
|
678
|
+
foreach ($file in $files) {
|
|
679
|
+
# Native arguments need literal paths, not provider wildcard escapes.
|
|
680
|
+
$path = $file.ToolTip
|
|
681
|
+
if ($file.ResultType -eq 'ProviderContainer') { $path += [IO.Path]::DirectorySeparatorChar }
|
|
682
|
+
$literal = "'" + [System.Management.Automation.Language.CodeGeneration]::EscapeSingleQuotedStringContent($path) + "'"
|
|
683
|
+
[System.Management.Automation.CompletionResult]::new($literal, $file.ListItemText, $file.ResultType, $file.ToolTip)
|
|
684
|
+
}
|
|
685
|
+
if ($files.Count -eq 0) { & $noMatch }
|
|
686
|
+
}
|
|
482
687
|
$commands = @(${CLI_COMMANDS.map((command) => `'${command}'`).join(', ')})
|
|
483
688
|
$options = @{
|
|
484
689
|
${Object.entries(commandOptions).map(([command, options]) => ` '${command}' = @(${options.map((option) => `'${option}'`).join(', ')})`).join('\n')}
|
|
690
|
+
}
|
|
691
|
+
$repeatOptions = @{
|
|
692
|
+
${CLI_COMMANDS.filter((command) => repeatableOptions(command).length > 0).map((command) => ` '${command}' = @(${repeatableOptions(command).map((option) => `'${option}'`).join(', ')})`).join('\n')}
|
|
693
|
+
}
|
|
694
|
+
$exclusions = @{
|
|
695
|
+
${[...COMPLETION_EXCLUSIONS].map(([key, excluded]) => ` '${key}' = @(${excluded.map((option) => `'${option}'`).join(', ')})`).join('\n')}
|
|
485
696
|
}
|
|
486
697
|
$values = @{
|
|
487
698
|
${[
|
|
@@ -528,36 +739,43 @@ ${Object.entries(conditionalIntegerRanges).map(([key, range]) => ` '${key}' =
|
|
|
528
739
|
if ($directLookup) { $command = 'lookup' }
|
|
529
740
|
$commandKnown = $options.ContainsKey($command)
|
|
530
741
|
$activeOptions = if ($commandKnown) { $options[$command] } else { @(${COMMAND_META_ALIASES.map((alias) => `'${alias}'`).join(', ')}) }
|
|
531
|
-
$previousOwned = $activeOptions -contains $previous
|
|
532
|
-
if ($previousOwned -and $fileOptions.ContainsKey($command) -and $fileOptions[$command] -contains $previous) {
|
|
533
|
-
$files = @(Get-ChildItem -Path "${'$'}wordToComplete*" -File -ErrorAction SilentlyContinue)
|
|
534
|
-
$files | ForEach-Object {
|
|
535
|
-
[System.Management.Automation.CompletionResult]::new($_.FullName, $_.Name, 'ProviderItem', $_.FullName)
|
|
536
|
-
}
|
|
537
|
-
if ($files.Count -eq 0) { & $noMatch }
|
|
538
|
-
return
|
|
539
|
-
}
|
|
540
|
-
if ($previousOwned -and $textOptions.ContainsKey($command) -and $textOptions[$command] -contains $previous) {
|
|
541
|
-
$completionText = if ([string]::IsNullOrEmpty($wordToComplete)) { ' ' } else { [string]$wordToComplete }
|
|
542
|
-
$listItemText = if ([string]::IsNullOrEmpty($wordToComplete)) { 'value' } else { [string]$wordToComplete }
|
|
543
|
-
[System.Management.Automation.CompletionResult]::new($completionText, $listItemText, 'ParameterValue', 'Enter a value')
|
|
544
|
-
return
|
|
545
|
-
}
|
|
546
742
|
$positionCount = 0
|
|
547
743
|
$expectValue = $false
|
|
548
744
|
$foreignOption = $false
|
|
745
|
+
$ended = $false
|
|
746
|
+
$valueOption = ''
|
|
549
747
|
$seenOptions = @()
|
|
748
|
+
$blockedOptions = @()
|
|
550
749
|
for ($index = 2; $index -le $previousIndex; $index += 1) {
|
|
551
750
|
$word = $elements[$index]
|
|
552
|
-
if ($expectValue) {
|
|
751
|
+
if ($expectValue) {
|
|
752
|
+
$key = "${'$'}{command}:${'$'}valueOption=$word"
|
|
753
|
+
if ($exclusions.ContainsKey($key)) { $blockedOptions += $exclusions[$key] }
|
|
754
|
+
$expectValue = $false; continue
|
|
755
|
+
}
|
|
756
|
+
if ($ended) { $positionCount += 1; continue }
|
|
757
|
+
if ($word -eq '--') { $ended = $true; continue }
|
|
553
758
|
if ($word.StartsWith('-')) {
|
|
554
759
|
if ($activeOptions -notcontains $word) { $foreignOption = $true; continue }
|
|
555
760
|
$seenOptions += $word
|
|
556
|
-
|
|
761
|
+
$key = "${'$'}{command}:$word"
|
|
762
|
+
if ($exclusions.ContainsKey($key)) { $blockedOptions += $exclusions[$key] }
|
|
763
|
+
if ($valueOptions.ContainsKey($command) -and $valueOptions[$command] -contains $word) { $expectValue = $true; $valueOption = $word }
|
|
557
764
|
} else {
|
|
558
765
|
$positionCount += 1
|
|
559
766
|
}
|
|
560
767
|
}
|
|
768
|
+
$previousOwned = -not $ended -and $expectValue -and $activeOptions -contains $previous
|
|
769
|
+
if ($previousOwned -and $fileOptions.ContainsKey($command) -and $fileOptions[$command] -contains $previous) {
|
|
770
|
+
& $completeFiles $wordToComplete
|
|
771
|
+
return
|
|
772
|
+
}
|
|
773
|
+
if ($previousOwned -and $textOptions.ContainsKey($command) -and $textOptions[$command] -contains $previous) {
|
|
774
|
+
$completionText = if ([string]::IsNullOrEmpty($wordToComplete)) { ' ' } else { [string]$wordToComplete }
|
|
775
|
+
$listItemText = if ([string]::IsNullOrEmpty($wordToComplete)) { 'value' } else { [string]$wordToComplete }
|
|
776
|
+
[System.Management.Automation.CompletionResult]::new($completionText, $listItemText, 'ParameterValue', 'Enter a value')
|
|
777
|
+
return
|
|
778
|
+
}
|
|
561
779
|
$integerKey = "${'$'}{command}:${'$'}previous"
|
|
562
780
|
if ($previousOwned -and $integerRanges.ContainsKey($integerKey)) {
|
|
563
781
|
$range = $integerRanges[$integerKey]
|
|
@@ -581,6 +799,7 @@ ${Object.entries(conditionalIntegerRanges).map(([key, range]) => ` '${key}' =
|
|
|
581
799
|
if ($previousOwned -and $values.ContainsKey($valueKey)) {
|
|
582
800
|
$matched = $false
|
|
583
801
|
foreach ($candidate in $values[$valueKey]) {
|
|
802
|
+
if ($blockedOptions -contains "$previous=$candidate") { continue }
|
|
584
803
|
if ($candidate.StartsWith($wordToComplete, [System.StringComparison]::OrdinalIgnoreCase)) {
|
|
585
804
|
[System.Management.Automation.CompletionResult]::new($candidate, $candidate, 'ParameterValue', $candidate)
|
|
586
805
|
$matched = $true
|
|
@@ -601,14 +820,11 @@ ${Object.entries(conditionalIntegerRanges).map(([key, range]) => ` '${key}' =
|
|
|
601
820
|
if (-not $matched) { & $noMatch }
|
|
602
821
|
return
|
|
603
822
|
}
|
|
604
|
-
if ($fileLimits.ContainsKey($command) -and $fileLimits[$command] -gt $positionCount -and -not $foreignOption -and -not $expectValue -and -not $wordToComplete.StartsWith('-')) {
|
|
605
|
-
$
|
|
606
|
-
$files | ForEach-Object {
|
|
607
|
-
[System.Management.Automation.CompletionResult]::new($_.FullName, $_.Name, 'ProviderItem', $_.FullName)
|
|
608
|
-
}
|
|
609
|
-
if ($files.Count -eq 0) { & $noMatch }
|
|
823
|
+
if ($fileLimits.ContainsKey($command) -and $fileLimits[$command] -gt $positionCount -and -not $foreignOption -and -not $expectValue -and ($ended -or -not $wordToComplete.StartsWith('-'))) {
|
|
824
|
+
& $completeFiles $wordToComplete
|
|
610
825
|
return
|
|
611
826
|
}
|
|
827
|
+
if ($ended -or $foreignOption -or $expectValue) { & $noMatch; return }
|
|
612
828
|
$candidates = if ($rootCompletion -and -not $directLookup) {
|
|
613
829
|
@($commands) + @(${ROOT_META_ALIASES.map((alias) => `'${alias}'`).join(', ')})
|
|
614
830
|
} elseif ($commandKnown) {
|
|
@@ -618,6 +834,9 @@ ${Object.entries(conditionalIntegerRanges).map(([key, range]) => ` '${key}' =
|
|
|
618
834
|
}
|
|
619
835
|
$matched = $false
|
|
620
836
|
foreach ($candidate in $candidates) {
|
|
837
|
+
if ($blockedOptions -contains $candidate) { continue }
|
|
838
|
+
if ($seenOptions -contains $candidate -and (-not $repeatOptions.ContainsKey($command) -or $repeatOptions[$command] -notcontains $candidate)) { continue }
|
|
839
|
+
if ($previousIndex -ge 2 -and @(${COMMAND_META_ALIASES.map((alias) => `'${alias}'`).join(', ')}) -contains $candidate) { continue }
|
|
621
840
|
if ($candidate.StartsWith($wordToComplete, [System.StringComparison]::OrdinalIgnoreCase)) {
|
|
622
841
|
[System.Management.Automation.CompletionResult]::new($candidate, $candidate, 'ParameterValue', $candidate)
|
|
623
842
|
$matched = $true
|