mirror of
https://github.com/morluto/rea.git
synced 2026-11-05 14:26:07 +00:00
Adapt the prepared descriptor lifecycle repair from xu91102/rea dbb7db16. Bind exact file cleanup leases, retain typed physical failures and cancellation reasons, and join CLI/MCP finalization and active preflight. Share canonical Evidence construction and retain completed reports across reader handoff cancellation. Scope active capture admission through shutdown and gate launch after close. Preserve successful descriptor peers and retry only unresolved owners. Separate process/transport retry ownership remains tracked in #4805. Validation: 168 focused checks passed, one Windows-only skip; built CLI and actual stdio MCP controlled descriptor recovery; build, static and docs checks. No natural OS fault or real Electron launch/teardown claim. Ordinary full replies and Evidence IDs remain byte identical across 30 small CLI tasks. Fixes #4658 Co-authored-by: hhang <169027046+xu91102@users.noreply.github.com>
133 lines
4.6 KiB
TypeScript
133 lines
4.6 KiB
TypeScript
import type { ExecutionOptions } from "../application/AnalysisProvider.js";
|
|
import type { ElectronFusePort } from "../application/electron/ElectronFusePort.js";
|
|
import { createElectronFuseEvidence } from "../domain/electron/electronFuseEvidence.js";
|
|
import { ArtifactResourceScope } from "../artifacts/ArtifactResourceScope.js";
|
|
import {
|
|
assertArtifactReadActive,
|
|
ArtifactFileCancelled,
|
|
ArtifactFileCleanupFailure,
|
|
} from "../artifacts/ArtifactFileOperation.js";
|
|
import type { AnalysisError } from "../domain/analysisErrorBase.js";
|
|
import { AnalysisOutputError } from "../domain/analysisErrorCore.js";
|
|
import {
|
|
describeElectronFuseFindings,
|
|
electronFuseReportSchema,
|
|
type ElectronFuseReport,
|
|
type InspectElectronFusesInput,
|
|
} from "../domain/electron/electronFuses.js";
|
|
import { err, ok, type Result } from "../domain/result.js";
|
|
import { resolveElectronFuseTarget } from "./ElectronFusePath.js";
|
|
import {
|
|
electronFuseReadLimitation,
|
|
readElectronFuseImage,
|
|
type ElectronFuseImage,
|
|
} from "./ElectronFuseReader.js";
|
|
import {
|
|
ELECTRON_FUSE_PROVIDER_IDENTITY,
|
|
electronFuseFailure,
|
|
} from "./ElectronFuseFailures.js";
|
|
|
|
export { ELECTRON_FUSE_PROVIDER_IDENTITY };
|
|
|
|
const OPERATION = "inspect_electron_fuses";
|
|
|
|
/** Read packaged Electron fuse wires from one selected local file or app bundle. */
|
|
export class ElectronFuseProvider implements ElectronFusePort {
|
|
readonly identity = ELECTRON_FUSE_PROVIDER_IDENTITY;
|
|
readonly #resources = new ArtifactResourceScope();
|
|
|
|
/** Retry the same failed descriptor owners, awaiting active reads first. */
|
|
async close(): Promise<void> {
|
|
try {
|
|
await this.#resources.close();
|
|
} catch (cause: unknown) {
|
|
throw electronFuseFailure(cause, "artifact-resources");
|
|
}
|
|
}
|
|
|
|
async inspect(
|
|
input: InspectElectronFusesInput,
|
|
options?: ExecutionOptions,
|
|
): Promise<Result<ElectronFuseReport, AnalysisError>> {
|
|
try {
|
|
return await this.#resources.run(() => this.#inspectFile(input, options));
|
|
} catch (cause: unknown) {
|
|
return err(electronFuseFailure(cause, input.path));
|
|
}
|
|
}
|
|
|
|
async #inspectFile(
|
|
input: InspectElectronFusesInput,
|
|
options?: ExecutionOptions,
|
|
): Promise<Result<ElectronFuseReport, AnalysisError>> {
|
|
let resolvedPath = input.path;
|
|
let completed: ElectronFuseReport | undefined;
|
|
try {
|
|
assertArtifactReadActive(options?.signal);
|
|
const located = await resolveElectronFuseTarget(
|
|
input.path,
|
|
options?.signal,
|
|
);
|
|
assertArtifactReadActive(options?.signal);
|
|
resolvedPath = located.file.path;
|
|
const image = await readElectronFuseImage(located.file.path, {
|
|
resources: this.#resources,
|
|
selection: located.file,
|
|
...(options?.signal === undefined ? {} : { signal: options.signal }),
|
|
});
|
|
completed = reportFor(image, located.selectedPath, resolvedPath);
|
|
assertArtifactReadActive(options?.signal);
|
|
return ok(completed);
|
|
} catch (cause: unknown) {
|
|
const failure = electronFuseFailure(cause, input.path);
|
|
// Only this reader's completed physical outcome supplies partial fuse facts.
|
|
const outcome =
|
|
cause instanceof ArtifactFileCleanupFailure
|
|
? (cause as ArtifactFileCleanupFailure<ElectronFuseImage>)
|
|
.fileCleanupFailure.outcome
|
|
: cause instanceof ArtifactFileCancelled
|
|
? (cause as ArtifactFileCancelled<ElectronFuseImage>)
|
|
.fileCancellation.outcome
|
|
: undefined;
|
|
const report =
|
|
completed ??
|
|
(outcome?.kind === "completed"
|
|
? reportFor(outcome.value, input.path, resolvedPath)
|
|
: undefined);
|
|
if (report !== undefined) {
|
|
failure.retainPartialObservation(
|
|
createElectronFuseEvidence(input, report, this.identity),
|
|
);
|
|
}
|
|
return err(failure);
|
|
}
|
|
}
|
|
}
|
|
|
|
const reportFor = (
|
|
image: ElectronFuseImage,
|
|
selectedPath: string,
|
|
resolvedPath: string,
|
|
): ElectronFuseReport => {
|
|
const findings = describeElectronFuseFindings(image.slices, image.format);
|
|
const parsed = electronFuseReportSchema.safeParse({
|
|
artifact: {
|
|
selected_path: selectedPath,
|
|
resolved_path: resolvedPath,
|
|
sha256: image.sha256,
|
|
bytes: image.bytes,
|
|
format: image.format,
|
|
},
|
|
slices: image.slices,
|
|
runtime_implications: findings.runtime_implications,
|
|
limitations: [...findings.limitations, electronFuseReadLimitation()],
|
|
});
|
|
if (!parsed.success)
|
|
throw new AnalysisOutputError(
|
|
OPERATION,
|
|
"Electron fuse reader produced a report outside its schema.",
|
|
{ cause: parsed.error },
|
|
);
|
|
return parsed.data;
|
|
};
|