mirror of
https://github.com/morluto/rea
synced 2026-10-26 14:25:49 -04:00
f2150eb9a7
* feat(harmony): project HarmonyOS package inventory evidence First HarmonyOS increment from #1431: HAP, HSP, and App Pack roots are classified through the hardened ZIP reader and projected into an execution-free application graph with packaging-model hints, exact component paths and hashes, App Pack children by path, runtime-family hints, and path-based N-API bridge hypotheses. HarmonyOS .har stays unclassified by suffix because .har also names HTTP Archive JSON. Adds the project_harmony_application_graph tool (CLI and MCP), synthetic composition coverage, a pinned real-package verify lane, and docs. * refactor(domain): share application graph projection scaffolding The HarmonyOS projection mirrors the Android and Apple projections by design, which pushed jscpd duplication over its 2.5 percent budget. Extract the identical pieces (inventory input schema, projected component schema and occurrence flattening, bridge candidate schema factory, coverage assembly, comparator, canonical digest) into domain/mobileApplicationGraphProjection.ts and route all three projections plus the composition fixtures through it. Duplication drops from 2.52 to 2.49 percent with no behavior change: projection ids, classifications, and limitations are byte-identical. * fix(harmony): address review, stable app-pack roots, and rotated fixture - rebased onto main; the shared projection scaffolding now keeps main's nativeRuntimeConvention loop and the harmony runtime families adopt the same react-native/flutter/unity path conventions - the .app App Pack suffix is now a root-input rule only (verified ZIP magic in classifyRootFormat and BinaryTargetResolver); nested .app ZIP members inside other packages keep the generic zip classification, with a regression test, so existing inventories do not change - the signing limitation now states that an empty signing list does not mean unsigned because packaging-tool signatures live in the ZIP signing block, and the app-pack root rule is disclosed as suffix-based - repinned the real-package fixture to the current VHome 2.6.14-beta asset after the upstream deleted v5.0.111-beta and replaced the v2.6.14-beta asset within hours; the pin records both observed digests --------- Co-authored-by: morluto <76467478+morluto@users.noreply.github.com>
238 lines
9.0 KiB
TypeScript
238 lines
9.0 KiB
TypeScript
import { reconstructionCoverageEvaluationInputSchema } from "../domain/reconstructionCoverageInput.js";
|
|
import { Cli, z } from "incur";
|
|
|
|
import { CLI_COMMANDS } from "../cliCommandNames.js";
|
|
import { parseCliJsonInput } from "../cliJsonInput.js";
|
|
import { logCliCommand } from "../cliLogging.js";
|
|
import { projectAnalysisError } from "../domain/analysisErrorProjection.js";
|
|
import type { AnalysisError } from "../domain/analysisErrorBase.js";
|
|
import { androidApplicationProjectionInputSchema } from "../domain/android/androidApplication.js";
|
|
import { appleApplicationProjectionInputSchema } from "../domain/apple/appleApplication.js";
|
|
import { harmonyApplicationProjectionInputSchema } from "../domain/harmony/harmonyApplication.js";
|
|
import { jsonValueSchema, type JsonValue } from "../domain/jsonValue.js";
|
|
import type { Logger } from "pino";
|
|
import { traceApplicationFeatureInputSchema } from "../domain/javascript/javascriptFeatureTraceSchemas.js";
|
|
import { traceJavaScriptSemanticsInputSchema } from "../domain/javascript/javascriptSemanticTraceSchemas.js";
|
|
import { compareApplicationVersionsInputSchema } from "../domain/javascript/javascriptApplicationVersionComparisonSchemas.js";
|
|
import { compareSourceToBundleInputSchema } from "../domain/javascript/sourceToBundleComparisonSchemas.js";
|
|
import { compareJavaScriptExportShapesInputSchema } from "../domain/javascript/javascriptExportShapeComparisonSchemas.js";
|
|
import { analysisInputErrorFromIssues } from "../domain/inputIssueProjection.js";
|
|
import { reconstructionObligationLedgerInputSchema } from "../domain/reconstructionObligationLedgerSchemas.js";
|
|
|
|
type CliInstance = ReturnType<typeof Cli.create>;
|
|
|
|
/** Register CLI equivalents of provider-neutral application graph workflows. */
|
|
export const registerApplicationCommands = (
|
|
cli: ReturnType<typeof Cli.create>,
|
|
logger: Logger,
|
|
): void => {
|
|
registerJsonCommand({
|
|
cli,
|
|
logger,
|
|
name: CLI_COMMANDS.traceApplicationFeature,
|
|
description:
|
|
"Trace a typed seed through authenticated application Evidence JSON",
|
|
inputSchema: traceApplicationFeatureInputSchema,
|
|
workflow: async (input) => {
|
|
const { traceApplicationFeatureEvidenceValidated } =
|
|
await import("../application/javascript/JavaScriptApplicationWorkflowService.js");
|
|
return traceApplicationFeatureEvidenceValidated(input);
|
|
},
|
|
});
|
|
registerJsonCommand({
|
|
cli,
|
|
logger,
|
|
name: CLI_COMMANDS.traceJavaScriptSemantics,
|
|
description:
|
|
"Trace bounded static semantic relations through authenticated application Evidence",
|
|
inputSchema: traceJavaScriptSemanticsInputSchema,
|
|
workflow: async (input) => {
|
|
const { traceJavaScriptSemanticsEvidenceValidated } =
|
|
await import("../application/javascript/JavaScriptSemanticTraceService.js");
|
|
return traceJavaScriptSemanticsEvidenceValidated(input);
|
|
},
|
|
});
|
|
registerJsonCommand({
|
|
cli,
|
|
logger,
|
|
name: CLI_COMMANDS.compareApplicationVersions,
|
|
description:
|
|
"Compare two authenticated JavaScript Application Graph versions",
|
|
inputSchema: compareApplicationVersionsInputSchema,
|
|
workflow: async (input) => {
|
|
const { compareApplicationVersionsEvidenceValidated } =
|
|
await import("../application/javascript/JavaScriptApplicationWorkflowService.js");
|
|
return compareApplicationVersionsEvidenceValidated(input);
|
|
},
|
|
});
|
|
registerJsonCommand({
|
|
cli,
|
|
logger,
|
|
name: CLI_COMMANDS.compareSourceToBundle,
|
|
description:
|
|
"Compare committed historical source with authenticated application Evidence",
|
|
inputSchema: compareSourceToBundleInputSchema,
|
|
workflow: async (input) => {
|
|
const { compareSourceToBundleEvidenceValidated } =
|
|
await import("../application/javascript/JavaScriptApplicationWorkflowService.js");
|
|
return compareSourceToBundleEvidenceValidated(input);
|
|
},
|
|
});
|
|
registerJsonCommand({
|
|
cli,
|
|
logger,
|
|
name: CLI_COMMANDS.compareJavaScriptExportShapes,
|
|
description:
|
|
"Compare exact static JavaScript export return shapes without execution",
|
|
inputSchema: compareJavaScriptExportShapesInputSchema,
|
|
workflow: async (input) => {
|
|
const { compareJavaScriptExportShapesEvidenceValidated } =
|
|
await import("../application/javascript/JavaScriptApplicationWorkflowService.js");
|
|
return compareJavaScriptExportShapesEvidenceValidated(input);
|
|
},
|
|
});
|
|
registerObligationLedgerCommand(cli, logger);
|
|
registerCoverageCommand(cli, logger);
|
|
registerJsonCommand({
|
|
cli,
|
|
logger,
|
|
name: CLI_COMMANDS.projectAndroidApplicationGraph,
|
|
description:
|
|
"Project authenticated APK inventory Evidence into an Android application graph",
|
|
inputSchema: androidApplicationProjectionInputSchema,
|
|
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) }
|
|
: result;
|
|
},
|
|
});
|
|
registerJsonCommand({
|
|
cli,
|
|
logger,
|
|
name: CLI_COMMANDS.projectAppleApplicationGraph,
|
|
description:
|
|
"Project authenticated IPA or macOS app inventory Evidence into an Apple application graph",
|
|
inputSchema: appleApplicationProjectionInputSchema,
|
|
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) }
|
|
: result;
|
|
},
|
|
});
|
|
registerJsonCommand({
|
|
cli,
|
|
logger,
|
|
name: CLI_COMMANDS.projectHarmonyApplicationGraph,
|
|
description:
|
|
"Project authenticated HarmonyOS HAP, HSP, or App Pack inventory Evidence into an application graph",
|
|
inputSchema: harmonyApplicationProjectionInputSchema,
|
|
workflow: async (input) => {
|
|
const { projectHarmonyApplicationEvidence } =
|
|
await import("../application/harmony/HarmonyApplicationService.js");
|
|
const result = projectHarmonyApplicationEvidence(input);
|
|
return result.ok
|
|
? { ok: true, value: jsonValueSchema.parse(result.value) }
|
|
: result;
|
|
},
|
|
});
|
|
};
|
|
|
|
const registerObligationLedgerCommand = (
|
|
cli: CliInstance,
|
|
logger: Logger,
|
|
): void =>
|
|
registerJsonCommand({
|
|
cli,
|
|
logger,
|
|
name: CLI_COMMANDS.buildReconstructionObligationLedger,
|
|
description:
|
|
"Generate a deterministic Evidence-backed reconstruction obligation ledger page",
|
|
inputSchema: reconstructionObligationLedgerInputSchema,
|
|
workflow: async (input) => {
|
|
const { buildReconstructionObligationLedgerEvidenceValidated } =
|
|
await import("../application/ReconstructionObligationLedgerService.js");
|
|
const result =
|
|
buildReconstructionObligationLedgerEvidenceValidated(input);
|
|
return result.ok
|
|
? { ok: true, value: jsonValueSchema.parse(result.value) }
|
|
: result;
|
|
},
|
|
});
|
|
|
|
const registerCoverageCommand = (cli: CliInstance, logger: Logger): void =>
|
|
registerJsonCommand({
|
|
cli,
|
|
logger,
|
|
name: CLI_COMMANDS.evaluateReconstructionCoverage,
|
|
description: "Evaluate inline fail-closed reconstruction coverage",
|
|
inputSchema: reconstructionCoverageEvaluationInputSchema,
|
|
workflow: async (input) => {
|
|
const { evaluateReconstructionCoverage } =
|
|
await import("../application/ReconstructionCoverageService.js");
|
|
const result = evaluateReconstructionCoverage(input);
|
|
return result.ok
|
|
? { ok: true, value: jsonValueSchema.parse(result.value) }
|
|
: result;
|
|
},
|
|
});
|
|
|
|
interface JsonCommandOptions<Schema extends z.ZodType> {
|
|
readonly cli: CliInstance;
|
|
readonly logger: Logger;
|
|
readonly name: string;
|
|
readonly description: string;
|
|
readonly inputSchema: Schema;
|
|
readonly workflow: (
|
|
input: z.output<Schema>,
|
|
) => Promise<
|
|
| { readonly ok: true; readonly value: JsonValue }
|
|
| { readonly ok: false; readonly error: AnalysisError }
|
|
>;
|
|
}
|
|
|
|
const registerJsonCommand = <Schema extends z.ZodType>({
|
|
cli,
|
|
logger,
|
|
name,
|
|
description,
|
|
inputSchema,
|
|
workflow,
|
|
}: JsonCommandOptions<Schema>): void => {
|
|
cli.command(name, {
|
|
description,
|
|
args: z.object({
|
|
inputJson: z.string().describe("Inline workflow JSON or JSON file path"),
|
|
}),
|
|
run: ({ args }) =>
|
|
logCliCommand(logger, name, async () => {
|
|
const input = await parseCliJsonInput(args.inputJson, name);
|
|
if (!input.ok) return input.error;
|
|
const parsed = inputSchema.safeParse(input.value);
|
|
if (!parsed.success)
|
|
return {
|
|
error: "Application workflow failed",
|
|
...projectAnalysisError(
|
|
analysisInputErrorFromIssues(
|
|
name,
|
|
parsed.error.issues,
|
|
input.value,
|
|
),
|
|
),
|
|
};
|
|
const result = await workflow(parsed.data);
|
|
return result.ok
|
|
? result.value
|
|
: {
|
|
error: "Application workflow failed",
|
|
...projectAnalysisError(result.error),
|
|
};
|
|
}),
|
|
});
|
|
};
|