@effect-agent/platform-cloudflare 0.1.0-beta.117 → 0.1.0-beta.119
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/ProtectedBrowser.d.mts +6 -1
- package/dist/ProtectedBrowser.mjs +58 -19
- package/dist/ProtectedBrowser.mjs.map +1 -1
- package/dist/{ThreadObject-S05rNeF7.d.mts → ThreadObject-D_prWu8-.d.mts} +30 -30
- package/dist/ThreadObject.d.mts +1 -1
- package/dist/index.d.mts +1 -1
- package/package.json +1 -1
- package/src/protected-browser/host.ts +4 -1
- package/src/protected-browser/inspect-frame.ts +21 -7
- package/src/protected-browser/native.ts +14 -2
- package/src/protected-browser/policy.ts +18 -3
|
@@ -710,19 +710,35 @@ declare const encodeAdminResponse: (input: AdminFailed | ExplainedRecovery | Obl
|
|
|
710
710
|
readonly message: string;
|
|
711
711
|
readonly cause?: Schema.Json | undefined;
|
|
712
712
|
} | {
|
|
713
|
-
readonly _tag: "
|
|
714
|
-
readonly
|
|
713
|
+
readonly _tag: "AdmissionPolicyError";
|
|
714
|
+
readonly reason: "occupied" | "refused" | "unavailable";
|
|
715
|
+
readonly code: string;
|
|
716
|
+
readonly cause?: import("effect-agent/failure-diagnostic").Diagnostic | undefined;
|
|
717
|
+
readonly stack?: string | undefined;
|
|
718
|
+
} | {
|
|
719
|
+
readonly _tag: "SettlementConflict";
|
|
720
|
+
readonly submissionId: string;
|
|
721
|
+
readonly existingOutcome: "aborted" | "completed" | "failed";
|
|
715
722
|
} | {
|
|
716
723
|
readonly _tag: "LedgerError";
|
|
717
724
|
readonly operation: string;
|
|
718
725
|
readonly message: string;
|
|
719
726
|
readonly cause?: import("effect-agent/failure-diagnostic").Diagnostic | undefined;
|
|
720
727
|
} | {
|
|
721
|
-
readonly _tag: "
|
|
722
|
-
readonly
|
|
723
|
-
readonly
|
|
724
|
-
readonly cause?:
|
|
725
|
-
readonly
|
|
728
|
+
readonly _tag: "ThreadStoreError";
|
|
729
|
+
readonly operation: string;
|
|
730
|
+
readonly message: string;
|
|
731
|
+
readonly cause?: Schema.Json | undefined;
|
|
732
|
+
readonly diagnostic?: {
|
|
733
|
+
readonly causeTag: string;
|
|
734
|
+
readonly operation: string;
|
|
735
|
+
readonly decoder?: string | undefined;
|
|
736
|
+
readonly issueTag?: string | undefined;
|
|
737
|
+
readonly sequence?: number | undefined;
|
|
738
|
+
} | undefined;
|
|
739
|
+
} | {
|
|
740
|
+
readonly _tag: "ThreadNotMaterialized";
|
|
741
|
+
readonly threadId: string;
|
|
726
742
|
} | {
|
|
727
743
|
readonly _tag: "AppendConflict";
|
|
728
744
|
readonly threadId: string;
|
|
@@ -730,33 +746,21 @@ declare const encodeAdminResponse: (input: AdminFailed | ExplainedRecovery | Obl
|
|
|
730
746
|
readonly reason: "batch-digest" | "record-identity" | "tail";
|
|
731
747
|
readonly actualTailSequence?: number | undefined;
|
|
732
748
|
readonly actualTailDigest?: string | undefined;
|
|
733
|
-
} | {
|
|
734
|
-
readonly _tag: "DigestError";
|
|
735
|
-
readonly message: string;
|
|
736
|
-
readonly cause?: Schema.Json | undefined;
|
|
737
|
-
} | {
|
|
738
|
-
readonly _tag: "DurableRuntimeFailpointError";
|
|
739
|
-
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";
|
|
740
749
|
} | {
|
|
741
750
|
readonly _tag: "FenceRejected";
|
|
742
751
|
readonly threadId: string;
|
|
743
752
|
readonly actualEpoch: number;
|
|
744
753
|
readonly attemptedEpoch: number;
|
|
745
754
|
} | {
|
|
746
|
-
readonly _tag: "
|
|
747
|
-
readonly
|
|
755
|
+
readonly _tag: "HostProtocolError";
|
|
756
|
+
readonly message: string;
|
|
748
757
|
} | {
|
|
749
|
-
readonly _tag: "
|
|
750
|
-
readonly operation: string;
|
|
758
|
+
readonly _tag: "DigestError";
|
|
751
759
|
readonly message: string;
|
|
752
760
|
readonly cause?: Schema.Json | undefined;
|
|
753
|
-
|
|
754
|
-
|
|
755
|
-
|
|
756
|
-
readonly decoder?: string | undefined;
|
|
757
|
-
readonly issueTag?: string | undefined;
|
|
758
|
-
readonly sequence?: number | undefined;
|
|
759
|
-
} | undefined;
|
|
761
|
+
} | {
|
|
762
|
+
readonly _tag: "DurableRuntimeFailpointError";
|
|
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";
|
|
760
764
|
} | {
|
|
761
765
|
readonly _tag: "OperationDenied";
|
|
762
766
|
readonly operation: "abort" | "awaitSettlement" | "explain" | "observe" | "resolveApproval" | "resolveUnknown" | "retry" | "scanObligations" | "verify" | "wake";
|
|
@@ -777,10 +781,6 @@ declare const encodeAdminResponse: (input: AdminFailed | ExplainedRecovery | Obl
|
|
|
777
781
|
readonly _tag: "OwnershipLost";
|
|
778
782
|
readonly submissionId: string;
|
|
779
783
|
readonly actualEpoch: number;
|
|
780
|
-
} | {
|
|
781
|
-
readonly _tag: "SettlementConflict";
|
|
782
|
-
readonly submissionId: string;
|
|
783
|
-
readonly existingOutcome: "aborted" | "completed" | "failed";
|
|
784
784
|
};
|
|
785
785
|
}, Schema.SchemaError, never>;
|
|
786
786
|
declare const decodeAdminResponse: (input: unknown, options?: import("effect/SchemaAST").ParseOptions) => Effect.Effect<AdminFailed | ExplainedRecovery | ObligationsScanned | RetryExecuted | VerifiedIntegrity, Schema.SchemaError, never>;
|
|
@@ -833,4 +833,4 @@ interface Class<EventServices = never> {
|
|
|
833
833
|
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>;
|
|
834
834
|
//#endregion
|
|
835
835
|
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 };
|
|
836
|
-
//# sourceMappingURL=ThreadObject-
|
|
836
|
+
//# sourceMappingURL=ThreadObject-D_prWu8-.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-D_prWu8-.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-D_prWu8-.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.119","dependencies":{"@effect-agent/storage-cloudflare":"0.1.0-beta.119","@effect/platform-browser":"4.0.0-rc.116","@effect/sql-sqlite-do":"4.0.0-rc.116","effect-agent":"0.1.0-beta.119"},"devDependencies":{"@cloudflare/puppeteer":"1.1.0","@cloudflare/vitest-pool-workers":"0.21.3","@cloudflare/workers-types":"5.20260825.1","@effect-agent/testing":"0.1.0-beta.119","@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":{"@cloudflare/puppeteer":"^1.1.0","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"},"peerDependenciesMeta":{"@cloudflare/puppeteer":{"optional":true}}}
|
|
@@ -62,7 +62,10 @@ export interface BrowserRunProtectedSession {
|
|
|
62
62
|
readonly getHandoffState: Effect.Effect<BrowserRunHandoffState, ProtectedBrowserError>;
|
|
63
63
|
/** Read only the exact page’s current origin after its recorded handoff completes; tools remain paused. */
|
|
64
64
|
readonly getReturnOrigin: Effect.Effect<typeof CredentialOrigin.Type, ProtectedBrowserError>;
|
|
65
|
-
/**
|
|
65
|
+
/**
|
|
66
|
+
* Resume a committed pause; human handoffs also require provider completion. Returns no page data.
|
|
67
|
+
* A fresh authorized observation must succeed before page actions or credential operations.
|
|
68
|
+
*/
|
|
66
69
|
readonly returnControl: Effect.Effect<void, ProtectedBrowserError>;
|
|
67
70
|
/** Release this attachment only after committing its suspended checkpoint; preserve the remote browser. */
|
|
68
71
|
readonly detach: Effect.Effect<void, ProtectedBrowserError>;
|
|
@@ -60,12 +60,26 @@ export const inspectFrame = `(() => {
|
|
|
60
60
|
} else if (el instanceof HTMLButtonElement || (el instanceof HTMLInputElement && ['submit','button'].includes(type))) {
|
|
61
61
|
if (!el.matches(':disabled')) role = type === 'submit' && form ? 'submit' : type === 'button' ? 'button' : 'unsupported';
|
|
62
62
|
} else if (el instanceof HTMLAnchorElement) role = 'link';
|
|
63
|
+
const options = [];
|
|
64
|
+
let truncated = false;
|
|
65
|
+
if (role === 'select') {
|
|
66
|
+
for (let index = 0; index < el.options.length; index++) {
|
|
67
|
+
if (index === 256) { truncated = true; break; }
|
|
68
|
+
const option = el.options[index];
|
|
69
|
+
const label = option.label.trim();
|
|
70
|
+
// Never publish a shortened label as an exact fill choice.
|
|
71
|
+
if (label.length > 200) { truncated = true; continue; }
|
|
72
|
+
options.push({label, selected: option.selected, disabled: option.matches(':disabled')});
|
|
73
|
+
}
|
|
74
|
+
}
|
|
63
75
|
const fingerprint = JSON.stringify([role, action, method, enctype, name, completion, type, choiceValue]);
|
|
64
76
|
return { role, formIndex, action, fingerprint, ...(isChoice(el) ? {checked: el.checked} : {}),
|
|
77
|
+
...(role === 'select' ? {options, truncated} : {}),
|
|
65
78
|
label: (labelText(el) ?? (nativeField ? '' : el.textContent) ?? '').slice(0,200) };
|
|
66
79
|
};
|
|
67
|
-
const expose = ({role, formIndex, action, label, checked}) =>
|
|
68
|
-
({role, formIndex, action, label, ...(checked === undefined ? {} : {checked})
|
|
80
|
+
const expose = ({role, formIndex, action, label, checked, options, truncated}) =>
|
|
81
|
+
({role, formIndex, action, label, ...(checked === undefined ? {} : {checked}),
|
|
82
|
+
...(options === undefined ? {} : {options, truncated})});
|
|
69
83
|
const original = elements.map(describe);
|
|
70
84
|
const validate = (index) => {
|
|
71
85
|
const current = describe(elements[index]);
|
|
@@ -95,13 +109,13 @@ export const inspectFrame = `(() => {
|
|
|
95
109
|
const current = validate(index);
|
|
96
110
|
if (!current || current.role !== role) return false;
|
|
97
111
|
const el = elements[index];
|
|
98
|
-
if (
|
|
112
|
+
if (el instanceof HTMLSelectElement) {
|
|
99
113
|
const options = [...el.options].filter(option => !option.matches(':disabled'));
|
|
100
|
-
|
|
101
|
-
const matches =
|
|
102
|
-
if (matches.length !== 1) return 'unsupported';
|
|
114
|
+
// Ordinary fills name the visible choice; credential material stays a private value.
|
|
115
|
+
const matches = options.filter(option => (role === 'select' ? option.label.trim() : option.value) === value);
|
|
116
|
+
if (matches.length !== 1) return 'unsupported-before-write';
|
|
103
117
|
value = matches[0].value;
|
|
104
|
-
if ([...el.options].filter(option => option.value === value).length !== 1) return 'unsupported';
|
|
118
|
+
if ([...el.options].filter(option => option.value === value).length !== 1) return 'unsupported-before-write';
|
|
105
119
|
}
|
|
106
120
|
let prototype = Object.getPrototypeOf(el);
|
|
107
121
|
let setter;
|
|
@@ -36,9 +36,12 @@ const Description = Schema.Struct({
|
|
|
36
36
|
action: Schema.String.check(Schema.isMaxLength(maxAttributeLength)),
|
|
37
37
|
label: Schema.String.check(Schema.isMaxLength(200)),
|
|
38
38
|
checked: Schema.optionalKey(Schema.Boolean),
|
|
39
|
+
options: ProtectedBrowserControl.fields.options,
|
|
40
|
+
truncated: Schema.optionalKey(Schema.Boolean),
|
|
39
41
|
});
|
|
40
42
|
|
|
41
43
|
const Descriptions = Schema.Array(Schema.NullOr(Description)).check(Schema.isMaxLength(65));
|
|
44
|
+
const FillResult = Schema.Literals([true, false, "unsupported-before-write", "unsupported"]);
|
|
42
45
|
|
|
43
46
|
interface FrameState {
|
|
44
47
|
readonly frame: Frame;
|
|
@@ -367,6 +370,7 @@ export const makeProtectedNativeTransport = Effect.fn("ProtectedNativeTransport.
|
|
|
367
370
|
const desc = descriptions[index];
|
|
368
371
|
|
|
369
372
|
if (!desc) continue;
|
|
373
|
+
if (desc.truncated) truncated = true;
|
|
370
374
|
if (discovered.length === 64) {
|
|
371
375
|
truncated = true;
|
|
372
376
|
break;
|
|
@@ -387,6 +391,7 @@ export const makeProtectedNativeTransport = Effect.fn("ProtectedNativeTransport.
|
|
|
387
391
|
role: desc.role,
|
|
388
392
|
label: desc.label,
|
|
389
393
|
...(desc.checked === undefined ? {} : { checked: desc.checked }),
|
|
394
|
+
...(desc.options === undefined ? {} : { options: desc.options }),
|
|
390
395
|
...(desc.role === "link" ? { url: desc.action } : {}),
|
|
391
396
|
target: CredentialTarget.make({
|
|
392
397
|
topOrigin: before.topOrigin,
|
|
@@ -439,7 +444,7 @@ export const makeProtectedNativeTransport = Effect.fn("ProtectedNativeTransport.
|
|
|
439
444
|
// CDP dispatch may mutate before its reply is lost, so uncertainty starts here.
|
|
440
445
|
yield* dispatch.mark;
|
|
441
446
|
|
|
442
|
-
const
|
|
447
|
+
const raw = yield* remote("protected.fill", () =>
|
|
443
448
|
state.frame.handle.evaluate(
|
|
444
449
|
(held, index, expectedRole, secret) => {
|
|
445
450
|
if (typeof held !== "object" || held === null) return false;
|
|
@@ -452,8 +457,15 @@ export const makeProtectedNativeTransport = Effect.fn("ProtectedNativeTransport.
|
|
|
452
457
|
),
|
|
453
458
|
);
|
|
454
459
|
|
|
460
|
+
const filled = yield* decode(FillResult, raw);
|
|
461
|
+
|
|
462
|
+
if (filled === false || filled === "unsupported-before-write") {
|
|
463
|
+
yield* dispatch.confirmNoWrite;
|
|
464
|
+
|
|
465
|
+
return yield* transportError(filled === false ? "stale-reference" : "unsupported");
|
|
466
|
+
}
|
|
467
|
+
|
|
455
468
|
if (filled === "unsupported") return yield* transportError("unsupported");
|
|
456
|
-
if (filled !== true) return yield* transportError("stale-reference");
|
|
457
469
|
}),
|
|
458
470
|
click: Effect.fn("ProtectedNativeTransport.click")(function* (ref) {
|
|
459
471
|
const state = yield* get(ref);
|
|
@@ -57,7 +57,11 @@ export class ProtectedTransportError extends Schema.TaggedError<ProtectedTranspo
|
|
|
57
57
|
/** Per-write evidence authority, provided by the policy immediately around a transport fill. */
|
|
58
58
|
export class ProtectedBrowserDispatch extends Context.Service<
|
|
59
59
|
ProtectedBrowserDispatch,
|
|
60
|
-
{
|
|
60
|
+
{
|
|
61
|
+
readonly mark: Effect.Effect<void>;
|
|
62
|
+
/** Only an acknowledged reply before assignment may restore the preceding dispatch evidence. */
|
|
63
|
+
readonly confirmNoWrite: Effect.Effect<void>;
|
|
64
|
+
}
|
|
61
65
|
>()("@effect-agent/platform-cloudflare/ProtectedBrowserDispatch") {}
|
|
62
66
|
|
|
63
67
|
/** Decoded adapter boundary. SDK exceptions and page diagnostics never cross this port. */
|
|
@@ -404,7 +408,8 @@ export const makeProtectedBrowserPolicy = Effect.fn("ProtectedBrowser.open")(fun
|
|
|
404
408
|
const interrupt = cause.reasons.some((reason) => reason._tag === "Interrupt");
|
|
405
409
|
|
|
406
410
|
if (
|
|
407
|
-
dispatch
|
|
411
|
+
dispatch === "possibly-dispatched" ||
|
|
412
|
+
(dispatch === "dispatched" && reason !== "busy") ||
|
|
408
413
|
interrupt ||
|
|
409
414
|
reason === "provider" ||
|
|
410
415
|
reason === "timeout"
|
|
@@ -576,6 +581,8 @@ export const makeProtectedBrowserPolicy = Effect.fn("ProtectedBrowser.open")(fun
|
|
|
576
581
|
target: control.target,
|
|
577
582
|
role: control.role,
|
|
578
583
|
});
|
|
584
|
+
const previousDispatch = dispatch;
|
|
585
|
+
|
|
579
586
|
yield* publicFailure(
|
|
580
587
|
driver.fill(decoded.ref, control.role, Redacted.make(decoded.value)),
|
|
581
588
|
).pipe(
|
|
@@ -583,6 +590,9 @@ export const makeProtectedBrowserPolicy = Effect.fn("ProtectedBrowser.open")(fun
|
|
|
583
590
|
mark: Effect.sync(() => {
|
|
584
591
|
dispatch = "possibly-dispatched";
|
|
585
592
|
}),
|
|
593
|
+
confirmNoWrite: Effect.sync(() => {
|
|
594
|
+
dispatch = previousDispatch;
|
|
595
|
+
}),
|
|
586
596
|
}),
|
|
587
597
|
);
|
|
588
598
|
dispatch = "dispatched";
|
|
@@ -725,6 +735,8 @@ export const makeProtectedBrowserPolicy = Effect.fn("ProtectedBrowser.open")(fun
|
|
|
725
735
|
const value = secretFor(material, field.role);
|
|
726
736
|
|
|
727
737
|
if (value === undefined) return yield* fail("missing-credential");
|
|
738
|
+
const previousDispatch = dispatch;
|
|
739
|
+
|
|
728
740
|
observation = "protected";
|
|
729
741
|
yield* publicFailure(driver.fill(field.ref, field.role, value)).pipe(
|
|
730
742
|
Effect.provideService(ProtectedBrowserDispatch, {
|
|
@@ -733,6 +745,10 @@ export const makeProtectedBrowserPolicy = Effect.fn("ProtectedBrowser.open")(fun
|
|
|
733
745
|
if (!exposures.some((target) => sameTarget(target, offer.target)))
|
|
734
746
|
exposures.push(offer.target);
|
|
735
747
|
}),
|
|
748
|
+
// A refused later field cannot erase a completed earlier field or its exposure.
|
|
749
|
+
confirmNoWrite: Effect.sync(() => {
|
|
750
|
+
dispatch = previousDispatch;
|
|
751
|
+
}),
|
|
736
752
|
}),
|
|
737
753
|
);
|
|
738
754
|
dispatch = "dispatched";
|
|
@@ -815,7 +831,6 @@ export const makeProtectedBrowserPolicy = Effect.fn("ProtectedBrowser.open")(fun
|
|
|
815
831
|
Effect.gen(function* () {
|
|
816
832
|
if (detached || observation === "closed") return yield* fail("closed");
|
|
817
833
|
if (!suspended) return yield* fail("denied");
|
|
818
|
-
yield* permitObservation;
|
|
819
834
|
offers.clear();
|
|
820
835
|
driver.resetReferences();
|
|
821
836
|
needsObservation = true;
|