@cloudflare/sandbox 0.2.3 → 0.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (44) hide show
  1. package/CHANGELOG.md +69 -0
  2. package/Dockerfile +9 -11
  3. package/README.md +69 -7
  4. package/container_src/control-process.ts +784 -0
  5. package/container_src/handler/exec.ts +99 -254
  6. package/container_src/handler/file.ts +204 -642
  7. package/container_src/handler/git.ts +28 -80
  8. package/container_src/handler/process.ts +443 -515
  9. package/container_src/handler/session.ts +92 -0
  10. package/container_src/index.ts +74 -129
  11. package/container_src/isolation.ts +1039 -0
  12. package/container_src/jupyter-service.ts +8 -5
  13. package/container_src/shell-escape.ts +42 -0
  14. package/container_src/types.ts +35 -12
  15. package/dist/{chunk-VTKZL632.js → chunk-BEQUGUY4.js} +2 -2
  16. package/dist/{chunk-4KELYYKS.js → chunk-GTGWAEED.js} +239 -265
  17. package/dist/chunk-GTGWAEED.js.map +1 -0
  18. package/dist/{chunk-CUHYLCMT.js → chunk-SMUEY5JR.js} +111 -99
  19. package/dist/chunk-SMUEY5JR.js.map +1 -0
  20. package/dist/{client-bzEV222a.d.ts → client-Dny_ro_v.d.ts} +48 -84
  21. package/dist/client.d.ts +1 -1
  22. package/dist/client.js +1 -1
  23. package/dist/index.d.ts +2 -2
  24. package/dist/index.js +8 -9
  25. package/dist/interpreter.d.ts +2 -2
  26. package/dist/jupyter-client.d.ts +2 -2
  27. package/dist/jupyter-client.js +2 -2
  28. package/dist/request-handler.d.ts +3 -3
  29. package/dist/request-handler.js +3 -5
  30. package/dist/sandbox.d.ts +2 -2
  31. package/dist/sandbox.js +3 -5
  32. package/dist/types.d.ts +127 -21
  33. package/dist/types.js +35 -9
  34. package/dist/types.js.map +1 -1
  35. package/package.json +1 -1
  36. package/src/client.ts +175 -187
  37. package/src/index.ts +23 -13
  38. package/src/sandbox.ts +297 -332
  39. package/src/types.ts +125 -24
  40. package/dist/chunk-4KELYYKS.js.map +0 -1
  41. package/dist/chunk-CUHYLCMT.js.map +0 -1
  42. package/dist/chunk-S5FFBU4Y.js +0 -46
  43. package/dist/chunk-S5FFBU4Y.js.map +0 -1
  44. /package/dist/{chunk-VTKZL632.js.map → chunk-BEQUGUY4.js.map} +0 -0
package/src/sandbox.ts CHANGED
@@ -16,8 +16,11 @@ import {
16
16
  } from "./security";
17
17
  import { parseSSEStream } from "./sse-parser";
18
18
  import type {
19
+ ExecEvent,
19
20
  ExecOptions,
20
21
  ExecResult,
22
+ ExecuteResponse,
23
+ ExecutionSession,
21
24
  ISandbox,
22
25
  Process,
23
26
  ProcessOptions,
@@ -41,6 +44,7 @@ export class Sandbox<Env = unknown> extends Container<Env> implements ISandbox {
41
44
  client: JupyterClient;
42
45
  private sandboxName: string | null = null;
43
46
  private codeInterpreter: CodeInterpreter;
47
+ private defaultSession: ExecutionSession | null = null;
44
48
 
45
49
  constructor(ctx: DurableObjectState, env: Env) {
46
50
  super(ctx, env);
@@ -86,6 +90,11 @@ export class Sandbox<Env = unknown> extends Container<Env> implements ISandbox {
86
90
  async setEnvVars(envVars: Record<string, string>): Promise<void> {
87
91
  this.envVars = { ...this.envVars, ...envVars };
88
92
  console.log(`[Sandbox] Updated environment variables`);
93
+
94
+ // If we have a default session, update its environment too
95
+ if (this.defaultSession) {
96
+ await this.defaultSession.setEnvVars(envVars);
97
+ }
89
98
  }
90
99
 
91
100
  override onStart() {
@@ -94,9 +103,6 @@ export class Sandbox<Env = unknown> extends Container<Env> implements ISandbox {
94
103
 
95
104
  override onStop() {
96
105
  console.log("Sandbox successfully shut down");
97
- if (this.client) {
98
- this.client.clearSession();
99
- }
100
106
  }
101
107
 
102
108
  override onError(error: unknown) {
@@ -134,380 +140,95 @@ export class Sandbox<Env = unknown> extends Container<Env> implements ISandbox {
134
140
  return 3000;
135
141
  }
136
142
 
137
- // Enhanced exec method - always returns ExecResult with optional streaming
138
- // This replaces the old exec method to match ISandbox interface
139
- async exec(command: string, options?: ExecOptions): Promise<ExecResult> {
140
- const startTime = Date.now();
141
- const timestamp = new Date().toISOString();
142
-
143
- // Handle timeout
144
- let timeoutId: NodeJS.Timeout | undefined;
145
-
146
- try {
147
- // Handle cancellation
148
- if (options?.signal?.aborted) {
149
- throw new Error("Operation was aborted");
150
- }
151
-
152
- let result: ExecResult;
153
-
154
- if (options?.stream && options?.onOutput) {
155
- // Streaming with callbacks - we need to collect the final result
156
- result = await this.executeWithStreaming(
157
- command,
158
- options,
159
- startTime,
160
- timestamp
161
- );
162
- } else {
163
- // Regular execution
164
- const response = await this.client.execute(command, {
165
- sessionId: options?.sessionId,
166
- cwd: options?.cwd,
167
- env: options?.env,
168
- });
169
-
170
- const duration = Date.now() - startTime;
171
- result = this.mapExecuteResponseToExecResult(
172
- response,
173
- duration,
174
- options?.sessionId
175
- );
176
- }
177
-
178
- // Call completion callback if provided
179
- if (options?.onComplete) {
180
- options.onComplete(result);
181
- }
182
-
183
- return result;
184
- } catch (error) {
185
- if (options?.onError && error instanceof Error) {
186
- options.onError(error);
187
- }
188
- throw error;
189
- } finally {
190
- if (timeoutId) {
191
- clearTimeout(timeoutId);
192
- }
143
+ // Helper to ensure default session is initialized
144
+ private async ensureDefaultSession(): Promise<ExecutionSession> {
145
+ if (!this.defaultSession) {
146
+ const sessionId = `sandbox-${this.sandboxName || 'default'}`;
147
+ this.defaultSession = await this.createSession({
148
+ id: sessionId,
149
+ env: this.envVars || {},
150
+ cwd: '/workspace',
151
+ isolation: true
152
+ });
153
+ console.log(`[Sandbox] Default session initialized: ${sessionId}`);
193
154
  }
155
+ return this.defaultSession;
194
156
  }
195
157
 
196
- private async executeWithStreaming(
197
- command: string,
198
- options: ExecOptions,
199
- startTime: number,
200
- timestamp: string
201
- ): Promise<ExecResult> {
202
- let stdout = "";
203
- let stderr = "";
204
-
205
- try {
206
- const stream = await this.client.executeCommandStream(
207
- command,
208
- options.sessionId
209
- );
210
-
211
- for await (const event of parseSSEStream<import("./types").ExecEvent>(
212
- stream
213
- )) {
214
- // Check for cancellation
215
- if (options.signal?.aborted) {
216
- throw new Error("Operation was aborted");
217
- }
218
-
219
- switch (event.type) {
220
- case "stdout":
221
- case "stderr":
222
- if (event.data) {
223
- // Update accumulated output
224
- if (event.type === "stdout") stdout += event.data;
225
- if (event.type === "stderr") stderr += event.data;
226
-
227
- // Call user's callback
228
- if (options.onOutput) {
229
- options.onOutput(event.type, event.data);
230
- }
231
- }
232
- break;
233
-
234
- case "complete": {
235
- // Use result from complete event if available
236
- const duration = Date.now() - startTime;
237
- return (
238
- event.result || {
239
- success: event.exitCode === 0,
240
- exitCode: event.exitCode || 0,
241
- stdout,
242
- stderr,
243
- command,
244
- duration,
245
- timestamp,
246
- sessionId: options.sessionId,
247
- }
248
- );
249
- }
250
-
251
- case "error":
252
- throw new Error(event.error || "Command execution failed");
253
- }
254
- }
255
-
256
- // If we get here without a complete event, something went wrong
257
- throw new Error("Stream ended without completion event");
258
- } catch (error) {
259
- if (options.signal?.aborted) {
260
- throw new Error("Operation was aborted");
261
- }
262
- throw error;
263
- }
264
- }
265
158
 
266
- private mapExecuteResponseToExecResult(
267
- response: import("./client").ExecuteResponse,
268
- duration: number,
269
- sessionId?: string
270
- ): ExecResult {
271
- return {
272
- success: response.success,
273
- exitCode: response.exitCode,
274
- stdout: response.stdout,
275
- stderr: response.stderr,
276
- command: response.command,
277
- duration,
278
- timestamp: response.timestamp,
279
- sessionId,
280
- };
159
+ async exec(command: string, options?: ExecOptions): Promise<ExecResult> {
160
+ const session = await this.ensureDefaultSession();
161
+ return session.exec(command, options);
281
162
  }
282
163
 
283
- // Background process management
284
164
  async startProcess(
285
165
  command: string,
286
166
  options?: ProcessOptions
287
167
  ): Promise<Process> {
288
- // Use the new HttpClient method to start the process
289
- try {
290
- const response = await this.client.startProcess(command, {
291
- processId: options?.processId,
292
- sessionId: options?.sessionId,
293
- timeout: options?.timeout,
294
- env: options?.env,
295
- cwd: options?.cwd,
296
- encoding: options?.encoding,
297
- autoCleanup: options?.autoCleanup,
298
- });
299
-
300
- const process = response.process;
301
- const processObj: Process = {
302
- id: process.id,
303
- pid: process.pid,
304
- command: process.command,
305
- status: process.status as ProcessStatus,
306
- startTime: new Date(process.startTime),
307
- endTime: undefined,
308
- exitCode: undefined,
309
- sessionId: process.sessionId,
310
-
311
- async kill(): Promise<void> {
312
- throw new Error("Method will be replaced");
313
- },
314
- async getStatus(): Promise<ProcessStatus> {
315
- throw new Error("Method will be replaced");
316
- },
317
- async getLogs(): Promise<{ stdout: string; stderr: string }> {
318
- throw new Error("Method will be replaced");
319
- },
320
- };
321
-
322
- // Bind context properly
323
- processObj.kill = async (signal?: string) => {
324
- await this.killProcess(process.id, signal);
325
- };
326
-
327
- processObj.getStatus = async () => {
328
- const current = await this.getProcess(process.id);
329
- return current?.status || "error";
330
- };
331
-
332
- processObj.getLogs = async () => {
333
- const logs = await this.getProcessLogs(process.id);
334
- return { stdout: logs.stdout, stderr: logs.stderr };
335
- };
336
-
337
- // Call onStart callback if provided
338
- if (options?.onStart) {
339
- options.onStart(processObj);
340
- }
341
-
342
- return processObj;
343
- } catch (error) {
344
- if (options?.onError && error instanceof Error) {
345
- options.onError(error);
346
- }
347
-
348
- throw error;
349
- }
168
+ const session = await this.ensureDefaultSession();
169
+ return session.startProcess(command, options);
350
170
  }
351
171
 
352
172
  async listProcesses(): Promise<Process[]> {
353
- const response = await this.client.listProcesses();
354
-
355
- return response.processes.map((processData) => ({
356
- id: processData.id,
357
- pid: processData.pid,
358
- command: processData.command,
359
- status: processData.status,
360
- startTime: new Date(processData.startTime),
361
- endTime: processData.endTime ? new Date(processData.endTime) : undefined,
362
- exitCode: processData.exitCode,
363
- sessionId: processData.sessionId,
364
-
365
- kill: async (signal?: string) => {
366
- await this.killProcess(processData.id, signal);
367
- },
368
-
369
- getStatus: async () => {
370
- const current = await this.getProcess(processData.id);
371
- return current?.status || "error";
372
- },
373
-
374
- getLogs: async () => {
375
- const logs = await this.getProcessLogs(processData.id);
376
- return { stdout: logs.stdout, stderr: logs.stderr };
377
- },
378
- }));
173
+ const session = await this.ensureDefaultSession();
174
+ return session.listProcesses();
379
175
  }
380
176
 
381
177
  async getProcess(id: string): Promise<Process | null> {
382
- const response = await this.client.getProcess(id);
383
- if (!response.process) {
384
- return null;
385
- }
386
-
387
- const processData = response.process;
388
- return {
389
- id: processData.id,
390
- pid: processData.pid,
391
- command: processData.command,
392
- status: processData.status,
393
- startTime: new Date(processData.startTime),
394
- endTime: processData.endTime ? new Date(processData.endTime) : undefined,
395
- exitCode: processData.exitCode,
396
- sessionId: processData.sessionId,
397
-
398
- kill: async (signal?: string) => {
399
- await this.killProcess(processData.id, signal);
400
- },
401
-
402
- getStatus: async () => {
403
- const current = await this.getProcess(processData.id);
404
- return current?.status || "error";
405
- },
406
-
407
- getLogs: async () => {
408
- const logs = await this.getProcessLogs(processData.id);
409
- return { stdout: logs.stdout, stderr: logs.stderr };
410
- },
411
- };
178
+ const session = await this.ensureDefaultSession();
179
+ return session.getProcess(id);
412
180
  }
413
181
 
414
- async killProcess(id: string, _signal?: string): Promise<void> {
415
- try {
416
- // Note: signal parameter is not currently supported by the HttpClient implementation
417
- await this.client.killProcess(id);
418
- } catch (error) {
419
- if (
420
- error instanceof Error &&
421
- error.message.includes("Process not found")
422
- ) {
423
- throw new ProcessNotFoundError(id);
424
- }
425
- throw new SandboxError(
426
- `Failed to kill process ${id}: ${
427
- error instanceof Error ? error.message : "Unknown error"
428
- }`,
429
- "KILL_PROCESS_FAILED"
430
- );
431
- }
182
+ async killProcess(id: string, signal?: string): Promise<void> {
183
+ const session = await this.ensureDefaultSession();
184
+ return session.killProcess(id, signal);
432
185
  }
433
186
 
434
187
  async killAllProcesses(): Promise<number> {
435
- const response = await this.client.killAllProcesses();
436
- return response.killedCount;
188
+ const session = await this.ensureDefaultSession();
189
+ return session.killAllProcesses();
437
190
  }
438
191
 
439
192
  async cleanupCompletedProcesses(): Promise<number> {
440
- // For now, this would need to be implemented as a container endpoint
441
- // as we no longer maintain local process storage
442
- // We'll return 0 as a placeholder until the container endpoint is added
443
- return 0;
193
+ const session = await this.ensureDefaultSession();
194
+ return session.cleanupCompletedProcesses();
444
195
  }
445
196
 
446
197
  async getProcessLogs(
447
198
  id: string
448
199
  ): Promise<{ stdout: string; stderr: string }> {
449
- try {
450
- const response = await this.client.getProcessLogs(id);
451
- return {
452
- stdout: response.stdout,
453
- stderr: response.stderr,
454
- };
455
- } catch (error) {
456
- if (
457
- error instanceof Error &&
458
- error.message.includes("Process not found")
459
- ) {
460
- throw new ProcessNotFoundError(id);
461
- }
462
- throw error;
463
- }
200
+ const session = await this.ensureDefaultSession();
201
+ return session.getProcessLogs(id);
464
202
  }
465
203
 
466
- // Streaming methods - return ReadableStream for RPC compatibility
204
+ // Streaming methods - delegates to default session
467
205
  async execStream(
468
206
  command: string,
469
207
  options?: StreamOptions
470
208
  ): Promise<ReadableStream<Uint8Array>> {
471
- // Check for cancellation
472
- if (options?.signal?.aborted) {
473
- throw new Error("Operation was aborted");
474
- }
475
-
476
- // Get the stream from HttpClient (need to add this method)
477
- const stream = await this.client.executeCommandStream(
478
- command,
479
- options?.sessionId
480
- );
481
-
482
- // Return the ReadableStream directly - can be converted to AsyncIterable by consumers
483
- return stream;
209
+ const session = await this.ensureDefaultSession();
210
+ return session.execStream(command, options);
484
211
  }
485
212
 
486
213
  async streamProcessLogs(
487
214
  processId: string,
488
215
  options?: { signal?: AbortSignal }
489
216
  ): Promise<ReadableStream<Uint8Array>> {
490
- // Check for cancellation
491
- if (options?.signal?.aborted) {
492
- throw new Error("Operation was aborted");
493
- }
494
-
495
- // Get the stream from HttpClient
496
- const stream = await this.client.streamProcessLogs(processId);
497
-
498
- // Return the ReadableStream directly - can be converted to AsyncIterable by consumers
499
- return stream;
217
+ const session = await this.ensureDefaultSession();
218
+ return session.streamProcessLogs(processId, options);
500
219
  }
501
220
 
502
221
  async gitCheckout(
503
222
  repoUrl: string,
504
223
  options: { branch?: string; targetDir?: string }
505
224
  ) {
506
- return this.client.gitCheckout(repoUrl, options.branch, options.targetDir);
225
+ const session = await this.ensureDefaultSession();
226
+ return session.gitCheckout(repoUrl, options);
507
227
  }
508
228
 
509
229
  async mkdir(path: string, options: { recursive?: boolean } = {}) {
510
- return this.client.mkdir(path, options.recursive);
230
+ const session = await this.ensureDefaultSession();
231
+ return session.mkdir(path, options);
511
232
  }
512
233
 
513
234
  async writeFile(
@@ -515,23 +236,39 @@ export class Sandbox<Env = unknown> extends Container<Env> implements ISandbox {
515
236
  content: string,
516
237
  options: { encoding?: string } = {}
517
238
  ) {
518
- return this.client.writeFile(path, content, options.encoding);
239
+ const session = await this.ensureDefaultSession();
240
+ return session.writeFile(path, content, options);
519
241
  }
520
242
 
521
243
  async deleteFile(path: string) {
522
- return this.client.deleteFile(path);
244
+ const session = await this.ensureDefaultSession();
245
+ return session.deleteFile(path);
523
246
  }
524
247
 
525
248
  async renameFile(oldPath: string, newPath: string) {
526
- return this.client.renameFile(oldPath, newPath);
249
+ const session = await this.ensureDefaultSession();
250
+ return session.renameFile(oldPath, newPath);
527
251
  }
528
252
 
529
253
  async moveFile(sourcePath: string, destinationPath: string) {
530
- return this.client.moveFile(sourcePath, destinationPath);
254
+ const session = await this.ensureDefaultSession();
255
+ return session.moveFile(sourcePath, destinationPath);
531
256
  }
532
257
 
533
258
  async readFile(path: string, options: { encoding?: string } = {}) {
534
- return this.client.readFile(path, options.encoding);
259
+ const session = await this.ensureDefaultSession();
260
+ return session.readFile(path, options);
261
+ }
262
+
263
+ async listFiles(
264
+ path: string,
265
+ options: {
266
+ recursive?: boolean;
267
+ includeHidden?: boolean;
268
+ } = {}
269
+ ) {
270
+ const session = await this.ensureDefaultSession();
271
+ return session.listFiles(path, options);
535
272
  }
536
273
 
537
274
  async exposePort(port: number, options: { name?: string; hostname: string }) {
@@ -775,4 +512,232 @@ export class Sandbox<Env = unknown> extends Container<Env> implements ISandbox {
775
512
  async deleteCodeContext(contextId: string): Promise<void> {
776
513
  return this.codeInterpreter.deleteCodeContext(contextId);
777
514
  }
515
+
516
+ // ============================================================================
517
+ // Session Management (Simple Isolation)
518
+ // ============================================================================
519
+
520
+ /**
521
+ * Create a new execution session with isolation
522
+ * Returns a session object with exec() method
523
+ */
524
+
525
+ async createSession(options: {
526
+ id?: string;
527
+ env?: Record<string, string>;
528
+ cwd?: string;
529
+ isolation?: boolean;
530
+ }): Promise<ExecutionSession> {
531
+ const sessionId = options.id || `session-${Date.now()}`;
532
+
533
+ await this.client.createSession({
534
+ id: sessionId,
535
+ env: options.env,
536
+ cwd: options.cwd,
537
+ isolation: options.isolation
538
+ });
539
+ // Return comprehensive ExecutionSession object that implements all ISandbox methods
540
+ return {
541
+ id: sessionId,
542
+
543
+ // Command execution - clean method names
544
+ exec: async (command: string, options?: ExecOptions) => {
545
+ const result = await this.client.exec(sessionId, command);
546
+ return {
547
+ ...result,
548
+ command,
549
+ duration: 0,
550
+ timestamp: new Date().toISOString()
551
+ };
552
+ },
553
+
554
+ execStream: async (command: string, options?: StreamOptions) => {
555
+ return await this.client.execStream(sessionId, command);
556
+ },
557
+
558
+ // Process management - route to session-aware methods
559
+ startProcess: async (command: string, options?: ProcessOptions) => {
560
+ // Use session-specific process management
561
+ const response = await this.client.startProcess(command, sessionId, {
562
+ processId: options?.processId,
563
+ timeout: options?.timeout,
564
+ env: options?.env,
565
+ cwd: options?.cwd,
566
+ encoding: options?.encoding,
567
+ autoCleanup: options?.autoCleanup,
568
+ });
569
+
570
+ // Convert response to Process object with bound methods
571
+ const process = response.process;
572
+ return {
573
+ id: process.id,
574
+ pid: process.pid,
575
+ command: process.command,
576
+ status: process.status as ProcessStatus,
577
+ startTime: new Date(process.startTime),
578
+ endTime: process.endTime ? new Date(process.endTime) : undefined,
579
+ exitCode: process.exitCode ?? undefined,
580
+ kill: async (signal?: string) => {
581
+ await this.client.killProcess(process.id);
582
+ },
583
+ getStatus: async () => {
584
+ const resp = await this.client.getProcess(process.id);
585
+ return resp.process?.status as ProcessStatus || "error";
586
+ },
587
+ getLogs: async () => {
588
+ return await this.client.getProcessLogs(process.id);
589
+ },
590
+ };
591
+ },
592
+
593
+ listProcesses: async () => {
594
+ // Get processes for this specific session
595
+ const response = await this.client.listProcesses(sessionId);
596
+
597
+ // Convert to Process objects with bound methods
598
+ return response.processes.map(p => ({
599
+ id: p.id,
600
+ pid: p.pid,
601
+ command: p.command,
602
+ status: p.status as ProcessStatus,
603
+ startTime: new Date(p.startTime),
604
+ endTime: p.endTime ? new Date(p.endTime) : undefined,
605
+ exitCode: p.exitCode ?? undefined,
606
+ kill: async (signal?: string) => {
607
+ await this.client.killProcess(p.id);
608
+ },
609
+ getStatus: async () => {
610
+ const processResp = await this.client.getProcess(p.id);
611
+ return processResp.process?.status as ProcessStatus || "error";
612
+ },
613
+ getLogs: async () => {
614
+ return this.client.getProcessLogs(p.id);
615
+ },
616
+ }));
617
+ },
618
+
619
+ getProcess: async (id: string) => {
620
+ const response = await this.client.getProcess(id);
621
+ if (!response.process) return null;
622
+
623
+ const p = response.process;
624
+ return {
625
+ id: p.id,
626
+ pid: p.pid,
627
+ command: p.command,
628
+ status: p.status as ProcessStatus,
629
+ startTime: new Date(p.startTime),
630
+ endTime: p.endTime ? new Date(p.endTime) : undefined,
631
+ exitCode: p.exitCode ?? undefined,
632
+ kill: async (signal?: string) => {
633
+ await this.client.killProcess(p.id);
634
+ },
635
+ getStatus: async () => {
636
+ const processResp = await this.client.getProcess(p.id);
637
+ return processResp.process?.status as ProcessStatus || "error";
638
+ },
639
+ getLogs: async () => {
640
+ return this.client.getProcessLogs(p.id);
641
+ },
642
+ };
643
+ },
644
+
645
+ killProcess: async (id: string, signal?: string) => {
646
+ await this.client.killProcess(id);
647
+ },
648
+
649
+ killAllProcesses: async () => {
650
+ // Kill all processes for this specific session
651
+ const response = await this.client.killAllProcesses(sessionId);
652
+ return response.killedCount;
653
+ },
654
+
655
+ streamProcessLogs: async (processId: string, options?: { signal?: AbortSignal }) => {
656
+ return await this.client.streamProcessLogs(processId, options);
657
+ },
658
+
659
+ getProcessLogs: async (id: string) => {
660
+ return await this.client.getProcessLogs(id);
661
+ },
662
+
663
+ cleanupCompletedProcesses: async () => {
664
+ // This would need a new endpoint to cleanup processes for a specific session
665
+ // For now, return 0 as no cleanup is performed
666
+ return 0;
667
+ },
668
+
669
+ // File operations - clean method names (no "InSession" suffix)
670
+ writeFile: async (path: string, content: string, options?: { encoding?: string }) => {
671
+ return await this.client.writeFile(path, content, options?.encoding, sessionId);
672
+ },
673
+
674
+ readFile: async (path: string, options?: { encoding?: string }) => {
675
+ return await this.client.readFile(path, options?.encoding, sessionId);
676
+ },
677
+
678
+ mkdir: async (path: string, options?: { recursive?: boolean }) => {
679
+ return await this.client.mkdir(path, options?.recursive, sessionId);
680
+ },
681
+
682
+ deleteFile: async (path: string) => {
683
+ return await this.client.deleteFile(path, sessionId);
684
+ },
685
+
686
+ renameFile: async (oldPath: string, newPath: string) => {
687
+ return await this.client.renameFile(oldPath, newPath, sessionId);
688
+ },
689
+
690
+ moveFile: async (sourcePath: string, destinationPath: string) => {
691
+ return await this.client.moveFile(sourcePath, destinationPath, sessionId);
692
+ },
693
+
694
+ listFiles: async (path: string, options?: { recursive?: boolean; includeHidden?: boolean }) => {
695
+ return await this.client.listFiles(path, sessionId, options);
696
+ },
697
+
698
+ gitCheckout: async (repoUrl: string, options?: { branch?: string; targetDir?: string }) => {
699
+ return await this.client.gitCheckout(repoUrl, sessionId, options?.branch, options?.targetDir);
700
+ },
701
+
702
+ // Port management
703
+ exposePort: async (port: number, options: { name?: string; hostname: string }) => {
704
+ return await this.exposePort(port, options);
705
+ },
706
+
707
+ unexposePort: async (port: number) => {
708
+ return await this.unexposePort(port);
709
+ },
710
+
711
+ getExposedPorts: async (hostname: string) => {
712
+ return await this.getExposedPorts(hostname);
713
+ },
714
+
715
+ // Environment management
716
+ setEnvVars: async (envVars: Record<string, string>) => {
717
+ // TODO: Implement session-specific environment updates
718
+ console.log(`[Session ${sessionId}] Environment variables update not yet implemented`);
719
+ },
720
+
721
+ // Code Interpreter API
722
+ createCodeContext: async (options?: any) => {
723
+ return await this.createCodeContext(options);
724
+ },
725
+
726
+ runCode: async (code: string, options?: any) => {
727
+ return await this.runCode(code, options);
728
+ },
729
+
730
+ runCodeStream: async (code: string, options?: any) => {
731
+ return await this.runCodeStream(code, options);
732
+ },
733
+
734
+ listCodeContexts: async () => {
735
+ return await this.listCodeContexts();
736
+ },
737
+
738
+ deleteCodeContext: async (contextId: string) => {
739
+ return await this.deleteCodeContext(contextId);
740
+ }
741
+ };
742
+ }
778
743
  }