@aj-archipelago/cortex 1.3.57 → 1.3.59

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.
Files changed (47) hide show
  1. package/README.md +6 -0
  2. package/config.js +22 -0
  3. package/helper-apps/cortex-file-handler/INTERFACE.md +20 -9
  4. package/helper-apps/cortex-file-handler/package-lock.json +2 -2
  5. package/helper-apps/cortex-file-handler/package.json +1 -1
  6. package/helper-apps/cortex-file-handler/scripts/setup-azure-container.js +17 -17
  7. package/helper-apps/cortex-file-handler/scripts/setup-test-containers.js +35 -35
  8. package/helper-apps/cortex-file-handler/src/blobHandler.js +1010 -909
  9. package/helper-apps/cortex-file-handler/src/constants.js +98 -98
  10. package/helper-apps/cortex-file-handler/src/docHelper.js +27 -27
  11. package/helper-apps/cortex-file-handler/src/fileChunker.js +224 -214
  12. package/helper-apps/cortex-file-handler/src/helper.js +93 -93
  13. package/helper-apps/cortex-file-handler/src/index.js +584 -550
  14. package/helper-apps/cortex-file-handler/src/localFileHandler.js +86 -86
  15. package/helper-apps/cortex-file-handler/src/redis.js +186 -90
  16. package/helper-apps/cortex-file-handler/src/services/ConversionService.js +301 -273
  17. package/helper-apps/cortex-file-handler/src/services/FileConversionService.js +55 -55
  18. package/helper-apps/cortex-file-handler/src/services/storage/AzureStorageProvider.js +174 -154
  19. package/helper-apps/cortex-file-handler/src/services/storage/GCSStorageProvider.js +239 -223
  20. package/helper-apps/cortex-file-handler/src/services/storage/LocalStorageProvider.js +161 -159
  21. package/helper-apps/cortex-file-handler/src/services/storage/StorageFactory.js +73 -71
  22. package/helper-apps/cortex-file-handler/src/services/storage/StorageProvider.js +46 -45
  23. package/helper-apps/cortex-file-handler/src/services/storage/StorageService.js +256 -213
  24. package/helper-apps/cortex-file-handler/src/start.js +4 -1
  25. package/helper-apps/cortex-file-handler/src/utils/filenameUtils.js +59 -25
  26. package/helper-apps/cortex-file-handler/tests/FileConversionService.test.js +119 -116
  27. package/helper-apps/cortex-file-handler/tests/blobHandler.test.js +257 -257
  28. package/helper-apps/cortex-file-handler/tests/cleanup.test.js +676 -0
  29. package/helper-apps/cortex-file-handler/tests/conversionResilience.test.js +124 -124
  30. package/helper-apps/cortex-file-handler/tests/fileChunker.test.js +249 -208
  31. package/helper-apps/cortex-file-handler/tests/fileUpload.test.js +439 -380
  32. package/helper-apps/cortex-file-handler/tests/getOperations.test.js +299 -263
  33. package/helper-apps/cortex-file-handler/tests/postOperations.test.js +265 -239
  34. package/helper-apps/cortex-file-handler/tests/start.test.js +1230 -1201
  35. package/helper-apps/cortex-file-handler/tests/storage/AzureStorageProvider.test.js +110 -105
  36. package/helper-apps/cortex-file-handler/tests/storage/GCSStorageProvider.test.js +201 -175
  37. package/helper-apps/cortex-file-handler/tests/storage/LocalStorageProvider.test.js +128 -125
  38. package/helper-apps/cortex-file-handler/tests/storage/StorageFactory.test.js +78 -73
  39. package/helper-apps/cortex-file-handler/tests/storage/StorageService.test.js +99 -99
  40. package/helper-apps/cortex-file-handler/tests/testUtils.helper.js +74 -70
  41. package/package.json +1 -1
  42. package/pathways/translate_apptek.js +33 -0
  43. package/pathways/translate_subtitle.js +15 -8
  44. package/server/plugins/apptekTranslatePlugin.js +46 -91
  45. package/tests/apptekTranslatePlugin.test.js +0 -2
  46. package/tests/integration/apptekTranslatePlugin.integration.test.js +159 -93
  47. package/tests/translate_apptek.test.js +16 -0
@@ -1,182 +1,184 @@
1
- import fs from 'fs';
2
- import path from 'path';
3
- import { v4 as uuidv4 } from 'uuid';
4
- import { fileURLToPath } from 'url';
5
- import { ipAddress, port } from '../../start.js';
6
- import { sanitizeFilename } from '../../utils/filenameUtils.js';
1
+ import fs from "fs";
2
+ import path from "path";
3
+ import { generateShortId } from "../../utils/filenameUtils.js";
4
+ import { ipAddress, port } from "../../start.js";
7
5
 
8
- import { StorageProvider } from './StorageProvider.js';
6
+ import { StorageProvider } from "./StorageProvider.js";
9
7
 
10
8
  export class LocalStorageProvider extends StorageProvider {
11
- constructor(publicFolder) {
12
- super();
13
- if (!publicFolder) {
14
- throw new Error('Missing public folder path');
15
- }
16
- this.publicFolder = publicFolder;
17
- this.ensurePublicFolder();
9
+ constructor(publicFolder) {
10
+ super();
11
+ if (!publicFolder) {
12
+ throw new Error("Missing public folder path");
18
13
  }
14
+ this.publicFolder = publicFolder;
15
+ this.ensurePublicFolder();
16
+ }
19
17
 
20
- ensurePublicFolder() {
21
- if (!fs.existsSync(this.publicFolder)) {
22
- fs.mkdirSync(this.publicFolder, { recursive: true });
23
- }
18
+ ensurePublicFolder() {
19
+ if (!fs.existsSync(this.publicFolder)) {
20
+ fs.mkdirSync(this.publicFolder, { recursive: true });
24
21
  }
22
+ }
25
23
 
26
- async uploadFile(context, filePath, requestId, hash = null) {
27
- // Create request folder if it doesn't exist
28
- const requestFolder = path.join(this.publicFolder, requestId);
29
- if (!fs.existsSync(requestFolder)) {
30
- fs.mkdirSync(requestFolder, { recursive: true });
31
- }
32
-
33
- // Generate unique filename
34
- let baseName = sanitizeFilename(path.basename(filePath));
35
- // Local storage URLs shouldn't be double-encoded, so leave the name raw in the FS path,
36
- // but use encodeURIComponent when building the external URL so browsers read it fine.
37
- const encodedForUrl = encodeURIComponent(baseName);
38
- const uniqueFileName = `${uuidv4()}_${encodedForUrl}`;
39
- const destinationPath = path.join(requestFolder, uniqueFileName);
40
-
41
- // Copy file to public folder
42
- await fs.promises.copyFile(filePath, destinationPath);
43
-
44
- // Generate full URL
45
- const url = `http://${ipAddress}:${port}/files/${requestId}/${uniqueFileName}`;
46
-
47
- return {
48
- url,
49
- blobName: path.join(requestId, uniqueFileName)
50
- };
24
+ async uploadFile(context, filePath, requestId, hash = null, filename = null) {
25
+ // Create request folder if it doesn't exist
26
+ const requestFolder = path.join(this.publicFolder, requestId);
27
+ if (!fs.existsSync(requestFolder)) {
28
+ fs.mkdirSync(requestFolder, { recursive: true });
51
29
  }
52
30
 
53
- async deleteFiles(requestId) {
54
- if (!requestId) throw new Error('Missing requestId parameter');
55
-
56
- const requestFolder = path.join(this.publicFolder, requestId);
57
- const result = [];
58
-
59
- if (fs.existsSync(requestFolder)) {
60
- const files = await fs.promises.readdir(requestFolder);
61
- for (const file of files) {
62
- const filePath = path.join(requestFolder, file);
63
- await fs.promises.unlink(filePath);
64
- // Return the full path relative to the public folder
65
- result.push(path.join(requestId, file));
66
- }
67
- await fs.promises.rmdir(requestFolder);
68
- }
69
-
70
- return result;
31
+ // Use provided filename or generate LLM-friendly naming
32
+ let uniqueFileName;
33
+ if (filename) {
34
+ uniqueFileName = filename;
35
+ } else {
36
+ const fileExtension = path.extname(filePath);
37
+ const shortId = generateShortId();
38
+ uniqueFileName = `${shortId}${fileExtension}`;
71
39
  }
72
40
 
73
- async deleteFile(url) {
74
- if (!url) throw new Error('Missing URL parameter');
75
-
76
- const filePath = this.urlToFilePath(url);
77
- if (!filePath) {
78
- throw new Error('Invalid URL');
79
- }
80
-
81
- if (fs.existsSync(filePath)) {
82
- await fs.promises.unlink(filePath);
83
- // Try to remove the parent directory if it's empty
84
- const parentDir = path.dirname(filePath);
85
- try {
86
- await fs.promises.rmdir(parentDir);
87
- } catch (error) {
88
- // Ignore error if directory is not empty
89
- }
90
- return path.relative(this.publicFolder, filePath);
91
- }
92
-
93
- return null;
41
+ const destinationPath = path.join(requestFolder, uniqueFileName);
42
+
43
+ // Copy file to public folder
44
+ await fs.promises.copyFile(filePath, destinationPath);
45
+
46
+ // Generate full URL
47
+ const url = `http://${ipAddress}:${port}/files/${requestId}/${uniqueFileName}`;
48
+
49
+ return {
50
+ url,
51
+ blobName: path.join(requestId, uniqueFileName),
52
+ };
53
+ }
54
+
55
+ async deleteFiles(requestId) {
56
+ if (!requestId) throw new Error("Missing requestId parameter");
57
+
58
+ const requestFolder = path.join(this.publicFolder, requestId);
59
+ const result = [];
60
+
61
+ if (fs.existsSync(requestFolder)) {
62
+ const files = await fs.promises.readdir(requestFolder);
63
+ for (const file of files) {
64
+ const filePath = path.join(requestFolder, file);
65
+ await fs.promises.unlink(filePath);
66
+ // Return the full path relative to the public folder
67
+ result.push(path.join(requestId, file));
68
+ }
69
+ await fs.promises.rmdir(requestFolder);
94
70
  }
95
71
 
96
- async fileExists(url) {
97
- try {
98
- if (!url) {
99
- return false;
100
- }
101
-
102
- const filePath = this.urlToFilePath(url);
103
- if (!filePath) {
104
- return false;
105
- }
106
-
107
- // Check if file exists and is accessible
108
- try {
109
- await fs.promises.access(filePath, fs.constants.R_OK);
110
- return true;
111
- } catch (error) {
112
- return false;
113
- }
114
- } catch (error) {
115
- console.error('Error checking if file exists:', error);
116
- return false;
117
- }
72
+ return result;
73
+ }
74
+
75
+ async deleteFile(url) {
76
+ if (!url) throw new Error("Missing URL parameter");
77
+
78
+ const filePath = this.urlToFilePath(url);
79
+ if (!filePath) {
80
+ throw new Error("Invalid URL");
118
81
  }
119
82
 
120
- async downloadFile(url, destinationPath) {
121
- const sourcePath = this.urlToFilePath(url);
122
- if (!fs.existsSync(sourcePath)) {
123
- throw new Error('File not found');
124
- }
125
- await fs.promises.copyFile(sourcePath, destinationPath);
83
+ if (fs.existsSync(filePath)) {
84
+ await fs.promises.unlink(filePath);
85
+ // Try to remove the parent directory if it's empty
86
+ const parentDir = path.dirname(filePath);
87
+ try {
88
+ await fs.promises.rmdir(parentDir);
89
+ } catch (error) {
90
+ // Ignore error if directory is not empty
91
+ }
92
+ return path.relative(this.publicFolder, filePath);
126
93
  }
127
94
 
128
- async cleanup(urls) {
129
- if (!urls || !urls.length) return;
130
-
131
- const result = [];
132
- for (const url of urls) {
133
- try {
134
- const filePath = this.urlToFilePath(url);
135
- if (fs.existsSync(filePath)) {
136
- await fs.promises.unlink(filePath);
137
- result.push(path.relative(this.publicFolder, filePath));
138
- }
139
- } catch (error) {
140
- console.error(`Error cleaning up file ${url}:`, error);
141
- }
142
- }
143
-
144
- return result;
95
+ return null;
96
+ }
97
+
98
+ async fileExists(url) {
99
+ try {
100
+ if (!url) {
101
+ return false;
102
+ }
103
+
104
+ const filePath = this.urlToFilePath(url);
105
+ if (!filePath) {
106
+ return false;
107
+ }
108
+
109
+ // Check if file exists and is accessible
110
+ try {
111
+ await fs.promises.access(filePath, fs.constants.R_OK);
112
+ return true;
113
+ } catch (error) {
114
+ return false;
115
+ }
116
+ } catch (error) {
117
+ console.error("Error checking if file exists:", error);
118
+ return false;
145
119
  }
120
+ }
146
121
 
147
- isEncoded(str) {
148
- return /%[0-9A-Fa-f]{2}/.test(str);
122
+ async downloadFile(url, destinationPath) {
123
+ const sourcePath = this.urlToFilePath(url);
124
+ if (!fs.existsSync(sourcePath)) {
125
+ throw new Error("File not found");
149
126
  }
127
+ await fs.promises.copyFile(sourcePath, destinationPath);
128
+ }
129
+
130
+ async cleanup(urls) {
131
+ if (!urls || !urls.length) return;
150
132
 
151
- urlToFilePath(url) {
152
- try {
153
- // If it's a full URL, extract the pathname
154
- let urlPath = url;
155
- if (url.startsWith('http://') || url.startsWith('https://')) {
156
- const urlObj = new URL(url);
157
- urlPath = urlObj.pathname;
158
- }
159
-
160
- // Remove leading slash if present
161
- if (urlPath.startsWith('/')) {
162
- urlPath = urlPath.substring(1);
163
- }
164
-
165
- // Split into parts and decode each part
166
- const parts = urlPath.split('/');
167
- const decodedParts = parts.map(part => decodeURIComponent(part));
168
-
169
- // If the URL path starts with 'files', remove that segment because
170
- // our publicFolder already represents the root of '/files'.
171
- if (decodedParts.length && decodedParts[0] === 'files') {
172
- decodedParts.shift();
173
- }
174
-
175
- // Join with the public folder path
176
- return path.join(this.publicFolder, ...decodedParts);
177
- } catch (error) {
178
- console.error('Error converting URL to file path:', error);
179
- return null;
133
+ const result = [];
134
+ for (const url of urls) {
135
+ try {
136
+ const filePath = this.urlToFilePath(url);
137
+ if (fs.existsSync(filePath)) {
138
+ await fs.promises.unlink(filePath);
139
+ result.push(path.relative(this.publicFolder, filePath));
180
140
  }
141
+ } catch (error) {
142
+ console.error(`Error cleaning up file ${url}:`, error);
143
+ }
144
+ }
145
+
146
+ return result;
147
+ }
148
+
149
+ isEncoded(str) {
150
+ return /%[0-9A-Fa-f]{2}/.test(str);
151
+ }
152
+
153
+ urlToFilePath(url) {
154
+ try {
155
+ // If it's a full URL, extract the pathname
156
+ let urlPath = url;
157
+ if (url.startsWith("http://") || url.startsWith("https://")) {
158
+ const urlObj = new URL(url);
159
+ urlPath = urlObj.pathname;
160
+ }
161
+
162
+ // Remove leading slash if present
163
+ if (urlPath.startsWith("/")) {
164
+ urlPath = urlPath.substring(1);
165
+ }
166
+
167
+ // Split into parts and decode each part
168
+ const parts = urlPath.split("/");
169
+ const decodedParts = parts.map((part) => decodeURIComponent(part));
170
+
171
+ // If the URL path starts with 'files', remove that segment because
172
+ // our publicFolder already represents the root of '/files'.
173
+ if (decodedParts.length && decodedParts[0] === "files") {
174
+ decodedParts.shift();
175
+ }
176
+
177
+ // Join with the public folder path
178
+ return path.join(this.publicFolder, ...decodedParts);
179
+ } catch (error) {
180
+ console.error("Error converting URL to file path:", error);
181
+ return null;
181
182
  }
182
- }
183
+ }
184
+ }
@@ -1,86 +1,88 @@
1
- import { AzureStorageProvider } from './AzureStorageProvider.js';
2
- import { GCSStorageProvider } from './GCSStorageProvider.js';
3
- import { LocalStorageProvider } from './LocalStorageProvider.js';
4
- import path from 'path';
5
- import { fileURLToPath } from 'url';
1
+ import { AzureStorageProvider } from "./AzureStorageProvider.js";
2
+ import { GCSStorageProvider } from "./GCSStorageProvider.js";
3
+ import { LocalStorageProvider } from "./LocalStorageProvider.js";
4
+ import path from "path";
5
+ import { fileURLToPath } from "url";
6
6
 
7
7
  export class StorageFactory {
8
- constructor() {
9
- this.providers = new Map();
10
- }
8
+ constructor() {
9
+ this.providers = new Map();
10
+ }
11
11
 
12
- getPrimaryProvider() {
13
- if (process.env.AZURE_STORAGE_CONNECTION_STRING) {
14
- return this.getAzureProvider();
15
- }
16
- return this.getLocalProvider();
12
+ getPrimaryProvider() {
13
+ if (process.env.AZURE_STORAGE_CONNECTION_STRING) {
14
+ return this.getAzureProvider();
17
15
  }
16
+ return this.getLocalProvider();
17
+ }
18
18
 
19
- getAzureProvider() {
20
- const key = 'azure';
21
- if (!this.providers.has(key)) {
22
- const provider = new AzureStorageProvider(
23
- process.env.AZURE_STORAGE_CONNECTION_STRING,
24
- process.env.AZURE_STORAGE_CONTAINER_NAME || 'whispertempfiles'
25
- );
26
- this.providers.set(key, provider);
27
- }
28
- return this.providers.get(key);
19
+ getAzureProvider() {
20
+ const key = "azure";
21
+ if (!this.providers.has(key)) {
22
+ const provider = new AzureStorageProvider(
23
+ process.env.AZURE_STORAGE_CONNECTION_STRING,
24
+ process.env.AZURE_STORAGE_CONTAINER_NAME || "whispertempfiles",
25
+ );
26
+ this.providers.set(key, provider);
29
27
  }
28
+ return this.providers.get(key);
29
+ }
30
30
 
31
- getGCSProvider() {
32
- const key = 'gcs';
33
- if (!this.providers.has(key)) {
34
- const credentials = this.parseGCSCredentials();
35
- if (!credentials) {
36
- return null;
37
- }
38
- const provider = new GCSStorageProvider(
39
- credentials,
40
- process.env.GCS_BUCKETNAME || 'cortextempfiles'
41
- );
42
- this.providers.set(key, provider);
43
- }
44
- return this.providers.get(key);
31
+ getGCSProvider() {
32
+ const key = "gcs";
33
+ if (!this.providers.has(key)) {
34
+ const credentials = this.parseGCSCredentials();
35
+ if (!credentials) {
36
+ return null;
37
+ }
38
+ const provider = new GCSStorageProvider(
39
+ credentials,
40
+ process.env.GCS_BUCKETNAME || "cortextempfiles",
41
+ );
42
+ this.providers.set(key, provider);
45
43
  }
44
+ return this.providers.get(key);
45
+ }
46
46
 
47
- getLocalProvider() {
48
- const key = 'local';
49
- if (!this.providers.has(key)) {
50
- let folder = process.env.PUBLIC_FOLDER;
51
- if (!folder) {
52
- // Compute src/files relative to current directory
53
- const __dirname = path.dirname(fileURLToPath(import.meta.url));
54
- folder = path.join(__dirname, '..', '..', 'files');
55
- }
56
- const provider = new LocalStorageProvider(folder);
57
- this.providers.set(key, provider);
58
- }
59
- return this.providers.get(key);
47
+ getLocalProvider() {
48
+ const key = "local";
49
+ if (!this.providers.has(key)) {
50
+ let folder = process.env.PUBLIC_FOLDER;
51
+ if (!folder) {
52
+ // Compute src/files relative to current directory
53
+ const __dirname = path.dirname(fileURLToPath(import.meta.url));
54
+ folder = path.join(__dirname, "..", "..", "files");
55
+ }
56
+ const provider = new LocalStorageProvider(folder);
57
+ this.providers.set(key, provider);
60
58
  }
59
+ return this.providers.get(key);
60
+ }
61
61
 
62
- parseGCSCredentials() {
63
- const key = process.env.GCP_SERVICE_ACCOUNT_KEY_BASE64 || process.env.GCP_SERVICE_ACCOUNT_KEY;
64
- if (!key) {
65
- return null;
66
- }
62
+ parseGCSCredentials() {
63
+ const key =
64
+ process.env.GCP_SERVICE_ACCOUNT_KEY_BASE64 ||
65
+ process.env.GCP_SERVICE_ACCOUNT_KEY;
66
+ if (!key) {
67
+ return null;
68
+ }
67
69
 
68
- try {
69
- if (this.isBase64(key)) {
70
- return JSON.parse(Buffer.from(key, 'base64').toString());
71
- }
72
- return JSON.parse(key);
73
- } catch (error) {
74
- console.error('Error parsing GCS credentials:', error);
75
- return null;
76
- }
70
+ try {
71
+ if (this.isBase64(key)) {
72
+ return JSON.parse(Buffer.from(key, "base64").toString());
73
+ }
74
+ return JSON.parse(key);
75
+ } catch (error) {
76
+ console.error("Error parsing GCS credentials:", error);
77
+ return null;
77
78
  }
79
+ }
78
80
 
79
- isBase64(str) {
80
- try {
81
- return btoa(atob(str)) === str;
82
- } catch (err) {
83
- return false;
84
- }
81
+ isBase64(str) {
82
+ try {
83
+ return btoa(atob(str)) === str;
84
+ } catch (err) {
85
+ return false;
85
86
  }
86
- }
87
+ }
88
+ }
@@ -2,52 +2,53 @@
2
2
  * Base interface for storage providers
3
3
  */
4
4
  export class StorageProvider {
5
- /**
6
- * Upload a file to storage
7
- * @param {Object} context - The context object
8
- * @param {string} filePath - Path to the file to upload
9
- * @param {string} requestId - Unique identifier for the request
10
- * @param {string} [hash] - Optional hash of the file
11
- * @returns {Promise<{url: string, blobName: string}>} The URL and blob name of the uploaded file
12
- */
13
- async uploadFile(context, filePath, requestId, hash = null) {
14
- throw new Error('Method not implemented');
15
- }
5
+ /**
6
+ * Upload a file to storage
7
+ * @param {Object} context - The context object
8
+ * @param {string} filePath - Path to the file to upload
9
+ * @param {string} requestId - Unique identifier for the request
10
+ * @param {string} [hash] - Optional hash of the file
11
+ * @param {string} [filename] - Optional filename to use (if not provided, provider will generate one)
12
+ * @returns {Promise<{url: string, blobName: string}>} The URL and blob name of the uploaded file
13
+ */
14
+ async uploadFile(context, filePath, requestId, hash = null, filename = null) {
15
+ throw new Error("Method not implemented");
16
+ }
16
17
 
17
- /**
18
- * Delete files associated with a request ID
19
- * @param {string} requestId - The request ID to delete files for
20
- * @returns {Promise<string[]>} Array of deleted file URLs
21
- */
22
- async deleteFiles(requestId) {
23
- throw new Error('Method not implemented');
24
- }
18
+ /**
19
+ * Delete files associated with a request ID
20
+ * @param {string} requestId - The request ID to delete files for
21
+ * @returns {Promise<string[]>} Array of deleted file URLs
22
+ */
23
+ async deleteFiles(requestId) {
24
+ throw new Error("Method not implemented");
25
+ }
25
26
 
26
- /**
27
- * Check if a file exists at the given URL
28
- * @param {string} url - The URL to check
29
- * @returns {Promise<boolean>} Whether the file exists
30
- */
31
- async fileExists(url) {
32
- throw new Error('Method not implemented');
33
- }
27
+ /**
28
+ * Check if a file exists at the given URL
29
+ * @param {string} url - The URL to check
30
+ * @returns {Promise<boolean>} Whether the file exists
31
+ */
32
+ async fileExists(url) {
33
+ throw new Error("Method not implemented");
34
+ }
34
35
 
35
- /**
36
- * Download a file from storage
37
- * @param {string} url - The URL of the file to download
38
- * @param {string} destinationPath - Where to save the downloaded file
39
- * @returns {Promise<void>}
40
- */
41
- async downloadFile(url, destinationPath) {
42
- throw new Error('Method not implemented');
43
- }
36
+ /**
37
+ * Download a file from storage
38
+ * @param {string} url - The URL of the file to download
39
+ * @param {string} destinationPath - Where to save the downloaded file
40
+ * @returns {Promise<void>}
41
+ */
42
+ async downloadFile(url, destinationPath) {
43
+ throw new Error("Method not implemented");
44
+ }
44
45
 
45
- /**
46
- * Clean up files by their URLs
47
- * @param {string[]} urls - Array of URLs to clean up
48
- * @returns {Promise<void>}
49
- */
50
- async cleanup(urls) {
51
- throw new Error('Method not implemented');
52
- }
53
- }
46
+ /**
47
+ * Clean up files by their URLs
48
+ * @param {string[]} urls - Array of URLs to clean up
49
+ * @returns {Promise<void>}
50
+ */
51
+ async cleanup(urls) {
52
+ throw new Error("Method not implemented");
53
+ }
54
+ }