@daytonaio/sdk 0.18.0-alpha.1 → 0.18.0-alpha.2

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/Daytona.d.ts CHANGED
@@ -1,5 +1,4 @@
1
1
  import { CreateWorkspaceTargetEnum as SandboxTargetRegion, WorkspaceVolume } from '@daytonaio/api-client';
2
- import { Image } from './Image';
3
2
  import { Sandbox, Sandbox as Workspace } from './Sandbox';
4
3
  import { VolumeService } from './Volume';
5
4
  /**
@@ -89,7 +88,7 @@ export interface SandboxResources {
89
88
  * Parameters for creating a new Sandbox.
90
89
  *
91
90
  * @interface
92
- * @property {string | Image} [image] - Optional Docker image to use for the Sandbox or an Image instance
91
+ * @property {string} [image] - Optional Docker image to use for the Sandbox
93
92
  * @property {string} [user] - Optional os user to use for the Sandbox
94
93
  * @property {CodeLanguage | string} [language] - Programming language for direct code execution
95
94
  * @property {Record<string, string>} [envVars] - Optional environment variables to set in the Sandbox
@@ -114,8 +113,8 @@ export interface SandboxResources {
114
113
  * const sandbox = await daytona.create(params, 50);
115
114
  */
116
115
  export type CreateSandboxParams = {
117
- /** Optional Docker image to use for the Sandbox or an Image instance */
118
- image?: string | Image;
116
+ /** Optional Docker image to use for the Sandbox */
117
+ image?: string;
119
118
  /** Optional os user to use for the Sandbox */
120
119
  user?: string;
121
120
  /** Programming language for direct code execution */
@@ -180,8 +179,6 @@ export type SandboxFilter = {
180
179
  export declare class Daytona {
181
180
  private readonly sandboxApi;
182
181
  private readonly toolboxApi;
183
- private readonly imagesApi;
184
- private readonly objectStorageApi;
185
182
  private readonly target;
186
183
  private readonly apiKey?;
187
184
  private readonly jwtToken?;
@@ -196,8 +193,6 @@ export declare class Daytona {
196
193
  */
197
194
  constructor(config?: DaytonaConfig);
198
195
  /**
199
- * @deprecated Use `create` with `options` object instead. This method will be removed in a future version.
200
- *
201
196
  * Creates Sandboxes with default or custom configurations. You can specify various parameters,
202
197
  * including language, image, resources, environment variables, and volumes for the Sandbox.
203
198
  *
@@ -226,45 +221,7 @@ export declare class Daytona {
226
221
  * };
227
222
  * const sandbox = await daytona.create(params, 40);
228
223
  */
229
- create(params?: CreateSandboxParams, options?: number): Promise<Sandbox>;
230
- /**
231
- * Creates Sandboxes with default or custom configurations. You can specify various parameters,
232
- * including language, image, resources, environment variables, and volumes for the Sandbox.
233
- *
234
- * @param {CreateSandboxParams} [params] - Parameters for Sandbox creation
235
- * @param {object} [options] - Options for the create operation
236
- * @param {number} [options.timeout] - Timeout in seconds (0 means no timeout, default is 60)
237
- * @param {function} [options.onImageBuildLogs] - Callback function to handle image build logs.
238
- * It's invoked only when `params.image` is an instance of `Image` and there's no existing
239
- * image in Daytona with the same configuration.
240
- * @returns {Promise<Sandbox>} The created Sandbox instance
241
- *
242
- * @example
243
- * const image = Image.debianSlim('3.12').pipInstall('numpy');
244
- * const sandbox = await daytona.create({ image }, { timeout: 90, onImageBuildLogs: console.log });
245
- *
246
- * @example
247
- * // Create a custom sandbox
248
- * const image = Image.debianSlim('3.12').pipInstall('numpy');
249
- * const params: CreateSandboxParams = {
250
- * language: 'typescript',
251
- * image,
252
- * envVars: {
253
- * NODE_ENV: 'development',
254
- * DEBUG: 'true'
255
- * },
256
- * resources: {
257
- * cpu: 2,
258
- * memory: 4 // 4GB RAM
259
- * },
260
- * autoStopInterval: 60
261
- * };
262
- * const sandbox = await daytona.create(params, { timeout: 100, onImageBuildLogs: console.log });
263
- */
264
- create(params?: CreateSandboxParams, options?: {
265
- onImageBuildLogs?: (chunk: string) => void;
266
- timeout?: number;
267
- }): Promise<Sandbox>;
224
+ create(params?: CreateSandboxParams, timeout?: number): Promise<Sandbox>;
268
225
  /**
269
226
  * Gets a Sandbox by its ID.
270
227
  *
@@ -358,24 +315,6 @@ export declare class Daytona {
358
315
  * console.log(`Current sandbox state: ${sandbox.instance.state}`);
359
316
  */
360
317
  getCurrentSandbox(sandboxId: string): Promise<Sandbox>;
361
- /**
362
- * Creates and registers a new image from the given Image definition.
363
- *
364
- * @param {string} name - The name of the image to create.
365
- * @param {Image} image - The Image instance.
366
- * @param {object} options - Options for the create operation.
367
- * @param {boolean} options.verbose - Default is false. Whether to log progress information upon each state change of the image.
368
- * @param {number} options.timeout - Default is no timeout. Timeout in seconds (0 means no timeout).
369
- * @returns {Promise<void>}
370
- *
371
- * @example
372
- * const image = Image.debianSlim('3.12').pipInstall('numpy');
373
- * await daytona.createImage('my-python-image', image);
374
- */
375
- createImage(name: string, image: Image, options?: {
376
- onLogs?: (chunk: string) => void;
377
- timeout?: number;
378
- }): Promise<void>;
379
318
  /**
380
319
  * Gets the appropriate code toolbox based on language.
381
320
  *
@@ -385,12 +324,4 @@ export declare class Daytona {
385
324
  * @throws {DaytonaError} - `DaytonaError` - When an unsupported language is specified
386
325
  */
387
326
  private getCodeToolbox;
388
- /**
389
- * Processes the image contexts by uploading them to object storage
390
- *
391
- * @private
392
- * @param {Image} image - The Image instance.
393
- * @returns {Promise<string[]>} The list of context hashes stored in object storage.
394
- */
395
- private processImageContext;
396
327
  }
package/dist/Daytona.js CHANGED
@@ -43,10 +43,7 @@ const dotenv_1 = __importDefault(require("dotenv"));
43
43
  const SandboxPythonCodeToolbox_1 = require("./code-toolbox/SandboxPythonCodeToolbox");
44
44
  const SandboxTsCodeToolbox_1 = require("./code-toolbox/SandboxTsCodeToolbox");
45
45
  const DaytonaError_1 = require("./errors/DaytonaError");
46
- const Image_1 = require("./Image");
47
- const ObjectStorage_1 = require("./ObjectStorage");
48
46
  const Sandbox_1 = require("./Sandbox");
49
- const Stream_1 = require("./utils/Stream");
50
47
  const Volume_1 = require("./Volume");
51
48
  /**
52
49
  * Supported programming languages for code execution
@@ -161,16 +158,39 @@ class Daytona {
161
158
  this.sandboxApi = new api_client_1.WorkspaceApi(configuration, '', axiosInstance);
162
159
  this.toolboxApi = new api_client_1.ToolboxApi(configuration, '', axiosInstance);
163
160
  this.volume = new Volume_1.VolumeService(new api_client_1.VolumesApi(configuration, '', axiosInstance));
164
- this.imagesApi = new api_client_1.ImagesApi(configuration, '', axiosInstance);
165
- this.objectStorageApi = new api_client_1.ObjectStorageApi(configuration, '', axiosInstance);
166
161
  }
167
- async create(params, options = { timeout: 60 }) {
162
+ /**
163
+ * Creates Sandboxes with default or custom configurations. You can specify various parameters,
164
+ * including language, image, resources, environment variables, and volumes for the Sandbox.
165
+ *
166
+ * @param {CreateSandboxParams} [params] - Parameters for Sandbox creation
167
+ * @param {number} [timeout] - Timeout in seconds (0 means no timeout, default is 60)
168
+ * @returns {Promise<Sandbox>} The created Sandbox instance
169
+ *
170
+ * @example
171
+ * // Create a default sandbox
172
+ * const sandbox = await daytona.create();
173
+ *
174
+ * @example
175
+ * // Create a custom sandbox
176
+ * const params: CreateSandboxParams = {
177
+ * language: 'typescript',
178
+ * image: 'node:18',
179
+ * envVars: {
180
+ * NODE_ENV: 'development',
181
+ * DEBUG: 'true'
182
+ * },
183
+ * resources: {
184
+ * cpu: 2,
185
+ * memory: 4 // 4GB RAM
186
+ * },
187
+ * autoStopInterval: 60
188
+ * };
189
+ * const sandbox = await daytona.create(params, 40);
190
+ */
191
+ async create(params, timeout = 60) {
168
192
  var _a, _b, _c, _d;
169
193
  const startTime = Date.now();
170
- options = typeof options === 'number' ? { timeout: options } : { ...options };
171
- if (options.timeout == undefined || options.timeout == null) {
172
- options.timeout = 60;
173
- }
174
194
  if (params == null) {
175
195
  params = { language: 'python' };
176
196
  }
@@ -179,7 +199,7 @@ class Daytona {
179
199
  labels['code-toolbox-language'] = params.language;
180
200
  }
181
201
  // remove this when params.timeout is removed
182
- const effectiveTimeout = params.timeout || options.timeout;
202
+ const effectiveTimeout = params.timeout || timeout;
183
203
  if (effectiveTimeout < 0) {
184
204
  throw new DaytonaError_1.DaytonaError('Timeout must be a non-negative number');
185
205
  }
@@ -189,22 +209,8 @@ class Daytona {
189
209
  }
190
210
  const codeToolbox = this.getCodeToolbox(params.language);
191
211
  try {
192
- // Handle Image instance if provided
193
- let imageStr;
194
- let buildInfo;
195
- if (typeof params.image === 'string') {
196
- imageStr = params.image;
197
- }
198
- else if (params.image instanceof Image_1.Image) {
199
- const contextHashes = await this.processImageContext(params.image);
200
- buildInfo = {
201
- contextHashes,
202
- dockerfileContent: params.image.dockerfile,
203
- };
204
- }
205
212
  const response = await this.sandboxApi.createWorkspace({
206
- image: imageStr,
207
- buildInfo,
213
+ image: params.image,
208
214
  user: params.user,
209
215
  env: params.envVars || {},
210
216
  labels: params.labels,
@@ -219,21 +225,14 @@ class Daytona {
219
225
  }, undefined, {
220
226
  timeout: effectiveTimeout * 1000,
221
227
  });
222
- let sandboxInstance = response.data;
223
- if (sandboxInstance.state === api_client_1.WorkspaceState.PENDING_BUILD && options.onImageBuildLogs) {
224
- const terminalStates = [api_client_1.WorkspaceState.STARTED, api_client_1.WorkspaceState.STARTING, api_client_1.WorkspaceState.ERROR];
225
- await (0, Stream_1.processStreamingResponse)(() => this.sandboxApi.getBuildLogs(sandboxInstance.id, undefined, true, { responseType: 'stream' }), options.onImageBuildLogs, async () => {
226
- sandboxInstance = (await this.sandboxApi.getWorkspace(sandboxInstance.id)).data;
227
- return sandboxInstance.state !== undefined && terminalStates.includes(sandboxInstance.state);
228
- });
229
- }
228
+ const sandboxInstance = response.data;
230
229
  const sandboxInfo = Sandbox_1.Sandbox.toSandboxInfo(sandboxInstance);
231
230
  sandboxInstance.info = {
232
231
  ...sandboxInfo,
233
232
  name: '',
234
233
  };
235
234
  const sandbox = new Sandbox_1.Sandbox(sandboxInstance.id, sandboxInstance, this.sandboxApi, this.toolboxApi, codeToolbox);
236
- if (!params.async && sandbox.instance.state !== 'started') {
235
+ if (!params.async) {
237
236
  const timeElapsed = Date.now() - startTime;
238
237
  await sandbox.waitUntilStarted(effectiveTimeout ? effectiveTimeout - timeElapsed / 1000 : 0);
239
238
  }
@@ -379,58 +378,6 @@ class Daytona {
379
378
  async getCurrentSandbox(sandboxId) {
380
379
  return await this.get(sandboxId);
381
380
  }
382
- /**
383
- * Creates and registers a new image from the given Image definition.
384
- *
385
- * @param {string} name - The name of the image to create.
386
- * @param {Image} image - The Image instance.
387
- * @param {object} options - Options for the create operation.
388
- * @param {boolean} options.verbose - Default is false. Whether to log progress information upon each state change of the image.
389
- * @param {number} options.timeout - Default is no timeout. Timeout in seconds (0 means no timeout).
390
- * @returns {Promise<void>}
391
- *
392
- * @example
393
- * const image = Image.debianSlim('3.12').pipInstall('numpy');
394
- * await daytona.createImage('my-python-image', image);
395
- */
396
- async createImage(name, image, options = {}) {
397
- const contextHashes = await this.processImageContext(image);
398
- let builtImage = (await this.imagesApi.buildImage({
399
- name,
400
- buildInfo: {
401
- contextHashes,
402
- dockerfileContent: image.dockerfile,
403
- },
404
- }, undefined, {
405
- timeout: (options.timeout || 0) * 1000,
406
- })).data;
407
- const terminalStates = [api_client_1.ImageState.ACTIVE, api_client_1.ImageState.ERROR];
408
- const imageRef = { builtImage };
409
- let streamPromise;
410
- if (options.onLogs) {
411
- options.onLogs(`Building image ${builtImage.name} (${builtImage.state})`);
412
- streamPromise = (0, Stream_1.processStreamingResponse)(() => this.imagesApi.getImageBuildLogs(builtImage.id, undefined, true, { responseType: 'stream' }), options.onLogs, async () => terminalStates.includes(imageRef.builtImage.state));
413
- }
414
- let previousState = builtImage.state;
415
- while (!terminalStates.includes(builtImage.state)) {
416
- if (options.onLogs && previousState !== builtImage.state) {
417
- options.onLogs(`Building image ${builtImage.name} (${builtImage.state})`);
418
- previousState = builtImage.state;
419
- }
420
- await new Promise((resolve) => setTimeout(resolve, 1000));
421
- builtImage = (await this.imagesApi.getImage(builtImage.id)).data;
422
- imageRef.builtImage = builtImage;
423
- }
424
- if (options.onLogs) {
425
- await streamPromise;
426
- if (builtImage.state === api_client_1.ImageState.ACTIVE) {
427
- options.onLogs(`Built image ${builtImage.name} (${builtImage.state})`);
428
- }
429
- }
430
- if (builtImage.state === api_client_1.ImageState.ERROR) {
431
- throw new DaytonaError_1.DaytonaError(`Failed to build image. Image ended in the ERROR state. name: ${builtImage.name}; error reason: ${builtImage.errorReason}`);
432
- }
433
- }
434
381
  /**
435
382
  * Gets the appropriate code toolbox based on language.
436
383
  *
@@ -451,31 +398,5 @@ class Daytona {
451
398
  throw new DaytonaError_1.DaytonaError(`Unsupported language: ${language}, supported languages: ${Object.values(CodeLanguage).join(', ')}`);
452
399
  }
453
400
  }
454
- /**
455
- * Processes the image contexts by uploading them to object storage
456
- *
457
- * @private
458
- * @param {Image} image - The Image instance.
459
- * @returns {Promise<string[]>} The list of context hashes stored in object storage.
460
- */
461
- async processImageContext(image) {
462
- if (!image.contextList || !image.contextList.length) {
463
- return [];
464
- }
465
- const pushAccessCreds = (await this.objectStorageApi.getPushAccess()).data;
466
- const objectStorage = new ObjectStorage_1.ObjectStorage({
467
- endpointUrl: pushAccessCreds.storageUrl,
468
- accessKeyId: pushAccessCreds.accessKey,
469
- secretAccessKey: pushAccessCreds.secret,
470
- sessionToken: pushAccessCreds.sessionToken,
471
- bucketName: pushAccessCreds.bucket,
472
- });
473
- const contextHashes = [];
474
- for (const context of image.contextList) {
475
- const contextHash = await objectStorage.upload(context.sourcePath, pushAccessCreds.organizationId, context.archivePath);
476
- contextHashes.push(contextHash);
477
- }
478
- return contextHashes;
479
- }
480
401
  }
481
402
  exports.Daytona = Daytona;
@@ -27,14 +27,14 @@ export type FilePermissionsParams = {
27
27
  * Represents a file to be uploaded to the Sandbox.
28
28
  *
29
29
  * @interface
30
- * @property {string} path - Absolute destination path in the Sandbox
31
- * @property {File} content - File to upload
30
+ * @property {string | Buffer} source - File to upload. If a Buffer, it is interpreted as the file content which is loaded into memory.
31
+ * Make sure it fits into memory, otherwise use the local file path which content will be streamed to the Sandbox.
32
+ * @property {string} destination - Absolute destination path in the Sandbox. Relative paths are resolved based on the user's
33
+ * root directory.
32
34
  */
33
35
  export interface FileUpload {
34
- /** Absolute destination path in the Sandbox */
35
- path: string;
36
- /** File to upload */
37
- content: File;
36
+ source: string | Buffer;
37
+ destination: string;
38
38
  }
39
39
  /**
40
40
  * Provides file system operations within a Sandbox.
@@ -72,18 +72,37 @@ export declare class FileSystem {
72
72
  */
73
73
  deleteFile(path: string): Promise<void>;
74
74
  /**
75
- * Downloads a file from the Sandbox.
75
+ * Downloads a file from the Sandbox. This method loads the entire file into memory, so it is not recommended
76
+ * for downloading large files.
76
77
  *
77
- * @param {string} path - Path to the file to download. Relative paths are resolved based on the user's
78
+ * @param {string} remotePath - Path to the file to download. Relative paths are resolved based on the user's
78
79
  * root directory.
79
- * @returns {Promise<Blob>} The file contents as a Blob
80
+ * @param {number} [timeout] - Timeout for the download operation in seconds. 0 means no timeout.
81
+ * Default is 30 minutes.
82
+ * @returns {Promise<Buffer>} The file contents as a Buffer.
80
83
  *
81
84
  * @example
82
85
  * // Download and process a file
83
- * const fileBlob = await fs.downloadFile('app/data.json');
84
- * console.log('File content:', fileBlob.toString());
86
+ * const fileBuffer = await fs.downloadFile('tmp/data.json');
87
+ * console.log('File content:', fileBuffer.toString());
85
88
  */
86
- downloadFile(path: string): Promise<Blob>;
89
+ downloadFile(remotePath: string, timeout?: number): Promise<Buffer>;
90
+ /**
91
+ * Downloads a file from the Sandbox and saves it to a local file. This method uses streaming to download the file,
92
+ * so it is recommended for downloading larger files.
93
+ *
94
+ * @param {string} remotePath - Path to the file to download in the Sandbox. Relative paths are resolved based on the user's
95
+ * root directory.
96
+ * @param {string} localPath - Path to save the downloaded file.
97
+ * @param {number} [timeout] - Timeout for the download operation in seconds. 0 means no timeout.
98
+ * Default is 30 minutes.
99
+ * @returns {Promise<void>}
100
+ *
101
+ * @example
102
+ * // Download and save a file
103
+ * await fs.downloadFile('tmp/data.json', 'local_file.json');
104
+ */
105
+ downloadFile(remotePath: string, localPath: string, timeout?: number): Promise<void>;
87
106
  /**
88
107
  * Searches for text patterns within files in the Sandbox.
89
108
  *
@@ -190,44 +209,63 @@ export declare class FileSystem {
190
209
  */
191
210
  setFilePermissions(path: string, permissions: FilePermissionsParams): Promise<void>;
192
211
  /**
193
- * Uploads a file to the Sandbox.
212
+ * Uploads a file to the Sandbox. This method loads the entire file into memory, so it is not recommended
213
+ * for uploading large files.
194
214
  *
215
+ * @param {Buffer} file - Buffer of the file to upload.
195
216
  * @param {string} path - Destination path in the Sandbox. Relative paths are resolved based on the user's
196
217
  * root directory.
197
- * @param {File} file - File to upload
218
+ * @param {number} [timeout] - Timeout for the upload operation in seconds. 0 means no timeout.
219
+ * Default is 30 minutes.
198
220
  * @returns {Promise<void>}
199
221
  *
200
222
  * @example
201
223
  * // Upload a configuration file
202
- * const configFile = new File(['{"setting": "value"}'], 'config.json');
203
- * await fs.uploadFile('app/config.json', configFile);
224
+ * await fs.uploadFile(Buffer.from('{"setting": "value"}'), 'tmp/config.json');
204
225
  */
205
- uploadFile(path: string, file: File): Promise<void>;
226
+ uploadFile(file: Buffer, path: string, timeout?: number): Promise<void>;
206
227
  /**
207
- * Uploads multiple files to the Sandbox. The parent directories must exist.
208
- * If files already exist at the destination paths, they will be overwritten.
228
+ * Uploads a file from the local file system to the Sandbox. This method uses streaming to upload the file,
229
+ * so it is recommended for uploading larger files.
209
230
  *
210
- * @param {FileUpload[]} files - Array of files to upload. Relative paths are resolved based on the user's
231
+ * @param {string} localPath - Path to the local file to upload.
232
+ * @param {string} remotePath - Destination path in the Sandbox. Relative paths are resolved based on the user's
211
233
  * root directory.
234
+ * @param {number} [timeout] - Timeout for the upload operation in seconds. 0 means no timeout.
235
+ * Default is 30 minutes.
236
+ * @returns {Promise<void>}
237
+ *
238
+ * @example
239
+ * // Upload a local file
240
+ * await fs.uploadFile('local_file.txt', 'tmp/file.txt');
241
+ */
242
+ uploadFile(localPath: string, remotePath: string, timeout?: number): Promise<void>;
243
+ /**
244
+ * Uploads multiple files to the Sandbox. If files already exist at the destination paths,
245
+ * they will be overwritten.
246
+ *
247
+ * @param {FileUpload[]} files - Array of files to upload.
248
+ * @param {number} [timeout] - Timeout for the upload operation in seconds. 0 means no timeout.
249
+ * Default is 30 minutes.
212
250
  * @returns {Promise<void>}
213
251
  *
214
252
  * @example
215
253
  * // Upload multiple text files
216
254
  * const files = [
217
255
  * {
218
- * path: 'app/data/file1.txt',
219
- * content: new File(['Content of file 1'], 'file1.txt')
256
+ * source: Buffer.from('Content of file 1'),
257
+ * destination: '/tmp/file1.txt'
220
258
  * },
221
259
  * {
222
- * path: 'app/data/file2.txt',
223
- * content: new File(['Content of file 2'], 'file2.txt')
260
+ * source: 'app/data/file2.txt',
261
+ * destination: '/tmp/file2.txt'
224
262
  * },
225
263
  * {
226
- * path: 'app/config/settings.json',
227
- * content: new File(['{"key": "value"}'], 'settings.json')
264
+ * source: Buffer.from('{"key": "value"}'),
265
+ * destination: '/tmp/config.json'
228
266
  * }
229
267
  * ];
230
268
  * await fs.uploadFiles(files);
231
269
  */
232
- uploadFiles(files: FileUpload[]): Promise<void>;
270
+ uploadFiles(files: FileUpload[], timeout?: number): Promise<void>;
233
271
  }
@@ -1,8 +1,13 @@
1
1
  "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
2
5
  Object.defineProperty(exports, "__esModule", { value: true });
3
6
  exports.FileSystem = void 0;
4
- const DaytonaError_1 = require("./errors/DaytonaError");
5
7
  const Path_1 = require("./utils/Path");
8
+ const fs_1 = __importDefault(require("fs"));
9
+ const stream_1 = require("stream");
10
+ const form_data_1 = __importDefault(require("form-data"));
6
11
  /**
7
12
  * Provides file system operations within a Sandbox.
8
13
  *
@@ -45,21 +50,29 @@ class FileSystem {
45
50
  const response = await this.toolboxApi.deleteFile(this.instance.id, (0, Path_1.prefixRelativePath)(await this.getRootDir(), path));
46
51
  return response.data;
47
52
  }
48
- /**
49
- * Downloads a file from the Sandbox.
50
- *
51
- * @param {string} path - Path to the file to download. Relative paths are resolved based on the user's
52
- * root directory.
53
- * @returns {Promise<Blob>} The file contents as a Blob
54
- *
55
- * @example
56
- * // Download and process a file
57
- * const fileBlob = await fs.downloadFile('app/data.json');
58
- * console.log('File content:', fileBlob.toString());
59
- */
60
- async downloadFile(path) {
61
- const response = await this.toolboxApi.downloadFile(this.instance.id, (0, Path_1.prefixRelativePath)(await this.getRootDir(), path));
62
- return response.data;
53
+ async downloadFile(src, dst, timeout = 30 * 60) {
54
+ const remotePath = (0, Path_1.prefixRelativePath)(await this.getRootDir(), src);
55
+ if (typeof dst !== 'string') {
56
+ timeout = dst;
57
+ const { data } = await this.toolboxApi.downloadFile(this.instance.id, remotePath, undefined, {
58
+ responseType: 'arraybuffer',
59
+ timeout: timeout * 1000,
60
+ });
61
+ if (Buffer.isBuffer(data)) {
62
+ return data;
63
+ }
64
+ return Buffer.from(await data.arrayBuffer());
65
+ }
66
+ const response = await this.toolboxApi.downloadFile(this.instance.id, remotePath, undefined, {
67
+ responseType: 'stream',
68
+ timeout: timeout * 1000,
69
+ });
70
+ const writer = fs_1.default.createWriteStream(dst);
71
+ response.data.pipe(writer);
72
+ await new Promise((resolve, reject) => {
73
+ writer.on('finish', () => resolve());
74
+ writer.on('error', (err) => reject(err));
75
+ });
63
76
  }
64
77
  /**
65
78
  * Searches for text patterns within files in the Sandbox.
@@ -195,64 +208,51 @@ class FileSystem {
195
208
  const response = await this.toolboxApi.setFilePermissions(this.instance.id, (0, Path_1.prefixRelativePath)(await this.getRootDir(), path), undefined, permissions.owner, permissions.group, permissions.mode);
196
209
  return response.data;
197
210
  }
198
- /**
199
- * Uploads a file to the Sandbox.
200
- *
201
- * @param {string} path - Destination path in the Sandbox. Relative paths are resolved based on the user's
202
- * root directory.
203
- * @param {File} file - File to upload
204
- * @returns {Promise<void>}
205
- *
206
- * @example
207
- * // Upload a configuration file
208
- * const configFile = new File(['{"setting": "value"}'], 'config.json');
209
- * await fs.uploadFile('app/config.json', configFile);
210
- */
211
- async uploadFile(path, file) {
212
- const response = await this.toolboxApi.uploadFile(this.instance.id, (0, Path_1.prefixRelativePath)(await this.getRootDir(), path), undefined, file);
213
- return response.data;
211
+ async uploadFile(src, dst, timeout = 30 * 60) {
212
+ this.uploadFiles([{ source: src, destination: dst }], timeout);
214
213
  }
215
214
  /**
216
- * Uploads multiple files to the Sandbox. The parent directories must exist.
217
- * If files already exist at the destination paths, they will be overwritten.
215
+ * Uploads multiple files to the Sandbox. If files already exist at the destination paths,
216
+ * they will be overwritten.
218
217
  *
219
- * @param {FileUpload[]} files - Array of files to upload. Relative paths are resolved based on the user's
220
- * root directory.
218
+ * @param {FileUpload[]} files - Array of files to upload.
219
+ * @param {number} [timeout] - Timeout for the upload operation in seconds. 0 means no timeout.
220
+ * Default is 30 minutes.
221
221
  * @returns {Promise<void>}
222
222
  *
223
223
  * @example
224
224
  * // Upload multiple text files
225
225
  * const files = [
226
226
  * {
227
- * path: 'app/data/file1.txt',
228
- * content: new File(['Content of file 1'], 'file1.txt')
227
+ * source: Buffer.from('Content of file 1'),
228
+ * destination: '/tmp/file1.txt'
229
229
  * },
230
230
  * {
231
- * path: 'app/data/file2.txt',
232
- * content: new File(['Content of file 2'], 'file2.txt')
231
+ * source: 'app/data/file2.txt',
232
+ * destination: '/tmp/file2.txt'
233
233
  * },
234
234
  * {
235
- * path: 'app/config/settings.json',
236
- * content: new File(['{"key": "value"}'], 'settings.json')
235
+ * source: Buffer.from('{"key": "value"}'),
236
+ * destination: '/tmp/config.json'
237
237
  * }
238
238
  * ];
239
239
  * await fs.uploadFiles(files);
240
240
  */
241
- async uploadFiles(files) {
242
- for (const file of files) {
243
- file.path = (0, Path_1.prefixRelativePath)(await this.getRootDir(), file.path);
244
- }
245
- const results = await Promise.allSettled(files.map((file) => this.uploadFile(file.path, file.content)));
246
- const failedUploads = results
247
- .map((result, index) => ({
248
- path: files[index].path,
249
- error: result.status === 'rejected' ? result.reason : undefined,
250
- }))
251
- .filter(({ error }) => error !== undefined);
252
- if (failedUploads.length > 0) {
253
- const errorMessage = failedUploads.map(({ path, error }) => `\nFailed to upload '${path}': ${error}`).join('');
254
- throw new DaytonaError_1.DaytonaError(errorMessage);
255
- }
241
+ async uploadFiles(files, timeout = 30 * 60) {
242
+ const form = new form_data_1.default();
243
+ const rootDir = await this.getRootDir();
244
+ files.forEach(({ source, destination }, i) => {
245
+ const dst = (0, Path_1.prefixRelativePath)(rootDir, destination);
246
+ form.append(`files[${i}].path`, dst);
247
+ const stream = typeof source === 'string' ? fs_1.default.createReadStream(source) : stream_1.Readable.from(source);
248
+ // the third arg sets filename in Content-Disposition
249
+ form.append(`files[${i}].file`, stream, dst);
250
+ });
251
+ await this.toolboxApi.uploadFiles(this.instance.id, undefined, {
252
+ data: form,
253
+ maxRedirects: 0,
254
+ timeout: timeout * 1000,
255
+ });
256
256
  }
257
257
  }
258
258
  exports.FileSystem = FileSystem;