diff --git a/src/cli/applicationCommands.ts b/src/cli/applicationCommands.ts index 929e9064d..5125814b1 100644 --- a/src/cli/applicationCommands.ts +++ b/src/cli/applicationCommands.ts @@ -1,17 +1,6 @@ import { reconstructionCoverageEvaluationInputSchema } from "../domain/reconstructionCoverageInput.js"; import { Cli, z } from "incur"; -import { projectAndroidApplicationEvidence } from "../application/android/AndroidApplicationService.js"; -import { projectAppleApplicationEvidence } from "../application/apple/AppleApplicationService.js"; -import { - compareApplicationVersionsEvidenceValidated, - compareJavaScriptExportShapesEvidenceValidated, - compareSourceToBundleEvidenceValidated, - traceApplicationFeatureEvidenceValidated, -} from "../application/javascript/JavaScriptApplicationWorkflowService.js"; -import { traceJavaScriptSemanticsEvidenceValidated } from "../application/javascript/JavaScriptSemanticTraceService.js"; -import { evaluateReconstructionCoverage } from "../application/ReconstructionCoverageService.js"; -import { buildReconstructionObligationLedgerEvidenceValidated } from "../application/ReconstructionObligationLedgerService.js"; import { CLI_COMMANDS } from "../cliCommandNames.js"; import { parseCliJsonInput } from "../cliJsonInput.js"; import { logCliCommand } from "../cliLogging.js"; @@ -43,7 +32,11 @@ export const registerApplicationCommands = ( description: "Trace a typed seed through authenticated application Evidence JSON", inputSchema: traceApplicationFeatureInputSchema, - workflow: traceApplicationFeatureEvidenceValidated, + workflow: async (input) => { + const { traceApplicationFeatureEvidenceValidated } = + await import("../application/javascript/JavaScriptApplicationWorkflowService.js"); + return traceApplicationFeatureEvidenceValidated(input); + }, }); registerJsonCommand({ cli, @@ -52,7 +45,11 @@ export const registerApplicationCommands = ( description: "Trace bounded static semantic relations through authenticated application Evidence", inputSchema: traceJavaScriptSemanticsInputSchema, - workflow: traceJavaScriptSemanticsEvidenceValidated, + workflow: async (input) => { + const { traceJavaScriptSemanticsEvidenceValidated } = + await import("../application/javascript/JavaScriptSemanticTraceService.js"); + return traceJavaScriptSemanticsEvidenceValidated(input); + }, }); registerJsonCommand({ cli, @@ -61,7 +58,11 @@ export const registerApplicationCommands = ( description: "Compare two authenticated JavaScript Application Graph versions", inputSchema: compareApplicationVersionsInputSchema, - workflow: compareApplicationVersionsEvidenceValidated, + workflow: async (input) => { + const { compareApplicationVersionsEvidenceValidated } = + await import("../application/javascript/JavaScriptApplicationWorkflowService.js"); + return compareApplicationVersionsEvidenceValidated(input); + }, }); registerJsonCommand({ cli, @@ -70,7 +71,11 @@ export const registerApplicationCommands = ( description: "Compare committed historical source with authenticated application Evidence", inputSchema: compareSourceToBundleInputSchema, - workflow: compareSourceToBundleEvidenceValidated, + workflow: async (input) => { + const { compareSourceToBundleEvidenceValidated } = + await import("../application/javascript/JavaScriptApplicationWorkflowService.js"); + return compareSourceToBundleEvidenceValidated(input); + }, }); registerJsonCommand({ cli, @@ -79,7 +84,11 @@ export const registerApplicationCommands = ( description: "Compare exact static JavaScript export return shapes without execution", inputSchema: compareJavaScriptExportShapesInputSchema, - workflow: compareJavaScriptExportShapesEvidenceValidated, + workflow: async (input) => { + const { compareJavaScriptExportShapesEvidenceValidated } = + await import("../application/javascript/JavaScriptApplicationWorkflowService.js"); + return compareJavaScriptExportShapesEvidenceValidated(input); + }, }); registerObligationLedgerCommand(cli, logger); registerCoverageCommand(cli, logger); @@ -90,7 +99,9 @@ export const registerApplicationCommands = ( description: "Project authenticated APK inventory Evidence into an Android application graph", inputSchema: androidApplicationProjectionInputSchema, - workflow: (input) => { + workflow: async (input) => { + const { projectAndroidApplicationEvidence } = + await import("../application/android/AndroidApplicationService.js"); const result = projectAndroidApplicationEvidence(input); return result.ok ? { ok: true, value: jsonValueSchema.parse(result.value) } @@ -104,7 +115,9 @@ export const registerApplicationCommands = ( description: "Project authenticated IPA or macOS app inventory Evidence into an Apple application graph", inputSchema: appleApplicationProjectionInputSchema, - workflow: (input) => { + workflow: async (input) => { + const { projectAppleApplicationEvidence } = + await import("../application/apple/AppleApplicationService.js"); const result = projectAppleApplicationEvidence(input); return result.ok ? { ok: true, value: jsonValueSchema.parse(result.value) } @@ -124,7 +137,9 @@ const registerObligationLedgerCommand = ( description: "Generate a deterministic Evidence-backed reconstruction obligation ledger page", inputSchema: reconstructionObligationLedgerInputSchema, - workflow: (input) => { + workflow: async (input) => { + const { buildReconstructionObligationLedgerEvidenceValidated } = + await import("../application/ReconstructionObligationLedgerService.js"); const result = buildReconstructionObligationLedgerEvidenceValidated(input); return result.ok @@ -140,7 +155,9 @@ const registerCoverageCommand = (cli: CliInstance, logger: Logger): void => name: CLI_COMMANDS.evaluateReconstructionCoverage, description: "Evaluate inline fail-closed reconstruction coverage", inputSchema: reconstructionCoverageEvaluationInputSchema, - workflow: (input) => { + workflow: async (input) => { + const { evaluateReconstructionCoverage } = + await import("../application/ReconstructionCoverageService.js"); const result = evaluateReconstructionCoverage(input); return result.ok ? { ok: true, value: jsonValueSchema.parse(result.value) } @@ -156,9 +173,10 @@ interface JsonCommandOptions { readonly inputSchema: Schema; readonly workflow: ( input: z.output, - ) => + ) => Promise< | { readonly ok: true; readonly value: JsonValue } - | { readonly ok: false; readonly error: AnalysisError }; + | { readonly ok: false; readonly error: AnalysisError } + >; } const registerJsonCommand = ({ @@ -190,7 +208,7 @@ const registerJsonCommand = ({ ), ), }; - const result = workflow(parsed.data); + const result = await workflow(parsed.data); return result.ok ? result.value : { diff --git a/src/cli/electronCommands.ts b/src/cli/electronCommands.ts index e570cbfc5..9f55c02c3 100644 --- a/src/cli/electronCommands.ts +++ b/src/cli/electronCommands.ts @@ -1,11 +1,5 @@ import { Cli, z } from "incur"; -import { - inspectElectronPage, - listElectronTargets, -} from "../application/javascript/ElectronObservationService.js"; -import { captureElectronScenario } from "../application/javascript/ElectronActiveObservationService.js"; -import { reconcileJavaScriptRuntimeEvidence } from "../application/javascript/JavaScriptRuntimeReconciliationService.js"; import { logCliCommand } from "../cliLogging.js"; import { inspectElectronPageInputSchema, @@ -76,6 +70,8 @@ const registerElectronActiveCommand = ( parsed.error.issues, input.value, ); + const { captureElectronScenario } = + await import("../application/javascript/ElectronActiveObservationService.js"); const { createElectronScenarioProvider } = await import("../composition/electronScenario.js"); const result = await captureElectronScenario( @@ -114,6 +110,8 @@ const registerJavaScriptRuntimeReconciliationCommand = ( "reconcile_javascript_runtime", ); if (!input.ok) return input.error; + const { reconcileJavaScriptRuntimeEvidence } = + await import("../application/javascript/JavaScriptRuntimeReconciliationService.js"); const result = reconcileJavaScriptRuntimeEvidence(input.value); return result.ok ? result.value : cliError(result.error); }, @@ -146,6 +144,8 @@ const registerElectronTargetList = ( cdp_endpoint: args.endpoint, }); if (!parsed.success) return inputError("list_electron_targets"); + const { listElectronTargets } = + await import("../application/javascript/ElectronObservationService.js"); const result = await listElectronTargets(context.provider, parsed.data); return result.ok ? result.value : cliError(result.error); }), @@ -173,6 +173,8 @@ const registerElectronPageInspection = ( include_script_sources: options.includeScriptSources, }); if (!parsed.success) return inputError("inspect_electron_page"); + const { inspectElectronPage } = + await import("../application/javascript/ElectronObservationService.js"); const result = await inspectElectronPage(context.provider, parsed.data); return result.ok ? result.value : cliError(result.error); }), diff --git a/src/cli/javascriptApplicationAnalysis.ts b/src/cli/javascriptApplicationAnalysis.ts index 30796dfef..e2f171bec 100644 --- a/src/cli/javascriptApplicationAnalysis.ts +++ b/src/cli/javascriptApplicationAnalysis.ts @@ -1,4 +1,3 @@ -import { analyzeJavaScriptApplication } from "../application/javascript/JavaScriptApplicationService.js"; import { JAVASCRIPT_APPLICATION_PROVIDER } from "../application/InvestigationProviders.js"; import { createProgressReporter } from "../application/ProgressReporter.js"; import { ArtifactReaderFailure } from "../artifacts/ArtifactReader.js"; @@ -13,6 +12,8 @@ export const runCliJavaScriptApplicationAnalysis = async ( input: unknown, signal?: AbortSignal, ): Promise => { + const { analyzeJavaScriptApplication } = + await import("../application/javascript/JavaScriptApplicationService.js"); const progress = createProgressReporter( async (update) => { process.stderr.write(`${JSON.stringify({ rea_progress: update })}\n`); diff --git a/tests/boundary/cli/cliStartupImports.test.ts b/tests/boundary/cli/cliStartupImports.test.ts index 3c71bc540..a792c82b4 100644 --- a/tests/boundary/cli/cliStartupImports.test.ts +++ b/tests/boundary/cli/cliStartupImports.test.ts @@ -74,6 +74,46 @@ const DEFERRED_IMPORTS = [ name: "binary-composition", path: "/dist/composition/binary.js", }, + { + name: "javascript-application", + path: "/dist/application/javascript/JavaScriptApplicationService.js", + }, + { + name: "javascript-workflows", + path: "/dist/application/javascript/JavaScriptApplicationWorkflowService.js", + }, + { + name: "javascript-semantic-trace", + path: "/dist/application/javascript/JavaScriptSemanticTraceService.js", + }, + { + name: "electron-observation", + path: "/dist/application/javascript/ElectronObservationService.js", + }, + { + name: "electron-active-observation", + path: "/dist/application/javascript/ElectronActiveObservationService.js", + }, + { + name: "javascript-runtime-reconciliation", + path: "/dist/application/javascript/JavaScriptRuntimeReconciliationService.js", + }, + { + name: "android-application", + path: "/dist/application/android/AndroidApplicationService.js", + }, + { + name: "apple-application", + path: "/dist/application/apple/AppleApplicationService.js", + }, + { + name: "reconstruction-coverage", + path: "/dist/application/ReconstructionCoverageService.js", + }, + { + name: "reconstruction-obligation-ledger", + path: "/dist/application/ReconstructionObligationLedgerService.js", + }, ] as const; // Report each resolved deferred package on stderr without changing resolution. const RESOLUTION_HOOK = `data:text/javascript,${encodeURIComponent(` @@ -174,7 +214,7 @@ describe("CLI startup imports", () => { source, "--json", ]), - ).resolves.toEqual(new Set()); + ).resolves.toEqual(new Set(["javascript-application"])); }); it("loads native provider composition when a provider operation is called", async () => {