rea/src/domain/javascriptRuntimeReconcilia...

132 lines
3.9 KiB
TypeScript

import { createHash } from "node:crypto";
import { isAbsolute } from "node:path";
import { isPathWithinRoot } from "./localPath.js";
import canonicalize from "canonicalize";
import { z } from "zod";
import {
classifyBrowserCompleteness,
type BrowserCompleteness,
} from "./browserCompleteness.js";
import {
electronActiveObservationResultSchema,
type ElectronActiveObservationResult,
} from "./electronActiveObservation.js";
import type { Evidence } from "./evidence.js";
import type { ParsedRuntimeCapture } from "./javascriptRuntimeReconciliationParsing.js";
/** Parse active Electron Evidence as a bounded, target-only runtime capture. */
export const parseActiveElectronCapture = (
evidence: Evidence,
): ParsedRuntimeCapture => {
assertIdentity(evidence);
const result = electronActiveObservationResultSchema.parse(
evidence.normalized_result,
);
const parameters = z
.object({
application_path: absolutePathSchema,
application_root: absolutePathSchema,
})
.passthrough()
.parse(evidence.parameters);
if (
result.application.application_path !== parameters.application_path ||
!isPathWithinRoot(
parameters.application_root,
result.application.application_path,
)
)
throw new TypeError(
"Active Electron Evidence application path disagrees with its configured root",
);
return {
kind: "electron-active",
evidence,
inspection: normalizeInspection(result),
captureSha256: digestCanonical(result),
scriptsCompleteWithinScope: false,
};
};
const absolutePathSchema = z.string().min(1).max(16_384).refine(isAbsolute);
const assertIdentity = (evidence: Evidence): void => {
if (
evidence.operation !== "capture_electron_scenario" ||
evidence.predicate_type !== "rea.electron-active-scenario/v1" ||
evidence.provider.id !== "rea-playwright-electron-active" ||
evidence.provider.name !==
"REA Playwright active Electron observation provider" ||
evidence.provider.version !== "1" ||
evidence.authority !== "controlled-replay" ||
evidence.confidence !== "observed"
)
throw new TypeError(
"Evidence does not match the supported capture_electron_scenario contract",
);
};
const normalizeInspection = (
result: ElectronActiveObservationResult,
): {
readonly target: {
readonly target_id: string;
readonly type: string;
readonly title: string;
readonly attached: boolean;
readonly file_path: string;
};
readonly frames: readonly [];
readonly scripts: { readonly items: readonly [] };
readonly workers: readonly [];
readonly completeness: BrowserCompleteness;
} => ({
target: {
target_id: `electron-active:${digestCanonical(result.application.application_path).slice(0, 32)}`,
type: "electron-application",
title: result.application.application_path,
attached: true,
file_path: result.application.application_path,
},
frames: [],
scripts: { items: [] },
workers: [],
completeness: classifyBrowserCompleteness({
policyFilteredSections: new Set(),
attachLimitedSections: new Set(),
truncatedSections: new Set(
result.windows_truncated ||
result.processes.truncated ||
result.ipc.truncated ||
result.timeline.truncated
? ["timeline"]
: [],
),
unavailableSections: new Set([
"frames",
"scripts",
"script_sources",
"workers",
]),
excluded: [],
droppedEvents: {
scripts: 0,
network_requests: 0,
console_events: 0,
websocket_connections: 0,
websocket_frames: 0,
webmcp_tools: 0,
timeline_events: result.timeline.truncated ? result.timeline.observed : 0,
},
}),
});
const digestCanonical = (value: unknown): string => {
const encoded = canonicalize(value);
if (encoded === undefined)
throw new TypeError("Runtime reconciliation could not canonicalize input");
return createHash("sha256").update(encoded).digest("hex");
};