@hanzo/runtime 0.0.0-dev

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/src/Daytona.ts ADDED
@@ -0,0 +1,644 @@
1
+ /*
2
+ * Copyright 2025 Daytona Platforms Inc.
3
+ * SPDX-License-Identifier: Apache-2.0
4
+ */
5
+
6
+ import {
7
+ Configuration,
8
+ SnapshotsApi,
9
+ ObjectStorageApi,
10
+ SandboxApi,
11
+ SandboxState,
12
+ CreateSandboxTargetEnum,
13
+ ToolboxApi,
14
+ VolumesApi,
15
+ SandboxVolume,
16
+ } from '@daytonaio/api-client'
17
+ import axios, { AxiosError } from 'axios'
18
+ import * as dotenv from 'dotenv'
19
+ import { SandboxPythonCodeToolbox } from './code-toolbox/SandboxPythonCodeToolbox'
20
+ import { SandboxTsCodeToolbox } from './code-toolbox/SandboxTsCodeToolbox'
21
+ import { DaytonaError, DaytonaNotFoundError } from './errors/DaytonaError'
22
+ import { Image } from './Image'
23
+ import { Sandbox } from './Sandbox'
24
+ import { SnapshotService } from './Snapshot'
25
+ import { VolumeService } from './Volume'
26
+ import * as packageJson from '../package.json'
27
+ import { processStreamingResponse } from './utils/Stream'
28
+
29
+ /**
30
+ * Represents a volume mount for a Sandbox.
31
+ *
32
+ * @interface
33
+ * @property {string} volumeId - ID of the Volume to mount
34
+ * @property {string} mountPath - Path on the Sandbox to mount the Volume
35
+ */
36
+
37
+ export interface VolumeMount extends SandboxVolume {
38
+ volumeId: string
39
+ mountPath: string
40
+ }
41
+
42
+ /**
43
+ * Configuration options for initializing the Daytona client.
44
+ *
45
+ * @interface
46
+ * @property {string} apiKey - API key for authentication with the Daytona API
47
+ * @property {string} jwtToken - JWT token for authentication with the Daytona API. If not set, it must be provided
48
+ * via the environment variable `DAYTONA_JWT_TOKEN`, or an API key must be provided instead.
49
+ * @property {string} organizationId - Organization ID used for JWT-based authentication. Required if a JWT token
50
+ * is provided, and must be set either here or in the environment variable `DAYTONA_ORGANIZATION_ID`.
51
+ * @property {string} apiUrl - URL of the Daytona API. Defaults to 'https://app.daytona.io/api'
52
+ * if not set here and not set in environment variable DAYTONA_API_URL.
53
+ * @property {string} target - Target location for Sandboxes
54
+ *
55
+ * @example
56
+ * const config: DaytonaConfig = {
57
+ * apiKey: "your-api-key",
58
+ * apiUrl: "https://your-api.com",
59
+ * target: "us"
60
+ * };
61
+ * const daytona = new Daytona(config);
62
+ */
63
+ export interface DaytonaConfig {
64
+ /** API key for authentication with the Daytona API */
65
+ apiKey?: string
66
+ /** JWT token for authentication with the Daytona API */
67
+ jwtToken?: string
68
+ /** Organization ID for authentication with the Daytona API */
69
+ organizationId?: string
70
+ /** URL of the Daytona API.
71
+ */
72
+ apiUrl?: string
73
+ /**
74
+ * @deprecated Use `apiUrl` instead. This property will be removed in future versions.
75
+ */
76
+ serverUrl?: string
77
+ /** Target environment for sandboxes */
78
+ target?: string
79
+ }
80
+
81
+ /**
82
+ * Supported programming languages for code execution
83
+ */
84
+ export enum CodeLanguage {
85
+ PYTHON = 'python',
86
+ TYPESCRIPT = 'typescript',
87
+ JAVASCRIPT = 'javascript',
88
+ }
89
+
90
+ /**
91
+ * Resource allocation for a Sandbox.
92
+ *
93
+ * @interface
94
+ * @property {number} [cpu] - CPU allocation for the Sandbox in cores
95
+ * @property {number} [gpu] - GPU allocation for the Sandbox in units
96
+ * @property {number} [memory] - Memory allocation for the Sandbox in GiB
97
+ * @property {number} [disk] - Disk space allocation for the Sandbox in GiB
98
+ *
99
+ * @example
100
+ * const resources: SandboxResources = {
101
+ * cpu: 2,
102
+ * memory: 4, // 4GiB RAM
103
+ * disk: 20 // 20GiB disk
104
+ * };
105
+ */
106
+ export interface Resources {
107
+ /** CPU allocation for the Sandbox */
108
+ cpu?: number
109
+ /** GPU allocation for the Sandbox */
110
+ gpu?: number
111
+ /** Memory allocation for the Sandbox in GiB */
112
+ memory?: number
113
+ /** Disk space allocation for the Sandbox in GiB */
114
+ disk?: number
115
+ }
116
+
117
+ /**
118
+ * Base parameters for creating a new Sandbox.
119
+ *
120
+ * @interface
121
+ * @property {string} [user] - Optional os user to use for the Sandbox
122
+ * @property {CodeLanguage | string} [language] - Programming language for direct code execution
123
+ * @property {Record<string, string>} [envVars] - Optional environment variables to set in the Sandbox
124
+ * @property {Record<string, string>} [labels] - Sandbox labels
125
+ * @property {boolean} [public] - Is the Sandbox port preview public
126
+ * @property {number} [autoStopInterval] - Auto-stop interval in minutes (0 means disabled). Default is 15 minutes.
127
+ * @property {number} [autoArchiveInterval] - Auto-archive interval in minutes (0 means the maximum interval will be used). Default is 7 days.
128
+ * @property {number} [autoDeleteInterval] - Auto-delete interval in minutes (negative value means disabled, 0 means delete immediately upon stopping). By default, auto-delete is disabled.
129
+ */
130
+ export type CreateSandboxBaseParams = {
131
+ user?: string
132
+ language?: CodeLanguage | string
133
+ envVars?: Record<string, string>
134
+ labels?: Record<string, string>
135
+ public?: boolean
136
+ autoStopInterval?: number
137
+ autoArchiveInterval?: number
138
+ autoDeleteInterval?: number
139
+ volumes?: VolumeMount[]
140
+ }
141
+
142
+ /**
143
+ * Parameters for creating a new Sandbox.
144
+ *
145
+ * @interface
146
+ * @property {string | Image} [image] - Custom Docker image to use for the Sandbox. If an Image object is provided,
147
+ * the image will be dynamically built.
148
+ * @property {Resources} [resources] - Resource allocation for the Sandbox. If not provided, sandbox will
149
+ * have default resources.
150
+ */
151
+ export type CreateSandboxFromImageParams = CreateSandboxBaseParams & {
152
+ image: string | Image
153
+ resources?: Resources
154
+ }
155
+
156
+ /**
157
+ * Parameters for creating a new Sandbox from a snapshot.
158
+ *
159
+ * @interface
160
+ * @property {string} [snapshot] - Name of the snapshot to use for the Sandbox.
161
+ */
162
+ export type CreateSandboxFromSnapshotParams = CreateSandboxBaseParams & {
163
+ snapshot?: string
164
+ }
165
+
166
+ /**
167
+ * Filter for Sandboxes.
168
+ *
169
+ * @interface
170
+ * @property {string} [id] - The ID of the Sandbox to retrieve
171
+ * @property {Record<string, string>} [labels] - Labels to filter Sandboxes
172
+ */
173
+ export type SandboxFilter = {
174
+ id?: string
175
+ labels?: Record<string, string>
176
+ }
177
+
178
+ /**
179
+ * Main class for interacting with the Daytona API.
180
+ * Provides methods for creating, managing, and interacting with Daytona Sandboxes.
181
+ * Can be initialized either with explicit configuration or using environment variables.
182
+ *
183
+ * @property {VolumeService} volume - Service for managing Daytona Volumes
184
+ * @property {SnapshotService} snapshot - Service for managing Daytona Snapshots
185
+ *
186
+ * @example
187
+ * // Using environment variables
188
+ * // Uses DAYTONA_API_KEY, DAYTONA_API_URL, DAYTONA_TARGET
189
+ * const daytona = new Daytona();
190
+ * const sandbox = await daytona.create();
191
+ *
192
+ * @example
193
+ * // Using explicit configuration
194
+ * const config: DaytonaConfig = {
195
+ * apiKey: "your-api-key",
196
+ * apiUrl: "https://your-api.com",
197
+ * target: "us"
198
+ * };
199
+ * const daytona = new Daytona(config);
200
+ *
201
+ * @class
202
+ */
203
+ export class Daytona {
204
+ private readonly sandboxApi: SandboxApi
205
+ private readonly toolboxApi: ToolboxApi
206
+ private readonly objectStorageApi: ObjectStorageApi
207
+ private readonly target?: string
208
+ private readonly apiKey?: string
209
+ private readonly jwtToken?: string
210
+ private readonly organizationId?: string
211
+ private readonly apiUrl: string
212
+ public readonly volume: VolumeService
213
+ public readonly snapshot: SnapshotService
214
+
215
+ /**
216
+ * Creates a new Daytona client instance.
217
+ *
218
+ * @param {DaytonaConfig} [config] - Configuration options
219
+ * @throws {DaytonaError} - `DaytonaError` - When API key is missing
220
+ */
221
+ constructor(config?: DaytonaConfig) {
222
+ let apiUrl: string | undefined
223
+ if (config) {
224
+ this.apiKey = !config?.apiKey && config?.jwtToken ? undefined : config?.apiKey
225
+ this.jwtToken = config?.jwtToken
226
+ this.organizationId = config?.organizationId
227
+ apiUrl = config?.apiUrl || config?.serverUrl
228
+ this.target = config?.target
229
+ }
230
+
231
+ if (
232
+ !config ||
233
+ (!(this.apiKey && apiUrl && this.target) && !(this.jwtToken && this.organizationId && apiUrl && this.target))
234
+ ) {
235
+ dotenv.config()
236
+ dotenv.config({ path: '.env.local', override: true })
237
+ this.apiKey = this.apiKey || (this.jwtToken ? undefined : process?.env['DAYTONA_API_KEY'])
238
+ this.jwtToken = this.jwtToken || process?.env['DAYTONA_JWT_TOKEN']
239
+ this.organizationId = this.organizationId || process?.env['DAYTONA_ORGANIZATION_ID']
240
+ apiUrl =
241
+ apiUrl || process?.env['DAYTONA_API_URL'] || process?.env['DAYTONA_SERVER_URL'] || 'https://app.daytona.io/api'
242
+ this.target = this.target || process?.env['DAYTONA_TARGET']
243
+
244
+ if (process?.env['DAYTONA_SERVER_URL'] && !process?.env['DAYTONA_API_URL']) {
245
+ console.warn(
246
+ '[Deprecation Warning] Environment variable `DAYTONA_SERVER_URL` is deprecated and will be removed in future versions. Use `DAYTONA_API_URL` instead.',
247
+ )
248
+ }
249
+ }
250
+
251
+ this.apiUrl = apiUrl
252
+
253
+ const orgHeader: Record<string, string> = {}
254
+ if (!this.apiKey) {
255
+ if (!this.organizationId) {
256
+ throw new DaytonaError('Organization ID is required when using JWT token')
257
+ }
258
+ orgHeader['X-Daytona-Organization-ID'] = this.organizationId
259
+ }
260
+
261
+ const configuration = new Configuration({
262
+ basePath: this.apiUrl,
263
+ baseOptions: {
264
+ headers: {
265
+ Authorization: `Bearer ${this.apiKey || this.jwtToken}`,
266
+ 'X-Daytona-Source': 'typescript-sdk',
267
+ 'X-Daytona-SDK-Version': packageJson.version,
268
+ ...orgHeader,
269
+ },
270
+ },
271
+ })
272
+
273
+ const axiosInstance = axios.create({
274
+ timeout: 24 * 60 * 60 * 1000, // 24 hours
275
+ })
276
+ axiosInstance.interceptors.response.use(
277
+ (response) => {
278
+ return response
279
+ },
280
+ (error) => {
281
+ let errorMessage: string
282
+
283
+ if (error instanceof AxiosError && error.message.includes('timeout of')) {
284
+ errorMessage = 'Operation timed out'
285
+ } else {
286
+ errorMessage = error.response?.data?.message || error.response?.data || error.message || String(error)
287
+ }
288
+
289
+ try {
290
+ errorMessage = JSON.stringify(errorMessage)
291
+ } catch {
292
+ errorMessage = String(errorMessage)
293
+ }
294
+
295
+ switch (error.response?.data?.statusCode) {
296
+ case 404:
297
+ throw new DaytonaNotFoundError(errorMessage)
298
+ default:
299
+ throw new DaytonaError(errorMessage)
300
+ }
301
+ },
302
+ )
303
+
304
+ this.sandboxApi = new SandboxApi(configuration, '', axiosInstance)
305
+ this.toolboxApi = new ToolboxApi(configuration, '', axiosInstance)
306
+ this.objectStorageApi = new ObjectStorageApi(configuration, '', axiosInstance)
307
+ this.volume = new VolumeService(new VolumesApi(configuration, '', axiosInstance))
308
+ this.snapshot = new SnapshotService(new SnapshotsApi(configuration, '', axiosInstance), this.objectStorageApi)
309
+ }
310
+
311
+ /**
312
+ * Creates Sandboxes from specified or default snapshot. You can specify various parameters,
313
+ * including language, image, environment variables, and volumes.
314
+ *
315
+ * @param {CreateSandboxFromSnapshotParams} [params] - Parameters for Sandbox creation from snapshot
316
+ * @param {object} [options] - Options for the create operation
317
+ * @param {number} [options.timeout] - Timeout in seconds (0 means no timeout, default is 60)
318
+ * @returns {Promise<Sandbox>} The created Sandbox instance
319
+ *
320
+ * @example
321
+ * const sandbox = await daytona.create();
322
+ *
323
+ * @example
324
+ * // Create a custom sandbox
325
+ * const params: CreateSandboxFromSnapshotParams = {
326
+ * language: 'typescript',
327
+ * snapshot: 'my-snapshot-id',
328
+ * envVars: {
329
+ * NODE_ENV: 'development',
330
+ * DEBUG: 'true'
331
+ * },
332
+ * autoStopInterval: 60,
333
+ * autoArchiveInterval: 60,
334
+ * autoDeleteInterval: 120
335
+ * };
336
+ * const sandbox = await daytona.create(params, { timeout: 100 });
337
+ */
338
+ public async create(params?: CreateSandboxFromSnapshotParams, options?: { timeout?: number }): Promise<Sandbox>
339
+ /**
340
+ * Creates Sandboxes from specified image available on some registry or declarative Daytona Image. You can specify various parameters,
341
+ * including resources, language, image, environment variables, and volumes. Daytona creates snapshot from
342
+ * provided image and uses it to create Sandbox.
343
+ *
344
+ * @param {CreateSandboxFromImageParams} [params] - Parameters for Sandbox creation from image
345
+ * @param {object} [options] - Options for the create operation
346
+ * @param {number} [options.timeout] - Timeout in seconds (0 means no timeout, default is 60)
347
+ * @param {function} [options.onSnapshotCreateLogs] - Callback function to handle snapshot creation logs.
348
+ * @returns {Promise<Sandbox>} The created Sandbox instance
349
+ *
350
+ * @example
351
+ * const sandbox = await daytona.create({ image: 'debian:12.9' }, { timeout: 90, onSnapshotCreateLogs: console.log });
352
+ *
353
+ * @example
354
+ * // Create a custom sandbox
355
+ * const image = Image.base('alpine:3.18').pipInstall('numpy');
356
+ * const params: CreateSandboxFromImageParams = {
357
+ * language: 'typescript',
358
+ * image,
359
+ * envVars: {
360
+ * NODE_ENV: 'development',
361
+ * DEBUG: 'true'
362
+ * },
363
+ * resources: {
364
+ * cpu: 2,
365
+ * memory: 4 // 4GB RAM
366
+ * },
367
+ * autoStopInterval: 60,
368
+ * autoArchiveInterval: 60,
369
+ * autoDeleteInterval: 120
370
+ * };
371
+ * const sandbox = await daytona.create(params, { timeout: 100, onSnapshotCreateLogs: console.log });
372
+ */
373
+ public async create(
374
+ params?: CreateSandboxFromImageParams,
375
+ options?: { onSnapshotCreateLogs?: (chunk: string) => void; timeout?: number },
376
+ ): Promise<Sandbox>
377
+ public async create(
378
+ params?: CreateSandboxFromSnapshotParams | CreateSandboxFromImageParams,
379
+ options: { onSnapshotCreateLogs?: (chunk: string) => void; timeout?: number } = { timeout: 60 },
380
+ ): Promise<Sandbox> {
381
+ const startTime = Date.now()
382
+
383
+ options = typeof options === 'number' ? { timeout: options } : { ...options }
384
+ if (options.timeout == undefined || options.timeout == null) {
385
+ options.timeout = 60
386
+ }
387
+
388
+ if (params == null) {
389
+ params = { language: 'python' }
390
+ }
391
+
392
+ const labels = params.labels || {}
393
+ if (params.language) {
394
+ labels['code-toolbox-language'] = params.language
395
+ }
396
+
397
+ if (options.timeout < 0) {
398
+ throw new DaytonaError('Timeout must be a non-negative number')
399
+ }
400
+
401
+ if (
402
+ params.autoStopInterval !== undefined &&
403
+ (!Number.isInteger(params.autoStopInterval) || params.autoStopInterval < 0)
404
+ ) {
405
+ throw new DaytonaError('autoStopInterval must be a non-negative integer')
406
+ }
407
+
408
+ if (
409
+ params.autoArchiveInterval !== undefined &&
410
+ (!Number.isInteger(params.autoArchiveInterval) || params.autoArchiveInterval < 0)
411
+ ) {
412
+ throw new DaytonaError('autoArchiveInterval must be a non-negative integer')
413
+ }
414
+
415
+ const codeToolbox = this.getCodeToolbox(params.language as CodeLanguage)
416
+
417
+ try {
418
+ let buildInfo: any | undefined
419
+ let snapshot: string | undefined
420
+ let resources: Resources | undefined
421
+
422
+ if ('snapshot' in params) {
423
+ snapshot = params.snapshot
424
+ }
425
+
426
+ if ('image' in params) {
427
+ if (typeof params.image === 'string') {
428
+ buildInfo = {
429
+ dockerfileContent: Image.base(params.image).dockerfile,
430
+ }
431
+ } else if (params.image instanceof Image) {
432
+ const contextHashes = await SnapshotService.processImageContext(this.objectStorageApi, params.image)
433
+ buildInfo = {
434
+ contextHashes,
435
+ dockerfileContent: params.image.dockerfile,
436
+ }
437
+ }
438
+ }
439
+
440
+ if ('resources' in params) {
441
+ resources = params.resources
442
+ }
443
+
444
+ const response = await this.sandboxApi.createSandbox(
445
+ {
446
+ snapshot: snapshot,
447
+ buildInfo,
448
+ user: params.user,
449
+ env: params.envVars || {},
450
+ labels: labels,
451
+ public: params.public,
452
+ target: this.target as CreateSandboxTargetEnum,
453
+ cpu: resources?.cpu,
454
+ gpu: resources?.gpu,
455
+ memory: resources?.memory,
456
+ disk: resources?.disk,
457
+ autoStopInterval: params.autoStopInterval,
458
+ autoArchiveInterval: params.autoArchiveInterval,
459
+ autoDeleteInterval: params.autoDeleteInterval,
460
+ volumes: params.volumes,
461
+ },
462
+ undefined,
463
+ {
464
+ timeout: options.timeout * 1000,
465
+ },
466
+ )
467
+
468
+ let sandboxInstance = response.data
469
+
470
+ if (sandboxInstance.state === SandboxState.PENDING_BUILD && options.onSnapshotCreateLogs) {
471
+ const terminalStates: SandboxState[] = [
472
+ SandboxState.STARTED,
473
+ SandboxState.STARTING,
474
+ SandboxState.ERROR,
475
+ SandboxState.BUILD_FAILED,
476
+ ]
477
+
478
+ while (sandboxInstance.state === SandboxState.PENDING_BUILD) {
479
+ await new Promise((resolve) => setTimeout(resolve, 1000))
480
+ sandboxInstance = (await this.sandboxApi.getSandbox(sandboxInstance.id)).data
481
+ }
482
+
483
+ await processStreamingResponse(
484
+ () => this.sandboxApi.getBuildLogs(sandboxInstance.id, undefined, true, { responseType: 'stream' }),
485
+ (chunk) => options.onSnapshotCreateLogs?.(chunk.trimEnd()),
486
+ async () => {
487
+ sandboxInstance = (await this.sandboxApi.getSandbox(sandboxInstance.id)).data
488
+ return sandboxInstance.state !== undefined && terminalStates.includes(sandboxInstance.state)
489
+ },
490
+ )
491
+ }
492
+
493
+ const sandbox = new Sandbox(sandboxInstance, this.sandboxApi, this.toolboxApi, codeToolbox)
494
+
495
+ if (sandbox.state !== 'started') {
496
+ const timeElapsed = Date.now() - startTime
497
+ await sandbox.waitUntilStarted(options.timeout ? options.timeout - timeElapsed / 1000 : 0)
498
+ }
499
+
500
+ return sandbox
501
+ } catch (error) {
502
+ if (error instanceof DaytonaError && error.message.includes('Operation timed out')) {
503
+ const errMsg = `Failed to create and start sandbox within ${options.timeout} seconds. Operation timed out.`
504
+ throw new DaytonaError(errMsg)
505
+ }
506
+ throw error
507
+ }
508
+ }
509
+
510
+ /**
511
+ * Gets a Sandbox by its ID.
512
+ *
513
+ * @param {string} sandboxId - The ID of the Sandbox to retrieve
514
+ * @returns {Promise<Sandbox>} The Sandbox
515
+ *
516
+ * @example
517
+ * const sandbox = await daytona.get('my-sandbox-id');
518
+ * console.log(`Sandbox state: ${sandbox.state}`);
519
+ */
520
+ public async get(sandboxId: string): Promise<Sandbox> {
521
+ const response = await this.sandboxApi.getSandbox(sandboxId)
522
+ const sandboxInstance = response.data
523
+ const language = sandboxInstance.labels && sandboxInstance.labels['code-toolbox-language']
524
+ const codeToolbox = this.getCodeToolbox(language as CodeLanguage)
525
+
526
+ return new Sandbox(sandboxInstance, this.sandboxApi, this.toolboxApi, codeToolbox)
527
+ }
528
+
529
+ /**
530
+ * Finds a Sandbox by its ID or labels.
531
+ *
532
+ * @param {SandboxFilter} filter - Filter for Sandboxes
533
+ * @returns {Promise<Sandbox>} First Sandbox that matches the ID or labels.
534
+ *
535
+ * @example
536
+ * const sandbox = await daytona.findOne({ labels: { 'my-label': 'my-value' } });
537
+ * console.log(`Sandbox ID: ${sandbox.id}, State: ${sandbox.state}`);
538
+ */
539
+ public async findOne(filter: SandboxFilter): Promise<Sandbox> {
540
+ if (filter.id) {
541
+ return this.get(filter.id)
542
+ }
543
+
544
+ const sandboxes = await this.list(filter.labels)
545
+ if (sandboxes.length === 0) {
546
+ const errMsg = `No sandbox found with labels ${JSON.stringify(filter.labels)}`
547
+ throw new DaytonaError(errMsg)
548
+ }
549
+ return sandboxes[0]
550
+ }
551
+
552
+ /**
553
+ * Lists all Sandboxes filtered by labels.
554
+ *
555
+ * @param {Record<string, string>} [labels] - Labels to filter Sandboxes
556
+ * @returns {Promise<Sandbox[]>} Array of Sandboxes that match the labels.
557
+ *
558
+ * @example
559
+ * const sandboxes = await daytona.list({ 'my-label': 'my-value' });
560
+ * for (const sandbox of sandboxes) {
561
+ * console.log(`${sandbox.id}: ${sandbox.state}`);
562
+ * }
563
+ */
564
+ public async list(labels?: Record<string, string>): Promise<Sandbox[]> {
565
+ const response = await this.sandboxApi.listSandboxes(
566
+ undefined,
567
+ undefined,
568
+ labels ? JSON.stringify(labels) : undefined,
569
+ )
570
+ return response.data.map((sandbox) => {
571
+ const language = sandbox.labels?.['code-toolbox-language'] as CodeLanguage
572
+
573
+ return new Sandbox(sandbox, this.sandboxApi, this.toolboxApi, this.getCodeToolbox(language))
574
+ })
575
+ }
576
+
577
+ /**
578
+ * Starts a Sandbox and waits for it to be ready.
579
+ *
580
+ * @param {Sandbox} sandbox - The Sandbox to start
581
+ * @param {number} [timeout] - Optional timeout in seconds (0 means no timeout)
582
+ * @returns {Promise<void>}
583
+ *
584
+ * @example
585
+ * const sandbox = await daytona.get('my-sandbox-id');
586
+ * // Wait up to 60 seconds for the sandbox to start
587
+ * await daytona.start(sandbox, 60);
588
+ */
589
+ public async start(sandbox: Sandbox, timeout?: number) {
590
+ await sandbox.start(timeout)
591
+ }
592
+
593
+ /**
594
+ * Stops a Sandbox.
595
+ *
596
+ * @param {Sandbox} sandbox - The Sandbox to stop
597
+ * @returns {Promise<void>}
598
+ *
599
+ * @example
600
+ * const sandbox = await daytona.get('my-sandbox-id');
601
+ * await daytona.stop(sandbox);
602
+ */
603
+ public async stop(sandbox: Sandbox) {
604
+ await sandbox.stop()
605
+ }
606
+
607
+ /**
608
+ * Deletes a Sandbox.
609
+ *
610
+ * @param {Sandbox} sandbox - The Sandbox to delete
611
+ * @param {number} timeout - Timeout in seconds (0 means no timeout, default is 60)
612
+ * @returns {Promise<void>}
613
+ *
614
+ * @example
615
+ * const sandbox = await daytona.get('my-sandbox-id');
616
+ * await daytona.delete(sandbox);
617
+ */
618
+ public async delete(sandbox: Sandbox, timeout = 60) {
619
+ await sandbox.delete(timeout)
620
+ }
621
+
622
+ /**
623
+ * Gets the appropriate code toolbox based on language.
624
+ *
625
+ * @private
626
+ * @param {CodeLanguage} [language] - Programming language for the toolbox
627
+ * @returns {SandboxCodeToolbox} The appropriate code toolbox instance
628
+ * @throws {DaytonaError} - `DaytonaError` - When an unsupported language is specified
629
+ */
630
+ private getCodeToolbox(language?: CodeLanguage) {
631
+ switch (language) {
632
+ case CodeLanguage.JAVASCRIPT:
633
+ case CodeLanguage.TYPESCRIPT:
634
+ return new SandboxTsCodeToolbox()
635
+ case CodeLanguage.PYTHON:
636
+ case undefined:
637
+ return new SandboxPythonCodeToolbox()
638
+ default: {
639
+ const errMsg = `Unsupported language: ${language}, supported languages: ${Object.values(CodeLanguage).join(', ')}`
640
+ throw new DaytonaError(errMsg)
641
+ }
642
+ }
643
+ }
644
+ }