@computesdk/cloudflare 1.3.37 → 1.4.0

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/package.json CHANGED
@@ -1,12 +1,15 @@
1
1
  {
2
2
  "name": "@computesdk/cloudflare",
3
- "version": "1.3.37",
3
+ "version": "1.4.0",
4
4
  "description": "Cloudflare provider for ComputeSDK - edge code execution using Cloudflare Workers and Durable Objects",
5
5
  "author": "Garrison",
6
6
  "license": "MIT",
7
7
  "main": "./dist/index.js",
8
8
  "module": "./dist/index.mjs",
9
9
  "types": "./dist/index.d.ts",
10
+ "bin": {
11
+ "computesdk-cloudflare": "./dist/setup.mjs"
12
+ },
10
13
  "exports": {
11
14
  ".": {
12
15
  "types": "./dist/index.d.ts",
@@ -15,12 +18,13 @@
15
18
  }
16
19
  },
17
20
  "files": [
18
- "dist"
21
+ "dist",
22
+ "src/worker"
19
23
  ],
20
24
  "dependencies": {
21
- "@cloudflare/sandbox": "^0.3.0",
22
- "@computesdk/provider": "1.0.30",
23
- "computesdk": "2.5.0"
25
+ "@cloudflare/sandbox": "^0.7.0",
26
+ "@computesdk/provider": "1.0.29",
27
+ "computesdk": "2.4.0"
24
28
  },
25
29
  "keywords": [
26
30
  "computesdk",
@@ -0,0 +1 @@
1
+ FROM docker.io/cloudflare/sandbox:0.7.17
@@ -0,0 +1,142 @@
1
+ /**
2
+ * ComputeSDK Cloudflare Gateway Worker
3
+ *
4
+ * Deployed to the user's Cloudflare account via `npx @computesdk/cloudflare`.
5
+ * Exposes a REST API that proxies operations to a Sandbox Durable Object,
6
+ * allowing ComputeSDK to create and manage sandboxes remotely.
7
+ */
8
+
9
+ import { getSandbox, proxyToSandbox, type Sandbox } from '@cloudflare/sandbox';
10
+ export { Sandbox } from '@cloudflare/sandbox';
11
+
12
+ type Env = {
13
+ Sandbox: DurableObjectNamespace<Sandbox>;
14
+ SANDBOX_SECRET: string;
15
+ };
16
+
17
+ export default {
18
+ async fetch(request: Request, env: Env): Promise<Response> {
19
+ // Route preview URL traffic to exposed sandbox ports
20
+ const proxy = await proxyToSandbox(request, env);
21
+ if (proxy) return proxy;
22
+
23
+ const url = new URL(request.url);
24
+
25
+ // Health check (no auth required)
26
+ if (url.pathname === '/v1/health') {
27
+ return Response.json({ status: 'ok', provider: 'cloudflare' });
28
+ }
29
+
30
+ // Auth check
31
+ const auth = request.headers.get('Authorization');
32
+ if (auth !== `Bearer ${env.SANDBOX_SECRET}`) {
33
+ return Response.json({ error: 'Unauthorized' }, { status: 401 });
34
+ }
35
+
36
+ let body: Record<string, any> = {};
37
+ if (request.method === 'POST') {
38
+ try {
39
+ body = await request.json() as Record<string, any>;
40
+ } catch {
41
+ return Response.json({ error: 'Invalid JSON in request body' }, { status: 400 });
42
+ }
43
+ }
44
+
45
+ const sandboxId = body.sandboxId || url.searchParams.get('sandboxId');
46
+ if (!sandboxId) {
47
+ return Response.json({ error: 'Missing sandboxId' }, { status: 400 });
48
+ }
49
+
50
+ try {
51
+ const sandbox = getSandbox(env.Sandbox, sandboxId);
52
+
53
+ // --- Sandbox lifecycle ---
54
+
55
+ if (url.pathname === '/v1/sandbox/create') {
56
+ if (body.envVars) await sandbox.setEnvVars(body.envVars);
57
+ // Trigger lazy container start
58
+ await sandbox.exec('true');
59
+ return Response.json({ sandboxId, status: 'running' });
60
+ }
61
+
62
+ if (url.pathname === '/v1/sandbox/destroy') {
63
+ await sandbox.destroy();
64
+ return Response.json({ success: true });
65
+ }
66
+
67
+ if (url.pathname === '/v1/sandbox/info') {
68
+ const processes = await sandbox.listProcesses();
69
+ return Response.json({ sandboxId, status: 'running', processes });
70
+ }
71
+
72
+ // --- Command execution ---
73
+
74
+ if (url.pathname === '/v1/sandbox/exec') {
75
+ if (!body.command) return Response.json({ error: 'Missing required field: command' }, { status: 400 });
76
+ const result = await sandbox.exec(body.command, {
77
+ cwd: body.cwd,
78
+ env: body.env,
79
+ timeout: body.timeout,
80
+ });
81
+ return Response.json(result);
82
+ }
83
+
84
+ // --- Code interpreter ---
85
+
86
+ if (url.pathname === '/v1/sandbox/runCode') {
87
+ if (!body.code) return Response.json({ error: 'Missing required field: code' }, { status: 400 });
88
+ const result = await sandbox.runCode(body.code, {
89
+ language: body.language,
90
+ timeout: body.timeout,
91
+ });
92
+ return Response.json(result);
93
+ }
94
+
95
+ // --- Filesystem ---
96
+
97
+ if (url.pathname === '/v1/sandbox/readFile') {
98
+ if (!body.path) return Response.json({ error: 'Missing required field: path' }, { status: 400 });
99
+ const file = await sandbox.readFile(body.path);
100
+ return Response.json(file);
101
+ }
102
+
103
+ if (url.pathname === '/v1/sandbox/writeFile') {
104
+ if (!body.path) return Response.json({ error: 'Missing required field: path' }, { status: 400 });
105
+ if (body.content === undefined) return Response.json({ error: 'Missing required field: content' }, { status: 400 });
106
+ await sandbox.writeFile(body.path, body.content);
107
+ return Response.json({ success: true });
108
+ }
109
+
110
+ if (url.pathname === '/v1/sandbox/mkdir') {
111
+ if (!body.path) return Response.json({ error: 'Missing required field: path' }, { status: 400 });
112
+ await sandbox.mkdir(body.path, { recursive: true });
113
+ return Response.json({ success: true });
114
+ }
115
+
116
+ if (url.pathname === '/v1/sandbox/exists') {
117
+ if (!body.path) return Response.json({ error: 'Missing required field: path' }, { status: 400 });
118
+ const result = await sandbox.exists(body.path);
119
+ return Response.json(result);
120
+ }
121
+
122
+ if (url.pathname === '/v1/sandbox/deleteFile') {
123
+ if (!body.path) return Response.json({ error: 'Missing required field: path' }, { status: 400 });
124
+ await sandbox.deleteFile(body.path);
125
+ return Response.json({ success: true });
126
+ }
127
+
128
+ // --- Ports ---
129
+
130
+ if (url.pathname === '/v1/sandbox/exposePort') {
131
+ if (!body.port) return Response.json({ error: 'Missing required field: port' }, { status: 400 });
132
+ const result = await sandbox.exposePort(body.port, body.options || {});
133
+ return Response.json(result);
134
+ }
135
+
136
+ return Response.json({ error: 'Not found' }, { status: 404 });
137
+ } catch (error) {
138
+ const message = error instanceof Error ? error.message : String(error);
139
+ return Response.json({ error: message }, { status: 500 });
140
+ }
141
+ },
142
+ };
@@ -0,0 +1,8 @@
1
+ {
2
+ "name": "computesdk-sandbox-worker",
3
+ "private": true,
4
+ "type": "module",
5
+ "dependencies": {
6
+ "@cloudflare/sandbox": "^0.7.0"
7
+ }
8
+ }
@@ -0,0 +1,21 @@
1
+ {
2
+ "name": "computesdk-sandbox",
3
+ "main": "index.ts",
4
+ "compatibility_date": "2025-10-13",
5
+ "compatibility_flags": ["nodejs_compat"],
6
+ "containers": [
7
+ {
8
+ "class_name": "Sandbox",
9
+ "image": "./Dockerfile",
10
+ "max_instances": 10
11
+ }
12
+ ],
13
+ "durable_objects": {
14
+ "bindings": [
15
+ { "class_name": "Sandbox", "name": "Sandbox" }
16
+ ]
17
+ },
18
+ "migrations": [
19
+ { "new_sqlite_classes": ["Sandbox"], "tag": "v1" }
20
+ ]
21
+ }