@cloudflare/sandbox 0.0.9 → 0.1.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.
- package/CHANGELOG.md +18 -0
- package/Dockerfile +1 -14
- package/container_src/handler/exec.ts +337 -0
- package/container_src/handler/file.ts +844 -0
- package/container_src/handler/git.ts +182 -0
- package/container_src/handler/ports.ts +314 -0
- package/container_src/handler/process.ts +640 -0
- package/container_src/index.ts +82 -2973
- package/container_src/types.ts +103 -0
- package/dist/chunk-6THNBO4S.js +46 -0
- package/dist/chunk-6THNBO4S.js.map +1 -0
- package/dist/chunk-6UAWTJ5S.js +85 -0
- package/dist/chunk-6UAWTJ5S.js.map +1 -0
- package/dist/chunk-G4XT4SP7.js +638 -0
- package/dist/chunk-G4XT4SP7.js.map +1 -0
- package/dist/chunk-ISFOIYQC.js +585 -0
- package/dist/chunk-ISFOIYQC.js.map +1 -0
- package/dist/chunk-NNGBXDMY.js +89 -0
- package/dist/chunk-NNGBXDMY.js.map +1 -0
- package/dist/client-Da-mLX4p.d.ts +210 -0
- package/dist/client.d.ts +2 -1
- package/dist/client.js +3 -37
- package/dist/index.d.ts +3 -1
- package/dist/index.js +13 -3
- package/dist/request-handler.d.ts +2 -1
- package/dist/request-handler.js +4 -2
- package/dist/sandbox.d.ts +2 -1
- package/dist/sandbox.js +4 -2
- package/dist/security.d.ts +30 -0
- package/dist/security.js +13 -0
- package/dist/security.js.map +1 -0
- package/dist/sse-parser.d.ts +28 -0
- package/dist/sse-parser.js +11 -0
- package/dist/sse-parser.js.map +1 -0
- package/dist/types.d.ts +284 -0
- package/dist/types.js +19 -0
- package/dist/types.js.map +1 -0
- package/package.json +2 -7
- package/src/client.ts +235 -1286
- package/src/index.ts +6 -0
- package/src/request-handler.ts +69 -20
- package/src/sandbox.ts +463 -70
- package/src/security.ts +113 -0
- package/src/sse-parser.ts +147 -0
- package/src/types.ts +386 -0
- package/tsconfig.json +1 -1
- package/README.md +0 -65
- package/dist/chunk-4J5LQCCN.js +0 -1446
- package/dist/chunk-4J5LQCCN.js.map +0 -1
- package/dist/chunk-5SZ3RVJZ.js +0 -250
- package/dist/chunk-5SZ3RVJZ.js.map +0 -1
- package/dist/client-BuVjqV00.d.ts +0 -247
- package/tests/client.example.ts +0 -308
- package/tests/connection-test.ts +0 -81
- package/tests/simple-test.ts +0 -81
- package/tests/test1.ts +0 -281
- package/tests/test2.ts +0 -929
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/client.ts"],"sourcesContent":["import type { Sandbox } from \"./index\";\nimport type {\n GetProcessLogsResponse,\n GetProcessResponse,\n ListProcessesResponse,\n StartProcessRequest,\n StartProcessResponse\n} from \"./types\";\n\ninterface ExecuteRequest {\n command: string;\n sessionId?: string;\n}\n\nexport interface ExecuteResponse {\n success: boolean;\n stdout: string;\n stderr: string;\n exitCode: number;\n command: string;\n timestamp: string;\n}\n\ninterface SessionResponse {\n sessionId: string;\n message: string;\n timestamp: string;\n}\n\ninterface SessionListResponse {\n sessions: Array<{\n sessionId: string;\n hasActiveProcess: boolean;\n createdAt: string;\n }>;\n count: number;\n timestamp: string;\n}\n\ninterface CommandsResponse {\n availableCommands: string[];\n timestamp: string;\n}\n\ninterface GitCheckoutRequest {\n repoUrl: string;\n branch?: string;\n targetDir?: string;\n sessionId?: string;\n}\n\nexport interface GitCheckoutResponse {\n success: boolean;\n stdout: string;\n stderr: string;\n exitCode: number;\n repoUrl: string;\n branch: string;\n targetDir: string;\n timestamp: string;\n}\n\ninterface MkdirRequest {\n path: string;\n recursive?: boolean;\n sessionId?: string;\n}\n\nexport interface MkdirResponse {\n success: boolean;\n stdout: string;\n stderr: string;\n exitCode: number;\n path: string;\n recursive: boolean;\n timestamp: string;\n}\n\ninterface WriteFileRequest {\n path: string;\n content: string;\n encoding?: string;\n sessionId?: string;\n}\n\nexport interface WriteFileResponse {\n success: boolean;\n exitCode: number;\n path: string;\n timestamp: string;\n}\n\ninterface ReadFileRequest {\n path: string;\n encoding?: string;\n sessionId?: string;\n}\n\nexport interface ReadFileResponse {\n success: boolean;\n exitCode: number;\n path: string;\n content: string;\n timestamp: string;\n}\n\ninterface DeleteFileRequest {\n path: string;\n sessionId?: string;\n}\n\nexport interface DeleteFileResponse {\n success: boolean;\n exitCode: number;\n path: string;\n timestamp: string;\n}\n\ninterface RenameFileRequest {\n oldPath: string;\n newPath: string;\n sessionId?: string;\n}\n\nexport interface RenameFileResponse {\n success: boolean;\n exitCode: number;\n oldPath: string;\n newPath: string;\n timestamp: string;\n}\n\ninterface MoveFileRequest {\n sourcePath: string;\n destinationPath: string;\n sessionId?: string;\n}\n\nexport interface MoveFileResponse {\n success: boolean;\n exitCode: number;\n sourcePath: string;\n destinationPath: string;\n timestamp: string;\n}\n\ninterface PreviewInfo {\n url: string;\n port: number;\n name?: string;\n}\n\ninterface ExposedPort extends PreviewInfo {\n exposedAt: string;\n timestamp: string;\n}\n\ninterface ExposePortResponse {\n success: boolean;\n port: number;\n name?: string;\n exposedAt: string;\n timestamp: string;\n}\n\ninterface UnexposePortResponse {\n success: boolean;\n port: number;\n timestamp: string;\n}\n\ninterface GetExposedPortsResponse {\n ports: ExposedPort[];\n count: number;\n timestamp: string;\n}\n\ninterface PingResponse {\n message: string;\n timestamp: string;\n}\n\ninterface HttpClientOptions {\n stub?: Sandbox;\n baseUrl?: string;\n port?: number;\n onCommandStart?: (command: string) => void;\n onOutput?: (\n stream: \"stdout\" | \"stderr\",\n data: string,\n command: string\n ) => void;\n onCommandComplete?: (\n success: boolean,\n exitCode: number,\n stdout: string,\n stderr: string,\n command: string\n ) => void;\n onError?: (error: string, command?: string) => void;\n}\n\nexport class HttpClient {\n private baseUrl: string;\n private options: HttpClientOptions;\n private sessionId: string | null = null;\n\n constructor(options: HttpClientOptions = {}) {\n this.options = {\n ...options,\n };\n this.baseUrl = this.options.baseUrl!;\n }\n\n private async doFetch(\n path: string,\n options?: RequestInit\n ): Promise<Response> {\n const url = this.options.stub\n ? `http://localhost:${this.options.port}${path}`\n : `${this.baseUrl}${path}`;\n const method = options?.method || \"GET\";\n\n console.log(`[HTTP Client] Making ${method} request to ${url}`);\n\n try {\n let response: Response;\n\n if (this.options.stub) {\n response = await this.options.stub.containerFetch(\n url,\n options,\n this.options.port\n );\n } else {\n response = await fetch(url, options);\n }\n\n console.log(\n `[HTTP Client] Response: ${response.status} ${response.statusText}`\n );\n\n if (!response.ok) {\n console.error(\n `[HTTP Client] Request failed: ${method} ${url} - ${response.status} ${response.statusText}`\n );\n }\n\n return response;\n } catch (error) {\n console.error(`[HTTP Client] Request error: ${method} ${url}`, error);\n throw error;\n }\n }\n\n async execute(\n command: string,\n sessionId?: string\n ): Promise<ExecuteResponse> {\n try {\n const targetSessionId = sessionId || this.sessionId;\n\n const response = await this.doFetch(`/api/execute`, {\n body: JSON.stringify({\n command,\n sessionId: targetSessionId,\n } as ExecuteRequest),\n headers: {\n \"Content-Type\": \"application/json\",\n },\n method: \"POST\",\n });\n\n if (!response.ok) {\n const errorData = (await response.json().catch(() => ({}))) as {\n error?: string;\n };\n throw new Error(\n errorData.error || `HTTP error! status: ${response.status}`\n );\n }\n\n const data: ExecuteResponse = await response.json();\n console.log(\n `[HTTP Client] Command executed: ${command}, Success: ${data.success}`\n );\n\n // Call the callback if provided\n this.options.onCommandComplete?.(\n data.success,\n data.exitCode,\n data.stdout,\n data.stderr,\n data.command\n );\n\n return data;\n } catch (error) {\n console.error(\"[HTTP Client] Error executing command:\", error);\n this.options.onError?.(\n error instanceof Error ? error.message : \"Unknown error\",\n command\n );\n throw error;\n }\n }\n\n\n async executeCommandStream(\n command: string,\n sessionId?: string\n ): Promise<ReadableStream<Uint8Array>> {\n try {\n const targetSessionId = sessionId || this.sessionId;\n\n const response = await this.doFetch(`/api/execute/stream`, {\n body: JSON.stringify({\n command,\n sessionId: targetSessionId,\n }),\n headers: {\n \"Content-Type\": \"application/json\",\n \"Accept\": \"text/event-stream\",\n },\n method: \"POST\",\n });\n\n if (!response.ok) {\n const errorData = (await response.json().catch(() => ({}))) as {\n error?: string;\n };\n throw new Error(\n errorData.error || `HTTP error! status: ${response.status}`\n );\n }\n\n if (!response.body) {\n throw new Error(\"No response body for streaming request\");\n }\n\n console.log(\n `[HTTP Client] Started command stream: ${command}`\n );\n\n return response.body;\n } catch (error) {\n console.error(\"[HTTP Client] Error in command stream:\", error);\n throw error;\n }\n }\n\n async gitCheckout(\n repoUrl: string,\n branch: string = \"main\",\n targetDir?: string,\n sessionId?: string\n ): Promise<GitCheckoutResponse> {\n try {\n const targetSessionId = sessionId || this.sessionId;\n\n const response = await this.doFetch(`/api/git/checkout`, {\n body: JSON.stringify({\n branch,\n repoUrl,\n sessionId: targetSessionId,\n targetDir,\n } as GitCheckoutRequest),\n headers: {\n \"Content-Type\": \"application/json\",\n },\n method: \"POST\",\n });\n\n if (!response.ok) {\n const errorData = (await response.json().catch(() => ({}))) as {\n error?: string;\n };\n throw new Error(\n errorData.error || `HTTP error! status: ${response.status}`\n );\n }\n\n const data: GitCheckoutResponse = await response.json();\n console.log(\n `[HTTP Client] Git checkout completed: ${repoUrl}, Success: ${data.success}, Target: ${data.targetDir}`\n );\n\n return data;\n } catch (error) {\n console.error(\"[HTTP Client] Error in git checkout:\", error);\n throw error;\n }\n }\n\n\n async mkdir(\n path: string,\n recursive: boolean = false,\n sessionId?: string\n ): Promise<MkdirResponse> {\n try {\n const targetSessionId = sessionId || this.sessionId;\n\n const response = await this.doFetch(`/api/mkdir`, {\n body: JSON.stringify({\n path,\n recursive,\n sessionId: targetSessionId,\n } as MkdirRequest),\n headers: {\n \"Content-Type\": \"application/json\",\n },\n method: \"POST\",\n });\n\n if (!response.ok) {\n const errorData = (await response.json().catch(() => ({}))) as {\n error?: string;\n };\n throw new Error(\n errorData.error || `HTTP error! status: ${response.status}`\n );\n }\n\n const data: MkdirResponse = await response.json();\n console.log(\n `[HTTP Client] Directory created: ${path}, Success: ${data.success}, Recursive: ${data.recursive}`\n );\n\n return data;\n } catch (error) {\n console.error(\"[HTTP Client] Error creating directory:\", error);\n throw error;\n }\n }\n\n\n async writeFile(\n path: string,\n content: string,\n encoding: string = \"utf-8\",\n sessionId?: string\n ): Promise<WriteFileResponse> {\n try {\n const targetSessionId = sessionId || this.sessionId;\n\n const response = await this.doFetch(`/api/write`, {\n body: JSON.stringify({\n content,\n encoding,\n path,\n sessionId: targetSessionId,\n } as WriteFileRequest),\n headers: {\n \"Content-Type\": \"application/json\",\n },\n method: \"POST\",\n });\n\n if (!response.ok) {\n const errorData = (await response.json().catch(() => ({}))) as {\n error?: string;\n };\n throw new Error(\n errorData.error || `HTTP error! status: ${response.status}`\n );\n }\n\n const data: WriteFileResponse = await response.json();\n console.log(\n `[HTTP Client] File written: ${path}, Success: ${data.success}`\n );\n\n return data;\n } catch (error) {\n console.error(\"[HTTP Client] Error writing file:\", error);\n throw error;\n }\n }\n\n\n async readFile(\n path: string,\n encoding: string = \"utf-8\",\n sessionId?: string\n ): Promise<ReadFileResponse> {\n try {\n const targetSessionId = sessionId || this.sessionId;\n\n const response = await this.doFetch(`/api/read`, {\n body: JSON.stringify({\n encoding,\n path,\n sessionId: targetSessionId,\n } as ReadFileRequest),\n headers: {\n \"Content-Type\": \"application/json\",\n },\n method: \"POST\",\n });\n\n if (!response.ok) {\n const errorData = (await response.json().catch(() => ({}))) as {\n error?: string;\n };\n throw new Error(\n errorData.error || `HTTP error! status: ${response.status}`\n );\n }\n\n const data: ReadFileResponse = await response.json();\n console.log(\n `[HTTP Client] File read: ${path}, Success: ${data.success}, Content length: ${data.content.length}`\n );\n\n return data;\n } catch (error) {\n console.error(\"[HTTP Client] Error reading file:\", error);\n throw error;\n }\n }\n\n\n async deleteFile(\n path: string,\n sessionId?: string\n ): Promise<DeleteFileResponse> {\n try {\n const targetSessionId = sessionId || this.sessionId;\n\n const response = await this.doFetch(`/api/delete`, {\n body: JSON.stringify({\n path,\n sessionId: targetSessionId,\n } as DeleteFileRequest),\n headers: {\n \"Content-Type\": \"application/json\",\n },\n method: \"POST\",\n });\n\n if (!response.ok) {\n const errorData = (await response.json().catch(() => ({}))) as {\n error?: string;\n };\n throw new Error(\n errorData.error || `HTTP error! status: ${response.status}`\n );\n }\n\n const data: DeleteFileResponse = await response.json();\n console.log(\n `[HTTP Client] File deleted: ${path}, Success: ${data.success}`\n );\n\n return data;\n } catch (error) {\n console.error(\"[HTTP Client] Error deleting file:\", error);\n throw error;\n }\n }\n\n\n async renameFile(\n oldPath: string,\n newPath: string,\n sessionId?: string\n ): Promise<RenameFileResponse> {\n try {\n const targetSessionId = sessionId || this.sessionId;\n\n const response = await this.doFetch(`/api/rename`, {\n body: JSON.stringify({\n newPath,\n oldPath,\n sessionId: targetSessionId,\n } as RenameFileRequest),\n headers: {\n \"Content-Type\": \"application/json\",\n },\n method: \"POST\",\n });\n\n if (!response.ok) {\n const errorData = (await response.json().catch(() => ({}))) as {\n error?: string;\n };\n throw new Error(\n errorData.error || `HTTP error! status: ${response.status}`\n );\n }\n\n const data: RenameFileResponse = await response.json();\n console.log(\n `[HTTP Client] File renamed: ${oldPath} -> ${newPath}, Success: ${data.success}`\n );\n\n return data;\n } catch (error) {\n console.error(\"[HTTP Client] Error renaming file:\", error);\n throw error;\n }\n }\n\n\n async moveFile(\n sourcePath: string,\n destinationPath: string,\n sessionId?: string\n ): Promise<MoveFileResponse> {\n try {\n const targetSessionId = sessionId || this.sessionId;\n\n const response = await this.doFetch(`/api/move`, {\n body: JSON.stringify({\n destinationPath,\n sessionId: targetSessionId,\n sourcePath,\n } as MoveFileRequest),\n headers: {\n \"Content-Type\": \"application/json\",\n },\n method: \"POST\",\n });\n\n if (!response.ok) {\n const errorData = (await response.json().catch(() => ({}))) as {\n error?: string;\n };\n throw new Error(\n errorData.error || `HTTP error! status: ${response.status}`\n );\n }\n\n const data: MoveFileResponse = await response.json();\n console.log(\n `[HTTP Client] File moved: ${sourcePath} -> ${destinationPath}, Success: ${data.success}`\n );\n\n return data;\n } catch (error) {\n console.error(\"[HTTP Client] Error moving file:\", error);\n throw error;\n }\n }\n\n\n async exposePort(port: number, name?: string): Promise<ExposePortResponse> {\n try {\n const response = await this.doFetch(`/api/expose-port`, {\n body: JSON.stringify({\n port,\n name,\n }),\n headers: {\n \"Content-Type\": \"application/json\",\n },\n method: \"POST\",\n });\n\n if (!response.ok) {\n const errorData = (await response.json().catch(() => ({}))) as {\n error?: string;\n };\n console.log(errorData);\n throw new Error(\n errorData.error || `HTTP error! status: ${response.status}`\n );\n }\n\n const data: ExposePortResponse = await response.json();\n console.log(\n `[HTTP Client] Port exposed: ${port}${name ? ` (${name})` : \"\"}, Success: ${data.success}`\n );\n\n return data;\n } catch (error) {\n console.error(\"[HTTP Client] Error exposing port:\", error);\n throw error;\n }\n }\n\n async unexposePort(port: number): Promise<UnexposePortResponse> {\n try {\n const response = await this.doFetch(`/api/unexpose-port`, {\n body: JSON.stringify({\n port,\n }),\n headers: {\n \"Content-Type\": \"application/json\",\n },\n method: \"DELETE\",\n });\n\n if (!response.ok) {\n const errorData = (await response.json().catch(() => ({}))) as {\n error?: string;\n };\n throw new Error(\n errorData.error || `HTTP error! status: ${response.status}`\n );\n }\n\n const data: UnexposePortResponse = await response.json();\n console.log(\n `[HTTP Client] Port unexposed: ${port}, Success: ${data.success}`\n );\n\n return data;\n } catch (error) {\n console.error(\"[HTTP Client] Error unexposing port:\", error);\n throw error;\n }\n }\n\n async getExposedPorts(): Promise<GetExposedPortsResponse> {\n try {\n const response = await this.doFetch(`/api/exposed-ports`, {\n headers: {\n \"Content-Type\": \"application/json\",\n },\n method: \"GET\",\n });\n\n if (!response.ok) {\n const errorData = (await response.json().catch(() => ({}))) as {\n error?: string;\n };\n throw new Error(\n errorData.error || `HTTP error! status: ${response.status}`\n );\n }\n\n const data: GetExposedPortsResponse = await response.json();\n console.log(\n `[HTTP Client] Got ${data.count} exposed ports`\n );\n\n return data;\n } catch (error) {\n console.error(\"[HTTP Client] Error getting exposed ports:\", error);\n throw error;\n }\n }\n\n async ping(): Promise<string> {\n try {\n const response = await this.doFetch(`/api/ping`, {\n headers: {\n \"Content-Type\": \"application/json\",\n },\n method: \"GET\",\n });\n\n if (!response.ok) {\n throw new Error(`HTTP error! status: ${response.status}`);\n }\n\n const data: PingResponse = await response.json();\n console.log(`[HTTP Client] Ping response: ${data.message}`);\n return data.timestamp;\n } catch (error) {\n console.error(\"[HTTP Client] Error pinging server:\", error);\n throw error;\n }\n }\n\n async getCommands(): Promise<string[]> {\n try {\n const response = await fetch(`${this.baseUrl}/api/commands`, {\n headers: {\n \"Content-Type\": \"application/json\",\n },\n method: \"GET\",\n });\n\n if (!response.ok) {\n throw new Error(`HTTP error! status: ${response.status}`);\n }\n\n const data: CommandsResponse = await response.json();\n console.log(\n `[HTTP Client] Available commands: ${data.availableCommands.length}`\n );\n return data.availableCommands;\n } catch (error) {\n console.error(\"[HTTP Client] Error getting commands:\", error);\n throw error;\n }\n }\n\n getSessionId(): string | null {\n return this.sessionId;\n }\n\n setSessionId(sessionId: string): void {\n this.sessionId = sessionId;\n }\n\n clearSession(): void {\n this.sessionId = null;\n }\n\n // Process management methods\n async startProcess(\n command: string,\n options?: {\n processId?: string;\n sessionId?: string;\n timeout?: number;\n env?: Record<string, string>;\n cwd?: string;\n encoding?: string;\n autoCleanup?: boolean;\n }\n ): Promise<StartProcessResponse> {\n try {\n const targetSessionId = options?.sessionId || this.sessionId;\n\n const response = await this.doFetch(\"/api/process/start\", {\n body: JSON.stringify({\n command,\n options: {\n ...options,\n sessionId: targetSessionId,\n },\n } as StartProcessRequest),\n headers: {\n \"Content-Type\": \"application/json\",\n },\n method: \"POST\",\n });\n\n if (!response.ok) {\n const errorData = (await response.json().catch(() => ({}))) as {\n error?: string;\n };\n throw new Error(\n errorData.error || `HTTP error! status: ${response.status}`\n );\n }\n\n const data: StartProcessResponse = await response.json();\n console.log(\n `[HTTP Client] Process started: ${command}, ID: ${data.process.id}`\n );\n\n return data;\n } catch (error) {\n console.error(\"[HTTP Client] Error starting process:\", error);\n throw error;\n }\n }\n\n async listProcesses(): Promise<ListProcessesResponse> {\n try {\n const response = await this.doFetch(\"/api/process/list\", {\n headers: {\n \"Content-Type\": \"application/json\",\n },\n method: \"GET\",\n });\n\n if (!response.ok) {\n const errorData = (await response.json().catch(() => ({}))) as {\n error?: string;\n };\n throw new Error(\n errorData.error || `HTTP error! status: ${response.status}`\n );\n }\n\n const data: ListProcessesResponse = await response.json();\n console.log(\n `[HTTP Client] Listed ${data.processes.length} processes`\n );\n\n return data;\n } catch (error) {\n console.error(\"[HTTP Client] Error listing processes:\", error);\n throw error;\n }\n }\n\n async getProcess(processId: string): Promise<GetProcessResponse> {\n try {\n const response = await this.doFetch(`/api/process/${processId}`, {\n headers: {\n \"Content-Type\": \"application/json\",\n },\n method: \"GET\",\n });\n\n if (!response.ok) {\n const errorData = (await response.json().catch(() => ({}))) as {\n error?: string;\n };\n throw new Error(\n errorData.error || `HTTP error! status: ${response.status}`\n );\n }\n\n const data: GetProcessResponse = await response.json();\n console.log(\n `[HTTP Client] Got process ${processId}: ${data.process?.status || 'not found'}`\n );\n\n return data;\n } catch (error) {\n console.error(\"[HTTP Client] Error getting process:\", error);\n throw error;\n }\n }\n\n async killProcess(processId: string): Promise<{ success: boolean; message: string }> {\n try {\n const response = await this.doFetch(`/api/process/${processId}`, {\n headers: {\n \"Content-Type\": \"application/json\",\n },\n method: \"DELETE\",\n });\n\n if (!response.ok) {\n const errorData = (await response.json().catch(() => ({}))) as {\n error?: string;\n };\n throw new Error(\n errorData.error || `HTTP error! status: ${response.status}`\n );\n }\n\n const data = await response.json() as { success: boolean; message: string };\n console.log(\n `[HTTP Client] Killed process ${processId}`\n );\n\n return data;\n } catch (error) {\n console.error(\"[HTTP Client] Error killing process:\", error);\n throw error;\n }\n }\n\n async killAllProcesses(): Promise<{ success: boolean; killedCount: number; message: string }> {\n try {\n const response = await this.doFetch(\"/api/process/kill-all\", {\n headers: {\n \"Content-Type\": \"application/json\",\n },\n method: \"DELETE\",\n });\n\n if (!response.ok) {\n const errorData = (await response.json().catch(() => ({}))) as {\n error?: string;\n };\n throw new Error(\n errorData.error || `HTTP error! status: ${response.status}`\n );\n }\n\n const data = await response.json() as { success: boolean; killedCount: number; message: string };\n console.log(\n `[HTTP Client] Killed ${data.killedCount} processes`\n );\n\n return data;\n } catch (error) {\n console.error(\"[HTTP Client] Error killing all processes:\", error);\n throw error;\n }\n }\n\n async getProcessLogs(processId: string): Promise<GetProcessLogsResponse> {\n try {\n const response = await this.doFetch(`/api/process/${processId}/logs`, {\n headers: {\n \"Content-Type\": \"application/json\",\n },\n method: \"GET\",\n });\n\n if (!response.ok) {\n const errorData = (await response.json().catch(() => ({}))) as {\n error?: string;\n };\n throw new Error(\n errorData.error || `HTTP error! status: ${response.status}`\n );\n }\n\n const data: GetProcessLogsResponse = await response.json();\n console.log(\n `[HTTP Client] Got logs for process ${processId}`\n );\n\n return data;\n } catch (error) {\n console.error(\"[HTTP Client] Error getting process logs:\", error);\n throw error;\n }\n }\n\n async streamProcessLogs(processId: string): Promise<ReadableStream<Uint8Array>> {\n try {\n const response = await this.doFetch(`/api/process/${processId}/stream`, {\n headers: {\n \"Accept\": \"text/event-stream\",\n \"Cache-Control\": \"no-cache\",\n },\n method: \"GET\",\n });\n\n if (!response.ok) {\n const errorData = (await response.json().catch(() => ({}))) as {\n error?: string;\n };\n throw new Error(\n errorData.error || `HTTP error! status: ${response.status}`\n );\n }\n\n if (!response.body) {\n throw new Error(\"No response body for streaming request\");\n }\n\n console.log(\n `[HTTP Client] Started streaming logs for process ${processId}`\n );\n\n return response.body;\n } catch (error) {\n console.error(\"[HTTP Client] Error streaming process logs:\", error);\n throw error;\n }\n }\n}\n"],"mappings":"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","names":[]}
|
|
@@ -0,0 +1,585 @@
|
|
|
1
|
+
import {
|
|
2
|
+
HttpClient
|
|
3
|
+
} from "./chunk-G4XT4SP7.js";
|
|
4
|
+
import {
|
|
5
|
+
SecurityError,
|
|
6
|
+
logSecurityEvent,
|
|
7
|
+
sanitizeSandboxId,
|
|
8
|
+
validatePort
|
|
9
|
+
} from "./chunk-6UAWTJ5S.js";
|
|
10
|
+
import {
|
|
11
|
+
ProcessNotFoundError,
|
|
12
|
+
SandboxError
|
|
13
|
+
} from "./chunk-6THNBO4S.js";
|
|
14
|
+
|
|
15
|
+
// src/sandbox.ts
|
|
16
|
+
import { Container, getContainer } from "@cloudflare/containers";
|
|
17
|
+
|
|
18
|
+
// src/request-handler.ts
|
|
19
|
+
async function proxyToSandbox(request, env) {
|
|
20
|
+
try {
|
|
21
|
+
const url = new URL(request.url);
|
|
22
|
+
const routeInfo = extractSandboxRoute(url);
|
|
23
|
+
if (!routeInfo) {
|
|
24
|
+
return null;
|
|
25
|
+
}
|
|
26
|
+
const { sandboxId, port, path } = routeInfo;
|
|
27
|
+
const sandbox = getSandbox(env.Sandbox, sandboxId);
|
|
28
|
+
let proxyUrl;
|
|
29
|
+
if (port !== 3e3) {
|
|
30
|
+
proxyUrl = `http://localhost:${port}${path}${url.search}`;
|
|
31
|
+
} else {
|
|
32
|
+
proxyUrl = `http://localhost:3000${path}${url.search}`;
|
|
33
|
+
}
|
|
34
|
+
const proxyRequest = new Request(proxyUrl, {
|
|
35
|
+
method: request.method,
|
|
36
|
+
headers: {
|
|
37
|
+
...Object.fromEntries(request.headers),
|
|
38
|
+
"X-Original-URL": request.url,
|
|
39
|
+
"X-Forwarded-Host": url.hostname,
|
|
40
|
+
"X-Forwarded-Proto": url.protocol.replace(":", ""),
|
|
41
|
+
"X-Sandbox-Name": sandboxId
|
|
42
|
+
// Pass the friendly name
|
|
43
|
+
},
|
|
44
|
+
body: request.body
|
|
45
|
+
});
|
|
46
|
+
return sandbox.containerFetch(proxyRequest, port);
|
|
47
|
+
} catch (error) {
|
|
48
|
+
console.error("[Sandbox] Proxy routing error:", error);
|
|
49
|
+
return new Response("Proxy routing error", { status: 500 });
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
function extractSandboxRoute(url) {
|
|
53
|
+
const subdomainMatch = url.hostname.match(/^(\d{4,5})-([^.-][^.]*[^.-]|[^.-])\.(.+)$/);
|
|
54
|
+
if (!subdomainMatch) {
|
|
55
|
+
if (url.hostname.includes("-") && url.hostname.includes(".")) {
|
|
56
|
+
logSecurityEvent("MALFORMED_SUBDOMAIN_ATTEMPT", {
|
|
57
|
+
hostname: url.hostname,
|
|
58
|
+
url: url.toString()
|
|
59
|
+
}, "medium");
|
|
60
|
+
}
|
|
61
|
+
return null;
|
|
62
|
+
}
|
|
63
|
+
const portStr = subdomainMatch[1];
|
|
64
|
+
const sandboxId = subdomainMatch[2];
|
|
65
|
+
const domain = subdomainMatch[3];
|
|
66
|
+
const port = parseInt(portStr, 10);
|
|
67
|
+
if (!validatePort(port)) {
|
|
68
|
+
logSecurityEvent("INVALID_PORT_IN_SUBDOMAIN", {
|
|
69
|
+
port,
|
|
70
|
+
portStr,
|
|
71
|
+
sandboxId,
|
|
72
|
+
hostname: url.hostname,
|
|
73
|
+
url: url.toString()
|
|
74
|
+
}, "high");
|
|
75
|
+
return null;
|
|
76
|
+
}
|
|
77
|
+
let sanitizedSandboxId;
|
|
78
|
+
try {
|
|
79
|
+
sanitizedSandboxId = sanitizeSandboxId(sandboxId);
|
|
80
|
+
} catch (error) {
|
|
81
|
+
logSecurityEvent("INVALID_SANDBOX_ID_IN_SUBDOMAIN", {
|
|
82
|
+
sandboxId,
|
|
83
|
+
port,
|
|
84
|
+
hostname: url.hostname,
|
|
85
|
+
url: url.toString(),
|
|
86
|
+
error: error instanceof Error ? error.message : "Unknown error"
|
|
87
|
+
}, "high");
|
|
88
|
+
return null;
|
|
89
|
+
}
|
|
90
|
+
if (sandboxId.length > 63) {
|
|
91
|
+
logSecurityEvent("SANDBOX_ID_LENGTH_VIOLATION", {
|
|
92
|
+
sandboxId,
|
|
93
|
+
length: sandboxId.length,
|
|
94
|
+
port,
|
|
95
|
+
hostname: url.hostname
|
|
96
|
+
}, "medium");
|
|
97
|
+
return null;
|
|
98
|
+
}
|
|
99
|
+
logSecurityEvent("SANDBOX_ROUTE_EXTRACTED", {
|
|
100
|
+
port,
|
|
101
|
+
sandboxId: sanitizedSandboxId,
|
|
102
|
+
domain,
|
|
103
|
+
path: url.pathname || "/",
|
|
104
|
+
hostname: url.hostname
|
|
105
|
+
}, "low");
|
|
106
|
+
return {
|
|
107
|
+
port,
|
|
108
|
+
sandboxId: sanitizedSandboxId,
|
|
109
|
+
path: url.pathname || "/"
|
|
110
|
+
};
|
|
111
|
+
}
|
|
112
|
+
function isLocalhostPattern(hostname) {
|
|
113
|
+
const hostPart = hostname.split(":")[0];
|
|
114
|
+
return hostPart === "localhost" || hostPart === "127.0.0.1" || hostPart === "::1" || hostPart === "[::1]" || hostPart === "0.0.0.0";
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
// src/sandbox.ts
|
|
118
|
+
function getSandbox(ns, id) {
|
|
119
|
+
const stub = getContainer(ns, id);
|
|
120
|
+
stub.setSandboxName?.(id);
|
|
121
|
+
return stub;
|
|
122
|
+
}
|
|
123
|
+
var Sandbox = class extends Container {
|
|
124
|
+
sleepAfter = "3m";
|
|
125
|
+
// Sleep the sandbox if no requests are made in this timeframe
|
|
126
|
+
client;
|
|
127
|
+
sandboxName = null;
|
|
128
|
+
constructor(ctx, env) {
|
|
129
|
+
super(ctx, env);
|
|
130
|
+
this.client = new HttpClient({
|
|
131
|
+
onCommandComplete: (success, exitCode, _stdout, _stderr, command) => {
|
|
132
|
+
console.log(
|
|
133
|
+
`[Container] Command completed: ${command}, Success: ${success}, Exit code: ${exitCode}`
|
|
134
|
+
);
|
|
135
|
+
},
|
|
136
|
+
onCommandStart: (command) => {
|
|
137
|
+
console.log(
|
|
138
|
+
`[Container] Command started: ${command}`
|
|
139
|
+
);
|
|
140
|
+
},
|
|
141
|
+
onError: (error, _command) => {
|
|
142
|
+
console.error(`[Container] Command error: ${error}`);
|
|
143
|
+
},
|
|
144
|
+
onOutput: (stream, data, _command) => {
|
|
145
|
+
console.log(`[Container] [${stream}] ${data}`);
|
|
146
|
+
},
|
|
147
|
+
port: 3e3,
|
|
148
|
+
// Control plane port
|
|
149
|
+
stub: this
|
|
150
|
+
});
|
|
151
|
+
this.ctx.blockConcurrencyWhile(async () => {
|
|
152
|
+
this.sandboxName = await this.ctx.storage.get("sandboxName") || null;
|
|
153
|
+
});
|
|
154
|
+
}
|
|
155
|
+
// RPC method to set the sandbox name
|
|
156
|
+
async setSandboxName(name) {
|
|
157
|
+
if (!this.sandboxName) {
|
|
158
|
+
this.sandboxName = name;
|
|
159
|
+
await this.ctx.storage.put("sandboxName", name);
|
|
160
|
+
console.log(`[Sandbox] Stored sandbox name via RPC: ${name}`);
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
// RPC method to set environment variables
|
|
164
|
+
async setEnvVars(envVars) {
|
|
165
|
+
this.envVars = { ...this.envVars, ...envVars };
|
|
166
|
+
console.log(`[Sandbox] Updated environment variables`);
|
|
167
|
+
}
|
|
168
|
+
onStart() {
|
|
169
|
+
console.log("Sandbox successfully started");
|
|
170
|
+
}
|
|
171
|
+
onStop() {
|
|
172
|
+
console.log("Sandbox successfully shut down");
|
|
173
|
+
if (this.client) {
|
|
174
|
+
this.client.clearSession();
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
onError(error) {
|
|
178
|
+
console.log("Sandbox error:", error);
|
|
179
|
+
}
|
|
180
|
+
// Override fetch to route internal container requests to appropriate ports
|
|
181
|
+
async fetch(request) {
|
|
182
|
+
const url = new URL(request.url);
|
|
183
|
+
if (!this.sandboxName && request.headers.has("X-Sandbox-Name")) {
|
|
184
|
+
const name = request.headers.get("X-Sandbox-Name");
|
|
185
|
+
this.sandboxName = name;
|
|
186
|
+
await this.ctx.storage.put("sandboxName", name);
|
|
187
|
+
console.log(`[Sandbox] Stored sandbox name: ${this.sandboxName}`);
|
|
188
|
+
}
|
|
189
|
+
const port = this.determinePort(url);
|
|
190
|
+
return await this.containerFetch(request, port);
|
|
191
|
+
}
|
|
192
|
+
determinePort(url) {
|
|
193
|
+
const proxyMatch = url.pathname.match(/^\/proxy\/(\d+)/);
|
|
194
|
+
if (proxyMatch) {
|
|
195
|
+
return parseInt(proxyMatch[1]);
|
|
196
|
+
}
|
|
197
|
+
return 3e3;
|
|
198
|
+
}
|
|
199
|
+
// Enhanced exec method - always returns ExecResult with optional streaming
|
|
200
|
+
// This replaces the old exec method to match ISandbox interface
|
|
201
|
+
async exec(command, options) {
|
|
202
|
+
const startTime = Date.now();
|
|
203
|
+
const timestamp = (/* @__PURE__ */ new Date()).toISOString();
|
|
204
|
+
let timeoutId;
|
|
205
|
+
try {
|
|
206
|
+
if (options?.signal?.aborted) {
|
|
207
|
+
throw new Error("Operation was aborted");
|
|
208
|
+
}
|
|
209
|
+
let result;
|
|
210
|
+
if (options?.stream && options?.onOutput) {
|
|
211
|
+
result = await this.executeWithStreaming(command, options, startTime, timestamp);
|
|
212
|
+
} else {
|
|
213
|
+
const response = await this.client.execute(
|
|
214
|
+
command,
|
|
215
|
+
options?.sessionId
|
|
216
|
+
);
|
|
217
|
+
const duration = Date.now() - startTime;
|
|
218
|
+
result = this.mapExecuteResponseToExecResult(response, duration, options?.sessionId);
|
|
219
|
+
}
|
|
220
|
+
if (options?.onComplete) {
|
|
221
|
+
options.onComplete(result);
|
|
222
|
+
}
|
|
223
|
+
return result;
|
|
224
|
+
} catch (error) {
|
|
225
|
+
if (options?.onError && error instanceof Error) {
|
|
226
|
+
options.onError(error);
|
|
227
|
+
}
|
|
228
|
+
throw error;
|
|
229
|
+
} finally {
|
|
230
|
+
if (timeoutId) {
|
|
231
|
+
clearTimeout(timeoutId);
|
|
232
|
+
}
|
|
233
|
+
}
|
|
234
|
+
}
|
|
235
|
+
async executeWithStreaming(command, options, startTime, timestamp) {
|
|
236
|
+
let stdout = "";
|
|
237
|
+
let stderr = "";
|
|
238
|
+
try {
|
|
239
|
+
const stream = await this.client.executeCommandStream(command, options.sessionId);
|
|
240
|
+
const { parseSSEStream } = await import("./sse-parser.js");
|
|
241
|
+
for await (const event of parseSSEStream(stream)) {
|
|
242
|
+
if (options.signal?.aborted) {
|
|
243
|
+
throw new Error("Operation was aborted");
|
|
244
|
+
}
|
|
245
|
+
switch (event.type) {
|
|
246
|
+
case "stdout":
|
|
247
|
+
case "stderr":
|
|
248
|
+
if (event.data) {
|
|
249
|
+
if (event.type === "stdout") stdout += event.data;
|
|
250
|
+
if (event.type === "stderr") stderr += event.data;
|
|
251
|
+
if (options.onOutput) {
|
|
252
|
+
options.onOutput(event.type, event.data);
|
|
253
|
+
}
|
|
254
|
+
}
|
|
255
|
+
break;
|
|
256
|
+
case "complete": {
|
|
257
|
+
const duration = Date.now() - startTime;
|
|
258
|
+
return event.result || {
|
|
259
|
+
success: event.exitCode === 0,
|
|
260
|
+
exitCode: event.exitCode || 0,
|
|
261
|
+
stdout,
|
|
262
|
+
stderr,
|
|
263
|
+
command,
|
|
264
|
+
duration,
|
|
265
|
+
timestamp,
|
|
266
|
+
sessionId: options.sessionId
|
|
267
|
+
};
|
|
268
|
+
}
|
|
269
|
+
case "error":
|
|
270
|
+
throw new Error(event.error || "Command execution failed");
|
|
271
|
+
}
|
|
272
|
+
}
|
|
273
|
+
throw new Error("Stream ended without completion event");
|
|
274
|
+
} catch (error) {
|
|
275
|
+
if (options.signal?.aborted) {
|
|
276
|
+
throw new Error("Operation was aborted");
|
|
277
|
+
}
|
|
278
|
+
throw error;
|
|
279
|
+
}
|
|
280
|
+
}
|
|
281
|
+
mapExecuteResponseToExecResult(response, duration, sessionId) {
|
|
282
|
+
return {
|
|
283
|
+
success: response.success,
|
|
284
|
+
exitCode: response.exitCode,
|
|
285
|
+
stdout: response.stdout,
|
|
286
|
+
stderr: response.stderr,
|
|
287
|
+
command: response.command,
|
|
288
|
+
duration,
|
|
289
|
+
timestamp: response.timestamp,
|
|
290
|
+
sessionId
|
|
291
|
+
};
|
|
292
|
+
}
|
|
293
|
+
// Background process management
|
|
294
|
+
async startProcess(command, options) {
|
|
295
|
+
try {
|
|
296
|
+
const response = await this.client.startProcess(command, {
|
|
297
|
+
processId: options?.processId,
|
|
298
|
+
sessionId: options?.sessionId,
|
|
299
|
+
timeout: options?.timeout,
|
|
300
|
+
env: options?.env,
|
|
301
|
+
cwd: options?.cwd,
|
|
302
|
+
encoding: options?.encoding,
|
|
303
|
+
autoCleanup: options?.autoCleanup
|
|
304
|
+
});
|
|
305
|
+
const process = response.process;
|
|
306
|
+
const processObj = {
|
|
307
|
+
id: process.id,
|
|
308
|
+
pid: process.pid,
|
|
309
|
+
command: process.command,
|
|
310
|
+
status: process.status,
|
|
311
|
+
startTime: new Date(process.startTime),
|
|
312
|
+
endTime: void 0,
|
|
313
|
+
exitCode: void 0,
|
|
314
|
+
sessionId: process.sessionId,
|
|
315
|
+
async kill() {
|
|
316
|
+
throw new Error("Method will be replaced");
|
|
317
|
+
},
|
|
318
|
+
async getStatus() {
|
|
319
|
+
throw new Error("Method will be replaced");
|
|
320
|
+
},
|
|
321
|
+
async getLogs() {
|
|
322
|
+
throw new Error("Method will be replaced");
|
|
323
|
+
}
|
|
324
|
+
};
|
|
325
|
+
processObj.kill = async (signal) => {
|
|
326
|
+
await this.killProcess(process.id, signal);
|
|
327
|
+
};
|
|
328
|
+
processObj.getStatus = async () => {
|
|
329
|
+
const current = await this.getProcess(process.id);
|
|
330
|
+
return current?.status || "error";
|
|
331
|
+
};
|
|
332
|
+
processObj.getLogs = async () => {
|
|
333
|
+
const logs = await this.getProcessLogs(process.id);
|
|
334
|
+
return { stdout: logs.stdout, stderr: logs.stderr };
|
|
335
|
+
};
|
|
336
|
+
if (options?.onStart) {
|
|
337
|
+
options.onStart(processObj);
|
|
338
|
+
}
|
|
339
|
+
return processObj;
|
|
340
|
+
} catch (error) {
|
|
341
|
+
if (options?.onError && error instanceof Error) {
|
|
342
|
+
options.onError(error);
|
|
343
|
+
}
|
|
344
|
+
throw error;
|
|
345
|
+
}
|
|
346
|
+
}
|
|
347
|
+
async listProcesses() {
|
|
348
|
+
const response = await this.client.listProcesses();
|
|
349
|
+
return response.processes.map((processData) => ({
|
|
350
|
+
id: processData.id,
|
|
351
|
+
pid: processData.pid,
|
|
352
|
+
command: processData.command,
|
|
353
|
+
status: processData.status,
|
|
354
|
+
startTime: new Date(processData.startTime),
|
|
355
|
+
endTime: processData.endTime ? new Date(processData.endTime) : void 0,
|
|
356
|
+
exitCode: processData.exitCode,
|
|
357
|
+
sessionId: processData.sessionId,
|
|
358
|
+
kill: async (signal) => {
|
|
359
|
+
await this.killProcess(processData.id, signal);
|
|
360
|
+
},
|
|
361
|
+
getStatus: async () => {
|
|
362
|
+
const current = await this.getProcess(processData.id);
|
|
363
|
+
return current?.status || "error";
|
|
364
|
+
},
|
|
365
|
+
getLogs: async () => {
|
|
366
|
+
const logs = await this.getProcessLogs(processData.id);
|
|
367
|
+
return { stdout: logs.stdout, stderr: logs.stderr };
|
|
368
|
+
}
|
|
369
|
+
}));
|
|
370
|
+
}
|
|
371
|
+
async getProcess(id) {
|
|
372
|
+
const response = await this.client.getProcess(id);
|
|
373
|
+
if (!response.process) {
|
|
374
|
+
return null;
|
|
375
|
+
}
|
|
376
|
+
const processData = response.process;
|
|
377
|
+
return {
|
|
378
|
+
id: processData.id,
|
|
379
|
+
pid: processData.pid,
|
|
380
|
+
command: processData.command,
|
|
381
|
+
status: processData.status,
|
|
382
|
+
startTime: new Date(processData.startTime),
|
|
383
|
+
endTime: processData.endTime ? new Date(processData.endTime) : void 0,
|
|
384
|
+
exitCode: processData.exitCode,
|
|
385
|
+
sessionId: processData.sessionId,
|
|
386
|
+
kill: async (signal) => {
|
|
387
|
+
await this.killProcess(processData.id, signal);
|
|
388
|
+
},
|
|
389
|
+
getStatus: async () => {
|
|
390
|
+
const current = await this.getProcess(processData.id);
|
|
391
|
+
return current?.status || "error";
|
|
392
|
+
},
|
|
393
|
+
getLogs: async () => {
|
|
394
|
+
const logs = await this.getProcessLogs(processData.id);
|
|
395
|
+
return { stdout: logs.stdout, stderr: logs.stderr };
|
|
396
|
+
}
|
|
397
|
+
};
|
|
398
|
+
}
|
|
399
|
+
async killProcess(id, _signal) {
|
|
400
|
+
try {
|
|
401
|
+
await this.client.killProcess(id);
|
|
402
|
+
} catch (error) {
|
|
403
|
+
if (error instanceof Error && error.message.includes("Process not found")) {
|
|
404
|
+
throw new ProcessNotFoundError(id);
|
|
405
|
+
}
|
|
406
|
+
throw new SandboxError(
|
|
407
|
+
`Failed to kill process ${id}: ${error instanceof Error ? error.message : "Unknown error"}`,
|
|
408
|
+
"KILL_PROCESS_FAILED"
|
|
409
|
+
);
|
|
410
|
+
}
|
|
411
|
+
}
|
|
412
|
+
async killAllProcesses() {
|
|
413
|
+
const response = await this.client.killAllProcesses();
|
|
414
|
+
return response.killedCount;
|
|
415
|
+
}
|
|
416
|
+
async cleanupCompletedProcesses() {
|
|
417
|
+
return 0;
|
|
418
|
+
}
|
|
419
|
+
async getProcessLogs(id) {
|
|
420
|
+
try {
|
|
421
|
+
const response = await this.client.getProcessLogs(id);
|
|
422
|
+
return {
|
|
423
|
+
stdout: response.stdout,
|
|
424
|
+
stderr: response.stderr
|
|
425
|
+
};
|
|
426
|
+
} catch (error) {
|
|
427
|
+
if (error instanceof Error && error.message.includes("Process not found")) {
|
|
428
|
+
throw new ProcessNotFoundError(id);
|
|
429
|
+
}
|
|
430
|
+
throw error;
|
|
431
|
+
}
|
|
432
|
+
}
|
|
433
|
+
// Streaming methods - return ReadableStream for RPC compatibility
|
|
434
|
+
async execStream(command, options) {
|
|
435
|
+
if (options?.signal?.aborted) {
|
|
436
|
+
throw new Error("Operation was aborted");
|
|
437
|
+
}
|
|
438
|
+
const stream = await this.client.executeCommandStream(command, options?.sessionId);
|
|
439
|
+
return stream;
|
|
440
|
+
}
|
|
441
|
+
async streamProcessLogs(processId, options) {
|
|
442
|
+
if (options?.signal?.aborted) {
|
|
443
|
+
throw new Error("Operation was aborted");
|
|
444
|
+
}
|
|
445
|
+
const stream = await this.client.streamProcessLogs(processId);
|
|
446
|
+
return stream;
|
|
447
|
+
}
|
|
448
|
+
async gitCheckout(repoUrl, options) {
|
|
449
|
+
return this.client.gitCheckout(repoUrl, options.branch, options.targetDir);
|
|
450
|
+
}
|
|
451
|
+
async mkdir(path, options = {}) {
|
|
452
|
+
return this.client.mkdir(path, options.recursive);
|
|
453
|
+
}
|
|
454
|
+
async writeFile(path, content, options = {}) {
|
|
455
|
+
return this.client.writeFile(path, content, options.encoding);
|
|
456
|
+
}
|
|
457
|
+
async deleteFile(path) {
|
|
458
|
+
return this.client.deleteFile(path);
|
|
459
|
+
}
|
|
460
|
+
async renameFile(oldPath, newPath) {
|
|
461
|
+
return this.client.renameFile(oldPath, newPath);
|
|
462
|
+
}
|
|
463
|
+
async moveFile(sourcePath, destinationPath) {
|
|
464
|
+
return this.client.moveFile(sourcePath, destinationPath);
|
|
465
|
+
}
|
|
466
|
+
async readFile(path, options = {}) {
|
|
467
|
+
return this.client.readFile(path, options.encoding);
|
|
468
|
+
}
|
|
469
|
+
async exposePort(port, options) {
|
|
470
|
+
await this.client.exposePort(port, options?.name);
|
|
471
|
+
if (!this.sandboxName) {
|
|
472
|
+
throw new Error("Sandbox name not available. Ensure sandbox is accessed through getSandbox()");
|
|
473
|
+
}
|
|
474
|
+
const url = this.constructPreviewUrl(port, this.sandboxName, options.hostname);
|
|
475
|
+
return {
|
|
476
|
+
url,
|
|
477
|
+
port,
|
|
478
|
+
name: options?.name
|
|
479
|
+
};
|
|
480
|
+
}
|
|
481
|
+
async unexposePort(port) {
|
|
482
|
+
if (!validatePort(port)) {
|
|
483
|
+
logSecurityEvent("INVALID_PORT_UNEXPOSE", {
|
|
484
|
+
port
|
|
485
|
+
}, "high");
|
|
486
|
+
throw new SecurityError(`Invalid port number: ${port}. Must be between 1024-65535 and not reserved.`);
|
|
487
|
+
}
|
|
488
|
+
await this.client.unexposePort(port);
|
|
489
|
+
logSecurityEvent("PORT_UNEXPOSED", {
|
|
490
|
+
port
|
|
491
|
+
}, "low");
|
|
492
|
+
}
|
|
493
|
+
async getExposedPorts(hostname) {
|
|
494
|
+
const response = await this.client.getExposedPorts();
|
|
495
|
+
if (!this.sandboxName) {
|
|
496
|
+
throw new Error("Sandbox name not available. Ensure sandbox is accessed through getSandbox()");
|
|
497
|
+
}
|
|
498
|
+
return response.ports.map((port) => ({
|
|
499
|
+
url: this.constructPreviewUrl(port.port, this.sandboxName, hostname),
|
|
500
|
+
port: port.port,
|
|
501
|
+
name: port.name,
|
|
502
|
+
exposedAt: port.exposedAt
|
|
503
|
+
}));
|
|
504
|
+
}
|
|
505
|
+
constructPreviewUrl(port, sandboxId, hostname) {
|
|
506
|
+
if (!validatePort(port)) {
|
|
507
|
+
logSecurityEvent("INVALID_PORT_REJECTED", {
|
|
508
|
+
port,
|
|
509
|
+
sandboxId,
|
|
510
|
+
hostname
|
|
511
|
+
}, "high");
|
|
512
|
+
throw new SecurityError(`Invalid port number: ${port}. Must be between 1024-65535 and not reserved.`);
|
|
513
|
+
}
|
|
514
|
+
let sanitizedSandboxId;
|
|
515
|
+
try {
|
|
516
|
+
sanitizedSandboxId = sanitizeSandboxId(sandboxId);
|
|
517
|
+
} catch (error) {
|
|
518
|
+
logSecurityEvent("INVALID_SANDBOX_ID_REJECTED", {
|
|
519
|
+
sandboxId,
|
|
520
|
+
port,
|
|
521
|
+
hostname,
|
|
522
|
+
error: error instanceof Error ? error.message : "Unknown error"
|
|
523
|
+
}, "high");
|
|
524
|
+
throw error;
|
|
525
|
+
}
|
|
526
|
+
const isLocalhost = isLocalhostPattern(hostname);
|
|
527
|
+
if (isLocalhost) {
|
|
528
|
+
const [host, portStr] = hostname.split(":");
|
|
529
|
+
const mainPort = portStr || "80";
|
|
530
|
+
try {
|
|
531
|
+
const baseUrl = new URL(`http://${host}:${mainPort}`);
|
|
532
|
+
const subdomainHost = `${port}-${sanitizedSandboxId}.${host}`;
|
|
533
|
+
baseUrl.hostname = subdomainHost;
|
|
534
|
+
const finalUrl = baseUrl.toString();
|
|
535
|
+
logSecurityEvent("PREVIEW_URL_CONSTRUCTED", {
|
|
536
|
+
port,
|
|
537
|
+
sandboxId: sanitizedSandboxId,
|
|
538
|
+
hostname,
|
|
539
|
+
resultUrl: finalUrl,
|
|
540
|
+
environment: "localhost"
|
|
541
|
+
}, "low");
|
|
542
|
+
return finalUrl;
|
|
543
|
+
} catch (error) {
|
|
544
|
+
logSecurityEvent("URL_CONSTRUCTION_FAILED", {
|
|
545
|
+
port,
|
|
546
|
+
sandboxId: sanitizedSandboxId,
|
|
547
|
+
hostname,
|
|
548
|
+
error: error instanceof Error ? error.message : "Unknown error"
|
|
549
|
+
}, "high");
|
|
550
|
+
throw new SecurityError(`Failed to construct preview URL: ${error instanceof Error ? error.message : "Unknown error"}`);
|
|
551
|
+
}
|
|
552
|
+
}
|
|
553
|
+
try {
|
|
554
|
+
const protocol = "https";
|
|
555
|
+
const baseUrl = new URL(`${protocol}://${hostname}`);
|
|
556
|
+
const subdomainHost = `${port}-${sanitizedSandboxId}.${hostname}`;
|
|
557
|
+
baseUrl.hostname = subdomainHost;
|
|
558
|
+
const finalUrl = baseUrl.toString();
|
|
559
|
+
logSecurityEvent("PREVIEW_URL_CONSTRUCTED", {
|
|
560
|
+
port,
|
|
561
|
+
sandboxId: sanitizedSandboxId,
|
|
562
|
+
hostname,
|
|
563
|
+
resultUrl: finalUrl,
|
|
564
|
+
environment: "production"
|
|
565
|
+
}, "low");
|
|
566
|
+
return finalUrl;
|
|
567
|
+
} catch (error) {
|
|
568
|
+
logSecurityEvent("URL_CONSTRUCTION_FAILED", {
|
|
569
|
+
port,
|
|
570
|
+
sandboxId: sanitizedSandboxId,
|
|
571
|
+
hostname,
|
|
572
|
+
error: error instanceof Error ? error.message : "Unknown error"
|
|
573
|
+
}, "high");
|
|
574
|
+
throw new SecurityError(`Failed to construct preview URL: ${error instanceof Error ? error.message : "Unknown error"}`);
|
|
575
|
+
}
|
|
576
|
+
}
|
|
577
|
+
};
|
|
578
|
+
|
|
579
|
+
export {
|
|
580
|
+
getSandbox,
|
|
581
|
+
Sandbox,
|
|
582
|
+
proxyToSandbox,
|
|
583
|
+
isLocalhostPattern
|
|
584
|
+
};
|
|
585
|
+
//# sourceMappingURL=chunk-ISFOIYQC.js.map
|