computesdk 1.10.3 → 1.11.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -40,8 +40,11 @@ For more control, use `setConfig()` to explicitly configure the provider:
40
40
  import { compute } from 'computesdk';
41
41
 
42
42
  compute.setConfig({
43
- provider: 'e2b',
44
- e2b: { apiKey: 'your_api_key' }
43
+ provider: 'your-provider',
44
+ apiKey: process.env.COMPUTESDK_API_KEY,
45
+ 'your-provider': {
46
+ apiKey: process.env.YOUR_PROVIDER_API_KEY
47
+ }
45
48
  });
46
49
 
47
50
  const sandbox = await compute.sandbox.create();
@@ -84,8 +87,11 @@ Configure the gateway with explicit provider settings.
84
87
 
85
88
  ```typescript
86
89
  compute.setConfig({
87
- provider: 'e2b',
88
- e2b: { apiKey: 'your_api_key' }
90
+ provider: 'your-provider',
91
+ apiKey: process.env.COMPUTESDK_API_KEY,
92
+ 'your-provider': {
93
+ apiKey: process.env.YOUR_PROVIDER_API_KEY
94
+ }
89
95
  });
90
96
  ```
91
97
 
@@ -95,6 +101,7 @@ compute.setConfig({
95
101
  // E2B
96
102
  compute.setConfig({
97
103
  provider: 'e2b',
104
+ apiKey: process.env.COMPUTESDK_API_KEY,
98
105
  e2b: {
99
106
  apiKey: 'e2b_xxx',
100
107
  templateId: 'optional_template'
@@ -104,6 +111,7 @@ compute.setConfig({
104
111
  // Modal
105
112
  compute.setConfig({
106
113
  provider: 'modal',
114
+ apiKey: process.env.COMPUTESDK_API_KEY,
107
115
  modal: {
108
116
  tokenId: 'ak-xxx',
109
117
  tokenSecret: 'as-xxx'
@@ -113,6 +121,7 @@ compute.setConfig({
113
121
  // Railway
114
122
  compute.setConfig({
115
123
  provider: 'railway',
124
+ apiKey: process.env.COMPUTESDK_API_KEY,
116
125
  railway: {
117
126
  apiToken: 'your_token',
118
127
  projectId: 'project_id',
@@ -123,12 +132,14 @@ compute.setConfig({
123
132
  // Daytona
124
133
  compute.setConfig({
125
134
  provider: 'daytona',
135
+ apiKey: process.env.COMPUTESDK_API_KEY,
126
136
  daytona: { apiKey: 'your_api_key' }
127
137
  });
128
138
 
129
139
  // Vercel
130
140
  compute.setConfig({
131
141
  provider: 'vercel',
142
+ apiKey: process.env.COMPUTESDK_API_KEY,
132
143
  vercel: {
133
144
  token: 'your_token',
134
145
  teamId: 'team_xxx',
@@ -272,6 +283,209 @@ Remove a file or directory.
272
283
  await sandbox.filesystem.remove('/tmp/hello.py');
273
284
  ```
274
285
 
286
+ ### Terminal Operations
287
+
288
+ The sandbox provides terminal access in two modes: **PTY mode** (interactive shell) and **Exec mode** (command tracking).
289
+
290
+ #### `sandbox.terminal.create(options?)`
291
+
292
+ Create a new terminal session.
293
+
294
+ ```typescript
295
+ // PTY mode - Interactive shell
296
+ const pty = await sandbox.terminal.create({ pty: true, shell: '/bin/bash' });
297
+
298
+ // Exec mode - Command tracking (default)
299
+ const exec = await sandbox.terminal.create({ pty: false });
300
+ ```
301
+
302
+ **Options:**
303
+ - `pty?: boolean` - Terminal mode: `true` = PTY (interactive), `false` = exec (command tracking). Default: `false`
304
+ - `shell?: string` - Shell to use (e.g., '/bin/bash'). PTY mode only
305
+ - `encoding?: 'raw' | 'base64'` - Output encoding. Default: `'raw'`
306
+
307
+ **Returns:** `TerminalInstance`
308
+
309
+ #### `sandbox.terminal.list()`
310
+
311
+ List all active terminals.
312
+
313
+ ```typescript
314
+ const terminals = await sandbox.terminal.list();
315
+ console.log(terminals); // [{ id: 'term-1', pty: true, status: 'running', ... }]
316
+ ```
317
+
318
+ #### `sandbox.terminal.retrieve(id)`
319
+
320
+ Retrieve a specific terminal by ID.
321
+
322
+ ```typescript
323
+ const terminal = await sandbox.terminal.retrieve('term-123');
324
+ console.log(terminal.id, terminal.status);
325
+ ```
326
+
327
+ #### `sandbox.terminal.destroy(id)`
328
+
329
+ Destroy a terminal by ID.
330
+
331
+ ```typescript
332
+ await sandbox.terminal.destroy('term-123');
333
+ ```
334
+
335
+ ### PTY Mode (Interactive Shell)
336
+
337
+ PTY mode creates an interactive shell session with real-time input/output over WebSocket.
338
+
339
+ #### `terminal.write(input)`
340
+
341
+ Send input to the terminal shell.
342
+
343
+ ```typescript
344
+ const pty = await sandbox.terminal.create({ pty: true });
345
+ pty.on('output', (data) => console.log(data));
346
+ pty.write('ls -la\n');
347
+ pty.write('pwd\n');
348
+ await pty.destroy();
349
+ ```
350
+
351
+ #### `terminal.resize(cols, rows)`
352
+
353
+ Resize the terminal window.
354
+
355
+ ```typescript
356
+ pty.resize(120, 40);
357
+ ```
358
+
359
+ #### `terminal.on(event, handler)`
360
+
361
+ Register an event handler. Events: `'output'`, `'error'`, `'destroyed'`.
362
+
363
+ ```typescript
364
+ pty.on('output', (data) => console.log('Output:', data));
365
+ pty.on('error', (error) => console.error('Error:', error));
366
+ pty.on('destroyed', () => console.log('Terminal destroyed'));
367
+ ```
368
+
369
+ #### `terminal.off(event, handler)`
370
+
371
+ Unregister an event handler.
372
+
373
+ ```typescript
374
+ const handler = (data) => console.log(data);
375
+ pty.on('output', handler);
376
+ pty.off('output', handler);
377
+ ```
378
+
379
+ #### Terminal Properties
380
+
381
+ ```typescript
382
+ console.log(pty.id); // Terminal ID
383
+ console.log(pty.status); // 'running' | 'stopped' | 'active' | 'ready'
384
+ console.log(pty.channel); // WebSocket channel (PTY only)
385
+ console.log(pty.pty); // true for PTY mode
386
+ ```
387
+
388
+ ### Exec Mode (Command Tracking)
389
+
390
+ Exec mode executes commands with structured result tracking, suitable for automation.
391
+
392
+ #### `terminal.command.run(command, options?)`
393
+
394
+ Execute a command in the terminal.
395
+
396
+ ```typescript
397
+ const exec = await sandbox.terminal.create({ pty: false });
398
+
399
+ // Foreground execution (waits for completion)
400
+ const cmd = await exec.command.run('npm test');
401
+ console.log(cmd.stdout);
402
+ console.log(cmd.stderr);
403
+ console.log(cmd.exitCode);
404
+
405
+ // Background execution (returns immediately)
406
+ const bgCmd = await exec.command.run('npm install', { background: true });
407
+ console.log(bgCmd.status); // 'running'
408
+ await bgCmd.wait(); // Wait for completion
409
+ console.log(bgCmd.exitCode);
410
+ ```
411
+
412
+ **Parameters:**
413
+ - `command: string` - The command to execute
414
+ - `options?: { background?: boolean }` - Execution options
415
+
416
+ **Returns:** `Command` object
417
+
418
+ #### `terminal.command.list()`
419
+
420
+ List all commands executed in this terminal.
421
+
422
+ ```typescript
423
+ const commands = await exec.command.list();
424
+ commands.forEach(cmd => {
425
+ console.log(cmd.id, cmd.command, cmd.status, cmd.exitCode);
426
+ });
427
+ ```
428
+
429
+ #### `terminal.command.retrieve(cmdId)`
430
+
431
+ Retrieve a specific command by ID.
432
+
433
+ ```typescript
434
+ const cmd = await exec.command.retrieve('cmd-123');
435
+ console.log(cmd.stdout);
436
+ ```
437
+
438
+ ### Command Object
439
+
440
+ The `Command` object represents a command execution result.
441
+
442
+ #### Command Properties
443
+
444
+ ```typescript
445
+ console.log(cmd.id); // Command ID
446
+ console.log(cmd.terminalId); // Parent terminal ID
447
+ console.log(cmd.command); // Executed command
448
+ console.log(cmd.status); // 'running' | 'completed' | 'failed'
449
+ console.log(cmd.stdout); // Standard output
450
+ console.log(cmd.stderr); // Standard error
451
+ console.log(cmd.exitCode); // Exit code (undefined if running)
452
+ console.log(cmd.durationMs); // Execution time in milliseconds
453
+ console.log(cmd.startedAt); // Start timestamp
454
+ console.log(cmd.finishedAt); // Finish timestamp (undefined if running)
455
+ ```
456
+
457
+ #### `command.wait(timeout?)`
458
+
459
+ Wait for a background command to complete.
460
+
461
+ ```typescript
462
+ const cmd = await exec.command.run('sleep 5', { background: true });
463
+ await cmd.wait(); // Waits up to default timeout
464
+ console.log(cmd.exitCode);
465
+
466
+ // With custom timeout (in seconds, 0 = no timeout)
467
+ await cmd.wait(10);
468
+ ```
469
+
470
+ #### `command.refresh()`
471
+
472
+ Refresh the command status from the server.
473
+
474
+ ```typescript
475
+ const cmd = await exec.command.run('npm build', { background: true });
476
+ // ... later ...
477
+ await cmd.refresh();
478
+ console.log(cmd.status, cmd.exitCode);
479
+ ```
480
+
481
+ #### `terminal.destroy()`
482
+
483
+ Destroy the terminal and clean up resources.
484
+
485
+ ```typescript
486
+ await exec.destroy();
487
+ ```
488
+
275
489
  ## Examples
276
490
 
277
491
  ### Data Science Workflow
@@ -380,6 +594,82 @@ console.log(runResult.stdout);
380
594
  await sandbox.destroy();
381
595
  ```
382
596
 
597
+ ### Terminal Command Execution
598
+
599
+ ```typescript
600
+ import { compute } from 'computesdk';
601
+
602
+ const sandbox = await compute.sandbox.create({ runtime: 'node' });
603
+
604
+ // Create exec mode terminal for command tracking
605
+ const terminal = await sandbox.terminal.create({ pty: false });
606
+
607
+ // Run build commands with tracking
608
+ const install = await terminal.command.run('npm install');
609
+ console.log('Install exit code:', install.exitCode);
610
+
611
+ const build = await terminal.command.run('npm run build');
612
+ console.log('Build output:', build.stdout);
613
+
614
+ // Run tests in background
615
+ const tests = await terminal.command.run('npm test', { background: true });
616
+ console.log('Tests started:', tests.status);
617
+
618
+ // Wait for tests to complete
619
+ await tests.wait(60); // 60 second timeout
620
+ console.log('Tests completed:', tests.exitCode === 0 ? 'PASSED' : 'FAILED');
621
+ console.log('Test output:', tests.stdout);
622
+
623
+ // List all commands
624
+ const commands = await terminal.command.list();
625
+ console.log(`Executed ${commands.length} commands`);
626
+
627
+ await terminal.destroy();
628
+ await sandbox.destroy();
629
+ ```
630
+
631
+ ### Interactive Terminal Session
632
+
633
+ ```typescript
634
+ import { compute } from 'computesdk';
635
+
636
+ const sandbox = await compute.sandbox.create({ runtime: 'node' });
637
+
638
+ // Create PTY terminal for interactive shell
639
+ const pty = await sandbox.terminal.create({
640
+ pty: true,
641
+ shell: '/bin/bash'
642
+ });
643
+
644
+ // Collect all output
645
+ let output = '';
646
+ pty.on('output', (data) => {
647
+ output += data;
648
+ console.log(data);
649
+ });
650
+
651
+ pty.on('error', (error) => {
652
+ console.error('Terminal error:', error);
653
+ });
654
+
655
+ // Execute interactive commands
656
+ pty.write('echo "Starting project setup"\n');
657
+ pty.write('mkdir -p /workspace/myproject\n');
658
+ pty.write('cd /workspace/myproject\n');
659
+ pty.write('npm init -y\n');
660
+ pty.write('npm install express\n');
661
+ pty.write('echo "Setup complete"\n');
662
+
663
+ // Wait for operations to complete
664
+ await new Promise(resolve => setTimeout(resolve, 5000));
665
+
666
+ // Clean up
667
+ await pty.destroy();
668
+ await sandbox.destroy();
669
+
670
+ console.log('Complete output:', output);
671
+ ```
672
+
383
673
  ### Using Different Providers
384
674
 
385
675
  ```typescript
package/dist/index.d.mts CHANGED
@@ -250,6 +250,42 @@ interface TerminalErrorMessage {
250
250
  error: string;
251
251
  };
252
252
  }
253
+ /**
254
+ * Command stdout streaming message (exec mode with stream: true)
255
+ */
256
+ interface CommandStdoutMessage {
257
+ type: 'command:stdout';
258
+ channel: string;
259
+ data: {
260
+ terminal_id: string;
261
+ cmd_id: string;
262
+ output: string;
263
+ };
264
+ }
265
+ /**
266
+ * Command stderr streaming message (exec mode with stream: true)
267
+ */
268
+ interface CommandStderrMessage {
269
+ type: 'command:stderr';
270
+ channel: string;
271
+ data: {
272
+ terminal_id: string;
273
+ cmd_id: string;
274
+ output: string;
275
+ };
276
+ }
277
+ /**
278
+ * Command exit message (exec mode with stream: true)
279
+ */
280
+ interface CommandExitMessage {
281
+ type: 'command:exit';
282
+ channel: string;
283
+ data: {
284
+ terminal_id: string;
285
+ cmd_id: string;
286
+ exit_code: number;
287
+ };
288
+ }
253
289
  /**
254
290
  * File watcher created notification
255
291
  */
@@ -427,6 +463,12 @@ declare class WebSocketManager {
427
463
  * Resize terminal window
428
464
  */
429
465
  resizeTerminal(terminalId: string, cols: number, rows: number): void;
466
+ /**
467
+ * Start a pending streaming command
468
+ * Used in two-phase streaming flow: HTTP request creates pending command,
469
+ * then this signal triggers execution after client has subscribed.
470
+ */
471
+ startCommand(cmdId: string): void;
430
472
  /**
431
473
  * Register event handler
432
474
  */
@@ -438,6 +480,9 @@ declare class WebSocketManager {
438
480
  on(event: 'terminal:output', handler: MessageHandler<TerminalOutputMessage>): void;
439
481
  on(event: 'terminal:destroyed', handler: MessageHandler<TerminalDestroyedMessage>): void;
440
482
  on(event: 'terminal:error', handler: MessageHandler<TerminalErrorMessage>): void;
483
+ on(event: 'command:stdout', handler: MessageHandler<CommandStdoutMessage>): void;
484
+ on(event: 'command:stderr', handler: MessageHandler<CommandStderrMessage>): void;
485
+ on(event: 'command:exit', handler: MessageHandler<CommandExitMessage>): void;
441
486
  on(event: 'watcher:created', handler: MessageHandler<WatcherCreatedMessage>): void;
442
487
  on(event: 'file:changed', handler: MessageHandler<FileChangedMessage>): void;
443
488
  on(event: 'watcher:destroyed', handler: MessageHandler<WatcherDestroyedMessage>): void;
@@ -1957,6 +2002,13 @@ interface SandboxConfig {
1957
2002
  protocol?: 'json' | 'binary';
1958
2003
  /** Optional metadata associated with the sandbox */
1959
2004
  metadata?: Record<string, unknown>;
2005
+ /**
2006
+ * Handler called when destroy() is invoked.
2007
+ * If provided, this is called to destroy the sandbox (e.g., via gateway API).
2008
+ * If not provided, destroy() only disconnects the WebSocket.
2009
+ * @internal
2010
+ */
2011
+ destroyHandler?: () => Promise<void>;
1960
2012
  }
1961
2013
  /**
1962
2014
  * Health check response
@@ -2076,7 +2128,7 @@ interface FileResponse {
2076
2128
  };
2077
2129
  }
2078
2130
  /**
2079
- * Command execution response
2131
+ * Command execution response (used by both /run/command and /terminals/{id}/execute)
2080
2132
  */
2081
2133
  interface CommandExecutionResponse {
2082
2134
  message: string;
@@ -2084,12 +2136,12 @@ interface CommandExecutionResponse {
2084
2136
  terminal_id?: string;
2085
2137
  cmd_id?: string;
2086
2138
  command: string;
2087
- output?: string;
2088
2139
  stdout: string;
2089
2140
  stderr: string;
2090
2141
  exit_code?: number;
2091
2142
  duration_ms?: number;
2092
2143
  status?: 'running' | 'completed' | 'failed';
2144
+ channel?: string;
2093
2145
  pty?: boolean;
2094
2146
  };
2095
2147
  }
@@ -2142,22 +2194,6 @@ interface CodeExecutionResponse {
2142
2194
  language: string;
2143
2195
  };
2144
2196
  }
2145
- /**
2146
- * Run command response (POST /run/command)
2147
- */
2148
- interface RunCommandResponse {
2149
- message: string;
2150
- data: {
2151
- terminal_id?: string;
2152
- cmd_id?: string;
2153
- command: string;
2154
- stdout: string;
2155
- stderr: string;
2156
- exit_code?: number;
2157
- duration_ms?: number;
2158
- status?: 'running' | 'completed' | 'failed';
2159
- };
2160
- }
2161
2197
  /**
2162
2198
  * File watcher information
2163
2199
  */
@@ -2629,9 +2665,10 @@ declare class Sandbox {
2629
2665
  command: string;
2630
2666
  shell?: string;
2631
2667
  background?: boolean;
2668
+ stream?: boolean;
2632
2669
  cwd?: string;
2633
2670
  env?: Record<string, string>;
2634
- }): Promise<RunCommandResponse>;
2671
+ }): Promise<CommandExecutionResponse>;
2635
2672
  /**
2636
2673
  * List files at the specified path
2637
2674
  */
@@ -2644,6 +2681,11 @@ declare class Sandbox {
2644
2681
  * Get file metadata (without content)
2645
2682
  */
2646
2683
  getFile(path: string): Promise<FileResponse>;
2684
+ /**
2685
+ * Encode a file path for use in URLs
2686
+ * Strips leading slash and encodes each segment separately to preserve path structure
2687
+ */
2688
+ private encodeFilePath;
2647
2689
  /**
2648
2690
  * Read file content
2649
2691
  */
@@ -2916,6 +2958,8 @@ declare class Sandbox {
2916
2958
  * @param options.background - Run in background (server uses goroutines)
2917
2959
  * @param options.cwd - Working directory (server uses cmd.Dir)
2918
2960
  * @param options.env - Environment variables (server uses cmd.Env)
2961
+ * @param options.onStdout - Callback for streaming stdout data
2962
+ * @param options.onStderr - Callback for streaming stderr data
2919
2963
  * @returns Command execution result
2920
2964
  *
2921
2965
  * @example
@@ -2931,12 +2975,20 @@ declare class Sandbox {
2931
2975
  * background: true,
2932
2976
  * env: { PORT: '3000' }
2933
2977
  * })
2978
+ *
2979
+ * // With streaming output
2980
+ * await sandbox.runCommand('npm install', {
2981
+ * onStdout: (data) => console.log(data),
2982
+ * onStderr: (data) => console.error(data),
2983
+ * })
2934
2984
  * ```
2935
2985
  */
2936
2986
  runCommand(command: string, options?: {
2937
2987
  background?: boolean;
2938
2988
  cwd?: string;
2939
2989
  env?: Record<string, string>;
2990
+ onStdout?: (data: string) => void;
2991
+ onStderr?: (data: string) => void;
2940
2992
  }): Promise<{
2941
2993
  stdout: string;
2942
2994
  stderr: string;
@@ -2979,6 +3031,9 @@ declare class Sandbox {
2979
3031
  getInstance(): this;
2980
3032
  /**
2981
3033
  * Destroy the sandbox (Sandbox interface method)
3034
+ *
3035
+ * If a destroyHandler was provided (e.g., from gateway), calls it to destroy
3036
+ * the sandbox on the backend. Otherwise, only disconnects the WebSocket.
2982
3037
  */
2983
3038
  destroy(): Promise<void>;
2984
3039
  /**
@@ -3076,8 +3131,13 @@ declare function getMissingEnvVars(provider: ProviderName): string[];
3076
3131
  interface ExplicitComputeConfig {
3077
3132
  /** Provider name to use */
3078
3133
  provider: ProviderName;
3134
+ /**
3135
+ * ComputeSDK API key (required for gateway mode)
3136
+ * @deprecated Use `computesdkApiKey` for clarity
3137
+ */
3138
+ apiKey?: string;
3079
3139
  /** ComputeSDK API key (required for gateway mode) */
3080
- apiKey: string;
3140
+ computesdkApiKey?: string;
3081
3141
  /** Optional gateway URL override */
3082
3142
  gatewayUrl?: string;
3083
3143
  /** Provider-specific configurations */