@effect-agent/platform-cloudflare 0.1.0-beta.120 → 0.1.0-beta.121
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/CloudflareThreadClient.d.mts +50 -50
- package/dist/{InteractiveBrowser-BQo7QKaP.d.mts → InteractiveBrowser-Cm4xgiGy.d.mts} +2 -2
- package/dist/{InteractiveBrowser-CBUFAECo.mjs → InteractiveBrowser-DhOkdUx9.mjs} +57 -26
- package/dist/InteractiveBrowser-DhOkdUx9.mjs.map +1 -0
- package/dist/InteractiveBrowser.d.mts +1 -1
- package/dist/InteractiveBrowser.mjs +1 -1
- package/dist/ProtectedBrowser.d.mts +1 -1
- package/dist/ProtectedBrowser.mjs +5 -5
- package/dist/ProtectedBrowser.mjs.map +1 -1
- package/dist/{ThreadObject-9IVibJM7.d.mts → ThreadObject-mt0uxvZZ.d.mts} +27 -27
- package/dist/ThreadObject.d.mts +1 -1
- package/dist/index.d.mts +1 -1
- package/package.json +1 -1
- package/src/InteractiveBrowser.ts +17 -12
- package/src/internal/browser-binding.ts +81 -31
- package/src/internal/browser-file-selection.ts +1 -1
- package/src/protected-browser/binding.ts +31 -19
- package/src/protected-browser/native.ts +7 -9
- package/dist/InteractiveBrowser-CBUFAECo.mjs.map +0 -1
|
@@ -705,20 +705,17 @@ declare const encodeAdminResponse: (input: AdminFailed | ExplainedRecovery | Obl
|
|
|
705
705
|
} | {
|
|
706
706
|
readonly _tag: "AdminFailed";
|
|
707
707
|
readonly failure: {
|
|
708
|
-
readonly _tag: "
|
|
709
|
-
readonly threadId: string;
|
|
710
|
-
} | {
|
|
711
|
-
readonly _tag: "ThreadStoreError";
|
|
708
|
+
readonly _tag: "DurableAlarmError";
|
|
712
709
|
readonly operation: string;
|
|
713
710
|
readonly message: string;
|
|
714
711
|
readonly cause?: Schema.Json | undefined;
|
|
715
|
-
|
|
716
|
-
|
|
717
|
-
|
|
718
|
-
|
|
719
|
-
|
|
720
|
-
|
|
721
|
-
|
|
712
|
+
} | {
|
|
713
|
+
readonly _tag: "HostProtocolError";
|
|
714
|
+
readonly message: string;
|
|
715
|
+
} | {
|
|
716
|
+
readonly _tag: "DigestError";
|
|
717
|
+
readonly message: string;
|
|
718
|
+
readonly cause?: Schema.Json | undefined;
|
|
722
719
|
} | {
|
|
723
720
|
readonly _tag: "AdmissionPolicyError";
|
|
724
721
|
readonly reason: "occupied" | "refused" | "unavailable";
|
|
@@ -734,6 +731,21 @@ declare const encodeAdminResponse: (input: AdminFailed | ExplainedRecovery | Obl
|
|
|
734
731
|
readonly operation: string;
|
|
735
732
|
readonly message: string;
|
|
736
733
|
readonly cause?: import("effect-agent/failure-diagnostic").Diagnostic | undefined;
|
|
734
|
+
} | {
|
|
735
|
+
readonly _tag: "ThreadStoreError";
|
|
736
|
+
readonly operation: string;
|
|
737
|
+
readonly message: string;
|
|
738
|
+
readonly cause?: Schema.Json | undefined;
|
|
739
|
+
readonly diagnostic?: {
|
|
740
|
+
readonly causeTag: string;
|
|
741
|
+
readonly operation: string;
|
|
742
|
+
readonly decoder?: string | undefined;
|
|
743
|
+
readonly issueTag?: string | undefined;
|
|
744
|
+
readonly sequence?: number | undefined;
|
|
745
|
+
} | undefined;
|
|
746
|
+
} | {
|
|
747
|
+
readonly _tag: "ThreadNotMaterialized";
|
|
748
|
+
readonly threadId: string;
|
|
737
749
|
} | {
|
|
738
750
|
readonly _tag: "AppendConflict";
|
|
739
751
|
readonly threadId: string;
|
|
@@ -746,30 +758,18 @@ declare const encodeAdminResponse: (input: AdminFailed | ExplainedRecovery | Obl
|
|
|
746
758
|
readonly threadId: string;
|
|
747
759
|
readonly actualEpoch: number;
|
|
748
760
|
readonly attemptedEpoch: number;
|
|
749
|
-
} | {
|
|
750
|
-
readonly _tag: "DurableAlarmError";
|
|
751
|
-
readonly operation: string;
|
|
752
|
-
readonly message: string;
|
|
753
|
-
readonly cause?: Schema.Json | undefined;
|
|
754
|
-
} | {
|
|
755
|
-
readonly _tag: "HostProtocolError";
|
|
756
|
-
readonly message: string;
|
|
757
|
-
} | {
|
|
758
|
-
readonly _tag: "DigestError";
|
|
759
|
-
readonly message: string;
|
|
760
|
-
readonly cause?: Schema.Json | undefined;
|
|
761
761
|
} | {
|
|
762
762
|
readonly _tag: "DurableRuntimeFailpointError";
|
|
763
763
|
readonly location: "abort:after-intent" | "approval:after-request-append" | "approval:after-suspend" | "checkpoint:after-save" | "checkpoint:before-save" | "claim:after-claim" | "compaction:after-canonical-append" | "compaction:before-canonical-append" | "input:after-canonical-append" | "join:after-canonical-append" | "join:after-claim" | "policy:after-reservation-append" | "policy:before-reservation-append" | "resolve:after-intent" | "run:after-duration-append" | "run:after-start-append" | "run:before-duration-append" | "run:before-start-append" | "step:after-step-append" | "subagent:after-admit" | "subagent:after-child-abort-intent" | "subagent:after-child-ready" | "subagent:after-join-append" | "subagent:after-release" | "subagent:after-release-pending" | "subagent:after-request-append" | "subagent:after-reserve" | "subagent:after-sibling-settle" | "subagent:after-start-append" | "subagent:after-suspend" | "subagent:before-join-append" | "submit:after-admit" | "submit:after-materialize" | "terminalize:after-canonical-append" | "terminalize:after-reserve" | "tools:after-prepared-append" | "tools:after-unavailable-append" | "tools:before-prepared-append" | "tools:before-unavailable-append" | "turn:after-canonical-append" | "turn:after-response-append" | "turn:after-results-append" | "update:after-canonical-append" | "update:after-delivery-insert" | "update:before-canonical-append" | "update:before-delivery-insert" | "worker:after-completion-append" | "worker:after-origin-append" | "worker:after-report-append" | "worker:after-report-delivery" | "worker:after-source-append" | "worker:after-stop-append" | "worker:after-stop-seal" | "worker:after-subtree-append" | "worker:before-completion-append" | "worker:before-origin-append" | "worker:before-report-append" | "worker:before-report-delivery" | "worker:before-source-append" | "worker:before-stop-append" | "worker:before-stop-seal" | "worker:before-subtree-append";
|
|
764
|
-
} | {
|
|
765
|
-
readonly _tag: "ApprovalSuspensionError";
|
|
766
|
-
readonly cause: Schema.Json;
|
|
767
764
|
} | {
|
|
768
765
|
readonly _tag: "OperationDenied";
|
|
769
766
|
readonly operation: "abort" | "awaitSettlement" | "explain" | "observe" | "resolveApproval" | "resolveUnknown" | "retry" | "scanObligations" | "verify" | "wake";
|
|
770
767
|
readonly reason: string;
|
|
771
768
|
readonly threadId?: string | undefined;
|
|
772
769
|
readonly submissionId?: string | undefined;
|
|
770
|
+
} | {
|
|
771
|
+
readonly _tag: "ApprovalSuspensionError";
|
|
772
|
+
readonly cause: Schema.Json;
|
|
773
773
|
} | {
|
|
774
774
|
readonly _tag: "RetryRefused";
|
|
775
775
|
readonly submissionId: string;
|
|
@@ -836,4 +836,4 @@ interface Class<EventServices = never> {
|
|
|
836
836
|
declare const make: <ApplicationServices, ApplicationError, EventServices = never, EventLayerError = never>(applicationLayer: Layer.Layer<CloudflareDurableRuntimeServices | ApplicationServices, ApplicationError, CloudflareBootstrapServices | DurableObjectState$1.DurableObjectState | WorkerEnvironment | DurableObjectContext | ThreadObjectNamespace>, options: Options<ApplicationServices, EventServices, EventLayerError>) => Class<ApplicationServices | EventServices>;
|
|
837
837
|
//#endregion
|
|
838
838
|
export { ThreadPublicationOptions as A, portCall as C, CloudflareDurableRuntimeOptions as D, CloudflareDurableRuntimeInitializationError as E, layerConfig as M, layerHostConfig as N, CloudflareDurableRuntimeServices as O, layerInHost as P, make as S, CloudflareBootstrapServices as T, decodeAdminVerifyRequest as _, AdminVerifyRequest as a, encodeAdminResponse as b, Instance as c, RetryExecuted as d, ThreadObject_d_exports as f, decodeAdminResponse as g, decodeAdminExplainRequest as h, AdminResponse as i, layer as j, ThreadObjectPorts as k, ObligationsScanned as l, VerifiedIntegrity as m, AdminFailed as n, Class as o, ThreadRpcOperation as p, AdminFailure as r, ExplainedRecovery as s, AdminExplainRequest as t, Options as u, decodeObligationThresholds as v, submit as w, handleRpc as x, decodeRetryCommand as y };
|
|
839
|
-
//# sourceMappingURL=ThreadObject-
|
|
839
|
+
//# sourceMappingURL=ThreadObject-mt0uxvZZ.d.mts.map
|
package/dist/ThreadObject.d.mts
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import { A as ThreadPublicationOptions, C as portCall, D as CloudflareDurableRuntimeOptions, E as CloudflareDurableRuntimeInitializationError, M as layerConfig, N as layerHostConfig, O as CloudflareDurableRuntimeServices, P as layerInHost, S as make, T as CloudflareBootstrapServices, _ as decodeAdminVerifyRequest, a as AdminVerifyRequest, b as encodeAdminResponse, c as Instance, d as RetryExecuted, g as decodeAdminResponse, h as decodeAdminExplainRequest, i as AdminResponse, j as layer, k as ThreadObjectPorts, l as ObligationsScanned, m as VerifiedIntegrity, n as AdminFailed, o as Class, p as ThreadRpcOperation, r as AdminFailure, s as ExplainedRecovery, t as AdminExplainRequest, u as Options, v as decodeObligationThresholds, w as submit, x as handleRpc, y as decodeRetryCommand } from "./ThreadObject-
|
|
1
|
+
import { A as ThreadPublicationOptions, C as portCall, D as CloudflareDurableRuntimeOptions, E as CloudflareDurableRuntimeInitializationError, M as layerConfig, N as layerHostConfig, O as CloudflareDurableRuntimeServices, P as layerInHost, S as make, T as CloudflareBootstrapServices, _ as decodeAdminVerifyRequest, a as AdminVerifyRequest, b as encodeAdminResponse, c as Instance, d as RetryExecuted, g as decodeAdminResponse, h as decodeAdminExplainRequest, i as AdminResponse, j as layer, k as ThreadObjectPorts, l as ObligationsScanned, m as VerifiedIntegrity, n as AdminFailed, o as Class, p as ThreadRpcOperation, r as AdminFailure, s as ExplainedRecovery, t as AdminExplainRequest, u as Options, v as decodeObligationThresholds, w as submit, x as handleRpc, y as decodeRetryCommand } from "./ThreadObject-mt0uxvZZ.mjs";
|
|
2
2
|
export { AdminExplainRequest, AdminFailed, AdminFailure, AdminResponse, AdminVerifyRequest, type CloudflareBootstrapServices as BootstrapServices, Class, ExplainedRecovery, type CloudflareDurableRuntimeInitializationError as InitializationError, Instance, ObligationsScanned, Options, type ThreadPublicationOptions as PublicationOptions, RetryExecuted, type CloudflareDurableRuntimeOptions as RuntimeOptions, type CloudflareDurableRuntimeServices as Services, ThreadObjectPorts, ThreadRpcOperation, VerifiedIntegrity, decodeAdminExplainRequest, decodeAdminResponse, decodeAdminVerifyRequest, decodeObligationThresholds, decodeRetryCommand, encodeAdminResponse, handleRpc, layer, layerConfig, layerHostConfig, layerInHost, make, portCall, submit };
|
package/dist/index.d.mts
CHANGED
|
@@ -8,6 +8,6 @@ import { t as CloudflareMemory_d_exports } from "./CloudflareMemory.mjs";
|
|
|
8
8
|
import { t as CloudflareScheduling_d_exports } from "./CloudflareScheduling.mjs";
|
|
9
9
|
import { t as CloudflareSubscriptions_d_exports } from "./CloudflareSubscriptions.mjs";
|
|
10
10
|
import { t as CloudflareThreadClient_d_exports } from "./CloudflareThreadClient.mjs";
|
|
11
|
-
import { f as ThreadObject_d_exports } from "./ThreadObject-
|
|
11
|
+
import { f as ThreadObject_d_exports } from "./ThreadObject-mt0uxvZZ.mjs";
|
|
12
12
|
import { t as WakeScheduler_d_exports } from "./WakeScheduler.mjs";
|
|
13
13
|
export { Alarm_d_exports as Alarm, CloudflareAiGateway_d_exports as CloudflareAiGateway, CloudflareBindings_d_exports as CloudflareBindings, CloudflareBrowser_d_exports as CloudflareBrowser, CloudflareCodeMode_d_exports as CloudflareCodeMode, CloudflareConfig_d_exports as CloudflareConfig, CloudflareMemory_d_exports as CloudflareMemory, CloudflareScheduling_d_exports as CloudflareScheduling, CloudflareSubscriptions_d_exports as CloudflareSubscriptions, CloudflareThreadClient_d_exports as CloudflareThreadClient, ThreadObject_d_exports as ThreadObject, WakeScheduler_d_exports as WakeScheduler };
|
package/package.json
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"name":"@effect-agent/platform-cloudflare","version":"0.1.0-beta.
|
|
1
|
+
{"name":"@effect-agent/platform-cloudflare","version":"0.1.0-beta.121","dependencies":{"@effect-agent/storage-cloudflare":"0.1.0-beta.121","@effect/platform-browser":"4.0.0-rc.116","@effect/sql-sqlite-do":"4.0.0-rc.116","effect-agent":"0.1.0-beta.121","puppeteer-core":"24.29.0"},"devDependencies":{"@cloudflare/vitest-pool-workers":"0.21.3","@cloudflare/workers-types":"5.20260825.1","@effect-agent/testing":"0.1.0-beta.121","@effect/platform-node":"4.0.0-rc.116","@effect/sql-d1":"4.0.0-rc.116","@effect/vitest":"4.0.0-rc.116","effect":"4.0.0-rc.116","effect-cf":"0.44.1","esbuild":"0.28.1","miniflare":"5.20260811.1-alpha","typescript":"7.0.2","vite-plus":"0.3.2","vitest":"4.1.11"},"peerDependencies":{"effect":"^4.0.0-rc.116","effect-cf":"^0.44.1 || ^0.45.0"},"exports":{".":{"types":"./dist/index.d.mts","default":"./dist/index.mjs"},"./alarm":{"types":"./dist/Alarm.d.mts","default":"./dist/Alarm.mjs"},"./browser-rest-capture":{"types":"./dist/BrowserRestCapture.d.mts","default":"./dist/BrowserRestCapture.mjs"},"./browser-rest-crawl":{"types":"./dist/BrowserRestCrawl.d.mts","default":"./dist/BrowserRestCrawl.mjs"},"./cloudflare-bindings":{"types":"./dist/CloudflareBindings.d.mts","default":"./dist/CloudflareBindings.mjs"},"./cloudflare-browser":{"types":"./dist/CloudflareBrowser.d.mts","default":"./dist/CloudflareBrowser.mjs"},"./cloudflare-code-mode":{"types":"./dist/CloudflareCodeMode.d.mts","default":"./dist/CloudflareCodeMode.mjs"},"./cloudflare-config":{"types":"./dist/CloudflareConfig.d.mts","default":"./dist/CloudflareConfig.mjs"},"./cloudflare-memory":{"types":"./dist/CloudflareMemory.d.mts","default":"./dist/CloudflareMemory.mjs"},"./cloudflare-scheduling":{"types":"./dist/CloudflareScheduling.d.mts","default":"./dist/CloudflareScheduling.mjs"},"./cloudflare-subscriptions":{"types":"./dist/CloudflareSubscriptions.d.mts","default":"./dist/CloudflareSubscriptions.mjs"},"./cloudflare-thread-client":{"types":"./dist/CloudflareThreadClient.d.mts","default":"./dist/CloudflareThreadClient.mjs"},"./interactive-browser":{"types":"./dist/InteractiveBrowser.d.mts","default":"./dist/InteractiveBrowser.mjs"},"./protected-browser":{"types":"./dist/ProtectedBrowser.d.mts","default":"./dist/ProtectedBrowser.mjs"},"./thread-object":{"types":"./dist/ThreadObject.d.mts","default":"./dist/ThreadObject.mjs"},"./wake-scheduler":{"types":"./dist/WakeScheduler.d.mts","default":"./dist/WakeScheduler.mjs"},"./cloudflare-ai-gateway":{"types":"./dist/CloudflareAiGateway.d.mts","default":"./dist/CloudflareAiGateway.mjs"}},"description":"Cloudflare Layer assembly for Effect Agent: Durable Objects and Browser Run adapters.","license":"MIT","repository":{"type":"git","url":"git+https://github.com/danieljvdm/effect-agent.git","directory":"packages/platform-cloudflare"},"files":["dist","src"],"type":"module","sideEffects":[],"publishConfig":{"access":"public"},"scripts":{"build":"vp pack","check":"tsc --noEmit -p tsconfig.json"}}
|
|
@@ -1,12 +1,5 @@
|
|
|
1
1
|
/// <reference types="@cloudflare/workers-types" />
|
|
2
2
|
|
|
3
|
-
import {
|
|
4
|
-
type Browser,
|
|
5
|
-
type BrowserContext,
|
|
6
|
-
type CDPSession,
|
|
7
|
-
type HTTPRequest,
|
|
8
|
-
type Page,
|
|
9
|
-
} from "@cloudflare/puppeteer";
|
|
10
3
|
import {
|
|
11
4
|
Cause,
|
|
12
5
|
Context,
|
|
@@ -47,6 +40,13 @@ import {
|
|
|
47
40
|
} from "effect-agent/interactive-browser";
|
|
48
41
|
import { PageScreenshotResult } from "effect-agent/page-screenshot";
|
|
49
42
|
import { SandboxImplementation } from "effect-agent/sandbox";
|
|
43
|
+
import {
|
|
44
|
+
type Browser,
|
|
45
|
+
type BrowserContext,
|
|
46
|
+
type CDPSession,
|
|
47
|
+
type HTTPRequest,
|
|
48
|
+
type Page,
|
|
49
|
+
} from "puppeteer-core/lib/esm/puppeteer/puppeteer-core-browser.js";
|
|
50
50
|
|
|
51
51
|
import { BrowserRunBinding } from "./internal/browser-binding.ts";
|
|
52
52
|
import {
|
|
@@ -380,7 +380,10 @@ export class BrowserRunInteractiveBinding extends Context.Service<
|
|
|
380
380
|
BrowserRunInteractiveBinding,
|
|
381
381
|
{
|
|
382
382
|
readonly acquire: (keepAliveMillis: number) => Promise<unknown>;
|
|
383
|
-
readonly connect: (
|
|
383
|
+
readonly connect: (
|
|
384
|
+
sessionId: string,
|
|
385
|
+
signal?: AbortSignal,
|
|
386
|
+
) => Promise<BrowserRunInteractiveBrowser>;
|
|
384
387
|
/** Success proves whole-browser termination or exact-session absence. */
|
|
385
388
|
readonly closeSession: (
|
|
386
389
|
sessionId: Redacted.Redacted<string>,
|
|
@@ -406,9 +409,10 @@ export class BrowserRunInteractiveBinding extends Context.Service<
|
|
|
406
409
|
return {
|
|
407
410
|
acquire: async (keepAliveMillis: number) =>
|
|
408
411
|
binding.acquire(keepAliveMillis, "interactive.acquire"),
|
|
409
|
-
connect: async (sessionId: string) =>
|
|
412
|
+
connect: async (sessionId: string, signal?: AbortSignal) =>
|
|
410
413
|
makeProductionBrowser(
|
|
411
|
-
await binding.connect(sessionId, "interactive.connect"),
|
|
414
|
+
await binding.connect(sessionId, "interactive.connect", signal),
|
|
415
|
+
sessionId,
|
|
412
416
|
viewport,
|
|
413
417
|
),
|
|
414
418
|
closeSession: (sessionId: Redacted.Redacted<string>) =>
|
|
@@ -1385,6 +1389,7 @@ const makeProductionContext = (
|
|
|
1385
1389
|
|
|
1386
1390
|
const makeProductionBrowser = (
|
|
1387
1391
|
browser: Browser,
|
|
1392
|
+
sessionId: string,
|
|
1388
1393
|
viewport?: BrowserRunViewport,
|
|
1389
1394
|
): BrowserRunInteractiveBrowser => ({
|
|
1390
1395
|
detach: () => browser.disconnect(),
|
|
@@ -1417,7 +1422,7 @@ const makeProductionBrowser = (
|
|
|
1417
1422
|
if (!isRemoteClosure(cause)) throw cause;
|
|
1418
1423
|
}
|
|
1419
1424
|
},
|
|
1420
|
-
sessionId: () =>
|
|
1425
|
+
sessionId: () => sessionId,
|
|
1421
1426
|
isConnected: () => browser.isConnected(),
|
|
1422
1427
|
onDisconnected: (listener) => {
|
|
1423
1428
|
browser.on("disconnected", listener);
|
|
@@ -2673,7 +2678,7 @@ const makeHostService = (
|
|
|
2673
2678
|
signal,
|
|
2674
2679
|
closeLateAcquisition(
|
|
2675
2680
|
signal,
|
|
2676
|
-
() => binding.connect(sessionIdValue),
|
|
2681
|
+
() => binding.connect(sessionIdValue, signal),
|
|
2677
2682
|
(acquired) =>
|
|
2678
2683
|
runCleanup(
|
|
2679
2684
|
resume === undefined
|
|
@@ -1,8 +1,11 @@
|
|
|
1
1
|
/// <reference types="@cloudflare/workers-types" />
|
|
2
|
-
import puppeteer, { type Browser } from "@cloudflare/puppeteer";
|
|
3
2
|
import { Context, Layer, Schema } from "effect";
|
|
3
|
+
import puppeteer, {
|
|
4
|
+
type Browser,
|
|
5
|
+
type ConnectionTransport,
|
|
6
|
+
} from "puppeteer-core/lib/esm/puppeteer/puppeteer-core-browser.js";
|
|
4
7
|
|
|
5
|
-
import { browserFailure, BrowserRunFailure } from "./browser-failure.ts";
|
|
8
|
+
import { browserFailure, BrowserRunFailure, reportedBrowserError } from "./browser-failure.ts";
|
|
6
9
|
|
|
7
10
|
const Acquired = Schema.Struct({ sessionId: Schema.String.check(Schema.isUUID()) });
|
|
8
11
|
|
|
@@ -14,7 +17,11 @@ export class BrowserRunBinding extends Context.Service<
|
|
|
14
17
|
keepAliveMillis: number,
|
|
15
18
|
operation: "protected.acquire" | "interactive.acquire",
|
|
16
19
|
) => Promise<string>;
|
|
17
|
-
readonly connect: (
|
|
20
|
+
readonly connect: (
|
|
21
|
+
sessionId: string,
|
|
22
|
+
operation: string,
|
|
23
|
+
signal?: AbortSignal,
|
|
24
|
+
) => Promise<Browser>;
|
|
18
25
|
}
|
|
19
26
|
>()("@effect-agent/platform-cloudflare/internal/BrowserRunBinding") {
|
|
20
27
|
static layer(browser: Pick<BrowserRun, "fetch">) {
|
|
@@ -43,38 +50,81 @@ export class BrowserRunBinding extends Context.Service<
|
|
|
43
50
|
|
|
44
51
|
return Schema.decodeUnknownSync(Acquired)(await response.json()).sessionId;
|
|
45
52
|
},
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
let
|
|
53
|
+
connect: async (sessionId, operation, signal) => {
|
|
54
|
+
let socket: WebSocket | undefined;
|
|
55
|
+
let closedByAbort = false;
|
|
49
56
|
|
|
50
57
|
try {
|
|
51
|
-
|
|
52
|
-
{
|
|
53
|
-
|
|
54
|
-
try {
|
|
55
|
-
const response = await browser.fetch(input, init);
|
|
56
|
-
|
|
57
|
-
if (response.status !== 101) {
|
|
58
|
-
failure = new BrowserRunFailure({
|
|
59
|
-
operation,
|
|
60
|
-
reason: "provider",
|
|
61
|
-
status: response.status,
|
|
62
|
-
});
|
|
63
|
-
await response.body?.cancel();
|
|
64
|
-
throw failure;
|
|
65
|
-
}
|
|
66
|
-
|
|
67
|
-
return response;
|
|
68
|
-
} catch (cause) {
|
|
69
|
-
failure ??= browserFailure(operation, cause);
|
|
70
|
-
throw failure;
|
|
71
|
-
}
|
|
72
|
-
},
|
|
73
|
-
},
|
|
74
|
-
sessionId,
|
|
58
|
+
const response = await browser.fetch(
|
|
59
|
+
`https://browser-rendering.cloudflare.com/v1/devtools/browser/${sessionId}`,
|
|
60
|
+
{ headers: { Upgrade: "websocket" }, ...(signal === undefined ? {} : { signal }) },
|
|
75
61
|
);
|
|
62
|
+
|
|
63
|
+
if (response.status !== 101) {
|
|
64
|
+
const failure = new BrowserRunFailure({
|
|
65
|
+
operation,
|
|
66
|
+
reason: "provider",
|
|
67
|
+
status: response.status,
|
|
68
|
+
});
|
|
69
|
+
|
|
70
|
+
try {
|
|
71
|
+
await response.body?.cancel();
|
|
72
|
+
} catch {
|
|
73
|
+
// Body cancellation cannot replace the provider refusal.
|
|
74
|
+
}
|
|
75
|
+
throw failure;
|
|
76
|
+
}
|
|
77
|
+
const connected = response.webSocket;
|
|
78
|
+
|
|
79
|
+
if (connected === null) throw new BrowserRunFailure({ operation, reason: "malformed" });
|
|
80
|
+
socket = connected;
|
|
81
|
+
|
|
82
|
+
const transport: ConnectionTransport = {
|
|
83
|
+
send: (message) => connected.send(message),
|
|
84
|
+
close: () => connected.close(),
|
|
85
|
+
};
|
|
86
|
+
|
|
87
|
+
connected.addEventListener("message", (event) => {
|
|
88
|
+
transport.onmessage?.(event.data);
|
|
89
|
+
});
|
|
90
|
+
connected.addEventListener("close", () => transport.onclose?.());
|
|
91
|
+
connected.accept();
|
|
92
|
+
|
|
93
|
+
const abort = () => {
|
|
94
|
+
try {
|
|
95
|
+
const wasOpen = connected.readyState === WebSocket.OPEN;
|
|
96
|
+
|
|
97
|
+
connected.close();
|
|
98
|
+
closedByAbort = wasOpen;
|
|
99
|
+
} catch {
|
|
100
|
+
// The local transport may already have closed; never terminate the provider here.
|
|
101
|
+
}
|
|
102
|
+
};
|
|
103
|
+
|
|
104
|
+
signal?.addEventListener("abort", abort, { once: true });
|
|
105
|
+
try {
|
|
106
|
+
signal?.throwIfAborted();
|
|
107
|
+
|
|
108
|
+
return await puppeteer.connect({ transport });
|
|
109
|
+
} finally {
|
|
110
|
+
signal?.removeEventListener("abort", abort);
|
|
111
|
+
}
|
|
76
112
|
} catch (cause) {
|
|
77
|
-
|
|
113
|
+
try {
|
|
114
|
+
socket?.close();
|
|
115
|
+
} catch {
|
|
116
|
+
// Preserve the connection failure when local release also fails.
|
|
117
|
+
}
|
|
118
|
+
const failure = browserFailure(operation, cause);
|
|
119
|
+
|
|
120
|
+
// A local abort can reject SDK initialization as target-closed. Genuine late
|
|
121
|
+
// provider failures still report, even when the caller's signal has aborted.
|
|
122
|
+
const localCancellation =
|
|
123
|
+
cause instanceof Error &&
|
|
124
|
+
signal?.aborted === true &&
|
|
125
|
+
(cause === signal.reason || (closedByAbort && cause.name === "TargetCloseError"));
|
|
126
|
+
|
|
127
|
+
throw localCancellation ? reportedBrowserError(failure) : failure;
|
|
78
128
|
}
|
|
79
129
|
},
|
|
80
130
|
});
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import type { Page } from "@cloudflare/puppeteer";
|
|
2
1
|
import { Encoding } from "effect";
|
|
3
2
|
import type { BrowserSelectFileRequest } from "effect-agent/interactive-browser";
|
|
3
|
+
import type { Page } from "puppeteer-core/lib/esm/puppeteer/puppeteer-core-browser.js";
|
|
4
4
|
|
|
5
5
|
type Element = NonNullable<Awaited<ReturnType<Page["$"]>>>;
|
|
6
6
|
|
|
@@ -1,8 +1,13 @@
|
|
|
1
1
|
/// <reference types="@cloudflare/workers-types" />
|
|
2
|
-
|
|
2
|
+
|
|
3
3
|
import { Cause, Context, Crypto, Effect, Layer, Redacted, Schema, type Scope } from "effect";
|
|
4
4
|
import { type InteractiveBrowserPolicy } from "effect-agent/interactive-browser";
|
|
5
5
|
import { ProtectedBrowserError } from "effect-agent/protected-browser";
|
|
6
|
+
import {
|
|
7
|
+
type Browser,
|
|
8
|
+
type Page,
|
|
9
|
+
type CDPSession,
|
|
10
|
+
} from "puppeteer-core/lib/esm/puppeteer/puppeteer-core-browser.js";
|
|
6
11
|
|
|
7
12
|
import { BrowserRunBinding } from "../internal/browser-binding.ts";
|
|
8
13
|
import {
|
|
@@ -89,23 +94,29 @@ const protectedBindingLayer = (options: { readonly browser: Pick<BrowserRun, "fe
|
|
|
89
94
|
driver?.invalidate();
|
|
90
95
|
if (sessionId === undefined) return "unconfirmed" as const;
|
|
91
96
|
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
)
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
reportBrowserCause(
|
|
102
|
-
"protected.close",
|
|
103
|
-
Cause.fail(
|
|
104
|
-
new BrowserRunFailure({ operation: "protected.close", reason: "timeout" }),
|
|
97
|
+
// A failed resume has not acquired the host's retained provider. Release only
|
|
98
|
+
// this connection; successful attachments and new allocations own termination.
|
|
99
|
+
const cleanup = yield* identity !== undefined && driver === undefined
|
|
100
|
+
? Effect.succeed("unconfirmed" as const)
|
|
101
|
+
: lifecycle.close(sessionId).pipe(
|
|
102
|
+
Effect.as("confirmed" as const),
|
|
103
|
+
Effect.catchCause((cause) =>
|
|
104
|
+
reportBrowserCause("protected.close", cause).pipe(
|
|
105
|
+
Effect.as("unconfirmed" as const),
|
|
105
106
|
),
|
|
106
|
-
)
|
|
107
|
-
|
|
108
|
-
|
|
107
|
+
),
|
|
108
|
+
Effect.interruptible,
|
|
109
|
+
Effect.timeoutOrElse({
|
|
110
|
+
duration: "10 seconds",
|
|
111
|
+
orElse: () =>
|
|
112
|
+
reportBrowserCause(
|
|
113
|
+
"protected.close",
|
|
114
|
+
Cause.fail(
|
|
115
|
+
new BrowserRunFailure({ operation: "protected.close", reason: "timeout" }),
|
|
116
|
+
),
|
|
117
|
+
).pipe(Effect.as("unconfirmed" as const)),
|
|
118
|
+
}),
|
|
119
|
+
);
|
|
109
120
|
|
|
110
121
|
// Local disconnect is not remote-closure evidence. Do it even when confirmation fails.
|
|
111
122
|
const connected = browser;
|
|
@@ -155,7 +166,7 @@ const protectedBindingLayer = (options: { readonly browser: Pick<BrowserRun, "fe
|
|
|
155
166
|
}
|
|
156
167
|
// Resume only a host-persisted exact page. Never open a replacement page or context.
|
|
157
168
|
stage = "protected.connect";
|
|
158
|
-
browser = await binding.connect(Redacted.value(sessionId), stage);
|
|
169
|
+
browser = await binding.connect(Redacted.value(sessionId), stage, signal);
|
|
159
170
|
if (signal.aborted || invalid) {
|
|
160
171
|
await runCleanup(terminate);
|
|
161
172
|
throw new ProtectedTransportError({ reason: "stale-reference" });
|
|
@@ -262,7 +273,8 @@ const protectedBindingLayer = (options: { readonly browser: Pick<BrowserRun, "fe
|
|
|
262
273
|
new ProtectedBrowserError({
|
|
263
274
|
...failure(),
|
|
264
275
|
reason: error.reason === "timeout" ? "timeout" : "provider",
|
|
265
|
-
cleanup
|
|
276
|
+
cleanup:
|
|
277
|
+
identity !== undefined && driver === undefined ? "not-requested" : cleanup,
|
|
266
278
|
}),
|
|
267
279
|
),
|
|
268
280
|
),
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/// <reference types="@cloudflare/workers-types" />
|
|
2
|
-
|
|
2
|
+
|
|
3
3
|
import { Cause, Clock, Context, Crypto, Effect, Redacted, Schema, type Scope } from "effect";
|
|
4
4
|
import { type InteractiveBrowserPolicy } from "effect-agent/interactive-browser";
|
|
5
5
|
import {
|
|
@@ -7,6 +7,12 @@ import {
|
|
|
7
7
|
CredentialTarget,
|
|
8
8
|
ProtectedBrowserControl,
|
|
9
9
|
} from "effect-agent/protected-browser";
|
|
10
|
+
import type {
|
|
11
|
+
Browser,
|
|
12
|
+
Frame,
|
|
13
|
+
JSHandle,
|
|
14
|
+
Page,
|
|
15
|
+
} from "puppeteer-core/lib/esm/puppeteer/puppeteer-core-browser.js";
|
|
10
16
|
|
|
11
17
|
import {
|
|
12
18
|
browserFailure,
|
|
@@ -22,14 +28,6 @@ import {
|
|
|
22
28
|
type ProtectedBrowserTransport,
|
|
23
29
|
} from "./policy.ts";
|
|
24
30
|
|
|
25
|
-
// The pinned SDK implements this method but strips its internal declaration from lib/types.d.ts.
|
|
26
|
-
// Keep the declaration narrow and refuse runtimes without it. No main-world fallback is safe.
|
|
27
|
-
declare module "@cloudflare/puppeteer" {
|
|
28
|
-
interface Frame {
|
|
29
|
-
isolatedRealm(): { evaluateHandle(source: string): Promise<JSHandle<unknown>> };
|
|
30
|
-
}
|
|
31
|
-
}
|
|
32
|
-
|
|
33
31
|
const Description = Schema.Struct({
|
|
34
32
|
role: ProtectedBrowserControl.fields.role,
|
|
35
33
|
formIndex: Schema.Natural,
|