@cloudflare/sandbox 0.2.4 → 0.3.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (43) hide show
  1. package/CHANGELOG.md +75 -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 +179 -837
  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 +68 -130
  11. package/container_src/isolation.ts +1038 -0
  12. package/container_src/shell-escape.ts +42 -0
  13. package/container_src/types.ts +27 -13
  14. package/dist/{chunk-HHUDRGPY.js → chunk-BEQUGUY4.js} +2 -2
  15. package/dist/{chunk-CKIGERRS.js → chunk-LFLJGISB.js} +240 -264
  16. package/dist/chunk-LFLJGISB.js.map +1 -0
  17. package/dist/{chunk-3CQ6THKA.js → chunk-SMUEY5JR.js} +85 -103
  18. package/dist/chunk-SMUEY5JR.js.map +1 -0
  19. package/dist/{client-Ce40ujDF.d.ts → client-Dny_ro_v.d.ts} +41 -25
  20. package/dist/client.d.ts +1 -1
  21. package/dist/client.js +1 -1
  22. package/dist/index.d.ts +2 -2
  23. package/dist/index.js +8 -9
  24. package/dist/interpreter.d.ts +1 -1
  25. package/dist/jupyter-client.d.ts +1 -1
  26. package/dist/jupyter-client.js +2 -2
  27. package/dist/request-handler.d.ts +1 -1
  28. package/dist/request-handler.js +3 -5
  29. package/dist/sandbox.d.ts +1 -1
  30. package/dist/sandbox.js +3 -5
  31. package/dist/types.d.ts +10 -21
  32. package/dist/types.js +35 -9
  33. package/dist/types.js.map +1 -1
  34. package/package.json +2 -2
  35. package/src/client.ts +120 -135
  36. package/src/index.ts +8 -0
  37. package/src/sandbox.ts +290 -331
  38. package/src/types.ts +15 -24
  39. package/dist/chunk-3CQ6THKA.js.map +0 -1
  40. package/dist/chunk-6EWSYSO7.js +0 -46
  41. package/dist/chunk-6EWSYSO7.js.map +0 -1
  42. package/dist/chunk-CKIGERRS.js.map +0 -1
  43. /package/dist/{chunk-HHUDRGPY.js.map → chunk-BEQUGUY4.js.map} +0 -0
package/src/sandbox.ts CHANGED
@@ -20,6 +20,7 @@ import type {
20
20
  ExecOptions,
21
21
  ExecResult,
22
22
  ExecuteResponse,
23
+ ExecutionSession,
23
24
  ISandbox,
24
25
  Process,
25
26
  ProcessOptions,
@@ -43,6 +44,7 @@ export class Sandbox<Env = unknown> extends Container<Env> implements ISandbox {
43
44
  client: JupyterClient;
44
45
  private sandboxName: string | null = null;
45
46
  private codeInterpreter: CodeInterpreter;
47
+ private defaultSession: ExecutionSession | null = null;
46
48
 
47
49
  constructor(ctx: DurableObjectState, env: Env) {
48
50
  super(ctx, env);
@@ -88,6 +90,11 @@ export class Sandbox<Env = unknown> extends Container<Env> implements ISandbox {
88
90
  async setEnvVars(envVars: Record<string, string>): Promise<void> {
89
91
  this.envVars = { ...this.envVars, ...envVars };
90
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
+ }
91
98
  }
92
99
 
93
100
  override onStart() {
@@ -96,9 +103,6 @@ export class Sandbox<Env = unknown> extends Container<Env> implements ISandbox {
96
103
 
97
104
  override onStop() {
98
105
  console.log("Sandbox successfully shut down");
99
- if (this.client) {
100
- this.client.clearSession();
101
- }
102
106
  }
103
107
 
104
108
  override onError(error: unknown) {
@@ -131,383 +135,104 @@ export class Sandbox<Env = unknown> extends Container<Env> implements ISandbox {
131
135
  return parseInt(proxyMatch[1]);
132
136
  }
133
137
 
138
+ if (url.port) {
139
+ return parseInt(url.port);
140
+ }
141
+
134
142
  // All other requests go to control plane on port 3000
135
143
  // This includes /api/* endpoints and any other control requests
136
144
  return 3000;
137
145
  }
138
146
 
139
- // Enhanced exec method - always returns ExecResult with optional streaming
140
- // This replaces the old exec method to match ISandbox interface
141
- async exec(command: string, options?: ExecOptions): Promise<ExecResult> {
142
- const startTime = Date.now();
143
- const timestamp = new Date().toISOString();
144
-
145
- // Handle timeout
146
- let timeoutId: NodeJS.Timeout | undefined;
147
-
148
- try {
149
- // Handle cancellation
150
- if (options?.signal?.aborted) {
151
- throw new Error("Operation was aborted");
152
- }
153
-
154
- let result: ExecResult;
155
-
156
- if (options?.stream && options?.onOutput) {
157
- // Streaming with callbacks - we need to collect the final result
158
- result = await this.executeWithStreaming(
159
- command,
160
- options,
161
- startTime,
162
- timestamp
163
- );
164
- } else {
165
- // Regular execution
166
- const response = await this.client.execute(command, {
167
- sessionId: options?.sessionId,
168
- cwd: options?.cwd,
169
- env: options?.env,
170
- });
171
-
172
- const duration = Date.now() - startTime;
173
- result = this.mapExecuteResponseToExecResult(
174
- response,
175
- duration,
176
- options?.sessionId
177
- );
178
- }
179
-
180
- // Call completion callback if provided
181
- if (options?.onComplete) {
182
- options.onComplete(result);
183
- }
184
-
185
- return result;
186
- } catch (error) {
187
- if (options?.onError && error instanceof Error) {
188
- options.onError(error);
189
- }
190
- throw error;
191
- } finally {
192
- if (timeoutId) {
193
- clearTimeout(timeoutId);
194
- }
147
+ // Helper to ensure default session is initialized
148
+ private async ensureDefaultSession(): Promise<ExecutionSession> {
149
+ if (!this.defaultSession) {
150
+ const sessionId = `sandbox-${this.sandboxName || 'default'}`;
151
+ this.defaultSession = await this.createSession({
152
+ id: sessionId,
153
+ env: this.envVars || {},
154
+ cwd: '/workspace',
155
+ isolation: true
156
+ });
157
+ console.log(`[Sandbox] Default session initialized: ${sessionId}`);
195
158
  }
159
+ return this.defaultSession;
196
160
  }
197
161
 
198
- private async executeWithStreaming(
199
- command: string,
200
- options: ExecOptions,
201
- startTime: number,
202
- timestamp: string
203
- ): Promise<ExecResult> {
204
- let stdout = "";
205
- let stderr = "";
206
-
207
- try {
208
- const stream = await this.client.executeCommandStream(
209
- command,
210
- options.sessionId
211
- );
212
162
 
213
- for await (const event of parseSSEStream<ExecEvent>(stream)) {
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
-
266
- private mapExecuteResponseToExecResult(
267
- response: 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
- };
163
+ async exec(command: string, options?: ExecOptions): Promise<ExecResult> {
164
+ const session = await this.ensureDefaultSession();
165
+ return session.exec(command, options);
281
166
  }
282
167
 
283
- // Background process management
284
168
  async startProcess(
285
169
  command: string,
286
170
  options?: ProcessOptions
287
171
  ): 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
- }
172
+ const session = await this.ensureDefaultSession();
173
+ return session.startProcess(command, options);
350
174
  }
351
175
 
352
176
  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
- }));
177
+ const session = await this.ensureDefaultSession();
178
+ return session.listProcesses();
379
179
  }
380
180
 
381
181
  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
- };
182
+ const session = await this.ensureDefaultSession();
183
+ return session.getProcess(id);
412
184
  }
413
185
 
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
- }
186
+ async killProcess(id: string, signal?: string): Promise<void> {
187
+ const session = await this.ensureDefaultSession();
188
+ return session.killProcess(id, signal);
432
189
  }
433
190
 
434
191
  async killAllProcesses(): Promise<number> {
435
- const response = await this.client.killAllProcesses();
436
- return response.killedCount;
192
+ const session = await this.ensureDefaultSession();
193
+ return session.killAllProcesses();
437
194
  }
438
195
 
439
196
  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;
197
+ const session = await this.ensureDefaultSession();
198
+ return session.cleanupCompletedProcesses();
444
199
  }
445
200
 
446
201
  async getProcessLogs(
447
202
  id: string
448
203
  ): 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
- }
204
+ const session = await this.ensureDefaultSession();
205
+ return session.getProcessLogs(id);
464
206
  }
465
207
 
466
- // Streaming methods - return ReadableStream for RPC compatibility
208
+ // Streaming methods - delegates to default session
467
209
  async execStream(
468
210
  command: string,
469
211
  options?: StreamOptions
470
212
  ): 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;
213
+ const session = await this.ensureDefaultSession();
214
+ return session.execStream(command, options);
484
215
  }
485
216
 
486
217
  async streamProcessLogs(
487
218
  processId: string,
488
219
  options?: { signal?: AbortSignal }
489
220
  ): 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;
221
+ const session = await this.ensureDefaultSession();
222
+ return session.streamProcessLogs(processId, options);
500
223
  }
501
224
 
502
225
  async gitCheckout(
503
226
  repoUrl: string,
504
227
  options: { branch?: string; targetDir?: string }
505
228
  ) {
506
- return this.client.gitCheckout(repoUrl, options.branch, options.targetDir);
229
+ const session = await this.ensureDefaultSession();
230
+ return session.gitCheckout(repoUrl, options);
507
231
  }
508
232
 
509
233
  async mkdir(path: string, options: { recursive?: boolean } = {}) {
510
- return this.client.mkdir(path, options.recursive);
234
+ const session = await this.ensureDefaultSession();
235
+ return session.mkdir(path, options);
511
236
  }
512
237
 
513
238
  async writeFile(
@@ -515,23 +240,28 @@ export class Sandbox<Env = unknown> extends Container<Env> implements ISandbox {
515
240
  content: string,
516
241
  options: { encoding?: string } = {}
517
242
  ) {
518
- return this.client.writeFile(path, content, options.encoding);
243
+ const session = await this.ensureDefaultSession();
244
+ return session.writeFile(path, content, options);
519
245
  }
520
246
 
521
247
  async deleteFile(path: string) {
522
- return this.client.deleteFile(path);
248
+ const session = await this.ensureDefaultSession();
249
+ return session.deleteFile(path);
523
250
  }
524
251
 
525
252
  async renameFile(oldPath: string, newPath: string) {
526
- return this.client.renameFile(oldPath, newPath);
253
+ const session = await this.ensureDefaultSession();
254
+ return session.renameFile(oldPath, newPath);
527
255
  }
528
256
 
529
257
  async moveFile(sourcePath: string, destinationPath: string) {
530
- return this.client.moveFile(sourcePath, destinationPath);
258
+ const session = await this.ensureDefaultSession();
259
+ return session.moveFile(sourcePath, destinationPath);
531
260
  }
532
261
 
533
262
  async readFile(path: string, options: { encoding?: string } = {}) {
534
- return this.client.readFile(path, options.encoding);
263
+ const session = await this.ensureDefaultSession();
264
+ return session.readFile(path, options);
535
265
  }
536
266
 
537
267
  async listFiles(
@@ -541,7 +271,8 @@ export class Sandbox<Env = unknown> extends Container<Env> implements ISandbox {
541
271
  includeHidden?: boolean;
542
272
  } = {}
543
273
  ) {
544
- return this.client.listFiles(path, options);
274
+ const session = await this.ensureDefaultSession();
275
+ return session.listFiles(path, options);
545
276
  }
546
277
 
547
278
  async exposePort(port: number, options: { name?: string; hostname: string }) {
@@ -785,4 +516,232 @@ export class Sandbox<Env = unknown> extends Container<Env> implements ISandbox {
785
516
  async deleteCodeContext(contextId: string): Promise<void> {
786
517
  return this.codeInterpreter.deleteCodeContext(contextId);
787
518
  }
519
+
520
+ // ============================================================================
521
+ // Session Management (Simple Isolation)
522
+ // ============================================================================
523
+
524
+ /**
525
+ * Create a new execution session with isolation
526
+ * Returns a session object with exec() method
527
+ */
528
+
529
+ async createSession(options: {
530
+ id?: string;
531
+ env?: Record<string, string>;
532
+ cwd?: string;
533
+ isolation?: boolean;
534
+ }): Promise<ExecutionSession> {
535
+ const sessionId = options.id || `session-${Date.now()}`;
536
+
537
+ await this.client.createSession({
538
+ id: sessionId,
539
+ env: options.env,
540
+ cwd: options.cwd,
541
+ isolation: options.isolation
542
+ });
543
+ // Return comprehensive ExecutionSession object that implements all ISandbox methods
544
+ return {
545
+ id: sessionId,
546
+
547
+ // Command execution - clean method names
548
+ exec: async (command: string, options?: ExecOptions) => {
549
+ const result = await this.client.exec(sessionId, command);
550
+ return {
551
+ ...result,
552
+ command,
553
+ duration: 0,
554
+ timestamp: new Date().toISOString()
555
+ };
556
+ },
557
+
558
+ execStream: async (command: string, options?: StreamOptions) => {
559
+ return await this.client.execStream(sessionId, command);
560
+ },
561
+
562
+ // Process management - route to session-aware methods
563
+ startProcess: async (command: string, options?: ProcessOptions) => {
564
+ // Use session-specific process management
565
+ const response = await this.client.startProcess(command, sessionId, {
566
+ processId: options?.processId,
567
+ timeout: options?.timeout,
568
+ env: options?.env,
569
+ cwd: options?.cwd,
570
+ encoding: options?.encoding,
571
+ autoCleanup: options?.autoCleanup,
572
+ });
573
+
574
+ // Convert response to Process object with bound methods
575
+ const process = response.process;
576
+ return {
577
+ id: process.id,
578
+ pid: process.pid,
579
+ command: process.command,
580
+ status: process.status as ProcessStatus,
581
+ startTime: new Date(process.startTime),
582
+ endTime: process.endTime ? new Date(process.endTime) : undefined,
583
+ exitCode: process.exitCode ?? undefined,
584
+ kill: async (signal?: string) => {
585
+ await this.client.killProcess(process.id);
586
+ },
587
+ getStatus: async () => {
588
+ const resp = await this.client.getProcess(process.id);
589
+ return resp.process?.status as ProcessStatus || "error";
590
+ },
591
+ getLogs: async () => {
592
+ return await this.client.getProcessLogs(process.id);
593
+ },
594
+ };
595
+ },
596
+
597
+ listProcesses: async () => {
598
+ // Get processes for this specific session
599
+ const response = await this.client.listProcesses(sessionId);
600
+
601
+ // Convert to Process objects with bound methods
602
+ return response.processes.map(p => ({
603
+ id: p.id,
604
+ pid: p.pid,
605
+ command: p.command,
606
+ status: p.status as ProcessStatus,
607
+ startTime: new Date(p.startTime),
608
+ endTime: p.endTime ? new Date(p.endTime) : undefined,
609
+ exitCode: p.exitCode ?? undefined,
610
+ kill: async (signal?: string) => {
611
+ await this.client.killProcess(p.id);
612
+ },
613
+ getStatus: async () => {
614
+ const processResp = await this.client.getProcess(p.id);
615
+ return processResp.process?.status as ProcessStatus || "error";
616
+ },
617
+ getLogs: async () => {
618
+ return this.client.getProcessLogs(p.id);
619
+ },
620
+ }));
621
+ },
622
+
623
+ getProcess: async (id: string) => {
624
+ const response = await this.client.getProcess(id);
625
+ if (!response.process) return null;
626
+
627
+ const p = response.process;
628
+ return {
629
+ id: p.id,
630
+ pid: p.pid,
631
+ command: p.command,
632
+ status: p.status as ProcessStatus,
633
+ startTime: new Date(p.startTime),
634
+ endTime: p.endTime ? new Date(p.endTime) : undefined,
635
+ exitCode: p.exitCode ?? undefined,
636
+ kill: async (signal?: string) => {
637
+ await this.client.killProcess(p.id);
638
+ },
639
+ getStatus: async () => {
640
+ const processResp = await this.client.getProcess(p.id);
641
+ return processResp.process?.status as ProcessStatus || "error";
642
+ },
643
+ getLogs: async () => {
644
+ return this.client.getProcessLogs(p.id);
645
+ },
646
+ };
647
+ },
648
+
649
+ killProcess: async (id: string, signal?: string) => {
650
+ await this.client.killProcess(id);
651
+ },
652
+
653
+ killAllProcesses: async () => {
654
+ // Kill all processes for this specific session
655
+ const response = await this.client.killAllProcesses(sessionId);
656
+ return response.killedCount;
657
+ },
658
+
659
+ streamProcessLogs: async (processId: string, options?: { signal?: AbortSignal }) => {
660
+ return await this.client.streamProcessLogs(processId, options);
661
+ },
662
+
663
+ getProcessLogs: async (id: string) => {
664
+ return await this.client.getProcessLogs(id);
665
+ },
666
+
667
+ cleanupCompletedProcesses: async () => {
668
+ // This would need a new endpoint to cleanup processes for a specific session
669
+ // For now, return 0 as no cleanup is performed
670
+ return 0;
671
+ },
672
+
673
+ // File operations - clean method names (no "InSession" suffix)
674
+ writeFile: async (path: string, content: string, options?: { encoding?: string }) => {
675
+ return await this.client.writeFile(path, content, options?.encoding, sessionId);
676
+ },
677
+
678
+ readFile: async (path: string, options?: { encoding?: string }) => {
679
+ return await this.client.readFile(path, options?.encoding, sessionId);
680
+ },
681
+
682
+ mkdir: async (path: string, options?: { recursive?: boolean }) => {
683
+ return await this.client.mkdir(path, options?.recursive, sessionId);
684
+ },
685
+
686
+ deleteFile: async (path: string) => {
687
+ return await this.client.deleteFile(path, sessionId);
688
+ },
689
+
690
+ renameFile: async (oldPath: string, newPath: string) => {
691
+ return await this.client.renameFile(oldPath, newPath, sessionId);
692
+ },
693
+
694
+ moveFile: async (sourcePath: string, destinationPath: string) => {
695
+ return await this.client.moveFile(sourcePath, destinationPath, sessionId);
696
+ },
697
+
698
+ listFiles: async (path: string, options?: { recursive?: boolean; includeHidden?: boolean }) => {
699
+ return await this.client.listFiles(path, sessionId, options);
700
+ },
701
+
702
+ gitCheckout: async (repoUrl: string, options?: { branch?: string; targetDir?: string }) => {
703
+ return await this.client.gitCheckout(repoUrl, sessionId, options?.branch, options?.targetDir);
704
+ },
705
+
706
+ // Port management
707
+ exposePort: async (port: number, options: { name?: string; hostname: string }) => {
708
+ return await this.exposePort(port, options);
709
+ },
710
+
711
+ unexposePort: async (port: number) => {
712
+ return await this.unexposePort(port);
713
+ },
714
+
715
+ getExposedPorts: async (hostname: string) => {
716
+ return await this.getExposedPorts(hostname);
717
+ },
718
+
719
+ // Environment management
720
+ setEnvVars: async (envVars: Record<string, string>) => {
721
+ // TODO: Implement session-specific environment updates
722
+ console.log(`[Session ${sessionId}] Environment variables update not yet implemented`);
723
+ },
724
+
725
+ // Code Interpreter API
726
+ createCodeContext: async (options?: any) => {
727
+ return await this.createCodeContext(options);
728
+ },
729
+
730
+ runCode: async (code: string, options?: any) => {
731
+ return await this.runCode(code, options);
732
+ },
733
+
734
+ runCodeStream: async (code: string, options?: any) => {
735
+ return await this.runCodeStream(code, options);
736
+ },
737
+
738
+ listCodeContexts: async () => {
739
+ return await this.listCodeContexts();
740
+ },
741
+
742
+ deleteCodeContext: async (contextId: string) => {
743
+ return await this.deleteCodeContext(contextId);
744
+ }
745
+ };
746
+ }
788
747
  }