mirror of
https://github.com/morluto/rea
synced 2026-10-26 14:25:49 -04:00
93c620c3e7
Own CDP transports from admission through observed close, retain failed owners for retry, and keep active peers separate from pending cleanup. Preserve primary errors and completed facts through final cancellation. Forward passive Electron CLI cancellation and dispose browser, runtime and scenario owners from CLI and MCP shutdown. Refs #4255, #4256, #4257
138 lines
4.6 KiB
TypeScript
138 lines
4.6 KiB
TypeScript
import { z } from "zod";
|
|
import type { ExecutionOptions } from "../../application/AnalysisProvider.js";
|
|
import type { WebRuntimePort } from "../../application/WebRuntimePort.js";
|
|
import {
|
|
AnalysisError,
|
|
type AnalysisPartialObservation,
|
|
} from "../../domain/analysisErrorBase.js";
|
|
import {
|
|
AnalysisCancelledError,
|
|
AnalysisOutputError,
|
|
} from "../../domain/analysisErrorCore.js";
|
|
import type { BrowserObservationOperation } from "../../domain/browserObservationErrors.js";
|
|
import { ProviderAdapterError } from "../../domain/providerAdapterError.js";
|
|
import { analysisErrorWithCleanupFailure } from "../../domain/analysisErrorCleanup.js";
|
|
import { err, ok, type Result } from "../../domain/result.js";
|
|
import {
|
|
webExecutionSchema,
|
|
type ObserveWebExecutionInput,
|
|
type WebExecution,
|
|
} from "../../domain/webExecution.js";
|
|
import {
|
|
webEventListenersSchema,
|
|
type InspectWebEventListenersInput,
|
|
type WebEventListeners,
|
|
} from "../../domain/webEventListeners.js";
|
|
import { CDP_BROWSER_PROVIDER_IDENTITY } from "../providerIdentities.js";
|
|
import { CdpTargetSessionOwner } from "../CdpTargetSessionOwner.js";
|
|
import { CdpRuntimeSession } from "./CdpRuntimeSession.js";
|
|
import { CdpRuntimeSources } from "./CdpRuntimeSources.js";
|
|
import { captureWebExecution } from "./CdpExecutionCapture.js";
|
|
import {
|
|
captureWebEventListeners,
|
|
eventListenerObjectGroup,
|
|
} from "./CdpEventListeners.js";
|
|
|
|
/** Native CDP integration with distinct inspection/instrumentation authority and confirmed cleanup. */
|
|
export class CdpWebRuntimeProvider implements WebRuntimePort {
|
|
readonly #owner = new CdpTargetSessionOwner();
|
|
|
|
close(): Promise<void> {
|
|
return this.#owner.close();
|
|
}
|
|
|
|
/** Identify this adapter independently of the captured browser version. */
|
|
identity() {
|
|
return CDP_BROWSER_PROVIDER_IDENTITY;
|
|
}
|
|
|
|
/** Observe one instrumented window while preserving the externally owned page. */
|
|
observeExecution(
|
|
input: ObserveWebExecutionInput,
|
|
options: ExecutionOptions = {},
|
|
): Promise<Result<WebExecution, AnalysisError>> {
|
|
return this.run(
|
|
input,
|
|
"observe_web_execution",
|
|
options,
|
|
webExecutionSchema,
|
|
(sources) => captureWebExecution(input, sources),
|
|
);
|
|
}
|
|
/** Inspect one selected node's registered callback locations. */
|
|
inspectEventListeners(
|
|
input: InspectWebEventListenersInput,
|
|
options: ExecutionOptions = {},
|
|
): Promise<Result<WebEventListeners, AnalysisError>> {
|
|
const group = eventListenerObjectGroup();
|
|
return this.run(
|
|
input,
|
|
"inspect_web_event_listeners",
|
|
options,
|
|
webEventListenersSchema,
|
|
(sources) => captureWebEventListeners(input, sources, group),
|
|
group,
|
|
);
|
|
}
|
|
|
|
private async run<T extends AnalysisPartialObservation>(
|
|
input: ObserveWebExecutionInput | InspectWebEventListenersInput,
|
|
operation: BrowserObservationOperation,
|
|
options: ExecutionOptions,
|
|
schema: z.ZodType<T>,
|
|
capture: (sources: CdpRuntimeSources) => Promise<T>,
|
|
group?: string,
|
|
): Promise<Result<T, AnalysisError>> {
|
|
let session: CdpRuntimeSession | undefined;
|
|
let result: Result<T, AnalysisError>;
|
|
try {
|
|
session = await CdpRuntimeSession.open(
|
|
input,
|
|
operation,
|
|
options,
|
|
this.#owner,
|
|
);
|
|
result = ok(schema.parse(await capture(new CdpRuntimeSources(session))));
|
|
} catch (cause: unknown) {
|
|
result = err(
|
|
cause instanceof AnalysisError
|
|
? cause
|
|
: cause instanceof z.ZodError
|
|
? new AnalysisOutputError(operation, cause.message)
|
|
: new ProviderAdapterError(this.identity().id, operation, {
|
|
cause,
|
|
}),
|
|
);
|
|
}
|
|
if (session !== undefined) {
|
|
const failure = await session.close(
|
|
result.ok ? undefined : result.error,
|
|
group,
|
|
);
|
|
if (failure !== undefined)
|
|
return err(
|
|
result.ok
|
|
? options.signal?.aborted === true
|
|
? analysisErrorWithCleanupFailure(
|
|
new AnalysisCancelledError(operation, {
|
|
cause: options.signal.reason,
|
|
partialObservation: result.value,
|
|
}),
|
|
failure,
|
|
operation,
|
|
)
|
|
: failure.retainPartialObservation(result.value)
|
|
: analysisErrorWithCleanupFailure(result.error, failure, operation),
|
|
);
|
|
}
|
|
if (result.ok && options.signal?.aborted === true)
|
|
return err(
|
|
new AnalysisCancelledError(operation, {
|
|
cause: options.signal.reason,
|
|
partialObservation: result.value,
|
|
}),
|
|
);
|
|
return result;
|
|
}
|
|
}
|