@cloudflare/sandbox 0.0.0-d81d2a5 → 0.0.0-d86b60e

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/src/types.ts CHANGED
@@ -1,11 +1,6 @@
1
1
  // Core Types
2
2
 
3
3
  export interface BaseExecOptions {
4
- /**
5
- * Session ID for grouping related commands
6
- */
7
- sessionId?: string;
8
-
9
4
  /**
10
5
  * Maximum execution time in milliseconds
11
6
  */
@@ -90,11 +85,6 @@ export interface ExecResult {
90
85
  * ISO timestamp when command started
91
86
  */
92
87
  timestamp: string;
93
-
94
- /**
95
- * Session ID if provided
96
- */
97
- sessionId?: string;
98
88
  }
99
89
 
100
90
  // Background Process Types
@@ -177,11 +167,6 @@ export interface Process {
177
167
  */
178
168
  readonly exitCode?: number;
179
169
 
180
- /**
181
- * Session ID if provided
182
- */
183
- readonly sessionId?: string;
184
-
185
170
  /**
186
171
  * Kill the process
187
172
  */
@@ -208,7 +193,6 @@ export interface ExecEvent {
208
193
  exitCode?: number;
209
194
  result?: ExecResult;
210
195
  error?: string; // Changed to string for serialization
211
- sessionId?: string;
212
196
  }
213
197
 
214
198
  export interface LogEvent {
@@ -216,7 +200,6 @@ export interface LogEvent {
216
200
  timestamp: string;
217
201
  data: string;
218
202
  processId: string;
219
- sessionId?: string;
220
203
  exitCode?: number; // For 'exit' events
221
204
  }
222
205
 
@@ -272,10 +255,8 @@ export interface ProcessRecord {
272
255
  startTime: Date;
273
256
  endTime?: Date;
274
257
  exitCode?: number;
275
- sessionId?: string;
276
258
 
277
259
  // Internal fields
278
- childProcess?: any; // Node.js ChildProcess
279
260
  stdout: string; // Accumulated output (ephemeral)
280
261
  stderr: string; // Accumulated output (ephemeral)
281
262
 
@@ -290,7 +271,6 @@ export interface StartProcessRequest {
290
271
  command: string;
291
272
  options?: {
292
273
  processId?: string;
293
- sessionId?: string;
294
274
  timeout?: number;
295
275
  env?: Record<string, string>;
296
276
  cwd?: string;
@@ -304,9 +284,11 @@ export interface StartProcessResponse {
304
284
  id: string;
305
285
  pid?: number;
306
286
  command: string;
307
- status: ProcessStatus;
287
+ status: ProcessStatus;
308
288
  startTime: string;
309
- sessionId?: string;
289
+ endTime?: string | null;
290
+ exitCode?: number | null;
291
+ sessionId: string;
310
292
  };
311
293
  }
312
294
 
@@ -319,7 +301,6 @@ export interface ListProcessesResponse {
319
301
  startTime: string;
320
302
  endTime?: string;
321
303
  exitCode?: number;
322
- sessionId?: string;
323
304
  }>;
324
305
  }
325
306
 
@@ -332,7 +313,6 @@ export interface GetProcessResponse {
332
313
  startTime: string;
333
314
  endTime?: string;
334
315
  exitCode?: number;
335
- sessionId?: string;
336
316
  } | null;
337
317
  }
338
318
 
@@ -371,6 +351,25 @@ export interface ISandbox {
371
351
  cleanupCompletedProcesses(): Promise<number>;
372
352
  getProcessLogs(id: string): Promise<{ stdout: string; stderr: string }>;
373
353
 
354
+ // File operations
355
+ gitCheckout(repoUrl: string, options: { branch?: string; targetDir?: string }): Promise<GitCheckoutResponse>;
356
+ mkdir(path: string, options?: { recursive?: boolean }): Promise<MkdirResponse>;
357
+ writeFile(path: string, content: string, options?: { encoding?: string }): Promise<WriteFileResponse>;
358
+ deleteFile(path: string): Promise<DeleteFileResponse>;
359
+ renameFile(oldPath: string, newPath: string): Promise<RenameFileResponse>;
360
+ moveFile(sourcePath: string, destinationPath: string): Promise<MoveFileResponse>;
361
+ readFile(path: string, options?: { encoding?: string }): Promise<ReadFileResponse>;
362
+ listFiles(path: string, options?: { recursive?: boolean; includeHidden?: boolean }): Promise<ListFilesResponse>;
363
+
364
+ // Port management
365
+ exposePort(port: number, options: { name?: string; hostname: string }): Promise<{ url: string; port: number; name?: string }>;
366
+ unexposePort(port: number): Promise<void>;
367
+ getExposedPorts(hostname: string): Promise<Array<{ url: string; port: number; name?: string; exposedAt: string }>>;
368
+
369
+ // Environment management
370
+ setEnvVars(envVars: Record<string, string>): Promise<void>;
371
+ setSandboxName(name: string): Promise<void>;
372
+
374
373
  // Code Interpreter API
375
374
  createCodeContext(options?: CreateContextOptions): Promise<CodeContext>;
376
375
  runCode(code: string, options?: RunCodeOptions): Promise<ExecutionResult>;
@@ -379,6 +378,108 @@ export interface ISandbox {
379
378
  deleteCodeContext(contextId: string): Promise<void>;
380
379
  }
381
380
 
381
+ // Execution session returned by createSession()
382
+ // Sessions are full-featured sandbox objects with scoped execution context
383
+ // Inherits all ISandbox methods except createSession (sessions can't create sub-sessions),
384
+ // and setSandboxName (sessions inherit sandbox name).
385
+ export interface ExecutionSession extends Omit<ISandbox, 'createSession' | 'setSandboxName'> {
386
+ /**
387
+ * Session ID
388
+ */
389
+ id: string;
390
+ }
391
+
392
+ // API Response Types
393
+
394
+ export interface ExecuteResponse {
395
+ success: boolean;
396
+ stdout: string;
397
+ stderr: string;
398
+ exitCode: number;
399
+ command: string;
400
+ timestamp: string;
401
+ }
402
+
403
+ export interface GitCheckoutResponse {
404
+ success: boolean;
405
+ stdout: string;
406
+ stderr: string;
407
+ exitCode: number;
408
+ repoUrl: string;
409
+ branch: string;
410
+ targetDir: string;
411
+ timestamp: string;
412
+ }
413
+
414
+ export interface MkdirResponse {
415
+ success: boolean;
416
+ stdout: string;
417
+ stderr: string;
418
+ exitCode: number;
419
+ path: string;
420
+ recursive: boolean;
421
+ timestamp: string;
422
+ }
423
+
424
+ export interface WriteFileResponse {
425
+ success: boolean;
426
+ exitCode: number;
427
+ path: string;
428
+ timestamp: string;
429
+ }
430
+
431
+ export interface ReadFileResponse {
432
+ success: boolean;
433
+ exitCode: number;
434
+ path: string;
435
+ content: string;
436
+ timestamp: string;
437
+ }
438
+
439
+ export interface DeleteFileResponse {
440
+ success: boolean;
441
+ exitCode: number;
442
+ path: string;
443
+ timestamp: string;
444
+ }
445
+
446
+ export interface RenameFileResponse {
447
+ success: boolean;
448
+ exitCode: number;
449
+ oldPath: string;
450
+ newPath: string;
451
+ timestamp: string;
452
+ }
453
+
454
+ export interface MoveFileResponse {
455
+ success: boolean;
456
+ exitCode: number;
457
+ sourcePath: string;
458
+ destinationPath: string;
459
+ timestamp: string;
460
+ }
461
+
462
+ export interface ListFilesResponse {
463
+ success: boolean;
464
+ exitCode: number;
465
+ path: string;
466
+ files: Array<{
467
+ name: string;
468
+ absolutePath: string;
469
+ relativePath: string;
470
+ type: 'file' | 'directory' | 'symlink' | 'other';
471
+ size: number;
472
+ modifiedAt: string;
473
+ mode: string;
474
+ permissions: {
475
+ readable: boolean;
476
+ writable: boolean;
477
+ executable: boolean;
478
+ };
479
+ }>;
480
+ timestamp: string;
481
+ }
482
+
382
483
  // Type Guards
383
484
 
384
485
  export function isExecResult(value: any): value is ExecResult {
@@ -1,336 +0,0 @@
1
- import { type Kernel, KernelManager, ServerConnection } from "@jupyterlab/services";
2
- import type {
3
- IDisplayDataMsg,
4
- IErrorMsg,
5
- IExecuteResultMsg,
6
- IIOPubMessage,
7
- IStreamMsg
8
- } from "@jupyterlab/services/lib/kernel/messages";
9
- import {
10
- isDisplayDataMsg,
11
- isErrorMsg,
12
- isExecuteResultMsg,
13
- isStreamMsg
14
- } from "@jupyterlab/services/lib/kernel/messages";
15
- import { v4 as uuidv4 } from "uuid";
16
- import type { ExecutionResult } from "./mime-processor";
17
- import { processJupyterMessage } from "./mime-processor";
18
-
19
- export interface JupyterContext {
20
- id: string;
21
- language: string;
22
- connection: Kernel.IKernelConnection;
23
- cwd: string;
24
- createdAt: Date;
25
- lastUsed: Date;
26
- }
27
-
28
- export interface CreateContextRequest {
29
- language?: string;
30
- cwd?: string;
31
- envVars?: Record<string, string>;
32
- }
33
-
34
- export interface ExecuteCodeRequest {
35
- context_id?: string;
36
- code: string;
37
- language?: string;
38
- env_vars?: Record<string, string>;
39
- }
40
-
41
- export class JupyterServer {
42
- private kernelManager: KernelManager;
43
- private contexts = new Map<string, JupyterContext>();
44
- private defaultContexts = new Map<string, string>(); // language -> context_id
45
-
46
- constructor() {
47
- // Configure connection to local Jupyter server
48
- const serverSettings = ServerConnection.makeSettings({
49
- baseUrl: "http://localhost:8888",
50
- token: "",
51
- appUrl: "",
52
- wsUrl: "ws://localhost:8888",
53
- appendToken: false,
54
- init: {
55
- headers: {
56
- 'Content-Type': 'application/json'
57
- }
58
- }
59
- });
60
-
61
- this.kernelManager = new KernelManager({ serverSettings });
62
- }
63
-
64
- async initialize() {
65
- await this.kernelManager.ready;
66
- console.log("[JupyterServer] Kernel manager initialized");
67
-
68
- // Create default Python context
69
- const pythonContext = await this.createContext({ language: "python" });
70
- this.defaultContexts.set("python", pythonContext.id);
71
- console.log(
72
- "[JupyterServer] Default Python context created:",
73
- pythonContext.id
74
- );
75
- }
76
-
77
- async createContext(req: CreateContextRequest): Promise<JupyterContext> {
78
- const language = req.language || "python";
79
- const cwd = req.cwd || "/workspace";
80
-
81
- const kernelModel = await this.kernelManager.startNew({
82
- name: this.getKernelName(language),
83
- });
84
-
85
- const connection = this.kernelManager.connectTo({ model: kernelModel });
86
-
87
- const context: JupyterContext = {
88
- id: uuidv4(),
89
- language,
90
- connection,
91
- cwd,
92
- createdAt: new Date(),
93
- lastUsed: new Date(),
94
- };
95
-
96
- this.contexts.set(context.id, context);
97
-
98
- // Set working directory
99
- if (cwd !== "/workspace") {
100
- await this.changeWorkingDirectory(context, cwd);
101
- }
102
-
103
- // Set environment variables if provided
104
- if (req.envVars) {
105
- await this.setEnvironmentVariables(context, req.envVars);
106
- }
107
-
108
- return context;
109
- }
110
-
111
- async executeCode(
112
- contextId: string | undefined,
113
- code: string,
114
- language?: string
115
- ): Promise<Response> {
116
- let context: JupyterContext | undefined;
117
-
118
- if (contextId) {
119
- context = this.contexts.get(contextId);
120
- if (!context) {
121
- return new Response(
122
- JSON.stringify({ error: `Context ${contextId} not found` }),
123
- {
124
- status: 404,
125
- headers: { "Content-Type": "application/json" },
126
- }
127
- );
128
- }
129
- } else if (language) {
130
- // Use default context for the language
131
- const defaultContextId = this.defaultContexts.get(language);
132
- if (defaultContextId) {
133
- context = this.contexts.get(defaultContextId);
134
- }
135
-
136
- // Create new default context if needed
137
- if (!context) {
138
- context = await this.createContext({ language });
139
- this.defaultContexts.set(language, context.id);
140
- }
141
- } else {
142
- // Use default Python context
143
- const pythonContextId = this.defaultContexts.get("python");
144
- context = pythonContextId
145
- ? this.contexts.get(pythonContextId)
146
- : undefined;
147
- }
148
-
149
- if (!context) {
150
- return new Response(JSON.stringify({ error: "No context available" }), {
151
- status: 400,
152
- headers: { "Content-Type": "application/json" },
153
- });
154
- }
155
-
156
- // Update last used
157
- context.lastUsed = new Date();
158
-
159
- // Execute with streaming
160
- return this.streamExecution(context.connection, code);
161
- }
162
-
163
- private async streamExecution(
164
- connection: Kernel.IKernelConnection,
165
- code: string
166
- ): Promise<Response> {
167
- const stream = new ReadableStream({
168
- async start(controller) {
169
- const future = connection.requestExecute({
170
- code,
171
- stop_on_error: false,
172
- store_history: true,
173
- silent: false,
174
- allow_stdin: false,
175
- });
176
-
177
- // Handle different message types
178
- future.onIOPub = (msg: IIOPubMessage) => {
179
- const result = processJupyterMessage(msg);
180
- if (result) {
181
- controller.enqueue(
182
- new TextEncoder().encode(`${JSON.stringify(result)}\n`)
183
- );
184
- }
185
- };
186
-
187
- future.onReply = (msg: any) => {
188
- if (msg.content.status === "ok") {
189
- controller.enqueue(
190
- new TextEncoder().encode(
191
- `${JSON.stringify({
192
- type: "execution_complete",
193
- execution_count: msg.content.execution_count,
194
- })}\n`
195
- )
196
- );
197
- } else if (msg.content.status === "error") {
198
- controller.enqueue(
199
- new TextEncoder().encode(
200
- `${JSON.stringify({
201
- type: "error",
202
- ename: msg.content.ename,
203
- evalue: msg.content.evalue,
204
- traceback: msg.content.traceback,
205
- })}\n`
206
- )
207
- );
208
- }
209
- controller.close();
210
- };
211
-
212
- future.onStdin = (msg: any) => {
213
- // We don't support stdin for now
214
- console.warn("[JupyterServer] Stdin requested but not supported");
215
- };
216
- },
217
- });
218
-
219
- return new Response(stream, {
220
- headers: {
221
- "Content-Type": "text/event-stream",
222
- "Cache-Control": "no-cache",
223
- Connection: "keep-alive",
224
- },
225
- });
226
- }
227
-
228
- private getKernelName(language: string): string {
229
- const kernelMap: Record<string, string> = {
230
- python: "python3",
231
- javascript: "javascript",
232
- typescript: "javascript",
233
- js: "javascript",
234
- ts: "javascript",
235
- };
236
- return kernelMap[language.toLowerCase()] || "python3";
237
- }
238
-
239
- private async changeWorkingDirectory(context: JupyterContext, cwd: string) {
240
- const code =
241
- context.language === "python"
242
- ? `import os; os.chdir('${cwd}')`
243
- : `process.chdir('${cwd}')`;
244
-
245
- const future = context.connection.requestExecute({
246
- code,
247
- silent: true,
248
- store_history: false,
249
- });
250
-
251
- return future.done;
252
- }
253
-
254
- private async setEnvironmentVariables(
255
- context: JupyterContext,
256
- envVars: Record<string, string>
257
- ) {
258
- const commands: string[] = [];
259
-
260
- for (const [key, value] of Object.entries(envVars)) {
261
- if (context.language === "python") {
262
- commands.push(`import os; os.environ['${key}'] = '${value}'`);
263
- } else if (
264
- context.language === "javascript" ||
265
- context.language === "typescript"
266
- ) {
267
- commands.push(`process.env['${key}'] = '${value}'`);
268
- }
269
- }
270
-
271
- if (commands.length > 0) {
272
- const code = commands.join("\n");
273
- const future = context.connection.requestExecute({
274
- code,
275
- silent: true,
276
- store_history: false,
277
- });
278
-
279
- return future.done;
280
- }
281
- }
282
-
283
- async listContexts(): Promise<
284
- Array<{
285
- id: string;
286
- language: string;
287
- cwd: string;
288
- createdAt: Date;
289
- lastUsed: Date;
290
- }>
291
- > {
292
- return Array.from(this.contexts.values()).map((ctx) => ({
293
- id: ctx.id,
294
- language: ctx.language,
295
- cwd: ctx.cwd,
296
- createdAt: ctx.createdAt,
297
- lastUsed: ctx.lastUsed,
298
- }));
299
- }
300
-
301
- async deleteContext(contextId: string): Promise<void> {
302
- const context = this.contexts.get(contextId);
303
- if (!context) {
304
- throw new Error(`Context ${contextId} not found`);
305
- }
306
-
307
- // Shutdown the kernel
308
- await context.connection.shutdown();
309
-
310
- // Remove from maps
311
- this.contexts.delete(contextId);
312
-
313
- // Remove from default contexts if it was a default
314
- for (const [lang, id] of this.defaultContexts.entries()) {
315
- if (id === contextId) {
316
- this.defaultContexts.delete(lang);
317
- break;
318
- }
319
- }
320
- }
321
-
322
- async shutdown() {
323
- // Shutdown all kernels
324
- for (const context of this.contexts.values()) {
325
- try {
326
- await context.connection.shutdown();
327
- } catch (error) {
328
- console.error("[JupyterServer] Error shutting down kernel:", error);
329
- }
330
- }
331
-
332
- this.contexts.clear();
333
- this.defaultContexts.clear();
334
- }
335
- }
336
-