@effect-agent/platform-cloudflare 0.1.0-beta.9 → 0.1.0-beta.91
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/Alarm.d.mts +188 -0
- package/dist/Alarm.mjs +451 -0
- package/dist/Alarm.mjs.map +1 -0
- package/dist/BrowserRestCapture.d.mts +35 -0
- package/dist/BrowserRestCapture.mjs +238 -0
- package/dist/BrowserRestCapture.mjs.map +1 -0
- package/dist/BrowserRestCrawl.d.mts +17 -0
- package/dist/BrowserRestCrawl.mjs +244 -0
- package/dist/BrowserRestCrawl.mjs.map +1 -0
- package/dist/CloudflareAiGateway.d.mts +64 -0
- package/dist/CloudflareAiGateway.mjs +70 -0
- package/dist/CloudflareAiGateway.mjs.map +1 -0
- package/dist/CloudflareBindings.d.mts +115 -0
- package/dist/CloudflareBindings.mjs +113 -0
- package/dist/CloudflareBindings.mjs.map +1 -0
- package/dist/CloudflareBrowser-DV6kya1H.mjs +495 -0
- package/dist/CloudflareBrowser-DV6kya1H.mjs.map +1 -0
- package/dist/CloudflareBrowser-Doe1M7R5.d.mts +80 -0
- package/dist/CloudflareBrowser.d.mts +2 -0
- package/dist/CloudflareBrowser.mjs +2 -0
- package/dist/CloudflareCodeMode.d.mts +44 -0
- package/dist/CloudflareCodeMode.mjs +616 -0
- package/dist/CloudflareCodeMode.mjs.map +1 -0
- package/dist/CloudflareConfig-f3CqTel1.d.mts +101 -0
- package/dist/CloudflareConfig.d.mts +2 -0
- package/dist/CloudflareConfig.mjs +122 -0
- package/dist/CloudflareConfig.mjs.map +1 -0
- package/dist/CloudflareMemory.d.mts +142 -0
- package/dist/CloudflareMemory.mjs +202 -0
- package/dist/CloudflareMemory.mjs.map +1 -0
- package/dist/CloudflareScheduling.d.mts +52 -0
- package/dist/CloudflareScheduling.mjs +396 -0
- package/dist/CloudflareScheduling.mjs.map +1 -0
- package/dist/CloudflareSubscriptions.d.mts +87 -0
- package/dist/CloudflareSubscriptions.mjs +530 -0
- package/dist/CloudflareSubscriptions.mjs.map +1 -0
- package/dist/CloudflareThreadClient.d.mts +1496 -0
- package/dist/CloudflareThreadClient.mjs +382 -0
- package/dist/CloudflareThreadClient.mjs.map +1 -0
- package/dist/InteractiveBrowser-DUE_m12-.d.mts +161 -0
- package/dist/InteractiveBrowser-DupDtfw2.mjs +1169 -0
- package/dist/InteractiveBrowser-DupDtfw2.mjs.map +1 -0
- package/dist/InteractiveBrowser.d.mts +2 -0
- package/dist/InteractiveBrowser.mjs +2 -0
- package/dist/ProtectedBrowser.d.mts +123 -0
- package/dist/ProtectedBrowser.mjs +1135 -0
- package/dist/ProtectedBrowser.mjs.map +1 -0
- package/dist/ThreadObject-CIbmXZCY.d.mts +812 -0
- package/dist/ThreadObject-DvRG0dDz.mjs +835 -0
- package/dist/ThreadObject-DvRG0dDz.mjs.map +1 -0
- package/dist/ThreadObject.d.mts +2 -0
- package/dist/ThreadObject.mjs +3 -0
- package/dist/WakeScheduler.d.mts +23 -0
- package/dist/WakeScheduler.mjs +57 -0
- package/dist/WakeScheduler.mjs.map +1 -0
- package/dist/boundary-BguazVkh.mjs +42 -0
- package/dist/boundary-BguazVkh.mjs.map +1 -0
- package/dist/index.d.mts +13 -1548
- package/dist/index.mjs +13 -1636
- package/dist/prepared-admission-DU0_T-ev.mjs +73 -0
- package/dist/prepared-admission-DU0_T-ev.mjs.map +1 -0
- package/dist/rolldown-runtime-D7D4PA-g.mjs +13 -0
- package/package.json +1 -53
- package/src/Alarm.ts +973 -0
- package/src/BrowserRestCapture.ts +477 -0
- package/src/BrowserRestCrawl.ts +540 -0
- package/src/CloudflareAiGateway.ts +170 -0
- package/src/CloudflareBindings.ts +199 -0
- package/src/CloudflareBrowser.ts +15 -0
- package/src/{code-mode-executor.ts → CloudflareCodeMode.ts} +372 -204
- package/src/{config.ts → CloudflareConfig.ts} +9 -8
- package/src/CloudflareMemory.ts +420 -0
- package/src/CloudflareScheduling.ts +747 -0
- package/src/CloudflareSubscriptions.ts +1041 -0
- package/src/CloudflareThreadClient.ts +809 -0
- package/src/InteractiveBrowser.ts +2354 -0
- package/src/ProtectedBrowser.ts +9 -0
- package/src/ThreadObject.ts +1179 -0
- package/src/{wake-scheduler.ts → WakeScheduler.ts} +32 -26
- package/src/index.ts +12 -23
- package/src/internal/boundary.ts +55 -0
- package/src/internal/browser-quick-action.ts +857 -0
- package/src/internal/browser-session-lifecycle.ts +160 -0
- package/src/internal/layers.ts +751 -0
- package/src/internal/message-delivery.ts +152 -0
- package/src/internal/prepared-admission.ts +116 -0
- package/src/internal/progress-wait.ts +121 -0
- package/src/internal/transport.ts +44 -0
- package/src/protected-browser/binding.ts +313 -0
- package/src/protected-browser/host.ts +344 -0
- package/src/protected-browser/inspect-frame.ts +125 -0
- package/src/protected-browser/native.ts +445 -0
- package/src/protected-browser/policy.ts +821 -0
- package/dist/index.mjs.map +0 -1
- package/src/alarm.ts +0 -334
- package/src/bindings.ts +0 -145
- package/src/client.ts +0 -646
- package/src/conversation-object.ts +0 -768
- package/src/layers.ts +0 -376
- package/src/transport.ts +0 -38
|
@@ -0,0 +1,616 @@
|
|
|
1
|
+
import { t as __exportAll } from "./rolldown-runtime-D7D4PA-g.mjs";
|
|
2
|
+
import { n as safeCauseDiagnostic, r as safeCauseMessage } from "./boundary-BguazVkh.mjs";
|
|
3
|
+
import { Clock, Duration, Effect, Exit, Fiber, Layer, Option, Queue, Schema } from "effect";
|
|
4
|
+
import { SandboxImplementation } from "effect-agent/sandbox";
|
|
5
|
+
import { RpcTarget } from "cloudflare:workers";
|
|
6
|
+
import { CodeExecutionHost, CodeExecutionProtocolError, CodeExecutionResourceUse, CodeExecutionResult, CodeExecutionTimeoutError, CodeExecutor, CodeExecutorStartError, CodeExecutorTerminatedError, CodeExecutorUnsupportedError, CodeHostCall, CodeHostCallLimitError, CodeHostCallResult, CodeOutputLimitError, CodeProgramFailedError, CodeSourceError } from "effect-agent/code-executor";
|
|
7
|
+
//#region src/CloudflareCodeMode.ts
|
|
8
|
+
var CloudflareCodeMode_exports = /* @__PURE__ */ __exportAll({
|
|
9
|
+
CloudflareCodeMode: () => CloudflareCodeMode,
|
|
10
|
+
disposeRpcHandle: () => disposeRpcHandle,
|
|
11
|
+
dynamicWorkerCodeExecutorLayer: () => dynamicWorkerCodeExecutorLayer,
|
|
12
|
+
dynamicWorkerImplementation: () => dynamicWorkerImplementation
|
|
13
|
+
});
|
|
14
|
+
/**
|
|
15
|
+
* The Cloudflare Dynamic Worker `CodeExecutor` adapter (C4 of ADR-0017;
|
|
16
|
+
* DEPLOY-011). Each pass loads one fresh Worker through the Worker Loader
|
|
17
|
+
* with `globalOutbound: null`, so generated code has no ambient network,
|
|
18
|
+
* bindings, or secrets; its only authority is the pass-scoped RPC target that
|
|
19
|
+
* routes back into the owning event's `CodeExecutionHost` service. Platform
|
|
20
|
+
* CPU limits stop synchronous runaway programs; the executor-owned wall-clock
|
|
21
|
+
* deadline interrupts asynchronously suspended passes. Deployment class `E`
|
|
22
|
+
* only: the adapter records no persistent state and a later pass may run in a
|
|
23
|
+
* completely different isolate.
|
|
24
|
+
*/
|
|
25
|
+
const dynamicWorkerImplementation = SandboxImplementation.make({
|
|
26
|
+
isolation: "isolated",
|
|
27
|
+
identity: "cloudflare-dynamic-worker"
|
|
28
|
+
});
|
|
29
|
+
/**
|
|
30
|
+
* One object-capability endpoint for one execution pass. Workers RPC invokes
|
|
31
|
+
* the target in the request context where it was created, so the native
|
|
32
|
+
* Promise returned by `dispatch` and the Effect fiber that settles it share
|
|
33
|
+
* one I/O owner. Passing the target as `run()`'s argument also scopes the
|
|
34
|
+
* remote stub to that RPC call; no request state lives at module scope.
|
|
35
|
+
*/
|
|
36
|
+
var CodeModePassHostTarget = class extends RpcTarget {
|
|
37
|
+
#dispatch;
|
|
38
|
+
constructor(dispatch) {
|
|
39
|
+
super();
|
|
40
|
+
this.#dispatch = dispatch;
|
|
41
|
+
}
|
|
42
|
+
call(hostCall) {
|
|
43
|
+
return this.#dispatch(hostCall);
|
|
44
|
+
}
|
|
45
|
+
};
|
|
46
|
+
/**
|
|
47
|
+
* The fixed harness loaded as the dynamic worker's main module. The generated
|
|
48
|
+
* source becomes `program.mjs` (`export default (<expression>);`) — a module,
|
|
49
|
+
* never `eval`. The harness installs namespace globals and a bounded console,
|
|
50
|
+
* imports the program, invokes it exactly once, and returns one envelope the
|
|
51
|
+
* host validates through Effect Schema.
|
|
52
|
+
*/
|
|
53
|
+
const HARNESS_MODULE = String.raw`
|
|
54
|
+
import { WorkerEntrypoint } from "cloudflare:workers";
|
|
55
|
+
import programDefault from "./program.js";
|
|
56
|
+
|
|
57
|
+
const encoder = new TextEncoder();
|
|
58
|
+
const utf8 = (text) => encoder.encode(text).byteLength;
|
|
59
|
+
const safeText = (value) => {
|
|
60
|
+
try {
|
|
61
|
+
if (value instanceof Error) return (value.name + ": " + value.message).slice(0, 4000);
|
|
62
|
+
if (typeof value === "string") return value.slice(0, 4000);
|
|
63
|
+
const encoded = JSON.stringify(value);
|
|
64
|
+
return (encoded === undefined ? String(value) : encoded).slice(0, 4000);
|
|
65
|
+
} catch {
|
|
66
|
+
return "[unserializable value]";
|
|
67
|
+
}
|
|
68
|
+
};
|
|
69
|
+
const safeJson = (value) => {
|
|
70
|
+
try {
|
|
71
|
+
const encoded = JSON.stringify(value);
|
|
72
|
+
if (encoded !== undefined && encoded.length <= 4000) return JSON.parse(encoded);
|
|
73
|
+
} catch {}
|
|
74
|
+
return safeText(value);
|
|
75
|
+
};
|
|
76
|
+
|
|
77
|
+
export default class CodeModeHarness extends WorkerEntrypoint {
|
|
78
|
+
async run(host) {
|
|
79
|
+
const config = this.env.CODE_MODE_PASS;
|
|
80
|
+
const limits = config.limits;
|
|
81
|
+
const logs = [];
|
|
82
|
+
let logBytes = 0;
|
|
83
|
+
let fatal;
|
|
84
|
+
const boundedLogs = () => logs.slice(0, 4096);
|
|
85
|
+
const write = (...values) => {
|
|
86
|
+
const joined = values.map(safeText).join(" ");
|
|
87
|
+
const line = joined.length > 16000 ? joined.slice(0, 15999) + "…" : joined;
|
|
88
|
+
const bytes = utf8(line);
|
|
89
|
+
if (logs.length >= 4096 || logBytes + bytes > limits.maxLogBytes) {
|
|
90
|
+
fatal = fatal ?? { _tag: "log-limit", observed: logBytes + bytes, logs: boundedLogs() };
|
|
91
|
+
throw new Error("code-mode log limit exceeded");
|
|
92
|
+
}
|
|
93
|
+
logs.push(line);
|
|
94
|
+
logBytes += bytes;
|
|
95
|
+
};
|
|
96
|
+
globalThis.console = { log: write, info: write, warn: write, error: write, debug: write };
|
|
97
|
+
|
|
98
|
+
let hostCalls = 0;
|
|
99
|
+
const makeMethod = (namespace, method) => async (argument) => {
|
|
100
|
+
hostCalls += 1;
|
|
101
|
+
if (hostCalls > limits.maxHostCalls) {
|
|
102
|
+
fatal = fatal ?? { _tag: "host-call-limit", logs: boundedLogs() };
|
|
103
|
+
throw new Error("code-mode host-call limit exceeded");
|
|
104
|
+
}
|
|
105
|
+
let argText;
|
|
106
|
+
try {
|
|
107
|
+
argText = JSON.stringify(argument);
|
|
108
|
+
} catch {}
|
|
109
|
+
if (argText === undefined || utf8(argText) > limits.maxHostCallArgumentBytes) {
|
|
110
|
+
fatal = fatal ?? {
|
|
111
|
+
_tag: "argument-limit",
|
|
112
|
+
observed: argText === undefined ? 0 : utf8(argText),
|
|
113
|
+
logs: boundedLogs(),
|
|
114
|
+
};
|
|
115
|
+
throw new Error("code-mode host-call argument limit exceeded");
|
|
116
|
+
}
|
|
117
|
+
const outcome = await host.call({
|
|
118
|
+
namespace,
|
|
119
|
+
method,
|
|
120
|
+
argument: JSON.parse(argText),
|
|
121
|
+
});
|
|
122
|
+
if (outcome !== null && typeof outcome === "object" && outcome._tag === "CodeHostCallSuccess") {
|
|
123
|
+
return outcome.value;
|
|
124
|
+
}
|
|
125
|
+
if (outcome !== null && typeof outcome === "object" && outcome._tag === "CodeHostCallFailure") {
|
|
126
|
+
throw outcome.error;
|
|
127
|
+
}
|
|
128
|
+
fatal = fatal ?? { _tag: "protocol", message: "host returned an unrecognized outcome" };
|
|
129
|
+
throw new Error("code-mode host protocol violation");
|
|
130
|
+
};
|
|
131
|
+
for (const namespace of config.namespaces) {
|
|
132
|
+
const methods = {};
|
|
133
|
+
for (const method of namespace.methods) {
|
|
134
|
+
methods[method] = makeMethod(namespace.name, method);
|
|
135
|
+
}
|
|
136
|
+
globalThis[namespace.name] = methods;
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
// program.js is imported statically at the top of this module, so a
|
|
140
|
+
// syntactically invalid program fails the whole harness at load (mapped
|
|
141
|
+
// to a source error by the host). Using a static import keeps this module
|
|
142
|
+
// free of dynamic-import expressions, which single-script Miniflare hosts
|
|
143
|
+
// reject. The isolation boundary does NOT depend on the ordering of this
|
|
144
|
+
// import versus the console/namespace shims installed below: the loaded
|
|
145
|
+
// Worker has globalOutbound: null and no bindings, secrets, or env from
|
|
146
|
+
// the Worker Loader config BEFORE any module in the graph evaluates, so
|
|
147
|
+
// module-level program code has no ambient authority regardless. The
|
|
148
|
+
// shims below are usability wrappers (bounded console, namespace globals),
|
|
149
|
+
// and the accepted program is a single async-function expression whose
|
|
150
|
+
// body runs only when invoked here — after the shims exist.
|
|
151
|
+
const program = programDefault;
|
|
152
|
+
if (typeof program !== "function") {
|
|
153
|
+
return { _tag: "source-not-a-function", actual: typeof program };
|
|
154
|
+
}
|
|
155
|
+
try {
|
|
156
|
+
const value = await program();
|
|
157
|
+
if (fatal !== undefined) return fatal;
|
|
158
|
+
let text;
|
|
159
|
+
try {
|
|
160
|
+
text = JSON.stringify(value);
|
|
161
|
+
} catch {}
|
|
162
|
+
if (text === undefined) {
|
|
163
|
+
return {
|
|
164
|
+
_tag: "program-failed",
|
|
165
|
+
reason: "non-json-result",
|
|
166
|
+
thrown: null,
|
|
167
|
+
message: "The program must return a JSON value",
|
|
168
|
+
logs: boundedLogs(),
|
|
169
|
+
};
|
|
170
|
+
}
|
|
171
|
+
const resultBytes = utf8(text);
|
|
172
|
+
if (resultBytes > limits.maxResultBytes) {
|
|
173
|
+
return { _tag: "result-limit", observed: resultBytes, logs: boundedLogs() };
|
|
174
|
+
}
|
|
175
|
+
return {
|
|
176
|
+
_tag: "completed",
|
|
177
|
+
value: JSON.parse(text),
|
|
178
|
+
logs: boundedLogs(),
|
|
179
|
+
hostCalls,
|
|
180
|
+
logBytes,
|
|
181
|
+
resultBytes,
|
|
182
|
+
};
|
|
183
|
+
} catch (cause) {
|
|
184
|
+
if (fatal !== undefined) return fatal;
|
|
185
|
+
return {
|
|
186
|
+
_tag: "program-failed",
|
|
187
|
+
reason: cause instanceof Error ? "threw" : "rejected",
|
|
188
|
+
thrown: safeJson(cause),
|
|
189
|
+
message: safeText(cause),
|
|
190
|
+
logs: boundedLogs(),
|
|
191
|
+
};
|
|
192
|
+
}
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
`;
|
|
196
|
+
const BoundedLogs = Schema.Array(Schema.String.check(Schema.isMaxLength(16384))).check(Schema.isMaxLength(4096));
|
|
197
|
+
const HarnessCompleted = Schema.TaggedStruct("completed", {
|
|
198
|
+
value: Schema.Json,
|
|
199
|
+
logs: BoundedLogs,
|
|
200
|
+
hostCalls: Schema.Natural,
|
|
201
|
+
logBytes: Schema.Natural,
|
|
202
|
+
resultBytes: Schema.Natural
|
|
203
|
+
});
|
|
204
|
+
const HarnessNotAFunction = Schema.TaggedStruct("source-not-a-function", { actual: Schema.String });
|
|
205
|
+
const HarnessProgramFailed = Schema.TaggedStruct("program-failed", {
|
|
206
|
+
reason: Schema.Literals([
|
|
207
|
+
"threw",
|
|
208
|
+
"rejected",
|
|
209
|
+
"non-json-result"
|
|
210
|
+
]),
|
|
211
|
+
thrown: Schema.Json,
|
|
212
|
+
message: Schema.String,
|
|
213
|
+
logs: BoundedLogs
|
|
214
|
+
});
|
|
215
|
+
const HarnessLogLimit = Schema.TaggedStruct("log-limit", {
|
|
216
|
+
observed: Schema.Natural,
|
|
217
|
+
logs: BoundedLogs
|
|
218
|
+
});
|
|
219
|
+
const HarnessArgumentLimit = Schema.TaggedStruct("argument-limit", {
|
|
220
|
+
observed: Schema.Natural,
|
|
221
|
+
logs: BoundedLogs
|
|
222
|
+
});
|
|
223
|
+
const HarnessResultLimit = Schema.TaggedStruct("result-limit", {
|
|
224
|
+
observed: Schema.Natural,
|
|
225
|
+
logs: BoundedLogs
|
|
226
|
+
});
|
|
227
|
+
const HarnessHostCallLimit = Schema.TaggedStruct("host-call-limit", { logs: BoundedLogs });
|
|
228
|
+
const HarnessProtocol = Schema.TaggedStruct("protocol", { message: Schema.String });
|
|
229
|
+
const HarnessOutcome = Schema.Union([
|
|
230
|
+
HarnessCompleted,
|
|
231
|
+
HarnessNotAFunction,
|
|
232
|
+
HarnessProgramFailed,
|
|
233
|
+
HarnessLogLimit,
|
|
234
|
+
HarnessArgumentLimit,
|
|
235
|
+
HarnessResultLimit,
|
|
236
|
+
HarnessHostCallLimit,
|
|
237
|
+
HarnessProtocol
|
|
238
|
+
]);
|
|
239
|
+
const HarnessPassConfig = Schema.Struct({
|
|
240
|
+
namespaces: Schema.Array(Schema.Struct({
|
|
241
|
+
name: Schema.NonEmptyString,
|
|
242
|
+
methods: Schema.Array(Schema.NonEmptyString).check(Schema.isMaxLength(64))
|
|
243
|
+
})).check(Schema.isMaxLength(32)),
|
|
244
|
+
limits: Schema.Struct({
|
|
245
|
+
maxLogBytes: Schema.Natural,
|
|
246
|
+
maxResultBytes: Schema.Natural,
|
|
247
|
+
maxHostCalls: Schema.Natural,
|
|
248
|
+
maxHostCallArgumentBytes: Schema.Natural
|
|
249
|
+
})
|
|
250
|
+
});
|
|
251
|
+
const encodeHarnessPassConfig = Schema.encodeSync(HarnessPassConfig);
|
|
252
|
+
const encodeJsonPayload = Schema.encodeSync(Schema.fromJsonString(Schema.Json));
|
|
253
|
+
const decodeJsonPayload = Schema.decodeUnknownOption(Schema.fromJsonString(Schema.Json));
|
|
254
|
+
const decodeHarnessOutcome = (value) => {
|
|
255
|
+
try {
|
|
256
|
+
return Schema.decodeUnknownOption(HarnessOutcome)(value);
|
|
257
|
+
} catch {
|
|
258
|
+
return Option.none();
|
|
259
|
+
}
|
|
260
|
+
};
|
|
261
|
+
const decodeHostCall = (value) => {
|
|
262
|
+
try {
|
|
263
|
+
return Schema.decodeUnknownOption(CodeHostCall)(value);
|
|
264
|
+
} catch {
|
|
265
|
+
return Option.none();
|
|
266
|
+
}
|
|
267
|
+
};
|
|
268
|
+
const decodeHostCallResult = (value) => {
|
|
269
|
+
try {
|
|
270
|
+
return Schema.decodeUnknownOption(CodeHostCallResult)(value);
|
|
271
|
+
} catch {
|
|
272
|
+
return Option.none();
|
|
273
|
+
}
|
|
274
|
+
};
|
|
275
|
+
/** Dispose a Cloudflare RPC handle when the runtime supplies its untyped disposal hook. @internal */
|
|
276
|
+
const disposeRpcHandle = (handle) => Effect.try({
|
|
277
|
+
try: () => {
|
|
278
|
+
if (typeof handle !== "object" && typeof handle !== "function" || handle === null) return;
|
|
279
|
+
if (!(Symbol.dispose in handle)) return;
|
|
280
|
+
const dispose = Reflect.get(handle, Symbol.dispose);
|
|
281
|
+
if (typeof dispose === "function") Reflect.apply(dispose, handle, []);
|
|
282
|
+
},
|
|
283
|
+
catch: (cause) => safeCauseDiagnostic(cause, "The Cloudflare RPC disposal hook failed without a diagnostic")
|
|
284
|
+
}).pipe(Effect.catch((diagnostic) => Effect.logWarning(`Cloudflare RPC handle disposal failed: ${diagnostic}`).pipe(Effect.ignoreCause)));
|
|
285
|
+
const encodeHostResultPayload = (outcome) => {
|
|
286
|
+
try {
|
|
287
|
+
const payload = outcome._tag === "CodeHostCallSuccess" ? outcome.value : outcome.error;
|
|
288
|
+
const encodedPayload = encodeJsonPayload(payload);
|
|
289
|
+
return {
|
|
290
|
+
encodedPayload,
|
|
291
|
+
resultBytes: utf8ByteLength(encodedPayload)
|
|
292
|
+
};
|
|
293
|
+
} catch {
|
|
294
|
+
return;
|
|
295
|
+
}
|
|
296
|
+
};
|
|
297
|
+
const utf8ByteLength = (value) => {
|
|
298
|
+
let total = 0;
|
|
299
|
+
for (const character of value) {
|
|
300
|
+
const codePoint = character.codePointAt(0) ?? 0;
|
|
301
|
+
total += codePoint <= 127 ? 1 : codePoint <= 2047 ? 2 : codePoint <= 65535 ? 3 : 4;
|
|
302
|
+
}
|
|
303
|
+
return total;
|
|
304
|
+
};
|
|
305
|
+
/** Reserved global names the harness owns inside the dynamic worker. */
|
|
306
|
+
const reservedHarnessGlobals = /* @__PURE__ */ new Set(["console"]);
|
|
307
|
+
const makeExecute = (options, clock) => Effect.fn("DynamicWorkerCodeExecutor.execute")(function* (request) {
|
|
308
|
+
if (request.network._tag !== "NetworkDisabled") return yield* CodeExecutorUnsupportedError.make({
|
|
309
|
+
implementation: dynamicWorkerImplementation,
|
|
310
|
+
feature: "network",
|
|
311
|
+
message: "The Dynamic Worker executor denies all egress with globalOutbound: null; an allowlist is not supported in the first slice"
|
|
312
|
+
});
|
|
313
|
+
const sourceBytes = utf8ByteLength(request.source);
|
|
314
|
+
if (sourceBytes > request.limits.maxSourceBytes) return yield* CodeSourceError.make({
|
|
315
|
+
implementation: dynamicWorkerImplementation,
|
|
316
|
+
reason: "oversized",
|
|
317
|
+
message: `Source is ${sourceBytes} bytes; the request allows ${request.limits.maxSourceBytes}`
|
|
318
|
+
});
|
|
319
|
+
for (const namespace of request.namespaces) if (reservedHarnessGlobals.has(namespace.name)) return yield* CodeExecutorUnsupportedError.make({
|
|
320
|
+
implementation: dynamicWorkerImplementation,
|
|
321
|
+
feature: "namespaces",
|
|
322
|
+
message: `Namespace ${namespace.name} collides with a harness binding`
|
|
323
|
+
});
|
|
324
|
+
const host = yield* CodeExecutionHost;
|
|
325
|
+
const startedAt = clock.monotonicTimeNanosUnsafe();
|
|
326
|
+
const passDeadline = startedAt + Duration.toNanosUnsafe(request.limits.maxWallTime);
|
|
327
|
+
const remainingPassWallTime = () => {
|
|
328
|
+
const now = clock.monotonicTimeNanosUnsafe();
|
|
329
|
+
return Duration.nanos(passDeadline > now ? passDeadline - now : 0n);
|
|
330
|
+
};
|
|
331
|
+
let issuedHostCalls = 0;
|
|
332
|
+
let passOpen = true;
|
|
333
|
+
const queuedHostCalls = [];
|
|
334
|
+
let passFailure;
|
|
335
|
+
const failPass = (error) => {
|
|
336
|
+
if (passFailure === void 0) passFailure = error;
|
|
337
|
+
};
|
|
338
|
+
const rejectQueuedHostCalls = (reason) => {
|
|
339
|
+
for (const queued of queuedHostCalls.splice(0)) queued.reject(reason);
|
|
340
|
+
};
|
|
341
|
+
const queue = yield* Queue.unbounded();
|
|
342
|
+
const deliverHostOutcome = (queued, outcome) => Effect.gen(function* () {
|
|
343
|
+
const decoded = decodeHostCallResult(outcome);
|
|
344
|
+
if (Option.isNone(decoded)) {
|
|
345
|
+
const error = CodeExecutionProtocolError.make({
|
|
346
|
+
implementation: dynamicWorkerImplementation,
|
|
347
|
+
message: "The execution host returned a value outside the CodeHostCallResult schema"
|
|
348
|
+
});
|
|
349
|
+
failPass(error);
|
|
350
|
+
return yield* error;
|
|
351
|
+
}
|
|
352
|
+
const encoded = encodeHostResultPayload(decoded.value);
|
|
353
|
+
if (encoded === void 0 || encoded.resultBytes > request.limits.maxHostCallResultBytes) {
|
|
354
|
+
const error = CodeOutputLimitError.make({
|
|
355
|
+
implementation: dynamicWorkerImplementation,
|
|
356
|
+
surface: "host-call-result",
|
|
357
|
+
limit: request.limits.maxHostCallResultBytes,
|
|
358
|
+
observed: encoded?.resultBytes ?? 0,
|
|
359
|
+
logs: []
|
|
360
|
+
});
|
|
361
|
+
failPass(error);
|
|
362
|
+
return yield* error;
|
|
363
|
+
}
|
|
364
|
+
const normalizedPayload = decodeJsonPayload(encoded.encodedPayload);
|
|
365
|
+
if (Option.isNone(normalizedPayload)) {
|
|
366
|
+
const error = CodeExecutionProtocolError.make({
|
|
367
|
+
implementation: dynamicWorkerImplementation,
|
|
368
|
+
message: "The execution host returned a result that could not cross the JSON boundary"
|
|
369
|
+
});
|
|
370
|
+
failPass(error);
|
|
371
|
+
return yield* error;
|
|
372
|
+
}
|
|
373
|
+
queued.resolve(decoded.value._tag === "CodeHostCallSuccess" ? {
|
|
374
|
+
_tag: "CodeHostCallSuccess",
|
|
375
|
+
value: normalizedPayload.value
|
|
376
|
+
} : {
|
|
377
|
+
_tag: "CodeHostCallFailure",
|
|
378
|
+
error: normalizedPayload.value
|
|
379
|
+
});
|
|
380
|
+
});
|
|
381
|
+
const serveHostCalls = Effect.gen(function* () {
|
|
382
|
+
while (true) {
|
|
383
|
+
const work = yield* Queue.take(queue);
|
|
384
|
+
if (work._tag === "limit") {
|
|
385
|
+
const error = CodeHostCallLimitError.make({
|
|
386
|
+
implementation: dynamicWorkerImplementation,
|
|
387
|
+
limit: request.limits.maxHostCalls,
|
|
388
|
+
logs: []
|
|
389
|
+
});
|
|
390
|
+
failPass(error);
|
|
391
|
+
return yield* error;
|
|
392
|
+
}
|
|
393
|
+
const queued = work.queued;
|
|
394
|
+
yield* host.call(queued.call).pipe(Effect.timeoutOrElse({
|
|
395
|
+
duration: remainingPassWallTime(),
|
|
396
|
+
orElse: () => {
|
|
397
|
+
const error = CodeExecutionTimeoutError.make({
|
|
398
|
+
implementation: dynamicWorkerImplementation,
|
|
399
|
+
kind: "wall-clock",
|
|
400
|
+
maxWallTime: request.limits.maxWallTime,
|
|
401
|
+
logs: []
|
|
402
|
+
});
|
|
403
|
+
failPass(error);
|
|
404
|
+
return error;
|
|
405
|
+
}
|
|
406
|
+
}), Effect.flatMap((outcome) => deliverHostOutcome(queued, outcome)), Effect.tapError(() => Effect.sync(() => queued.reject(/* @__PURE__ */ new Error("Code Mode host call failed")))), Effect.onInterrupt(() => Effect.sync(() => queued.reject(/* @__PURE__ */ new Error("Code Mode pass is closing")))));
|
|
407
|
+
}
|
|
408
|
+
});
|
|
409
|
+
const concurrency = request.limits.maxHostCallConcurrency ?? 4;
|
|
410
|
+
const server = yield* Effect.all(Array.from({ length: concurrency }, () => serveHostCalls), {
|
|
411
|
+
concurrency,
|
|
412
|
+
discard: true
|
|
413
|
+
}).pipe(Effect.andThen(Effect.never), Effect.forkScoped);
|
|
414
|
+
const dispatch = (hostCall) => {
|
|
415
|
+
if (!passOpen) return Promise.reject(/* @__PURE__ */ new Error("Code Mode pass is closing"));
|
|
416
|
+
issuedHostCalls += 1;
|
|
417
|
+
if (issuedHostCalls > request.limits.maxHostCalls) {
|
|
418
|
+
const error = CodeHostCallLimitError.make({
|
|
419
|
+
implementation: dynamicWorkerImplementation,
|
|
420
|
+
limit: request.limits.maxHostCalls,
|
|
421
|
+
logs: []
|
|
422
|
+
});
|
|
423
|
+
failPass(error);
|
|
424
|
+
Queue.offerUnsafe(queue, { _tag: "limit" });
|
|
425
|
+
return Promise.reject(/* @__PURE__ */ new Error("host-call limit exceeded"));
|
|
426
|
+
}
|
|
427
|
+
const decoded = decodeHostCall(hostCall);
|
|
428
|
+
if (Option.isNone(decoded)) return Promise.reject(/* @__PURE__ */ new TypeError("host calls must match the CodeHostCall schema"));
|
|
429
|
+
return new Promise((resolve, reject) => {
|
|
430
|
+
const queued = {
|
|
431
|
+
call: decoded.value,
|
|
432
|
+
resolve,
|
|
433
|
+
reject
|
|
434
|
+
};
|
|
435
|
+
queuedHostCalls.push(queued);
|
|
436
|
+
Queue.offerUnsafe(queue, {
|
|
437
|
+
_tag: "call",
|
|
438
|
+
queued
|
|
439
|
+
});
|
|
440
|
+
});
|
|
441
|
+
};
|
|
442
|
+
const closeAdmission = Effect.sync(() => {
|
|
443
|
+
passOpen = false;
|
|
444
|
+
rejectQueuedHostCalls(/* @__PURE__ */ new Error("Code Mode pass is closing"));
|
|
445
|
+
});
|
|
446
|
+
yield* Effect.addFinalizer(() => closeAdmission.pipe(Effect.andThen(Fiber.interrupt(server))));
|
|
447
|
+
const workerCode = {
|
|
448
|
+
compatibilityDate: options.compatibilityDate ?? "2025-05-01",
|
|
449
|
+
mainModule: "harness.js",
|
|
450
|
+
modules: {
|
|
451
|
+
"harness.js": HARNESS_MODULE,
|
|
452
|
+
"program.js": `export default (\n${request.source}\n);`
|
|
453
|
+
},
|
|
454
|
+
env: { CODE_MODE_PASS: encodeHarnessPassConfig({
|
|
455
|
+
namespaces: request.namespaces.map((namespace) => ({
|
|
456
|
+
name: namespace.name,
|
|
457
|
+
methods: namespace.methods
|
|
458
|
+
})),
|
|
459
|
+
limits: {
|
|
460
|
+
maxLogBytes: request.limits.maxLogBytes,
|
|
461
|
+
maxResultBytes: request.limits.maxResultBytes,
|
|
462
|
+
maxHostCalls: request.limits.maxHostCalls,
|
|
463
|
+
maxHostCallArgumentBytes: request.limits.maxHostCallArgumentBytes
|
|
464
|
+
}
|
|
465
|
+
}) },
|
|
466
|
+
globalOutbound: null,
|
|
467
|
+
...request.limits.cpuMillis === void 0 ? {} : { limits: {
|
|
468
|
+
cpuMs: request.limits.cpuMillis,
|
|
469
|
+
subRequests: request.limits.maxHostCalls + 8
|
|
470
|
+
} }
|
|
471
|
+
};
|
|
472
|
+
const worker = yield* Effect.acquireRelease(Effect.try({
|
|
473
|
+
try: () => options.loader.load(workerCode),
|
|
474
|
+
catch: (cause) => {
|
|
475
|
+
const text = safeCauseMessage(cause, "The Worker Loader failed without a diagnostic");
|
|
476
|
+
if (/syntaxerror|failed to (compile|parse)/i.test(text)) return CodeSourceError.make({
|
|
477
|
+
implementation: dynamicWorkerImplementation,
|
|
478
|
+
reason: "invalid",
|
|
479
|
+
message: text.slice(0, 8e3)
|
|
480
|
+
});
|
|
481
|
+
return CodeExecutorStartError.make({
|
|
482
|
+
implementation: dynamicWorkerImplementation,
|
|
483
|
+
message: `The Worker Loader rejected the pass: ${text}`.slice(0, 8e3),
|
|
484
|
+
cause
|
|
485
|
+
});
|
|
486
|
+
}
|
|
487
|
+
}), disposeRpcHandle);
|
|
488
|
+
const entrypoint = yield* Effect.acquireRelease(Effect.try({
|
|
489
|
+
try: () => worker.getEntrypoint(),
|
|
490
|
+
catch: (cause) => classifyWorkerFailure(cause, request.limits.maxWallTime)
|
|
491
|
+
}), disposeRpcHandle);
|
|
492
|
+
const rpc = Effect.tryPromise({
|
|
493
|
+
try: () => entrypoint.run(new CodeModePassHostTarget(dispatch)),
|
|
494
|
+
catch: (cause) => classifyWorkerFailure(cause, request.limits.maxWallTime)
|
|
495
|
+
});
|
|
496
|
+
const exit = yield* Effect.raceFirst(rpc.pipe(Effect.timeoutOrElse({
|
|
497
|
+
duration: remainingPassWallTime(),
|
|
498
|
+
orElse: () => CodeExecutionTimeoutError.make({
|
|
499
|
+
implementation: dynamicWorkerImplementation,
|
|
500
|
+
kind: "wall-clock",
|
|
501
|
+
maxWallTime: request.limits.maxWallTime,
|
|
502
|
+
logs: []
|
|
503
|
+
})
|
|
504
|
+
})), Fiber.join(server)).pipe(Effect.exit);
|
|
505
|
+
yield* closeAdmission;
|
|
506
|
+
yield* Fiber.interrupt(server);
|
|
507
|
+
if (passFailure !== void 0) return yield* passFailure;
|
|
508
|
+
if (Exit.isFailure(exit)) return yield* Effect.failCause(exit.cause);
|
|
509
|
+
const raw = exit.value;
|
|
510
|
+
const finishedAt = clock.monotonicTimeNanosUnsafe();
|
|
511
|
+
const outcome = decodeHarnessOutcome(raw);
|
|
512
|
+
if (Option.isNone(outcome)) return yield* CodeExecutionProtocolError.make({
|
|
513
|
+
implementation: dynamicWorkerImplementation,
|
|
514
|
+
message: "The dynamic worker returned a value outside the harness envelope schema"
|
|
515
|
+
});
|
|
516
|
+
switch (outcome.value._tag) {
|
|
517
|
+
case "completed": return CodeExecutionResult.make({
|
|
518
|
+
implementation: dynamicWorkerImplementation,
|
|
519
|
+
value: outcome.value.value,
|
|
520
|
+
logs: outcome.value.logs,
|
|
521
|
+
resourceUse: CodeExecutionResourceUse.make({
|
|
522
|
+
wallTime: Duration.nanos(finishedAt > startedAt ? finishedAt - startedAt : 0n),
|
|
523
|
+
hostCalls: outcome.value.hostCalls,
|
|
524
|
+
logBytes: outcome.value.logBytes,
|
|
525
|
+
resultBytes: outcome.value.resultBytes
|
|
526
|
+
})
|
|
527
|
+
});
|
|
528
|
+
case "source-not-a-function": return yield* CodeSourceError.make({
|
|
529
|
+
implementation: dynamicWorkerImplementation,
|
|
530
|
+
reason: "not-a-function",
|
|
531
|
+
message: `The source expression evaluated to ${outcome.value.actual}; it must evaluate to one async function`
|
|
532
|
+
});
|
|
533
|
+
case "program-failed": return yield* CodeProgramFailedError.make({
|
|
534
|
+
implementation: dynamicWorkerImplementation,
|
|
535
|
+
reason: outcome.value.reason,
|
|
536
|
+
thrown: outcome.value.thrown,
|
|
537
|
+
message: outcome.value.message.slice(0, 8e3),
|
|
538
|
+
logs: outcome.value.logs
|
|
539
|
+
});
|
|
540
|
+
case "log-limit": return yield* CodeOutputLimitError.make({
|
|
541
|
+
implementation: dynamicWorkerImplementation,
|
|
542
|
+
surface: "logs",
|
|
543
|
+
limit: request.limits.maxLogBytes,
|
|
544
|
+
observed: outcome.value.observed,
|
|
545
|
+
logs: outcome.value.logs
|
|
546
|
+
});
|
|
547
|
+
case "argument-limit": return yield* CodeOutputLimitError.make({
|
|
548
|
+
implementation: dynamicWorkerImplementation,
|
|
549
|
+
surface: "host-call-argument",
|
|
550
|
+
limit: request.limits.maxHostCallArgumentBytes,
|
|
551
|
+
observed: outcome.value.observed,
|
|
552
|
+
logs: outcome.value.logs
|
|
553
|
+
});
|
|
554
|
+
case "result-limit": return yield* CodeOutputLimitError.make({
|
|
555
|
+
implementation: dynamicWorkerImplementation,
|
|
556
|
+
surface: "result",
|
|
557
|
+
limit: request.limits.maxResultBytes,
|
|
558
|
+
observed: outcome.value.observed,
|
|
559
|
+
logs: outcome.value.logs
|
|
560
|
+
});
|
|
561
|
+
case "host-call-limit": return yield* CodeHostCallLimitError.make({
|
|
562
|
+
implementation: dynamicWorkerImplementation,
|
|
563
|
+
limit: request.limits.maxHostCalls,
|
|
564
|
+
logs: outcome.value.logs
|
|
565
|
+
});
|
|
566
|
+
case "protocol": return yield* CodeExecutionProtocolError.make({
|
|
567
|
+
implementation: dynamicWorkerImplementation,
|
|
568
|
+
message: outcome.value.message.slice(0, 8e3)
|
|
569
|
+
});
|
|
570
|
+
}
|
|
571
|
+
});
|
|
572
|
+
/**
|
|
573
|
+
* Expected worker-level failures map into the typed union with bounded
|
|
574
|
+
* diagnostics; anything unrecognized stays a start/termination error rather
|
|
575
|
+
* than a fabricated program result.
|
|
576
|
+
*/
|
|
577
|
+
const classifyWorkerFailure = (cause, maxWallTime) => {
|
|
578
|
+
const text = safeCauseDiagnostic(cause, "[unserializable worker failure]");
|
|
579
|
+
if (/syntaxerror|failed to (compile|parse)/i.test(text)) return CodeSourceError.make({
|
|
580
|
+
implementation: dynamicWorkerImplementation,
|
|
581
|
+
reason: "invalid",
|
|
582
|
+
message: text.slice(0, 8e3)
|
|
583
|
+
});
|
|
584
|
+
if (/cpu/i.test(text)) return CodeExecutionTimeoutError.make({
|
|
585
|
+
implementation: dynamicWorkerImplementation,
|
|
586
|
+
kind: "cpu",
|
|
587
|
+
maxWallTime,
|
|
588
|
+
logs: []
|
|
589
|
+
});
|
|
590
|
+
if (/failed to start worker/i.test(text)) return CodeExecutorStartError.make({
|
|
591
|
+
implementation: dynamicWorkerImplementation,
|
|
592
|
+
message: text.slice(0, 8e3),
|
|
593
|
+
cause
|
|
594
|
+
});
|
|
595
|
+
return CodeExecutorTerminatedError.make({
|
|
596
|
+
implementation: dynamicWorkerImplementation,
|
|
597
|
+
message: text.slice(0, 8e3)
|
|
598
|
+
});
|
|
599
|
+
};
|
|
600
|
+
/** Layer building the Dynamic Worker `CodeExecutor` from resolved bindings. */
|
|
601
|
+
const dynamicWorkerCodeExecutorLayer = (options) => Layer.effect(CodeExecutor, Effect.gen(function* () {
|
|
602
|
+
const clock = yield* Clock.Clock;
|
|
603
|
+
return CodeExecutor.of({ execute: makeExecute(options, clock) });
|
|
604
|
+
}));
|
|
605
|
+
/** Assemble Code Mode handlers with the isolated Dynamic Worker executor. */
|
|
606
|
+
const CloudflareCodeMode = {
|
|
607
|
+
/**
|
|
608
|
+
* Provide the selected tool handlers at construction, where Code Mode captures them.
|
|
609
|
+
* Their errors and remaining dependencies stay visible. The definition still owns the
|
|
610
|
+
* allowlist and limits; the runtime supplies the live Tool broker for each scoped pass.
|
|
611
|
+
*/
|
|
612
|
+
layer: (definition, options) => definition.handlers.pipe(Layer.provide(options.handlers), Layer.provide(dynamicWorkerCodeExecutorLayer(options))) };
|
|
613
|
+
//#endregion
|
|
614
|
+
export { CloudflareCodeMode, disposeRpcHandle, dynamicWorkerCodeExecutorLayer, dynamicWorkerImplementation, CloudflareCodeMode_exports as t };
|
|
615
|
+
|
|
616
|
+
//# sourceMappingURL=CloudflareCodeMode.mjs.map
|