computesdk 3.0.0 → 4.1.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,71 +13,52 @@ 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
- ```
20
-
21
16
  ```typescript
22
17
  import { compute } from 'computesdk';
18
+ import { e2b } from '@computesdk/e2b';
19
+
20
+ compute.setConfig({
21
+ provider: e2b({
22
+ apiKey: process.env.E2B_API_KEY,
23
+ }),
24
+ });
23
25
 
24
- // Auto-detects E2B from environment
25
26
  const sandbox = await compute.sandbox.create();
26
27
 
27
28
  // Execute code
28
- const result = await sandbox.runCode('print("Hello World!")');
29
- console.log(result.output); // "Hello World!"
29
+ const result = await sandbox.runCommand('python -c "print(\"Hello World!\")"');
30
+ console.log(result.stdout); // "Hello World!"
30
31
 
31
32
  // Clean up
32
33
  await sandbox.destroy();
33
34
  ```
34
35
 
35
- ### Explicit Configuration
36
+ ### Multi-Provider Configuration
36
37
 
37
- For more control, use `setConfig()` to explicitly configure the provider:
38
+ Configure multiple providers for resilience and routing:
38
39
 
39
40
  ```typescript
40
41
  import { compute } from 'computesdk';
42
+ import { e2b } from '@computesdk/e2b';
43
+ import { modal } from '@computesdk/modal';
41
44
 
42
45
  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
- }
46
+ providers: [
47
+ e2b({ apiKey: process.env.E2B_API_KEY }),
48
+ modal({
49
+ tokenId: process.env.MODAL_TOKEN_ID,
50
+ tokenSecret: process.env.MODAL_TOKEN_SECRET,
51
+ }),
52
+ ],
53
+ providerStrategy: 'round-robin', // or 'priority'
54
+ fallbackOnError: true,
48
55
  });
49
56
 
57
+ // Uses configured strategy
50
58
  const sandbox = await compute.sandbox.create();
51
- ```
52
-
53
- ## Supported Providers
54
-
55
- ComputeSDK automatically detects providers based on environment variables:
56
-
57
- | Provider | Environment Variables | Use Cases |
58
- |----------|----------------------|-----------|
59
- | **E2B** | `E2B_API_KEY` | Data science, Python/Node.js, interactive terminals |
60
- | **Modal** | `MODAL_TOKEN_ID`, `MODAL_TOKEN_SECRET` | GPU computing, ML inference, Python workloads |
61
- | **Railway** | `RAILWAY_TOKEN` | Full-stack deployments, persistent storage |
62
- | **Daytona** | `DAYTONA_API_KEY` | Development workspaces, custom environments |
63
- | **Runloop** | `RUNLOOP_API_KEY` | Code execution, automation |
64
- | **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
69
-
70
- When using zero-config mode, ComputeSDK detects providers in this order:
71
59
 
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
60
+ // Force a specific provider for one call
61
+ const modalSandbox = await compute.sandbox.create({ provider: 'modal' });
78
62
  ```
79
63
 
80
64
  ## API Reference
@@ -83,71 +67,51 @@ export COMPUTESDK_PROVIDER=modal
83
67
 
84
68
  #### `compute.setConfig(config)`
85
69
 
86
- Configure the gateway with explicit provider settings.
70
+ Configure `compute` with `provider` or `providers`.
87
71
 
88
72
  ```typescript
89
73
  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
- }
74
+ provider: e2b({
75
+ apiKey: process.env.E2B_API_KEY,
76
+ }),
95
77
  });
96
78
  ```
97
79
 
98
- **Provider-specific configs:**
80
+ `compute(...)` callable mode is also supported:
99
81
 
100
82
  ```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
- }
83
+ const scopedCompute = compute({
84
+ provider: vercel({
85
+ token: process.env.VERCEL_TOKEN,
86
+ teamId: process.env.VERCEL_TEAM_ID,
87
+ projectId: process.env.VERCEL_PROJECT_ID,
88
+ }),
109
89
  });
110
90
 
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
- });
91
+ const sandbox = await scopedCompute.sandbox.create();
92
+ ```
120
93
 
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
- });
94
+ Multi-provider config shape:
131
95
 
132
- // Daytona
96
+ ```typescript
133
97
  compute.setConfig({
134
- provider: 'daytona',
135
- apiKey: process.env.COMPUTESDK_API_KEY,
136
- daytona: { apiKey: 'your_api_key' }
98
+ providers: [e2b({...}), modal({...})],
99
+ providerStrategy: 'priority', // default: 'priority'
100
+ fallbackOnError: true, // default: true
137
101
  });
102
+ ```
138
103
 
139
- // Vercel
104
+ You can also combine both `provider` and `providers`:
105
+
106
+ ```typescript
140
107
  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
- }
108
+ provider: e2b({...}), // primary provider (first choice)
109
+ providers: [modal({...})], // fallback/secondary providers
148
110
  });
149
111
  ```
150
112
 
113
+ When both are present, `provider` is treated as the primary provider and is placed first.
114
+
151
115
  ### Sandbox Management
152
116
 
153
117
  #### `compute.sandbox.create(options?)`
@@ -171,9 +135,9 @@ const sandbox = await compute.sandbox.create({
171
135
  - `metadata?: Record<string, any>` - Custom metadata
172
136
  - `envs?: Record<string, string>` - Environment variables
173
137
  - `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
138
+ - `name?: string` - Human-readable name for the sandbox
139
+
140
+ > **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
141
 
178
142
  #### `compute.sandbox.getById(sandboxId)`
179
143
 
@@ -183,40 +147,8 @@ Get an existing sandbox by ID.
183
147
  const sandbox = await compute.sandbox.getById('sandbox-id');
184
148
  ```
185
149
 
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
150
  ### Sandbox Operations
209
151
 
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
152
  #### `sandbox.runCommand(command, options?)`
221
153
 
222
154
  Run a shell command.
@@ -297,209 +229,6 @@ Remove a file or directory.
297
229
  await sandbox.filesystem.remove('/tmp/hello.py');
298
230
  ```
299
231
 
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
232
  ## Examples
504
233
 
505
234
  ### Multi-Step Build Process
@@ -539,119 +268,39 @@ const installResult = await sandbox.runCommand('npm install', { cwd: '/app' });
539
268
  console.log('Install:', installResult.stdout);
540
269
 
541
270
  // 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
- `);
271
+ const runResult = await sandbox.runCommand('node src/index.js', { cwd: '/app' });
547
272
 
548
- console.log(runResult.output);
273
+ console.log(runResult.stdout);
549
274
 
550
275
  await sandbox.destroy();
551
276
  ```
552
277
 
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
278
  ### Using Different Providers
630
279
 
631
280
  ```typescript
632
281
  import { compute } from 'computesdk';
282
+ import { e2b } from '@computesdk/e2b';
283
+ import { modal } from '@computesdk/modal';
633
284
 
634
285
  // Use E2B for data science
635
286
  compute.setConfig({
636
- provider: 'e2b',
637
- e2b: { apiKey: process.env.E2B_API_KEY }
287
+ provider: e2b({ apiKey: process.env.E2B_API_KEY }),
638
288
  });
639
289
 
640
290
  const e2bSandbox = await compute.sandbox.create();
641
- await e2bSandbox.runCode('import pandas as pd; print(pd.__version__)');
291
+ await e2bSandbox.runCommand('python -c "import pandas as pd; print(pd.__version__)"');
642
292
  await e2bSandbox.destroy();
643
293
 
644
294
  // Switch to Modal for GPU workloads
645
295
  compute.setConfig({
646
- provider: 'modal',
647
- modal: {
296
+ provider: modal({
648
297
  tokenId: process.env.MODAL_TOKEN_ID,
649
- tokenSecret: process.env.MODAL_TOKEN_SECRET
650
- }
298
+ tokenSecret: process.env.MODAL_TOKEN_SECRET,
299
+ }),
651
300
  });
652
301
 
653
302
  const modalSandbox = await compute.sandbox.create();
654
- await modalSandbox.runCode('import torch; print(torch.cuda.is_available())');
303
+ await modalSandbox.runCommand('python -c "import torch; print(torch.cuda.is_available())"');
655
304
  await modalSandbox.destroy();
656
305
  ```
657
306
 
@@ -660,24 +309,24 @@ await modalSandbox.destroy();
660
309
  ```typescript
661
310
  try {
662
311
  const sandbox = await compute.sandbox.create();
663
- const result = await sandbox.runCode('invalid python code');
312
+ const result = await sandbox.runCommand('invalid python code');
664
313
  } catch (error) {
665
314
  console.error('Execution failed:', error.message);
666
315
 
667
316
  // Check for specific error types
668
- if (error.message.includes('No provider detected')) {
669
- console.error('Set provider credentials in environment variables');
317
+ if (error.message.includes('No provider instance configured')) {
318
+ console.error('Configure compute.setConfig({ provider: e2b({...}) }) first');
670
319
  }
671
320
  }
672
321
  ```
673
322
 
674
- ## Direct Mode (Advanced)
323
+ ## Provider Packages (Advanced)
675
324
 
676
- For advanced use cases where you want to bypass the gateway and use provider SDKs directly, see individual provider packages:
325
+ For advanced use cases where you want to use provider SDKs directly, see individual provider packages:
677
326
 
678
327
  - **[@computesdk/e2b](../e2b)** - E2B provider
679
328
  - **[@computesdk/modal](../modal)** - Modal provider
680
- - **[@computesdk/railway](../railway)** - Railway provider
329
+ - **[@computesdk/vercel](../vercel)** - Vercel provider
681
330
  - **[@computesdk/daytona](../daytona)** - Daytona provider
682
331
 
683
332
  Example direct mode usage: