@cloudflare/sandbox 0.0.0-46eb4e6 → 0.0.0-485cf61

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 (95) hide show
  1. package/CHANGELOG.md +0 -6
  2. package/Dockerfile +82 -18
  3. package/README.md +89 -824
  4. package/dist/chunk-3NEP4CNV.js +99 -0
  5. package/dist/chunk-3NEP4CNV.js.map +1 -0
  6. package/dist/chunk-6IYG2RIN.js +117 -0
  7. package/dist/chunk-6IYG2RIN.js.map +1 -0
  8. package/dist/chunk-HB44YO2A.js +2331 -0
  9. package/dist/chunk-HB44YO2A.js.map +1 -0
  10. package/dist/chunk-KPVMMMIP.js +105 -0
  11. package/dist/chunk-KPVMMMIP.js.map +1 -0
  12. package/dist/chunk-NNGBXDMY.js +89 -0
  13. package/dist/chunk-NNGBXDMY.js.map +1 -0
  14. package/dist/file-stream.d.ts +43 -0
  15. package/dist/file-stream.js +9 -0
  16. package/dist/file-stream.js.map +1 -0
  17. package/dist/index.d.ts +9 -0
  18. package/dist/index.js +55 -0
  19. package/dist/index.js.map +1 -0
  20. package/dist/interpreter.d.ts +33 -0
  21. package/dist/interpreter.js +8 -0
  22. package/dist/interpreter.js.map +1 -0
  23. package/dist/request-handler.d.ts +18 -0
  24. package/dist/request-handler.js +12 -0
  25. package/dist/request-handler.js.map +1 -0
  26. package/dist/sandbox-CtlKjZwf.d.ts +583 -0
  27. package/dist/sandbox.d.ts +4 -0
  28. package/dist/sandbox.js +12 -0
  29. package/dist/sandbox.js.map +1 -0
  30. package/dist/security.d.ts +35 -0
  31. package/dist/security.js +15 -0
  32. package/dist/security.js.map +1 -0
  33. package/dist/sse-parser.d.ts +28 -0
  34. package/dist/sse-parser.js +11 -0
  35. package/dist/sse-parser.js.map +1 -0
  36. package/package.json +11 -5
  37. package/src/clients/base-client.ts +297 -0
  38. package/src/clients/command-client.ts +118 -0
  39. package/src/clients/file-client.ts +272 -0
  40. package/src/clients/git-client.ts +95 -0
  41. package/src/clients/index.ts +63 -0
  42. package/src/{interpreter-client.ts → clients/interpreter-client.ts} +151 -171
  43. package/src/clients/port-client.ts +108 -0
  44. package/src/clients/process-client.ts +180 -0
  45. package/src/clients/sandbox-client.ts +41 -0
  46. package/src/clients/types.ts +81 -0
  47. package/src/clients/utility-client.ts +97 -0
  48. package/src/errors/adapter.ts +180 -0
  49. package/src/errors/classes.ts +469 -0
  50. package/src/errors/index.ts +105 -0
  51. package/src/file-stream.ts +119 -117
  52. package/src/index.ts +81 -69
  53. package/src/interpreter.ts +17 -8
  54. package/src/request-handler.ts +61 -7
  55. package/src/sandbox.ts +698 -495
  56. package/src/security.ts +20 -0
  57. package/startup.sh +7 -0
  58. package/tests/base-client.test.ts +328 -0
  59. package/tests/command-client.test.ts +407 -0
  60. package/tests/file-client.test.ts +643 -0
  61. package/tests/file-stream.test.ts +306 -0
  62. package/tests/git-client.test.ts +328 -0
  63. package/tests/port-client.test.ts +301 -0
  64. package/tests/process-client.test.ts +658 -0
  65. package/tests/sandbox.test.ts +465 -0
  66. package/tests/sse-parser.test.ts +291 -0
  67. package/tests/utility-client.test.ts +266 -0
  68. package/tests/wrangler.jsonc +35 -0
  69. package/tsconfig.json +9 -1
  70. package/vitest.config.ts +31 -0
  71. package/container_src/bun.lock +0 -76
  72. package/container_src/circuit-breaker.ts +0 -121
  73. package/container_src/control-process.ts +0 -784
  74. package/container_src/handler/exec.ts +0 -185
  75. package/container_src/handler/file.ts +0 -457
  76. package/container_src/handler/git.ts +0 -130
  77. package/container_src/handler/ports.ts +0 -314
  78. package/container_src/handler/process.ts +0 -568
  79. package/container_src/handler/session.ts +0 -92
  80. package/container_src/index.ts +0 -600
  81. package/container_src/interpreter-service.ts +0 -276
  82. package/container_src/isolation.ts +0 -1213
  83. package/container_src/mime-processor.ts +0 -255
  84. package/container_src/package.json +0 -18
  85. package/container_src/runtime/executors/javascript/node_executor.ts +0 -123
  86. package/container_src/runtime/executors/python/ipython_executor.py +0 -338
  87. package/container_src/runtime/executors/typescript/ts_executor.ts +0 -138
  88. package/container_src/runtime/process-pool.ts +0 -464
  89. package/container_src/shell-escape.ts +0 -42
  90. package/container_src/startup.sh +0 -11
  91. package/container_src/types.ts +0 -131
  92. package/src/client.ts +0 -1048
  93. package/src/errors.ts +0 -219
  94. package/src/interpreter-types.ts +0 -390
  95. package/src/types.ts +0 -571
@@ -0,0 +1,41 @@
1
+ import { CommandClient } from './command-client';
2
+ import { FileClient } from './file-client';
3
+ import { GitClient } from './git-client';
4
+ import { InterpreterClient } from './interpreter-client';
5
+ import { PortClient } from './port-client';
6
+ import { ProcessClient } from './process-client';
7
+ import type { HttpClientOptions } from './types';
8
+ import { UtilityClient } from './utility-client';
9
+
10
+ /**
11
+ * Main sandbox client that composes all domain-specific clients
12
+ * Provides organized access to all sandbox functionality
13
+ */
14
+ export class SandboxClient {
15
+ public readonly commands: CommandClient;
16
+ public readonly files: FileClient;
17
+ public readonly processes: ProcessClient;
18
+ public readonly ports: PortClient;
19
+ public readonly git: GitClient;
20
+ public readonly interpreter: InterpreterClient;
21
+ public readonly utils: UtilityClient;
22
+
23
+ constructor(options: HttpClientOptions = {}) {
24
+ // Ensure baseUrl is provided for all clients
25
+ const clientOptions = {
26
+ baseUrl: 'http://localhost:3000',
27
+ ...options,
28
+ };
29
+
30
+ // Initialize all domain clients with shared options
31
+ this.commands = new CommandClient(clientOptions);
32
+ this.files = new FileClient(clientOptions);
33
+ this.processes = new ProcessClient(clientOptions);
34
+ this.ports = new PortClient(clientOptions);
35
+ this.git = new GitClient(clientOptions);
36
+ this.interpreter = new InterpreterClient(clientOptions);
37
+ this.utils = new UtilityClient(clientOptions);
38
+ }
39
+
40
+
41
+ }
@@ -0,0 +1,81 @@
1
+ /**
2
+ * Minimal interface for container fetch functionality
3
+ */
4
+ export interface ContainerStub {
5
+ containerFetch(url: string, options: RequestInit, port?: number): Promise<Response>;
6
+ }
7
+
8
+ /**
9
+ * Shared HTTP client configuration options
10
+ */
11
+ export interface HttpClientOptions {
12
+ baseUrl?: string;
13
+ port?: number;
14
+ stub?: ContainerStub;
15
+ onCommandComplete?: (
16
+ success: boolean,
17
+ exitCode: number,
18
+ stdout: string,
19
+ stderr: string,
20
+ command: string
21
+ ) => void;
22
+ onError?: (error: string, command?: string) => void;
23
+ }
24
+
25
+ /**
26
+ * Base response interface for all API responses
27
+ */
28
+ export interface BaseApiResponse {
29
+ success: boolean;
30
+ timestamp: string;
31
+ }
32
+
33
+ /**
34
+ * Standard error response structure - matches BaseHandler.createErrorResponse()
35
+ */
36
+ export interface ApiErrorResponse {
37
+ success: false;
38
+ error: string;
39
+ code: string;
40
+ details?: any;
41
+ timestamp: string;
42
+ }
43
+
44
+ /**
45
+ * Validation error response structure - matches ValidationMiddleware
46
+ */
47
+ export interface ValidationErrorResponse {
48
+ error: string;
49
+ message: string;
50
+ details?: any[];
51
+ timestamp: string;
52
+ }
53
+
54
+ /**
55
+ * Legacy error response interface - deprecated, use ApiErrorResponse
56
+ */
57
+ export interface ErrorResponse {
58
+ error: string;
59
+ details?: string;
60
+ code?: string;
61
+ }
62
+
63
+ /**
64
+ * HTTP request configuration
65
+ */
66
+ export interface RequestConfig extends RequestInit {
67
+ endpoint: string;
68
+ data?: Record<string, any>;
69
+ }
70
+
71
+ /**
72
+ * Typed response handler
73
+ */
74
+ export type ResponseHandler<T> = (response: Response) => Promise<T>;
75
+
76
+ /**
77
+ * Common session-aware request interface
78
+ */
79
+ export interface SessionRequest {
80
+ sessionId?: string;
81
+ }
@@ -0,0 +1,97 @@
1
+ import { BaseHttpClient } from './base-client';
2
+ import type { BaseApiResponse, HttpClientOptions } from './types';
3
+
4
+ /**
5
+ * Response interface for ping operations
6
+ */
7
+ export interface PingResponse extends BaseApiResponse {
8
+ message: string;
9
+ uptime?: number;
10
+ }
11
+
12
+ /**
13
+ * Response interface for getting available commands
14
+ */
15
+ export interface CommandsResponse extends BaseApiResponse {
16
+ availableCommands: string[];
17
+ count: number;
18
+ }
19
+
20
+ /**
21
+ * Request interface for creating sessions
22
+ */
23
+ export interface CreateSessionRequest {
24
+ id: string;
25
+ env?: Record<string, string>;
26
+ cwd?: string;
27
+ }
28
+
29
+ /**
30
+ * Response interface for creating sessions
31
+ */
32
+ export interface CreateSessionResponse extends BaseApiResponse {
33
+ id: string;
34
+ message: string;
35
+ }
36
+
37
+ /**
38
+ * Client for health checks and utility operations
39
+ */
40
+ export class UtilityClient extends BaseHttpClient {
41
+ constructor(options: HttpClientOptions = {}) {
42
+ super(options);
43
+ }
44
+
45
+ /**
46
+ * Ping the sandbox to check if it's responsive
47
+ */
48
+ async ping(): Promise<string> {
49
+ try {
50
+ const response = await this.get<PingResponse>('/api/ping');
51
+
52
+ this.logSuccess('Ping successful', response.message);
53
+ return response.message;
54
+ } catch (error) {
55
+ this.logError('ping', error);
56
+ throw error;
57
+ }
58
+ }
59
+
60
+ /**
61
+ * Get list of available commands in the sandbox environment
62
+ */
63
+ async getCommands(): Promise<string[]> {
64
+ try {
65
+ const response = await this.get<CommandsResponse>('/api/commands');
66
+
67
+ this.logSuccess(
68
+ 'Commands retrieved',
69
+ `${response.count} commands available`
70
+ );
71
+
72
+ return response.availableCommands;
73
+ } catch (error) {
74
+ this.logError('getCommands', error);
75
+ throw error;
76
+ }
77
+ }
78
+
79
+ /**
80
+ * Create a new execution session
81
+ * @param options - Session configuration (id, env, cwd)
82
+ */
83
+ async createSession(options: CreateSessionRequest): Promise<CreateSessionResponse> {
84
+ try {
85
+ const response = await this.post<CreateSessionResponse>(
86
+ '/api/session/create',
87
+ options
88
+ );
89
+
90
+ this.logSuccess('Session created', `ID: ${options.id}`);
91
+ return response;
92
+ } catch (error) {
93
+ this.logError('createSession', error);
94
+ throw error;
95
+ }
96
+ }
97
+ }
@@ -0,0 +1,180 @@
1
+ /**
2
+ * Error adapter that converts ErrorResponse to appropriate Error class
3
+ *
4
+ * Simple switch statement - we trust the container sends correct context
5
+ * No validation overhead since we control both sides
6
+ */
7
+
8
+ import type {
9
+ CodeExecutionContext,
10
+ CommandErrorContext,
11
+ CommandNotFoundContext,
12
+ ContextNotFoundContext,ErrorResponse,
13
+ FileExistsContext,
14
+ FileNotFoundContext,
15
+ FileSystemContext,
16
+ GitAuthFailedContext,
17
+ GitBranchNotFoundContext,
18
+ GitErrorContext,
19
+ GitRepositoryNotFoundContext,
20
+ InternalErrorContext,
21
+ InterpreterNotReadyContext,
22
+ InvalidPortContext,
23
+ PortAlreadyExposedContext,
24
+ PortErrorContext,
25
+ PortNotExposedContext,
26
+ ProcessErrorContext,
27
+ ProcessNotFoundContext,
28
+ ValidationFailedContext,} from '@repo/shared/errors';
29
+ import { ErrorCode } from '@repo/shared/errors';
30
+
31
+ import {
32
+ CodeExecutionError,
33
+ CommandError,
34
+ CommandNotFoundError,
35
+ ContextNotFoundError,
36
+ CustomDomainRequiredError,
37
+ FileExistsError,
38
+ FileNotFoundError,
39
+ FileSystemError,
40
+ GitAuthenticationError,
41
+ GitBranchNotFoundError,
42
+ GitCheckoutError,
43
+ GitCloneError,
44
+ GitError,
45
+ GitNetworkError,
46
+ GitRepositoryNotFoundError,
47
+ InterpreterNotReadyError,
48
+ InvalidGitUrlError,
49
+ InvalidPortError,
50
+ PermissionDeniedError,
51
+ PortAlreadyExposedError,
52
+ PortError,
53
+ PortInUseError,
54
+ PortNotExposedError,
55
+ ProcessError,
56
+ ProcessNotFoundError,
57
+ SandboxError,
58
+ ServiceNotRespondingError,
59
+ ValidationFailedError,
60
+ } from './classes';
61
+
62
+ /**
63
+ * Convert ErrorResponse to appropriate Error class
64
+ * Simple switch statement - we trust the container sends correct context
65
+ */
66
+ export function createErrorFromResponse(errorResponse: ErrorResponse): Error {
67
+ // We trust the container sends correct context, use type assertions
68
+ switch (errorResponse.code) {
69
+ // File System Errors
70
+ case ErrorCode.FILE_NOT_FOUND:
71
+ return new FileNotFoundError(errorResponse as unknown as ErrorResponse<FileNotFoundContext>);
72
+
73
+ case ErrorCode.FILE_EXISTS:
74
+ return new FileExistsError(errorResponse as unknown as ErrorResponse<FileExistsContext>);
75
+
76
+ case ErrorCode.PERMISSION_DENIED:
77
+ return new PermissionDeniedError(errorResponse as unknown as ErrorResponse<FileSystemContext>);
78
+
79
+ case ErrorCode.IS_DIRECTORY:
80
+ case ErrorCode.NOT_DIRECTORY:
81
+ case ErrorCode.NO_SPACE:
82
+ case ErrorCode.TOO_MANY_FILES:
83
+ case ErrorCode.RESOURCE_BUSY:
84
+ case ErrorCode.READ_ONLY:
85
+ case ErrorCode.NAME_TOO_LONG:
86
+ case ErrorCode.TOO_MANY_LINKS:
87
+ case ErrorCode.FILESYSTEM_ERROR:
88
+ return new FileSystemError(errorResponse as unknown as ErrorResponse<FileSystemContext>);
89
+
90
+ // Command Errors
91
+ case ErrorCode.COMMAND_NOT_FOUND:
92
+ return new CommandNotFoundError(errorResponse as unknown as ErrorResponse<CommandNotFoundContext>);
93
+
94
+ case ErrorCode.COMMAND_PERMISSION_DENIED:
95
+ case ErrorCode.COMMAND_EXECUTION_ERROR:
96
+ case ErrorCode.INVALID_COMMAND:
97
+ case ErrorCode.STREAM_START_ERROR:
98
+ return new CommandError(errorResponse as unknown as ErrorResponse<CommandErrorContext>);
99
+
100
+ // Process Errors
101
+ case ErrorCode.PROCESS_NOT_FOUND:
102
+ return new ProcessNotFoundError(errorResponse as unknown as ErrorResponse<ProcessNotFoundContext>);
103
+
104
+ case ErrorCode.PROCESS_PERMISSION_DENIED:
105
+ case ErrorCode.PROCESS_ERROR:
106
+ return new ProcessError(errorResponse as unknown as ErrorResponse<ProcessErrorContext>);
107
+
108
+ // Port Errors
109
+ case ErrorCode.PORT_ALREADY_EXPOSED:
110
+ return new PortAlreadyExposedError(errorResponse as unknown as ErrorResponse<PortAlreadyExposedContext>);
111
+
112
+ case ErrorCode.PORT_NOT_EXPOSED:
113
+ return new PortNotExposedError(errorResponse as unknown as ErrorResponse<PortNotExposedContext>);
114
+
115
+ case ErrorCode.INVALID_PORT_NUMBER:
116
+ case ErrorCode.INVALID_PORT:
117
+ return new InvalidPortError(errorResponse as unknown as ErrorResponse<InvalidPortContext>);
118
+
119
+ case ErrorCode.SERVICE_NOT_RESPONDING:
120
+ return new ServiceNotRespondingError(errorResponse as unknown as ErrorResponse<PortErrorContext>);
121
+
122
+ case ErrorCode.PORT_IN_USE:
123
+ return new PortInUseError(errorResponse as unknown as ErrorResponse<PortErrorContext>);
124
+
125
+ case ErrorCode.PORT_OPERATION_ERROR:
126
+ return new PortError(errorResponse as unknown as ErrorResponse<PortErrorContext>);
127
+
128
+ case ErrorCode.CUSTOM_DOMAIN_REQUIRED:
129
+ return new CustomDomainRequiredError(errorResponse as unknown as ErrorResponse<InternalErrorContext>);
130
+
131
+ // Git Errors
132
+ case ErrorCode.GIT_REPOSITORY_NOT_FOUND:
133
+ return new GitRepositoryNotFoundError(errorResponse as unknown as ErrorResponse<GitRepositoryNotFoundContext>);
134
+
135
+ case ErrorCode.GIT_AUTH_FAILED:
136
+ return new GitAuthenticationError(errorResponse as unknown as ErrorResponse<GitAuthFailedContext>);
137
+
138
+ case ErrorCode.GIT_BRANCH_NOT_FOUND:
139
+ return new GitBranchNotFoundError(errorResponse as unknown as ErrorResponse<GitBranchNotFoundContext>);
140
+
141
+ case ErrorCode.GIT_NETWORK_ERROR:
142
+ return new GitNetworkError(errorResponse as unknown as ErrorResponse<GitErrorContext>);
143
+
144
+ case ErrorCode.GIT_CLONE_FAILED:
145
+ return new GitCloneError(errorResponse as unknown as ErrorResponse<GitErrorContext>);
146
+
147
+ case ErrorCode.GIT_CHECKOUT_FAILED:
148
+ return new GitCheckoutError(errorResponse as unknown as ErrorResponse<GitErrorContext>);
149
+
150
+ case ErrorCode.INVALID_GIT_URL:
151
+ return new InvalidGitUrlError(errorResponse as unknown as ErrorResponse<ValidationFailedContext>);
152
+
153
+ case ErrorCode.GIT_OPERATION_FAILED:
154
+ return new GitError(errorResponse as unknown as ErrorResponse<GitErrorContext>);
155
+
156
+ // Code Interpreter Errors
157
+ case ErrorCode.INTERPRETER_NOT_READY:
158
+ return new InterpreterNotReadyError(errorResponse as unknown as ErrorResponse<InterpreterNotReadyContext>);
159
+
160
+ case ErrorCode.CONTEXT_NOT_FOUND:
161
+ return new ContextNotFoundError(errorResponse as unknown as ErrorResponse<ContextNotFoundContext>);
162
+
163
+ case ErrorCode.CODE_EXECUTION_ERROR:
164
+ return new CodeExecutionError(errorResponse as unknown as ErrorResponse<CodeExecutionContext>);
165
+
166
+ // Validation Errors
167
+ case ErrorCode.VALIDATION_FAILED:
168
+ return new ValidationFailedError(errorResponse as unknown as ErrorResponse<ValidationFailedContext>);
169
+
170
+ // Generic Errors
171
+ case ErrorCode.INVALID_JSON_RESPONSE:
172
+ case ErrorCode.UNKNOWN_ERROR:
173
+ case ErrorCode.INTERNAL_ERROR:
174
+ return new SandboxError(errorResponse as unknown as ErrorResponse<InternalErrorContext>);
175
+
176
+ default:
177
+ // Fallback for unknown error codes
178
+ return new SandboxError(errorResponse);
179
+ }
180
+ }