computesdk 1.11.0 → 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
@@ -283,6 +283,209 @@ Remove a file or directory.
283
283
  await sandbox.filesystem.remove('/tmp/hello.py');
284
284
  ```
285
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
+
286
489
  ## Examples
287
490
 
288
491
  ### Data Science Workflow
@@ -391,6 +594,82 @@ console.log(runResult.stdout);
391
594
  await sandbox.destroy();
392
595
  ```
393
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
+
394
673
  ### Using Different Providers
395
674
 
396
675
  ```typescript
package/dist/index.js CHANGED
@@ -3620,6 +3620,34 @@ async function gatewayFetch(url, config, options = {}) {
3620
3620
  throw error;
3621
3621
  }
3622
3622
  }
3623
+ async function waitForSandboxStatus(config, endpoint, body, options = {}) {
3624
+ const maxWaitMs = options.maxWaitMs ?? 6e4;
3625
+ const initialDelayMs = 500;
3626
+ const maxDelayMs = 2e3;
3627
+ const backoffFactor = 1.5;
3628
+ const startTime = Date.now();
3629
+ let currentDelay = initialDelayMs;
3630
+ while (Date.now() - startTime < maxWaitMs) {
3631
+ const result = await gatewayFetch(endpoint, config, {
3632
+ method: "POST",
3633
+ body: JSON.stringify(body)
3634
+ });
3635
+ if (!result.success || !result.data) {
3636
+ return result;
3637
+ }
3638
+ if (result.data.status !== "creating") {
3639
+ return result;
3640
+ }
3641
+ if (process.env.COMPUTESDK_DEBUG) {
3642
+ console.log(`[Compute] Sandbox still creating, waiting ${currentDelay}ms...`);
3643
+ }
3644
+ await new Promise((resolve) => setTimeout(resolve, currentDelay));
3645
+ currentDelay = Math.min(currentDelay * backoffFactor, maxDelayMs);
3646
+ }
3647
+ throw new Error(
3648
+ `Sandbox is still being created after ${maxWaitMs}ms. This may indicate the sandbox failed to start. Check your provider dashboard.`
3649
+ );
3650
+ }
3623
3651
  var ComputeManager = class {
3624
3652
  constructor() {
3625
3653
  this.config = null;
@@ -3707,14 +3735,15 @@ var ComputeManager = class {
3707
3735
  findOrCreate: async (options) => {
3708
3736
  const config = this.getGatewayConfig();
3709
3737
  const { name, namespace, ...restOptions } = options;
3710
- const result = await gatewayFetch(`${config.gatewayUrl}/v1/sandboxes/find-or-create`, config, {
3711
- method: "POST",
3712
- body: JSON.stringify({
3738
+ const result = await waitForSandboxStatus(
3739
+ config,
3740
+ `${config.gatewayUrl}/v1/sandboxes/find-or-create`,
3741
+ {
3713
3742
  namespace: namespace || "default",
3714
3743
  name,
3715
3744
  ...restOptions
3716
- })
3717
- });
3745
+ }
3746
+ );
3718
3747
  if (!result.success || !result.data) {
3719
3748
  throw new Error(`Gateway returned invalid response`);
3720
3749
  }
@@ -3744,13 +3773,14 @@ var ComputeManager = class {
3744
3773
  */
3745
3774
  find: async (options) => {
3746
3775
  const config = this.getGatewayConfig();
3747
- const result = await gatewayFetch(`${config.gatewayUrl}/v1/sandboxes/find`, config, {
3748
- method: "POST",
3749
- body: JSON.stringify({
3776
+ const result = await waitForSandboxStatus(
3777
+ config,
3778
+ `${config.gatewayUrl}/v1/sandboxes/find`,
3779
+ {
3750
3780
  namespace: options.namespace || "default",
3751
3781
  name: options.name
3752
- })
3753
- });
3782
+ }
3783
+ );
3754
3784
  if (!result.success || !result.data) {
3755
3785
  return null;
3756
3786
  }