@cloudflare/sandbox 0.3.4 → 0.3.5
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 +6 -0
- package/Dockerfile +1 -0
- package/README.md +58 -5
- package/container_src/handler/file.ts +51 -0
- package/container_src/index.ts +8 -0
- package/container_src/isolation.ts +173 -9
- package/dist/{chunk-SMUEY5JR.js → chunk-32UDXUPC.js} +31 -1
- package/dist/chunk-32UDXUPC.js.map +1 -0
- package/dist/chunk-5DILEXGY.js +85 -0
- package/dist/chunk-5DILEXGY.js.map +1 -0
- package/dist/{chunk-Z6OZPC6U.js → chunk-D3U63BZP.js} +2 -2
- package/dist/{chunk-DEXT4CAF.js → chunk-SQLJNZ3K.js} +9 -2
- package/dist/chunk-SQLJNZ3K.js.map +1 -0
- package/dist/{client-DRhcuRza.d.ts → client-B3RUab0s.d.ts} +2 -0
- package/dist/client.d.ts +1 -1
- package/dist/client.js +1 -1
- package/dist/file-stream.d.ts +65 -0
- package/dist/file-stream.js +10 -0
- package/dist/file-stream.js.map +1 -0
- package/dist/index.d.ts +3 -2
- package/dist/index.js +10 -4
- package/dist/interpreter-client.d.ts +1 -1
- package/dist/interpreter-client.js +2 -2
- package/dist/interpreter.d.ts +1 -1
- package/dist/request-handler.d.ts +1 -1
- package/dist/request-handler.js +3 -3
- package/dist/sandbox.d.ts +1 -1
- package/dist/sandbox.js +3 -3
- package/dist/types.d.ts +56 -1
- package/dist/types.js.map +1 -1
- package/package.json +1 -1
- package/src/client.ts +39 -0
- package/src/file-stream.ts +162 -0
- package/src/index.ts +6 -0
- package/src/sandbox.ts +10 -1
- package/src/types.ts +69 -0
- package/dist/chunk-DEXT4CAF.js.map +0 -1
- package/dist/chunk-SMUEY5JR.js.map +0 -1
- /package/dist/{chunk-Z6OZPC6U.js.map → chunk-D3U63BZP.js.map} +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import {
|
|
2
2
|
HttpClient
|
|
3
|
-
} from "./chunk-
|
|
3
|
+
} from "./chunk-32UDXUPC.js";
|
|
4
4
|
import {
|
|
5
5
|
isRetryableError,
|
|
6
6
|
parseErrorResponse
|
|
@@ -237,4 +237,4 @@ var InterpreterClient = class extends HttpClient {
|
|
|
237
237
|
export {
|
|
238
238
|
InterpreterClient
|
|
239
239
|
};
|
|
240
|
-
//# sourceMappingURL=chunk-
|
|
240
|
+
//# sourceMappingURL=chunk-D3U63BZP.js.map
|
|
@@ -6,7 +6,7 @@ import {
|
|
|
6
6
|
} from "./chunk-6UAWTJ5S.js";
|
|
7
7
|
import {
|
|
8
8
|
InterpreterClient
|
|
9
|
-
} from "./chunk-
|
|
9
|
+
} from "./chunk-D3U63BZP.js";
|
|
10
10
|
import {
|
|
11
11
|
CodeInterpreter
|
|
12
12
|
} from "./chunk-JTKON2SH.js";
|
|
@@ -284,6 +284,10 @@ var Sandbox = class extends Container {
|
|
|
284
284
|
const session = await this.ensureDefaultSession();
|
|
285
285
|
return session.readFile(path, options);
|
|
286
286
|
}
|
|
287
|
+
async readFileStream(path) {
|
|
288
|
+
const session = await this.ensureDefaultSession();
|
|
289
|
+
return session.readFileStream(path);
|
|
290
|
+
}
|
|
287
291
|
async listFiles(path, options = {}) {
|
|
288
292
|
const session = await this.ensureDefaultSession();
|
|
289
293
|
return session.listFiles(path, options);
|
|
@@ -606,6 +610,9 @@ var Sandbox = class extends Container {
|
|
|
606
610
|
readFile: async (path, options2) => {
|
|
607
611
|
return await this.client.readFile(path, options2?.encoding, sessionId);
|
|
608
612
|
},
|
|
613
|
+
readFileStream: async (path) => {
|
|
614
|
+
return await this.client.readFileStream(path, sessionId);
|
|
615
|
+
},
|
|
609
616
|
mkdir: async (path, options2) => {
|
|
610
617
|
return await this.client.mkdir(path, options2?.recursive, sessionId);
|
|
611
618
|
},
|
|
@@ -664,4 +671,4 @@ export {
|
|
|
664
671
|
proxyToSandbox,
|
|
665
672
|
isLocalhostPattern
|
|
666
673
|
};
|
|
667
|
-
//# sourceMappingURL=chunk-
|
|
674
|
+
//# sourceMappingURL=chunk-SQLJNZ3K.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/sandbox.ts","../src/request-handler.ts"],"sourcesContent":["import { Container, getContainer } from \"@cloudflare/containers\";\nimport { CodeInterpreter } from \"./interpreter\";\nimport { InterpreterClient } from \"./interpreter-client\";\nimport type {\n CodeContext,\n CreateContextOptions,\n ExecutionResult,\n RunCodeOptions,\n} from \"./interpreter-types\";\nimport { isLocalhostPattern } from \"./request-handler\";\nimport {\n logSecurityEvent,\n SecurityError,\n sanitizeSandboxId,\n validatePort,\n} from \"./security\";\nimport { parseSSEStream } from \"./sse-parser\";\nimport type {\n ExecEvent,\n ExecOptions,\n ExecResult,\n ExecuteResponse,\n ExecutionSession,\n ISandbox,\n Process,\n ProcessOptions,\n ProcessStatus,\n StreamOptions,\n} from \"./types\";\nimport { ProcessNotFoundError, SandboxError } from \"./types\";\n\nexport function getSandbox(ns: DurableObjectNamespace<Sandbox>, id: string) {\n const stub = getContainer(ns, id);\n\n // Store the name on first access\n stub.setSandboxName?.(id);\n\n return stub;\n}\n\nexport class Sandbox<Env = unknown> extends Container<Env> implements ISandbox {\n defaultPort = 3000; // Default port for the container's Bun server\n sleepAfter = \"20m\"; // Keep container warm for 20 minutes to avoid cold starts\n client: InterpreterClient;\n private sandboxName: string | null = null;\n private codeInterpreter: CodeInterpreter;\n private defaultSession: ExecutionSession | null = null;\n\n constructor(ctx: DurableObjectState<{}>, env: Env) {\n super(ctx, env);\n this.client = new InterpreterClient({\n onCommandComplete: (success, exitCode, _stdout, _stderr, command) => {\n console.log(\n `[Container] Command completed: ${command}, Success: ${success}, Exit code: ${exitCode}`\n );\n },\n onCommandStart: (command) => {\n console.log(`[Container] Command started: ${command}`);\n },\n onError: (error, _command) => {\n console.error(`[Container] Command error: ${error}`);\n },\n onOutput: (stream, data, _command) => {\n console.log(`[Container] [${stream}] ${data}`);\n },\n port: 3000, // Control plane port\n stub: this,\n });\n\n // Initialize code interpreter\n this.codeInterpreter = new CodeInterpreter(this);\n\n // Load the sandbox name from storage on initialization\n this.ctx.blockConcurrencyWhile(async () => {\n this.sandboxName =\n (await this.ctx.storage.get<string>(\"sandboxName\")) || null;\n });\n }\n\n // RPC method to set the sandbox name\n async setSandboxName(name: string): Promise<void> {\n if (!this.sandboxName) {\n this.sandboxName = name;\n await this.ctx.storage.put(\"sandboxName\", name);\n console.log(`[Sandbox] Stored sandbox name via RPC: ${name}`);\n }\n }\n\n // RPC method to set environment variables\n async setEnvVars(envVars: Record<string, string>): Promise<void> {\n this.envVars = { ...this.envVars, ...envVars };\n console.log(`[Sandbox] Updated environment variables`);\n \n // If we have a default session, update its environment too\n if (this.defaultSession) {\n await this.defaultSession.setEnvVars(envVars);\n }\n }\n\n override onStart() {\n console.log(\"Sandbox successfully started\");\n }\n\n override onStop() {\n console.log(\"Sandbox successfully shut down\");\n }\n\n override onError(error: unknown) {\n console.log(\"Sandbox error:\", error);\n }\n\n // Override fetch to route internal container requests to appropriate ports\n override async fetch(request: Request): Promise<Response> {\n const url = new URL(request.url);\n\n // Capture and store the sandbox name from the header if present\n if (!this.sandboxName && request.headers.has(\"X-Sandbox-Name\")) {\n const name = request.headers.get(\"X-Sandbox-Name\")!;\n this.sandboxName = name;\n await this.ctx.storage.put(\"sandboxName\", name);\n console.log(`[Sandbox] Stored sandbox name: ${this.sandboxName}`);\n }\n\n // Determine which port to route to\n const port = this.determinePort(url);\n\n // Route to the appropriate port\n return await this.containerFetch(request, port);\n }\n\n private determinePort(url: URL): number {\n // Extract port from proxy requests (e.g., /proxy/8080/*)\n const proxyMatch = url.pathname.match(/^\\/proxy\\/(\\d+)/);\n if (proxyMatch) {\n return parseInt(proxyMatch[1]);\n }\n\n if (url.port) {\n return parseInt(url.port);\n }\n\n // All other requests go to control plane on port 3000\n // This includes /api/* endpoints and any other control requests\n return 3000;\n }\n\n // Helper to ensure default session is initialized\n private async ensureDefaultSession(): Promise<ExecutionSession> {\n if (!this.defaultSession) {\n const sessionId = `sandbox-${this.sandboxName || 'default'}`;\n this.defaultSession = await this.createSession({\n id: sessionId,\n env: this.envVars || {},\n cwd: '/workspace',\n isolation: true\n });\n console.log(`[Sandbox] Default session initialized: ${sessionId}`);\n }\n return this.defaultSession;\n }\n\n\n async exec(command: string, options?: ExecOptions): Promise<ExecResult> {\n const session = await this.ensureDefaultSession();\n return session.exec(command, options);\n }\n\n async startProcess(\n command: string,\n options?: ProcessOptions\n ): Promise<Process> {\n const session = await this.ensureDefaultSession();\n return session.startProcess(command, options);\n }\n\n async listProcesses(): Promise<Process[]> {\n const session = await this.ensureDefaultSession();\n return session.listProcesses();\n }\n\n async getProcess(id: string): Promise<Process | null> {\n const session = await this.ensureDefaultSession();\n return session.getProcess(id);\n }\n\n async killProcess(id: string, signal?: string): Promise<void> {\n const session = await this.ensureDefaultSession();\n return session.killProcess(id, signal);\n }\n\n async killAllProcesses(): Promise<number> {\n const session = await this.ensureDefaultSession();\n return session.killAllProcesses();\n }\n\n async cleanupCompletedProcesses(): Promise<number> {\n const session = await this.ensureDefaultSession();\n return session.cleanupCompletedProcesses();\n }\n\n async getProcessLogs(\n id: string\n ): Promise<{ stdout: string; stderr: string }> {\n const session = await this.ensureDefaultSession();\n return session.getProcessLogs(id);\n }\n\n // Streaming methods - delegates to default session\n async execStream(\n command: string,\n options?: StreamOptions\n ): Promise<ReadableStream<Uint8Array>> {\n const session = await this.ensureDefaultSession();\n return session.execStream(command, options);\n }\n\n async streamProcessLogs(\n processId: string,\n options?: { signal?: AbortSignal }\n ): Promise<ReadableStream<Uint8Array>> {\n const session = await this.ensureDefaultSession();\n return session.streamProcessLogs(processId, options);\n }\n\n async gitCheckout(\n repoUrl: string,\n options: { branch?: string; targetDir?: string }\n ) {\n const session = await this.ensureDefaultSession();\n return session.gitCheckout(repoUrl, options);\n }\n\n async mkdir(path: string, options: { recursive?: boolean } = {}) {\n const session = await this.ensureDefaultSession();\n return session.mkdir(path, options);\n }\n\n async writeFile(\n path: string,\n content: string,\n options: { encoding?: string } = {}\n ) {\n const session = await this.ensureDefaultSession();\n return session.writeFile(path, content, options);\n }\n\n async deleteFile(path: string) {\n const session = await this.ensureDefaultSession();\n return session.deleteFile(path);\n }\n\n async renameFile(oldPath: string, newPath: string) {\n const session = await this.ensureDefaultSession();\n return session.renameFile(oldPath, newPath);\n }\n\n async moveFile(sourcePath: string, destinationPath: string) {\n const session = await this.ensureDefaultSession();\n return session.moveFile(sourcePath, destinationPath);\n }\n\n async readFile(path: string, options: { encoding?: string } = {}) {\n const session = await this.ensureDefaultSession();\n return session.readFile(path, options);\n }\n\n async readFileStream(path: string): Promise<ReadableStream<Uint8Array>> {\n const session = await this.ensureDefaultSession();\n return session.readFileStream(path);\n }\n\n async listFiles(\n path: string,\n options: {\n recursive?: boolean;\n includeHidden?: boolean;\n } = {}\n ) {\n const session = await this.ensureDefaultSession();\n return session.listFiles(path, options);\n }\n\n async exposePort(port: number, options: { name?: string; hostname: string }) {\n await this.client.exposePort(port, options?.name);\n\n // We need the sandbox name to construct preview URLs\n if (!this.sandboxName) {\n throw new Error(\n \"Sandbox name not available. Ensure sandbox is accessed through getSandbox()\"\n );\n }\n\n const url = this.constructPreviewUrl(\n port,\n this.sandboxName,\n options.hostname\n );\n\n return {\n url,\n port,\n name: options?.name,\n };\n }\n\n async unexposePort(port: number) {\n if (!validatePort(port)) {\n logSecurityEvent(\n \"INVALID_PORT_UNEXPOSE\",\n {\n port,\n },\n \"high\"\n );\n throw new SecurityError(\n `Invalid port number: ${port}. Must be between 1024-65535 and not reserved.`\n );\n }\n\n await this.client.unexposePort(port);\n\n logSecurityEvent(\n \"PORT_UNEXPOSED\",\n {\n port,\n },\n \"low\"\n );\n }\n\n async getExposedPorts(hostname: string) {\n const response = await this.client.getExposedPorts();\n\n // We need the sandbox name to construct preview URLs\n if (!this.sandboxName) {\n throw new Error(\n \"Sandbox name not available. Ensure sandbox is accessed through getSandbox()\"\n );\n }\n\n return response.ports.map((port) => ({\n url: this.constructPreviewUrl(port.port, this.sandboxName!, hostname),\n port: port.port,\n name: port.name,\n exposedAt: port.exposedAt,\n }));\n }\n\n private constructPreviewUrl(\n port: number,\n sandboxId: string,\n hostname: string\n ): string {\n if (!validatePort(port)) {\n logSecurityEvent(\n \"INVALID_PORT_REJECTED\",\n {\n port,\n sandboxId,\n hostname,\n },\n \"high\"\n );\n throw new SecurityError(\n `Invalid port number: ${port}. Must be between 1024-65535 and not reserved.`\n );\n }\n\n let sanitizedSandboxId: string;\n try {\n sanitizedSandboxId = sanitizeSandboxId(sandboxId);\n } catch (error) {\n logSecurityEvent(\n \"INVALID_SANDBOX_ID_REJECTED\",\n {\n sandboxId,\n port,\n hostname,\n error: error instanceof Error ? error.message : \"Unknown error\",\n },\n \"high\"\n );\n throw error;\n }\n\n const isLocalhost = isLocalhostPattern(hostname);\n\n if (isLocalhost) {\n // Unified subdomain approach for localhost (RFC 6761)\n const [host, portStr] = hostname.split(\":\");\n const mainPort = portStr || \"80\";\n\n // Use URL constructor for safe URL building\n try {\n const baseUrl = new URL(`http://${host}:${mainPort}`);\n // Construct subdomain safely\n const subdomainHost = `${port}-${sanitizedSandboxId}.${host}`;\n baseUrl.hostname = subdomainHost;\n\n const finalUrl = baseUrl.toString();\n\n logSecurityEvent(\n \"PREVIEW_URL_CONSTRUCTED\",\n {\n port,\n sandboxId: sanitizedSandboxId,\n hostname,\n resultUrl: finalUrl,\n environment: \"localhost\",\n },\n \"low\"\n );\n\n return finalUrl;\n } catch (error) {\n logSecurityEvent(\n \"URL_CONSTRUCTION_FAILED\",\n {\n port,\n sandboxId: sanitizedSandboxId,\n hostname,\n error: error instanceof Error ? error.message : \"Unknown error\",\n },\n \"high\"\n );\n throw new SecurityError(\n `Failed to construct preview URL: ${\n error instanceof Error ? error.message : \"Unknown error\"\n }`\n );\n }\n }\n\n // Production subdomain logic - enforce HTTPS\n try {\n // Always use HTTPS for production (non-localhost)\n const protocol = \"https\";\n const baseUrl = new URL(`${protocol}://${hostname}`);\n\n // Construct subdomain safely\n const subdomainHost = `${port}-${sanitizedSandboxId}.${hostname}`;\n baseUrl.hostname = subdomainHost;\n\n const finalUrl = baseUrl.toString();\n\n logSecurityEvent(\n \"PREVIEW_URL_CONSTRUCTED\",\n {\n port,\n sandboxId: sanitizedSandboxId,\n hostname,\n resultUrl: finalUrl,\n environment: \"production\",\n },\n \"low\"\n );\n\n return finalUrl;\n } catch (error) {\n logSecurityEvent(\n \"URL_CONSTRUCTION_FAILED\",\n {\n port,\n sandboxId: sanitizedSandboxId,\n hostname,\n error: error instanceof Error ? error.message : \"Unknown error\",\n },\n \"high\"\n );\n throw new SecurityError(\n `Failed to construct preview URL: ${\n error instanceof Error ? error.message : \"Unknown error\"\n }`\n );\n }\n }\n\n // Code Interpreter Methods\n\n /**\n * Create a new code execution context\n */\n async createCodeContext(\n options?: CreateContextOptions\n ): Promise<CodeContext> {\n return this.codeInterpreter.createCodeContext(options);\n }\n\n /**\n * Run code with streaming callbacks\n */\n async runCode(\n code: string,\n options?: RunCodeOptions\n ): Promise<ExecutionResult> {\n const execution = await this.codeInterpreter.runCode(code, options);\n // Convert to plain object for RPC serialization\n return execution.toJSON();\n }\n\n /**\n * Run code and return a streaming response\n */\n async runCodeStream(\n code: string,\n options?: RunCodeOptions\n ): Promise<ReadableStream> {\n return this.codeInterpreter.runCodeStream(code, options);\n }\n\n /**\n * List all code contexts\n */\n async listCodeContexts(): Promise<CodeContext[]> {\n return this.codeInterpreter.listCodeContexts();\n }\n\n /**\n * Delete a code context\n */\n async deleteCodeContext(contextId: string): Promise<void> {\n return this.codeInterpreter.deleteCodeContext(contextId);\n }\n\n // ============================================================================\n // Session Management (Simple Isolation)\n // ============================================================================\n\n /**\n * Create a new execution session with isolation\n * Returns a session object with exec() method\n */\n\n async createSession(options: {\n id?: string;\n env?: Record<string, string>;\n cwd?: string;\n isolation?: boolean;\n }): Promise<ExecutionSession> {\n const sessionId = options.id || `session-${Date.now()}`;\n \n await this.client.createSession({\n id: sessionId,\n env: options.env,\n cwd: options.cwd,\n isolation: options.isolation\n });\n // Return comprehensive ExecutionSession object that implements all ISandbox methods\n return {\n id: sessionId,\n \n // Command execution - clean method names\n exec: async (command: string, options?: ExecOptions) => {\n const result = await this.client.exec(sessionId, command);\n return {\n ...result,\n command,\n duration: 0,\n timestamp: new Date().toISOString()\n };\n },\n \n execStream: async (command: string, options?: StreamOptions) => {\n return await this.client.execStream(sessionId, command);\n },\n \n // Process management - route to session-aware methods\n startProcess: async (command: string, options?: ProcessOptions) => {\n // Use session-specific process management\n const response = await this.client.startProcess(command, sessionId, {\n processId: options?.processId,\n timeout: options?.timeout,\n env: options?.env,\n cwd: options?.cwd,\n encoding: options?.encoding,\n autoCleanup: options?.autoCleanup,\n });\n \n // Convert response to Process object with bound methods\n const process = response.process;\n return {\n id: process.id,\n pid: process.pid,\n command: process.command,\n status: process.status as ProcessStatus,\n startTime: new Date(process.startTime),\n endTime: process.endTime ? new Date(process.endTime) : undefined,\n exitCode: process.exitCode ?? undefined,\n kill: async (signal?: string) => {\n await this.client.killProcess(process.id);\n },\n getStatus: async () => {\n const resp = await this.client.getProcess(process.id);\n return resp.process?.status as ProcessStatus || \"error\";\n },\n getLogs: async () => {\n return await this.client.getProcessLogs(process.id);\n },\n };\n },\n \n listProcesses: async () => {\n // Get processes for this specific session\n const response = await this.client.listProcesses(sessionId);\n \n // Convert to Process objects with bound methods\n return response.processes.map(p => ({\n id: p.id,\n pid: p.pid,\n command: p.command,\n status: p.status as ProcessStatus,\n startTime: new Date(p.startTime),\n endTime: p.endTime ? new Date(p.endTime) : undefined,\n exitCode: p.exitCode ?? undefined,\n kill: async (signal?: string) => {\n await this.client.killProcess(p.id);\n },\n getStatus: async () => {\n const processResp = await this.client.getProcess(p.id);\n return processResp.process?.status as ProcessStatus || \"error\";\n },\n getLogs: async () => {\n return this.client.getProcessLogs(p.id);\n },\n }));\n },\n \n getProcess: async (id: string) => {\n const response = await this.client.getProcess(id);\n if (!response.process) return null;\n \n const p = response.process;\n return {\n id: p.id,\n pid: p.pid,\n command: p.command,\n status: p.status as ProcessStatus,\n startTime: new Date(p.startTime),\n endTime: p.endTime ? new Date(p.endTime) : undefined,\n exitCode: p.exitCode ?? undefined,\n kill: async (signal?: string) => {\n await this.client.killProcess(p.id);\n },\n getStatus: async () => {\n const processResp = await this.client.getProcess(p.id);\n return processResp.process?.status as ProcessStatus || \"error\";\n },\n getLogs: async () => {\n return this.client.getProcessLogs(p.id);\n },\n };\n },\n \n killProcess: async (id: string, signal?: string) => {\n await this.client.killProcess(id);\n },\n \n killAllProcesses: async () => {\n // Kill all processes for this specific session\n const response = await this.client.killAllProcesses(sessionId);\n return response.killedCount;\n },\n \n streamProcessLogs: async (processId: string, options?: { signal?: AbortSignal }) => {\n return await this.client.streamProcessLogs(processId, options);\n },\n \n getProcessLogs: async (id: string) => {\n return await this.client.getProcessLogs(id);\n },\n \n cleanupCompletedProcesses: async () => {\n // This would need a new endpoint to cleanup processes for a specific session\n // For now, return 0 as no cleanup is performed\n return 0;\n },\n \n // File operations - clean method names (no \"InSession\" suffix)\n writeFile: async (path: string, content: string, options?: { encoding?: string }) => {\n return await this.client.writeFile(path, content, options?.encoding, sessionId);\n },\n \n readFile: async (path: string, options?: { encoding?: string }) => {\n return await this.client.readFile(path, options?.encoding, sessionId);\n },\n\n readFileStream: async (path: string) => {\n return await this.client.readFileStream(path, sessionId);\n },\n\n mkdir: async (path: string, options?: { recursive?: boolean }) => {\n return await this.client.mkdir(path, options?.recursive, sessionId);\n },\n \n deleteFile: async (path: string) => {\n return await this.client.deleteFile(path, sessionId);\n },\n \n renameFile: async (oldPath: string, newPath: string) => {\n return await this.client.renameFile(oldPath, newPath, sessionId);\n },\n \n moveFile: async (sourcePath: string, destinationPath: string) => {\n return await this.client.moveFile(sourcePath, destinationPath, sessionId);\n },\n \n listFiles: async (path: string, options?: { recursive?: boolean; includeHidden?: boolean }) => {\n return await this.client.listFiles(path, sessionId, options);\n },\n \n gitCheckout: async (repoUrl: string, options?: { branch?: string; targetDir?: string }) => {\n return await this.client.gitCheckout(repoUrl, sessionId, options?.branch, options?.targetDir);\n },\n \n // Port management\n exposePort: async (port: number, options: { name?: string; hostname: string }) => {\n return await this.exposePort(port, options);\n },\n \n unexposePort: async (port: number) => {\n return await this.unexposePort(port);\n },\n \n getExposedPorts: async (hostname: string) => {\n return await this.getExposedPorts(hostname);\n },\n \n // Environment management\n setEnvVars: async (envVars: Record<string, string>) => {\n // TODO: Implement session-specific environment updates\n console.log(`[Session ${sessionId}] Environment variables update not yet implemented`);\n },\n \n // Code Interpreter API\n createCodeContext: async (options?: any) => {\n return await this.createCodeContext(options);\n },\n \n runCode: async (code: string, options?: any) => {\n return await this.runCode(code, options);\n },\n \n runCodeStream: async (code: string, options?: any) => {\n return await this.runCodeStream(code, options);\n },\n \n listCodeContexts: async () => {\n return await this.listCodeContexts();\n },\n \n deleteCodeContext: async (contextId: string) => {\n return await this.deleteCodeContext(contextId);\n }\n };\n }\n}\n","import { getSandbox, type Sandbox } from \"./sandbox\";\nimport {\n logSecurityEvent,\n sanitizeSandboxId,\n validatePort\n} from \"./security\";\n\nexport interface SandboxEnv {\n Sandbox: DurableObjectNamespace<Sandbox>;\n}\n\nexport interface RouteInfo {\n port: number;\n sandboxId: string;\n path: string;\n}\n\nexport async function proxyToSandbox<E extends SandboxEnv>(\n request: Request,\n env: E\n): Promise<Response | null> {\n try {\n const url = new URL(request.url);\n const routeInfo = extractSandboxRoute(url);\n\n if (!routeInfo) {\n return null; // Not a request to an exposed container port\n }\n\n const { sandboxId, port, path } = routeInfo;\n const sandbox = getSandbox(env.Sandbox, sandboxId);\n\n // Build proxy request with proper headers\n let proxyUrl: string;\n\n // Route based on the target port\n if (port !== 3000) {\n // Route directly to user's service on the specified port\n proxyUrl = `http://localhost:${port}${path}${url.search}`;\n } else {\n // Port 3000 is our control plane - route normally\n proxyUrl = `http://localhost:3000${path}${url.search}`;\n }\n\n const proxyRequest = new Request(proxyUrl, {\n method: request.method,\n headers: {\n ...Object.fromEntries(request.headers),\n 'X-Original-URL': request.url,\n 'X-Forwarded-Host': url.hostname,\n 'X-Forwarded-Proto': url.protocol.replace(':', ''),\n 'X-Sandbox-Name': sandboxId, // Pass the friendly name\n },\n body: request.body,\n });\n\n return sandbox.containerFetch(proxyRequest, port);\n } catch (error) {\n console.error('[Sandbox] Proxy routing error:', error);\n return new Response('Proxy routing error', { status: 500 });\n }\n}\n\nfunction extractSandboxRoute(url: URL): RouteInfo | null {\n // Parse subdomain pattern: port-sandboxId.domain\n const subdomainMatch = url.hostname.match(/^(\\d{4,5})-([^.-][^.]*[^.-]|[^.-])\\.(.+)$/);\n\n if (!subdomainMatch) {\n // Log malformed subdomain attempts\n if (url.hostname.includes('-') && url.hostname.includes('.')) {\n logSecurityEvent('MALFORMED_SUBDOMAIN_ATTEMPT', {\n hostname: url.hostname,\n url: url.toString()\n }, 'medium');\n }\n return null;\n }\n\n const portStr = subdomainMatch[1];\n const sandboxId = subdomainMatch[2];\n const domain = subdomainMatch[3];\n\n const port = parseInt(portStr, 10);\n if (!validatePort(port)) {\n logSecurityEvent('INVALID_PORT_IN_SUBDOMAIN', {\n port,\n portStr,\n sandboxId,\n hostname: url.hostname,\n url: url.toString()\n }, 'high');\n return null;\n }\n\n let sanitizedSandboxId: string;\n try {\n sanitizedSandboxId = sanitizeSandboxId(sandboxId);\n } catch (error) {\n logSecurityEvent('INVALID_SANDBOX_ID_IN_SUBDOMAIN', {\n sandboxId,\n port,\n hostname: url.hostname,\n url: url.toString(),\n error: error instanceof Error ? error.message : 'Unknown error'\n }, 'high');\n return null;\n }\n\n // DNS subdomain length limit is 63 characters\n if (sandboxId.length > 63) {\n logSecurityEvent('SANDBOX_ID_LENGTH_VIOLATION', {\n sandboxId,\n length: sandboxId.length,\n port,\n hostname: url.hostname\n }, 'medium');\n return null;\n }\n\n logSecurityEvent('SANDBOX_ROUTE_EXTRACTED', {\n port,\n sandboxId: sanitizedSandboxId,\n domain,\n path: url.pathname || \"/\",\n hostname: url.hostname\n }, 'low');\n\n return {\n port,\n sandboxId: sanitizedSandboxId,\n path: url.pathname || \"/\",\n };\n}\n\nexport function isLocalhostPattern(hostname: string): boolean {\n const hostPart = hostname.split(\":\")[0];\n return (\n hostPart === \"localhost\" ||\n hostPart === \"127.0.0.1\" ||\n hostPart === \"::1\" ||\n hostPart === \"[::1]\" ||\n hostPart === \"0.0.0.0\"\n );\n}\n"],"mappings":"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","names":["options"]}
|
|
@@ -76,6 +76,7 @@ declare class Sandbox<Env = unknown> extends Container<Env> implements ISandbox
|
|
|
76
76
|
readFile(path: string, options?: {
|
|
77
77
|
encoding?: string;
|
|
78
78
|
}): Promise<ReadFileResponse>;
|
|
79
|
+
readFileStream(path: string): Promise<ReadableStream<Uint8Array>>;
|
|
79
80
|
listFiles(path: string, options?: {
|
|
80
81
|
recursive?: boolean;
|
|
81
82
|
includeHidden?: boolean;
|
|
@@ -184,6 +185,7 @@ declare class HttpClient {
|
|
|
184
185
|
mkdir(path: string, recursive: boolean | undefined, sessionId: string): Promise<MkdirResponse>;
|
|
185
186
|
writeFile(path: string, content: string, encoding: string | undefined, sessionId: string): Promise<WriteFileResponse>;
|
|
186
187
|
readFile(path: string, encoding: string | undefined, sessionId: string): Promise<ReadFileResponse>;
|
|
188
|
+
readFileStream(path: string, sessionId: string): Promise<ReadableStream<Uint8Array>>;
|
|
187
189
|
deleteFile(path: string, sessionId: string): Promise<DeleteFileResponse>;
|
|
188
190
|
renameFile(oldPath: string, newPath: string, sessionId: string): Promise<RenameFileResponse>;
|
|
189
191
|
moveFile(sourcePath: string, destinationPath: string, sessionId: string): Promise<MoveFileResponse>;
|
package/dist/client.d.ts
CHANGED
package/dist/client.js
CHANGED
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
import { FileChunk, FileMetadata } from './types.js';
|
|
2
|
+
import './interpreter-types.js';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* File streaming utilities for reading binary and text files
|
|
6
|
+
* Provides simple AsyncIterable API over SSE stream with automatic base64 decoding
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* Convert ReadableStream of SSE file events to AsyncIterable of file chunks
|
|
11
|
+
* Automatically decodes base64 for binary files and provides metadata
|
|
12
|
+
*
|
|
13
|
+
* @param stream - The SSE ReadableStream from readFileStream()
|
|
14
|
+
* @param signal - Optional AbortSignal for cancellation
|
|
15
|
+
* @returns AsyncIterable that yields file chunks (string for text, Uint8Array for binary)
|
|
16
|
+
*
|
|
17
|
+
* @example
|
|
18
|
+
* ```typescript
|
|
19
|
+
* const stream = await sandbox.readFileStream('/path/to/file.png');
|
|
20
|
+
*
|
|
21
|
+
* for await (const chunk of streamFile(stream)) {
|
|
22
|
+
* if (chunk instanceof Uint8Array) {
|
|
23
|
+
* // Binary chunk - already decoded from base64
|
|
24
|
+
* console.log('Binary chunk:', chunk.byteLength, 'bytes');
|
|
25
|
+
* } else {
|
|
26
|
+
* // Text chunk
|
|
27
|
+
* console.log('Text chunk:', chunk);
|
|
28
|
+
* }
|
|
29
|
+
* }
|
|
30
|
+
*
|
|
31
|
+
* // Access metadata
|
|
32
|
+
* const iter = streamFile(stream);
|
|
33
|
+
* for await (const chunk of iter) {
|
|
34
|
+
* console.log('MIME type:', iter.metadata?.mimeType);
|
|
35
|
+
* // process chunk...
|
|
36
|
+
* }
|
|
37
|
+
* ```
|
|
38
|
+
*/
|
|
39
|
+
declare function streamFile(stream: ReadableStream<Uint8Array>, signal?: AbortSignal): AsyncGenerator<FileChunk, void, undefined>;
|
|
40
|
+
/**
|
|
41
|
+
* Helper to collect entire file from stream into memory
|
|
42
|
+
* Useful for smaller files where you want the complete content at once
|
|
43
|
+
*
|
|
44
|
+
* @param stream - The SSE ReadableStream from readFileStream()
|
|
45
|
+
* @param signal - Optional AbortSignal for cancellation
|
|
46
|
+
* @returns Object with content (string or Uint8Array) and metadata
|
|
47
|
+
*
|
|
48
|
+
* @example
|
|
49
|
+
* ```typescript
|
|
50
|
+
* const stream = await sandbox.readFileStream('/path/to/image.png');
|
|
51
|
+
* const { content, metadata } = await collectFile(stream);
|
|
52
|
+
*
|
|
53
|
+
* if (content instanceof Uint8Array) {
|
|
54
|
+
* console.log('Binary file:', metadata.mimeType, content.byteLength, 'bytes');
|
|
55
|
+
* } else {
|
|
56
|
+
* console.log('Text file:', metadata.mimeType, content.length, 'chars');
|
|
57
|
+
* }
|
|
58
|
+
* ```
|
|
59
|
+
*/
|
|
60
|
+
declare function collectFile(stream: ReadableStream<Uint8Array>, signal?: AbortSignal): Promise<{
|
|
61
|
+
content: string | Uint8Array;
|
|
62
|
+
metadata: FileMetadata;
|
|
63
|
+
}>;
|
|
64
|
+
|
|
65
|
+
export { collectFile, streamFile };
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":[],"sourcesContent":[],"mappings":"","names":[]}
|
package/dist/index.d.ts
CHANGED
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
export { CodeExecutionError, ContainerNotReadyError, ContextNotFoundError, InterpreterNotReadyError, InterpreterNotReadyError as JupyterNotReadyError, SandboxError, SandboxErrorResponse, SandboxNetworkError, ServiceUnavailableError, isInterpreterNotReadyError, isInterpreterNotReadyError as isJupyterNotReadyError, isRetryableError, isSandboxError, parseErrorResponse } from './errors.js';
|
|
2
2
|
export { ChartData, CodeContext, CreateContextOptions, Execution, ExecutionError, OutputMessage, Result, ResultImpl, RunCodeOptions } from './interpreter-types.js';
|
|
3
3
|
export { RouteInfo, SandboxEnv, proxyToSandbox } from './request-handler.js';
|
|
4
|
-
export { S as Sandbox, g as getSandbox } from './client-
|
|
4
|
+
export { S as Sandbox, g as getSandbox } from './client-B3RUab0s.js';
|
|
5
5
|
export { asyncIterableToSSEStream, parseSSEStream, responseToAsyncIterable } from './sse-parser.js';
|
|
6
|
-
export {
|
|
6
|
+
export { collectFile, streamFile } from './file-stream.js';
|
|
7
|
+
export { DeleteFileResponse, ExecEvent, ExecOptions, ExecResult, ExecuteResponse, ExecutionSession, FileChunk, FileMetadata, FileStream, FileStreamEvent, GitCheckoutResponse, ISandbox, ListFilesResponse, LogEvent, MkdirResponse, MoveFileResponse, Process, ProcessOptions, ProcessStatus, ReadFileResponse, RenameFileResponse, StreamOptions, WriteFileResponse } from './types.js';
|
|
7
8
|
import '@cloudflare/containers';
|
package/dist/index.js
CHANGED
|
@@ -1,3 +1,7 @@
|
|
|
1
|
+
import {
|
|
2
|
+
collectFile,
|
|
3
|
+
streamFile
|
|
4
|
+
} from "./chunk-5DILEXGY.js";
|
|
1
5
|
import {
|
|
2
6
|
asyncIterableToSSEStream,
|
|
3
7
|
parseSSEStream,
|
|
@@ -7,10 +11,10 @@ import {
|
|
|
7
11
|
Sandbox,
|
|
8
12
|
getSandbox,
|
|
9
13
|
proxyToSandbox
|
|
10
|
-
} from "./chunk-
|
|
14
|
+
} from "./chunk-SQLJNZ3K.js";
|
|
11
15
|
import "./chunk-6UAWTJ5S.js";
|
|
12
|
-
import "./chunk-
|
|
13
|
-
import "./chunk-
|
|
16
|
+
import "./chunk-D3U63BZP.js";
|
|
17
|
+
import "./chunk-32UDXUPC.js";
|
|
14
18
|
import {
|
|
15
19
|
CodeExecutionError,
|
|
16
20
|
ContainerNotReadyError,
|
|
@@ -40,6 +44,7 @@ export {
|
|
|
40
44
|
SandboxNetworkError,
|
|
41
45
|
ServiceUnavailableError,
|
|
42
46
|
asyncIterableToSSEStream,
|
|
47
|
+
collectFile,
|
|
43
48
|
getSandbox,
|
|
44
49
|
isInterpreterNotReadyError,
|
|
45
50
|
isInterpreterNotReadyError as isJupyterNotReadyError,
|
|
@@ -48,6 +53,7 @@ export {
|
|
|
48
53
|
parseErrorResponse,
|
|
49
54
|
parseSSEStream,
|
|
50
55
|
proxyToSandbox,
|
|
51
|
-
responseToAsyncIterable
|
|
56
|
+
responseToAsyncIterable,
|
|
57
|
+
streamFile
|
|
52
58
|
};
|
|
53
59
|
//# sourceMappingURL=index.js.map
|
package/dist/interpreter.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { CreateContextOptions, CodeContext, RunCodeOptions, Execution } from './interpreter-types.js';
|
|
2
|
-
import { S as Sandbox } from './client-
|
|
2
|
+
import { S as Sandbox } from './client-B3RUab0s.js';
|
|
3
3
|
import './types.js';
|
|
4
4
|
import '@cloudflare/containers';
|
|
5
5
|
|
package/dist/request-handler.js
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
import {
|
|
2
2
|
isLocalhostPattern,
|
|
3
3
|
proxyToSandbox
|
|
4
|
-
} from "./chunk-
|
|
4
|
+
} from "./chunk-SQLJNZ3K.js";
|
|
5
5
|
import "./chunk-6UAWTJ5S.js";
|
|
6
|
-
import "./chunk-
|
|
7
|
-
import "./chunk-
|
|
6
|
+
import "./chunk-D3U63BZP.js";
|
|
7
|
+
import "./chunk-32UDXUPC.js";
|
|
8
8
|
import "./chunk-FXYPFGOZ.js";
|
|
9
9
|
import "./chunk-JTKON2SH.js";
|
|
10
10
|
import "./chunk-W7TVRPBG.js";
|
package/dist/sandbox.d.ts
CHANGED
package/dist/sandbox.js
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
import {
|
|
2
2
|
Sandbox,
|
|
3
3
|
getSandbox
|
|
4
|
-
} from "./chunk-
|
|
4
|
+
} from "./chunk-SQLJNZ3K.js";
|
|
5
5
|
import "./chunk-6UAWTJ5S.js";
|
|
6
|
-
import "./chunk-
|
|
7
|
-
import "./chunk-
|
|
6
|
+
import "./chunk-D3U63BZP.js";
|
|
7
|
+
import "./chunk-32UDXUPC.js";
|
|
8
8
|
import "./chunk-FXYPFGOZ.js";
|
|
9
9
|
import "./chunk-JTKON2SH.js";
|
|
10
10
|
import "./chunk-W7TVRPBG.js";
|
package/dist/types.d.ts
CHANGED
|
@@ -169,6 +169,44 @@ interface StreamOptions extends BaseExecOptions {
|
|
|
169
169
|
*/
|
|
170
170
|
signal?: AbortSignal;
|
|
171
171
|
}
|
|
172
|
+
/**
|
|
173
|
+
* SSE events for file streaming
|
|
174
|
+
*/
|
|
175
|
+
type FileStreamEvent = {
|
|
176
|
+
type: 'metadata';
|
|
177
|
+
mimeType: string;
|
|
178
|
+
size: number;
|
|
179
|
+
isBinary: boolean;
|
|
180
|
+
encoding: 'utf-8' | 'base64';
|
|
181
|
+
} | {
|
|
182
|
+
type: 'chunk';
|
|
183
|
+
data: string;
|
|
184
|
+
} | {
|
|
185
|
+
type: 'complete';
|
|
186
|
+
bytesRead: number;
|
|
187
|
+
} | {
|
|
188
|
+
type: 'error';
|
|
189
|
+
error: string;
|
|
190
|
+
};
|
|
191
|
+
/**
|
|
192
|
+
* File metadata from streaming
|
|
193
|
+
*/
|
|
194
|
+
interface FileMetadata {
|
|
195
|
+
mimeType: string;
|
|
196
|
+
size: number;
|
|
197
|
+
isBinary: boolean;
|
|
198
|
+
encoding: 'utf-8' | 'base64';
|
|
199
|
+
}
|
|
200
|
+
/**
|
|
201
|
+
* File stream chunk - either string (text) or Uint8Array (binary, auto-decoded)
|
|
202
|
+
*/
|
|
203
|
+
type FileChunk = string | Uint8Array;
|
|
204
|
+
/**
|
|
205
|
+
* AsyncIterable of file chunks with metadata
|
|
206
|
+
*/
|
|
207
|
+
interface FileStream extends AsyncIterable<FileChunk> {
|
|
208
|
+
metadata?: FileMetadata;
|
|
209
|
+
}
|
|
172
210
|
declare class SandboxError extends Error {
|
|
173
211
|
code?: string | undefined;
|
|
174
212
|
constructor(message: string, code?: string | undefined);
|
|
@@ -278,6 +316,7 @@ interface ISandbox {
|
|
|
278
316
|
readFile(path: string, options?: {
|
|
279
317
|
encoding?: string;
|
|
280
318
|
}): Promise<ReadFileResponse>;
|
|
319
|
+
readFileStream(path: string): Promise<ReadableStream<Uint8Array>>;
|
|
281
320
|
listFiles(path: string, options?: {
|
|
282
321
|
recursive?: boolean;
|
|
283
322
|
includeHidden?: boolean;
|
|
@@ -350,6 +389,22 @@ interface ReadFileResponse {
|
|
|
350
389
|
path: string;
|
|
351
390
|
content: string;
|
|
352
391
|
timestamp: string;
|
|
392
|
+
/**
|
|
393
|
+
* Encoding used for content (utf-8 for text, base64 for binary)
|
|
394
|
+
*/
|
|
395
|
+
encoding?: 'utf-8' | 'base64';
|
|
396
|
+
/**
|
|
397
|
+
* Whether the file is detected as binary
|
|
398
|
+
*/
|
|
399
|
+
isBinary?: boolean;
|
|
400
|
+
/**
|
|
401
|
+
* MIME type of the file (e.g., 'image/png', 'text/plain')
|
|
402
|
+
*/
|
|
403
|
+
mimeType?: string;
|
|
404
|
+
/**
|
|
405
|
+
* File size in bytes
|
|
406
|
+
*/
|
|
407
|
+
size?: number;
|
|
353
408
|
}
|
|
354
409
|
interface DeleteFileResponse {
|
|
355
410
|
success: boolean;
|
|
@@ -395,4 +450,4 @@ declare function isExecResult(value: any): value is ExecResult;
|
|
|
395
450
|
declare function isProcess(value: any): value is Process;
|
|
396
451
|
declare function isProcessStatus(value: string): value is ProcessStatus;
|
|
397
452
|
|
|
398
|
-
export { type BaseExecOptions, type DeleteFileResponse, type ExecEvent, type ExecOptions, type ExecResult, type ExecuteResponse, type ExecutionSession, ExecutionTimeoutError, type GetProcessLogsResponse, type GetProcessResponse, type GitCheckoutResponse, type ISandbox, type ListFilesResponse, type ListProcessesResponse, type LogEvent, type MkdirResponse, type MoveFileResponse, type Process, ProcessAlreadyExistsError, ProcessNotFoundError, type ProcessOptions, type ProcessRecord, type ProcessStatus, type ReadFileResponse, type RenameFileResponse, SandboxError, type StartProcessRequest, type StartProcessResponse, type StreamOptions, type WriteFileResponse, isExecResult, isProcess, isProcessStatus };
|
|
453
|
+
export { type BaseExecOptions, type DeleteFileResponse, type ExecEvent, type ExecOptions, type ExecResult, type ExecuteResponse, type ExecutionSession, ExecutionTimeoutError, type FileChunk, type FileMetadata, type FileStream, type FileStreamEvent, type GetProcessLogsResponse, type GetProcessResponse, type GitCheckoutResponse, type ISandbox, type ListFilesResponse, type ListProcessesResponse, type LogEvent, type MkdirResponse, type MoveFileResponse, type Process, ProcessAlreadyExistsError, ProcessNotFoundError, type ProcessOptions, type ProcessRecord, type ProcessStatus, type ReadFileResponse, type RenameFileResponse, SandboxError, type StartProcessRequest, type StartProcessResponse, type StreamOptions, type WriteFileResponse, isExecResult, isProcess, isProcessStatus };
|
package/dist/types.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/types.ts"],"sourcesContent":["// Core Types\n\nexport interface BaseExecOptions {\n /**\n * Maximum execution time in milliseconds\n */\n timeout?: number;\n\n /**\n * Environment variables for the command\n */\n env?: Record<string, string>;\n\n /**\n * Working directory for command execution\n */\n cwd?: string;\n\n /**\n * Text encoding for output (default: 'utf8')\n */\n encoding?: string;\n}\n\nexport interface ExecOptions extends BaseExecOptions {\n /**\n * Enable real-time output streaming via callbacks\n */\n stream?: boolean;\n\n /**\n * Callback for real-time output data\n */\n onOutput?: (stream: 'stdout' | 'stderr', data: string) => void;\n\n /**\n * Callback when command completes (only when stream: true)\n */\n onComplete?: (result: ExecResult) => void;\n\n /**\n * Callback for execution errors\n */\n onError?: (error: Error) => void;\n\n /**\n * AbortSignal for cancelling execution\n */\n signal?: AbortSignal;\n}\n\nexport interface ExecResult {\n /**\n * Whether the command succeeded (exitCode === 0)\n */\n success: boolean;\n\n /**\n * Process exit code\n */\n exitCode: number;\n\n /**\n * Standard output content\n */\n stdout: string;\n\n /**\n * Standard error content\n */\n stderr: string;\n\n /**\n * Command that was executed\n */\n command: string;\n\n\n /**\n * Execution duration in milliseconds\n */\n duration: number;\n\n /**\n * ISO timestamp when command started\n */\n timestamp: string;\n}\n\n// Background Process Types\n\nexport interface ProcessOptions extends BaseExecOptions {\n /**\n * Custom process ID for later reference\n * If not provided, a UUID will be generated\n */\n processId?: string;\n\n /**\n * Automatically cleanup process record after exit (default: true)\n */\n autoCleanup?: boolean;\n\n /**\n * Callback when process exits\n */\n onExit?: (code: number | null) => void;\n\n /**\n * Callback for real-time output (background processes)\n */\n onOutput?: (stream: 'stdout' | 'stderr', data: string) => void;\n\n /**\n * Callback when process starts successfully\n */\n onStart?: (process: Process) => void;\n\n /**\n * Callback for process errors\n */\n onError?: (error: Error) => void;\n}\n\nexport type ProcessStatus =\n | 'starting' // Process is being initialized\n | 'running' // Process is actively running\n | 'completed' // Process exited successfully (code 0)\n | 'failed' // Process exited with non-zero code\n | 'killed' // Process was terminated by signal\n | 'error'; // Process failed to start or encountered error\n\nexport interface Process {\n /**\n * Unique process identifier\n */\n readonly id: string;\n\n /**\n * System process ID (if available and running)\n */\n readonly pid?: number;\n\n /**\n * Command that was executed\n */\n readonly command: string;\n\n\n /**\n * Current process status\n */\n readonly status: ProcessStatus;\n\n /**\n * When the process was started\n */\n readonly startTime: Date;\n\n /**\n * When the process ended (if completed)\n */\n readonly endTime?: Date;\n\n /**\n * Process exit code (if completed)\n */\n readonly exitCode?: number;\n\n /**\n * Kill the process\n */\n kill(signal?: string): Promise<void>;\n\n /**\n * Get current process status (refreshed)\n */\n getStatus(): Promise<ProcessStatus>;\n\n /**\n * Get accumulated logs\n */\n getLogs(): Promise<{ stdout: string; stderr: string }>;\n}\n\n// Streaming Types\n\nexport interface ExecEvent {\n type: 'start' | 'stdout' | 'stderr' | 'complete' | 'error';\n timestamp: string;\n data?: string;\n command?: string;\n exitCode?: number;\n result?: ExecResult;\n error?: string; // Changed to string for serialization\n}\n\nexport interface LogEvent {\n type: 'stdout' | 'stderr' | 'exit' | 'error';\n timestamp: string;\n data: string;\n processId: string;\n exitCode?: number; // For 'exit' events\n}\n\nexport interface StreamOptions extends BaseExecOptions {\n /**\n * Buffer size for streaming output\n */\n bufferSize?: number;\n\n /**\n * AbortSignal for cancelling stream\n */\n signal?: AbortSignal;\n}\n\n// Error Types\n\nexport class SandboxError extends Error {\n constructor(message: string, public code?: string) {\n super(message);\n this.name = 'SandboxError';\n }\n}\n\nexport class ProcessNotFoundError extends SandboxError {\n constructor(processId: string) {\n super(`Process not found: ${processId}`, 'PROCESS_NOT_FOUND');\n this.name = 'ProcessNotFoundError';\n }\n}\n\nexport class ProcessAlreadyExistsError extends SandboxError {\n constructor(processId: string) {\n super(`Process already exists: ${processId}`, 'PROCESS_EXISTS');\n this.name = 'ProcessAlreadyExistsError';\n }\n}\n\nexport class ExecutionTimeoutError extends SandboxError {\n constructor(timeout: number) {\n super(`Execution timed out after ${timeout}ms`, 'EXECUTION_TIMEOUT');\n this.name = 'ExecutionTimeoutError';\n }\n}\n\n// Internal Container Types\n\nexport interface ProcessRecord {\n id: string;\n pid?: number;\n command: string;\n status: ProcessStatus;\n startTime: Date;\n endTime?: Date;\n exitCode?: number;\n\n // Internal fields\n stdout: string; // Accumulated output (ephemeral)\n stderr: string; // Accumulated output (ephemeral)\n\n // Streaming\n outputListeners: Set<(stream: 'stdout' | 'stderr', data: string) => void>;\n statusListeners: Set<(status: ProcessStatus) => void>;\n}\n\n// Container Request/Response Types\n\nexport interface StartProcessRequest {\n command: string;\n options?: {\n processId?: string;\n timeout?: number;\n env?: Record<string, string>;\n cwd?: string;\n encoding?: string;\n autoCleanup?: boolean;\n };\n}\n\nexport interface StartProcessResponse {\n process: {\n id: string;\n pid?: number;\n command: string;\n status: ProcessStatus;\n startTime: string;\n endTime?: string | null;\n exitCode?: number | null;\n sessionId: string;\n };\n}\n\nexport interface ListProcessesResponse {\n processes: Array<{\n id: string;\n pid?: number;\n command: string;\n status: ProcessStatus;\n startTime: string;\n endTime?: string;\n exitCode?: number;\n }>;\n}\n\nexport interface GetProcessResponse {\n process: {\n id: string;\n pid?: number;\n command: string;\n status: ProcessStatus;\n startTime: string;\n endTime?: string;\n exitCode?: number;\n } | null;\n}\n\nexport interface GetProcessLogsResponse {\n stdout: string;\n stderr: string;\n processId: string;\n}\n\n// Import code interpreter types\nimport type {\n CodeContext,\n CreateContextOptions,\n ExecutionResult, \n RunCodeOptions\n} from './interpreter-types';\n\n// Main Sandbox Interface\n\nexport interface ISandbox {\n // Enhanced execution API\n exec(command: string, options?: ExecOptions): Promise<ExecResult>;\n\n // Background process management\n startProcess(command: string, options?: ProcessOptions): Promise<Process>;\n listProcesses(): Promise<Process[]>;\n getProcess(id: string): Promise<Process | null>;\n killProcess(id: string, signal?: string): Promise<void>;\n killAllProcesses(): Promise<number>;\n\n // Advanced streaming - returns ReadableStream that can be converted to AsyncIterable\n execStream(command: string, options?: StreamOptions): Promise<ReadableStream<Uint8Array>>;\n streamProcessLogs(processId: string, options?: { signal?: AbortSignal }): Promise<ReadableStream<Uint8Array>>;\n\n // Utility methods\n cleanupCompletedProcesses(): Promise<number>;\n getProcessLogs(id: string): Promise<{ stdout: string; stderr: string }>;\n\n // File operations\n gitCheckout(repoUrl: string, options: { branch?: string; targetDir?: string }): Promise<GitCheckoutResponse>;\n mkdir(path: string, options?: { recursive?: boolean }): Promise<MkdirResponse>;\n writeFile(path: string, content: string, options?: { encoding?: string }): Promise<WriteFileResponse>;\n deleteFile(path: string): Promise<DeleteFileResponse>;\n renameFile(oldPath: string, newPath: string): Promise<RenameFileResponse>;\n moveFile(sourcePath: string, destinationPath: string): Promise<MoveFileResponse>;\n readFile(path: string, options?: { encoding?: string }): Promise<ReadFileResponse>;\n listFiles(path: string, options?: { recursive?: boolean; includeHidden?: boolean }): Promise<ListFilesResponse>;\n\n // Port management\n exposePort(port: number, options: { name?: string; hostname: string }): Promise<{ url: string; port: number; name?: string }>;\n unexposePort(port: number): Promise<void>;\n getExposedPorts(hostname: string): Promise<Array<{ url: string; port: number; name?: string; exposedAt: string }>>;\n\n // Environment management\n setEnvVars(envVars: Record<string, string>): Promise<void>;\n setSandboxName(name: string): Promise<void>;\n\n // Code Interpreter API\n createCodeContext(options?: CreateContextOptions): Promise<CodeContext>;\n runCode(code: string, options?: RunCodeOptions): Promise<ExecutionResult>;\n runCodeStream(code: string, options?: RunCodeOptions): Promise<ReadableStream>;\n listCodeContexts(): Promise<CodeContext[]>;\n deleteCodeContext(contextId: string): Promise<void>;\n}\n\n// Execution session returned by createSession()\n// Sessions are full-featured sandbox objects with scoped execution context\n// Inherits all ISandbox methods except createSession (sessions can't create sub-sessions),\n// and setSandboxName (sessions inherit sandbox name).\nexport interface ExecutionSession extends Omit<ISandbox, 'createSession' | 'setSandboxName'> {\n /**\n * Session ID\n */\n id: string;\n}\n\n// API Response Types\n\nexport interface ExecuteResponse {\n success: boolean;\n stdout: string;\n stderr: string;\n exitCode: number;\n command: string;\n timestamp: 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\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\nexport interface WriteFileResponse {\n success: boolean;\n exitCode: number;\n path: string;\n timestamp: string;\n}\n\nexport interface ReadFileResponse {\n success: boolean;\n exitCode: number;\n path: string;\n content: string;\n timestamp: string;\n}\n\nexport interface DeleteFileResponse {\n success: boolean;\n exitCode: number;\n path: string;\n timestamp: string;\n}\n\nexport interface RenameFileResponse {\n success: boolean;\n exitCode: number;\n oldPath: string;\n newPath: string;\n timestamp: string;\n}\n\nexport interface MoveFileResponse {\n success: boolean;\n exitCode: number;\n sourcePath: string;\n destinationPath: string;\n timestamp: string;\n}\n\nexport interface ListFilesResponse {\n success: boolean;\n exitCode: number;\n path: string;\n files: Array<{\n name: string;\n absolutePath: string;\n relativePath: string;\n type: 'file' | 'directory' | 'symlink' | 'other';\n size: number;\n modifiedAt: string;\n mode: string;\n permissions: {\n readable: boolean;\n writable: boolean;\n executable: boolean;\n };\n }>;\n timestamp: string;\n}\n\n// Type Guards\n\nexport function isExecResult(value: any): value is ExecResult {\n return value &&\n typeof value.success === 'boolean' &&\n typeof value.exitCode === 'number' &&\n typeof value.stdout === 'string' &&\n typeof value.stderr === 'string';\n}\n\nexport function isProcess(value: any): value is Process {\n return value &&\n typeof value.id === 'string' &&\n typeof value.command === 'string' &&\n typeof value.status === 'string';\n}\n\nexport function isProcessStatus(value: string): value is ProcessStatus {\n return ['starting', 'running', 'completed', 'failed', 'killed', 'error'].includes(value);\n}"],"mappings":";AA2NO,IAAM,eAAN,cAA2B,MAAM;AAAA,EACtC,YAAY,SAAwB,MAAe;AACjD,UAAM,OAAO;AADqB;AAElC,SAAK,OAAO;AAAA,EACd;AACF;AAEO,IAAM,uBAAN,cAAmC,aAAa;AAAA,EACrD,YAAY,WAAmB;AAC7B,UAAM,sBAAsB,SAAS,IAAI,mBAAmB;AAC5D,SAAK,OAAO;AAAA,EACd;AACF;AAEO,IAAM,4BAAN,cAAwC,aAAa;AAAA,EAC1D,YAAY,WAAmB;AAC7B,UAAM,2BAA2B,SAAS,IAAI,gBAAgB;AAC9D,SAAK,OAAO;AAAA,EACd;AACF;AAEO,IAAM,wBAAN,cAAoC,aAAa;AAAA,EACtD,YAAY,SAAiB;AAC3B,UAAM,6BAA6B,OAAO,MAAM,mBAAmB;AACnE,SAAK,OAAO;AAAA,EACd;AACF;AA+OO,SAAS,aAAa,OAAiC;AAC5D,SAAO,SACL,OAAO,MAAM,YAAY,aACzB,OAAO,MAAM,aAAa,YAC1B,OAAO,MAAM,WAAW,YACxB,OAAO,MAAM,WAAW;AAC5B;AAEO,SAAS,UAAU,OAA8B;AACtD,SAAO,SACL,OAAO,MAAM,OAAO,YACpB,OAAO,MAAM,YAAY,YACzB,OAAO,MAAM,WAAW;AAC5B;AAEO,SAAS,gBAAgB,OAAuC;AACrE,SAAO,CAAC,YAAY,WAAW,aAAa,UAAU,UAAU,OAAO,EAAE,SAAS,KAAK;AACzF;","names":[]}
|
|
1
|
+
{"version":3,"sources":["../src/types.ts"],"sourcesContent":["// Core Types\n\nexport interface BaseExecOptions {\n /**\n * Maximum execution time in milliseconds\n */\n timeout?: number;\n\n /**\n * Environment variables for the command\n */\n env?: Record<string, string>;\n\n /**\n * Working directory for command execution\n */\n cwd?: string;\n\n /**\n * Text encoding for output (default: 'utf8')\n */\n encoding?: string;\n}\n\nexport interface ExecOptions extends BaseExecOptions {\n /**\n * Enable real-time output streaming via callbacks\n */\n stream?: boolean;\n\n /**\n * Callback for real-time output data\n */\n onOutput?: (stream: 'stdout' | 'stderr', data: string) => void;\n\n /**\n * Callback when command completes (only when stream: true)\n */\n onComplete?: (result: ExecResult) => void;\n\n /**\n * Callback for execution errors\n */\n onError?: (error: Error) => void;\n\n /**\n * AbortSignal for cancelling execution\n */\n signal?: AbortSignal;\n}\n\nexport interface ExecResult {\n /**\n * Whether the command succeeded (exitCode === 0)\n */\n success: boolean;\n\n /**\n * Process exit code\n */\n exitCode: number;\n\n /**\n * Standard output content\n */\n stdout: string;\n\n /**\n * Standard error content\n */\n stderr: string;\n\n /**\n * Command that was executed\n */\n command: string;\n\n\n /**\n * Execution duration in milliseconds\n */\n duration: number;\n\n /**\n * ISO timestamp when command started\n */\n timestamp: string;\n}\n\n// Background Process Types\n\nexport interface ProcessOptions extends BaseExecOptions {\n /**\n * Custom process ID for later reference\n * If not provided, a UUID will be generated\n */\n processId?: string;\n\n /**\n * Automatically cleanup process record after exit (default: true)\n */\n autoCleanup?: boolean;\n\n /**\n * Callback when process exits\n */\n onExit?: (code: number | null) => void;\n\n /**\n * Callback for real-time output (background processes)\n */\n onOutput?: (stream: 'stdout' | 'stderr', data: string) => void;\n\n /**\n * Callback when process starts successfully\n */\n onStart?: (process: Process) => void;\n\n /**\n * Callback for process errors\n */\n onError?: (error: Error) => void;\n}\n\nexport type ProcessStatus =\n | 'starting' // Process is being initialized\n | 'running' // Process is actively running\n | 'completed' // Process exited successfully (code 0)\n | 'failed' // Process exited with non-zero code\n | 'killed' // Process was terminated by signal\n | 'error'; // Process failed to start or encountered error\n\nexport interface Process {\n /**\n * Unique process identifier\n */\n readonly id: string;\n\n /**\n * System process ID (if available and running)\n */\n readonly pid?: number;\n\n /**\n * Command that was executed\n */\n readonly command: string;\n\n\n /**\n * Current process status\n */\n readonly status: ProcessStatus;\n\n /**\n * When the process was started\n */\n readonly startTime: Date;\n\n /**\n * When the process ended (if completed)\n */\n readonly endTime?: Date;\n\n /**\n * Process exit code (if completed)\n */\n readonly exitCode?: number;\n\n /**\n * Kill the process\n */\n kill(signal?: string): Promise<void>;\n\n /**\n * Get current process status (refreshed)\n */\n getStatus(): Promise<ProcessStatus>;\n\n /**\n * Get accumulated logs\n */\n getLogs(): Promise<{ stdout: string; stderr: string }>;\n}\n\n// Streaming Types\n\nexport interface ExecEvent {\n type: 'start' | 'stdout' | 'stderr' | 'complete' | 'error';\n timestamp: string;\n data?: string;\n command?: string;\n exitCode?: number;\n result?: ExecResult;\n error?: string; // Changed to string for serialization\n}\n\nexport interface LogEvent {\n type: 'stdout' | 'stderr' | 'exit' | 'error';\n timestamp: string;\n data: string;\n processId: string;\n exitCode?: number; // For 'exit' events\n}\n\nexport interface StreamOptions extends BaseExecOptions {\n /**\n * Buffer size for streaming output\n */\n bufferSize?: number;\n\n /**\n * AbortSignal for cancelling stream\n */\n signal?: AbortSignal;\n}\n\n// File Streaming Types\n\n/**\n * SSE events for file streaming\n */\nexport type FileStreamEvent =\n | {\n type: 'metadata';\n mimeType: string;\n size: number;\n isBinary: boolean;\n encoding: 'utf-8' | 'base64';\n }\n | {\n type: 'chunk';\n data: string; // base64 for binary, UTF-8 for text\n }\n | {\n type: 'complete';\n bytesRead: number;\n }\n | {\n type: 'error';\n error: string;\n };\n\n/**\n * File metadata from streaming\n */\nexport interface FileMetadata {\n mimeType: string;\n size: number;\n isBinary: boolean;\n encoding: 'utf-8' | 'base64';\n}\n\n/**\n * File stream chunk - either string (text) or Uint8Array (binary, auto-decoded)\n */\nexport type FileChunk = string | Uint8Array;\n\n/**\n * AsyncIterable of file chunks with metadata\n */\nexport interface FileStream extends AsyncIterable<FileChunk> {\n metadata?: FileMetadata;\n}\n\n// Error Types\n\nexport class SandboxError extends Error {\n constructor(message: string, public code?: string) {\n super(message);\n this.name = 'SandboxError';\n }\n}\n\nexport class ProcessNotFoundError extends SandboxError {\n constructor(processId: string) {\n super(`Process not found: ${processId}`, 'PROCESS_NOT_FOUND');\n this.name = 'ProcessNotFoundError';\n }\n}\n\nexport class ProcessAlreadyExistsError extends SandboxError {\n constructor(processId: string) {\n super(`Process already exists: ${processId}`, 'PROCESS_EXISTS');\n this.name = 'ProcessAlreadyExistsError';\n }\n}\n\nexport class ExecutionTimeoutError extends SandboxError {\n constructor(timeout: number) {\n super(`Execution timed out after ${timeout}ms`, 'EXECUTION_TIMEOUT');\n this.name = 'ExecutionTimeoutError';\n }\n}\n\n// Internal Container Types\n\nexport interface ProcessRecord {\n id: string;\n pid?: number;\n command: string;\n status: ProcessStatus;\n startTime: Date;\n endTime?: Date;\n exitCode?: number;\n\n // Internal fields\n stdout: string; // Accumulated output (ephemeral)\n stderr: string; // Accumulated output (ephemeral)\n\n // Streaming\n outputListeners: Set<(stream: 'stdout' | 'stderr', data: string) => void>;\n statusListeners: Set<(status: ProcessStatus) => void>;\n}\n\n// Container Request/Response Types\n\nexport interface StartProcessRequest {\n command: string;\n options?: {\n processId?: string;\n timeout?: number;\n env?: Record<string, string>;\n cwd?: string;\n encoding?: string;\n autoCleanup?: boolean;\n };\n}\n\nexport interface StartProcessResponse {\n process: {\n id: string;\n pid?: number;\n command: string;\n status: ProcessStatus;\n startTime: string;\n endTime?: string | null;\n exitCode?: number | null;\n sessionId: string;\n };\n}\n\nexport interface ListProcessesResponse {\n processes: Array<{\n id: string;\n pid?: number;\n command: string;\n status: ProcessStatus;\n startTime: string;\n endTime?: string;\n exitCode?: number;\n }>;\n}\n\nexport interface GetProcessResponse {\n process: {\n id: string;\n pid?: number;\n command: string;\n status: ProcessStatus;\n startTime: string;\n endTime?: string;\n exitCode?: number;\n } | null;\n}\n\nexport interface GetProcessLogsResponse {\n stdout: string;\n stderr: string;\n processId: string;\n}\n\n// Import code interpreter types\nimport type {\n CodeContext,\n CreateContextOptions,\n ExecutionResult, \n RunCodeOptions\n} from './interpreter-types';\n\n// Main Sandbox Interface\n\nexport interface ISandbox {\n // Enhanced execution API\n exec(command: string, options?: ExecOptions): Promise<ExecResult>;\n\n // Background process management\n startProcess(command: string, options?: ProcessOptions): Promise<Process>;\n listProcesses(): Promise<Process[]>;\n getProcess(id: string): Promise<Process | null>;\n killProcess(id: string, signal?: string): Promise<void>;\n killAllProcesses(): Promise<number>;\n\n // Advanced streaming - returns ReadableStream that can be converted to AsyncIterable\n execStream(command: string, options?: StreamOptions): Promise<ReadableStream<Uint8Array>>;\n streamProcessLogs(processId: string, options?: { signal?: AbortSignal }): Promise<ReadableStream<Uint8Array>>;\n\n // Utility methods\n cleanupCompletedProcesses(): Promise<number>;\n getProcessLogs(id: string): Promise<{ stdout: string; stderr: string }>;\n\n // File operations\n gitCheckout(repoUrl: string, options: { branch?: string; targetDir?: string }): Promise<GitCheckoutResponse>;\n mkdir(path: string, options?: { recursive?: boolean }): Promise<MkdirResponse>;\n writeFile(path: string, content: string, options?: { encoding?: string }): Promise<WriteFileResponse>;\n deleteFile(path: string): Promise<DeleteFileResponse>;\n renameFile(oldPath: string, newPath: string): Promise<RenameFileResponse>;\n moveFile(sourcePath: string, destinationPath: string): Promise<MoveFileResponse>;\n readFile(path: string, options?: { encoding?: string }): Promise<ReadFileResponse>;\n readFileStream(path: string): Promise<ReadableStream<Uint8Array>>;\n listFiles(path: string, options?: { recursive?: boolean; includeHidden?: boolean }): Promise<ListFilesResponse>;\n\n // Port management\n exposePort(port: number, options: { name?: string; hostname: string }): Promise<{ url: string; port: number; name?: string }>;\n unexposePort(port: number): Promise<void>;\n getExposedPorts(hostname: string): Promise<Array<{ url: string; port: number; name?: string; exposedAt: string }>>;\n\n // Environment management\n setEnvVars(envVars: Record<string, string>): Promise<void>;\n setSandboxName(name: string): Promise<void>;\n\n // Code Interpreter API\n createCodeContext(options?: CreateContextOptions): Promise<CodeContext>;\n runCode(code: string, options?: RunCodeOptions): Promise<ExecutionResult>;\n runCodeStream(code: string, options?: RunCodeOptions): Promise<ReadableStream>;\n listCodeContexts(): Promise<CodeContext[]>;\n deleteCodeContext(contextId: string): Promise<void>;\n}\n\n// Execution session returned by createSession()\n// Sessions are full-featured sandbox objects with scoped execution context\n// Inherits all ISandbox methods except createSession (sessions can't create sub-sessions),\n// and setSandboxName (sessions inherit sandbox name).\nexport interface ExecutionSession extends Omit<ISandbox, 'createSession' | 'setSandboxName'> {\n /**\n * Session ID\n */\n id: string;\n}\n\n// API Response Types\n\nexport interface ExecuteResponse {\n success: boolean;\n stdout: string;\n stderr: string;\n exitCode: number;\n command: string;\n timestamp: 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\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\nexport interface WriteFileResponse {\n success: boolean;\n exitCode: number;\n path: string;\n timestamp: string;\n}\n\nexport interface ReadFileResponse {\n success: boolean;\n exitCode: number;\n path: string;\n content: string;\n timestamp: string;\n\n /**\n * Encoding used for content (utf-8 for text, base64 for binary)\n */\n encoding?: 'utf-8' | 'base64';\n\n /**\n * Whether the file is detected as binary\n */\n isBinary?: boolean;\n\n /**\n * MIME type of the file (e.g., 'image/png', 'text/plain')\n */\n mimeType?: string;\n\n /**\n * File size in bytes\n */\n size?: number;\n}\n\nexport interface DeleteFileResponse {\n success: boolean;\n exitCode: number;\n path: string;\n timestamp: string;\n}\n\nexport interface RenameFileResponse {\n success: boolean;\n exitCode: number;\n oldPath: string;\n newPath: string;\n timestamp: string;\n}\n\nexport interface MoveFileResponse {\n success: boolean;\n exitCode: number;\n sourcePath: string;\n destinationPath: string;\n timestamp: string;\n}\n\nexport interface ListFilesResponse {\n success: boolean;\n exitCode: number;\n path: string;\n files: Array<{\n name: string;\n absolutePath: string;\n relativePath: string;\n type: 'file' | 'directory' | 'symlink' | 'other';\n size: number;\n modifiedAt: string;\n mode: string;\n permissions: {\n readable: boolean;\n writable: boolean;\n executable: boolean;\n };\n }>;\n timestamp: string;\n}\n\n// Type Guards\n\nexport function isExecResult(value: any): value is ExecResult {\n return value &&\n typeof value.success === 'boolean' &&\n typeof value.exitCode === 'number' &&\n typeof value.stdout === 'string' &&\n typeof value.stderr === 'string';\n}\n\nexport function isProcess(value: any): value is Process {\n return value &&\n typeof value.id === 'string' &&\n typeof value.command === 'string' &&\n typeof value.status === 'string';\n}\n\nexport function isProcessStatus(value: string): value is ProcessStatus {\n return ['starting', 'running', 'completed', 'failed', 'killed', 'error'].includes(value);\n}"],"mappings":";AA2QO,IAAM,eAAN,cAA2B,MAAM;AAAA,EACtC,YAAY,SAAwB,MAAe;AACjD,UAAM,OAAO;AADqB;AAElC,SAAK,OAAO;AAAA,EACd;AACF;AAEO,IAAM,uBAAN,cAAmC,aAAa;AAAA,EACrD,YAAY,WAAmB;AAC7B,UAAM,sBAAsB,SAAS,IAAI,mBAAmB;AAC5D,SAAK,OAAO;AAAA,EACd;AACF;AAEO,IAAM,4BAAN,cAAwC,aAAa;AAAA,EAC1D,YAAY,WAAmB;AAC7B,UAAM,2BAA2B,SAAS,IAAI,gBAAgB;AAC9D,SAAK,OAAO;AAAA,EACd;AACF;AAEO,IAAM,wBAAN,cAAoC,aAAa;AAAA,EACtD,YAAY,SAAiB;AAC3B,UAAM,6BAA6B,OAAO,MAAM,mBAAmB;AACnE,SAAK,OAAO;AAAA,EACd;AACF;AAoQO,SAAS,aAAa,OAAiC;AAC5D,SAAO,SACL,OAAO,MAAM,YAAY,aACzB,OAAO,MAAM,aAAa,YAC1B,OAAO,MAAM,WAAW,YACxB,OAAO,MAAM,WAAW;AAC5B;AAEO,SAAS,UAAU,OAA8B;AACtD,SAAO,SACL,OAAO,MAAM,OAAO,YACpB,OAAO,MAAM,YAAY,YACzB,OAAO,MAAM,WAAW;AAC5B;AAEO,SAAS,gBAAgB,OAAuC;AACrE,SAAO,CAAC,YAAY,WAAW,aAAa,UAAU,UAAU,OAAO,EAAE,SAAS,KAAK;AACzF;","names":[]}
|
package/package.json
CHANGED
package/src/client.ts
CHANGED
|
@@ -481,6 +481,45 @@ export class HttpClient {
|
|
|
481
481
|
}
|
|
482
482
|
}
|
|
483
483
|
|
|
484
|
+
async readFileStream(
|
|
485
|
+
path: string,
|
|
486
|
+
sessionId: string
|
|
487
|
+
): Promise<ReadableStream<Uint8Array>> {
|
|
488
|
+
try {
|
|
489
|
+
const response = await this.doFetch(`/api/read/stream`, {
|
|
490
|
+
method: "POST",
|
|
491
|
+
headers: {
|
|
492
|
+
"Content-Type": "application/json",
|
|
493
|
+
},
|
|
494
|
+
body: JSON.stringify({
|
|
495
|
+
path,
|
|
496
|
+
sessionId,
|
|
497
|
+
} as ReadFileRequest),
|
|
498
|
+
});
|
|
499
|
+
|
|
500
|
+
if (!response.ok) {
|
|
501
|
+
const errorData = (await response.json().catch(() => ({}))) as {
|
|
502
|
+
error?: string;
|
|
503
|
+
};
|
|
504
|
+
throw new Error(
|
|
505
|
+
errorData.error || `HTTP error! status: ${response.status}`
|
|
506
|
+
);
|
|
507
|
+
}
|
|
508
|
+
|
|
509
|
+
if (!response.body) {
|
|
510
|
+
throw new Error("No response body for file streaming");
|
|
511
|
+
}
|
|
512
|
+
|
|
513
|
+
console.log(
|
|
514
|
+
`[HTTP Client] Started streaming file: ${path}${sessionId ? ` in session: ${sessionId}` : ''}`
|
|
515
|
+
);
|
|
516
|
+
return response.body;
|
|
517
|
+
} catch (error) {
|
|
518
|
+
console.error("[HTTP Client] Error streaming file:", error);
|
|
519
|
+
throw error;
|
|
520
|
+
}
|
|
521
|
+
}
|
|
522
|
+
|
|
484
523
|
async deleteFile(
|
|
485
524
|
path: string,
|
|
486
525
|
sessionId: string
|