@cloudflare/sandbox 0.3.7 → 0.4.1

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.
Files changed (120) hide show
  1. package/.turbo/turbo-build.log +44 -0
  2. package/CHANGELOG.md +6 -14
  3. package/Dockerfile +82 -18
  4. package/README.md +89 -824
  5. package/dist/{chunk-JTKON2SH.js → chunk-BCJ7SF3Q.js} +9 -5
  6. package/dist/chunk-BCJ7SF3Q.js.map +1 -0
  7. package/dist/chunk-BFVUNTP4.js +104 -0
  8. package/dist/chunk-BFVUNTP4.js.map +1 -0
  9. package/dist/{chunk-NNGBXDMY.js → chunk-EKSWCBCA.js} +3 -6
  10. package/dist/chunk-EKSWCBCA.js.map +1 -0
  11. package/dist/chunk-HGF554LH.js +2236 -0
  12. package/dist/chunk-HGF554LH.js.map +1 -0
  13. package/dist/{chunk-6UAWTJ5S.js → chunk-Z532A7QC.js} +13 -20
  14. package/dist/{chunk-6UAWTJ5S.js.map → chunk-Z532A7QC.js.map} +1 -1
  15. package/dist/file-stream.d.ts +16 -38
  16. package/dist/file-stream.js +1 -2
  17. package/dist/index.d.ts +6 -5
  18. package/dist/index.js +35 -39
  19. package/dist/index.js.map +1 -1
  20. package/dist/interpreter.d.ts +3 -3
  21. package/dist/interpreter.js +2 -2
  22. package/dist/request-handler.d.ts +4 -3
  23. package/dist/request-handler.js +4 -7
  24. package/dist/sandbox-D9K2ypln.d.ts +583 -0
  25. package/dist/sandbox.d.ts +3 -3
  26. package/dist/sandbox.js +4 -7
  27. package/dist/security.d.ts +4 -3
  28. package/dist/security.js +3 -3
  29. package/dist/sse-parser.js +1 -1
  30. package/package.json +11 -5
  31. package/src/clients/base-client.ts +280 -0
  32. package/src/clients/command-client.ts +115 -0
  33. package/src/clients/file-client.ts +269 -0
  34. package/src/clients/git-client.ts +92 -0
  35. package/src/clients/index.ts +63 -0
  36. package/src/{interpreter-client.ts → clients/interpreter-client.ts} +148 -171
  37. package/src/clients/port-client.ts +105 -0
  38. package/src/clients/process-client.ts +177 -0
  39. package/src/clients/sandbox-client.ts +41 -0
  40. package/src/clients/types.ts +84 -0
  41. package/src/clients/utility-client.ts +94 -0
  42. package/src/errors/adapter.ts +180 -0
  43. package/src/errors/classes.ts +469 -0
  44. package/src/errors/index.ts +105 -0
  45. package/src/file-stream.ts +119 -117
  46. package/src/index.ts +81 -69
  47. package/src/interpreter.ts +17 -8
  48. package/src/request-handler.ts +69 -43
  49. package/src/sandbox.ts +694 -533
  50. package/src/security.ts +14 -23
  51. package/src/sse-parser.ts +4 -8
  52. package/startup.sh +3 -0
  53. package/tests/base-client.test.ts +328 -0
  54. package/tests/command-client.test.ts +407 -0
  55. package/tests/file-client.test.ts +643 -0
  56. package/tests/file-stream.test.ts +306 -0
  57. package/tests/git-client.test.ts +328 -0
  58. package/tests/port-client.test.ts +301 -0
  59. package/tests/process-client.test.ts +658 -0
  60. package/tests/sandbox.test.ts +465 -0
  61. package/tests/sse-parser.test.ts +290 -0
  62. package/tests/utility-client.test.ts +266 -0
  63. package/tests/wrangler.jsonc +35 -0
  64. package/tsconfig.json +9 -1
  65. package/vitest.config.ts +31 -0
  66. package/container_src/bun.lock +0 -76
  67. package/container_src/circuit-breaker.ts +0 -121
  68. package/container_src/control-process.ts +0 -784
  69. package/container_src/handler/exec.ts +0 -185
  70. package/container_src/handler/file.ts +0 -457
  71. package/container_src/handler/git.ts +0 -130
  72. package/container_src/handler/ports.ts +0 -314
  73. package/container_src/handler/process.ts +0 -568
  74. package/container_src/handler/session.ts +0 -92
  75. package/container_src/index.ts +0 -601
  76. package/container_src/interpreter-service.ts +0 -276
  77. package/container_src/isolation.ts +0 -1213
  78. package/container_src/mime-processor.ts +0 -255
  79. package/container_src/package.json +0 -18
  80. package/container_src/runtime/executors/javascript/node_executor.ts +0 -123
  81. package/container_src/runtime/executors/python/ipython_executor.py +0 -338
  82. package/container_src/runtime/executors/typescript/ts_executor.ts +0 -138
  83. package/container_src/runtime/process-pool.ts +0 -464
  84. package/container_src/shell-escape.ts +0 -42
  85. package/container_src/startup.sh +0 -11
  86. package/container_src/types.ts +0 -131
  87. package/dist/chunk-32UDXUPC.js +0 -671
  88. package/dist/chunk-32UDXUPC.js.map +0 -1
  89. package/dist/chunk-5DILEXGY.js +0 -85
  90. package/dist/chunk-5DILEXGY.js.map +0 -1
  91. package/dist/chunk-D3U63BZP.js +0 -240
  92. package/dist/chunk-D3U63BZP.js.map +0 -1
  93. package/dist/chunk-FXYPFGOZ.js +0 -129
  94. package/dist/chunk-FXYPFGOZ.js.map +0 -1
  95. package/dist/chunk-JTKON2SH.js.map +0 -1
  96. package/dist/chunk-NNGBXDMY.js.map +0 -1
  97. package/dist/chunk-SQLJNZ3K.js +0 -674
  98. package/dist/chunk-SQLJNZ3K.js.map +0 -1
  99. package/dist/chunk-W7TVRPBG.js +0 -108
  100. package/dist/chunk-W7TVRPBG.js.map +0 -1
  101. package/dist/client-B3RUab0s.d.ts +0 -225
  102. package/dist/client.d.ts +0 -4
  103. package/dist/client.js +0 -7
  104. package/dist/client.js.map +0 -1
  105. package/dist/errors.d.ts +0 -95
  106. package/dist/errors.js +0 -27
  107. package/dist/errors.js.map +0 -1
  108. package/dist/interpreter-client.d.ts +0 -4
  109. package/dist/interpreter-client.js +0 -9
  110. package/dist/interpreter-client.js.map +0 -1
  111. package/dist/interpreter-types.d.ts +0 -259
  112. package/dist/interpreter-types.js +0 -9
  113. package/dist/interpreter-types.js.map +0 -1
  114. package/dist/types.d.ts +0 -453
  115. package/dist/types.js +0 -45
  116. package/dist/types.js.map +0 -1
  117. package/src/client.ts +0 -1048
  118. package/src/errors.ts +0 -219
  119. package/src/interpreter-types.ts +0 -390
  120. package/src/types.ts +0 -571
@@ -1,130 +0,0 @@
1
- import { randomBytes } from "node:crypto";
2
- import type { Session, SessionManager } from "../isolation";
3
- import type { GitCheckoutRequest } from "../types";
4
-
5
- async function executeGitCheckout(
6
- sessionManager: SessionManager,
7
- sessionId: string | undefined,
8
- repoUrl: string,
9
- branch: string,
10
- targetDir: string
11
- ): Promise<{
12
- success: boolean;
13
- stdout: string;
14
- stderr: string;
15
- exitCode: number;
16
- }> {
17
- // Execute git clone through the session to respect working directory
18
- const command = `git clone -b ${branch} ${repoUrl} ${targetDir}`;
19
-
20
- // Use specific session if provided, otherwise use default session
21
- let session: Session | undefined;
22
-
23
- if (sessionId) {
24
- session = sessionManager.getSession(sessionId);
25
- if (!session) {
26
- throw new Error(`Session '${sessionId}' not found`);
27
- }
28
- } else {
29
- // Use the centralized method to get or create default session
30
- session = await sessionManager.getOrCreateDefaultSession();
31
- }
32
-
33
- return session.exec(command);
34
- }
35
-
36
- export async function handleGitCheckoutRequest(
37
- req: Request,
38
- corsHeaders: Record<string, string>,
39
- sessionManager: SessionManager
40
- ): Promise<Response> {
41
- try {
42
- const body = (await req.json()) as GitCheckoutRequest;
43
- const { repoUrl, branch = "main", targetDir, sessionId } = body;
44
-
45
- if (!repoUrl || typeof repoUrl !== "string") {
46
- return new Response(
47
- JSON.stringify({
48
- error: "Repository URL is required and must be a string",
49
- }),
50
- {
51
- headers: {
52
- "Content-Type": "application/json",
53
- ...corsHeaders,
54
- },
55
- status: 400,
56
- }
57
- );
58
- }
59
-
60
- // Validate repository URL format
61
- const urlPattern =
62
- /^(https?:\/\/|git@|ssh:\/\/).*\.git$|^https?:\/\/.*\/.*$/;
63
- if (!urlPattern.test(repoUrl)) {
64
- return new Response(
65
- JSON.stringify({
66
- error: "Invalid repository URL format",
67
- }),
68
- {
69
- headers: {
70
- "Content-Type": "application/json",
71
- ...corsHeaders,
72
- },
73
- status: 400,
74
- }
75
- );
76
- }
77
-
78
- // Generate target directory if not provided using cryptographically secure randomness
79
- const checkoutDir =
80
- targetDir ||
81
- `repo_${Date.now()}_${randomBytes(6).toString('hex')}`;
82
-
83
- console.log(
84
- `[Server] Checking out repository: ${repoUrl} to ${checkoutDir}${sessionId ? ` in session: ${sessionId}` : ''}`
85
- );
86
-
87
- const result = await executeGitCheckout(
88
- sessionManager,
89
- sessionId,
90
- repoUrl,
91
- branch,
92
- checkoutDir
93
- );
94
-
95
- return new Response(
96
- JSON.stringify({
97
- branch,
98
- exitCode: result.exitCode,
99
- repoUrl,
100
- stderr: result.stderr,
101
- stdout: result.stdout,
102
- success: result.success,
103
- targetDir: checkoutDir,
104
- timestamp: new Date().toISOString(),
105
- }),
106
- {
107
- headers: {
108
- "Content-Type": "application/json",
109
- ...corsHeaders,
110
- },
111
- }
112
- );
113
- } catch (error) {
114
- console.error("[Server] Error in handleGitCheckoutRequest:", error);
115
- return new Response(
116
- JSON.stringify({
117
- error: "Failed to checkout repository",
118
- message: error instanceof Error ? error.message : "Unknown error",
119
- }),
120
- {
121
- headers: {
122
- "Content-Type": "application/json",
123
- ...corsHeaders,
124
- },
125
- status: 500,
126
- }
127
- );
128
- }
129
- }
130
-
@@ -1,314 +0,0 @@
1
- import type { ExposePortRequest, UnexposePortRequest } from "../types";
2
-
3
- export async function handleExposePortRequest(
4
- exposedPorts: Map<number, { name?: string; exposedAt: Date }>,
5
- req: Request,
6
- corsHeaders: Record<string, string>
7
- ): Promise<Response> {
8
- try {
9
- const body = (await req.json()) as ExposePortRequest;
10
- const { port, name } = body;
11
-
12
- if (!port || typeof port !== "number") {
13
- return new Response(
14
- JSON.stringify({
15
- error: "Port is required and must be a number",
16
- }),
17
- {
18
- headers: {
19
- "Content-Type": "application/json",
20
- ...corsHeaders,
21
- },
22
- status: 400,
23
- }
24
- );
25
- }
26
-
27
- // Validate port range
28
- if (port < 1 || port > 65535) {
29
- return new Response(
30
- JSON.stringify({
31
- error: "Port must be between 1 and 65535",
32
- }),
33
- {
34
- headers: {
35
- "Content-Type": "application/json",
36
- ...corsHeaders,
37
- },
38
- status: 400,
39
- }
40
- );
41
- }
42
-
43
- // Store the exposed port
44
- exposedPorts.set(port, { name, exposedAt: new Date() });
45
-
46
- console.log(`[Server] Exposed port: ${port}${name ? ` (${name})` : ""}`);
47
-
48
- return new Response(
49
- JSON.stringify({
50
- port,
51
- name,
52
- exposedAt: new Date().toISOString(),
53
- success: true,
54
- timestamp: new Date().toISOString(),
55
- }),
56
- {
57
- headers: {
58
- "Content-Type": "application/json",
59
- ...corsHeaders,
60
- },
61
- }
62
- );
63
- } catch (error) {
64
- console.error("[Server] Error in handleExposePortRequest:", error);
65
- return new Response(
66
- JSON.stringify({
67
- error: "Failed to expose port",
68
- message: error instanceof Error ? error.message : "Unknown error",
69
- }),
70
- {
71
- headers: {
72
- "Content-Type": "application/json",
73
- ...corsHeaders,
74
- },
75
- status: 500,
76
- }
77
- );
78
- }
79
- }
80
-
81
- export async function handleUnexposePortRequest(
82
- exposedPorts: Map<number, { name?: string; exposedAt: Date }>,
83
- req: Request,
84
- corsHeaders: Record<string, string>
85
- ): Promise<Response> {
86
- try {
87
- const body = (await req.json()) as UnexposePortRequest;
88
- const { port } = body;
89
-
90
- if (!port || typeof port !== "number") {
91
- return new Response(
92
- JSON.stringify({
93
- error: "Port is required and must be a number",
94
- }),
95
- {
96
- headers: {
97
- "Content-Type": "application/json",
98
- ...corsHeaders,
99
- },
100
- status: 400,
101
- }
102
- );
103
- }
104
-
105
- // Check if port is exposed
106
- if (!exposedPorts.has(port)) {
107
- return new Response(
108
- JSON.stringify({
109
- error: "Port is not exposed",
110
- }),
111
- {
112
- headers: {
113
- "Content-Type": "application/json",
114
- ...corsHeaders,
115
- },
116
- status: 404,
117
- }
118
- );
119
- }
120
-
121
- // Remove the exposed port
122
- exposedPorts.delete(port);
123
-
124
- console.log(`[Server] Unexposed port: ${port}`);
125
-
126
- return new Response(
127
- JSON.stringify({
128
- port,
129
- success: true,
130
- timestamp: new Date().toISOString(),
131
- }),
132
- {
133
- headers: {
134
- "Content-Type": "application/json",
135
- ...corsHeaders,
136
- },
137
- }
138
- );
139
- } catch (error) {
140
- console.error("[Server] Error in handleUnexposePortRequest:", error);
141
- return new Response(
142
- JSON.stringify({
143
- error: "Failed to unexpose port",
144
- message: error instanceof Error ? error.message : "Unknown error",
145
- }),
146
- {
147
- headers: {
148
- "Content-Type": "application/json",
149
- ...corsHeaders,
150
- },
151
- status: 500,
152
- }
153
- );
154
- }
155
- }
156
-
157
- export async function handleGetExposedPortsRequest(
158
- exposedPorts: Map<number, { name?: string; exposedAt: Date }>,
159
- req: Request,
160
- corsHeaders: Record<string, string>
161
- ): Promise<Response> {
162
- try {
163
- const ports = Array.from(exposedPorts.entries()).map(([port, info]) => ({
164
- port,
165
- name: info.name,
166
- exposedAt: info.exposedAt.toISOString(),
167
- }));
168
-
169
- return new Response(
170
- JSON.stringify({
171
- ports,
172
- count: ports.length,
173
- timestamp: new Date().toISOString(),
174
- }),
175
- {
176
- headers: {
177
- "Content-Type": "application/json",
178
- ...corsHeaders,
179
- },
180
- }
181
- );
182
- } catch (error) {
183
- console.error("[Server] Error in handleGetExposedPortsRequest:", error);
184
- return new Response(
185
- JSON.stringify({
186
- error: "Failed to get exposed ports",
187
- message: error instanceof Error ? error.message : "Unknown error",
188
- }),
189
- {
190
- headers: {
191
- "Content-Type": "application/json",
192
- ...corsHeaders,
193
- },
194
- status: 500,
195
- }
196
- );
197
- }
198
- }
199
-
200
- export async function handleProxyRequest(
201
- exposedPorts: Map<number, { name?: string; exposedAt: Date }>,
202
- req: Request,
203
- corsHeaders: Record<string, string>
204
- ): Promise<Response> {
205
- try {
206
- const url = new URL(req.url);
207
- const pathParts = url.pathname.split("/");
208
-
209
- // Extract port from path like /proxy/3000/...
210
- if (pathParts.length < 3) {
211
- return new Response(
212
- JSON.stringify({
213
- error: "Invalid proxy path",
214
- }),
215
- {
216
- headers: {
217
- "Content-Type": "application/json",
218
- ...corsHeaders,
219
- },
220
- status: 400,
221
- }
222
- );
223
- }
224
-
225
- const port = parseInt(pathParts[2]);
226
- if (!port || Number.isNaN(port)) {
227
- return new Response(
228
- JSON.stringify({
229
- error: "Invalid port in proxy path",
230
- }),
231
- {
232
- headers: {
233
- "Content-Type": "application/json",
234
- ...corsHeaders,
235
- },
236
- status: 400,
237
- }
238
- );
239
- }
240
-
241
- // Check if port is exposed
242
- if (!exposedPorts.has(port)) {
243
- return new Response(
244
- JSON.stringify({
245
- error: `Port ${port} is not exposed`,
246
- }),
247
- {
248
- headers: {
249
- "Content-Type": "application/json",
250
- ...corsHeaders,
251
- },
252
- status: 404,
253
- }
254
- );
255
- }
256
-
257
- // Construct the target URL
258
- const targetPath = `/${pathParts.slice(3).join("/")}`;
259
- // Use 127.0.0.1 instead of localhost for more reliable container networking
260
- const targetUrl = `http://127.0.0.1:${port}${targetPath}${url.search}`;
261
-
262
- console.log(`[Server] Proxying request to: ${targetUrl}`);
263
- console.log(`[Server] Method: ${req.method}, Port: ${port}, Path: ${targetPath}`);
264
-
265
- try {
266
- // Forward the request to the target port
267
- const targetResponse = await fetch(targetUrl, {
268
- method: req.method,
269
- headers: req.headers,
270
- body: req.body,
271
- });
272
-
273
- // Return the response from the target
274
- return new Response(targetResponse.body, {
275
- status: targetResponse.status,
276
- statusText: targetResponse.statusText,
277
- headers: {
278
- ...Object.fromEntries(targetResponse.headers.entries()),
279
- ...corsHeaders,
280
- },
281
- });
282
- } catch (fetchError) {
283
- console.error(`[Server] Error proxying to port ${port}:`, fetchError);
284
- return new Response(
285
- JSON.stringify({
286
- error: `Service on port ${port} is not responding`,
287
- message: fetchError instanceof Error ? fetchError.message : "Unknown error",
288
- }),
289
- {
290
- headers: {
291
- "Content-Type": "application/json",
292
- ...corsHeaders,
293
- },
294
- status: 502,
295
- }
296
- );
297
- }
298
- } catch (error) {
299
- console.error("[Server] Error in handleProxyRequest:", error);
300
- return new Response(
301
- JSON.stringify({
302
- error: "Failed to proxy request",
303
- message: error instanceof Error ? error.message : "Unknown error",
304
- }),
305
- {
306
- headers: {
307
- "Content-Type": "application/json",
308
- ...corsHeaders,
309
- },
310
- status: 500,
311
- }
312
- );
313
- }
314
- }