computesdk 1.13.0 → 1.16.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/dist/index.mjs CHANGED
@@ -1335,10 +1335,15 @@ var Server = class {
1335
1335
  this.stopHandler = handlers.stop;
1336
1336
  this.restartHandler = handlers.restart;
1337
1337
  this.updateStatusHandler = handlers.updateStatus;
1338
+ this.logsHandler = handlers.logs;
1338
1339
  }
1339
1340
  /**
1340
1341
  * Start a new managed server with optional supervisor settings
1341
1342
  *
1343
+ * **Install Phase:**
1344
+ * If `install` is provided, it runs blocking before `start` (e.g., "npm install").
1345
+ * The server status will be `installing` during this phase.
1346
+ *
1342
1347
  * **Restart Policies:**
1343
1348
  * - `never` (default): No automatic restart on exit
1344
1349
  * - `on-failure`: Restart only on non-zero exit code
@@ -1356,14 +1361,15 @@ var Server = class {
1356
1361
  * // Basic server
1357
1362
  * const server = await sandbox.server.start({
1358
1363
  * slug: 'web',
1359
- * command: 'npm run dev',
1364
+ * start: 'npm run dev',
1360
1365
  * path: '/app',
1361
1366
  * });
1362
1367
  *
1363
- * // With supervisor settings
1368
+ * // With install command
1364
1369
  * const server = await sandbox.server.start({
1365
1370
  * slug: 'api',
1366
- * command: 'node server.js',
1371
+ * install: 'npm install',
1372
+ * start: 'node server.js',
1367
1373
  * environment: { NODE_ENV: 'production' },
1368
1374
  * restart_policy: 'always',
1369
1375
  * max_restarts: 0, // unlimited
@@ -1415,6 +1421,29 @@ var Server = class {
1415
1421
  async updateStatus(slug, status) {
1416
1422
  await this.updateStatusHandler(slug, status);
1417
1423
  }
1424
+ /**
1425
+ * Retrieve captured output (logs) for a managed server
1426
+ * @param slug - The server slug
1427
+ * @param options - Options for log retrieval
1428
+ * @returns Server logs info
1429
+ *
1430
+ * @example
1431
+ * ```typescript
1432
+ * // Get combined logs (default)
1433
+ * const logs = await sandbox.server.logs('api');
1434
+ * console.log(logs.logs);
1435
+ *
1436
+ * // Get only stdout
1437
+ * const stdout = await sandbox.server.logs('api', { stream: 'stdout' });
1438
+ *
1439
+ * // Get only stderr
1440
+ * const stderr = await sandbox.server.logs('api', { stream: 'stderr' });
1441
+ * ```
1442
+ */
1443
+ async logs(slug, options) {
1444
+ const response = await this.logsHandler(slug, options);
1445
+ return response.data;
1446
+ }
1418
1447
  };
1419
1448
 
1420
1449
  // src/client/resources/watcher.ts
@@ -1773,6 +1802,7 @@ var Run = class {
1773
1802
  constructor(handlers) {
1774
1803
  this.codeHandler = handlers.code;
1775
1804
  this.commandHandler = handlers.command;
1805
+ this.waitHandler = handlers.wait;
1776
1806
  }
1777
1807
  /**
1778
1808
  * Execute code with automatic language detection
@@ -1796,10 +1826,38 @@ var Run = class {
1796
1826
  * @param options.background - Run in background (optional)
1797
1827
  * @param options.cwd - Working directory for the command (optional)
1798
1828
  * @param options.env - Environment variables (optional)
1829
+ * @param options.waitForCompletion - If true (with background), wait for command to complete
1799
1830
  * @returns Command execution result with stdout, stderr, exit code, and duration
1800
1831
  */
1801
1832
  async command(command, options) {
1802
- return this.commandHandler(command, options);
1833
+ const result = await this.commandHandler(command, options);
1834
+ if (options?.background && options?.waitForCompletion && result.cmdId && result.terminalId) {
1835
+ if (!this.waitHandler) {
1836
+ throw new Error("Wait handler not configured");
1837
+ }
1838
+ const waitOptions = typeof options.waitForCompletion === "object" ? options.waitForCompletion : void 0;
1839
+ return this.waitHandler(result.terminalId, result.cmdId, waitOptions);
1840
+ }
1841
+ return result;
1842
+ }
1843
+ /**
1844
+ * Wait for a background command to complete
1845
+ *
1846
+ * Uses the configured wait handler to block until the command
1847
+ * is complete or fails (typically via server-side long-polling).
1848
+ * Throws an error if the command fails or times out.
1849
+ *
1850
+ * @param terminalId - Terminal ID from background command result
1851
+ * @param cmdId - Command ID from background command result
1852
+ * @param options - Wait options passed to the handler
1853
+ * @returns Command result with final status
1854
+ * @throws Error if command fails or times out
1855
+ */
1856
+ async waitForCompletion(terminalId, cmdId, options) {
1857
+ if (!this.waitHandler) {
1858
+ throw new Error("Wait handler not configured");
1859
+ }
1860
+ return this.waitHandler(terminalId, cmdId, options);
1803
1861
  }
1804
1862
  };
1805
1863
 
@@ -1856,18 +1914,25 @@ var Overlay = class {
1856
1914
  /**
1857
1915
  * Create a new overlay from a template directory
1858
1916
  *
1859
- * The overlay symlinks files from the source directory into the target path,
1860
- * allowing instant access to template files. Heavy directories (node_modules,
1861
- * .venv, etc.) are copied in the background.
1917
+ * The overlay copies files from the source directory into the target path
1918
+ * for better isolation. Heavy directories (node_modules, .venv, etc.) are
1919
+ * copied in the background. Use the `ignore` option to exclude files/directories.
1862
1920
  *
1863
1921
  * @param options - Overlay creation options
1864
1922
  * @param options.source - Absolute path to source directory
1865
1923
  * @param options.target - Relative path in sandbox
1924
+ * @param options.ignore - Glob patterns to ignore (e.g., ["node_modules", "*.log"])
1925
+ * @param options.waitForCompletion - If true or options object, wait for background copy to complete
1866
1926
  * @returns Overlay info with copy status
1867
1927
  */
1868
1928
  async create(options) {
1869
1929
  const response = await this.createHandler(options);
1870
- return this.toOverlayInfo(response);
1930
+ const overlay = this.toOverlayInfo(response);
1931
+ if (options.waitForCompletion) {
1932
+ const waitOptions = typeof options.waitForCompletion === "object" ? options.waitForCompletion : void 0;
1933
+ return this.waitForCompletion(overlay.id, waitOptions);
1934
+ }
1935
+ return overlay;
1871
1936
  }
1872
1937
  /**
1873
1938
  * List all overlays for the current sandbox
@@ -1896,6 +1961,48 @@ var Overlay = class {
1896
1961
  async destroy(id) {
1897
1962
  return this.destroyHandler(id);
1898
1963
  }
1964
+ /**
1965
+ * Wait for an overlay's background copy to complete
1966
+ *
1967
+ * Polls the overlay status with exponential backoff until the copy
1968
+ * is complete or fails. Throws an error if the copy fails or times out.
1969
+ *
1970
+ * @param id - Overlay ID
1971
+ * @param options - Polling options
1972
+ * @returns Overlay info with final copy status
1973
+ * @throws Error if copy fails or times out
1974
+ */
1975
+ async waitForCompletion(id, options = {}) {
1976
+ const maxRetries = options.maxRetries ?? 60;
1977
+ const initialDelayMs = options.initialDelayMs ?? 500;
1978
+ const maxDelayMs = options.maxDelayMs ?? 5e3;
1979
+ const backoffFactor = options.backoffFactor ?? 1.5;
1980
+ let currentDelay = initialDelayMs;
1981
+ for (let i = 0; i < maxRetries; i++) {
1982
+ const overlay = await this.retrieve(id);
1983
+ if (overlay.copyStatus === "complete") {
1984
+ return overlay;
1985
+ }
1986
+ if (overlay.copyStatus === "failed") {
1987
+ throw new Error(
1988
+ `Overlay copy failed: ${overlay.copyError || "Unknown error"}
1989
+ Overlay ID: ${id}`
1990
+ );
1991
+ }
1992
+ if (i < maxRetries - 1) {
1993
+ await new Promise((resolve) => setTimeout(resolve, currentDelay));
1994
+ currentDelay = Math.min(currentDelay * backoffFactor, maxDelayMs);
1995
+ }
1996
+ }
1997
+ const finalOverlay = await this.retrieve(id);
1998
+ throw new Error(
1999
+ `Overlay copy timed out after ${maxRetries} attempts.
2000
+ Overlay ID: ${id}
2001
+ Current status: ${finalOverlay.copyStatus}
2002
+
2003
+ Try increasing maxRetries or check if the source directory is very large.`
2004
+ );
2005
+ }
1899
2006
  /**
1900
2007
  * Convert API response to OverlayInfo
1901
2008
  */
@@ -1906,8 +2013,8 @@ var Overlay = class {
1906
2013
  target: response.target,
1907
2014
  createdAt: response.created_at,
1908
2015
  stats: {
1909
- symlinkedFiles: response.stats.symlinked_files,
1910
- symlinkedDirs: response.stats.symlinked_dirs,
2016
+ copiedFiles: response.stats.copied_files,
2017
+ copiedDirs: response.stats.copied_dirs,
1911
2018
  skipped: response.stats.skipped
1912
2019
  },
1913
2020
  copyStatus: this.validateCopyStatus(response.copy_status),
@@ -1939,6 +2046,7 @@ function isCommandExitError(error) {
1939
2046
  }
1940
2047
 
1941
2048
  // src/client/index.ts
2049
+ var MAX_TUNNEL_TIMEOUT_SECONDS = 300;
1942
2050
  var Sandbox = class {
1943
2051
  constructor(config) {
1944
2052
  this._token = null;
@@ -2057,8 +2165,15 @@ var Sandbox = class {
2057
2165
  stdout: result.data.stdout,
2058
2166
  stderr: result.data.stderr,
2059
2167
  exitCode: result.data.exit_code ?? 0,
2060
- durationMs: result.data.duration_ms ?? 0
2168
+ durationMs: result.data.duration_ms ?? 0,
2169
+ // Include cmdId and terminalId for background commands
2170
+ cmdId: result.data.cmd_id,
2171
+ terminalId: result.data.terminal_id,
2172
+ status: result.data.status
2061
2173
  };
2174
+ },
2175
+ wait: async (terminalId, cmdId, options) => {
2176
+ return this.waitForCommandCompletion(terminalId, cmdId, options);
2062
2177
  }
2063
2178
  });
2064
2179
  this.server = new Server({
@@ -2071,7 +2186,8 @@ var Sandbox = class {
2071
2186
  restart: async (slug) => this.restartServer(slug),
2072
2187
  updateStatus: async (slug, status) => {
2073
2188
  await this.updateServerStatus(slug, status);
2074
- }
2189
+ },
2190
+ logs: async (slug, options) => this.getServerLogs(slug, options)
2075
2191
  });
2076
2192
  this.watcher = new Watcher({
2077
2193
  create: async (path, options) => this.createWatcher(path, options),
@@ -2728,6 +2844,47 @@ API request failed (${response.status}): ${error}`
2728
2844
  const endpoint = `/terminals/${terminalId}/commands/${cmdId}/wait${params ? `?${params}` : ""}`;
2729
2845
  return this.request(endpoint);
2730
2846
  }
2847
+ /**
2848
+ * Wait for a background command to complete using long-polling
2849
+ *
2850
+ * Uses the server's long-polling endpoint with configurable timeout.
2851
+ * The tunnel supports up to 5 minutes (300 seconds) via X-Request-Timeout header.
2852
+ *
2853
+ * @param terminalId - The terminal ID
2854
+ * @param cmdId - The command ID
2855
+ * @param options - Wait options (timeoutSeconds, default 300)
2856
+ * @returns Command result with final status
2857
+ * @throws Error if command fails or times out
2858
+ * @internal
2859
+ */
2860
+ async waitForCommandCompletion(terminalId, cmdId, options) {
2861
+ const timeoutSeconds = options?.timeoutSeconds ?? MAX_TUNNEL_TIMEOUT_SECONDS;
2862
+ const response = await this.waitForCommandWithTimeout(terminalId, cmdId, timeoutSeconds);
2863
+ return {
2864
+ stdout: response.data.stdout,
2865
+ stderr: response.data.stderr,
2866
+ exitCode: response.data.exit_code ?? 0,
2867
+ durationMs: response.data.duration_ms ?? 0,
2868
+ cmdId: response.data.cmd_id,
2869
+ terminalId,
2870
+ status: response.data.status
2871
+ };
2872
+ }
2873
+ /**
2874
+ * Wait for a command with extended timeout support
2875
+ * Uses X-Request-Timeout header for tunnel timeout configuration
2876
+ * @internal
2877
+ */
2878
+ async waitForCommandWithTimeout(terminalId, cmdId, timeoutSeconds) {
2879
+ const params = new URLSearchParams({ timeout: timeoutSeconds.toString() });
2880
+ const endpoint = `/terminals/${terminalId}/commands/${cmdId}/wait?${params}`;
2881
+ const requestTimeout = Math.min(timeoutSeconds, MAX_TUNNEL_TIMEOUT_SECONDS);
2882
+ return this.request(endpoint, {
2883
+ headers: {
2884
+ "X-Request-Timeout": requestTimeout.toString()
2885
+ }
2886
+ });
2887
+ }
2731
2888
  // ============================================================================
2732
2889
  // File Watchers
2733
2890
  // ============================================================================
@@ -2916,7 +3073,8 @@ API request failed (${response.status}): ${error}`
2916
3073
  *
2917
3074
  * @param options - Server configuration
2918
3075
  * @param options.slug - Unique server identifier
2919
- * @param options.command - Command to start the server
3076
+ * @param options.install - Install command (optional, runs blocking before start, e.g., "npm install")
3077
+ * @param options.start - Command to start the server (e.g., "npm run dev")
2920
3078
  * @param options.path - Working directory (optional)
2921
3079
  * @param options.env_file - Path to .env file relative to path (optional)
2922
3080
  * @param options.environment - Inline environment variables (merged with env_file if both provided)
@@ -2930,14 +3088,15 @@ API request failed (${response.status}): ${error}`
2930
3088
  * // Basic server
2931
3089
  * await sandbox.startServer({
2932
3090
  * slug: 'web',
2933
- * command: 'npm run dev',
3091
+ * start: 'npm run dev',
2934
3092
  * path: '/app',
2935
3093
  * });
2936
3094
  *
2937
- * // With supervisor settings
3095
+ * // With install command and supervisor settings
2938
3096
  * await sandbox.startServer({
2939
3097
  * slug: 'api',
2940
- * command: 'node server.js',
3098
+ * install: 'npm install',
3099
+ * start: 'node server.js',
2941
3100
  * path: '/app',
2942
3101
  * environment: { NODE_ENV: 'production', PORT: '3000' },
2943
3102
  * restart_policy: 'on-failure',
@@ -2984,6 +3143,21 @@ API request failed (${response.status}): ${error}`
2984
3143
  }
2985
3144
  );
2986
3145
  }
3146
+ /**
3147
+ * Get logs for a managed server
3148
+ * @param slug - Server slug
3149
+ * @param options - Options for log retrieval
3150
+ */
3151
+ async getServerLogs(slug, options) {
3152
+ const params = new URLSearchParams();
3153
+ if (options?.stream) {
3154
+ params.set("stream", options.stream);
3155
+ }
3156
+ const queryString = params.toString();
3157
+ return this.request(
3158
+ `/servers/${encodeURIComponent(slug)}/logs${queryString ? `?${queryString}` : ""}`
3159
+ );
3160
+ }
2987
3161
  /**
2988
3162
  * Update server status (internal use)
2989
3163
  * @param slug - Server slug
@@ -3265,7 +3439,9 @@ var PROVIDER_HEADERS = {
3265
3439
  tokenSecret: "X-Modal-Token-Secret"
3266
3440
  },
3267
3441
  railway: {
3268
- apiToken: "X-Railway-API-Token"
3442
+ apiToken: "X-Railway-API-Key",
3443
+ projectId: "X-Railway-Project-ID",
3444
+ environmentId: "X-Railway-Environment-ID"
3269
3445
  },
3270
3446
  daytona: {
3271
3447
  apiKey: "X-Daytona-API-Key"