@mastra/archil 0.0.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.
@@ -0,0 +1,116 @@
1
+ /**
2
+ * Archil Filesystem Provider
3
+ *
4
+ * A filesystem implementation backed by Archil — elastic, serverless
5
+ * filesystems for AI agents. Uses the `disk` SDK's S3-compatible object
6
+ * API for fast reads/writes and `exec` for POSIX operations.
7
+ */
8
+ import type { FileContent, FileStat, FileEntry, ReadOptions, WriteOptions, ListOptions, RemoveOptions, CopyOptions, FilesystemInfo, FilesystemIcon, ProviderStatus, MastraFilesystemOptions } from '@mastra/core/workspace';
9
+ import { MastraFilesystem } from '@mastra/core/workspace';
10
+ import { Archil } from 'disk';
11
+ import type { Disk, CreateDiskRequest, ExecResult, GrepOptions, GrepResult, ListObjectsOptions, ListObjectsResult, ObjectMetadata, ShareUrlOptions, ShareUrlResult } from 'disk';
12
+ export interface ArchilFilesystemOptions extends MastraFilesystemOptions {
13
+ /** Unique identifier for this filesystem instance */
14
+ id?: string;
15
+ /** Human-friendly display name for the UI */
16
+ displayName?: string;
17
+ /** Icon identifier for the UI (defaults to 'cloud') */
18
+ icon?: FilesystemIcon;
19
+ /** Description shown in tooltips */
20
+ description?: string;
21
+ /** Mount as read-only (blocks write operations) */
22
+ readOnly?: boolean;
23
+ /**
24
+ * Existing disk ID to attach to (e.g. "dsk-0123456789abcdef").
25
+ * Mutually exclusive with `createDiskOptions`.
26
+ */
27
+ diskId?: string;
28
+ /**
29
+ * Options for creating a new disk on init.
30
+ * Mutually exclusive with `diskId`.
31
+ */
32
+ createDiskOptions?: CreateDiskRequest;
33
+ /**
34
+ * Archil API key. Falls back to ARCHIL_API_KEY env var.
35
+ */
36
+ apiKey?: string;
37
+ /**
38
+ * Archil region (e.g. "aws-us-east-1"). Falls back to ARCHIL_REGION env var.
39
+ */
40
+ region?: string;
41
+ /**
42
+ * Override the Archil control-plane base URL (for testing/self-hosted).
43
+ */
44
+ baseUrl?: string;
45
+ /**
46
+ * Override the S3-compatible API base URL.
47
+ * Falls back to ARCHIL_S3_BASE_URL env var.
48
+ */
49
+ s3BaseUrl?: string;
50
+ }
51
+ export declare class ArchilFilesystem extends MastraFilesystem {
52
+ readonly id: string;
53
+ readonly name = "ArchilFilesystem";
54
+ readonly provider = "archil";
55
+ readonly readOnly?: boolean;
56
+ readonly displayName?: string;
57
+ readonly icon: FilesystemIcon;
58
+ readonly description?: string;
59
+ status: ProviderStatus;
60
+ private _disk;
61
+ private _archil;
62
+ private readonly _diskId?;
63
+ private readonly _createDiskOptions?;
64
+ private readonly _archilOptions;
65
+ constructor(options: ArchilFilesystemOptions);
66
+ /** The underlying Archil Disk instance (available after init). */
67
+ get disk(): Disk;
68
+ /** The Archil SDK client instance. */
69
+ get archil(): Archil;
70
+ init(): Promise<void>;
71
+ destroy(): Promise<void>;
72
+ isReady(): boolean;
73
+ getInfo(): FilesystemInfo<{
74
+ diskId: string;
75
+ region: string;
76
+ diskName: string;
77
+ }>;
78
+ getInstructions(): string;
79
+ /**
80
+ * Execute a shell command on the disk's filesystem.
81
+ * The disk is mounted as the working directory.
82
+ */
83
+ exec(command: string): Promise<ExecResult>;
84
+ /**
85
+ * Parallel grep across files on the disk.
86
+ */
87
+ grep(opts: GrepOptions): Promise<GrepResult>;
88
+ /**
89
+ * Create a signed, time-limited download URL for a file.
90
+ */
91
+ share(key: string, opts?: ShareUrlOptions): Promise<ShareUrlResult>;
92
+ /**
93
+ * List objects using the S3-compatible API directly.
94
+ */
95
+ listObjects(prefix?: string, opts?: ListObjectsOptions): Promise<ListObjectsResult>;
96
+ /**
97
+ * Get object metadata without downloading.
98
+ */
99
+ headObject(key: string): Promise<ObjectMetadata | null>;
100
+ readFile(path: string, options?: ReadOptions): Promise<string | Buffer>;
101
+ writeFile(path: string, content: FileContent, options?: WriteOptions): Promise<void>;
102
+ appendFile(path: string, content: FileContent): Promise<void>;
103
+ deleteFile(path: string, options?: RemoveOptions): Promise<void>;
104
+ copyFile(src: string, dest: string, options?: CopyOptions): Promise<void>;
105
+ moveFile(src: string, dest: string, options?: CopyOptions): Promise<void>;
106
+ mkdir(path: string, options?: {
107
+ recursive?: boolean;
108
+ }): Promise<void>;
109
+ rmdir(path: string, options?: RemoveOptions): Promise<void>;
110
+ readdir(path: string, options?: ListOptions): Promise<FileEntry[]>;
111
+ private readdirRecursive;
112
+ exists(path: string): Promise<boolean>;
113
+ stat(path: string): Promise<FileStat>;
114
+ private assertWritable;
115
+ }
116
+ //# sourceMappingURL=filesystem.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"filesystem.d.ts","sourceRoot":"","sources":["../src/filesystem.ts"],"names":[],"mappings":"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"}
package/dist/index.cjs ADDED
@@ -0,0 +1,491 @@
1
+ 'use strict';
2
+
3
+ var workspace = require('@mastra/core/workspace');
4
+ var disk = require('disk');
5
+
6
+ // src/filesystem.ts
7
+ var MIME_TYPES = {
8
+ ".txt": "text/plain",
9
+ ".md": "text/markdown",
10
+ ".html": "text/html",
11
+ ".css": "text/css",
12
+ ".csv": "text/csv",
13
+ ".xml": "text/xml",
14
+ ".js": "text/javascript",
15
+ ".mjs": "text/javascript",
16
+ ".ts": "text/typescript",
17
+ ".tsx": "text/typescript",
18
+ ".jsx": "text/javascript",
19
+ ".json": "application/json",
20
+ ".yaml": "text/yaml",
21
+ ".yml": "text/yaml",
22
+ ".py": "text/x-python",
23
+ ".sh": "text/x-shellscript",
24
+ ".png": "image/png",
25
+ ".jpg": "image/jpeg",
26
+ ".jpeg": "image/jpeg",
27
+ ".gif": "image/gif",
28
+ ".svg": "image/svg+xml",
29
+ ".webp": "image/webp",
30
+ ".pdf": "application/pdf",
31
+ ".zip": "application/zip",
32
+ ".gz": "application/gzip",
33
+ ".tar": "application/x-tar"
34
+ };
35
+ function getMimeType(path) {
36
+ const ext = path.toLowerCase().match(/\.[^.]+$/)?.[0];
37
+ return ext ? MIME_TYPES[ext] ?? "application/octet-stream" : "application/octet-stream";
38
+ }
39
+ function trimSlashes(s) {
40
+ let start = 0;
41
+ let end = s.length;
42
+ while (start < end && s[start] === "/") start++;
43
+ while (end > start && s[end - 1] === "/") end--;
44
+ return s.slice(start, end);
45
+ }
46
+ function toKey(path) {
47
+ return trimSlashes(path) || "";
48
+ }
49
+ function basename(path) {
50
+ const key = toKey(path);
51
+ const idx = key.lastIndexOf("/");
52
+ return idx === -1 ? key : key.slice(idx + 1);
53
+ }
54
+ function dirname(path) {
55
+ const key = toKey(path);
56
+ const idx = key.lastIndexOf("/");
57
+ return idx === -1 ? "" : key.slice(0, idx);
58
+ }
59
+ function shellEscape(s) {
60
+ return "'" + s.replace(/'/g, "'\\''") + "'";
61
+ }
62
+ var ArchilFilesystem = class extends workspace.MastraFilesystem {
63
+ id;
64
+ name = "ArchilFilesystem";
65
+ provider = "archil";
66
+ readOnly;
67
+ displayName;
68
+ icon;
69
+ description;
70
+ status = "pending";
71
+ _disk = null;
72
+ _archil = null;
73
+ _diskId;
74
+ _createDiskOptions;
75
+ _archilOptions;
76
+ constructor(options) {
77
+ super({ ...options, name: "ArchilFilesystem" });
78
+ this.id = options.id ?? `archil-fs-${Date.now().toString(36)}-${Math.random().toString(36).slice(2, 8)}`;
79
+ this.readOnly = options.readOnly;
80
+ this.displayName = options.displayName ?? "Archil";
81
+ this.icon = options.icon ?? "cloud";
82
+ this.description = options.description ?? "Elastic serverless filesystem powered by Archil";
83
+ this._diskId = options.diskId;
84
+ this._createDiskOptions = options.createDiskOptions;
85
+ this._archilOptions = {
86
+ apiKey: options.apiKey,
87
+ region: options.region,
88
+ baseUrl: options.baseUrl,
89
+ s3BaseUrl: options.s3BaseUrl
90
+ };
91
+ }
92
+ // ---------------------------------------------------------------------------
93
+ // Public accessors
94
+ // ---------------------------------------------------------------------------
95
+ /** The underlying Archil Disk instance (available after init). */
96
+ get disk() {
97
+ if (!this._disk) {
98
+ throw new Error("ArchilFilesystem not initialized \u2014 call init() first");
99
+ }
100
+ return this._disk;
101
+ }
102
+ /** The Archil SDK client instance. */
103
+ get archil() {
104
+ if (!this._archil) {
105
+ this._archil = new disk.Archil(this._archilOptions);
106
+ }
107
+ return this._archil;
108
+ }
109
+ // ---------------------------------------------------------------------------
110
+ // Lifecycle
111
+ // ---------------------------------------------------------------------------
112
+ async init() {
113
+ try {
114
+ if (this._diskId && this._createDiskOptions) {
115
+ throw new Error("diskId and createDiskOptions are mutually exclusive");
116
+ }
117
+ if (this._diskId) {
118
+ this._disk = await this.archil.disks.get(this._diskId);
119
+ } else if (this._createDiskOptions) {
120
+ const result = await this.archil.disks.create(this._createDiskOptions);
121
+ this._disk = result.disk;
122
+ } else {
123
+ throw new Error("Either diskId or createDiskOptions must be provided");
124
+ }
125
+ this.status = "ready";
126
+ } catch (err) {
127
+ this.status = "error";
128
+ this.error = err instanceof Error ? err.message : String(err);
129
+ throw err;
130
+ }
131
+ }
132
+ async destroy() {
133
+ this._disk = null;
134
+ this._archil = null;
135
+ }
136
+ isReady() {
137
+ return this.status === "ready" && this._disk !== null;
138
+ }
139
+ getInfo() {
140
+ return {
141
+ id: this.id,
142
+ name: this.name,
143
+ provider: this.provider,
144
+ status: this.status,
145
+ error: this.error,
146
+ readOnly: this.readOnly,
147
+ icon: this.icon,
148
+ metadata: {
149
+ diskId: this._disk?.id ?? "",
150
+ region: this._disk?.region ?? "",
151
+ diskName: this._disk?.name ?? ""
152
+ }
153
+ };
154
+ }
155
+ getInstructions() {
156
+ const access = this.readOnly ? "Read-only" : "Persistent";
157
+ const diskName = this._disk?.name ?? "Archil disk";
158
+ return `Archil elastic filesystem "${diskName}". ${access} storage \u2014 files persist across sessions. Supports serverless execution via exec().`;
159
+ }
160
+ // ---------------------------------------------------------------------------
161
+ // Archil-specific operations
162
+ // ---------------------------------------------------------------------------
163
+ /**
164
+ * Execute a shell command on the disk's filesystem.
165
+ * The disk is mounted as the working directory.
166
+ */
167
+ async exec(command) {
168
+ await this.ensureReady();
169
+ this.assertWritable();
170
+ return this.disk.exec(command);
171
+ }
172
+ /**
173
+ * Parallel grep across files on the disk.
174
+ */
175
+ async grep(opts) {
176
+ await this.ensureReady();
177
+ return this.disk.grep(opts);
178
+ }
179
+ /**
180
+ * Create a signed, time-limited download URL for a file.
181
+ */
182
+ async share(key, opts) {
183
+ await this.ensureReady();
184
+ return this.disk.share(key, opts);
185
+ }
186
+ /**
187
+ * List objects using the S3-compatible API directly.
188
+ */
189
+ async listObjects(prefix, opts) {
190
+ await this.ensureReady();
191
+ return this.disk.listObjects(prefix, opts);
192
+ }
193
+ /**
194
+ * Get object metadata without downloading.
195
+ */
196
+ async headObject(key) {
197
+ await this.ensureReady();
198
+ return this.disk.headObject(key);
199
+ }
200
+ // ---------------------------------------------------------------------------
201
+ // File Operations (WorkspaceFilesystem interface)
202
+ // ---------------------------------------------------------------------------
203
+ async readFile(path, options) {
204
+ await this.ensureReady();
205
+ const key = toKey(path);
206
+ if (!key) {
207
+ throw new Error("Cannot read file at root path");
208
+ }
209
+ try {
210
+ const data = await this.disk.getObject(key);
211
+ if (options?.encoding) {
212
+ return Buffer.from(data).toString(options.encoding);
213
+ }
214
+ return Buffer.from(data);
215
+ } catch (err) {
216
+ if (isNotFound(err)) {
217
+ throw new workspace.FileNotFoundError(path);
218
+ }
219
+ throw err;
220
+ }
221
+ }
222
+ async writeFile(path, content, options) {
223
+ await this.ensureReady();
224
+ this.assertWritable();
225
+ const key = toKey(path);
226
+ if (!key) {
227
+ throw new Error("Cannot write file at root path");
228
+ }
229
+ if (options?.overwrite === false) {
230
+ const exists = await this.disk.objectExists(key);
231
+ if (exists) {
232
+ throw new workspace.FileExistsError(path);
233
+ }
234
+ }
235
+ if (options?.recursive) {
236
+ const dir = dirname(path);
237
+ if (dir) {
238
+ await this.disk.exec(`mkdir -p ${shellEscape(dir)}`);
239
+ }
240
+ }
241
+ const body = typeof content === "string" ? content : content instanceof Uint8Array ? content : new Uint8Array(content);
242
+ const mimeType = options?.mimeType ?? getMimeType(path);
243
+ await this.disk.putObject(key, body, mimeType);
244
+ }
245
+ async appendFile(path, content) {
246
+ await this.ensureReady();
247
+ this.assertWritable();
248
+ const key = toKey(path);
249
+ if (!key) {
250
+ throw new Error("Cannot append to root path");
251
+ }
252
+ const data = typeof content === "string" ? content : Buffer.from(content).toString("base64");
253
+ if (typeof content === "string") {
254
+ await this.disk.exec(`printf '%s' ${shellEscape(data)} >> ${shellEscape(key)}`);
255
+ } else {
256
+ await this.disk.exec(`printf '%s' ${shellEscape(data)} | base64 -d >> ${shellEscape(key)}`);
257
+ }
258
+ }
259
+ async deleteFile(path, options) {
260
+ await this.ensureReady();
261
+ this.assertWritable();
262
+ const key = toKey(path);
263
+ if (!key) {
264
+ throw new Error("Cannot delete root path");
265
+ }
266
+ if (!options?.force) {
267
+ const exists = await this.disk.objectExists(key);
268
+ if (!exists) {
269
+ throw new workspace.FileNotFoundError(path);
270
+ }
271
+ }
272
+ await this.disk.deleteObject(key);
273
+ }
274
+ async copyFile(src, dest, options) {
275
+ await this.ensureReady();
276
+ this.assertWritable();
277
+ const srcKey = toKey(src);
278
+ const destKey = toKey(dest);
279
+ if (!options?.overwrite) {
280
+ const exists = await this.disk.objectExists(destKey);
281
+ if (exists) {
282
+ throw new workspace.FileExistsError(dest);
283
+ }
284
+ }
285
+ const flags = options?.recursive ? "-r" : "";
286
+ const result = await this.disk.exec(`cp ${flags} ${shellEscape(srcKey)} ${shellEscape(destKey)}`);
287
+ if (result.exitCode !== 0) {
288
+ if (result.stderr.includes("No such file")) {
289
+ throw new workspace.FileNotFoundError(src);
290
+ }
291
+ throw new Error(`cp failed: ${result.stderr}`);
292
+ }
293
+ }
294
+ async moveFile(src, dest, options) {
295
+ await this.ensureReady();
296
+ this.assertWritable();
297
+ const srcKey = toKey(src);
298
+ const destKey = toKey(dest);
299
+ if (!options?.overwrite) {
300
+ const exists = await this.disk.objectExists(destKey);
301
+ if (exists) {
302
+ throw new workspace.FileExistsError(dest);
303
+ }
304
+ }
305
+ const result = await this.disk.exec(`mv ${shellEscape(srcKey)} ${shellEscape(destKey)}`);
306
+ if (result.exitCode !== 0) {
307
+ if (result.stderr.includes("No such file")) {
308
+ throw new workspace.FileNotFoundError(src);
309
+ }
310
+ throw new Error(`mv failed: ${result.stderr}`);
311
+ }
312
+ }
313
+ // ---------------------------------------------------------------------------
314
+ // Directory Operations
315
+ // ---------------------------------------------------------------------------
316
+ async mkdir(path, options) {
317
+ await this.ensureReady();
318
+ this.assertWritable();
319
+ const key = toKey(path);
320
+ if (!key) return;
321
+ const flag = options?.recursive ? "-p" : "";
322
+ const result = await this.disk.exec(`mkdir ${flag} ${shellEscape(key)}`);
323
+ if (result.exitCode !== 0) {
324
+ if (result.stderr.includes("File exists")) {
325
+ throw new workspace.FileExistsError(path);
326
+ }
327
+ throw new Error(`mkdir failed: ${result.stderr}`);
328
+ }
329
+ }
330
+ async rmdir(path, options) {
331
+ await this.ensureReady();
332
+ this.assertWritable();
333
+ const key = toKey(path);
334
+ if (!key) {
335
+ throw new Error("Cannot remove root directory");
336
+ }
337
+ const cmd = options?.recursive ? `rm -r${options?.force ? "f" : ""} ${shellEscape(key)}` : `rmdir ${shellEscape(key)}`;
338
+ const result = await this.disk.exec(options?.force ? `${cmd} 2>/dev/null; true` : cmd);
339
+ if (result.exitCode !== 0 && !options?.force) {
340
+ if (result.stderr.includes("No such file") || result.stderr.includes("not found")) {
341
+ throw new workspace.FileNotFoundError(path);
342
+ }
343
+ throw new Error(`rmdir failed: ${result.stderr}`);
344
+ }
345
+ }
346
+ async readdir(path, options) {
347
+ await this.ensureReady();
348
+ const key = toKey(path);
349
+ const prefix = key ? key + "/" : "";
350
+ if (options?.recursive) {
351
+ return this.readdirRecursive(prefix, options);
352
+ }
353
+ const result = await this.disk.listObjects(prefix, { recursive: false });
354
+ const entries = [];
355
+ for (const obj of result.objects) {
356
+ const name = obj.key.slice(prefix.length);
357
+ if (!name || name.includes("/")) continue;
358
+ if (options?.extension) {
359
+ const extensions = Array.isArray(options.extension) ? options.extension : [options.extension];
360
+ const ext = name.match(/\.[^.]+$/)?.[0] ?? "";
361
+ if (!extensions.includes(ext)) continue;
362
+ }
363
+ entries.push({
364
+ name,
365
+ type: "file",
366
+ size: obj.size
367
+ });
368
+ }
369
+ for (const cp of result.commonPrefixes) {
370
+ const name = trimSlashes(cp.slice(prefix.length));
371
+ if (!name) continue;
372
+ entries.push({
373
+ name,
374
+ type: "directory"
375
+ });
376
+ }
377
+ return entries;
378
+ }
379
+ async readdirRecursive(prefix, options) {
380
+ const result = await this.disk.listObjects(prefix, { recursive: true });
381
+ const entries = [];
382
+ for (const obj of result.objects) {
383
+ const relativePath = obj.key.slice(prefix.length);
384
+ if (!relativePath) continue;
385
+ if (options?.maxDepth !== void 0) {
386
+ const depth = relativePath.split("/").length - 1;
387
+ if (depth > options.maxDepth) continue;
388
+ }
389
+ if (options?.extension) {
390
+ const extensions = Array.isArray(options.extension) ? options.extension : [options.extension];
391
+ const ext = relativePath.match(/\.[^.]+$/)?.[0] ?? "";
392
+ if (!extensions.includes(ext)) continue;
393
+ }
394
+ entries.push({
395
+ name: relativePath,
396
+ type: "file",
397
+ size: obj.size
398
+ });
399
+ }
400
+ return entries;
401
+ }
402
+ // ---------------------------------------------------------------------------
403
+ // Path Operations
404
+ // ---------------------------------------------------------------------------
405
+ async exists(path) {
406
+ await this.ensureReady();
407
+ const key = toKey(path);
408
+ if (!key) return true;
409
+ const fileExists = await this.disk.objectExists(key);
410
+ if (fileExists) return true;
411
+ const result = await this.disk.listObjects(key + "/", { singlePage: true, limit: 1 });
412
+ return result.objects.length > 0 || result.commonPrefixes.length > 0;
413
+ }
414
+ async stat(path) {
415
+ await this.ensureReady();
416
+ const key = toKey(path);
417
+ if (!key) {
418
+ return {
419
+ name: "",
420
+ path: "/",
421
+ type: "directory",
422
+ size: 0,
423
+ createdAt: /* @__PURE__ */ new Date(0),
424
+ modifiedAt: /* @__PURE__ */ new Date(0)
425
+ };
426
+ }
427
+ const meta = await this.disk.headObject(key);
428
+ if (meta) {
429
+ return {
430
+ name: basename(path),
431
+ path: "/" + key,
432
+ type: "file",
433
+ size: meta.size,
434
+ createdAt: meta.lastModified ?? /* @__PURE__ */ new Date(0),
435
+ modifiedAt: meta.lastModified ?? /* @__PURE__ */ new Date(0),
436
+ mimeType: meta.contentType
437
+ };
438
+ }
439
+ const result = await this.disk.listObjects(key + "/", { singlePage: true, limit: 1 });
440
+ if (result.objects.length > 0 || result.commonPrefixes.length > 0) {
441
+ return {
442
+ name: basename(path),
443
+ path: "/" + key,
444
+ type: "directory",
445
+ size: 0,
446
+ createdAt: /* @__PURE__ */ new Date(0),
447
+ modifiedAt: /* @__PURE__ */ new Date(0)
448
+ };
449
+ }
450
+ throw new workspace.FileNotFoundError(path);
451
+ }
452
+ // ---------------------------------------------------------------------------
453
+ // Helpers
454
+ // ---------------------------------------------------------------------------
455
+ assertWritable() {
456
+ if (this.readOnly) {
457
+ throw new Error("Filesystem is read-only");
458
+ }
459
+ }
460
+ };
461
+ function isNotFound(err) {
462
+ if (!err || typeof err !== "object") return false;
463
+ const e = err;
464
+ return e.status === 404 || e.code === "NoSuchKey";
465
+ }
466
+
467
+ // src/provider.ts
468
+ var archilFilesystemProvider = {
469
+ id: "archil",
470
+ name: "Archil",
471
+ description: "Elastic, serverless filesystem for AI agents (Archil)",
472
+ configSchema: {
473
+ type: "object",
474
+ oneOf: [{ required: ["diskId"] }, { required: ["createDiskOptions"] }],
475
+ properties: {
476
+ diskId: { type: "string", description: 'Existing Archil disk ID (e.g. "dsk-0123456789abcdef")' },
477
+ createDiskOptions: { type: "object", description: "Options used to create a new Archil disk on init" },
478
+ apiKey: { type: "string", description: "Archil API key (falls back to ARCHIL_API_KEY env var)" },
479
+ region: { type: "string", description: 'Archil region (e.g. "aws-us-east-1")' },
480
+ readOnly: { type: "boolean", description: "Mount as read-only", default: false },
481
+ baseUrl: { type: "string", description: "Custom control-plane URL (for testing)" },
482
+ s3BaseUrl: { type: "string", description: "Custom S3 API URL" }
483
+ }
484
+ },
485
+ createFilesystem: (config) => new ArchilFilesystem(config)
486
+ };
487
+
488
+ exports.ArchilFilesystem = ArchilFilesystem;
489
+ exports.archilFilesystemProvider = archilFilesystemProvider;
490
+ //# sourceMappingURL=index.cjs.map
491
+ //# sourceMappingURL=index.cjs.map