History

feat: implement cloudflare worker-loader runtime adapter

Use Effect HTTP and Schema boundaries for dispatch/response handling, and verify cross-worker behavior with Wrangler-backed real worker integration tests instead of mock-only coverage.

Rhys Sullivan committed 8 months ago

Commit 889122f1 parent fe2d7fc6 files · +758 −11Browse files

Unified · Split

6 files
  1. v2/bun.lock +108 −0
  2. v2/packages/runtime-cloudflare-worker-loader/package.json +7 −0
  3. v2/packages/runtime-cloudflare-worker-loader/src/index.integration.test.ts +228 −0
  4. v2/packages/runtime-cloudflare-worker-loader/src/index.ts +291 −11
  5. v2/packages/runtime-cloudflare-worker-loader/test-worker/src/index.ts +118 −0
  6. v2/packages/runtime-cloudflare-worker-loader/test-worker/wrangler.jsonc +6 −0
v2/bun.lock+108−0
@@ -184,9 +184,15 @@ · 183 unmodified lines
184184 "packages/runtime-cloudflare-worker-loader": {
185185 "name": "@executor-v2/runtime-cloudflare-worker-loader",
186186 "dependencies": {
187+ "@effect/platform": "catalog:",
187188 "@executor-v2/engine": "workspace:*",
188189 "effect": "catalog:",
189190 },
191+ "devDependencies": {
192+ "@effect/vitest": "catalog:",
193+ "vitest": "catalog:",
194+ "wrangler": "^4.0.0",
195+ },
190196 },
191197 "packages/runtime-deno-subprocess": {
192198 "name": "@executor-v2/runtime-deno-subprocess",
@@ -314,6 +320,22 @@ · 121 unmodified lines
314320
315321 "@capsizecss/unpack": ["@capsizecss/unpack@4.0.0", "", { "dependencies": { "fontkitten": "^1.0.0" } }, "sha512-VERIM64vtTP1C4mxQ5thVT9fK0apjPFobqybMtA1UdUujWka24ERHbRHFGmpbbhp73MhV+KSsHQH9C6uOTdEQA=="],
316322
323+ "@cloudflare/kv-asset-handler": ["@cloudflare/kv-asset-handler@0.4.2", "", {}, "sha512-SIOD2DxrRRwQ+jgzlXCqoEFiKOFqaPjhnNTGKXSRLvp1HiOvapLaFG2kEr9dYQTYe8rKrd9uvDUzmAITeNyaHQ=="],
324+
325+ "@cloudflare/unenv-preset": ["@cloudflare/unenv-preset@2.14.0", "", { "peerDependencies": { "unenv": "2.0.0-rc.24", "workerd": "^1.20260218.0" }, "optionalPeers": ["workerd"] }, "sha512-XKAkWhi1nBdNsSEoNG9nkcbyvfUrSjSf+VYVPfOto3gLTZVc3F4g6RASCMh6IixBKCG2yDgZKQIHGKtjcnLnKg=="],
326+
327+ "@cloudflare/workerd-darwin-64": ["@cloudflare/workerd-darwin-64@1.20260305.0", "", { "os": "darwin", "cpu": "x64" }, "sha512-chhKOpymo0Eh9J3nymrauMqKGboCc4uz/j0gA1G4gioMnKsN2ZDKJ+qjRZDnCoVGy8u2C4pxlmyIfsXCAfIzhQ=="],
328+
329+ "@cloudflare/workerd-darwin-arm64": ["@cloudflare/workerd-darwin-arm64@1.20260305.0", "", { "os": "darwin", "cpu": "arm64" }, "sha512-K9aG2OQk5bBfOP+fyGPqLcqZ9OR3ra6uwnxJ8f2mveq2A2LsCI7ZeGxQiAj75Ti80ytH/gJffZIx4Np2JtU3aQ=="],
330+
331+ "@cloudflare/workerd-linux-64": ["@cloudflare/workerd-linux-64@1.20260305.0", "", { "os": "linux", "cpu": "x64" }, "sha512-tt7XUoIw/cYFeGbkPkcZ6XX1aZm26Aju/4ih+DXxOosbBeGshFSrNJDBfAKKOvkjsAZymJ+WWVDBU+hmNaGfwA=="],
332+
333+ "@cloudflare/workerd-linux-arm64": ["@cloudflare/workerd-linux-arm64@1.20260305.0", "", { "os": "linux", "cpu": "arm64" }, "sha512-72QTkY5EzylmvCZ8ZTrnJ9DctmQsfSof1OKyOWqu/pv/B2yACfuPMikq8RpPxvVu7hhS0ztGP6ZvXz72Htq4Zg=="],
334+
335+ "@cloudflare/workerd-windows-64": ["@cloudflare/workerd-windows-64@1.20260305.0", "", { "os": "win32", "cpu": "x64" }, "sha512-BA0uaQPOaI2F6mJtBDqplGnQQhpXCzwEMI33p/TnDxtSk9u8CGIfBFuI6uqo8mJ6ijIaPjeBLGOn2CiRMET4qg=="],
336+
337+ "@cspotcode/source-map-support": ["@cspotcode/source-map-support@0.8.1", "", { "dependencies": { "@jridgewell/trace-mapping": "0.3.9" } }, "sha512-IchNf6dN4tHoMFIn/7OE8LWZ19Y6q/67Bmf6vnGREv8RSbBVb9LPJxEcnwrcwX6ixSvaiGoomAUvu4YSxXrVgw=="],
338+
317339 "@edge-runtime/format": ["@edge-runtime/format@2.2.1", "", {}, "sha512-JQTRVuiusQLNNLe2W9tnzBlV/GvSVcozLl4XZHk5swnRZ/v6jp8TqR8P7sqmJsQqblDZ3EztcWmLDbhRje/+8g=="],
318340
319341 "@edge-runtime/node-utils": ["@edge-runtime/node-utils@2.3.0", "", {}, "sha512-uUtx8BFoO1hNxtHjp3eqVPC/mWImGb2exOfGjMLUoipuWgjej+f4o/VP4bUI8U40gu7Teogd5VTeZUkGvJSPOQ=="],
@@ -648,6 +670,12 @@ · 328 unmodified lines
648670
649671 "@parcel/watcher-win32-x64": ["@parcel/watcher-win32-x64@2.5.6", "", { "os": "win32", "cpu": "x64" }, "sha512-hbQlYcCq5dlAX9Qx+kFb0FHue6vbjlf0FrNzSKdYK2APUf7tGfGxQCk2ihEREmbR6ZMc0MVAD5RIX/41gpUzTw=="],
650672
673+ "@poppinss/colors": ["@poppinss/colors@4.1.6", "", { "dependencies": { "kleur": "^4.1.5" } }, "sha512-H9xkIdFswbS8n1d6vmRd8+c10t2Qe+rZITbbDHHkQixH5+2x1FDGmi/0K+WgWiqQFKPSlIYB7jlH6Kpfn6Fleg=="],
674+
675+ "@poppinss/dumper": ["@poppinss/dumper@0.6.5", "", { "dependencies": { "@poppinss/colors": "^4.1.5", "@sindresorhus/is": "^7.0.2", "supports-color": "^10.0.0" } }, "sha512-NBdYIb90J7LfOI32dOewKI1r7wnkiH6m920puQ3qHUeZkxNkQiFnXVWoE6YtFSv6QOiPPf7ys6i+HWWecDz7sw=="],
676+
677+ "@poppinss/exception": ["@poppinss/exception@1.2.3", "", {}, "sha512-dCED+QRChTVatE9ibtoaxc+WkdzOSjYTKi/+uacHWIsfodVfpsueo3+DKpgU5Px8qXjgmXkSvhXvSCz3fnP9lw=="],
678+
651679 "@renovatebot/pep440": ["@renovatebot/pep440@4.2.1", "", {}, "sha512-2FK1hF93Fuf1laSdfiEmJvSJPVIDHEUTz68D3Fi9s0IZrrpaEcj6pTFBTbYvsgC5du4ogrtf5re7yMMvrKNgkw=="],
652680
653681 "@rolldown/binding-android-arm64": ["@rolldown/binding-android-arm64@1.0.0-rc.1", "", { "os": "android", "cpu": "arm64" }, "sha512-He6ZoCfv5D7dlRbrhNBkuMVIHd0GDnjJwbICE1OWpG7G3S2gmJ+eXkcNLJjzjNDpeI2aRy56ou39AJM9AD8YFA=="],
@@ -746,6 +774,10 @@ · 92 unmodified lines
746774
747775 "@sinclair/typebox": ["@sinclair/typebox@0.25.24", "", {}, "sha512-XJfwUVUKDHF5ugKwIcxEgc9k8b7HbznCp6eUfWgu710hMPNIO4aw4/zB5RogDQz8nd6gyCDpU9O/m6qYEWY6yQ=="],
748776
777+ "@sindresorhus/is": ["@sindresorhus/is@7.2.0", "", {}, "sha512-P1Cz1dWaFfR4IR+U13mqqiGsLFf1KbayybWwdd2vfctdV6hDpUkgCY0nKOLLTMSoRd/jJNjtbqzf13K8DCCXQw=="],
778+
779+ "@speed-highlight/core": ["@speed-highlight/core@1.2.14", "", {}, "sha512-G4ewlBNhUtlLvrJTb88d2mdy2KRijzs4UhnlrOSRT4bmjh/IqNElZa3zkrZ+TC47TwtlDWzVLFADljF1Ijp5hA=="],
780+
749781 "@standard-schema/spec": ["@standard-schema/spec@1.1.0", "", {}, "sha512-l2aFy5jALhniG5HgqrD6jXLi/rUWrKvqN/qJx6yoJsgKhblVd+iqqU4RCXavm/jPityDo5TCvKMnpjKnOriy0w=="],
750782
751783 "@swc/helpers": ["@swc/helpers@0.5.15", "", { "dependencies": { "tslib": "^2.8.0" } }, "sha512-JQ5TuMi45Owi4/BIMAJBoSQoOJu12oOk/gADqlcUL9JEdHB8vyjUSsxqeNXnmXHjYKMi2WcYtezGEEhqUI/E2g=="],
@@ -962,6 +994,8 @@ · 210 unmodified lines
962994
963995 "bindings": ["bindings@1.5.0", "", { "dependencies": { "file-uri-to-path": "1.0.0" } }, "sha512-p2q/t/mhvuOj/UeLlV6566GD/guowlr0hHxClI0W9m7MWYkL1F0hLo+0Aexs9HSPCtR1SXQ0TD3MMKrXZajbiQ=="],
964996
997+ "blake3-wasm": ["blake3-wasm@2.1.5", "", {}, "sha512-F1+K8EbfOZE49dtoPtmxUQrpXaBIl3ICvasLh+nJta0xkz+9kF/7uet9fLnwKqhDrmj6g+6K3Tw9yQPUg2ka5g=="],
998+
965999 "body-parser": ["body-parser@2.2.2", "", { "dependencies": { "bytes": "^3.1.2", "content-type": "^1.0.5", "debug": "^4.4.3", "http-errors": "^2.0.0", "iconv-lite": "^0.7.0", "on-finished": "^2.4.1", "qs": "^6.14.1", "raw-body": "^3.0.1", "type-is": "^2.0.1" } }, "sha512-oP5VkATKlNwcgvxi0vM0p/D3n2C3EReYVX+DNYs5TjZFn/oQt2j+4sVJtSMr18pdRr8wjTcBl6LoV+FUwzPmNA=="],
9661000
9671001 "boolbase": ["boolbase@1.0.0", "", {}, "sha512-JZOSA7Mo9sNGB8+UjSgzdLtokWAky1zbztM3WRLCbZ70/3cTANmQmOdR7y2g+J0e2WXywy1yS468tY+IruqEww=="],
@@ -1142,6 +1176,8 @@ · 174 unmodified lines
11421176
11431177 "entities": ["entities@6.0.1", "", {}, "sha512-aN97NXWF6AWBTahfVOIrB/NShkzi5H7F9r1s9mD3cDj4Ko5f2qhhVoYMibXF7GlLveb/D2ioWay8lxI97Ven3g=="],
11441178
1179+ "error-stack-parser-es": ["error-stack-parser-es@1.0.5", "", {}, "sha512-5qucVt2XcuGMcEGgWI7i+yZpmpByQ8J1lHhcL7PwqCwu9FPP3VUXzT4ltHe5i2z9dePwEHcDVOAfSnHsOlCXRA=="],
1180+
11451181 "es-define-property": ["es-define-property@1.0.1", "", {}, "sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g=="],
11461182
11471183 "es-errors": ["es-errors@1.3.0", "", {}, "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw=="],
@@ -1502,6 +1538,8 @@ · 354 unmodified lines
15021538
15031539 "mimic-fn": ["mimic-fn@2.1.0", "", {}, "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg=="],
15041540
1541+ "miniflare": ["miniflare@4.20260305.0", "", { "dependencies": { "@cspotcode/source-map-support": "0.8.1", "sharp": "^0.34.5", "undici": "7.18.2", "workerd": "1.20260305.0", "ws": "8.18.0", "youch": "4.1.0-beta.10" }, "bin": { "miniflare": "bootstrap.js" } }, "sha512-jVhtKJtiwaZa3rI+WgoLvSJmEazDsoUmAPYRUmEe2VO6VSbvkhbnDRm+dsPbYRatgNIExwrpqG1rv96jHiSb0w=="],
1542+
15051543 "minimatch": ["minimatch@10.1.1", "", { "dependencies": { "@isaacs/brace-expansion": "^5.0.0" } }, "sha512-enIvLvRAFZYXJzkCYG5RKmPfrFArdLv+R+lbQ53BmIMLIry74bjKzX6iHAm8WYamJkhSSEabrWN5D97XnKObjQ=="],
15061544
15071545 "minipass": ["minipass@7.1.3", "", {}, "sha512-tEBHqDnIoM/1rXME1zgka9g6Q2lcoCkxHLuc7ODJ5BxbP5d4c2Z5cGgtXAku59200Cx7diuHTOYfSBD8n6mm8A=="],
@@ -1838,6 +1876,8 @@ · 330 unmodified lines
18381876
18391877 "sucrase": ["sucrase@3.35.1", "", { "dependencies": { "@jridgewell/gen-mapping": "^0.3.2", "commander": "^4.0.0", "lines-and-columns": "^1.1.6", "mz": "^2.7.0", "pirates": "^4.0.1", "tinyglobby": "^0.2.11", "ts-interface-checker": "^0.1.9" }, "bin": { "sucrase": "bin/sucrase", "sucrase-node": "bin/sucrase-node" } }, "sha512-DhuTmvZWux4H1UOnWMB3sk0sbaCVOoQZjv8u1rDoTV0HTdGem9hkAZtl4JZy8P2z4Bg0nT+YMeOFyVr4zcG5Tw=="],
18401878
1879+ "supports-color": ["supports-color@10.2.2", "", {}, "sha512-SS+jx45GF1QjgEXQx4NJZV9ImqmO2NPz5FNsIHrsDjh2YsHnawpan7SNQ1o8NuhrbHZy9AZhIoCUiCeaW/C80g=="],
1880+
18411881 "supports-preserve-symlinks-flag": ["supports-preserve-symlinks-flag@1.0.0", "", {}, "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w=="],
18421882
18431883 "svgo": ["svgo@4.0.0", "", { "dependencies": { "commander": "^11.1.0", "css-select": "^5.1.0", "css-tree": "^3.0.1", "css-what": "^6.1.0", "csso": "^5.0.5", "picocolors": "^1.1.1", "sax": "^1.4.1" }, "bin": "./bin/svgo.js" }, "sha512-VvrHQ+9uniE+Mvx3+C9IEe/lWasXCU0nXMY2kZeLrHNICuRiC8uMPyM14UEaMOFA5mhyQqEkB02VoQ16n3DLaw=="],
@@ -1930,6 +1970,8 @@ · 86 unmodified lines
19301970
19311971 "undici-types": ["undici-types@6.21.0", "", {}, "sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ=="],
19321972
1973+ "unenv": ["unenv@2.0.0-rc.24", "", { "dependencies": { "pathe": "^2.0.3" } }, "sha512-i7qRCmY42zmCwnYlh9H2SvLEypEFGye5iRmEMKjcGi7zk9UquigRjFtTLz0TYqr0ZGLZhaMHl/foy1bZR+Cwlw=="],
1974+
19331975 "unified": ["unified@11.0.5", "", { "dependencies": { "@types/unist": "^3.0.0", "bail": "^2.0.0", "devlop": "^1.0.0", "extend": "^3.0.0", "is-plain-obj": "^4.0.0", "trough": "^2.0.0", "vfile": "^6.0.0" } }, "sha512-xKvGhPWw3k84Qjh8bI3ZeJjqnyadK+GEFtazSfZv/rKeTkTjOJho6mFqh2SM96iIcZokxiOpg78GazTSg8+KHA=="],
19341976
19351977 "unifont": ["unifont@0.7.4", "", { "dependencies": { "css-tree": "^3.1.0", "ofetch": "^1.5.1", "ohash": "^2.0.11" } }, "sha512-oHeis4/xl42HUIeHuNZRGEvxj5AaIKR+bHPNegRq5LV1gdc3jundpONbjglKpihmJf+dswygdMJn3eftGIMemg=="],
@@ -2034,6 +2076,10 @@ · 98 unmodified lines
20342076
20352077 "widest-line": ["widest-line@5.0.0", "", { "dependencies": { "string-width": "^7.0.0" } }, "sha512-c9bZp7b5YtRj2wOe6dlj32MK+Bx/M/d+9VB2SHM1OtsUHR0aV0tdP6DWh/iMt0kWi1t5g1Iudu6hQRNd1A4PVA=="],
20362078
2079+ "workerd": ["workerd@1.20260305.0", "", { "optionalDependencies": { "@cloudflare/workerd-darwin-64": "1.20260305.0", "@cloudflare/workerd-darwin-arm64": "1.20260305.0", "@cloudflare/workerd-linux-64": "1.20260305.0", "@cloudflare/workerd-linux-arm64": "1.20260305.0", "@cloudflare/workerd-windows-64": "1.20260305.0" }, "bin": { "workerd": "bin/workerd" } }, "sha512-JkhfCLU+w+KbQmZ9k49IcDYc78GBo7eG8Mir8E2+KVjR7otQAmpcLlsous09YLh8WQ3Bt3Mi6/WMStvMAPukeA=="],
2080+
2081+ "wrangler": ["wrangler@4.69.0", "", { "dependencies": { "@cloudflare/kv-asset-handler": "0.4.2", "@cloudflare/unenv-preset": "2.14.0", "blake3-wasm": "2.1.5", "esbuild": "0.27.3", "miniflare": "4.20260305.0", "path-to-regexp": "6.3.0", "unenv": "2.0.0-rc.24", "workerd": "1.20260305.0" }, "optionalDependencies": { "fsevents": "~2.3.2" }, "peerDependencies": { "@cloudflare/workers-types": "^4.20260305.0" }, "optionalPeers": ["@cloudflare/workers-types"], "bin": { "wrangler": "bin/wrangler.js", "wrangler2": "bin/wrangler.js" } }, "sha512-EmVfIM65I5b4ITHe3Y9R7zQyf4NUBQ1leStakMlWiVR9n6VlDwuEltyQI2l3i0JciDnWyR3uqe+T6C08ivniTQ=="],
2082+
20372083 "wrap-ansi": ["wrap-ansi@9.0.2", "", { "dependencies": { "ansi-styles": "^6.2.1", "string-width": "^7.0.0", "strip-ansi": "^7.1.0" } }, "sha512-42AtmgqjV+X1VpdOfyTGOYRi0/zsoLqtXQckTmqTeybT+BDIbM/Guxo7x3pE2vtpr1ok6xRqM9OpBe+Jyoqyww=="],
20382084
20392085 "wrappy": ["wrappy@1.0.2", "", {}, "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ=="],
@@ -2070,6 +2116,10 @@ · 30 unmodified lines
20702116
20712117 "yoctocolors": ["yoctocolors@2.1.2", "", {}, "sha512-CzhO+pFNo8ajLM2d2IW/R93ipy99LWjtwblvC1RsoSUMZgyLbYFr221TnSNT7GjGdYui6P459mw9JH/g/zW2ug=="],
20722118
2119+ "youch": ["youch@4.1.0-beta.10", "", { "dependencies": { "@poppinss/colors": "^4.1.5", "@poppinss/dumper": "^0.6.4", "@speed-highlight/core": "^1.2.7", "cookie": "^1.0.2", "youch-core": "^0.3.3" } }, "sha512-rLfVLB4FgQneDr0dv1oddCVZmKjcJ6yX6mS4pU82Mq/Dt9a3cLZQ62pDBL4AUO+uVrCvtWz3ZFUL2HFAFJ/BXQ=="],
2120+
2121+ "youch-core": ["youch-core@0.3.3", "", { "dependencies": { "@poppinss/exception": "^1.2.2", "error-stack-parser-es": "^1.0.5" } }, "sha512-ho7XuGjLaJ2hWHoK8yFnsUGy2Y5uDpqSTq1FkHLK4/oqKtyUU1AFbOOxY4IpC9f0fTLjwYbslUz0Po5BpD1wrA=="],
2122+
20732123 "zod": ["zod@4.3.6", "", {}, "sha512-rftlrkhHZOcjDwkGlnUtZZkvaPHCsDATp4pGpuOOMDaTdDDXF91wuVDJoWoPsKX/3YPQ5fHuF3STjcYyKr+Qhg=="],
20742124
20752125 "zod-to-json-schema": ["zod-to-json-schema@3.25.1", "", { "peerDependencies": { "zod": "^3.25 || ^4" } }, "sha512-pM/SU9d3YAggzi6MtR4h7ruuQlqKtad8e9S0fmxcMi+ueAK5Korys/aWcV9LIIHTVbj01NdzxcnXSN+O74ZIVA=="],
@@ -2092,6 +2142,8 @@ · 16 unmodified lines
20922142
20932143 "@babel/helper-compilation-targets/semver": ["semver@6.3.1", "", { "bin": { "semver": "bin/semver.js" } }, "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA=="],
20942144
2145+ "@cspotcode/source-map-support/@jridgewell/trace-mapping": ["@jridgewell/trace-mapping@0.3.9", "", { "dependencies": { "@jridgewell/resolve-uri": "^3.0.3", "@jridgewell/sourcemap-codec": "^1.4.10" } }, "sha512-3Belt6tdc8bPgAtbcmdtNJlirVoTmEb5e2gC94PnkwEW9jI6CAHUeoG85tjWP5WquqfavoMtMwiG4P926ZKKuQ=="],
2146+
20952147 "@effect/platform-node-shared/ws": ["ws@8.19.0", "", { "peerDependencies": { "bufferutil": "^4.0.1", "utf-8-validate": ">=5.0.2" }, "optionalPeers": ["bufferutil", "utf-8-validate"] }, "sha512-blAT2mjOEIi0ZzruJfIhb3nps74PRWTCz1IjglWEEpQl5XS/UNama6u2/rjFkDDouqr4L67ry+1aGIALViWjDg=="],
20962148
20972149 "@executor-v2/confect/next": ["next@16.1.6", "", { "dependencies": { "@next/env": "16.1.6", "@swc/helpers": "0.5.15", "baseline-browser-mapping": "^2.8.3", "caniuse-lite": "^1.0.30001579", "postcss": "8.4.31", "styled-jsx": "5.1.6" }, "optionalDependencies": { "@next/swc-darwin-arm64": "16.1.6", "@next/swc-darwin-x64": "16.1.6", "@next/swc-linux-arm64-gnu": "16.1.6", "@next/swc-linux-arm64-musl": "16.1.6", "@next/swc-linux-x64-gnu": "16.1.6", "@next/swc-linux-x64-musl": "16.1.6", "@next/swc-win32-arm64-msvc": "16.1.6", "@next/swc-win32-x64-msvc": "16.1.6", "sharp": "^0.34.4" }, "peerDependencies": { "@opentelemetry/api": "^1.1.0", "@playwright/test": "^1.51.1", "babel-plugin-react-compiler": "*", "react": "^18.2.0 || 19.0.0-rc-de68d2f4-20241204 || ^19.0.0", "react-dom": "^18.2.0 || 19.0.0-rc-de68d2f4-20241204 || ^19.0.0", "sass": "^1.3.0" }, "optionalPeers": ["@opentelemetry/api", "@playwright/test", "babel-plugin-react-compiler", "sass"], "bin": { "next": "dist/bin/next" } }, "sha512-hkyRkcu5x/41KoqnROkfTm2pZVbKxvbZRuNvKXLRXxs3VfyO0WhY50TQS40EuKO9SW3rBj/sF3WbVwDACeMZyw=="],
@@ -2220,6 +2272,8 @@ · 122 unmodified lines
22202272
22212273 "micromatch/picomatch": ["picomatch@2.3.1", "", {}, "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA=="],
22222274
2275+ "miniflare/undici": ["undici@7.18.2", "", {}, "sha512-y+8YjDFzWdQlSE9N5nzKMT3g4a5UBX1HKowfdXh0uvAnTaqqwqB92Jt4UXBAeKekDs5IaDKyJFR4X1gYVCgXcw=="],
2276+
22232277 "path-scurry/lru-cache": ["lru-cache@11.2.6", "", {}, "sha512-ESL2CrkS/2wTPfuend7Zhkzo2u0daGJ/A2VucJOgQ/C48S/zB8MMeMHSGKYpXhIjbPxfuezITkaBH1wqv00DDQ=="],
22242278
22252279 "prompts/kleur": ["kleur@3.0.3", "", {}, "sha512-eTIzlVOSUR+JxdDFepEYcBMtZ9Qqdef+rnzWdRZuMbOywu5tO2w2N7rqjoANZ5k9vywhL6Br1VRjUIgTQx4E8w=="],
@@ -2250,6 +2304,8 @@ · 24 unmodified lines
22502304
22512305 "widest-line/string-width": ["string-width@7.2.0", "", { "dependencies": { "emoji-regex": "^10.3.0", "get-east-asian-width": "^1.0.0", "strip-ansi": "^7.1.0" } }, "sha512-tsaTIkKW9b4N+AEj+SVA+WhJzV7/zMhcSu78mLKWSk7cXMOSHsBKFWUs0fWwq8QyK3MgJBQRX6Gbi4kYbdvGkQ=="],
22522306
2307+ "wrangler/esbuild": ["esbuild@0.27.3", "", { "optionalDependencies": { "@esbuild/aix-ppc64": "0.27.3", "@esbuild/android-arm": "0.27.3", "@esbuild/android-arm64": "0.27.3", "@esbuild/android-x64": "0.27.3", "@esbuild/darwin-arm64": "0.27.3", "@esbuild/darwin-x64": "0.27.3", "@esbuild/freebsd-arm64": "0.27.3", "@esbuild/freebsd-x64": "0.27.3", "@esbuild/linux-arm": "0.27.3", "@esbuild/linux-arm64": "0.27.3", "@esbuild/linux-ia32": "0.27.3", "@esbuild/linux-loong64": "0.27.3", "@esbuild/linux-mips64el": "0.27.3", "@esbuild/linux-ppc64": "0.27.3", "@esbuild/linux-riscv64": "0.27.3", "@esbuild/linux-s390x": "0.27.3", "@esbuild/linux-x64": "0.27.3", "@esbuild/netbsd-arm64": "0.27.3", "@esbuild/netbsd-x64": "0.27.3", "@esbuild/openbsd-arm64": "0.27.3", "@esbuild/openbsd-x64": "0.27.3", "@esbuild/openharmony-arm64": "0.27.3", "@esbuild/sunos-x64": "0.27.3", "@esbuild/win32-arm64": "0.27.3", "@esbuild/win32-ia32": "0.27.3", "@esbuild/win32-x64": "0.27.3" }, "bin": { "esbuild": "bin/esbuild" } }, "sha512-8VwMnyGCONIs6cWue2IdpHxHnAjzxnw2Zr7MkVxB2vjmQ2ivqGFb4LEG3SMnv0Gb2F/G/2yA8zUaiL1gywDCCg=="],
2308+
22532309 "wrap-ansi/string-width": ["string-width@7.2.0", "", { "dependencies": { "emoji-regex": "^10.3.0", "get-east-asian-width": "^1.0.0", "strip-ansi": "^7.1.0" } }, "sha512-tsaTIkKW9b4N+AEj+SVA+WhJzV7/zMhcSu78mLKWSk7cXMOSHsBKFWUs0fWwq8QyK3MgJBQRX6Gbi4kYbdvGkQ=="],
22542310
22552311 "wrap-ansi/strip-ansi": ["strip-ansi@7.2.0", "", { "dependencies": { "ansi-regex": "^6.2.2" } }, "sha512-yDPMNjp4WyfYBkHnjIRLfca1i6KMyGCtsVgoKe/z1+6vukgaENdgGBZt+ZmKPc4gavvEZ5OgHfHdrazhgNyG7w=="],
@@ -2396,6 +2452,58 @@ · 140 unmodified lines
23962452
23972453 "widest-line/string-width/strip-ansi": ["strip-ansi@7.2.0", "", { "dependencies": { "ansi-regex": "^6.2.2" } }, "sha512-yDPMNjp4WyfYBkHnjIRLfca1i6KMyGCtsVgoKe/z1+6vukgaENdgGBZt+ZmKPc4gavvEZ5OgHfHdrazhgNyG7w=="],
23982454
2455+ "wrangler/esbuild/@esbuild/aix-ppc64": ["@esbuild/aix-ppc64@0.27.3", "", { "os": "aix", "cpu": "ppc64" }, "sha512-9fJMTNFTWZMh5qwrBItuziu834eOCUcEqymSH7pY+zoMVEZg3gcPuBNxH1EvfVYe9h0x/Ptw8KBzv7qxb7l8dg=="],
2456+
2457+ "wrangler/esbuild/@esbuild/android-arm": ["@esbuild/android-arm@0.27.3", "", { "os": "android", "cpu": "arm" }, "sha512-i5D1hPY7GIQmXlXhs2w8AWHhenb00+GxjxRncS2ZM7YNVGNfaMxgzSGuO8o8SJzRc/oZwU2bcScvVERk03QhzA=="],
2458+
2459+ "wrangler/esbuild/@esbuild/android-arm64": ["@esbuild/android-arm64@0.27.3", "", { "os": "android", "cpu": "arm64" }, "sha512-YdghPYUmj/FX2SYKJ0OZxf+iaKgMsKHVPF1MAq/P8WirnSpCStzKJFjOjzsW0QQ7oIAiccHdcqjbHmJxRb/dmg=="],
2460+
2461+ "wrangler/esbuild/@esbuild/android-x64": ["@esbuild/android-x64@0.27.3", "", { "os": "android", "cpu": "x64" }, "sha512-IN/0BNTkHtk8lkOM8JWAYFg4ORxBkZQf9zXiEOfERX/CzxW3Vg1ewAhU7QSWQpVIzTW+b8Xy+lGzdYXV6UZObQ=="],
2462+
2463+ "wrangler/esbuild/@esbuild/darwin-arm64": ["@esbuild/darwin-arm64@0.27.3", "", { "os": "darwin", "cpu": "arm64" }, "sha512-Re491k7ByTVRy0t3EKWajdLIr0gz2kKKfzafkth4Q8A5n1xTHrkqZgLLjFEHVD+AXdUGgQMq+Godfq45mGpCKg=="],
2464+
2465+ "wrangler/esbuild/@esbuild/darwin-x64": ["@esbuild/darwin-x64@0.27.3", "", { "os": "darwin", "cpu": "x64" }, "sha512-vHk/hA7/1AckjGzRqi6wbo+jaShzRowYip6rt6q7VYEDX4LEy1pZfDpdxCBnGtl+A5zq8iXDcyuxwtv3hNtHFg=="],
2466+
2467+ "wrangler/esbuild/@esbuild/freebsd-arm64": ["@esbuild/freebsd-arm64@0.27.3", "", { "os": "freebsd", "cpu": "arm64" }, "sha512-ipTYM2fjt3kQAYOvo6vcxJx3nBYAzPjgTCk7QEgZG8AUO3ydUhvelmhrbOheMnGOlaSFUoHXB6un+A7q4ygY9w=="],
2468+
2469+ "wrangler/esbuild/@esbuild/freebsd-x64": ["@esbuild/freebsd-x64@0.27.3", "", { "os": "freebsd", "cpu": "x64" }, "sha512-dDk0X87T7mI6U3K9VjWtHOXqwAMJBNN2r7bejDsc+j03SEjtD9HrOl8gVFByeM0aJksoUuUVU9TBaZa2rgj0oA=="],
2470+
2471+ "wrangler/esbuild/@esbuild/linux-arm": ["@esbuild/linux-arm@0.27.3", "", { "os": "linux", "cpu": "arm" }, "sha512-s6nPv2QkSupJwLYyfS+gwdirm0ukyTFNl3KTgZEAiJDd+iHZcbTPPcWCcRYH+WlNbwChgH2QkE9NSlNrMT8Gfw=="],
2472+
2473+ "wrangler/esbuild/@esbuild/linux-arm64": ["@esbuild/linux-arm64@0.27.3", "", { "os": "linux", "cpu": "arm64" }, "sha512-sZOuFz/xWnZ4KH3YfFrKCf1WyPZHakVzTiqji3WDc0BCl2kBwiJLCXpzLzUBLgmp4veFZdvN5ChW4Eq/8Fc2Fg=="],
2474+
2475+ "wrangler/esbuild/@esbuild/linux-ia32": ["@esbuild/linux-ia32@0.27.3", "", { "os": "linux", "cpu": "ia32" }, "sha512-yGlQYjdxtLdh0a3jHjuwOrxQjOZYD/C9PfdbgJJF3TIZWnm/tMd/RcNiLngiu4iwcBAOezdnSLAwQDPqTmtTYg=="],
2476+
2477+ "wrangler/esbuild/@esbuild/linux-loong64": ["@esbuild/linux-loong64@0.27.3", "", { "os": "linux", "cpu": "none" }, "sha512-WO60Sn8ly3gtzhyjATDgieJNet/KqsDlX5nRC5Y3oTFcS1l0KWba+SEa9Ja1GfDqSF1z6hif/SkpQJbL63cgOA=="],
2478+
2479+ "wrangler/esbuild/@esbuild/linux-mips64el": ["@esbuild/linux-mips64el@0.27.3", "", { "os": "linux", "cpu": "none" }, "sha512-APsymYA6sGcZ4pD6k+UxbDjOFSvPWyZhjaiPyl/f79xKxwTnrn5QUnXR5prvetuaSMsb4jgeHewIDCIWljrSxw=="],
2480+
2481+ "wrangler/esbuild/@esbuild/linux-ppc64": ["@esbuild/linux-ppc64@0.27.3", "", { "os": "linux", "cpu": "ppc64" }, "sha512-eizBnTeBefojtDb9nSh4vvVQ3V9Qf9Df01PfawPcRzJH4gFSgrObw+LveUyDoKU3kxi5+9RJTCWlj4FjYXVPEA=="],
2482+
2483+ "wrangler/esbuild/@esbuild/linux-riscv64": ["@esbuild/linux-riscv64@0.27.3", "", { "os": "linux", "cpu": "none" }, "sha512-3Emwh0r5wmfm3ssTWRQSyVhbOHvqegUDRd0WhmXKX2mkHJe1SFCMJhagUleMq+Uci34wLSipf8Lagt4LlpRFWQ=="],
2484+
2485+ "wrangler/esbuild/@esbuild/linux-s390x": ["@esbuild/linux-s390x@0.27.3", "", { "os": "linux", "cpu": "s390x" }, "sha512-pBHUx9LzXWBc7MFIEEL0yD/ZVtNgLytvx60gES28GcWMqil8ElCYR4kvbV2BDqsHOvVDRrOxGySBM9Fcv744hw=="],
2486+
2487+ "wrangler/esbuild/@esbuild/linux-x64": ["@esbuild/linux-x64@0.27.3", "", { "os": "linux", "cpu": "x64" }, "sha512-Czi8yzXUWIQYAtL/2y6vogER8pvcsOsk5cpwL4Gk5nJqH5UZiVByIY8Eorm5R13gq+DQKYg0+JyQoytLQas4dA=="],
2488+
2489+ "wrangler/esbuild/@esbuild/netbsd-arm64": ["@esbuild/netbsd-arm64@0.27.3", "", { "os": "none", "cpu": "arm64" }, "sha512-sDpk0RgmTCR/5HguIZa9n9u+HVKf40fbEUt+iTzSnCaGvY9kFP0YKBWZtJaraonFnqef5SlJ8/TiPAxzyS+UoA=="],
2490+
2491+ "wrangler/esbuild/@esbuild/netbsd-x64": ["@esbuild/netbsd-x64@0.27.3", "", { "os": "none", "cpu": "x64" }, "sha512-P14lFKJl/DdaE00LItAukUdZO5iqNH7+PjoBm+fLQjtxfcfFE20Xf5CrLsmZdq5LFFZzb5JMZ9grUwvtVYzjiA=="],
2492+
2493+ "wrangler/esbuild/@esbuild/openbsd-arm64": ["@esbuild/openbsd-arm64@0.27.3", "", { "os": "openbsd", "cpu": "arm64" }, "sha512-AIcMP77AvirGbRl/UZFTq5hjXK+2wC7qFRGoHSDrZ5v5b8DK/GYpXW3CPRL53NkvDqb9D+alBiC/dV0Fb7eJcw=="],
2494+
2495+ "wrangler/esbuild/@esbuild/openbsd-x64": ["@esbuild/openbsd-x64@0.27.3", "", { "os": "openbsd", "cpu": "x64" }, "sha512-DnW2sRrBzA+YnE70LKqnM3P+z8vehfJWHXECbwBmH/CU51z6FiqTQTHFenPlHmo3a8UgpLyH3PT+87OViOh1AQ=="],
2496+
2497+ "wrangler/esbuild/@esbuild/openharmony-arm64": ["@esbuild/openharmony-arm64@0.27.3", "", { "os": "none", "cpu": "arm64" }, "sha512-NinAEgr/etERPTsZJ7aEZQvvg/A6IsZG/LgZy+81wON2huV7SrK3e63dU0XhyZP4RKGyTm7aOgmQk0bGp0fy2g=="],
2498+
2499+ "wrangler/esbuild/@esbuild/sunos-x64": ["@esbuild/sunos-x64@0.27.3", "", { "os": "sunos", "cpu": "x64" }, "sha512-PanZ+nEz+eWoBJ8/f8HKxTTD172SKwdXebZ0ndd953gt1HRBbhMsaNqjTyYLGLPdoWHy4zLU7bDVJztF5f3BHA=="],
2500+
2501+ "wrangler/esbuild/@esbuild/win32-arm64": ["@esbuild/win32-arm64@0.27.3", "", { "os": "win32", "cpu": "arm64" }, "sha512-B2t59lWWYrbRDw/tjiWOuzSsFh1Y/E95ofKz7rIVYSQkUYBjfSgf6oeYPNWHToFRr2zx52JKApIcAS/D5TUBnA=="],
2502+
2503+ "wrangler/esbuild/@esbuild/win32-ia32": ["@esbuild/win32-ia32@0.27.3", "", { "os": "win32", "cpu": "ia32" }, "sha512-QLKSFeXNS8+tHW7tZpMtjlNb7HKau0QDpwm49u0vUp9y1WOF+PEzkU84y9GqYaAVW8aH8f3GcBck26jh54cX4Q=="],
2504+
2505+ "wrangler/esbuild/@esbuild/win32-x64": ["@esbuild/win32-x64@0.27.3", "", { "os": "win32", "cpu": "x64" }, "sha512-4uJGhsxuptu3OcpVAzli+/gWusVGwZZHTlS63hh++ehExkVT8SgiEf7/uC/PclrPPkLhZqGgCTjd0VWLo6xMqA=="],
2506+
23992507 "wrap-ansi/string-width/emoji-regex": ["emoji-regex@10.6.0", "", {}, "sha512-toUI84YS5YmxW219erniWD0CIVOo46xGKColeNQRgOzDorgBi1v4D71/OFzgD9GO2UGKIv1C3Sp8DAn0+j5w7A=="],
24002508
24012509 "wrap-ansi/strip-ansi/ansi-regex": ["ansi-regex@6.2.2", "", {}, "sha512-Bq3SmSpyFHaWjPk8If9yc6svM8c56dB5BAtW4Qbw5jHTwwXXcTLoRMkpDJp6VL0XzlWaCHTXrkFURMYmD0sLqg=="],
v2/packages/runtime-cloudflare-worker-loader/package.json+7−0
@@ -6,10 +6,17 @@ · 5 unmodified lines
66 ".": "./src/index.ts"
77 },
88 "scripts": {
9+ "test": "bunx vitest run --no-watch src/*.integration.test.ts",
910 "typecheck": "bunx tsc --noEmit -p tsconfig.json"
1011 },
1112 "dependencies": {
13+ "@effect/platform": "catalog:",
1214 "@executor-v2/engine": "workspace:*",
1315 "effect": "catalog:"
16+ },
17+ "devDependencies": {
18+ "@effect/vitest": "catalog:",
19+ "vitest": "catalog:",
20+ "wrangler": "^4.0.0"
1421 }
1522 }
v2/packages/runtime-cloudflare-worker-loader/src/index.integration.test.ts+228−0
@@ -0,0 +1,228 @@
1+import { spawn } from "node:child_process";
2+import { createServer } from "node:net";
3+import { fileURLToPath } from "node:url";
4+
5+import { afterAll, beforeAll, describe, expect, it } from "@effect/vitest";
6+import * as Effect from "effect/Effect";
7+import * as Either from "effect/Either";
8+
9+import { RuntimeAdapterError } from "@executor-v2/engine";
10+import { makeCloudflareWorkerLoaderRuntimeAdapter } from "./index";
11+
12+type WorkerProcess = ReturnType<typeof spawn>;
13+
14+const getFreePort = async (): Promise<number> =>
15+ await new Promise<number>((resolve, reject) => {
16+ const server = createServer();
17+
18+ server.listen(0, "127.0.0.1", () => {
19+ const address = server.address();
20+ if (!address || typeof address === "string") {
21+ server.close();
22+ reject(new Error("Failed to resolve free port"));
23+ return;
24+ }
25+
26+ const port = address.port;
27+ server.close((error) => {
28+ if (error) {
29+ reject(error);
30+ return;
31+ }
32+
33+ resolve(port);
34+ });
35+ });
36+
37+ server.on("error", (error) => {
38+ reject(error);
39+ });
40+ });
41+
42+const wait = (ms: number): Promise<void> =>
43+ new Promise((resolve) => {
44+ setTimeout(resolve, ms);
45+ });
46+
47+const waitForWorker = async (baseUrl: string, timeoutMs = 30_000): Promise<void> => {
48+ const startedAt = Date.now();
49+
50+ while (Date.now() - startedAt < timeoutMs) {
51+ try {
52+ const response = await fetch(`${baseUrl}/health`);
53+ if (response.status === 200) {
54+ return;
55+ }
56+ } catch {
57+ // Keep waiting while worker process starts.
58+ }
59+
60+ await wait(200);
61+ }
62+
63+ throw new Error(`Timed out waiting for worker health at ${baseUrl}/health`);
64+};
65+
66+const stopWorkerProcess = async (process: WorkerProcess): Promise<void> =>
67+ await new Promise<void>((resolve) => {
68+ if (process.killed || process.exitCode !== null) {
69+ resolve();
70+ return;
71+ }
72+
73+ const timer = setTimeout(() => {
74+ process.kill("SIGKILL");
75+ resolve();
76+ }, 3_000);
77+
78+ process.once("exit", () => {
79+ clearTimeout(timer);
80+ resolve();
81+ });
82+
83+ process.kill("SIGTERM");
84+ });
85+
86+describe("cloudflare worker loader runtime adapter integration", () => {
87+ let workerProcess: WorkerProcess | null = null;
88+ let workerPort = 0;
89+ let workerBaseUrl = "";
90+
91+ beforeAll(async () => {
92+ workerPort = await getFreePort();
93+ workerBaseUrl = `http://127.0.0.1:${workerPort}`;
94+
95+ const workerDir = fileURLToPath(new URL("../test-worker", import.meta.url));
96+
97+ const spawned = spawn(
98+ "bunx",
99+ [
100+ "wrangler",
101+ "dev",
102+ "--local",
103+ "--port",
104+ String(workerPort),
105+ "--config",
106+ "wrangler.jsonc",
107+ "--log-level",
108+ "error",
109+ ],
110+ {
111+ cwd: workerDir,
112+ env: process.env,
113+ stdio: ["ignore", "pipe", "pipe"],
114+ },
115+ );
116+ workerProcess = spawned;
117+
118+ spawned.stdout?.on("data", () => {
119+ // Ignore stdout, but keep stream flowing.
120+ });
121+
122+ spawned.stderr?.on("data", () => {
123+ // Keep stream flowing.
124+ });
125+
126+ await waitForWorker(workerBaseUrl, 45_000);
127+ });
128+
129+ afterAll(async () => {
130+ if (workerProcess) {
131+ await stopWorkerProcess(workerProcess);
132+ }
133+ });
134+
135+ it.effect("executes against a real local Cloudflare Worker process", () =>
136+ Effect.gen(function* () {
137+ const adapter = makeCloudflareWorkerLoaderRuntimeAdapter({
138+ runUrl: `${workerBaseUrl}/v1/runs?scenario=completed`,
139+ authToken: "sandbox-token",
140+ callbackUrl: "https://callbacks.example.test/runtimeCallbacks",
141+ callbackInternalSecret: "internal-token",
142+ });
143+
144+ const result = yield* adapter.execute({
145+ runId: "run_e2e_1",
146+ code: "return 42;",
147+ });
148+
149+ expect(result).toMatchObject({
150+ echoedRunId: "run_e2e_1",
151+ callback: {
152+ url: "https://callbacks.example.test/runtimeCallbacks",
153+ internalSecret: "internal-token",
154+ },
155+ });
156+ }),
157+ );
158+
159+ it.effect("maps worker terminal failures across the HTTP boundary", () =>
160+ Effect.gen(function* () {
161+ const adapter = makeCloudflareWorkerLoaderRuntimeAdapter({
162+ runUrl: `${workerBaseUrl}/v1/runs?scenario=failed`,
163+ authToken: "sandbox-token",
164+ callbackUrl: "https://callbacks.example.test/runtimeCallbacks",
165+ });
166+
167+ const result = yield* Effect.either(
168+ adapter.execute({
169+ runId: "run_e2e_2",
170+ code: "return 0;",
171+ }),
172+ );
173+
174+ expect(Either.isLeft(result)).toBe(true);
175+ if (Either.isLeft(result)) {
176+ expect(result.left).toBeInstanceOf(RuntimeAdapterError);
177+ expect(result.left.operation).toBe("execute_terminal");
178+ expect(result.left.message).toContain("failed");
179+ expect(result.left.details).toBe("tool call denied");
180+ }
181+ }),
182+ );
183+
184+ it.effect("surfaces non-200 responses as dispatch errors", () =>
185+ Effect.gen(function* () {
186+ const adapter = makeCloudflareWorkerLoaderRuntimeAdapter({
187+ runUrl: `${workerBaseUrl}/v1/runs?scenario=http-500`,
188+ authToken: "sandbox-token",
189+ callbackUrl: "https://callbacks.example.test/runtimeCallbacks",
190+ });
191+
192+ const result = yield* Effect.either(
193+ adapter.execute({
194+ runId: "run_e2e_3",
195+ code: "return 0;",
196+ }),
197+ );
198+
199+ expect(Either.isLeft(result)).toBe(true);
200+ if (Either.isLeft(result)) {
201+ expect(result.left.operation).toBe("dispatch");
202+ expect(result.left.message).toContain("500");
203+ }
204+ }),
205+ );
206+
207+ it.effect("surfaces invalid JSON from worker as decode errors", () =>
208+ Effect.gen(function* () {
209+ const adapter = makeCloudflareWorkerLoaderRuntimeAdapter({
210+ runUrl: `${workerBaseUrl}/v1/runs?scenario=invalid-json`,
211+ authToken: "sandbox-token",
212+ callbackUrl: "https://callbacks.example.test/runtimeCallbacks",
213+ });
214+
215+ const result = yield* Effect.either(
216+ adapter.execute({
217+ runId: "run_e2e_4",
218+ code: "return 0;",
219+ }),
220+ );
221+
222+ expect(Either.isLeft(result)).toBe(true);
223+ if (Either.isLeft(result)) {
224+ expect(result.left.operation).toBe("decode_response");
225+ }
226+ }),
227+ );
228+});
v2/packages/runtime-cloudflare-worker-loader/src/index.ts+291−11
@@ -1,18 +1,298 @@
1−import * as Effect from "effect/Effect";
1+import * as FetchHttpClient from "@effect/platform/FetchHttpClient";
2+import * as HttpClient from "@effect/platform/HttpClient";
3+import * as HttpClientRequest from "@effect/platform/HttpClientRequest";
4+import * as HttpClientResponse from "@effect/platform/HttpClientResponse";
25
36 import {
47 RuntimeAdapterError,
58 type RuntimeAdapter,
9+ type RuntimeAdapterKind,
10+ type RuntimeExecuteInput,
611 } from "@executor-v2/engine";
12+import * as Effect from "effect/Effect";
13+import * as Layer from "effect/Layer";
14+import * as Option from "effect/Option";
15+import * as ParseResult from "effect/ParseResult";
16+import * as Schema from "effect/Schema";
17+
18+const runtimeKind: RuntimeAdapterKind = "cloudflare-worker-loader";
19+
20+export type CloudflareWorkerLoaderConfig = {
21+ runUrl: string;
22+ authToken: string;
23+ callbackUrl: string;
24+ callbackInternalSecret: string | null;
25+ requestTimeoutMs: number;
26+};
27+
28+export type CloudflareWorkerLoaderRuntimeAdapterOptions = {
29+ runUrl?: string;
30+ authToken?: string;
31+ callbackUrl?: string;
32+ callbackInternalSecret?: string | null;
33+ requestTimeoutMs?: number;
34+ env?: Record<string, string | undefined>;
35+ httpClientLayer?: Layer.Layer<HttpClient.HttpClient>;
36+};
37+
38+const defaultRequestTimeoutMs = 120_000;
739
8−export const makeCloudflareWorkerLoaderRuntimeAdapter = (): RuntimeAdapter => ({
9− kind: "cloudflare-worker-loader",
10− isAvailable: () => Effect.succeed(false),
11− execute: () =>
12− new RuntimeAdapterError({
13− operation: "execute",
14− runtimeKind: "cloudflare-worker-loader",
15− message: "Cloudflare worker loader runtime adapter is not implemented yet",
16− details: null,
17− }),
40+const CloudflareRunRequestSchema = Schema.Struct({
41+ runId: Schema.String,
42+ taskId: Schema.String,
43+ code: Schema.String,
44+ timeoutMs: Schema.optional(Schema.Number),
45+ callback: Schema.Struct({
46+ url: Schema.String,
47+ internalSecret: Schema.optional(Schema.String),
48+ }),
1849 });
50+
51+const CloudflareCompletedRunResponseSchema = Schema.Struct({
52+ status: Schema.Literal("completed"),
53+ result: Schema.Unknown,
54+ error: Schema.optional(Schema.String),
55+ exitCode: Schema.optional(Schema.Number),
56+});
57+
58+const CloudflareFailedRunResponseSchema = Schema.Struct({
59+ status: Schema.Literal("failed", "timed_out", "denied"),
60+ result: Schema.optional(Schema.Unknown),
61+ error: Schema.optional(Schema.String),
62+ exitCode: Schema.optional(Schema.Number),
63+});
64+
65+const CloudflareRunResponseSchema = Schema.Union(
66+ CloudflareCompletedRunResponseSchema,
67+ CloudflareFailedRunResponseSchema,
68+);
69+
70+type CloudflareRunRequest = typeof CloudflareRunRequestSchema.Type;
71+type CloudflareRunResponse = typeof CloudflareRunResponseSchema.Type;
72+
73+const toRuntimeAdapterError = (
74+ operation: string,
75+ message: string,
76+ details: string | null,
77+): RuntimeAdapterError =>
78+ new RuntimeAdapterError({
79+ operation,
80+ runtimeKind,
81+ message,
82+ details,
83+ });
84+
85+const describeParseError = (cause: unknown): string =>
86+ ParseResult.isParseError(cause)
87+ ? ParseResult.TreeFormatter.formatErrorSync(cause)
88+ : String(cause);
89+
90+const normalizeString = (value: string | undefined): string | undefined => {
91+ if (!value) {
92+ return undefined;
93+ }
94+
95+ const trimmed = value.trim();
96+ return trimmed.length > 0 ? trimmed : undefined;
97+};
98+
99+const parseRequestTimeoutMs = (
100+ value: string | undefined,
101+ fallback: number,
102+): number => {
103+ if (!value) {
104+ return fallback;
105+ }
106+
107+ const parsed = Number.parseInt(value, 10);
108+ if (!Number.isFinite(parsed) || parsed <= 0) {
109+ return fallback;
110+ }
111+
112+ return parsed;
113+};
114+
115+const resolveConfig = (
116+ options: CloudflareWorkerLoaderRuntimeAdapterOptions = {},
117+): CloudflareWorkerLoaderConfig | null => {
118+ const env = options.env ?? process.env;
119+
120+ const runUrl = normalizeString(options.runUrl ?? env.CLOUDFLARE_SANDBOX_RUN_URL);
121+ const authToken = normalizeString(
122+ options.authToken ?? env.CLOUDFLARE_SANDBOX_AUTH_TOKEN,
123+ );
124+ const callbackUrl = normalizeString(
125+ options.callbackUrl ?? env.CLOUDFLARE_SANDBOX_CALLBACK_URL,
126+ );
127+ const callbackInternalSecret =
128+ options.callbackInternalSecret ??
129+ normalizeString(
130+ env.CLOUDFLARE_SANDBOX_CALLBACK_INTERNAL_SECRET ??
131+ env.EXECUTOR_INTERNAL_TOKEN,
132+ ) ??
133+ null;
134+
135+ const requestTimeoutMs =
136+ options.requestTimeoutMs ??
137+ parseRequestTimeoutMs(
138+ env.CLOUDFLARE_SANDBOX_REQUEST_TIMEOUT_MS,
139+ defaultRequestTimeoutMs,
140+ );
141+
142+ if (!runUrl || !authToken || !callbackUrl) {
143+ return null;
144+ }
145+
146+ return {
147+ runUrl,
148+ authToken,
149+ callbackUrl,
150+ callbackInternalSecret,
151+ requestTimeoutMs,
152+ };
153+};
154+
155+const makeDispatchRequest = (
156+ input: RuntimeExecuteInput,
157+ config: CloudflareWorkerLoaderConfig,
158+): Effect.Effect<HttpClientRequest.HttpClientRequest, RuntimeAdapterError> => {
159+ const requestPayload: CloudflareRunRequest = {
160+ runId: input.runId,
161+ taskId: input.runId,
162+ code: input.code,
163+ timeoutMs: input.timeoutMs,
164+ callback: {
165+ url: config.callbackUrl,
166+ internalSecret: config.callbackInternalSecret ?? undefined,
167+ },
168+ };
169+
170+ const baseRequest = HttpClientRequest.post(config.runUrl).pipe(
171+ HttpClientRequest.acceptJson,
172+ HttpClientRequest.bearerToken(config.authToken),
173+ );
174+
175+ return HttpClientRequest.schemaBodyJson(CloudflareRunRequestSchema)(
176+ requestPayload,
177+ )(baseRequest).pipe(
178+ Effect.mapError((cause) =>
179+ toRuntimeAdapterError(
180+ "encode_request",
181+ "Failed to encode Cloudflare worker dispatch request",
182+ describeParseError(cause),
183+ ),
184+ ),
185+ );
186+};
187+
188+const executeDispatchRequest = (
189+ input: RuntimeExecuteInput,
190+ config: CloudflareWorkerLoaderConfig,
191+): Effect.Effect<unknown, RuntimeAdapterError, HttpClient.HttpClient> =>
192+ Effect.gen(function* () {
193+ const request = yield* makeDispatchRequest(input, config);
194+
195+ const response = yield* HttpClient.execute(request).pipe(
196+ Effect.mapError((cause) =>
197+ toRuntimeAdapterError(
198+ "dispatch",
199+ "Cloudflare worker dispatch failed",
200+ String(cause),
201+ ),
202+ ),
203+ );
204+
205+ if (response.status !== 200) {
206+ const body = yield* response.text.pipe(
207+ Effect.mapError(() => null),
208+ Effect.orElseSucceed(() => null),
209+ );
210+
211+ return yield* toRuntimeAdapterError(
212+ "dispatch",
213+ `Cloudflare worker execution returned ${response.status}`,
214+ body,
215+ );
216+ }
217+
218+ const decodedResponse = yield* HttpClientResponse.schemaBodyJson(
219+ CloudflareRunResponseSchema,
220+ )(response).pipe(
221+ Effect.mapError((cause) =>
222+ toRuntimeAdapterError(
223+ "decode_response",
224+ "Cloudflare worker execution returned invalid response JSON",
225+ describeParseError(cause),
226+ ),
227+ ),
228+ );
229+
230+ const runResponse: CloudflareRunResponse = decodedResponse;
231+
232+ if (runResponse.status === "completed") {
233+ return runResponse.result;
234+ }
235+
236+ return yield* toRuntimeAdapterError(
237+ "execute_terminal",
238+ `Cloudflare worker execution ended with status '${runResponse.status}'`,
239+ typeof runResponse.error === "string" && runResponse.error.length > 0
240+ ? runResponse.error
241+ : null,
242+ );
243+ });
244+
245+const provideHttpClientDependencies = <A, E>(
246+ effect: Effect.Effect<A, E, HttpClient.HttpClient>,
247+ options: CloudflareWorkerLoaderRuntimeAdapterOptions,
248+): Effect.Effect<A, E> =>
249+ effect.pipe(Effect.provide(options.httpClientLayer ?? FetchHttpClient.layer));
250+
251+const dispatchCodeWithCloudflareWorkerLoader = (
252+ input: RuntimeExecuteInput,
253+ config: CloudflareWorkerLoaderConfig,
254+ options: CloudflareWorkerLoaderRuntimeAdapterOptions,
255+): Effect.Effect<unknown, RuntimeAdapterError> =>
256+ Effect.gen(function* () {
257+ const requestTimeoutMs = Math.max(
258+ config.requestTimeoutMs,
259+ (input.timeoutMs ?? 30_000) + 30_000,
260+ );
261+
262+ const response = yield* Effect.timeoutOption(
263+ executeDispatchRequest(input, config),
264+ requestTimeoutMs,
265+ );
266+
267+ if (Option.isNone(response)) {
268+ return yield* toRuntimeAdapterError(
269+ "dispatch",
270+ `Cloudflare worker dispatch timed out after ${requestTimeoutMs}ms`,
271+ null,
272+ );
273+ }
274+
275+ return response.value;
276+ }).pipe((effect) => provideHttpClientDependencies(effect, options));
277+
278+export const makeCloudflareWorkerLoaderRuntimeAdapter = (
279+ options: CloudflareWorkerLoaderRuntimeAdapterOptions = {},
280+): RuntimeAdapter => {
281+ const config = resolveConfig(options);
282+
283+ return {
284+ kind: runtimeKind,
285+ isAvailable: () => Effect.succeed(config !== null),
286+ execute: (input) => {
287+ if (!config) {
288+ return toRuntimeAdapterError(
289+ "config",
290+ "Cloudflare worker loader runtime is not configured",
291+ "Set CLOUDFLARE_SANDBOX_RUN_URL, CLOUDFLARE_SANDBOX_AUTH_TOKEN, and CLOUDFLARE_SANDBOX_CALLBACK_URL.",
292+ );
293+ }
294+
295+ return dispatchCodeWithCloudflareWorkerLoader(input, config, options);
296+ },
297+ };
298+};
v2/packages/runtime-cloudflare-worker-loader/test-worker/src/index.ts+118−0
@@ -0,0 +1,118 @@
1+const AUTH_TOKEN = "sandbox-token";
2+
3+type CloudflareRunRequest = {
4+ runId?: unknown;
5+ taskId?: unknown;
6+ code?: unknown;
7+ timeoutMs?: unknown;
8+ callback?: {
9+ url?: unknown;
10+ internalSecret?: unknown;
11+ };
12+};
13+
14+const json = (value: unknown, status = 200): Response =>
15+ new Response(JSON.stringify(value), {
16+ status,
17+ headers: {
18+ "content-type": "application/json",
19+ },
20+ });
21+
22+const isObjectRecord = (value: unknown): value is Record<string, unknown> =>
23+ typeof value === "object" && value !== null && !Array.isArray(value);
24+
25+const isValidRunRequest = (value: unknown): value is CloudflareRunRequest => {
26+ if (!isObjectRecord(value)) {
27+ return false;
28+ }
29+
30+ if (typeof value.runId !== "string") {
31+ return false;
32+ }
33+
34+ if (typeof value.taskId !== "string") {
35+ return false;
36+ }
37+
38+ if (typeof value.code !== "string") {
39+ return false;
40+ }
41+
42+ if (!isObjectRecord(value.callback)) {
43+ return false;
44+ }
45+
46+ if (typeof value.callback.url !== "string") {
47+ return false;
48+ }
49+
50+ if (
51+ value.callback.internalSecret !== undefined &&
52+ typeof value.callback.internalSecret !== "string"
53+ ) {
54+ return false;
55+ }
56+
57+ return true;
58+};
59+
60+export default {
61+ async fetch(request: Request): Promise<Response> {
62+ const url = new URL(request.url);
63+
64+ if (url.pathname === "/health") {
65+ return new Response("ok", { status: 200 });
66+ }
67+
68+ if (url.pathname !== "/v1/runs" || request.method !== "POST") {
69+ return json({ error: "not_found" }, 404);
70+ }
71+
72+ if (request.headers.get("authorization") !== `Bearer ${AUTH_TOKEN}`) {
73+ return json({ error: "unauthorized" }, 401);
74+ }
75+
76+ let payload: unknown;
77+ try {
78+ payload = await request.json();
79+ } catch {
80+ return json({ error: "invalid_json" }, 400);
81+ }
82+
83+ if (!isValidRunRequest(payload)) {
84+ return json({ error: "invalid_payload" }, 400);
85+ }
86+
87+ const scenario = url.searchParams.get("scenario") ?? "completed";
88+
89+ if (scenario === "http-500") {
90+ return json({ error: "host_failure" }, 500);
91+ }
92+
93+ if (scenario === "invalid-json") {
94+ return new Response("{bad-json", {
95+ status: 200,
96+ headers: {
97+ "content-type": "application/json",
98+ },
99+ });
100+ }
101+
102+ if (scenario === "failed") {
103+ return json({ status: "failed", error: "tool call denied" }, 200);
104+ }
105+
106+ return json(
107+ {
108+ status: "completed",
109+ result: {
110+ echoedRunId: payload.runId,
111+ callback: payload.callback,
112+ },
113+ exitCode: 0,
114+ },
115+ 200,
116+ );
117+ },
118+};
v2/packages/runtime-cloudflare-worker-loader/test-worker/wrangler.jsonc+6−0
@@ -0,0 +1,6 @@
1+{
2+ "name": "executor-v2-runtime-cloudflare-test-worker",
3+ "main": "src/index.ts",
4+ "compatibility_date": "2025-06-01",
5+ "compatibility_flags": ["nodejs_compat"]
6+}