mirror of
https://github.com/morluto/rea.git
synced 2026-11-07 07:16:13 +00:00
fix(electron): attribute unresolved paths to selected inputs
Resolve each caller-selected Electron path once at the application boundary, collect field-specific failures with original causes, and pass canonical paths directly to the provider. Update direct provider consumers and route the real verifier through the shared application workflow. Validation: public CLI/MCP invalid-path acceptance, canonicalization boundary, provider tests, and cancellation/cleanup checks. Live Electron launch was not verified. This does not address the separate Playwright launch-rejection crash in #2269. Fixes #2272
This commit is contained in:
1 parent
049132ab43
commit
b7169c8b74
8 files changed
+186
-92
No files matched your search
@@ -1,18 +1,11 @@
|
||||
import { digestCanonicalValue } from "../../domain/canonicalDigest.js";
|
||||
import type { ProviderIdentity } from "../AnalysisProvider.js";
|
||||
import type { CanonicalElectronActiveObservationInput } from "./ElectronActiveObservationPort.js";
|
||||
import type { Evidence, EvidenceObservation } from "../../domain/evidence.js";
|
||||
import { createEvidence } from "../../domain/evidence.js";
|
||||
import type {
|
||||
ElectronActiveObservationInput,
|
||||
ElectronActiveObservationResult,
|
||||
} from "../../domain/javascript/electronActiveObservation.js";
|
||||
import type { ElectronActiveObservationResult } from "../../domain/javascript/electronActiveObservation.js";
|
||||
import { jsonObjectSchema, jsonValueSchema } from "../../domain/jsonValue.js";
|
||||
|
||||
type CanonicalElectronActiveObservationInput =
|
||||
ElectronActiveObservationInput & {
|
||||
readonly application_root: string;
|
||||
};
|
||||
|
||||
/** Commit the selected Electron experiment and its observed result. */
|
||||
export const createElectronActiveEvidence = (
|
||||
input: CanonicalElectronActiveObservationInput,
|
||||
|
||||
@@ -9,11 +9,15 @@ import type {
|
||||
} from "../../domain/javascript/electronActiveObservation.js";
|
||||
import type { Result } from "../../domain/result.js";
|
||||
|
||||
/** Caller selections resolved once before entering the runtime adapter. */
|
||||
export type CanonicalElectronActiveObservationInput =
|
||||
ElectronActiveObservationInput & { readonly application_root: string };
|
||||
|
||||
/** Provider-neutral boundary for owned Electron runtime experiments. */
|
||||
export interface ElectronActiveObservationPort {
|
||||
identity(): ProviderIdentity;
|
||||
capture(
|
||||
input: ElectronActiveObservationInput,
|
||||
input: CanonicalElectronActiveObservationInput,
|
||||
options?: ExecutionOptions,
|
||||
): Promise<Result<ElectronActiveObservationResult, AnalysisError>>;
|
||||
}
|
||||
@@ -2,57 +2,80 @@ import { dirname } from "node:path";
|
||||
import { realpath } from "node:fs/promises";
|
||||
|
||||
import type { ExecutionOptions } from "../AnalysisProvider.js";
|
||||
import type { ElectronActiveObservationPort } from "./ElectronActiveObservationPort.js";
|
||||
import type {
|
||||
CanonicalElectronActiveObservationInput,
|
||||
ElectronActiveObservationPort,
|
||||
} from "./ElectronActiveObservationPort.js";
|
||||
import {
|
||||
AnalysisCapabilityUnavailableError,
|
||||
AnalysisInputError,
|
||||
type AnalysisInputIssue,
|
||||
} from "../../domain/analysisErrorCore.js";
|
||||
import type { AnalysisError } from "../../domain/analysisErrorBase.js";
|
||||
import type { Evidence } from "../../domain/evidence.js";
|
||||
import type { ElectronActiveObservationInput } from "../../domain/javascript/electronActiveObservation.js";
|
||||
import { err, type Result } from "../../domain/result.js";
|
||||
import { err, ok, type Result } from "../../domain/result.js";
|
||||
import { createElectronActiveEvidence } from "./ElectronActiveEvidence.js";
|
||||
|
||||
const OPERATION = "capture_electron_scenario" as const;
|
||||
const PROVIDER_ID = "rea-playwright-electron-active";
|
||||
type CanonicalElectronActiveObservationInput =
|
||||
ElectronActiveObservationInput & {
|
||||
readonly application_root: string;
|
||||
};
|
||||
|
||||
const resolveInputPath = async (
|
||||
field: "executable_path" | "application_path" | "application_root",
|
||||
selected: string,
|
||||
): Promise<
|
||||
Result<
|
||||
string,
|
||||
{ readonly issue: AnalysisInputIssue; readonly cause: unknown }
|
||||
>
|
||||
> => {
|
||||
try {
|
||||
return ok(await realpath(selected));
|
||||
} catch (cause: unknown) {
|
||||
return err({
|
||||
issue: {
|
||||
path: [field],
|
||||
reason: "invalid_value",
|
||||
message: `Cannot resolve ${field} ${selected}: ${cause instanceof Error ? cause.message : String(cause)}`,
|
||||
},
|
||||
cause,
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
const canonicalizeInput = async (
|
||||
input: ElectronActiveObservationInput,
|
||||
): Promise<Result<CanonicalElectronActiveObservationInput, AnalysisError>> => {
|
||||
try {
|
||||
const [executablePath, applicationPath] = await Promise.all([
|
||||
realpath(input.executable_path),
|
||||
realpath(input.application_path),
|
||||
]);
|
||||
const applicationRoot =
|
||||
input.application_root === undefined
|
||||
? dirname(applicationPath)
|
||||
: await realpath(input.application_root);
|
||||
return {
|
||||
ok: true,
|
||||
value: {
|
||||
...input,
|
||||
executable_path: executablePath,
|
||||
application_path: applicationPath,
|
||||
application_root: applicationRoot,
|
||||
},
|
||||
};
|
||||
} catch (cause: unknown) {
|
||||
const [executable, application, root] = await Promise.all([
|
||||
resolveInputPath("executable_path", input.executable_path),
|
||||
resolveInputPath("application_path", input.application_path),
|
||||
input.application_root === undefined
|
||||
? Promise.resolve(ok(undefined))
|
||||
: resolveInputPath("application_root", input.application_root),
|
||||
]);
|
||||
if (!executable.ok || !application.ok || !root.ok) {
|
||||
const failures = [executable, application, root].flatMap((result) =>
|
||||
result.ok ? [] : [result.error],
|
||||
);
|
||||
return err(
|
||||
new AnalysisInputError(OPERATION, { cause }, [
|
||||
new AnalysisInputError(
|
||||
OPERATION,
|
||||
{
|
||||
path: ["application_path"],
|
||||
reason: "invalid_format",
|
||||
message:
|
||||
"Electron executable, application, and root must resolve to existing paths",
|
||||
cause: new AggregateError(
|
||||
failures.map(({ cause }) => cause),
|
||||
"Electron paths could not be resolved",
|
||||
),
|
||||
},
|
||||
]),
|
||||
failures.map(({ issue }) => issue),
|
||||
),
|
||||
);
|
||||
}
|
||||
return ok({
|
||||
...input,
|
||||
executable_path: executable.value,
|
||||
application_path: application.value,
|
||||
application_root: root.value ?? dirname(application.value),
|
||||
});
|
||||
};
|
||||
|
||||
/** Canonicalize request paths and execute one provider-owned Electron run. */
|
||||
|
||||
Reference in new issue
Block a user