@@ -385,123 +385,87 @@ export interface SandboxDeleteOptions {
385385}
386386
387387/**
388- * Abstract interface for sandbox provider implementations.
388+ * Common error codes shared across all sandbox provider implementations.
389389 *
390- * Defines the lifecycle management interface for sandbox providers. Implementations
391- * should integrate with their respective SDKs to provide standardized sandbox
392- * lifecycle operations (list, getOrCreate, delete).
393- *
394- * This interface separates lifecycle management from sandbox execution:
395- * - `SandboxProvider` handles lifecycle (list, create, delete)
396- * - `SandboxBackendProtocol` handles execution (execute, file operations)
397- *
398- * @typeParam MetadataT - Type of the metadata field in sandbox listings.
399- * Providers can define their own interface for type-safe metadata access.
390+ * These represent the core error conditions that any sandbox provider may encounter.
391+ * Provider-specific error codes should extend this type with additional codes.
400392 *
401393 * @example
402394 * ```typescript
403- * interface MyMetadata {
404- * status: "running" | "stopped";
405- * template: string;
406- * }
395+ * // Provider-specific error code type extending the common codes:
396+ * type MySandboxErrorCode = SandboxErrorCode | "CUSTOM_ERROR";
397+ * ```
398+ */
399+ export type SandboxErrorCode =
400+ /** Sandbox has not been initialized - call initialize() first */
401+ | "NOT_INITIALIZED"
402+ /** Sandbox is already initialized - cannot initialize twice */
403+ | "ALREADY_INITIALIZED"
404+ /** Command execution timed out */
405+ | "COMMAND_TIMEOUT"
406+ /** Command execution failed */
407+ | "COMMAND_FAILED"
408+ /** File operation (read/write) failed */
409+ | "FILE_OPERATION_FAILED" ;
410+
411+ const SANDBOX_ERROR_SYMBOL = Symbol . for ( "sandbox.error" ) ;
412+
413+ /**
414+ * Custom error class for sandbox operations.
407415 *
408- * class MySandboxProvider implements SandboxProvider<MyMetadata> {
409- * async list(options?: SandboxListOptions): Promise<SandboxListResponse<MyMetadata>> {
410- * // Query provider API
411- * return { items: [...], cursor: null };
412- * }
416+ * @param message - Human-readable error description
417+ * @param code - Structured error code for programmatic handling
418+ * @returns SandboxError with message and code
413419 *
414- * async getOrCreate(options?: SandboxGetOrCreateOptions): Promise<SandboxBackendProtocol> {
415- * if (options?.sandboxId) {
416- * return this.get(options.sandboxId);
420+ * @example
421+ * ```typescript
422+ * try {
423+ * await sandbox.execute("some command");
424+ * } catch (error) {
425+ * if (error instanceof SandboxError) {
426+ * switch (error.code) {
427+ * case "NOT_INITIALIZED":
428+ * await sandbox.initialize();
429+ * break;
430+ * case "COMMAND_TIMEOUT":
431+ * console.error("Command took too long");
432+ * break;
433+ * default:
434+ * throw error;
417435 * }
418- * return this.create();
419- * }
420- *
421- * async delete(options: SandboxDeleteOptions): Promise<void> {
422- * // Idempotent - no error if already deleted
423- * await this.client.delete(options.sandboxId);
424436 * }
425437 * }
426- *
427- * // Usage
428- * const provider = new MySandboxProvider();
429- * const sandbox = await provider.getOrCreate();
430- * const result = await sandbox.execute("echo hello");
431- * await provider.delete({ sandboxId: sandbox.id });
432438 * ```
433439 */
434- export interface SandboxProvider < MetadataT = Record < string , unknown > > {
435- /**
436- * List available sandboxes with optional pagination.
437- *
438- * @param options - Optional list options including cursor for pagination
439- * @returns Paginated list of sandbox metadata
440- *
441- * @example
442- * ```typescript
443- * // First page
444- * const response = await provider.list();
445- * for (const sandbox of response.items) {
446- * console.log(sandbox.sandboxId);
447- * }
448- *
449- * // Next page if available
450- * if (response.cursor) {
451- * const nextPage = await provider.list({ cursor: response.cursor });
452- * }
453- * ```
454- */
455- list ( options ?: SandboxListOptions ) : Promise < SandboxListResponse < MetadataT > > ;
440+ export class SandboxError extends Error {
441+ /** Symbol for identifying sandbox error instances */
442+ [ SANDBOX_ERROR_SYMBOL ] = true as const ;
456443
457- /**
458- * Get an existing sandbox or create a new one.
459- *
460- * If sandboxId is provided, retrieves the existing sandbox. If the sandbox
461- * doesn't exist, throws an error (does NOT create a new one).
462- *
463- * If sandboxId is undefined, creates a new sandbox instance.
464- *
465- * @param options - Optional options including sandboxId to retrieve
466- * @returns A sandbox instance implementing SandboxBackendProtocol
467- * @throws Error if sandboxId is provided but the sandbox doesn't exist
468- *
469- * @example
470- * ```typescript
471- * // Create a new sandbox
472- * const sandbox = await provider.getOrCreate();
473- * console.log(sandbox.id); // "sb_new123"
474- *
475- * // Reconnect to existing sandbox
476- * const existing = await provider.getOrCreate({ sandboxId: "sb_new123" });
477- *
478- * // Use the sandbox
479- * const result = await sandbox.execute("node --version");
480- * ```
481- */
482- getOrCreate (
483- options ?: SandboxGetOrCreateOptions ,
484- ) : Promise < SandboxBackendProtocol > ;
444+ /** Error name for instanceof checks and logging */
445+ override readonly name : string = "SandboxError" ;
485446
486447 /**
487- * Delete a sandbox instance.
488- *
489- * This permanently destroys the sandbox and all its associated data.
490- * The operation is idempotent - calling delete on a non-existent sandbox
491- * should succeed without raising an error.
492- *
493- * @param options - Options including the sandboxId to delete
494- *
495- * @example
496- * ```typescript
497- * // Simple deletion
498- * await provider.delete({ sandboxId: "sb_123" });
448+ * Creates a new SandboxError.
499449 *
500- * // Safe to call multiple times (idempotent)
501- * await provider.delete({ sandboxId: "sb_123" }); // No error
502- * ```
450+ * @param message - Human-readable error description
451+ * @param code - Structured error code for programmatic handling
503452 */
504- delete ( options : SandboxDeleteOptions ) : Promise < void > ;
453+ constructor (
454+ message : string ,
455+ public readonly code : string ,
456+ public readonly cause ?: Error ,
457+ ) {
458+ super ( message ) ;
459+ Object . setPrototypeOf ( this , SandboxError . prototype ) ;
460+ }
461+
462+ static isInstance ( error : unknown ) : error is SandboxError {
463+ return (
464+ typeof error === "object" &&
465+ error !== null &&
466+ ( error as Record < symbol , unknown > ) [ SANDBOX_ERROR_SYMBOL ] === true
467+ ) ;
468+ }
505469}
506470
507471/**
0 commit comments