@cloudflare/sandbox 0.3.7 → 0.4.2

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 +8 -10
  3. package/Dockerfile +82 -18
  4. package/README.md +89 -824
  5. package/dist/chunk-53JFOF7F.js +2352 -0
  6. package/dist/chunk-53JFOF7F.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-JXZMAU2C.js +559 -0
  12. package/dist/chunk-JXZMAU2C.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 +45 -38
  19. package/dist/interpreter.d.ts +3 -3
  20. package/dist/interpreter.js +2 -2
  21. package/dist/request-handler.d.ts +4 -3
  22. package/dist/request-handler.js +4 -7
  23. package/dist/sandbox-D9K2ypln.d.ts +583 -0
  24. package/dist/sandbox.d.ts +3 -3
  25. package/dist/sandbox.js +4 -7
  26. package/dist/security.d.ts +4 -3
  27. package/dist/security.js +3 -3
  28. package/dist/sse-parser.js +1 -1
  29. package/package.json +12 -4
  30. package/src/clients/base-client.ts +280 -0
  31. package/src/clients/command-client.ts +115 -0
  32. package/src/clients/file-client.ts +269 -0
  33. package/src/clients/git-client.ts +92 -0
  34. package/src/clients/index.ts +63 -0
  35. package/src/{interpreter-client.ts → clients/interpreter-client.ts} +148 -171
  36. package/src/clients/port-client.ts +105 -0
  37. package/src/clients/process-client.ts +177 -0
  38. package/src/clients/sandbox-client.ts +41 -0
  39. package/src/clients/types.ts +84 -0
  40. package/src/clients/utility-client.ts +94 -0
  41. package/src/errors/adapter.ts +180 -0
  42. package/src/errors/classes.ts +469 -0
  43. package/src/errors/index.ts +105 -0
  44. package/src/file-stream.ts +119 -117
  45. package/src/index.ts +81 -69
  46. package/src/interpreter.ts +17 -8
  47. package/src/request-handler.ts +69 -43
  48. package/src/sandbox.ts +694 -533
  49. package/src/security.ts +14 -23
  50. package/src/sse-parser.ts +4 -8
  51. package/startup.sh +3 -0
  52. package/tests/base-client.test.ts +328 -0
  53. package/tests/command-client.test.ts +407 -0
  54. package/tests/file-client.test.ts +643 -0
  55. package/tests/file-stream.test.ts +306 -0
  56. package/tests/git-client.test.ts +328 -0
  57. package/tests/port-client.test.ts +301 -0
  58. package/tests/process-client.test.ts +658 -0
  59. package/tests/sandbox.test.ts +465 -0
  60. package/tests/sse-parser.test.ts +290 -0
  61. package/tests/utility-client.test.ts +266 -0
  62. package/tests/wrangler.jsonc +35 -0
  63. package/tsconfig.json +9 -1
  64. package/vitest.config.ts +31 -0
  65. package/container_src/bun.lock +0 -76
  66. package/container_src/circuit-breaker.ts +0 -121
  67. package/container_src/control-process.ts +0 -784
  68. package/container_src/handler/exec.ts +0 -185
  69. package/container_src/handler/file.ts +0 -457
  70. package/container_src/handler/git.ts +0 -130
  71. package/container_src/handler/ports.ts +0 -314
  72. package/container_src/handler/process.ts +0 -568
  73. package/container_src/handler/session.ts +0 -92
  74. package/container_src/index.ts +0 -601
  75. package/container_src/interpreter-service.ts +0 -276
  76. package/container_src/isolation.ts +0 -1213
  77. package/container_src/mime-processor.ts +0 -255
  78. package/container_src/package.json +0 -18
  79. package/container_src/runtime/executors/javascript/node_executor.ts +0 -123
  80. package/container_src/runtime/executors/python/ipython_executor.py +0 -338
  81. package/container_src/runtime/executors/typescript/ts_executor.ts +0 -138
  82. package/container_src/runtime/process-pool.ts +0 -464
  83. package/container_src/shell-escape.ts +0 -42
  84. package/container_src/startup.sh +0 -11
  85. package/container_src/types.ts +0 -131
  86. package/dist/chunk-32UDXUPC.js +0 -671
  87. package/dist/chunk-32UDXUPC.js.map +0 -1
  88. package/dist/chunk-5DILEXGY.js +0 -85
  89. package/dist/chunk-5DILEXGY.js.map +0 -1
  90. package/dist/chunk-D3U63BZP.js +0 -240
  91. package/dist/chunk-D3U63BZP.js.map +0 -1
  92. package/dist/chunk-FXYPFGOZ.js +0 -129
  93. package/dist/chunk-FXYPFGOZ.js.map +0 -1
  94. package/dist/chunk-JTKON2SH.js +0 -113
  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
- }