computesdk 3.0.0 → 4.0.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.
package/README.md CHANGED
@@ -1,6 +1,9 @@
1
1
  # computesdk
2
2
 
3
- The gateway SDK for running code in remote sandboxes. Zero-config auto-detection with support for E2B, Modal, Railway, Daytona, Vercel, and more.
3
+ The universal SDK for running code in remote sandboxes.
4
+
5
+ > Gateway/control-plane transport has been removed from `computesdk`.
6
+ > Configure `compute` with `provider` or `providers`, or use provider packages directly.
4
7
 
5
8
  ## Installation
6
9
 
@@ -10,23 +13,23 @@ npm install computesdk
10
13
 
11
14
  ## Quick Start
12
15
 
13
- ### Zero-Config Mode (Recommended)
14
-
15
- Set your provider credentials as environment variables and ComputeSDK automatically detects and configures everything:
16
-
17
- ```bash
18
- export E2B_API_KEY=your_e2b_api_key
19
- ```
16
+ ### Direct Provider Mode (Recommended)
20
17
 
21
18
  ```typescript
22
19
  import { compute } from 'computesdk';
20
+ import { e2b } from '@computesdk/e2b';
21
+
22
+ compute.setConfig({
23
+ provider: e2b({
24
+ apiKey: process.env.E2B_API_KEY,
25
+ }),
26
+ });
23
27
 
24
- // Auto-detects E2B from environment
25
28
  const sandbox = await compute.sandbox.create();
26
29
 
27
30
  // Execute code
28
- const result = await sandbox.runCode('print("Hello World!")');
29
- console.log(result.output); // "Hello World!"
31
+ const result = await sandbox.runCommand('python -c "print(\"Hello World!\")"');
32
+ console.log(result.stdout); // "Hello World!"
30
33
 
31
34
  // Clean up
32
35
  await sandbox.destroy();
@@ -34,47 +37,72 @@ await sandbox.destroy();
34
37
 
35
38
  ### Explicit Configuration
36
39
 
37
- For more control, use `setConfig()` to explicitly configure the provider:
40
+ For more control, configure `compute` with an explicit provider:
38
41
 
39
42
  ```typescript
40
43
  import { compute } from 'computesdk';
44
+ import { modal } from '@computesdk/modal';
41
45
 
42
46
  compute.setConfig({
43
- provider: 'your-provider',
44
- apiKey: process.env.COMPUTESDK_API_KEY,
45
- 'your-provider': {
46
- apiKey: process.env.YOUR_PROVIDER_API_KEY
47
- }
47
+ provider: modal({
48
+ tokenId: process.env.MODAL_TOKEN_ID,
49
+ tokenSecret: process.env.MODAL_TOKEN_SECRET,
50
+ }),
48
51
  });
49
52
 
50
53
  const sandbox = await compute.sandbox.create();
51
54
  ```
52
55
 
56
+ ### Multi-Provider Configuration
57
+
58
+ Configure multiple providers for resilience and routing:
59
+
60
+ ```typescript
61
+ import { compute } from 'computesdk';
62
+ import { e2b } from '@computesdk/e2b';
63
+ import { modal } from '@computesdk/modal';
64
+
65
+ compute.setConfig({
66
+ providers: [
67
+ e2b({ apiKey: process.env.E2B_API_KEY }),
68
+ modal({
69
+ tokenId: process.env.MODAL_TOKEN_ID,
70
+ tokenSecret: process.env.MODAL_TOKEN_SECRET,
71
+ }),
72
+ ],
73
+ providerStrategy: 'round-robin', // or 'priority'
74
+ fallbackOnError: true,
75
+ });
76
+
77
+ // Uses configured strategy
78
+ const sandbox = await compute.sandbox.create();
79
+
80
+ // Force a specific provider for one call
81
+ const modalSandbox = await compute.sandbox.create({ provider: 'modal' });
82
+ ```
83
+
53
84
  ## Supported Providers
54
85
 
55
- ComputeSDK automatically detects providers based on environment variables:
86
+ Use provider packages directly to create provider instances:
56
87
 
57
88
  | Provider | Environment Variables | Use Cases |
58
89
  |----------|----------------------|-----------|
59
90
  | **E2B** | `E2B_API_KEY` | Data science, Python/Node.js, interactive terminals |
60
91
  | **Modal** | `MODAL_TOKEN_ID`, `MODAL_TOKEN_SECRET` | GPU computing, ML inference, Python workloads |
61
- | **Railway** | `RAILWAY_TOKEN` | Full-stack deployments, persistent storage |
92
+ | **Railway** | `RAILWAY_API_KEY`, `RAILWAY_PROJECT_ID`, `RAILWAY_ENVIRONMENT_ID` | Full-stack deployments, persistent storage |
62
93
  | **Daytona** | `DAYTONA_API_KEY` | Development workspaces, custom environments |
63
94
  | **Runloop** | `RUNLOOP_API_KEY` | Code execution, automation |
64
95
  | **Vercel** | `VERCEL_TOKEN` or `VERCEL_OIDC_TOKEN` | Serverless functions, web apps |
65
- | **Cloudflare** | `CLOUDFLARE_API_TOKEN` | Edge computing |
66
- | **CodeSandbox** | `CODESANDBOX_TOKEN` | Collaborative development |
67
-
68
- ### Provider Detection Order
96
+ | **Cloudflare** | `CLOUDFLARE_SANDBOX_URL`, `CLOUDFLARE_SANDBOX_SECRET` | Edge computing |
97
+ | **CodeSandbox** | `CSB_API_KEY` | Collaborative development |
69
98
 
70
- When using zero-config mode, ComputeSDK detects providers in this order:
99
+ Example imports:
71
100
 
72
- **E2B → Railway → Daytona → Modal → Runloop → Vercel → Cloudflare → CodeSandbox**
73
-
74
- You can force a specific provider:
75
-
76
- ```bash
77
- export COMPUTESDK_PROVIDER=modal
101
+ ```typescript
102
+ import { e2b } from '@computesdk/e2b';
103
+ import { modal } from '@computesdk/modal';
104
+ import { vercel } from '@computesdk/vercel';
105
+ import { daytona } from '@computesdk/daytona';
78
106
  ```
79
107
 
80
108
  ## API Reference
@@ -83,71 +111,51 @@ export COMPUTESDK_PROVIDER=modal
83
111
 
84
112
  #### `compute.setConfig(config)`
85
113
 
86
- Configure the gateway with explicit provider settings.
114
+ Configure `compute` with `provider` or `providers`.
87
115
 
88
116
  ```typescript
89
117
  compute.setConfig({
90
- provider: 'your-provider',
91
- apiKey: process.env.COMPUTESDK_API_KEY,
92
- 'your-provider': {
93
- apiKey: process.env.YOUR_PROVIDER_API_KEY
94
- }
118
+ provider: e2b({
119
+ apiKey: process.env.E2B_API_KEY,
120
+ }),
95
121
  });
96
122
  ```
97
123
 
98
- **Provider-specific configs:**
124
+ `compute(...)` callable mode is also supported:
99
125
 
100
126
  ```typescript
101
- // E2B
102
- compute.setConfig({
103
- provider: 'e2b',
104
- apiKey: process.env.COMPUTESDK_API_KEY,
105
- e2b: {
106
- apiKey: 'e2b_xxx',
107
- templateId: 'optional_template'
108
- }
127
+ const scopedCompute = compute({
128
+ provider: vercel({
129
+ token: process.env.VERCEL_TOKEN,
130
+ teamId: process.env.VERCEL_TEAM_ID,
131
+ projectId: process.env.VERCEL_PROJECT_ID,
132
+ }),
109
133
  });
110
134
 
111
- // Modal
112
- compute.setConfig({
113
- provider: 'modal',
114
- apiKey: process.env.COMPUTESDK_API_KEY,
115
- modal: {
116
- tokenId: 'ak-xxx',
117
- tokenSecret: 'as-xxx'
118
- }
119
- });
135
+ const sandbox = await scopedCompute.sandbox.create();
136
+ ```
120
137
 
121
- // Railway
122
- compute.setConfig({
123
- provider: 'railway',
124
- apiKey: process.env.COMPUTESDK_API_KEY,
125
- railway: {
126
- apiToken: 'your_token',
127
- projectId: 'project_id',
128
- environmentId: 'env_id'
129
- }
130
- });
138
+ Multi-provider config shape:
131
139
 
132
- // Daytona
140
+ ```typescript
133
141
  compute.setConfig({
134
- provider: 'daytona',
135
- apiKey: process.env.COMPUTESDK_API_KEY,
136
- daytona: { apiKey: 'your_api_key' }
142
+ providers: [e2b({...}), modal({...})],
143
+ providerStrategy: 'priority', // default: 'priority'
144
+ fallbackOnError: true, // default: true
137
145
  });
146
+ ```
147
+
148
+ You can also combine both `provider` and `providers`:
138
149
 
139
- // Vercel
150
+ ```typescript
140
151
  compute.setConfig({
141
- provider: 'vercel',
142
- apiKey: process.env.COMPUTESDK_API_KEY,
143
- vercel: {
144
- token: 'your_token',
145
- teamId: 'team_xxx',
146
- projectId: 'prj_xxx'
147
- }
152
+ provider: e2b({...}), // primary provider (first choice)
153
+ providers: [modal({...})], // fallback/secondary providers
148
154
  });
149
155
  ```
150
156
 
157
+ When both are present, `provider` is treated as the primary provider and is placed first.
158
+
151
159
  ### Sandbox Management
152
160
 
153
161
  #### `compute.sandbox.create(options?)`
@@ -171,9 +179,9 @@ const sandbox = await compute.sandbox.create({
171
179
  - `metadata?: Record<string, any>` - Custom metadata
172
180
  - `envs?: Record<string, string>` - Environment variables
173
181
  - `namespace?: string` - Namespace for organizing sandboxes
174
- - `name?: string` - Name for the sandbox (enables findOrCreate)
175
- - `overlays?: SetupOverlayConfig[]` - Template overlays to apply
176
- - `servers?: ServerStartOptions[]` - Servers to start automatically
182
+ - `name?: string` - Human-readable name for the sandbox
183
+
184
+ > **Note:** Not every provider honors every option. Support for fields like `name`, `metadata`, and `envs` depends on the underlying provider SDK — some pass them through, some map them to a different field, and some ignore them silently. Check your provider package's README for the exact set of options it respects.
177
185
 
178
186
  #### `compute.sandbox.getById(sandboxId)`
179
187
 
@@ -183,40 +191,8 @@ Get an existing sandbox by ID.
183
191
  const sandbox = await compute.sandbox.getById('sandbox-id');
184
192
  ```
185
193
 
186
- #### `compute.sandbox.findOrCreate(options)`
187
-
188
- Find an existing sandbox by namespace and name, or create a new one.
189
-
190
- ```typescript
191
- const sandbox = await compute.sandbox.findOrCreate({
192
- namespace: 'my-org',
193
- name: 'my-project',
194
- });
195
- ```
196
-
197
- #### `compute.sandbox.find(options)`
198
-
199
- Find an existing sandbox by namespace and name (returns null if not found).
200
-
201
- ```typescript
202
- const sandbox = await compute.sandbox.find({
203
- namespace: 'my-org',
204
- name: 'my-project',
205
- });
206
- ```
207
-
208
194
  ### Sandbox Operations
209
195
 
210
- #### `sandbox.runCode(code, language?)`
211
-
212
- Execute code in the sandbox.
213
-
214
- ```typescript
215
- const result = await sandbox.runCode('print("Hello")', 'python');
216
- console.log(result.output); // "Hello"
217
- console.log(result.exitCode);
218
- ```
219
-
220
196
  #### `sandbox.runCommand(command, options?)`
221
197
 
222
198
  Run a shell command.
@@ -297,209 +273,6 @@ Remove a file or directory.
297
273
  await sandbox.filesystem.remove('/tmp/hello.py');
298
274
  ```
299
275
 
300
- ### Terminal Operations
301
-
302
- The sandbox provides terminal access in two modes: **PTY mode** (interactive shell) and **Exec mode** (command tracking).
303
-
304
- #### `sandbox.terminal.create(options?)`
305
-
306
- Create a new terminal session.
307
-
308
- ```typescript
309
- // PTY mode - Interactive shell
310
- const pty = await sandbox.terminal.create({ pty: true, shell: '/bin/bash' });
311
-
312
- // Exec mode - Command tracking (default)
313
- const exec = await sandbox.terminal.create({ pty: false });
314
- ```
315
-
316
- **Options:**
317
- - `pty?: boolean` - Terminal mode: `true` = PTY (interactive), `false` = exec (command tracking). Default: `false`
318
- - `shell?: string` - Shell to use (e.g., '/bin/bash'). PTY mode only
319
- - `encoding?: 'raw' | 'base64'` - Output encoding. Default: `'raw'`
320
-
321
- **Returns:** `TerminalInstance`
322
-
323
- #### `sandbox.terminal.list()`
324
-
325
- List all active terminals.
326
-
327
- ```typescript
328
- const terminals = await sandbox.terminal.list();
329
- console.log(terminals); // [{ id: 'term-1', pty: true, status: 'running', ... }]
330
- ```
331
-
332
- #### `sandbox.terminal.retrieve(id)`
333
-
334
- Retrieve a specific terminal by ID.
335
-
336
- ```typescript
337
- const terminal = await sandbox.terminal.retrieve('term-123');
338
- console.log(terminal.id, terminal.status);
339
- ```
340
-
341
- #### `sandbox.terminal.destroy(id)`
342
-
343
- Destroy a terminal by ID.
344
-
345
- ```typescript
346
- await sandbox.terminal.destroy('term-123');
347
- ```
348
-
349
- ### PTY Mode (Interactive Shell)
350
-
351
- PTY mode creates an interactive shell session with real-time input/output over WebSocket.
352
-
353
- #### `terminal.write(input)`
354
-
355
- Send input to the terminal shell.
356
-
357
- ```typescript
358
- const pty = await sandbox.terminal.create({ pty: true });
359
- pty.on('output', (data) => console.log(data));
360
- pty.write('ls -la\n');
361
- pty.write('pwd\n');
362
- await pty.destroy();
363
- ```
364
-
365
- #### `terminal.resize(cols, rows)`
366
-
367
- Resize the terminal window.
368
-
369
- ```typescript
370
- pty.resize(120, 40);
371
- ```
372
-
373
- #### `terminal.on(event, handler)`
374
-
375
- Register an event handler. Events: `'output'`, `'error'`, `'destroyed'`.
376
-
377
- ```typescript
378
- pty.on('output', (data) => console.log('Output:', data));
379
- pty.on('error', (error) => console.error('Error:', error));
380
- pty.on('destroyed', () => console.log('Terminal destroyed'));
381
- ```
382
-
383
- #### `terminal.off(event, handler)`
384
-
385
- Unregister an event handler.
386
-
387
- ```typescript
388
- const handler = (data) => console.log(data);
389
- pty.on('output', handler);
390
- pty.off('output', handler);
391
- ```
392
-
393
- #### Terminal Properties
394
-
395
- ```typescript
396
- console.log(pty.id); // Terminal ID
397
- console.log(pty.status); // 'running' | 'stopped' | 'active' | 'ready'
398
- console.log(pty.channel); // WebSocket channel (PTY only)
399
- console.log(pty.pty); // true for PTY mode
400
- ```
401
-
402
- ### Exec Mode (Command Tracking)
403
-
404
- Exec mode executes commands with structured result tracking, suitable for automation.
405
-
406
- #### `terminal.command.run(command, options?)`
407
-
408
- Execute a command in the terminal.
409
-
410
- ```typescript
411
- const exec = await sandbox.terminal.create({ pty: false });
412
-
413
- // Foreground execution (waits for completion)
414
- const cmd = await exec.command.run('npm test');
415
- console.log(cmd.stdout);
416
- console.log(cmd.stderr);
417
- console.log(cmd.exitCode);
418
-
419
- // Background execution (returns immediately)
420
- const bgCmd = await exec.command.run('npm install', { background: true });
421
- console.log(bgCmd.status); // 'running'
422
- await bgCmd.wait(); // Wait for completion
423
- console.log(bgCmd.exitCode);
424
- ```
425
-
426
- **Parameters:**
427
- - `command: string` - The command to execute
428
- - `options?: { background?: boolean }` - Execution options
429
-
430
- **Returns:** `Command` object
431
-
432
- #### `terminal.command.list()`
433
-
434
- List all commands executed in this terminal.
435
-
436
- ```typescript
437
- const commands = await exec.command.list();
438
- commands.forEach(cmd => {
439
- console.log(cmd.id, cmd.command, cmd.status, cmd.exitCode);
440
- });
441
- ```
442
-
443
- #### `terminal.command.retrieve(cmdId)`
444
-
445
- Retrieve a specific command by ID.
446
-
447
- ```typescript
448
- const cmd = await exec.command.retrieve('cmd-123');
449
- console.log(cmd.stdout);
450
- ```
451
-
452
- ### Command Object
453
-
454
- The `Command` object represents a command execution result.
455
-
456
- #### Command Properties
457
-
458
- ```typescript
459
- console.log(cmd.id); // Command ID
460
- console.log(cmd.terminalId); // Parent terminal ID
461
- console.log(cmd.command); // Executed command
462
- console.log(cmd.status); // 'running' | 'completed' | 'failed'
463
- console.log(cmd.stdout); // Standard output
464
- console.log(cmd.stderr); // Standard error
465
- console.log(cmd.exitCode); // Exit code (undefined if running)
466
- console.log(cmd.durationMs); // Execution time in milliseconds
467
- console.log(cmd.startedAt); // Start timestamp
468
- console.log(cmd.finishedAt); // Finish timestamp (undefined if running)
469
- ```
470
-
471
- #### `command.wait(timeout?)`
472
-
473
- Wait for a background command to complete.
474
-
475
- ```typescript
476
- const cmd = await exec.command.run('sleep 5', { background: true });
477
- await cmd.wait(); // Waits up to default timeout
478
- console.log(cmd.exitCode);
479
-
480
- // With custom timeout (in seconds, 0 = no timeout)
481
- await cmd.wait(10);
482
- ```
483
-
484
- #### `command.refresh()`
485
-
486
- Refresh the command status from the server.
487
-
488
- ```typescript
489
- const cmd = await exec.command.run('npm build', { background: true });
490
- // ... later ...
491
- await cmd.refresh();
492
- console.log(cmd.status, cmd.exitCode);
493
- ```
494
-
495
- #### `terminal.destroy()`
496
-
497
- Destroy the terminal and clean up resources.
498
-
499
- ```typescript
500
- await exec.destroy();
501
- ```
502
-
503
276
  ## Examples
504
277
 
505
278
  ### Multi-Step Build Process
@@ -539,119 +312,39 @@ const installResult = await sandbox.runCommand('npm install', { cwd: '/app' });
539
312
  console.log('Install:', installResult.stdout);
540
313
 
541
314
  // Run the app
542
- const runResult = await sandbox.runCode(`
543
- const { spawn } = require('child_process');
544
- const proc = spawn('node', ['src/index.js'], { cwd: '/app' });
545
- proc.stdout.on('data', (data) => console.log(data.toString()));
546
- `);
315
+ const runResult = await sandbox.runCommand('node src/index.js', { cwd: '/app' });
547
316
 
548
- console.log(runResult.output);
317
+ console.log(runResult.stdout);
549
318
 
550
319
  await sandbox.destroy();
551
320
  ```
552
321
 
553
- ### Terminal Command Execution
554
-
555
- ```typescript
556
- import { compute } from 'computesdk';
557
-
558
- const sandbox = await compute.sandbox.create();
559
-
560
- // Create exec mode terminal for command tracking
561
- const terminal = await sandbox.terminal.create({ pty: false });
562
-
563
- // Run build commands with tracking
564
- const install = await terminal.command.run('npm install');
565
- console.log('Install exit code:', install.exitCode);
566
-
567
- const build = await terminal.command.run('npm run build');
568
- console.log('Build output:', build.stdout);
569
-
570
- // Run tests in background
571
- const tests = await terminal.command.run('npm test', { background: true });
572
- console.log('Tests started:', tests.status);
573
-
574
- // Wait for tests to complete
575
- await tests.wait(60); // 60 second timeout
576
- console.log('Tests completed:', tests.exitCode === 0 ? 'PASSED' : 'FAILED');
577
- console.log('Test output:', tests.stdout);
578
-
579
- // List all commands
580
- const commands = await terminal.command.list();
581
- console.log(`Executed ${commands.length} commands`);
582
-
583
- await terminal.destroy();
584
- await sandbox.destroy();
585
- ```
586
-
587
- ### Interactive Terminal Session
588
-
589
- ```typescript
590
- import { compute } from 'computesdk';
591
-
592
- const sandbox = await compute.sandbox.create();
593
-
594
- // Create PTY terminal for interactive shell
595
- const pty = await sandbox.terminal.create({
596
- pty: true,
597
- shell: '/bin/bash'
598
- });
599
-
600
- // Collect all output
601
- let output = '';
602
- pty.on('output', (data) => {
603
- output += data;
604
- console.log(data);
605
- });
606
-
607
- pty.on('error', (error) => {
608
- console.error('Terminal error:', error);
609
- });
610
-
611
- // Execute interactive commands
612
- pty.write('echo "Starting project setup"\n');
613
- pty.write('mkdir -p /workspace/myproject\n');
614
- pty.write('cd /workspace/myproject\n');
615
- pty.write('npm init -y\n');
616
- pty.write('npm install express\n');
617
- pty.write('echo "Setup complete"\n');
618
-
619
- // Wait for operations to complete
620
- await new Promise(resolve => setTimeout(resolve, 5000));
621
-
622
- // Clean up
623
- await pty.destroy();
624
- await sandbox.destroy();
625
-
626
- console.log('Complete output:', output);
627
- ```
628
-
629
322
  ### Using Different Providers
630
323
 
631
324
  ```typescript
632
325
  import { compute } from 'computesdk';
326
+ import { e2b } from '@computesdk/e2b';
327
+ import { modal } from '@computesdk/modal';
633
328
 
634
329
  // Use E2B for data science
635
330
  compute.setConfig({
636
- provider: 'e2b',
637
- e2b: { apiKey: process.env.E2B_API_KEY }
331
+ provider: e2b({ apiKey: process.env.E2B_API_KEY }),
638
332
  });
639
333
 
640
334
  const e2bSandbox = await compute.sandbox.create();
641
- await e2bSandbox.runCode('import pandas as pd; print(pd.__version__)');
335
+ await e2bSandbox.runCommand('python -c "import pandas as pd; print(pd.__version__)"');
642
336
  await e2bSandbox.destroy();
643
337
 
644
338
  // Switch to Modal for GPU workloads
645
339
  compute.setConfig({
646
- provider: 'modal',
647
- modal: {
340
+ provider: modal({
648
341
  tokenId: process.env.MODAL_TOKEN_ID,
649
- tokenSecret: process.env.MODAL_TOKEN_SECRET
650
- }
342
+ tokenSecret: process.env.MODAL_TOKEN_SECRET,
343
+ }),
651
344
  });
652
345
 
653
346
  const modalSandbox = await compute.sandbox.create();
654
- await modalSandbox.runCode('import torch; print(torch.cuda.is_available())');
347
+ await modalSandbox.runCommand('python -c "import torch; print(torch.cuda.is_available())"');
655
348
  await modalSandbox.destroy();
656
349
  ```
657
350
 
@@ -660,20 +353,20 @@ await modalSandbox.destroy();
660
353
  ```typescript
661
354
  try {
662
355
  const sandbox = await compute.sandbox.create();
663
- const result = await sandbox.runCode('invalid python code');
356
+ const result = await sandbox.runCommand('invalid python code');
664
357
  } catch (error) {
665
358
  console.error('Execution failed:', error.message);
666
359
 
667
360
  // Check for specific error types
668
- if (error.message.includes('No provider detected')) {
669
- console.error('Set provider credentials in environment variables');
361
+ if (error.message.includes('No provider instance configured')) {
362
+ console.error('Configure compute.setConfig({ provider: e2b({...}) }) first');
670
363
  }
671
364
  }
672
365
  ```
673
366
 
674
- ## Direct Mode (Advanced)
367
+ ## Provider Packages (Advanced)
675
368
 
676
- For advanced use cases where you want to bypass the gateway and use provider SDKs directly, see individual provider packages:
369
+ For advanced use cases where you want to use provider SDKs directly, see individual provider packages:
677
370
 
678
371
  - **[@computesdk/e2b](../e2b)** - E2B provider
679
372
  - **[@computesdk/modal](../modal)** - Modal provider