@mastra/s3 0.6.1-alpha.0 → 0.6.1
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/CHANGELOG.md +35 -0
- package/dist/filesystem/index.d.ts +1 -1
- package/dist/filesystem/index.d.ts.map +1 -1
- package/dist/index.cjs +61 -29
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +61 -29
- package/dist/index.js.map +1 -1
- package/package.json +5 -5
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,40 @@
|
|
|
1
1
|
# @mastra/s3
|
|
2
2
|
|
|
3
|
+
## 0.6.1
|
|
4
|
+
|
|
5
|
+
### Patch Changes
|
|
6
|
+
|
|
7
|
+
- dependencies updates: ([#20406](https://github.com/mastra-ai/mastra/pull/20406))
|
|
8
|
+
- Updated dependency [`@aws-sdk/client-s3@^3.1095.0` ↗︎](https://www.npmjs.com/package/@aws-sdk/client-s3/v/3.1095.0) (from `^3.1058.0`, in `dependencies`)
|
|
9
|
+
|
|
10
|
+
- Fixed `mkdir()` on Google Cloud Storage and S3 workspaces. An empty directory is now created and stays visible to `readdir()`, `exists()` and `stat()` until you remove it with `rmdir()`. ([#21212](https://github.com/mastra-ai/mastra/pull/21212))
|
|
11
|
+
|
|
12
|
+
**Related fixes**
|
|
13
|
+
|
|
14
|
+
- `readdir()` reports a nested directory as its first path segment. After `mkdir('/a/b')`, `readdir('/')` returns one entry named `a`.
|
|
15
|
+
- A listing with `recursive` and `extension` returns files only. Directories no longer pass the extension filter.
|
|
16
|
+
- A path with a trailing slash always means a directory. `stat('/x/')` and `isFile('/x/')` no longer match a file named `x`, and `readFile('/x/')` throws `FileNotFoundError` instead of returning empty content.
|
|
17
|
+
- `mkdir()` works with a credential that can write but not read.
|
|
18
|
+
|
|
19
|
+
- Updated dependencies [[`e7109ee`](https://github.com/mastra-ai/mastra/commit/e7109ee6f731bacc79c885906f3c7dca8d8f013a), [`b8ce7ec`](https://github.com/mastra-ai/mastra/commit/b8ce7ec96e39343c6c2f36d12d68a9ad816c09f7), [`2e4624e`](https://github.com/mastra-ai/mastra/commit/2e4624edb6917e61249cb60ee377735e7af7e4a9), [`45a9147`](https://github.com/mastra-ai/mastra/commit/45a914741f578754d79d8b7de7b4e4f304d8e14a), [`a3a3624`](https://github.com/mastra-ai/mastra/commit/a3a3624f646b98e409424d8defccbd334da9e8b8), [`6246914`](https://github.com/mastra-ai/mastra/commit/62469146636911f3cbbe0880bd011c6a897a59a7), [`6445eba`](https://github.com/mastra-ai/mastra/commit/6445eba6020abac681aba1cc9289f446cb400cbe), [`86b7b77`](https://github.com/mastra-ai/mastra/commit/86b7b777980d30f66e1fd134a37d2af4c22e54cc), [`1c75e32`](https://github.com/mastra-ai/mastra/commit/1c75e32f7fc0b9fb6f548b4407feaec8a1440212), [`296dc9a`](https://github.com/mastra-ai/mastra/commit/296dc9af29f3616e786c7825ec32e0df92d754c5), [`f59032a`](https://github.com/mastra-ai/mastra/commit/f59032a73699443555a08a479e7ac578975784f2), [`cdd5c33`](https://github.com/mastra-ai/mastra/commit/cdd5c33ac6c7118a9f139e6dc0e14e6a8ae31658), [`3f73c07`](https://github.com/mastra-ai/mastra/commit/3f73c076727e8c36b4fff7a1b40290fb68957fa8), [`772c0c8`](https://github.com/mastra-ai/mastra/commit/772c0c897cec383258de2e6178147f8014767c7b), [`d7cf7fa`](https://github.com/mastra-ai/mastra/commit/d7cf7fafc1ae1b50bd8462dd0e6c671a8606db93), [`7c1ebb1`](https://github.com/mastra-ai/mastra/commit/7c1ebb15690c4b3f0eabb19077cf8af573311e57), [`0f9a448`](https://github.com/mastra-ai/mastra/commit/0f9a448502157e59f7b76f24360ad497168f5ef8), [`578bf2e`](https://github.com/mastra-ai/mastra/commit/578bf2e6a88e9d5b8bf502204e15a95dfbb679ae), [`c47165c`](https://github.com/mastra-ai/mastra/commit/c47165c983c87594c6952f1fd2fa51a90205034c), [`289f4ce`](https://github.com/mastra-ai/mastra/commit/289f4ce16e3293370440172132c52ee787cbc09f), [`df31eb0`](https://github.com/mastra-ai/mastra/commit/df31eb0c7087d782a0d9346e467f9a4af4b0eef6), [`9571e3a`](https://github.com/mastra-ai/mastra/commit/9571e3a06ed2c5220196460bf82a2129255c3a8b), [`4f16ff8`](https://github.com/mastra-ai/mastra/commit/4f16ff824bf2f9b0ddc93f210477c10c8a4fb1ab), [`b4c89b4`](https://github.com/mastra-ai/mastra/commit/b4c89b4371b0c86da57403ad1a3b3ef0681f3128), [`e6534fa`](https://github.com/mastra-ai/mastra/commit/e6534fab031216f6cb48c4c9907cbfdce9d60bc6), [`210cb7a`](https://github.com/mastra-ai/mastra/commit/210cb7a167998c7bbf72cb3b93e6eb0563330239), [`06b2d87`](https://github.com/mastra-ai/mastra/commit/06b2d87e63bcdd0ed59215c6789692b9b12de376), [`1c67d85`](https://github.com/mastra-ai/mastra/commit/1c67d85e9da8285662f4dbbf47e0378c3fee0747), [`ac01d63`](https://github.com/mastra-ai/mastra/commit/ac01d6355974aec73fdb8781449ed12bac582094), [`80a3324`](https://github.com/mastra-ai/mastra/commit/80a33245d3110204de6f56d61211523ffe338692), [`e44e8f3`](https://github.com/mastra-ai/mastra/commit/e44e8f370b66c339ddcaba946d33da6d3c3f06cd), [`d9d2881`](https://github.com/mastra-ai/mastra/commit/d9d2881ede6dd6c023d144215fc812062aed0890), [`a810a05`](https://github.com/mastra-ai/mastra/commit/a810a058f62ad407cfc1701e0be36ae91145d7cf), [`ba24be6`](https://github.com/mastra-ai/mastra/commit/ba24be662439c331ab23a600041f93803c89eca8), [`842b5fe`](https://github.com/mastra-ai/mastra/commit/842b5fe22b6a7fa811bd14e48eb9af523ac989f2), [`990611b`](https://github.com/mastra-ai/mastra/commit/990611ba76eb876d86c9c594371ae5f02f94b432), [`80bdf3a`](https://github.com/mastra-ai/mastra/commit/80bdf3ae16ade6ff63bde0cb16fa2df8ab7dd4dd), [`c967a5e`](https://github.com/mastra-ai/mastra/commit/c967a5eec150c5dc5418c4a4388982d1fb7ad27c), [`dc4a25d`](https://github.com/mastra-ai/mastra/commit/dc4a25d41af4e2fe97a816070eaec6aa963ab53b), [`9ba1247`](https://github.com/mastra-ai/mastra/commit/9ba12470c77f1c03642d720ce67e517e878f666e), [`fd96298`](https://github.com/mastra-ai/mastra/commit/fd96298a8367622f4ebfcaa97b5b6c1fbbd14564), [`66bbfb5`](https://github.com/mastra-ai/mastra/commit/66bbfb5f05b473d39f88c0e4a481ccac41634f3a), [`dc4a25d`](https://github.com/mastra-ai/mastra/commit/dc4a25d41af4e2fe97a816070eaec6aa963ab53b), [`f8da216`](https://github.com/mastra-ai/mastra/commit/f8da21633e7eb0e31c9ce0fc30567870d19416d3), [`4a09a9c`](https://github.com/mastra-ai/mastra/commit/4a09a9c0474ef643558fcb5f0edc542b82f1cab0), [`5f798b3`](https://github.com/mastra-ai/mastra/commit/5f798b3362e9bdf4d690f85245606e146eef60b9), [`6a84954`](https://github.com/mastra-ai/mastra/commit/6a84954a2667f85b6d59da652dab1bbff007ccb0), [`1e83a47`](https://github.com/mastra-ai/mastra/commit/1e83a4734ab61ba5926af6793e3569a78b72ed37), [`52d8ef0`](https://github.com/mastra-ai/mastra/commit/52d8ef03801f1deb7ee48532fc4190dd4a33916c), [`cdd5c33`](https://github.com/mastra-ai/mastra/commit/cdd5c33ac6c7118a9f139e6dc0e14e6a8ae31658), [`7fdcaa6`](https://github.com/mastra-ai/mastra/commit/7fdcaa66105d64290f9b14432a12ec99f39c4d3a), [`d6c56f9`](https://github.com/mastra-ai/mastra/commit/d6c56f951db3213330b98b0abafa9778c8770e58), [`e08e789`](https://github.com/mastra-ai/mastra/commit/e08e789c1bf4cd2fe46363f7a4728536ceccc9bd), [`bf936e2`](https://github.com/mastra-ai/mastra/commit/bf936e2c89b2ff0dad5695b873ddc009ba96d41e), [`7fb580a`](https://github.com/mastra-ai/mastra/commit/7fb580ac73fbcacf2ff00872a3395f73ae1b9fa5), [`ed5d606`](https://github.com/mastra-ai/mastra/commit/ed5d606739c5e3fbdfa9f272df7809aa5ab43b1d), [`f53d5bd`](https://github.com/mastra-ai/mastra/commit/f53d5bd4885b29e4ac29a428a6044088ea8d6aa3), [`32980a3`](https://github.com/mastra-ai/mastra/commit/32980a3e2413d0274ac244d32c37d910edc13f00), [`01a2943`](https://github.com/mastra-ai/mastra/commit/01a2943a7d886edefdff072bfa51f055bab54437), [`82e3365`](https://github.com/mastra-ai/mastra/commit/82e3365ef7c9bf7bee2e7a7029035ea262d68895), [`6104347`](https://github.com/mastra-ai/mastra/commit/61043473ba6bfd0a25156824e853e13165562e6c), [`35cc901`](https://github.com/mastra-ai/mastra/commit/35cc90102cf834a84827acaf9eee0b6d6d1e2a3b), [`a8b4cf0`](https://github.com/mastra-ai/mastra/commit/a8b4cf02823cffebc4751a53337dfacf097c1ae1), [`9571e3a`](https://github.com/mastra-ai/mastra/commit/9571e3a06ed2c5220196460bf82a2129255c3a8b), [`333785c`](https://github.com/mastra-ai/mastra/commit/333785c93cbb01e42c60167e995457c28897ddbf), [`bda2235`](https://github.com/mastra-ai/mastra/commit/bda22353ee28f2df0eaea555f7cae1549f979c0b), [`efd5c81`](https://github.com/mastra-ai/mastra/commit/efd5c81cc25fde3c2ddd86fc1178deb4ec176e19), [`1b482c2`](https://github.com/mastra-ai/mastra/commit/1b482c2d89244dd758c41e5f927a2b44041388d2), [`45bfb88`](https://github.com/mastra-ai/mastra/commit/45bfb88fd52f1dd3be20e2a38905777c96499c90), [`ff28284`](https://github.com/mastra-ai/mastra/commit/ff2828416f14daff9d956e6a352fdaa23c950979), [`4bcdfaf`](https://github.com/mastra-ai/mastra/commit/4bcdfaf0eac3199d7cb171b0a19a92c9c341eea4), [`e3b9307`](https://github.com/mastra-ai/mastra/commit/e3b9307098daefbfae2a52ae2ef51bc9fc701190), [`d6834c5`](https://github.com/mastra-ai/mastra/commit/d6834c5a7866b16734d23900163c2414ed70d791), [`f33264f`](https://github.com/mastra-ai/mastra/commit/f33264f517ae603279afd5c4251e2b40f6dd3618), [`689f2c4`](https://github.com/mastra-ai/mastra/commit/689f2c4b6c0835fe455702b01d21daa8abcd9331), [`fcd0667`](https://github.com/mastra-ai/mastra/commit/fcd0667a4e378be35c9a1b1eb19cce78fbfd7282), [`cfd0d9e`](https://github.com/mastra-ai/mastra/commit/cfd0d9ec77ec3c69dd96f79cdb579e03d79f22ce), [`acc3513`](https://github.com/mastra-ai/mastra/commit/acc3513b19f79bf0a7ec2998694580edca54086c), [`1670533`](https://github.com/mastra-ai/mastra/commit/1670533986f6bacf567746245348125e3a106448), [`a7eb4a1`](https://github.com/mastra-ai/mastra/commit/a7eb4a11450f6170274ed5141bffe821d4fdd5a6), [`0976933`](https://github.com/mastra-ai/mastra/commit/0976933142333ec78451feef265b68bcb45aa5e7), [`242b945`](https://github.com/mastra-ai/mastra/commit/242b94558777bfbdeb42cbfea84afff0b6ad0633), [`c52d346`](https://github.com/mastra-ai/mastra/commit/c52d3462ec831a5d95926ecd3d3373f5928ad2e5), [`af4636a`](https://github.com/mastra-ai/mastra/commit/af4636a74463275d71c1d13a38f7d2b738f128bf), [`01a2943`](https://github.com/mastra-ai/mastra/commit/01a2943a7d886edefdff072bfa51f055bab54437), [`2eabc09`](https://github.com/mastra-ai/mastra/commit/2eabc097d86d52fbd0123da36a7c874154cc384f), [`0023e79`](https://github.com/mastra-ai/mastra/commit/0023e7919431078280abd11c89d1edeae35fcc69), [`c2ad51e`](https://github.com/mastra-ai/mastra/commit/c2ad51e2467f901eecba8c9f4a45e22a50bd7c18), [`25ca73d`](https://github.com/mastra-ai/mastra/commit/25ca73d25dee7ce9f0ca72939e3a505c4db7257e), [`2f9ef3f`](https://github.com/mastra-ai/mastra/commit/2f9ef3f4ca06fc2dcdd5088c26b7f4da6a016791), [`e7eefcb`](https://github.com/mastra-ai/mastra/commit/e7eefcb162cda7c493e8c3bf43050ead0efbcb2c), [`fea5cae`](https://github.com/mastra-ai/mastra/commit/fea5caedc7e2cfea51784a15e015952692027abf), [`4d7aca2`](https://github.com/mastra-ai/mastra/commit/4d7aca2fe75f225c83d1502d63079568e6ec163f), [`e1cead1`](https://github.com/mastra-ai/mastra/commit/e1cead17b5f3653cf00d2f90cc19b113119c02ba), [`01a2943`](https://github.com/mastra-ai/mastra/commit/01a2943a7d886edefdff072bfa51f055bab54437), [`d9d93b2`](https://github.com/mastra-ai/mastra/commit/d9d93b25e4a65ad5fa153fa35be7ed149c8d587f), [`c4ec889`](https://github.com/mastra-ai/mastra/commit/c4ec889561c0264c43f66d04d587bee4ce35e792), [`4b59f78`](https://github.com/mastra-ai/mastra/commit/4b59f786cbc9a7d1ef07a07517dbd4b96865e99d), [`eeae63e`](https://github.com/mastra-ai/mastra/commit/eeae63e7fbe8e1f237adc69bca6e2ac13c5ca907), [`3dc97ea`](https://github.com/mastra-ai/mastra/commit/3dc97ea415fad353b48a13095fad1835933cc12a), [`94e7ae9`](https://github.com/mastra-ai/mastra/commit/94e7ae970b37c888cd1244ef013292639a2fe6d1), [`e6a2860`](https://github.com/mastra-ai/mastra/commit/e6a2860649cc51f87d32d78b766ae2126446ba07), [`7010c5d`](https://github.com/mastra-ai/mastra/commit/7010c5d15728bf9c5dfe4fb6b1bf80ce23bf143a), [`bab06b1`](https://github.com/mastra-ai/mastra/commit/bab06b18923873a584bdfc71a6b4ec7fb4727fb7), [`3d01cd3`](https://github.com/mastra-ai/mastra/commit/3d01cd387321b6f9c5cac31d487c84bf51b19c78), [`7bf3086`](https://github.com/mastra-ai/mastra/commit/7bf308663f0115ca74ad20554ade740f06640859), [`4c186a0`](https://github.com/mastra-ai/mastra/commit/4c186a017275f45e6ed4c09de0f89550e2d09e8c), [`b0fa077`](https://github.com/mastra-ai/mastra/commit/b0fa077bcbc9b08551846fe372a0d3d15b71ed72), [`0282e16`](https://github.com/mastra-ai/mastra/commit/0282e16115538c8e9b248b90f0748eb01cb5dc98), [`a8dd139`](https://github.com/mastra-ai/mastra/commit/a8dd1391a9fe9a6632c25809ef236980afa9a020), [`6a667b4`](https://github.com/mastra-ai/mastra/commit/6a667b4b7cd6a93fe41fcdd357b08c5a8c09b9ab), [`9be8878`](https://github.com/mastra-ai/mastra/commit/9be8878dcf0388e84fc4873e0eec27bd49b881a4), [`e5786be`](https://github.com/mastra-ai/mastra/commit/e5786be02bb903073082bd9d6da880ebaacc343f), [`2440e09`](https://github.com/mastra-ai/mastra/commit/2440e096ea6c2def1ccc1eb2d0f3f5b88c4af940), [`2093fbd`](https://github.com/mastra-ai/mastra/commit/2093fbd53bb744bae19ec89f6d73db9a66fbe8a7), [`a59049b`](https://github.com/mastra-ai/mastra/commit/a59049b1652a13efff66ac826326b5ed9a550342), [`7bd85ea`](https://github.com/mastra-ai/mastra/commit/7bd85ea7588b71c25ce9f4019c88f8539be5dcbc), [`83fa004`](https://github.com/mastra-ai/mastra/commit/83fa0044bfda8b703a83883dbd8bef204844d13f), [`a463cdf`](https://github.com/mastra-ai/mastra/commit/a463cdf1c95c3059e70f0bff27959e8558bb899d), [`e7a5da4`](https://github.com/mastra-ai/mastra/commit/e7a5da4ef8e4dd452d2f232961b4e682a85ffe43), [`7b4393d`](https://github.com/mastra-ai/mastra/commit/7b4393d557411fdcf07b0e30e5acaf7cc85154ae), [`0ea6b80`](https://github.com/mastra-ai/mastra/commit/0ea6b8001408ce02b56e8be0536b0fd8cbaf8ad2)]:
|
|
20
|
+
- @mastra/core@1.58.0
|
|
21
|
+
|
|
22
|
+
## 0.6.1-alpha.1
|
|
23
|
+
|
|
24
|
+
### Patch Changes
|
|
25
|
+
|
|
26
|
+
- Fixed `mkdir()` on Google Cloud Storage and S3 workspaces. An empty directory is now created and stays visible to `readdir()`, `exists()` and `stat()` until you remove it with `rmdir()`. ([#21212](https://github.com/mastra-ai/mastra/pull/21212))
|
|
27
|
+
|
|
28
|
+
**Related fixes**
|
|
29
|
+
|
|
30
|
+
- `readdir()` reports a nested directory as its first path segment. After `mkdir('/a/b')`, `readdir('/')` returns one entry named `a`.
|
|
31
|
+
- A listing with `recursive` and `extension` returns files only. Directories no longer pass the extension filter.
|
|
32
|
+
- A path with a trailing slash always means a directory. `stat('/x/')` and `isFile('/x/')` no longer match a file named `x`, and `readFile('/x/')` throws `FileNotFoundError` instead of returning empty content.
|
|
33
|
+
- `mkdir()` works with a credential that can write but not read.
|
|
34
|
+
|
|
35
|
+
- Updated dependencies [[`9571e3a`](https://github.com/mastra-ai/mastra/commit/9571e3a06ed2c5220196460bf82a2129255c3a8b), [`d6c56f9`](https://github.com/mastra-ai/mastra/commit/d6c56f951db3213330b98b0abafa9778c8770e58), [`9571e3a`](https://github.com/mastra-ai/mastra/commit/9571e3a06ed2c5220196460bf82a2129255c3a8b), [`acc3513`](https://github.com/mastra-ai/mastra/commit/acc3513b19f79bf0a7ec2998694580edca54086c), [`94e7ae9`](https://github.com/mastra-ai/mastra/commit/94e7ae970b37c888cd1244ef013292639a2fe6d1), [`6a667b4`](https://github.com/mastra-ai/mastra/commit/6a667b4b7cd6a93fe41fcdd357b08c5a8c09b9ab), [`2440e09`](https://github.com/mastra-ai/mastra/commit/2440e096ea6c2def1ccc1eb2d0f3f5b88c4af940), [`a59049b`](https://github.com/mastra-ai/mastra/commit/a59049b1652a13efff66ac826326b5ed9a550342)]:
|
|
36
|
+
- @mastra/core@1.58.0-alpha.13
|
|
37
|
+
|
|
3
38
|
## 0.6.1-alpha.0
|
|
4
39
|
|
|
5
40
|
### Patch Changes
|
|
@@ -176,7 +176,7 @@ export declare class S3Filesystem extends MastraFilesystem {
|
|
|
176
176
|
deleteFile(path: string, options?: RemoveOptions): Promise<void>;
|
|
177
177
|
copyFile(src: string, dest: string, options?: CopyOptions): Promise<void>;
|
|
178
178
|
moveFile(src: string, dest: string, options?: CopyOptions): Promise<void>;
|
|
179
|
-
mkdir(
|
|
179
|
+
mkdir(path: string, _options?: {
|
|
180
180
|
recursive?: boolean;
|
|
181
181
|
}): Promise<void>;
|
|
182
182
|
rmdir(path: string, options?: RemoveOptions): Promise<void>;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/filesystem/index.ts"],"names":[],"mappings":"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
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/filesystem/index.ts"],"names":[],"mappings":"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"}
|
package/dist/index.cjs
CHANGED
|
@@ -306,6 +306,7 @@ var S3Filesystem = class extends _mastra_core_workspace.MastraFilesystem {
|
|
|
306
306
|
return this.prefix + cleanPath;
|
|
307
307
|
}
|
|
308
308
|
async readFile(path, options) {
|
|
309
|
+
if (path.endsWith("/")) throw new _mastra_core_workspace.FileNotFoundError(path);
|
|
309
310
|
const client = await this.getReadyClient();
|
|
310
311
|
try {
|
|
311
312
|
const body = await (await client.send(new _aws_sdk_client_s3.GetObjectCommand({
|
|
@@ -364,6 +365,7 @@ var S3Filesystem = class extends _mastra_core_workspace.MastraFilesystem {
|
|
|
364
365
|
}
|
|
365
366
|
}
|
|
366
367
|
async copyFile(src, dest, options) {
|
|
368
|
+
if (src.endsWith("/")) throw new _mastra_core_workspace.FileNotFoundError(src);
|
|
367
369
|
const client = await this.getReadyClient();
|
|
368
370
|
if (options?.overwrite === false && await this.exists(dest)) throw new _mastra_core_workspace.FileExistsError(dest);
|
|
369
371
|
try {
|
|
@@ -381,10 +383,33 @@ var S3Filesystem = class extends _mastra_core_workspace.MastraFilesystem {
|
|
|
381
383
|
await this.copyFile(src, dest, options);
|
|
382
384
|
await this.deleteFile(src, { force: true });
|
|
383
385
|
}
|
|
384
|
-
async mkdir(
|
|
386
|
+
async mkdir(path, _options) {
|
|
387
|
+
const key = trimSlashes$1(this.toKey(path));
|
|
388
|
+
if (!key || key + "/" === this.prefix) return;
|
|
389
|
+
const client = await this.getReadyClient();
|
|
390
|
+
try {
|
|
391
|
+
await client.send(new _aws_sdk_client_s3.HeadObjectCommand({
|
|
392
|
+
Bucket: this.bucket,
|
|
393
|
+
Key: key
|
|
394
|
+
}));
|
|
395
|
+
throw new _mastra_core_workspace.FileExistsError(path);
|
|
396
|
+
} catch (error) {
|
|
397
|
+
if (error instanceof _mastra_core_workspace.FileExistsError) throw error;
|
|
398
|
+
}
|
|
399
|
+
await client.send(new _aws_sdk_client_s3.PutObjectCommand({
|
|
400
|
+
Bucket: this.bucket,
|
|
401
|
+
Key: key + "/",
|
|
402
|
+
Body: /* @__PURE__ */ new Uint8Array(0)
|
|
403
|
+
}));
|
|
404
|
+
}
|
|
385
405
|
async rmdir(path, options) {
|
|
386
406
|
if (!options?.recursive) {
|
|
387
407
|
if ((await this.readdir(path)).length > 0) throw new Error(`Directory not empty: ${path}`);
|
|
408
|
+
const key = trimSlashes$1(this.toKey(path));
|
|
409
|
+
if (key && key + "/" !== this.prefix) await (await this.getReadyClient()).send(new _aws_sdk_client_s3.DeleteObjectCommand({
|
|
410
|
+
Bucket: this.bucket,
|
|
411
|
+
Key: key + "/"
|
|
412
|
+
}));
|
|
388
413
|
return;
|
|
389
414
|
}
|
|
390
415
|
const client = await this.getReadyClient();
|
|
@@ -426,7 +451,9 @@ var S3Filesystem = class extends _mastra_core_workspace.MastraFilesystem {
|
|
|
426
451
|
const relativePath = key.slice(searchPrefix.length);
|
|
427
452
|
if (!relativePath) continue;
|
|
428
453
|
if (relativePath.endsWith("/")) {
|
|
429
|
-
|
|
454
|
+
if (options?.recursive && options.extension) continue;
|
|
455
|
+
const dirName = options?.recursive ? relativePath.slice(0, -1) : relativePath.split("/")[0];
|
|
456
|
+
if (!dirName) continue;
|
|
430
457
|
if (!seenDirs.has(dirName)) {
|
|
431
458
|
seenDirs.add(dirName);
|
|
432
459
|
entries.push({
|
|
@@ -482,7 +509,8 @@ var S3Filesystem = class extends _mastra_core_workspace.MastraFilesystem {
|
|
|
482
509
|
}))).Contents?.length ?? 0) > 0;
|
|
483
510
|
}
|
|
484
511
|
async stat(path) {
|
|
485
|
-
const key = this.toKey(path);
|
|
512
|
+
const key = trimSlashes$1(this.toKey(path));
|
|
513
|
+
const directoryOnly = path.endsWith("/");
|
|
486
514
|
if (!key) return {
|
|
487
515
|
name: "",
|
|
488
516
|
path,
|
|
@@ -491,35 +519,39 @@ var S3Filesystem = class extends _mastra_core_workspace.MastraFilesystem {
|
|
|
491
519
|
createdAt: /* @__PURE__ */ new Date(),
|
|
492
520
|
modifiedAt: /* @__PURE__ */ new Date()
|
|
493
521
|
};
|
|
494
|
-
const
|
|
495
|
-
|
|
496
|
-
const
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
type: "directory",
|
|
514
|
-
size: 0,
|
|
515
|
-
createdAt: /* @__PURE__ */ new Date(),
|
|
516
|
-
modifiedAt: /* @__PURE__ */ new Date()
|
|
517
|
-
};
|
|
518
|
-
throw new _mastra_core_workspace.FileNotFoundError(path);
|
|
522
|
+
const name = key.split("/").pop() ?? "";
|
|
523
|
+
if (!directoryOnly) {
|
|
524
|
+
const client = await this.getReadyClient();
|
|
525
|
+
try {
|
|
526
|
+
const response = await client.send(new _aws_sdk_client_s3.HeadObjectCommand({
|
|
527
|
+
Bucket: this.bucket,
|
|
528
|
+
Key: key
|
|
529
|
+
}));
|
|
530
|
+
return {
|
|
531
|
+
name,
|
|
532
|
+
path,
|
|
533
|
+
type: "file",
|
|
534
|
+
size: response.ContentLength ?? 0,
|
|
535
|
+
createdAt: response.LastModified ?? /* @__PURE__ */ new Date(),
|
|
536
|
+
modifiedAt: response.LastModified ?? /* @__PURE__ */ new Date()
|
|
537
|
+
};
|
|
538
|
+
} catch (error) {
|
|
539
|
+
if (!isNotFoundError$1(error)) throw this.handleError(error);
|
|
540
|
+
}
|
|
519
541
|
}
|
|
542
|
+
if (await this.isDirectory(path)) return {
|
|
543
|
+
name,
|
|
544
|
+
path,
|
|
545
|
+
type: "directory",
|
|
546
|
+
size: 0,
|
|
547
|
+
createdAt: /* @__PURE__ */ new Date(),
|
|
548
|
+
modifiedAt: /* @__PURE__ */ new Date()
|
|
549
|
+
};
|
|
550
|
+
throw new _mastra_core_workspace.FileNotFoundError(path);
|
|
520
551
|
}
|
|
521
552
|
async isFile(path) {
|
|
522
|
-
|
|
553
|
+
if (path.endsWith("/")) return false;
|
|
554
|
+
const key = trimSlashes$1(this.toKey(path));
|
|
523
555
|
if (!key) return false;
|
|
524
556
|
const client = await this.getReadyClient();
|
|
525
557
|
try {
|
package/dist/index.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.cjs","names":["isNotFoundError","trimSlashes","MastraFilesystem","S3Client","GetObjectCommand","FileNotFoundError","FileExistsError","PutObjectCommand","DeleteObjectCommand","CopyObjectCommand","ListObjectsV2Command","DeleteObjectsCommand","HeadObjectCommand","HeadBucketCommand","BlobStore","S3Client","PutObjectCommand","GetObjectCommand","HeadObjectCommand","DeleteObjectCommand","ListObjectsV2Command","DeleteObjectsCommand"],"sources":["../src/filesystem/index.ts","../src/blob-store/index.ts","../src/provider.ts"],"sourcesContent":["import {\n S3Client,\n GetObjectCommand,\n PutObjectCommand,\n DeleteObjectCommand,\n CopyObjectCommand,\n ListObjectsV2Command,\n DeleteObjectsCommand,\n HeadObjectCommand,\n HeadBucketCommand,\n} from '@aws-sdk/client-s3';\n\nimport type { AwsCredentialIdentity, AwsCredentialIdentityProvider } from '@aws-sdk/types';\n\nimport type {\n FileContent,\n FileStat,\n FileEntry,\n ReadOptions,\n WriteOptions,\n ListOptions,\n RemoveOptions,\n CopyOptions,\n FilesystemMountConfig,\n FilesystemIcon,\n FilesystemInfo,\n ProviderStatus,\n MastraFilesystemOptions,\n} from '@mastra/core/workspace';\nimport { MastraFilesystem, FileNotFoundError, FileExistsError } from '@mastra/core/workspace';\n\n/**\n * S3 mount configuration.\n * Returned by S3Filesystem.getMountConfig() for FUSE mounting in sandboxes.\n */\nexport interface S3MountConfig extends FilesystemMountConfig {\n type: 's3';\n /** S3 bucket name */\n bucket: string;\n /** AWS region (use 'auto' for R2) */\n region?: string;\n /** Optional endpoint for S3-compatible storage (MinIO, R2, etc.) */\n endpoint?: string;\n /** AWS access key ID */\n accessKeyId?: string;\n /** AWS secret access key */\n secretAccessKey?: string;\n /** AWS session token for temporary credentials (SSO, AssumeRole, container credentials, etc.) */\n sessionToken?: string;\n /**\n * Optional prefix (subdirectory) to mount instead of the entire bucket.\n * Uses s3fs `bucket:/prefix` syntax to scope the mount to a specific path.\n * Leading/trailing slashes are normalized automatically.\n */\n prefix?: string;\n /** Mount as read-only */\n readOnly?: boolean;\n}\n\n/**\n * Common MIME types by file extension.\n */\nconst MIME_TYPES: Record<string, string> = {\n // Text\n '.txt': 'text/plain',\n '.md': 'text/markdown',\n '.markdown': 'text/markdown',\n '.html': 'text/html',\n '.htm': 'text/html',\n '.css': 'text/css',\n '.csv': 'text/csv',\n '.xml': 'text/xml',\n // Code\n '.js': 'text/javascript',\n '.mjs': 'text/javascript',\n '.ts': 'text/typescript',\n '.tsx': 'text/typescript',\n '.jsx': 'text/javascript',\n '.json': 'application/json',\n '.yaml': 'text/yaml',\n '.yml': 'text/yaml',\n '.py': 'text/x-python',\n '.rb': 'text/x-ruby',\n '.sh': 'text/x-shellscript',\n '.bash': 'text/x-shellscript',\n // Images\n '.png': 'image/png',\n '.jpg': 'image/jpeg',\n '.jpeg': 'image/jpeg',\n '.gif': 'image/gif',\n '.svg': 'image/svg+xml',\n '.webp': 'image/webp',\n '.ico': 'image/x-icon',\n // Documents\n '.pdf': 'application/pdf',\n // Archives\n '.zip': 'application/zip',\n '.gz': 'application/gzip',\n '.tar': 'application/x-tar',\n};\n\n/**\n * Get MIME type from file path extension.\n */\nfunction getMimeType(path: string): string {\n const ext = path.toLowerCase().match(/\\.[^.]+$/)?.[0];\n return ext ? (MIME_TYPES[ext] ?? 'application/octet-stream') : 'application/octet-stream';\n}\n\n/** Check if an error is a \"not found\" error from the S3 SDK. */\nfunction isNotFoundError(error: unknown): boolean {\n if (!error || typeof error !== 'object' || !('name' in error)) return false;\n const name = (error as { name: string }).name;\n return name === 'NotFound' || name === 'NoSuchKey' || name === '404';\n}\n\n/** Check if an error is an access denied error from the S3 SDK. */\nfunction isAccessDeniedError(error: unknown): boolean {\n if (!error || typeof error !== 'object') return false;\n const err = error as { name?: string; $metadata?: { httpStatusCode?: number } };\n return err.name === 'AccessDenied' || err.$metadata?.httpStatusCode === 403;\n}\n\n/**\n * S3 filesystem provider configuration.\n */\nexport interface S3FilesystemOptions extends MastraFilesystemOptions {\n /** Unique identifier for this filesystem instance */\n id?: string;\n /** S3 bucket name */\n bucket: string;\n /** Human-friendly display name for the UI */\n displayName?: string;\n /** Icon identifier for the UI (defaults to 's3') */\n icon?: FilesystemIcon;\n /** Description shown in tooltips */\n description?: string;\n /** AWS region (use 'auto' for R2) */\n region: string;\n /**\n * AWS credentials or credential provider function.\n * Accepts static credentials or a provider that auto-refreshes\n * (e.g. fromNodeProviderChain() from @aws-sdk/credential-providers).\n * When set, takes precedence over accessKeyId/secretAccessKey/sessionToken.\n * When ALL credential options are omitted, the SDK default credential\n * provider chain is used (env vars, ~/.aws, IMDS, ECS container credentials).\n */\n credentials?: AwsCredentialIdentity | AwsCredentialIdentityProvider;\n /**\n * AWS access key ID.\n * Optional - omit to use the SDK default credential provider chain.\n */\n accessKeyId?: string;\n /**\n * AWS secret access key.\n * Optional - omit to use the SDK default credential provider chain.\n */\n secretAccessKey?: string;\n /**\n * AWS session token for temporary credentials.\n * Required when using SSO, AssumeRole, container credentials, or any other\n * temporary credential provider.\n */\n sessionToken?: string;\n /**\n * Custom endpoint URL for S3-compatible storage.\n * Examples:\n * - Cloudflare R2: 'https://{accountId}.r2.cloudflarestorage.com'\n * - MinIO: 'http://localhost:9000'\n * - DigitalOcean Spaces: 'https://{region}.digitaloceanspaces.com'\n */\n endpoint?: string;\n /** Force path-style URLs (required for some S3-compatible services) */\n forcePathStyle?: boolean;\n /** Optional prefix for all keys (acts like a subdirectory) */\n prefix?: string;\n /** Mount as read-only (blocks write operations, mounts read-only in sandboxes) */\n readOnly?: boolean;\n}\n\n/**\n * S3 filesystem implementation.\n *\n * Stores files in an S3 bucket or S3-compatible storage service.\n * Supports mounting into E2B sandboxes via s3fs-fuse.\n *\n * @example AWS S3\n * ```typescript\n * import { S3Filesystem } from '@mastra/s3';\n *\n * const fs = new S3Filesystem({\n * bucket: 'my-bucket',\n * region: 'us-east-1',\n * accessKeyId: process.env.AWS_ACCESS_KEY_ID!,\n * secretAccessKey: process.env.AWS_SECRET_ACCESS_KEY!,\n * });\n * ```\n *\n * @example Cloudflare R2\n * ```typescript\n * import { S3Filesystem } from '@mastra/s3';\n *\n * const fs = new S3Filesystem({\n * bucket: 'my-bucket',\n * region: 'auto',\n * accessKeyId: process.env.R2_ACCESS_KEY_ID!,\n * secretAccessKey: process.env.R2_SECRET_ACCESS_KEY!,\n * endpoint: `https://${process.env.R2_ACCOUNT_ID}.r2.cloudflarestorage.com`,\n * });\n * ```\n *\n * @example MinIO (local)\n * ```typescript\n * import { S3Filesystem } from '@mastra/s3';\n *\n * const fs = new S3Filesystem({\n * bucket: 'my-bucket',\n * region: 'us-east-1',\n * accessKeyId: 'minioadmin',\n * secretAccessKey: 'minioadmin',\n * endpoint: 'http://localhost:9000',\n * forcePathStyle: true,\n * });\n * ```\n */\n\n/** Trim leading and trailing slashes without regex (avoids polynomial regex on user input). */\nfunction trimSlashes(s: string): string {\n let start = 0;\n let end = s.length;\n while (start < end && s[start] === '/') start++;\n while (end > start && s[end - 1] === '/') end--;\n return s.slice(start, end);\n}\n\nexport class S3Filesystem extends MastraFilesystem {\n readonly id: string;\n readonly name = 'S3Filesystem';\n readonly provider = 's3';\n readonly readOnly?: boolean;\n\n status: ProviderStatus = 'pending';\n\n // Display metadata for UI\n readonly displayName?: string;\n readonly icon: FilesystemIcon = 's3';\n readonly description?: string;\n\n private readonly bucket: string;\n private readonly region: string;\n private readonly credentials?: AwsCredentialIdentity | AwsCredentialIdentityProvider;\n private readonly accessKeyId?: string;\n private readonly secretAccessKey?: string;\n private readonly sessionToken?: string;\n private readonly endpoint?: string;\n private readonly forcePathStyle: boolean;\n private readonly prefix: string;\n\n private _client: S3Client | null = null;\n\n constructor(options: S3FilesystemOptions) {\n super({ ...options, name: 'S3Filesystem' });\n this.id = options.id ?? `s3-fs-${Date.now().toString(36)}-${Math.random().toString(36).slice(2, 8)}`;\n this.bucket = options.bucket;\n this.region = options.region;\n this.credentials = options.credentials;\n this.accessKeyId = options.accessKeyId;\n this.secretAccessKey = options.secretAccessKey;\n this.sessionToken = options.sessionToken;\n this.endpoint = options.endpoint;\n this.forcePathStyle = options.forcePathStyle ?? !!options.endpoint; // Default true for custom endpoints\n // Trim leading/trailing slashes from prefix using iterative approach (avoids polynomial regex)\n const trimmedPrefix = options.prefix ? trimSlashes(options.prefix) : '';\n this.prefix = trimmedPrefix ? trimmedPrefix + '/' : '';\n\n // Display metadata - detect icon first, then derive displayName from it\n this.icon = options.icon ?? this.detectIconFromEndpoint(options.endpoint);\n this.displayName = options.displayName ?? this.getDefaultDisplayName(this.icon);\n this.description = options.description;\n this.readOnly = options.readOnly;\n }\n\n /**\n * Get the underlying S3Client instance for direct access to AWS S3 APIs.\n *\n * Use this when you need to access S3 features not exposed through the\n * WorkspaceFilesystem interface (e.g., presigned URLs, multipart uploads,\n * custom S3 operations, etc.).\n *\n * @example Generate a presigned URL\n * ```typescript\n * import { GetObjectCommand } from '@aws-sdk/client-s3';\n * import { getSignedUrl } from '@aws-sdk/s3-request-presigner';\n *\n * const s3Client = fs.client;\n * const url = await getSignedUrl(s3Client, new GetObjectCommand({\n * Bucket: 'my-bucket',\n * Key: 'my-file.txt',\n * }));\n * ```\n */\n get client(): S3Client {\n return this.getClient();\n }\n\n /**\n * Get mount configuration for E2B sandbox.\n * Returns S3-compatible config that works with s3fs-fuse.\n *\n * Only static `accessKeyId`/`secretAccessKey`/`sessionToken` are included in the\n * returned config. If credentials are provided only via the `credentials` option\n * (provider function), the returned config will have no credentials because FUSE\n * mounts cannot call a provider function. Use static credentials for sandbox\n * mount compatibility.\n */\n getMountConfig(): S3MountConfig {\n const config: S3MountConfig = {\n type: 's3',\n bucket: this.bucket,\n region: this.region,\n endpoint: this.endpoint,\n };\n\n if (this.accessKeyId && this.secretAccessKey) {\n config.accessKeyId = this.accessKeyId;\n config.secretAccessKey = this.secretAccessKey;\n if (this.sessionToken) {\n config.sessionToken = this.sessionToken;\n }\n }\n\n if (this.prefix) {\n config.prefix = this.prefix;\n }\n\n if (this.readOnly) {\n config.readOnly = true;\n }\n\n return config;\n }\n\n /**\n * Get filesystem info for status reporting.\n */\n getInfo(): FilesystemInfo<{\n bucket: string;\n region: string;\n endpoint?: string;\n prefix?: string;\n }> {\n return {\n id: this.id,\n name: this.name,\n provider: this.provider,\n status: this.status,\n error: this.error,\n readOnly: this.readOnly,\n icon: this.icon,\n metadata: {\n bucket: this.bucket,\n region: this.region,\n ...(this.endpoint && { endpoint: this.endpoint }),\n ...(this.prefix && { prefix: this.prefix }),\n },\n };\n }\n\n /**\n * Handle an error, checking for access denied and updating status accordingly.\n * Returns the error for re-throwing.\n */\n private handleError(error: unknown): unknown {\n if (isAccessDeniedError(error)) {\n this.status = 'error';\n this.error = 'Access denied - check credentials and bucket permissions';\n }\n return error;\n }\n\n /**\n * Get instructions describing this S3 filesystem.\n * Used by agents to understand storage semantics.\n */\n getInstructions(): string {\n const providerName = this.displayName || 'S3';\n const access = this.readOnly ? 'Read-only' : 'Persistent';\n return `${providerName} storage in bucket \"${this.bucket}\". ${access} storage - files are retained across sessions.`;\n }\n\n /**\n * Detect the appropriate icon based on the S3 endpoint.\n */\n private detectIconFromEndpoint(endpoint?: string): FilesystemIcon {\n if (!endpoint) {\n // No custom endpoint = AWS S3\n return 'aws-s3';\n }\n\n // Parse hostname from endpoint URL for secure matching\n let hostname: string;\n try {\n const url = new URL(endpoint);\n hostname = url.hostname.toLowerCase();\n } catch {\n // If URL parsing fails, use the endpoint as-is (lowercased)\n hostname = endpoint.toLowerCase();\n }\n\n // Check hostname suffix for known providers (use dot-prefix or exact match to prevent subdomain spoofing)\n if (\n hostname === 'r2.cloudflarestorage.com' ||\n hostname.endsWith('.r2.cloudflarestorage.com') ||\n hostname.endsWith('.cloudflare.com')\n ) {\n return 'r2';\n }\n\n if (\n hostname === 'storage.googleapis.com' ||\n hostname.endsWith('.storage.googleapis.com') ||\n hostname.endsWith('.googleapis.com')\n ) {\n return 'gcs';\n }\n\n if (\n hostname === 'blob.core.windows.net' ||\n hostname.endsWith('.blob.core.windows.net') ||\n hostname.endsWith('.azure.com')\n ) {\n return 'azure';\n }\n\n if (hostname.includes('minio')) {\n return 'minio';\n }\n\n // Generic S3-compatible (DigitalOcean Spaces, etc.)\n return 's3';\n }\n\n /**\n * Get a user-friendly display name based on the icon/provider.\n */\n private getDefaultDisplayName(icon: FilesystemIcon): string | undefined {\n switch (icon) {\n case 'aws-s3':\n return 'AWS S3';\n case 'r2':\n case 'cloudflare':\n case 'cloudflare-r2':\n return 'Cloudflare R2';\n case 'gcs':\n case 'google-cloud':\n case 'google-cloud-storage':\n return 'Google Cloud Storage';\n case 'azure':\n case 'azure-blob':\n return 'Azure Blob';\n case 'minio':\n return 'MinIO';\n case 's3':\n return 'S3';\n default:\n // Unknown icon - don't assume a display name\n return undefined;\n }\n }\n\n private getClient(): S3Client {\n if (this._client) return this._client;\n\n const hasStaticCredentials = this.accessKeyId && this.secretAccessKey;\n\n let credentials: AwsCredentialIdentity | AwsCredentialIdentityProvider | undefined;\n if (this.credentials) {\n credentials = this.credentials;\n } else if (hasStaticCredentials) {\n credentials = {\n accessKeyId: this.accessKeyId!,\n secretAccessKey: this.secretAccessKey!,\n ...(this.sessionToken && { sessionToken: this.sessionToken }),\n };\n }\n // When credentials is undefined, SDK uses its default provider chain\n\n this._client = new S3Client({\n region: this.region,\n ...(credentials !== undefined && { credentials }),\n endpoint: this.endpoint,\n forcePathStyle: this.forcePathStyle,\n });\n\n return this._client;\n }\n\n /**\n * Ensure the filesystem is initialized and return the S3 client.\n * Uses base class ensureReady() for status management, then returns client.\n */\n private async getReadyClient(): Promise<S3Client> {\n await this.ensureReady();\n return this.getClient();\n }\n\n private toKey(path: string): string {\n // Remove leading slashes, then resolve \".\" and \"./\" to empty string (root)\n const cleanPath = path.replace(/^\\/+/, '').replace(/^\\.(?:\\/|$)/, '');\n return this.prefix + cleanPath;\n }\n\n // ---------------------------------------------------------------------------\n // File Operations\n // ---------------------------------------------------------------------------\n\n async readFile(path: string, options?: ReadOptions): Promise<string | Buffer> {\n const client = await this.getReadyClient();\n\n try {\n const response = await client.send(\n new GetObjectCommand({\n Bucket: this.bucket,\n Key: this.toKey(path),\n }),\n );\n\n const body = await response.Body?.transformToByteArray();\n if (!body) throw new FileNotFoundError(path);\n\n const buffer = Buffer.from(body);\n if (options?.encoding) {\n return buffer.toString(options.encoding);\n }\n return buffer;\n } catch (error: unknown) {\n if (isNotFoundError(error)) {\n throw new FileNotFoundError(path);\n }\n throw this.handleError(error);\n }\n }\n\n async writeFile(path: string, content: FileContent, options?: WriteOptions): Promise<void> {\n const client = await this.getReadyClient();\n\n if (options?.overwrite === false && (await this.exists(path))) {\n throw new FileExistsError(path);\n }\n\n const body = typeof content === 'string' ? Buffer.from(content, 'utf-8') : Buffer.from(content);\n const contentType = getMimeType(path);\n\n await client.send(\n new PutObjectCommand({\n Bucket: this.bucket,\n Key: this.toKey(path),\n Body: body,\n ContentType: contentType,\n }),\n );\n }\n\n async appendFile(path: string, content: FileContent): Promise<void> {\n // S3 doesn't support append, so read + write\n let existing = '';\n try {\n existing = (await this.readFile(path, { encoding: 'utf-8' })) as string;\n } catch (error) {\n if (error instanceof FileNotFoundError) {\n // File doesn't exist, start fresh\n } else {\n throw error;\n }\n }\n\n const appendContent = typeof content === 'string' ? content : Buffer.from(content).toString('utf-8');\n await this.writeFile(path, existing + appendContent);\n }\n\n async deleteFile(path: string, options?: RemoveOptions): Promise<void> {\n // Check if this is a directory - if so, use rmdir instead\n const isDir = await this.isDirectory(path);\n if (isDir) {\n await this.rmdir(path, { recursive: true, force: options?.force });\n return;\n }\n\n const client = await this.getReadyClient();\n\n try {\n await client.send(\n new DeleteObjectCommand({\n Bucket: this.bucket,\n Key: this.toKey(path),\n }),\n );\n } catch (error: unknown) {\n if (options?.force) return;\n if (isNotFoundError(error)) {\n throw new FileNotFoundError(path);\n }\n throw this.handleError(error);\n }\n }\n\n async copyFile(src: string, dest: string, options?: CopyOptions): Promise<void> {\n const client = await this.getReadyClient();\n\n if (options?.overwrite === false && (await this.exists(dest))) {\n throw new FileExistsError(dest);\n }\n\n try {\n await client.send(\n new CopyObjectCommand({\n Bucket: this.bucket,\n CopySource: `${this.bucket}/${encodeURIComponent(this.toKey(src)).replace(/%2F/g, '/')}`,\n Key: this.toKey(dest),\n }),\n );\n } catch (error: unknown) {\n if (isNotFoundError(error)) {\n throw new FileNotFoundError(src);\n }\n throw this.handleError(error);\n }\n }\n\n async moveFile(src: string, dest: string, options?: CopyOptions): Promise<void> {\n await this.copyFile(src, dest, options);\n await this.deleteFile(src, { force: true });\n }\n\n // ---------------------------------------------------------------------------\n // Directory Operations\n // ---------------------------------------------------------------------------\n\n async mkdir(_path: string, _options?: { recursive?: boolean }): Promise<void> {\n // S3 doesn't have real directories - they're just key prefixes\n // No-op, directories are created implicitly when files are written\n }\n\n async rmdir(path: string, options?: RemoveOptions): Promise<void> {\n if (!options?.recursive) {\n // Check if directory is empty\n const entries = await this.readdir(path);\n if (entries.length > 0) {\n throw new Error(`Directory not empty: ${path}`);\n }\n return;\n }\n\n // Delete all objects with this prefix\n const client = await this.getReadyClient();\n\n const prefix = this.toKey(path).replace(/\\/$/, '') + '/';\n\n let continuationToken: string | undefined;\n do {\n const listResponse = await client.send(\n new ListObjectsV2Command({\n Bucket: this.bucket,\n Prefix: prefix,\n ContinuationToken: continuationToken,\n }),\n );\n\n if (listResponse.Contents && listResponse.Contents.length > 0) {\n const deleteResponse = await client.send(\n new DeleteObjectsCommand({\n Bucket: this.bucket,\n Delete: {\n Objects: listResponse.Contents.filter((obj): obj is { Key: string } => !!obj.Key).map(obj => ({\n Key: obj.Key,\n })),\n },\n }),\n );\n if (deleteResponse.Errors && deleteResponse.Errors.length > 0) {\n throw new Error(`Failed to delete ${deleteResponse.Errors.length} object(s) in ${path}`);\n }\n }\n\n continuationToken = listResponse.NextContinuationToken;\n } while (continuationToken);\n }\n\n async readdir(path: string, options?: ListOptions): Promise<FileEntry[]> {\n const client = await this.getReadyClient();\n\n const prefix = this.toKey(path).replace(/\\/$/, '');\n const searchPrefix = prefix ? prefix + '/' : '';\n\n const entries: FileEntry[] = [];\n const seenDirs = new Set<string>();\n\n let continuationToken: string | undefined;\n do {\n const response = await client.send(\n new ListObjectsV2Command({\n Bucket: this.bucket,\n Prefix: searchPrefix,\n Delimiter: options?.recursive ? undefined : '/',\n ContinuationToken: continuationToken,\n }),\n );\n\n // Add files\n if (response.Contents) {\n for (const obj of response.Contents) {\n const key = obj.Key;\n if (!key || key === searchPrefix) continue;\n\n const relativePath = key.slice(searchPrefix.length);\n if (!relativePath) continue;\n\n // Skip if this looks like a directory marker\n if (relativePath.endsWith('/')) {\n const dirName = relativePath.slice(0, -1);\n if (!seenDirs.has(dirName)) {\n seenDirs.add(dirName);\n entries.push({ name: dirName, type: 'directory' });\n }\n continue;\n }\n\n const name = options?.recursive ? relativePath : relativePath.split('/')[0];\n\n // Skip if name is undefined or empty\n if (!name) continue;\n\n // Filter by extension if specified\n if (options?.extension) {\n const extensions = Array.isArray(options.extension) ? options.extension : [options.extension];\n if (!extensions.some(ext => name.endsWith(ext))) {\n continue;\n }\n }\n\n entries.push({\n name,\n type: 'file',\n size: obj.Size,\n });\n }\n }\n\n // Add directories (common prefixes)\n if (response.CommonPrefixes) {\n for (const prefixObj of response.CommonPrefixes) {\n if (!prefixObj.Prefix) continue;\n const dirName = prefixObj.Prefix.slice(searchPrefix.length).replace(/\\/$/, '');\n if (dirName && !seenDirs.has(dirName)) {\n seenDirs.add(dirName);\n entries.push({ name: dirName, type: 'directory' });\n }\n }\n }\n\n continuationToken = response.NextContinuationToken;\n } while (continuationToken);\n\n return entries;\n }\n\n // ---------------------------------------------------------------------------\n // Path Operations\n // ---------------------------------------------------------------------------\n\n async exists(path: string): Promise<boolean> {\n const key = this.toKey(path);\n if (!key) return true; // Root always exists\n\n const client = await this.getReadyClient();\n\n // Check if it's a file\n try {\n await client.send(\n new HeadObjectCommand({\n Bucket: this.bucket,\n Key: key,\n }),\n );\n return true;\n } catch (error: unknown) {\n if (!isNotFoundError(error)) throw this.handleError(error);\n // Not a file, check if it's a \"directory\" (has objects with this prefix)\n }\n\n // Check if it's a directory prefix\n const response: { Contents?: unknown[] } = await client.send(\n new ListObjectsV2Command({\n Bucket: this.bucket,\n Prefix: key.replace(/\\/$/, '') + '/',\n MaxKeys: 1,\n }),\n );\n\n return (response.Contents?.length ?? 0) > 0;\n }\n\n async stat(path: string): Promise<FileStat> {\n const key = this.toKey(path);\n\n // Root is always a directory\n if (!key) {\n return {\n name: '',\n path,\n type: 'directory',\n size: 0,\n createdAt: new Date(),\n modifiedAt: new Date(),\n };\n }\n\n const client = await this.getReadyClient();\n\n try {\n const response: { ContentLength?: number; LastModified?: Date } = await client.send(\n new HeadObjectCommand({\n Bucket: this.bucket,\n Key: key,\n }),\n );\n\n const name = path.split('/').pop() ?? '';\n return {\n name,\n path,\n type: 'file',\n size: response.ContentLength ?? 0,\n createdAt: response.LastModified ?? new Date(),\n modifiedAt: response.LastModified ?? new Date(),\n };\n } catch (error: unknown) {\n if (!isNotFoundError(error)) throw this.handleError(error);\n // Check if it's a directory\n const isDir = await this.isDirectory(path);\n if (isDir) {\n const name = path.split('/').filter(Boolean).pop() ?? '';\n return {\n name,\n path,\n type: 'directory',\n size: 0,\n createdAt: new Date(),\n modifiedAt: new Date(),\n };\n }\n throw new FileNotFoundError(path);\n }\n }\n\n async isFile(path: string): Promise<boolean> {\n const key = this.toKey(path);\n if (!key) return false; // Root is a directory, not a file\n\n const client = await this.getReadyClient();\n\n try {\n await client.send(\n new HeadObjectCommand({\n Bucket: this.bucket,\n Key: key,\n }),\n );\n return true;\n } catch (error: unknown) {\n if (!isNotFoundError(error)) throw this.handleError(error);\n return false;\n }\n }\n\n async isDirectory(path: string): Promise<boolean> {\n const key = this.toKey(path);\n if (!key) return true; // Root is always a directory\n\n const client = await this.getReadyClient();\n\n const response: { Contents?: unknown[] } = await client.send(\n new ListObjectsV2Command({\n Bucket: this.bucket,\n Prefix: key.replace(/\\/$/, '') + '/',\n MaxKeys: 1,\n }),\n );\n\n return (response.Contents?.length ?? 0) > 0;\n }\n\n // ---------------------------------------------------------------------------\n // Lifecycle (overrides base class protected methods)\n // ---------------------------------------------------------------------------\n\n /**\n * Initialize the S3 client.\n * Status management is handled by the base class.\n */\n async init(): Promise<void> {\n // Verify we can access the bucket\n const client = this.getClient();\n try {\n await client.send(new HeadBucketCommand({ Bucket: this.bucket }));\n } catch (error) {\n // Extract httpStatusCode if available\n const statusCode = (error as { $metadata?: { httpStatusCode?: number } }).$metadata?.httpStatusCode;\n\n // Create error with status property for proper HTTP response codes\n const createError = (message: string) => {\n const err = new Error(message) as Error & { status?: number };\n if (statusCode) err.status = statusCode;\n return err;\n };\n\n // Provide better error messages for common S3 errors\n if (isAccessDeniedError(error)) {\n throw createError(`Access denied to bucket \"${this.bucket}\" - check credentials and permissions`);\n }\n if (isNotFoundError(error)) {\n throw createError(`Bucket \"${this.bucket}\" not found`);\n }\n const message = error instanceof Error ? error.message : String(error);\n if (statusCode) {\n throw createError(`Failed to access bucket \"${this.bucket}\" (HTTP ${statusCode}): ${message}`);\n }\n throw error;\n }\n }\n\n /**\n * Clean up the S3 client.\n * Status management is handled by the base class.\n */\n async destroy(): Promise<void> {\n this._client = null;\n }\n}\n","import {\n S3Client,\n GetObjectCommand,\n PutObjectCommand,\n DeleteObjectCommand,\n HeadObjectCommand,\n ListObjectsV2Command,\n DeleteObjectsCommand,\n} from '@aws-sdk/client-s3';\n\nimport type { AwsCredentialIdentity, AwsCredentialIdentityProvider } from '@aws-sdk/types';\n\nimport { BlobStore } from '@mastra/core/storage';\nimport type { StorageBlobEntry } from '@mastra/core/storage';\n\n/**\n * Configuration for S3BlobStore.\n *\n * Compatible with AWS S3, Cloudflare R2, MinIO, DigitalOcean Spaces, etc.\n */\nexport interface S3BlobStoreOptions {\n /** S3 bucket name */\n bucket: string;\n /** AWS region (use 'auto' for R2) */\n region: string;\n /**\n * AWS credentials or credential provider function.\n * Accepts static credentials or a provider that auto-refreshes\n * (e.g. fromNodeProviderChain() from @aws-sdk/credential-providers).\n * When set, takes precedence over accessKeyId/secretAccessKey/sessionToken.\n * When ALL credential options are omitted, the SDK default credential\n * provider chain is used (env vars, ~/.aws, IMDS, ECS container credentials).\n */\n credentials?: AwsCredentialIdentity | AwsCredentialIdentityProvider;\n /** AWS access key ID. Optional - omit to use the SDK default credential provider chain. */\n accessKeyId?: string;\n /** AWS secret access key. Optional - omit to use the SDK default credential provider chain. */\n secretAccessKey?: string;\n /** AWS session token for temporary credentials (SSO, AssumeRole, container credentials, etc.) */\n sessionToken?: string;\n /**\n * Custom endpoint URL for S3-compatible storage.\n * Examples:\n * - Cloudflare R2: 'https://{accountId}.r2.cloudflarestorage.com'\n * - MinIO: 'http://localhost:9000'\n */\n endpoint?: string;\n /** Force path-style URLs (required for some S3-compatible services like MinIO) */\n forcePathStyle?: boolean;\n /**\n * Key prefix for all blob objects.\n * Defaults to 'mastra_skill_blobs/'.\n */\n prefix?: string;\n}\n\n/** Trim leading and trailing slashes. */\nfunction trimSlashes(s: string): string {\n let start = 0;\n let end = s.length;\n while (start < end && s[start] === '/') start++;\n while (end > start && s[end - 1] === '/') end--;\n return s.slice(start, end);\n}\n\n/**\n * S3-backed content-addressable blob store for skill versioning.\n *\n * Each blob is stored as an S3 object keyed by its SHA-256 hash.\n * Metadata (size, mimeType, createdAt) is stored in S3 object user metadata.\n *\n * Since blobs are content-addressable, writes are idempotent — the same hash\n * always maps to the same content, so overwrites are safe and equivalent to\n * a no-op.\n *\n * @example AWS S3\n * ```typescript\n * import { S3BlobStore } from '@mastra/s3';\n *\n * const blobs = new S3BlobStore({\n * bucket: 'my-skill-blobs',\n * region: 'us-east-1',\n * accessKeyId: process.env.AWS_ACCESS_KEY_ID!,\n * secretAccessKey: process.env.AWS_SECRET_ACCESS_KEY!,\n * });\n * ```\n *\n * @example MinIO (local)\n * ```typescript\n * import { S3BlobStore } from '@mastra/s3';\n *\n * const blobs = new S3BlobStore({\n * bucket: 'skill-blobs',\n * region: 'us-east-1',\n * accessKeyId: 'minioadmin',\n * secretAccessKey: 'minioadmin',\n * endpoint: 'http://localhost:9000',\n * forcePathStyle: true,\n * });\n * ```\n */\nexport class S3BlobStore extends BlobStore {\n private readonly bucket: string;\n private readonly prefix: string;\n private _client: S3Client | null = null;\n\n private readonly region: string;\n private readonly credentials?: AwsCredentialIdentity | AwsCredentialIdentityProvider;\n private readonly accessKeyId?: string;\n private readonly secretAccessKey?: string;\n private readonly sessionToken?: string;\n private readonly endpoint?: string;\n private readonly forcePathStyle: boolean;\n\n constructor(options: S3BlobStoreOptions) {\n super();\n this.bucket = options.bucket;\n this.region = options.region;\n this.credentials = options.credentials;\n this.accessKeyId = options.accessKeyId;\n this.secretAccessKey = options.secretAccessKey;\n this.sessionToken = options.sessionToken;\n this.endpoint = options.endpoint;\n this.forcePathStyle = options.forcePathStyle ?? !!options.endpoint;\n this.prefix = options.prefix ? trimSlashes(options.prefix) + '/' : 'mastra_skill_blobs/';\n }\n\n private getClient(): S3Client {\n if (this._client) return this._client;\n\n const hasStaticCredentials = this.accessKeyId && this.secretAccessKey;\n\n let credentials: AwsCredentialIdentity | AwsCredentialIdentityProvider | undefined;\n if (this.credentials) {\n credentials = this.credentials;\n } else if (hasStaticCredentials) {\n credentials = {\n accessKeyId: this.accessKeyId!,\n secretAccessKey: this.secretAccessKey!,\n ...(this.sessionToken && { sessionToken: this.sessionToken }),\n };\n }\n // When credentials is undefined, SDK uses its default provider chain\n\n this._client = new S3Client({\n region: this.region,\n ...(credentials !== undefined && { credentials }),\n endpoint: this.endpoint,\n forcePathStyle: this.forcePathStyle,\n });\n return this._client;\n }\n\n private toKey(hash: string): string {\n return this.prefix + hash;\n }\n\n async init(): Promise<void> {\n // S3 doesn't require table creation — the bucket is expected to exist.\n }\n\n async put(entry: StorageBlobEntry): Promise<void> {\n const client = this.getClient();\n const now = entry.createdAt ?? new Date();\n\n await client.send(\n new PutObjectCommand({\n Bucket: this.bucket,\n Key: this.toKey(entry.hash),\n Body: entry.content,\n ContentType: entry.mimeType ?? 'application/octet-stream',\n Metadata: {\n size: String(entry.size),\n createdat: now.toISOString(),\n ...(entry.mimeType ? { mimetype: entry.mimeType } : {}),\n },\n }),\n );\n }\n\n async get(hash: string): Promise<StorageBlobEntry | null> {\n const client = this.getClient();\n\n try {\n const response = await client.send(\n new GetObjectCommand({\n Bucket: this.bucket,\n Key: this.toKey(hash),\n }),\n );\n\n const body = await response.Body?.transformToString('utf-8');\n if (body === undefined || body === null) return null;\n\n const metadata = response.Metadata ?? {};\n return {\n hash,\n content: body,\n size: metadata.size != null ? Number(metadata.size) : Buffer.byteLength(body, 'utf-8'),\n mimeType: metadata.mimetype || response.ContentType || undefined,\n createdAt: metadata.createdat ? new Date(metadata.createdat) : new Date(),\n };\n } catch (error: unknown) {\n if (isNotFoundError(error)) return null;\n throw error;\n }\n }\n\n async has(hash: string): Promise<boolean> {\n const client = this.getClient();\n\n try {\n await client.send(\n new HeadObjectCommand({\n Bucket: this.bucket,\n Key: this.toKey(hash),\n }),\n );\n return true;\n } catch (error: unknown) {\n if (isNotFoundError(error)) return false;\n throw error;\n }\n }\n\n async delete(hash: string): Promise<boolean> {\n // Pre-check is intentional: S3 DeleteObject returns 204 regardless of\n // whether the object existed, so we check first for an accurate return.\n // The TOCTOU gap is acceptable for content-addressable blobs.\n const existed = await this.has(hash);\n if (!existed) return false;\n\n const client = this.getClient();\n await client.send(\n new DeleteObjectCommand({\n Bucket: this.bucket,\n Key: this.toKey(hash),\n }),\n );\n return true;\n }\n\n async putMany(entries: StorageBlobEntry[]): Promise<void> {\n if (entries.length === 0) return;\n // S3 doesn't have a batch PUT, so we parallelize individual puts.\n // Content-addressable means duplicate writes are idempotent.\n await Promise.all(entries.map(entry => this.put(entry)));\n }\n\n async getMany(hashes: string[]): Promise<Map<string, StorageBlobEntry>> {\n const result = new Map<string, StorageBlobEntry>();\n if (hashes.length === 0) return result;\n\n // Parallelize individual gets\n const entries = await Promise.all(hashes.map(hash => this.get(hash)));\n for (const entry of entries) {\n if (entry) {\n result.set(entry.hash, entry);\n }\n }\n return result;\n }\n\n async dangerouslyClearAll(): Promise<void> {\n const client = this.getClient();\n\n let continuationToken: string | undefined;\n do {\n const listResponse = await client.send(\n new ListObjectsV2Command({\n Bucket: this.bucket,\n Prefix: this.prefix,\n ContinuationToken: continuationToken,\n }),\n );\n\n const objects = listResponse.Contents;\n if (objects && objects.length > 0) {\n await client.send(\n new DeleteObjectsCommand({\n Bucket: this.bucket,\n Delete: {\n Objects: objects.filter(obj => obj.Key != null).map(obj => ({ Key: obj.Key! })),\n Quiet: true,\n },\n }),\n );\n }\n\n continuationToken = listResponse.IsTruncated ? listResponse.NextContinuationToken : undefined;\n } while (continuationToken);\n }\n}\n\nfunction isNotFoundError(error: unknown): boolean {\n if (!error || typeof error !== 'object' || !('name' in error)) return false;\n const name = (error as { name: string }).name;\n return name === 'NotFound' || name === 'NoSuchKey' || name === '404';\n}\n","/**\n * S3 filesystem provider descriptor for MastraEditor.\n *\n * @example\n * ```typescript\n * import { s3FilesystemProvider } from '@mastra/s3';\n *\n * const editor = new MastraEditor({\n * filesystems: [s3FilesystemProvider],\n * });\n * ```\n */\nimport type { FilesystemProvider, BlobStoreProvider } from '@mastra/core/editor';\nimport { S3BlobStore } from './blob-store';\nimport type { S3BlobStoreOptions } from './blob-store';\nimport { S3Filesystem } from './filesystem';\nimport type { S3FilesystemOptions } from './filesystem';\n\nexport const s3FilesystemProvider: FilesystemProvider<S3FilesystemOptions> = {\n id: 's3',\n name: 'Amazon S3',\n description: 'S3 or S3-compatible storage (AWS, R2, MinIO, DO Spaces)',\n configSchema: {\n type: 'object',\n required: ['bucket', 'region'],\n properties: {\n bucket: { type: 'string', description: 'S3 bucket name' },\n region: { type: 'string', description: 'AWS region (use \"auto\" for R2)' },\n accessKeyId: { type: 'string', description: 'AWS access key ID' },\n secretAccessKey: { type: 'string', description: 'AWS secret access key' },\n sessionToken: { type: 'string', description: 'AWS session token for temporary credentials' },\n endpoint: { type: 'string', description: 'Custom endpoint URL for S3-compatible storage' },\n forcePathStyle: { type: 'boolean', description: 'Force path-style URLs', default: false },\n prefix: { type: 'string', description: 'Key prefix (acts like a subdirectory)' },\n readOnly: { type: 'boolean', description: 'Mount as read-only', default: false },\n },\n },\n createFilesystem: config => new S3Filesystem(config),\n};\n\n/**\n * S3 blob store provider descriptor for MastraEditor.\n *\n * @example\n * ```typescript\n * import { s3BlobStoreProvider } from '@mastra/s3';\n *\n * const editor = new MastraEditor({\n * blobStores: { s3: s3BlobStoreProvider },\n * });\n * ```\n */\nexport const s3BlobStoreProvider: BlobStoreProvider<S3BlobStoreOptions> = {\n id: 's3',\n name: 'Amazon S3 Blob Store',\n description: 'Content-addressable blob storage using S3 or S3-compatible storage (AWS, R2, MinIO, DO Spaces)',\n configSchema: {\n type: 'object',\n required: ['bucket', 'region', 'accessKeyId', 'secretAccessKey'],\n properties: {\n bucket: { type: 'string', description: 'S3 bucket name' },\n region: { type: 'string', description: 'AWS region (use \"auto\" for R2)' },\n accessKeyId: { type: 'string', description: 'AWS access key ID' },\n secretAccessKey: { type: 'string', description: 'AWS secret access key' },\n sessionToken: { type: 'string', description: 'AWS session token for temporary credentials' },\n endpoint: { type: 'string', description: 'Custom endpoint URL for S3-compatible storage' },\n forcePathStyle: { type: 'boolean', description: 'Force path-style URLs', default: false },\n prefix: { type: 'string', description: 'Key prefix for blob objects (default: mastra_skill_blobs/)' },\n },\n },\n createBlobStore: config => new S3BlobStore(config),\n};\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"index.cjs","names":["isNotFoundError","trimSlashes","MastraFilesystem","S3Client","FileNotFoundError","GetObjectCommand","FileExistsError","PutObjectCommand","DeleteObjectCommand","CopyObjectCommand","HeadObjectCommand","ListObjectsV2Command","DeleteObjectsCommand","HeadBucketCommand","BlobStore","S3Client","PutObjectCommand","GetObjectCommand","HeadObjectCommand","DeleteObjectCommand","ListObjectsV2Command","DeleteObjectsCommand"],"sources":["../src/filesystem/index.ts","../src/blob-store/index.ts","../src/provider.ts"],"sourcesContent":["import {\n S3Client,\n GetObjectCommand,\n PutObjectCommand,\n DeleteObjectCommand,\n CopyObjectCommand,\n ListObjectsV2Command,\n DeleteObjectsCommand,\n HeadObjectCommand,\n HeadBucketCommand,\n} from '@aws-sdk/client-s3';\n\nimport type { AwsCredentialIdentity, AwsCredentialIdentityProvider } from '@aws-sdk/types';\n\nimport type {\n FileContent,\n FileStat,\n FileEntry,\n ReadOptions,\n WriteOptions,\n ListOptions,\n RemoveOptions,\n CopyOptions,\n FilesystemMountConfig,\n FilesystemIcon,\n FilesystemInfo,\n ProviderStatus,\n MastraFilesystemOptions,\n} from '@mastra/core/workspace';\nimport { MastraFilesystem, FileNotFoundError, FileExistsError } from '@mastra/core/workspace';\n\n/**\n * S3 mount configuration.\n * Returned by S3Filesystem.getMountConfig() for FUSE mounting in sandboxes.\n */\nexport interface S3MountConfig extends FilesystemMountConfig {\n type: 's3';\n /** S3 bucket name */\n bucket: string;\n /** AWS region (use 'auto' for R2) */\n region?: string;\n /** Optional endpoint for S3-compatible storage (MinIO, R2, etc.) */\n endpoint?: string;\n /** AWS access key ID */\n accessKeyId?: string;\n /** AWS secret access key */\n secretAccessKey?: string;\n /** AWS session token for temporary credentials (SSO, AssumeRole, container credentials, etc.) */\n sessionToken?: string;\n /**\n * Optional prefix (subdirectory) to mount instead of the entire bucket.\n * Uses s3fs `bucket:/prefix` syntax to scope the mount to a specific path.\n * Leading/trailing slashes are normalized automatically.\n */\n prefix?: string;\n /** Mount as read-only */\n readOnly?: boolean;\n}\n\n/**\n * Common MIME types by file extension.\n */\nconst MIME_TYPES: Record<string, string> = {\n // Text\n '.txt': 'text/plain',\n '.md': 'text/markdown',\n '.markdown': 'text/markdown',\n '.html': 'text/html',\n '.htm': 'text/html',\n '.css': 'text/css',\n '.csv': 'text/csv',\n '.xml': 'text/xml',\n // Code\n '.js': 'text/javascript',\n '.mjs': 'text/javascript',\n '.ts': 'text/typescript',\n '.tsx': 'text/typescript',\n '.jsx': 'text/javascript',\n '.json': 'application/json',\n '.yaml': 'text/yaml',\n '.yml': 'text/yaml',\n '.py': 'text/x-python',\n '.rb': 'text/x-ruby',\n '.sh': 'text/x-shellscript',\n '.bash': 'text/x-shellscript',\n // Images\n '.png': 'image/png',\n '.jpg': 'image/jpeg',\n '.jpeg': 'image/jpeg',\n '.gif': 'image/gif',\n '.svg': 'image/svg+xml',\n '.webp': 'image/webp',\n '.ico': 'image/x-icon',\n // Documents\n '.pdf': 'application/pdf',\n // Archives\n '.zip': 'application/zip',\n '.gz': 'application/gzip',\n '.tar': 'application/x-tar',\n};\n\n/**\n * Get MIME type from file path extension.\n */\nfunction getMimeType(path: string): string {\n const ext = path.toLowerCase().match(/\\.[^.]+$/)?.[0];\n return ext ? (MIME_TYPES[ext] ?? 'application/octet-stream') : 'application/octet-stream';\n}\n\n/** Check if an error is a \"not found\" error from the S3 SDK. */\nfunction isNotFoundError(error: unknown): boolean {\n if (!error || typeof error !== 'object' || !('name' in error)) return false;\n const name = (error as { name: string }).name;\n return name === 'NotFound' || name === 'NoSuchKey' || name === '404';\n}\n\n/** Check if an error is an access denied error from the S3 SDK. */\nfunction isAccessDeniedError(error: unknown): boolean {\n if (!error || typeof error !== 'object') return false;\n const err = error as { name?: string; $metadata?: { httpStatusCode?: number } };\n return err.name === 'AccessDenied' || err.$metadata?.httpStatusCode === 403;\n}\n\n/**\n * S3 filesystem provider configuration.\n */\nexport interface S3FilesystemOptions extends MastraFilesystemOptions {\n /** Unique identifier for this filesystem instance */\n id?: string;\n /** S3 bucket name */\n bucket: string;\n /** Human-friendly display name for the UI */\n displayName?: string;\n /** Icon identifier for the UI (defaults to 's3') */\n icon?: FilesystemIcon;\n /** Description shown in tooltips */\n description?: string;\n /** AWS region (use 'auto' for R2) */\n region: string;\n /**\n * AWS credentials or credential provider function.\n * Accepts static credentials or a provider that auto-refreshes\n * (e.g. fromNodeProviderChain() from @aws-sdk/credential-providers).\n * When set, takes precedence over accessKeyId/secretAccessKey/sessionToken.\n * When ALL credential options are omitted, the SDK default credential\n * provider chain is used (env vars, ~/.aws, IMDS, ECS container credentials).\n */\n credentials?: AwsCredentialIdentity | AwsCredentialIdentityProvider;\n /**\n * AWS access key ID.\n * Optional - omit to use the SDK default credential provider chain.\n */\n accessKeyId?: string;\n /**\n * AWS secret access key.\n * Optional - omit to use the SDK default credential provider chain.\n */\n secretAccessKey?: string;\n /**\n * AWS session token for temporary credentials.\n * Required when using SSO, AssumeRole, container credentials, or any other\n * temporary credential provider.\n */\n sessionToken?: string;\n /**\n * Custom endpoint URL for S3-compatible storage.\n * Examples:\n * - Cloudflare R2: 'https://{accountId}.r2.cloudflarestorage.com'\n * - MinIO: 'http://localhost:9000'\n * - DigitalOcean Spaces: 'https://{region}.digitaloceanspaces.com'\n */\n endpoint?: string;\n /** Force path-style URLs (required for some S3-compatible services) */\n forcePathStyle?: boolean;\n /** Optional prefix for all keys (acts like a subdirectory) */\n prefix?: string;\n /** Mount as read-only (blocks write operations, mounts read-only in sandboxes) */\n readOnly?: boolean;\n}\n\n/**\n * S3 filesystem implementation.\n *\n * Stores files in an S3 bucket or S3-compatible storage service.\n * Supports mounting into E2B sandboxes via s3fs-fuse.\n *\n * @example AWS S3\n * ```typescript\n * import { S3Filesystem } from '@mastra/s3';\n *\n * const fs = new S3Filesystem({\n * bucket: 'my-bucket',\n * region: 'us-east-1',\n * accessKeyId: process.env.AWS_ACCESS_KEY_ID!,\n * secretAccessKey: process.env.AWS_SECRET_ACCESS_KEY!,\n * });\n * ```\n *\n * @example Cloudflare R2\n * ```typescript\n * import { S3Filesystem } from '@mastra/s3';\n *\n * const fs = new S3Filesystem({\n * bucket: 'my-bucket',\n * region: 'auto',\n * accessKeyId: process.env.R2_ACCESS_KEY_ID!,\n * secretAccessKey: process.env.R2_SECRET_ACCESS_KEY!,\n * endpoint: `https://${process.env.R2_ACCOUNT_ID}.r2.cloudflarestorage.com`,\n * });\n * ```\n *\n * @example MinIO (local)\n * ```typescript\n * import { S3Filesystem } from '@mastra/s3';\n *\n * const fs = new S3Filesystem({\n * bucket: 'my-bucket',\n * region: 'us-east-1',\n * accessKeyId: 'minioadmin',\n * secretAccessKey: 'minioadmin',\n * endpoint: 'http://localhost:9000',\n * forcePathStyle: true,\n * });\n * ```\n */\n\n/** Trim leading and trailing slashes without regex (avoids polynomial regex on user input). */\nfunction trimSlashes(s: string): string {\n let start = 0;\n let end = s.length;\n while (start < end && s[start] === '/') start++;\n while (end > start && s[end - 1] === '/') end--;\n return s.slice(start, end);\n}\n\nexport class S3Filesystem extends MastraFilesystem {\n readonly id: string;\n readonly name = 'S3Filesystem';\n readonly provider = 's3';\n readonly readOnly?: boolean;\n\n status: ProviderStatus = 'pending';\n\n // Display metadata for UI\n readonly displayName?: string;\n readonly icon: FilesystemIcon = 's3';\n readonly description?: string;\n\n private readonly bucket: string;\n private readonly region: string;\n private readonly credentials?: AwsCredentialIdentity | AwsCredentialIdentityProvider;\n private readonly accessKeyId?: string;\n private readonly secretAccessKey?: string;\n private readonly sessionToken?: string;\n private readonly endpoint?: string;\n private readonly forcePathStyle: boolean;\n private readonly prefix: string;\n\n private _client: S3Client | null = null;\n\n constructor(options: S3FilesystemOptions) {\n super({ ...options, name: 'S3Filesystem' });\n this.id = options.id ?? `s3-fs-${Date.now().toString(36)}-${Math.random().toString(36).slice(2, 8)}`;\n this.bucket = options.bucket;\n this.region = options.region;\n this.credentials = options.credentials;\n this.accessKeyId = options.accessKeyId;\n this.secretAccessKey = options.secretAccessKey;\n this.sessionToken = options.sessionToken;\n this.endpoint = options.endpoint;\n this.forcePathStyle = options.forcePathStyle ?? !!options.endpoint; // Default true for custom endpoints\n // Trim leading/trailing slashes from prefix using iterative approach (avoids polynomial regex)\n const trimmedPrefix = options.prefix ? trimSlashes(options.prefix) : '';\n this.prefix = trimmedPrefix ? trimmedPrefix + '/' : '';\n\n // Display metadata - detect icon first, then derive displayName from it\n this.icon = options.icon ?? this.detectIconFromEndpoint(options.endpoint);\n this.displayName = options.displayName ?? this.getDefaultDisplayName(this.icon);\n this.description = options.description;\n this.readOnly = options.readOnly;\n }\n\n /**\n * Get the underlying S3Client instance for direct access to AWS S3 APIs.\n *\n * Use this when you need to access S3 features not exposed through the\n * WorkspaceFilesystem interface (e.g., presigned URLs, multipart uploads,\n * custom S3 operations, etc.).\n *\n * @example Generate a presigned URL\n * ```typescript\n * import { GetObjectCommand } from '@aws-sdk/client-s3';\n * import { getSignedUrl } from '@aws-sdk/s3-request-presigner';\n *\n * const s3Client = fs.client;\n * const url = await getSignedUrl(s3Client, new GetObjectCommand({\n * Bucket: 'my-bucket',\n * Key: 'my-file.txt',\n * }));\n * ```\n */\n get client(): S3Client {\n return this.getClient();\n }\n\n /**\n * Get mount configuration for E2B sandbox.\n * Returns S3-compatible config that works with s3fs-fuse.\n *\n * Only static `accessKeyId`/`secretAccessKey`/`sessionToken` are included in the\n * returned config. If credentials are provided only via the `credentials` option\n * (provider function), the returned config will have no credentials because FUSE\n * mounts cannot call a provider function. Use static credentials for sandbox\n * mount compatibility.\n */\n getMountConfig(): S3MountConfig {\n const config: S3MountConfig = {\n type: 's3',\n bucket: this.bucket,\n region: this.region,\n endpoint: this.endpoint,\n };\n\n if (this.accessKeyId && this.secretAccessKey) {\n config.accessKeyId = this.accessKeyId;\n config.secretAccessKey = this.secretAccessKey;\n if (this.sessionToken) {\n config.sessionToken = this.sessionToken;\n }\n }\n\n if (this.prefix) {\n config.prefix = this.prefix;\n }\n\n if (this.readOnly) {\n config.readOnly = true;\n }\n\n return config;\n }\n\n /**\n * Get filesystem info for status reporting.\n */\n getInfo(): FilesystemInfo<{\n bucket: string;\n region: string;\n endpoint?: string;\n prefix?: string;\n }> {\n return {\n id: this.id,\n name: this.name,\n provider: this.provider,\n status: this.status,\n error: this.error,\n readOnly: this.readOnly,\n icon: this.icon,\n metadata: {\n bucket: this.bucket,\n region: this.region,\n ...(this.endpoint && { endpoint: this.endpoint }),\n ...(this.prefix && { prefix: this.prefix }),\n },\n };\n }\n\n /**\n * Handle an error, checking for access denied and updating status accordingly.\n * Returns the error for re-throwing.\n */\n private handleError(error: unknown): unknown {\n if (isAccessDeniedError(error)) {\n this.status = 'error';\n this.error = 'Access denied - check credentials and bucket permissions';\n }\n return error;\n }\n\n /**\n * Get instructions describing this S3 filesystem.\n * Used by agents to understand storage semantics.\n */\n getInstructions(): string {\n const providerName = this.displayName || 'S3';\n const access = this.readOnly ? 'Read-only' : 'Persistent';\n return `${providerName} storage in bucket \"${this.bucket}\". ${access} storage - files are retained across sessions.`;\n }\n\n /**\n * Detect the appropriate icon based on the S3 endpoint.\n */\n private detectIconFromEndpoint(endpoint?: string): FilesystemIcon {\n if (!endpoint) {\n // No custom endpoint = AWS S3\n return 'aws-s3';\n }\n\n // Parse hostname from endpoint URL for secure matching\n let hostname: string;\n try {\n const url = new URL(endpoint);\n hostname = url.hostname.toLowerCase();\n } catch {\n // If URL parsing fails, use the endpoint as-is (lowercased)\n hostname = endpoint.toLowerCase();\n }\n\n // Check hostname suffix for known providers (use dot-prefix or exact match to prevent subdomain spoofing)\n if (\n hostname === 'r2.cloudflarestorage.com' ||\n hostname.endsWith('.r2.cloudflarestorage.com') ||\n hostname.endsWith('.cloudflare.com')\n ) {\n return 'r2';\n }\n\n if (\n hostname === 'storage.googleapis.com' ||\n hostname.endsWith('.storage.googleapis.com') ||\n hostname.endsWith('.googleapis.com')\n ) {\n return 'gcs';\n }\n\n if (\n hostname === 'blob.core.windows.net' ||\n hostname.endsWith('.blob.core.windows.net') ||\n hostname.endsWith('.azure.com')\n ) {\n return 'azure';\n }\n\n if (hostname.includes('minio')) {\n return 'minio';\n }\n\n // Generic S3-compatible (DigitalOcean Spaces, etc.)\n return 's3';\n }\n\n /**\n * Get a user-friendly display name based on the icon/provider.\n */\n private getDefaultDisplayName(icon: FilesystemIcon): string | undefined {\n switch (icon) {\n case 'aws-s3':\n return 'AWS S3';\n case 'r2':\n case 'cloudflare':\n case 'cloudflare-r2':\n return 'Cloudflare R2';\n case 'gcs':\n case 'google-cloud':\n case 'google-cloud-storage':\n return 'Google Cloud Storage';\n case 'azure':\n case 'azure-blob':\n return 'Azure Blob';\n case 'minio':\n return 'MinIO';\n case 's3':\n return 'S3';\n default:\n // Unknown icon - don't assume a display name\n return undefined;\n }\n }\n\n private getClient(): S3Client {\n if (this._client) return this._client;\n\n const hasStaticCredentials = this.accessKeyId && this.secretAccessKey;\n\n let credentials: AwsCredentialIdentity | AwsCredentialIdentityProvider | undefined;\n if (this.credentials) {\n credentials = this.credentials;\n } else if (hasStaticCredentials) {\n credentials = {\n accessKeyId: this.accessKeyId!,\n secretAccessKey: this.secretAccessKey!,\n ...(this.sessionToken && { sessionToken: this.sessionToken }),\n };\n }\n // When credentials is undefined, SDK uses its default provider chain\n\n this._client = new S3Client({\n region: this.region,\n ...(credentials !== undefined && { credentials }),\n endpoint: this.endpoint,\n forcePathStyle: this.forcePathStyle,\n });\n\n return this._client;\n }\n\n /**\n * Ensure the filesystem is initialized and return the S3 client.\n * Uses base class ensureReady() for status management, then returns client.\n */\n private async getReadyClient(): Promise<S3Client> {\n await this.ensureReady();\n return this.getClient();\n }\n\n private toKey(path: string): string {\n // Remove leading slashes, then resolve \".\" and \"./\" to empty string (root)\n const cleanPath = path.replace(/^\\/+/, '').replace(/^\\.(?:\\/|$)/, '');\n return this.prefix + cleanPath;\n }\n\n // ---------------------------------------------------------------------------\n // File Operations\n // ---------------------------------------------------------------------------\n\n async readFile(path: string, options?: ReadOptions): Promise<string | Buffer> {\n // A trailing slash means a directory: never return the empty body of a directory marker\n if (path.endsWith('/')) {\n throw new FileNotFoundError(path);\n }\n\n const client = await this.getReadyClient();\n\n try {\n const response = await client.send(\n new GetObjectCommand({\n Bucket: this.bucket,\n Key: this.toKey(path),\n }),\n );\n\n const body = await response.Body?.transformToByteArray();\n if (!body) throw new FileNotFoundError(path);\n\n const buffer = Buffer.from(body);\n if (options?.encoding) {\n return buffer.toString(options.encoding);\n }\n return buffer;\n } catch (error: unknown) {\n if (isNotFoundError(error)) {\n throw new FileNotFoundError(path);\n }\n throw this.handleError(error);\n }\n }\n\n async writeFile(path: string, content: FileContent, options?: WriteOptions): Promise<void> {\n const client = await this.getReadyClient();\n\n if (options?.overwrite === false && (await this.exists(path))) {\n throw new FileExistsError(path);\n }\n\n const body = typeof content === 'string' ? Buffer.from(content, 'utf-8') : Buffer.from(content);\n const contentType = getMimeType(path);\n\n await client.send(\n new PutObjectCommand({\n Bucket: this.bucket,\n Key: this.toKey(path),\n Body: body,\n ContentType: contentType,\n }),\n );\n }\n\n async appendFile(path: string, content: FileContent): Promise<void> {\n // S3 doesn't support append, so read + write\n let existing = '';\n try {\n existing = (await this.readFile(path, { encoding: 'utf-8' })) as string;\n } catch (error) {\n if (error instanceof FileNotFoundError) {\n // File doesn't exist, start fresh\n } else {\n throw error;\n }\n }\n\n const appendContent = typeof content === 'string' ? content : Buffer.from(content).toString('utf-8');\n await this.writeFile(path, existing + appendContent);\n }\n\n async deleteFile(path: string, options?: RemoveOptions): Promise<void> {\n // Check if this is a directory - if so, use rmdir instead\n const isDir = await this.isDirectory(path);\n if (isDir) {\n await this.rmdir(path, { recursive: true, force: options?.force });\n return;\n }\n\n const client = await this.getReadyClient();\n\n try {\n await client.send(\n new DeleteObjectCommand({\n Bucket: this.bucket,\n Key: this.toKey(path),\n }),\n );\n } catch (error: unknown) {\n if (options?.force) return;\n if (isNotFoundError(error)) {\n throw new FileNotFoundError(path);\n }\n throw this.handleError(error);\n }\n }\n\n async copyFile(src: string, dest: string, options?: CopyOptions): Promise<void> {\n // A trailing slash means a directory: never copy a directory marker as a file\n if (src.endsWith('/')) {\n throw new FileNotFoundError(src);\n }\n\n const client = await this.getReadyClient();\n\n if (options?.overwrite === false && (await this.exists(dest))) {\n throw new FileExistsError(dest);\n }\n\n try {\n await client.send(\n new CopyObjectCommand({\n Bucket: this.bucket,\n CopySource: `${this.bucket}/${encodeURIComponent(this.toKey(src)).replace(/%2F/g, '/')}`,\n Key: this.toKey(dest),\n }),\n );\n } catch (error: unknown) {\n if (isNotFoundError(error)) {\n throw new FileNotFoundError(src);\n }\n throw this.handleError(error);\n }\n }\n\n async moveFile(src: string, dest: string, options?: CopyOptions): Promise<void> {\n await this.copyFile(src, dest, options);\n await this.deleteFile(src, { force: true });\n }\n\n // ---------------------------------------------------------------------------\n // Directory Operations\n // ---------------------------------------------------------------------------\n\n async mkdir(path: string, _options?: { recursive?: boolean }): Promise<void> {\n // S3 doesn't have real directories - they're just key prefixes.\n // Write a zero-byte marker object at \"<key>/\" (the convention the S3 console uses)\n // so an empty directory is visible to readdir(), exists() and stat().\n // Parent directories need no marker of their own: the marker key already\n // matches every parent prefix, so nested paths work without `recursive`.\n const key = trimSlashes(this.toKey(path));\n if (!key || key + '/' === this.prefix) return; // Root always exists\n\n const client = await this.getReadyClient();\n\n // A file already occupies this path. A credential that can write but not read\n // cannot probe, so treat a failed probe as \"no file there\" and let the write below\n // report a real problem.\n try {\n await client.send(new HeadObjectCommand({ Bucket: this.bucket, Key: key }));\n throw new FileExistsError(path);\n } catch (error: unknown) {\n if (error instanceof FileExistsError) throw error;\n }\n\n await client.send(\n new PutObjectCommand({\n Bucket: this.bucket,\n Key: key + '/',\n Body: new Uint8Array(0),\n }),\n );\n }\n\n async rmdir(path: string, options?: RemoveOptions): Promise<void> {\n if (!options?.recursive) {\n // Check if directory is empty (the directory marker is not listed as an entry)\n const entries = await this.readdir(path);\n if (entries.length > 0) {\n throw new Error(`Directory not empty: ${path}`);\n }\n // Remove the directory marker so the empty directory disappears\n const key = trimSlashes(this.toKey(path));\n if (key && key + '/' !== this.prefix) {\n const client = await this.getReadyClient();\n await client.send(\n new DeleteObjectCommand({\n Bucket: this.bucket,\n Key: key + '/',\n }),\n );\n }\n return;\n }\n\n // Delete all objects with this prefix\n const client = await this.getReadyClient();\n\n const prefix = this.toKey(path).replace(/\\/$/, '') + '/';\n\n let continuationToken: string | undefined;\n do {\n const listResponse = await client.send(\n new ListObjectsV2Command({\n Bucket: this.bucket,\n Prefix: prefix,\n ContinuationToken: continuationToken,\n }),\n );\n\n if (listResponse.Contents && listResponse.Contents.length > 0) {\n const deleteResponse = await client.send(\n new DeleteObjectsCommand({\n Bucket: this.bucket,\n Delete: {\n Objects: listResponse.Contents.filter((obj): obj is { Key: string } => !!obj.Key).map(obj => ({\n Key: obj.Key,\n })),\n },\n }),\n );\n if (deleteResponse.Errors && deleteResponse.Errors.length > 0) {\n throw new Error(`Failed to delete ${deleteResponse.Errors.length} object(s) in ${path}`);\n }\n }\n\n continuationToken = listResponse.NextContinuationToken;\n } while (continuationToken);\n }\n\n async readdir(path: string, options?: ListOptions): Promise<FileEntry[]> {\n const client = await this.getReadyClient();\n\n const prefix = this.toKey(path).replace(/\\/$/, '');\n const searchPrefix = prefix ? prefix + '/' : '';\n\n const entries: FileEntry[] = [];\n const seenDirs = new Set<string>();\n\n let continuationToken: string | undefined;\n do {\n const response = await client.send(\n new ListObjectsV2Command({\n Bucket: this.bucket,\n Prefix: searchPrefix,\n Delimiter: options?.recursive ? undefined : '/',\n ContinuationToken: continuationToken,\n }),\n );\n\n // Add files\n if (response.Contents) {\n for (const obj of response.Contents) {\n const key = obj.Key;\n if (!key || key === searchPrefix) continue;\n\n const relativePath = key.slice(searchPrefix.length);\n if (!relativePath) continue;\n\n // Skip if this looks like a directory marker\n if (relativePath.endsWith('/')) {\n // A directory is not a file, so an extension filter must exclude it. Only a\n // recursive listing reaches this branch for a nested marker.\n if (options?.recursive && options.extension) continue;\n // A nested marker such as \"a/b/\" belongs to the directory \"a\" in a\n // non-recursive listing; only a recursive listing reports the full path.\n const dirName = options?.recursive ? relativePath.slice(0, -1) : relativePath.split('/')[0]!;\n if (!dirName) continue;\n if (!seenDirs.has(dirName)) {\n seenDirs.add(dirName);\n entries.push({ name: dirName, type: 'directory' });\n }\n continue;\n }\n\n const name = options?.recursive ? relativePath : relativePath.split('/')[0];\n\n // Skip if name is undefined or empty\n if (!name) continue;\n\n // Filter by extension if specified\n if (options?.extension) {\n const extensions = Array.isArray(options.extension) ? options.extension : [options.extension];\n if (!extensions.some(ext => name.endsWith(ext))) {\n continue;\n }\n }\n\n entries.push({\n name,\n type: 'file',\n size: obj.Size,\n });\n }\n }\n\n // Add directories (common prefixes)\n if (response.CommonPrefixes) {\n for (const prefixObj of response.CommonPrefixes) {\n if (!prefixObj.Prefix) continue;\n const dirName = prefixObj.Prefix.slice(searchPrefix.length).replace(/\\/$/, '');\n if (dirName && !seenDirs.has(dirName)) {\n seenDirs.add(dirName);\n entries.push({ name: dirName, type: 'directory' });\n }\n }\n }\n\n continuationToken = response.NextContinuationToken;\n } while (continuationToken);\n\n return entries;\n }\n\n // ---------------------------------------------------------------------------\n // Path Operations\n // ---------------------------------------------------------------------------\n\n async exists(path: string): Promise<boolean> {\n const key = this.toKey(path);\n if (!key) return true; // Root always exists\n\n const client = await this.getReadyClient();\n\n // Check if it's a file\n try {\n await client.send(\n new HeadObjectCommand({\n Bucket: this.bucket,\n Key: key,\n }),\n );\n return true;\n } catch (error: unknown) {\n if (!isNotFoundError(error)) throw this.handleError(error);\n // Not a file, check if it's a \"directory\" (has objects with this prefix)\n }\n\n // Check if it's a directory prefix\n const response: { Contents?: unknown[] } = await client.send(\n new ListObjectsV2Command({\n Bucket: this.bucket,\n Prefix: key.replace(/\\/$/, '') + '/',\n MaxKeys: 1,\n }),\n );\n\n return (response.Contents?.length ?? 0) > 0;\n }\n\n async stat(path: string): Promise<FileStat> {\n // The key never keeps a trailing slash, so a lookup never matches a directory marker\n const key = trimSlashes(this.toKey(path));\n // A trailing slash always means a directory, so never look for a file\n const directoryOnly = path.endsWith('/');\n\n // Root is always a directory\n if (!key) {\n return {\n name: '',\n path,\n type: 'directory',\n size: 0,\n createdAt: new Date(),\n modifiedAt: new Date(),\n };\n }\n\n const name = key.split('/').pop() ?? '';\n\n if (!directoryOnly) {\n const client = await this.getReadyClient();\n\n try {\n const response: { ContentLength?: number; LastModified?: Date } = await client.send(\n new HeadObjectCommand({\n Bucket: this.bucket,\n Key: key,\n }),\n );\n\n return {\n name,\n path,\n type: 'file',\n size: response.ContentLength ?? 0,\n createdAt: response.LastModified ?? new Date(),\n modifiedAt: response.LastModified ?? new Date(),\n };\n } catch (error: unknown) {\n if (!isNotFoundError(error)) throw this.handleError(error);\n }\n }\n\n // Check if it's a directory\n const isDir = await this.isDirectory(path);\n if (isDir) {\n return {\n name,\n path,\n type: 'directory',\n size: 0,\n createdAt: new Date(),\n modifiedAt: new Date(),\n };\n }\n throw new FileNotFoundError(path);\n }\n\n async isFile(path: string): Promise<boolean> {\n // A trailing slash always means a directory\n if (path.endsWith('/')) return false;\n\n // The key never keeps a trailing slash, so a lookup never matches a directory marker\n const key = trimSlashes(this.toKey(path));\n if (!key) return false; // Root is a directory, not a file\n\n const client = await this.getReadyClient();\n\n try {\n await client.send(\n new HeadObjectCommand({\n Bucket: this.bucket,\n Key: key,\n }),\n );\n return true;\n } catch (error: unknown) {\n if (!isNotFoundError(error)) throw this.handleError(error);\n return false;\n }\n }\n\n async isDirectory(path: string): Promise<boolean> {\n const key = this.toKey(path);\n if (!key) return true; // Root is always a directory\n\n const client = await this.getReadyClient();\n\n const response: { Contents?: unknown[] } = await client.send(\n new ListObjectsV2Command({\n Bucket: this.bucket,\n Prefix: key.replace(/\\/$/, '') + '/',\n MaxKeys: 1,\n }),\n );\n\n return (response.Contents?.length ?? 0) > 0;\n }\n\n // ---------------------------------------------------------------------------\n // Lifecycle (overrides base class protected methods)\n // ---------------------------------------------------------------------------\n\n /**\n * Initialize the S3 client.\n * Status management is handled by the base class.\n */\n async init(): Promise<void> {\n // Verify we can access the bucket\n const client = this.getClient();\n try {\n await client.send(new HeadBucketCommand({ Bucket: this.bucket }));\n } catch (error) {\n // Extract httpStatusCode if available\n const statusCode = (error as { $metadata?: { httpStatusCode?: number } }).$metadata?.httpStatusCode;\n\n // Create error with status property for proper HTTP response codes\n const createError = (message: string) => {\n const err = new Error(message) as Error & { status?: number };\n if (statusCode) err.status = statusCode;\n return err;\n };\n\n // Provide better error messages for common S3 errors\n if (isAccessDeniedError(error)) {\n throw createError(`Access denied to bucket \"${this.bucket}\" - check credentials and permissions`);\n }\n if (isNotFoundError(error)) {\n throw createError(`Bucket \"${this.bucket}\" not found`);\n }\n const message = error instanceof Error ? error.message : String(error);\n if (statusCode) {\n throw createError(`Failed to access bucket \"${this.bucket}\" (HTTP ${statusCode}): ${message}`);\n }\n throw error;\n }\n }\n\n /**\n * Clean up the S3 client.\n * Status management is handled by the base class.\n */\n async destroy(): Promise<void> {\n this._client = null;\n }\n}\n","import {\n S3Client,\n GetObjectCommand,\n PutObjectCommand,\n DeleteObjectCommand,\n HeadObjectCommand,\n ListObjectsV2Command,\n DeleteObjectsCommand,\n} from '@aws-sdk/client-s3';\n\nimport type { AwsCredentialIdentity, AwsCredentialIdentityProvider } from '@aws-sdk/types';\n\nimport { BlobStore } from '@mastra/core/storage';\nimport type { StorageBlobEntry } from '@mastra/core/storage';\n\n/**\n * Configuration for S3BlobStore.\n *\n * Compatible with AWS S3, Cloudflare R2, MinIO, DigitalOcean Spaces, etc.\n */\nexport interface S3BlobStoreOptions {\n /** S3 bucket name */\n bucket: string;\n /** AWS region (use 'auto' for R2) */\n region: string;\n /**\n * AWS credentials or credential provider function.\n * Accepts static credentials or a provider that auto-refreshes\n * (e.g. fromNodeProviderChain() from @aws-sdk/credential-providers).\n * When set, takes precedence over accessKeyId/secretAccessKey/sessionToken.\n * When ALL credential options are omitted, the SDK default credential\n * provider chain is used (env vars, ~/.aws, IMDS, ECS container credentials).\n */\n credentials?: AwsCredentialIdentity | AwsCredentialIdentityProvider;\n /** AWS access key ID. Optional - omit to use the SDK default credential provider chain. */\n accessKeyId?: string;\n /** AWS secret access key. Optional - omit to use the SDK default credential provider chain. */\n secretAccessKey?: string;\n /** AWS session token for temporary credentials (SSO, AssumeRole, container credentials, etc.) */\n sessionToken?: string;\n /**\n * Custom endpoint URL for S3-compatible storage.\n * Examples:\n * - Cloudflare R2: 'https://{accountId}.r2.cloudflarestorage.com'\n * - MinIO: 'http://localhost:9000'\n */\n endpoint?: string;\n /** Force path-style URLs (required for some S3-compatible services like MinIO) */\n forcePathStyle?: boolean;\n /**\n * Key prefix for all blob objects.\n * Defaults to 'mastra_skill_blobs/'.\n */\n prefix?: string;\n}\n\n/** Trim leading and trailing slashes. */\nfunction trimSlashes(s: string): string {\n let start = 0;\n let end = s.length;\n while (start < end && s[start] === '/') start++;\n while (end > start && s[end - 1] === '/') end--;\n return s.slice(start, end);\n}\n\n/**\n * S3-backed content-addressable blob store for skill versioning.\n *\n * Each blob is stored as an S3 object keyed by its SHA-256 hash.\n * Metadata (size, mimeType, createdAt) is stored in S3 object user metadata.\n *\n * Since blobs are content-addressable, writes are idempotent — the same hash\n * always maps to the same content, so overwrites are safe and equivalent to\n * a no-op.\n *\n * @example AWS S3\n * ```typescript\n * import { S3BlobStore } from '@mastra/s3';\n *\n * const blobs = new S3BlobStore({\n * bucket: 'my-skill-blobs',\n * region: 'us-east-1',\n * accessKeyId: process.env.AWS_ACCESS_KEY_ID!,\n * secretAccessKey: process.env.AWS_SECRET_ACCESS_KEY!,\n * });\n * ```\n *\n * @example MinIO (local)\n * ```typescript\n * import { S3BlobStore } from '@mastra/s3';\n *\n * const blobs = new S3BlobStore({\n * bucket: 'skill-blobs',\n * region: 'us-east-1',\n * accessKeyId: 'minioadmin',\n * secretAccessKey: 'minioadmin',\n * endpoint: 'http://localhost:9000',\n * forcePathStyle: true,\n * });\n * ```\n */\nexport class S3BlobStore extends BlobStore {\n private readonly bucket: string;\n private readonly prefix: string;\n private _client: S3Client | null = null;\n\n private readonly region: string;\n private readonly credentials?: AwsCredentialIdentity | AwsCredentialIdentityProvider;\n private readonly accessKeyId?: string;\n private readonly secretAccessKey?: string;\n private readonly sessionToken?: string;\n private readonly endpoint?: string;\n private readonly forcePathStyle: boolean;\n\n constructor(options: S3BlobStoreOptions) {\n super();\n this.bucket = options.bucket;\n this.region = options.region;\n this.credentials = options.credentials;\n this.accessKeyId = options.accessKeyId;\n this.secretAccessKey = options.secretAccessKey;\n this.sessionToken = options.sessionToken;\n this.endpoint = options.endpoint;\n this.forcePathStyle = options.forcePathStyle ?? !!options.endpoint;\n this.prefix = options.prefix ? trimSlashes(options.prefix) + '/' : 'mastra_skill_blobs/';\n }\n\n private getClient(): S3Client {\n if (this._client) return this._client;\n\n const hasStaticCredentials = this.accessKeyId && this.secretAccessKey;\n\n let credentials: AwsCredentialIdentity | AwsCredentialIdentityProvider | undefined;\n if (this.credentials) {\n credentials = this.credentials;\n } else if (hasStaticCredentials) {\n credentials = {\n accessKeyId: this.accessKeyId!,\n secretAccessKey: this.secretAccessKey!,\n ...(this.sessionToken && { sessionToken: this.sessionToken }),\n };\n }\n // When credentials is undefined, SDK uses its default provider chain\n\n this._client = new S3Client({\n region: this.region,\n ...(credentials !== undefined && { credentials }),\n endpoint: this.endpoint,\n forcePathStyle: this.forcePathStyle,\n });\n return this._client;\n }\n\n private toKey(hash: string): string {\n return this.prefix + hash;\n }\n\n async init(): Promise<void> {\n // S3 doesn't require table creation — the bucket is expected to exist.\n }\n\n async put(entry: StorageBlobEntry): Promise<void> {\n const client = this.getClient();\n const now = entry.createdAt ?? new Date();\n\n await client.send(\n new PutObjectCommand({\n Bucket: this.bucket,\n Key: this.toKey(entry.hash),\n Body: entry.content,\n ContentType: entry.mimeType ?? 'application/octet-stream',\n Metadata: {\n size: String(entry.size),\n createdat: now.toISOString(),\n ...(entry.mimeType ? { mimetype: entry.mimeType } : {}),\n },\n }),\n );\n }\n\n async get(hash: string): Promise<StorageBlobEntry | null> {\n const client = this.getClient();\n\n try {\n const response = await client.send(\n new GetObjectCommand({\n Bucket: this.bucket,\n Key: this.toKey(hash),\n }),\n );\n\n const body = await response.Body?.transformToString('utf-8');\n if (body === undefined || body === null) return null;\n\n const metadata = response.Metadata ?? {};\n return {\n hash,\n content: body,\n size: metadata.size != null ? Number(metadata.size) : Buffer.byteLength(body, 'utf-8'),\n mimeType: metadata.mimetype || response.ContentType || undefined,\n createdAt: metadata.createdat ? new Date(metadata.createdat) : new Date(),\n };\n } catch (error: unknown) {\n if (isNotFoundError(error)) return null;\n throw error;\n }\n }\n\n async has(hash: string): Promise<boolean> {\n const client = this.getClient();\n\n try {\n await client.send(\n new HeadObjectCommand({\n Bucket: this.bucket,\n Key: this.toKey(hash),\n }),\n );\n return true;\n } catch (error: unknown) {\n if (isNotFoundError(error)) return false;\n throw error;\n }\n }\n\n async delete(hash: string): Promise<boolean> {\n // Pre-check is intentional: S3 DeleteObject returns 204 regardless of\n // whether the object existed, so we check first for an accurate return.\n // The TOCTOU gap is acceptable for content-addressable blobs.\n const existed = await this.has(hash);\n if (!existed) return false;\n\n const client = this.getClient();\n await client.send(\n new DeleteObjectCommand({\n Bucket: this.bucket,\n Key: this.toKey(hash),\n }),\n );\n return true;\n }\n\n async putMany(entries: StorageBlobEntry[]): Promise<void> {\n if (entries.length === 0) return;\n // S3 doesn't have a batch PUT, so we parallelize individual puts.\n // Content-addressable means duplicate writes are idempotent.\n await Promise.all(entries.map(entry => this.put(entry)));\n }\n\n async getMany(hashes: string[]): Promise<Map<string, StorageBlobEntry>> {\n const result = new Map<string, StorageBlobEntry>();\n if (hashes.length === 0) return result;\n\n // Parallelize individual gets\n const entries = await Promise.all(hashes.map(hash => this.get(hash)));\n for (const entry of entries) {\n if (entry) {\n result.set(entry.hash, entry);\n }\n }\n return result;\n }\n\n async dangerouslyClearAll(): Promise<void> {\n const client = this.getClient();\n\n let continuationToken: string | undefined;\n do {\n const listResponse = await client.send(\n new ListObjectsV2Command({\n Bucket: this.bucket,\n Prefix: this.prefix,\n ContinuationToken: continuationToken,\n }),\n );\n\n const objects = listResponse.Contents;\n if (objects && objects.length > 0) {\n await client.send(\n new DeleteObjectsCommand({\n Bucket: this.bucket,\n Delete: {\n Objects: objects.filter(obj => obj.Key != null).map(obj => ({ Key: obj.Key! })),\n Quiet: true,\n },\n }),\n );\n }\n\n continuationToken = listResponse.IsTruncated ? listResponse.NextContinuationToken : undefined;\n } while (continuationToken);\n }\n}\n\nfunction isNotFoundError(error: unknown): boolean {\n if (!error || typeof error !== 'object' || !('name' in error)) return false;\n const name = (error as { name: string }).name;\n return name === 'NotFound' || name === 'NoSuchKey' || name === '404';\n}\n","/**\n * S3 filesystem provider descriptor for MastraEditor.\n *\n * @example\n * ```typescript\n * import { s3FilesystemProvider } from '@mastra/s3';\n *\n * const editor = new MastraEditor({\n * filesystems: [s3FilesystemProvider],\n * });\n * ```\n */\nimport type { FilesystemProvider, BlobStoreProvider } from '@mastra/core/editor';\nimport { S3BlobStore } from './blob-store';\nimport type { S3BlobStoreOptions } from './blob-store';\nimport { S3Filesystem } from './filesystem';\nimport type { S3FilesystemOptions } from './filesystem';\n\nexport const s3FilesystemProvider: FilesystemProvider<S3FilesystemOptions> = {\n id: 's3',\n name: 'Amazon S3',\n description: 'S3 or S3-compatible storage (AWS, R2, MinIO, DO Spaces)',\n configSchema: {\n type: 'object',\n required: ['bucket', 'region'],\n properties: {\n bucket: { type: 'string', description: 'S3 bucket name' },\n region: { type: 'string', description: 'AWS region (use \"auto\" for R2)' },\n accessKeyId: { type: 'string', description: 'AWS access key ID' },\n secretAccessKey: { type: 'string', description: 'AWS secret access key' },\n sessionToken: { type: 'string', description: 'AWS session token for temporary credentials' },\n endpoint: { type: 'string', description: 'Custom endpoint URL for S3-compatible storage' },\n forcePathStyle: { type: 'boolean', description: 'Force path-style URLs', default: false },\n prefix: { type: 'string', description: 'Key prefix (acts like a subdirectory)' },\n readOnly: { type: 'boolean', description: 'Mount as read-only', default: false },\n },\n },\n createFilesystem: config => new S3Filesystem(config),\n};\n\n/**\n * S3 blob store provider descriptor for MastraEditor.\n *\n * @example\n * ```typescript\n * import { s3BlobStoreProvider } from '@mastra/s3';\n *\n * const editor = new MastraEditor({\n * blobStores: { s3: s3BlobStoreProvider },\n * });\n * ```\n */\nexport const s3BlobStoreProvider: BlobStoreProvider<S3BlobStoreOptions> = {\n id: 's3',\n name: 'Amazon S3 Blob Store',\n description: 'Content-addressable blob storage using S3 or S3-compatible storage (AWS, R2, MinIO, DO Spaces)',\n configSchema: {\n type: 'object',\n required: ['bucket', 'region', 'accessKeyId', 'secretAccessKey'],\n properties: {\n bucket: { type: 'string', description: 'S3 bucket name' },\n region: { type: 'string', description: 'AWS region (use \"auto\" for R2)' },\n accessKeyId: { type: 'string', description: 'AWS access key ID' },\n secretAccessKey: { type: 'string', description: 'AWS secret access key' },\n sessionToken: { type: 'string', description: 'AWS session token for temporary credentials' },\n endpoint: { type: 'string', description: 'Custom endpoint URL for S3-compatible storage' },\n forcePathStyle: { type: 'boolean', description: 'Force path-style URLs', default: false },\n prefix: { type: 'string', description: 'Key prefix for blob objects (default: mastra_skill_blobs/)' },\n },\n },\n createBlobStore: config => new S3BlobStore(config),\n};\n"],"mappings":"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"}
|
package/dist/index.js
CHANGED
|
@@ -305,6 +305,7 @@ var S3Filesystem = class extends MastraFilesystem {
|
|
|
305
305
|
return this.prefix + cleanPath;
|
|
306
306
|
}
|
|
307
307
|
async readFile(path, options) {
|
|
308
|
+
if (path.endsWith("/")) throw new FileNotFoundError(path);
|
|
308
309
|
const client = await this.getReadyClient();
|
|
309
310
|
try {
|
|
310
311
|
const body = await (await client.send(new GetObjectCommand({
|
|
@@ -363,6 +364,7 @@ var S3Filesystem = class extends MastraFilesystem {
|
|
|
363
364
|
}
|
|
364
365
|
}
|
|
365
366
|
async copyFile(src, dest, options) {
|
|
367
|
+
if (src.endsWith("/")) throw new FileNotFoundError(src);
|
|
366
368
|
const client = await this.getReadyClient();
|
|
367
369
|
if (options?.overwrite === false && await this.exists(dest)) throw new FileExistsError(dest);
|
|
368
370
|
try {
|
|
@@ -380,10 +382,33 @@ var S3Filesystem = class extends MastraFilesystem {
|
|
|
380
382
|
await this.copyFile(src, dest, options);
|
|
381
383
|
await this.deleteFile(src, { force: true });
|
|
382
384
|
}
|
|
383
|
-
async mkdir(
|
|
385
|
+
async mkdir(path, _options) {
|
|
386
|
+
const key = trimSlashes$1(this.toKey(path));
|
|
387
|
+
if (!key || key + "/" === this.prefix) return;
|
|
388
|
+
const client = await this.getReadyClient();
|
|
389
|
+
try {
|
|
390
|
+
await client.send(new HeadObjectCommand({
|
|
391
|
+
Bucket: this.bucket,
|
|
392
|
+
Key: key
|
|
393
|
+
}));
|
|
394
|
+
throw new FileExistsError(path);
|
|
395
|
+
} catch (error) {
|
|
396
|
+
if (error instanceof FileExistsError) throw error;
|
|
397
|
+
}
|
|
398
|
+
await client.send(new PutObjectCommand({
|
|
399
|
+
Bucket: this.bucket,
|
|
400
|
+
Key: key + "/",
|
|
401
|
+
Body: /* @__PURE__ */ new Uint8Array(0)
|
|
402
|
+
}));
|
|
403
|
+
}
|
|
384
404
|
async rmdir(path, options) {
|
|
385
405
|
if (!options?.recursive) {
|
|
386
406
|
if ((await this.readdir(path)).length > 0) throw new Error(`Directory not empty: ${path}`);
|
|
407
|
+
const key = trimSlashes$1(this.toKey(path));
|
|
408
|
+
if (key && key + "/" !== this.prefix) await (await this.getReadyClient()).send(new DeleteObjectCommand({
|
|
409
|
+
Bucket: this.bucket,
|
|
410
|
+
Key: key + "/"
|
|
411
|
+
}));
|
|
387
412
|
return;
|
|
388
413
|
}
|
|
389
414
|
const client = await this.getReadyClient();
|
|
@@ -425,7 +450,9 @@ var S3Filesystem = class extends MastraFilesystem {
|
|
|
425
450
|
const relativePath = key.slice(searchPrefix.length);
|
|
426
451
|
if (!relativePath) continue;
|
|
427
452
|
if (relativePath.endsWith("/")) {
|
|
428
|
-
|
|
453
|
+
if (options?.recursive && options.extension) continue;
|
|
454
|
+
const dirName = options?.recursive ? relativePath.slice(0, -1) : relativePath.split("/")[0];
|
|
455
|
+
if (!dirName) continue;
|
|
429
456
|
if (!seenDirs.has(dirName)) {
|
|
430
457
|
seenDirs.add(dirName);
|
|
431
458
|
entries.push({
|
|
@@ -481,7 +508,8 @@ var S3Filesystem = class extends MastraFilesystem {
|
|
|
481
508
|
}))).Contents?.length ?? 0) > 0;
|
|
482
509
|
}
|
|
483
510
|
async stat(path) {
|
|
484
|
-
const key = this.toKey(path);
|
|
511
|
+
const key = trimSlashes$1(this.toKey(path));
|
|
512
|
+
const directoryOnly = path.endsWith("/");
|
|
485
513
|
if (!key) return {
|
|
486
514
|
name: "",
|
|
487
515
|
path,
|
|
@@ -490,35 +518,39 @@ var S3Filesystem = class extends MastraFilesystem {
|
|
|
490
518
|
createdAt: /* @__PURE__ */ new Date(),
|
|
491
519
|
modifiedAt: /* @__PURE__ */ new Date()
|
|
492
520
|
};
|
|
493
|
-
const
|
|
494
|
-
|
|
495
|
-
const
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
type: "directory",
|
|
513
|
-
size: 0,
|
|
514
|
-
createdAt: /* @__PURE__ */ new Date(),
|
|
515
|
-
modifiedAt: /* @__PURE__ */ new Date()
|
|
516
|
-
};
|
|
517
|
-
throw new FileNotFoundError(path);
|
|
521
|
+
const name = key.split("/").pop() ?? "";
|
|
522
|
+
if (!directoryOnly) {
|
|
523
|
+
const client = await this.getReadyClient();
|
|
524
|
+
try {
|
|
525
|
+
const response = await client.send(new HeadObjectCommand({
|
|
526
|
+
Bucket: this.bucket,
|
|
527
|
+
Key: key
|
|
528
|
+
}));
|
|
529
|
+
return {
|
|
530
|
+
name,
|
|
531
|
+
path,
|
|
532
|
+
type: "file",
|
|
533
|
+
size: response.ContentLength ?? 0,
|
|
534
|
+
createdAt: response.LastModified ?? /* @__PURE__ */ new Date(),
|
|
535
|
+
modifiedAt: response.LastModified ?? /* @__PURE__ */ new Date()
|
|
536
|
+
};
|
|
537
|
+
} catch (error) {
|
|
538
|
+
if (!isNotFoundError$1(error)) throw this.handleError(error);
|
|
539
|
+
}
|
|
518
540
|
}
|
|
541
|
+
if (await this.isDirectory(path)) return {
|
|
542
|
+
name,
|
|
543
|
+
path,
|
|
544
|
+
type: "directory",
|
|
545
|
+
size: 0,
|
|
546
|
+
createdAt: /* @__PURE__ */ new Date(),
|
|
547
|
+
modifiedAt: /* @__PURE__ */ new Date()
|
|
548
|
+
};
|
|
549
|
+
throw new FileNotFoundError(path);
|
|
519
550
|
}
|
|
520
551
|
async isFile(path) {
|
|
521
|
-
|
|
552
|
+
if (path.endsWith("/")) return false;
|
|
553
|
+
const key = trimSlashes$1(this.toKey(path));
|
|
522
554
|
if (!key) return false;
|
|
523
555
|
const client = await this.getReadyClient();
|
|
524
556
|
try {
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","names":["isNotFoundError","trimSlashes"],"sources":["../src/filesystem/index.ts","../src/blob-store/index.ts","../src/provider.ts"],"sourcesContent":["import {\n S3Client,\n GetObjectCommand,\n PutObjectCommand,\n DeleteObjectCommand,\n CopyObjectCommand,\n ListObjectsV2Command,\n DeleteObjectsCommand,\n HeadObjectCommand,\n HeadBucketCommand,\n} from '@aws-sdk/client-s3';\n\nimport type { AwsCredentialIdentity, AwsCredentialIdentityProvider } from '@aws-sdk/types';\n\nimport type {\n FileContent,\n FileStat,\n FileEntry,\n ReadOptions,\n WriteOptions,\n ListOptions,\n RemoveOptions,\n CopyOptions,\n FilesystemMountConfig,\n FilesystemIcon,\n FilesystemInfo,\n ProviderStatus,\n MastraFilesystemOptions,\n} from '@mastra/core/workspace';\nimport { MastraFilesystem, FileNotFoundError, FileExistsError } from '@mastra/core/workspace';\n\n/**\n * S3 mount configuration.\n * Returned by S3Filesystem.getMountConfig() for FUSE mounting in sandboxes.\n */\nexport interface S3MountConfig extends FilesystemMountConfig {\n type: 's3';\n /** S3 bucket name */\n bucket: string;\n /** AWS region (use 'auto' for R2) */\n region?: string;\n /** Optional endpoint for S3-compatible storage (MinIO, R2, etc.) */\n endpoint?: string;\n /** AWS access key ID */\n accessKeyId?: string;\n /** AWS secret access key */\n secretAccessKey?: string;\n /** AWS session token for temporary credentials (SSO, AssumeRole, container credentials, etc.) */\n sessionToken?: string;\n /**\n * Optional prefix (subdirectory) to mount instead of the entire bucket.\n * Uses s3fs `bucket:/prefix` syntax to scope the mount to a specific path.\n * Leading/trailing slashes are normalized automatically.\n */\n prefix?: string;\n /** Mount as read-only */\n readOnly?: boolean;\n}\n\n/**\n * Common MIME types by file extension.\n */\nconst MIME_TYPES: Record<string, string> = {\n // Text\n '.txt': 'text/plain',\n '.md': 'text/markdown',\n '.markdown': 'text/markdown',\n '.html': 'text/html',\n '.htm': 'text/html',\n '.css': 'text/css',\n '.csv': 'text/csv',\n '.xml': 'text/xml',\n // Code\n '.js': 'text/javascript',\n '.mjs': 'text/javascript',\n '.ts': 'text/typescript',\n '.tsx': 'text/typescript',\n '.jsx': 'text/javascript',\n '.json': 'application/json',\n '.yaml': 'text/yaml',\n '.yml': 'text/yaml',\n '.py': 'text/x-python',\n '.rb': 'text/x-ruby',\n '.sh': 'text/x-shellscript',\n '.bash': 'text/x-shellscript',\n // Images\n '.png': 'image/png',\n '.jpg': 'image/jpeg',\n '.jpeg': 'image/jpeg',\n '.gif': 'image/gif',\n '.svg': 'image/svg+xml',\n '.webp': 'image/webp',\n '.ico': 'image/x-icon',\n // Documents\n '.pdf': 'application/pdf',\n // Archives\n '.zip': 'application/zip',\n '.gz': 'application/gzip',\n '.tar': 'application/x-tar',\n};\n\n/**\n * Get MIME type from file path extension.\n */\nfunction getMimeType(path: string): string {\n const ext = path.toLowerCase().match(/\\.[^.]+$/)?.[0];\n return ext ? (MIME_TYPES[ext] ?? 'application/octet-stream') : 'application/octet-stream';\n}\n\n/** Check if an error is a \"not found\" error from the S3 SDK. */\nfunction isNotFoundError(error: unknown): boolean {\n if (!error || typeof error !== 'object' || !('name' in error)) return false;\n const name = (error as { name: string }).name;\n return name === 'NotFound' || name === 'NoSuchKey' || name === '404';\n}\n\n/** Check if an error is an access denied error from the S3 SDK. */\nfunction isAccessDeniedError(error: unknown): boolean {\n if (!error || typeof error !== 'object') return false;\n const err = error as { name?: string; $metadata?: { httpStatusCode?: number } };\n return err.name === 'AccessDenied' || err.$metadata?.httpStatusCode === 403;\n}\n\n/**\n * S3 filesystem provider configuration.\n */\nexport interface S3FilesystemOptions extends MastraFilesystemOptions {\n /** Unique identifier for this filesystem instance */\n id?: string;\n /** S3 bucket name */\n bucket: string;\n /** Human-friendly display name for the UI */\n displayName?: string;\n /** Icon identifier for the UI (defaults to 's3') */\n icon?: FilesystemIcon;\n /** Description shown in tooltips */\n description?: string;\n /** AWS region (use 'auto' for R2) */\n region: string;\n /**\n * AWS credentials or credential provider function.\n * Accepts static credentials or a provider that auto-refreshes\n * (e.g. fromNodeProviderChain() from @aws-sdk/credential-providers).\n * When set, takes precedence over accessKeyId/secretAccessKey/sessionToken.\n * When ALL credential options are omitted, the SDK default credential\n * provider chain is used (env vars, ~/.aws, IMDS, ECS container credentials).\n */\n credentials?: AwsCredentialIdentity | AwsCredentialIdentityProvider;\n /**\n * AWS access key ID.\n * Optional - omit to use the SDK default credential provider chain.\n */\n accessKeyId?: string;\n /**\n * AWS secret access key.\n * Optional - omit to use the SDK default credential provider chain.\n */\n secretAccessKey?: string;\n /**\n * AWS session token for temporary credentials.\n * Required when using SSO, AssumeRole, container credentials, or any other\n * temporary credential provider.\n */\n sessionToken?: string;\n /**\n * Custom endpoint URL for S3-compatible storage.\n * Examples:\n * - Cloudflare R2: 'https://{accountId}.r2.cloudflarestorage.com'\n * - MinIO: 'http://localhost:9000'\n * - DigitalOcean Spaces: 'https://{region}.digitaloceanspaces.com'\n */\n endpoint?: string;\n /** Force path-style URLs (required for some S3-compatible services) */\n forcePathStyle?: boolean;\n /** Optional prefix for all keys (acts like a subdirectory) */\n prefix?: string;\n /** Mount as read-only (blocks write operations, mounts read-only in sandboxes) */\n readOnly?: boolean;\n}\n\n/**\n * S3 filesystem implementation.\n *\n * Stores files in an S3 bucket or S3-compatible storage service.\n * Supports mounting into E2B sandboxes via s3fs-fuse.\n *\n * @example AWS S3\n * ```typescript\n * import { S3Filesystem } from '@mastra/s3';\n *\n * const fs = new S3Filesystem({\n * bucket: 'my-bucket',\n * region: 'us-east-1',\n * accessKeyId: process.env.AWS_ACCESS_KEY_ID!,\n * secretAccessKey: process.env.AWS_SECRET_ACCESS_KEY!,\n * });\n * ```\n *\n * @example Cloudflare R2\n * ```typescript\n * import { S3Filesystem } from '@mastra/s3';\n *\n * const fs = new S3Filesystem({\n * bucket: 'my-bucket',\n * region: 'auto',\n * accessKeyId: process.env.R2_ACCESS_KEY_ID!,\n * secretAccessKey: process.env.R2_SECRET_ACCESS_KEY!,\n * endpoint: `https://${process.env.R2_ACCOUNT_ID}.r2.cloudflarestorage.com`,\n * });\n * ```\n *\n * @example MinIO (local)\n * ```typescript\n * import { S3Filesystem } from '@mastra/s3';\n *\n * const fs = new S3Filesystem({\n * bucket: 'my-bucket',\n * region: 'us-east-1',\n * accessKeyId: 'minioadmin',\n * secretAccessKey: 'minioadmin',\n * endpoint: 'http://localhost:9000',\n * forcePathStyle: true,\n * });\n * ```\n */\n\n/** Trim leading and trailing slashes without regex (avoids polynomial regex on user input). */\nfunction trimSlashes(s: string): string {\n let start = 0;\n let end = s.length;\n while (start < end && s[start] === '/') start++;\n while (end > start && s[end - 1] === '/') end--;\n return s.slice(start, end);\n}\n\nexport class S3Filesystem extends MastraFilesystem {\n readonly id: string;\n readonly name = 'S3Filesystem';\n readonly provider = 's3';\n readonly readOnly?: boolean;\n\n status: ProviderStatus = 'pending';\n\n // Display metadata for UI\n readonly displayName?: string;\n readonly icon: FilesystemIcon = 's3';\n readonly description?: string;\n\n private readonly bucket: string;\n private readonly region: string;\n private readonly credentials?: AwsCredentialIdentity | AwsCredentialIdentityProvider;\n private readonly accessKeyId?: string;\n private readonly secretAccessKey?: string;\n private readonly sessionToken?: string;\n private readonly endpoint?: string;\n private readonly forcePathStyle: boolean;\n private readonly prefix: string;\n\n private _client: S3Client | null = null;\n\n constructor(options: S3FilesystemOptions) {\n super({ ...options, name: 'S3Filesystem' });\n this.id = options.id ?? `s3-fs-${Date.now().toString(36)}-${Math.random().toString(36).slice(2, 8)}`;\n this.bucket = options.bucket;\n this.region = options.region;\n this.credentials = options.credentials;\n this.accessKeyId = options.accessKeyId;\n this.secretAccessKey = options.secretAccessKey;\n this.sessionToken = options.sessionToken;\n this.endpoint = options.endpoint;\n this.forcePathStyle = options.forcePathStyle ?? !!options.endpoint; // Default true for custom endpoints\n // Trim leading/trailing slashes from prefix using iterative approach (avoids polynomial regex)\n const trimmedPrefix = options.prefix ? trimSlashes(options.prefix) : '';\n this.prefix = trimmedPrefix ? trimmedPrefix + '/' : '';\n\n // Display metadata - detect icon first, then derive displayName from it\n this.icon = options.icon ?? this.detectIconFromEndpoint(options.endpoint);\n this.displayName = options.displayName ?? this.getDefaultDisplayName(this.icon);\n this.description = options.description;\n this.readOnly = options.readOnly;\n }\n\n /**\n * Get the underlying S3Client instance for direct access to AWS S3 APIs.\n *\n * Use this when you need to access S3 features not exposed through the\n * WorkspaceFilesystem interface (e.g., presigned URLs, multipart uploads,\n * custom S3 operations, etc.).\n *\n * @example Generate a presigned URL\n * ```typescript\n * import { GetObjectCommand } from '@aws-sdk/client-s3';\n * import { getSignedUrl } from '@aws-sdk/s3-request-presigner';\n *\n * const s3Client = fs.client;\n * const url = await getSignedUrl(s3Client, new GetObjectCommand({\n * Bucket: 'my-bucket',\n * Key: 'my-file.txt',\n * }));\n * ```\n */\n get client(): S3Client {\n return this.getClient();\n }\n\n /**\n * Get mount configuration for E2B sandbox.\n * Returns S3-compatible config that works with s3fs-fuse.\n *\n * Only static `accessKeyId`/`secretAccessKey`/`sessionToken` are included in the\n * returned config. If credentials are provided only via the `credentials` option\n * (provider function), the returned config will have no credentials because FUSE\n * mounts cannot call a provider function. Use static credentials for sandbox\n * mount compatibility.\n */\n getMountConfig(): S3MountConfig {\n const config: S3MountConfig = {\n type: 's3',\n bucket: this.bucket,\n region: this.region,\n endpoint: this.endpoint,\n };\n\n if (this.accessKeyId && this.secretAccessKey) {\n config.accessKeyId = this.accessKeyId;\n config.secretAccessKey = this.secretAccessKey;\n if (this.sessionToken) {\n config.sessionToken = this.sessionToken;\n }\n }\n\n if (this.prefix) {\n config.prefix = this.prefix;\n }\n\n if (this.readOnly) {\n config.readOnly = true;\n }\n\n return config;\n }\n\n /**\n * Get filesystem info for status reporting.\n */\n getInfo(): FilesystemInfo<{\n bucket: string;\n region: string;\n endpoint?: string;\n prefix?: string;\n }> {\n return {\n id: this.id,\n name: this.name,\n provider: this.provider,\n status: this.status,\n error: this.error,\n readOnly: this.readOnly,\n icon: this.icon,\n metadata: {\n bucket: this.bucket,\n region: this.region,\n ...(this.endpoint && { endpoint: this.endpoint }),\n ...(this.prefix && { prefix: this.prefix }),\n },\n };\n }\n\n /**\n * Handle an error, checking for access denied and updating status accordingly.\n * Returns the error for re-throwing.\n */\n private handleError(error: unknown): unknown {\n if (isAccessDeniedError(error)) {\n this.status = 'error';\n this.error = 'Access denied - check credentials and bucket permissions';\n }\n return error;\n }\n\n /**\n * Get instructions describing this S3 filesystem.\n * Used by agents to understand storage semantics.\n */\n getInstructions(): string {\n const providerName = this.displayName || 'S3';\n const access = this.readOnly ? 'Read-only' : 'Persistent';\n return `${providerName} storage in bucket \"${this.bucket}\". ${access} storage - files are retained across sessions.`;\n }\n\n /**\n * Detect the appropriate icon based on the S3 endpoint.\n */\n private detectIconFromEndpoint(endpoint?: string): FilesystemIcon {\n if (!endpoint) {\n // No custom endpoint = AWS S3\n return 'aws-s3';\n }\n\n // Parse hostname from endpoint URL for secure matching\n let hostname: string;\n try {\n const url = new URL(endpoint);\n hostname = url.hostname.toLowerCase();\n } catch {\n // If URL parsing fails, use the endpoint as-is (lowercased)\n hostname = endpoint.toLowerCase();\n }\n\n // Check hostname suffix for known providers (use dot-prefix or exact match to prevent subdomain spoofing)\n if (\n hostname === 'r2.cloudflarestorage.com' ||\n hostname.endsWith('.r2.cloudflarestorage.com') ||\n hostname.endsWith('.cloudflare.com')\n ) {\n return 'r2';\n }\n\n if (\n hostname === 'storage.googleapis.com' ||\n hostname.endsWith('.storage.googleapis.com') ||\n hostname.endsWith('.googleapis.com')\n ) {\n return 'gcs';\n }\n\n if (\n hostname === 'blob.core.windows.net' ||\n hostname.endsWith('.blob.core.windows.net') ||\n hostname.endsWith('.azure.com')\n ) {\n return 'azure';\n }\n\n if (hostname.includes('minio')) {\n return 'minio';\n }\n\n // Generic S3-compatible (DigitalOcean Spaces, etc.)\n return 's3';\n }\n\n /**\n * Get a user-friendly display name based on the icon/provider.\n */\n private getDefaultDisplayName(icon: FilesystemIcon): string | undefined {\n switch (icon) {\n case 'aws-s3':\n return 'AWS S3';\n case 'r2':\n case 'cloudflare':\n case 'cloudflare-r2':\n return 'Cloudflare R2';\n case 'gcs':\n case 'google-cloud':\n case 'google-cloud-storage':\n return 'Google Cloud Storage';\n case 'azure':\n case 'azure-blob':\n return 'Azure Blob';\n case 'minio':\n return 'MinIO';\n case 's3':\n return 'S3';\n default:\n // Unknown icon - don't assume a display name\n return undefined;\n }\n }\n\n private getClient(): S3Client {\n if (this._client) return this._client;\n\n const hasStaticCredentials = this.accessKeyId && this.secretAccessKey;\n\n let credentials: AwsCredentialIdentity | AwsCredentialIdentityProvider | undefined;\n if (this.credentials) {\n credentials = this.credentials;\n } else if (hasStaticCredentials) {\n credentials = {\n accessKeyId: this.accessKeyId!,\n secretAccessKey: this.secretAccessKey!,\n ...(this.sessionToken && { sessionToken: this.sessionToken }),\n };\n }\n // When credentials is undefined, SDK uses its default provider chain\n\n this._client = new S3Client({\n region: this.region,\n ...(credentials !== undefined && { credentials }),\n endpoint: this.endpoint,\n forcePathStyle: this.forcePathStyle,\n });\n\n return this._client;\n }\n\n /**\n * Ensure the filesystem is initialized and return the S3 client.\n * Uses base class ensureReady() for status management, then returns client.\n */\n private async getReadyClient(): Promise<S3Client> {\n await this.ensureReady();\n return this.getClient();\n }\n\n private toKey(path: string): string {\n // Remove leading slashes, then resolve \".\" and \"./\" to empty string (root)\n const cleanPath = path.replace(/^\\/+/, '').replace(/^\\.(?:\\/|$)/, '');\n return this.prefix + cleanPath;\n }\n\n // ---------------------------------------------------------------------------\n // File Operations\n // ---------------------------------------------------------------------------\n\n async readFile(path: string, options?: ReadOptions): Promise<string | Buffer> {\n const client = await this.getReadyClient();\n\n try {\n const response = await client.send(\n new GetObjectCommand({\n Bucket: this.bucket,\n Key: this.toKey(path),\n }),\n );\n\n const body = await response.Body?.transformToByteArray();\n if (!body) throw new FileNotFoundError(path);\n\n const buffer = Buffer.from(body);\n if (options?.encoding) {\n return buffer.toString(options.encoding);\n }\n return buffer;\n } catch (error: unknown) {\n if (isNotFoundError(error)) {\n throw new FileNotFoundError(path);\n }\n throw this.handleError(error);\n }\n }\n\n async writeFile(path: string, content: FileContent, options?: WriteOptions): Promise<void> {\n const client = await this.getReadyClient();\n\n if (options?.overwrite === false && (await this.exists(path))) {\n throw new FileExistsError(path);\n }\n\n const body = typeof content === 'string' ? Buffer.from(content, 'utf-8') : Buffer.from(content);\n const contentType = getMimeType(path);\n\n await client.send(\n new PutObjectCommand({\n Bucket: this.bucket,\n Key: this.toKey(path),\n Body: body,\n ContentType: contentType,\n }),\n );\n }\n\n async appendFile(path: string, content: FileContent): Promise<void> {\n // S3 doesn't support append, so read + write\n let existing = '';\n try {\n existing = (await this.readFile(path, { encoding: 'utf-8' })) as string;\n } catch (error) {\n if (error instanceof FileNotFoundError) {\n // File doesn't exist, start fresh\n } else {\n throw error;\n }\n }\n\n const appendContent = typeof content === 'string' ? content : Buffer.from(content).toString('utf-8');\n await this.writeFile(path, existing + appendContent);\n }\n\n async deleteFile(path: string, options?: RemoveOptions): Promise<void> {\n // Check if this is a directory - if so, use rmdir instead\n const isDir = await this.isDirectory(path);\n if (isDir) {\n await this.rmdir(path, { recursive: true, force: options?.force });\n return;\n }\n\n const client = await this.getReadyClient();\n\n try {\n await client.send(\n new DeleteObjectCommand({\n Bucket: this.bucket,\n Key: this.toKey(path),\n }),\n );\n } catch (error: unknown) {\n if (options?.force) return;\n if (isNotFoundError(error)) {\n throw new FileNotFoundError(path);\n }\n throw this.handleError(error);\n }\n }\n\n async copyFile(src: string, dest: string, options?: CopyOptions): Promise<void> {\n const client = await this.getReadyClient();\n\n if (options?.overwrite === false && (await this.exists(dest))) {\n throw new FileExistsError(dest);\n }\n\n try {\n await client.send(\n new CopyObjectCommand({\n Bucket: this.bucket,\n CopySource: `${this.bucket}/${encodeURIComponent(this.toKey(src)).replace(/%2F/g, '/')}`,\n Key: this.toKey(dest),\n }),\n );\n } catch (error: unknown) {\n if (isNotFoundError(error)) {\n throw new FileNotFoundError(src);\n }\n throw this.handleError(error);\n }\n }\n\n async moveFile(src: string, dest: string, options?: CopyOptions): Promise<void> {\n await this.copyFile(src, dest, options);\n await this.deleteFile(src, { force: true });\n }\n\n // ---------------------------------------------------------------------------\n // Directory Operations\n // ---------------------------------------------------------------------------\n\n async mkdir(_path: string, _options?: { recursive?: boolean }): Promise<void> {\n // S3 doesn't have real directories - they're just key prefixes\n // No-op, directories are created implicitly when files are written\n }\n\n async rmdir(path: string, options?: RemoveOptions): Promise<void> {\n if (!options?.recursive) {\n // Check if directory is empty\n const entries = await this.readdir(path);\n if (entries.length > 0) {\n throw new Error(`Directory not empty: ${path}`);\n }\n return;\n }\n\n // Delete all objects with this prefix\n const client = await this.getReadyClient();\n\n const prefix = this.toKey(path).replace(/\\/$/, '') + '/';\n\n let continuationToken: string | undefined;\n do {\n const listResponse = await client.send(\n new ListObjectsV2Command({\n Bucket: this.bucket,\n Prefix: prefix,\n ContinuationToken: continuationToken,\n }),\n );\n\n if (listResponse.Contents && listResponse.Contents.length > 0) {\n const deleteResponse = await client.send(\n new DeleteObjectsCommand({\n Bucket: this.bucket,\n Delete: {\n Objects: listResponse.Contents.filter((obj): obj is { Key: string } => !!obj.Key).map(obj => ({\n Key: obj.Key,\n })),\n },\n }),\n );\n if (deleteResponse.Errors && deleteResponse.Errors.length > 0) {\n throw new Error(`Failed to delete ${deleteResponse.Errors.length} object(s) in ${path}`);\n }\n }\n\n continuationToken = listResponse.NextContinuationToken;\n } while (continuationToken);\n }\n\n async readdir(path: string, options?: ListOptions): Promise<FileEntry[]> {\n const client = await this.getReadyClient();\n\n const prefix = this.toKey(path).replace(/\\/$/, '');\n const searchPrefix = prefix ? prefix + '/' : '';\n\n const entries: FileEntry[] = [];\n const seenDirs = new Set<string>();\n\n let continuationToken: string | undefined;\n do {\n const response = await client.send(\n new ListObjectsV2Command({\n Bucket: this.bucket,\n Prefix: searchPrefix,\n Delimiter: options?.recursive ? undefined : '/',\n ContinuationToken: continuationToken,\n }),\n );\n\n // Add files\n if (response.Contents) {\n for (const obj of response.Contents) {\n const key = obj.Key;\n if (!key || key === searchPrefix) continue;\n\n const relativePath = key.slice(searchPrefix.length);\n if (!relativePath) continue;\n\n // Skip if this looks like a directory marker\n if (relativePath.endsWith('/')) {\n const dirName = relativePath.slice(0, -1);\n if (!seenDirs.has(dirName)) {\n seenDirs.add(dirName);\n entries.push({ name: dirName, type: 'directory' });\n }\n continue;\n }\n\n const name = options?.recursive ? relativePath : relativePath.split('/')[0];\n\n // Skip if name is undefined or empty\n if (!name) continue;\n\n // Filter by extension if specified\n if (options?.extension) {\n const extensions = Array.isArray(options.extension) ? options.extension : [options.extension];\n if (!extensions.some(ext => name.endsWith(ext))) {\n continue;\n }\n }\n\n entries.push({\n name,\n type: 'file',\n size: obj.Size,\n });\n }\n }\n\n // Add directories (common prefixes)\n if (response.CommonPrefixes) {\n for (const prefixObj of response.CommonPrefixes) {\n if (!prefixObj.Prefix) continue;\n const dirName = prefixObj.Prefix.slice(searchPrefix.length).replace(/\\/$/, '');\n if (dirName && !seenDirs.has(dirName)) {\n seenDirs.add(dirName);\n entries.push({ name: dirName, type: 'directory' });\n }\n }\n }\n\n continuationToken = response.NextContinuationToken;\n } while (continuationToken);\n\n return entries;\n }\n\n // ---------------------------------------------------------------------------\n // Path Operations\n // ---------------------------------------------------------------------------\n\n async exists(path: string): Promise<boolean> {\n const key = this.toKey(path);\n if (!key) return true; // Root always exists\n\n const client = await this.getReadyClient();\n\n // Check if it's a file\n try {\n await client.send(\n new HeadObjectCommand({\n Bucket: this.bucket,\n Key: key,\n }),\n );\n return true;\n } catch (error: unknown) {\n if (!isNotFoundError(error)) throw this.handleError(error);\n // Not a file, check if it's a \"directory\" (has objects with this prefix)\n }\n\n // Check if it's a directory prefix\n const response: { Contents?: unknown[] } = await client.send(\n new ListObjectsV2Command({\n Bucket: this.bucket,\n Prefix: key.replace(/\\/$/, '') + '/',\n MaxKeys: 1,\n }),\n );\n\n return (response.Contents?.length ?? 0) > 0;\n }\n\n async stat(path: string): Promise<FileStat> {\n const key = this.toKey(path);\n\n // Root is always a directory\n if (!key) {\n return {\n name: '',\n path,\n type: 'directory',\n size: 0,\n createdAt: new Date(),\n modifiedAt: new Date(),\n };\n }\n\n const client = await this.getReadyClient();\n\n try {\n const response: { ContentLength?: number; LastModified?: Date } = await client.send(\n new HeadObjectCommand({\n Bucket: this.bucket,\n Key: key,\n }),\n );\n\n const name = path.split('/').pop() ?? '';\n return {\n name,\n path,\n type: 'file',\n size: response.ContentLength ?? 0,\n createdAt: response.LastModified ?? new Date(),\n modifiedAt: response.LastModified ?? new Date(),\n };\n } catch (error: unknown) {\n if (!isNotFoundError(error)) throw this.handleError(error);\n // Check if it's a directory\n const isDir = await this.isDirectory(path);\n if (isDir) {\n const name = path.split('/').filter(Boolean).pop() ?? '';\n return {\n name,\n path,\n type: 'directory',\n size: 0,\n createdAt: new Date(),\n modifiedAt: new Date(),\n };\n }\n throw new FileNotFoundError(path);\n }\n }\n\n async isFile(path: string): Promise<boolean> {\n const key = this.toKey(path);\n if (!key) return false; // Root is a directory, not a file\n\n const client = await this.getReadyClient();\n\n try {\n await client.send(\n new HeadObjectCommand({\n Bucket: this.bucket,\n Key: key,\n }),\n );\n return true;\n } catch (error: unknown) {\n if (!isNotFoundError(error)) throw this.handleError(error);\n return false;\n }\n }\n\n async isDirectory(path: string): Promise<boolean> {\n const key = this.toKey(path);\n if (!key) return true; // Root is always a directory\n\n const client = await this.getReadyClient();\n\n const response: { Contents?: unknown[] } = await client.send(\n new ListObjectsV2Command({\n Bucket: this.bucket,\n Prefix: key.replace(/\\/$/, '') + '/',\n MaxKeys: 1,\n }),\n );\n\n return (response.Contents?.length ?? 0) > 0;\n }\n\n // ---------------------------------------------------------------------------\n // Lifecycle (overrides base class protected methods)\n // ---------------------------------------------------------------------------\n\n /**\n * Initialize the S3 client.\n * Status management is handled by the base class.\n */\n async init(): Promise<void> {\n // Verify we can access the bucket\n const client = this.getClient();\n try {\n await client.send(new HeadBucketCommand({ Bucket: this.bucket }));\n } catch (error) {\n // Extract httpStatusCode if available\n const statusCode = (error as { $metadata?: { httpStatusCode?: number } }).$metadata?.httpStatusCode;\n\n // Create error with status property for proper HTTP response codes\n const createError = (message: string) => {\n const err = new Error(message) as Error & { status?: number };\n if (statusCode) err.status = statusCode;\n return err;\n };\n\n // Provide better error messages for common S3 errors\n if (isAccessDeniedError(error)) {\n throw createError(`Access denied to bucket \"${this.bucket}\" - check credentials and permissions`);\n }\n if (isNotFoundError(error)) {\n throw createError(`Bucket \"${this.bucket}\" not found`);\n }\n const message = error instanceof Error ? error.message : String(error);\n if (statusCode) {\n throw createError(`Failed to access bucket \"${this.bucket}\" (HTTP ${statusCode}): ${message}`);\n }\n throw error;\n }\n }\n\n /**\n * Clean up the S3 client.\n * Status management is handled by the base class.\n */\n async destroy(): Promise<void> {\n this._client = null;\n }\n}\n","import {\n S3Client,\n GetObjectCommand,\n PutObjectCommand,\n DeleteObjectCommand,\n HeadObjectCommand,\n ListObjectsV2Command,\n DeleteObjectsCommand,\n} from '@aws-sdk/client-s3';\n\nimport type { AwsCredentialIdentity, AwsCredentialIdentityProvider } from '@aws-sdk/types';\n\nimport { BlobStore } from '@mastra/core/storage';\nimport type { StorageBlobEntry } from '@mastra/core/storage';\n\n/**\n * Configuration for S3BlobStore.\n *\n * Compatible with AWS S3, Cloudflare R2, MinIO, DigitalOcean Spaces, etc.\n */\nexport interface S3BlobStoreOptions {\n /** S3 bucket name */\n bucket: string;\n /** AWS region (use 'auto' for R2) */\n region: string;\n /**\n * AWS credentials or credential provider function.\n * Accepts static credentials or a provider that auto-refreshes\n * (e.g. fromNodeProviderChain() from @aws-sdk/credential-providers).\n * When set, takes precedence over accessKeyId/secretAccessKey/sessionToken.\n * When ALL credential options are omitted, the SDK default credential\n * provider chain is used (env vars, ~/.aws, IMDS, ECS container credentials).\n */\n credentials?: AwsCredentialIdentity | AwsCredentialIdentityProvider;\n /** AWS access key ID. Optional - omit to use the SDK default credential provider chain. */\n accessKeyId?: string;\n /** AWS secret access key. Optional - omit to use the SDK default credential provider chain. */\n secretAccessKey?: string;\n /** AWS session token for temporary credentials (SSO, AssumeRole, container credentials, etc.) */\n sessionToken?: string;\n /**\n * Custom endpoint URL for S3-compatible storage.\n * Examples:\n * - Cloudflare R2: 'https://{accountId}.r2.cloudflarestorage.com'\n * - MinIO: 'http://localhost:9000'\n */\n endpoint?: string;\n /** Force path-style URLs (required for some S3-compatible services like MinIO) */\n forcePathStyle?: boolean;\n /**\n * Key prefix for all blob objects.\n * Defaults to 'mastra_skill_blobs/'.\n */\n prefix?: string;\n}\n\n/** Trim leading and trailing slashes. */\nfunction trimSlashes(s: string): string {\n let start = 0;\n let end = s.length;\n while (start < end && s[start] === '/') start++;\n while (end > start && s[end - 1] === '/') end--;\n return s.slice(start, end);\n}\n\n/**\n * S3-backed content-addressable blob store for skill versioning.\n *\n * Each blob is stored as an S3 object keyed by its SHA-256 hash.\n * Metadata (size, mimeType, createdAt) is stored in S3 object user metadata.\n *\n * Since blobs are content-addressable, writes are idempotent — the same hash\n * always maps to the same content, so overwrites are safe and equivalent to\n * a no-op.\n *\n * @example AWS S3\n * ```typescript\n * import { S3BlobStore } from '@mastra/s3';\n *\n * const blobs = new S3BlobStore({\n * bucket: 'my-skill-blobs',\n * region: 'us-east-1',\n * accessKeyId: process.env.AWS_ACCESS_KEY_ID!,\n * secretAccessKey: process.env.AWS_SECRET_ACCESS_KEY!,\n * });\n * ```\n *\n * @example MinIO (local)\n * ```typescript\n * import { S3BlobStore } from '@mastra/s3';\n *\n * const blobs = new S3BlobStore({\n * bucket: 'skill-blobs',\n * region: 'us-east-1',\n * accessKeyId: 'minioadmin',\n * secretAccessKey: 'minioadmin',\n * endpoint: 'http://localhost:9000',\n * forcePathStyle: true,\n * });\n * ```\n */\nexport class S3BlobStore extends BlobStore {\n private readonly bucket: string;\n private readonly prefix: string;\n private _client: S3Client | null = null;\n\n private readonly region: string;\n private readonly credentials?: AwsCredentialIdentity | AwsCredentialIdentityProvider;\n private readonly accessKeyId?: string;\n private readonly secretAccessKey?: string;\n private readonly sessionToken?: string;\n private readonly endpoint?: string;\n private readonly forcePathStyle: boolean;\n\n constructor(options: S3BlobStoreOptions) {\n super();\n this.bucket = options.bucket;\n this.region = options.region;\n this.credentials = options.credentials;\n this.accessKeyId = options.accessKeyId;\n this.secretAccessKey = options.secretAccessKey;\n this.sessionToken = options.sessionToken;\n this.endpoint = options.endpoint;\n this.forcePathStyle = options.forcePathStyle ?? !!options.endpoint;\n this.prefix = options.prefix ? trimSlashes(options.prefix) + '/' : 'mastra_skill_blobs/';\n }\n\n private getClient(): S3Client {\n if (this._client) return this._client;\n\n const hasStaticCredentials = this.accessKeyId && this.secretAccessKey;\n\n let credentials: AwsCredentialIdentity | AwsCredentialIdentityProvider | undefined;\n if (this.credentials) {\n credentials = this.credentials;\n } else if (hasStaticCredentials) {\n credentials = {\n accessKeyId: this.accessKeyId!,\n secretAccessKey: this.secretAccessKey!,\n ...(this.sessionToken && { sessionToken: this.sessionToken }),\n };\n }\n // When credentials is undefined, SDK uses its default provider chain\n\n this._client = new S3Client({\n region: this.region,\n ...(credentials !== undefined && { credentials }),\n endpoint: this.endpoint,\n forcePathStyle: this.forcePathStyle,\n });\n return this._client;\n }\n\n private toKey(hash: string): string {\n return this.prefix + hash;\n }\n\n async init(): Promise<void> {\n // S3 doesn't require table creation — the bucket is expected to exist.\n }\n\n async put(entry: StorageBlobEntry): Promise<void> {\n const client = this.getClient();\n const now = entry.createdAt ?? new Date();\n\n await client.send(\n new PutObjectCommand({\n Bucket: this.bucket,\n Key: this.toKey(entry.hash),\n Body: entry.content,\n ContentType: entry.mimeType ?? 'application/octet-stream',\n Metadata: {\n size: String(entry.size),\n createdat: now.toISOString(),\n ...(entry.mimeType ? { mimetype: entry.mimeType } : {}),\n },\n }),\n );\n }\n\n async get(hash: string): Promise<StorageBlobEntry | null> {\n const client = this.getClient();\n\n try {\n const response = await client.send(\n new GetObjectCommand({\n Bucket: this.bucket,\n Key: this.toKey(hash),\n }),\n );\n\n const body = await response.Body?.transformToString('utf-8');\n if (body === undefined || body === null) return null;\n\n const metadata = response.Metadata ?? {};\n return {\n hash,\n content: body,\n size: metadata.size != null ? Number(metadata.size) : Buffer.byteLength(body, 'utf-8'),\n mimeType: metadata.mimetype || response.ContentType || undefined,\n createdAt: metadata.createdat ? new Date(metadata.createdat) : new Date(),\n };\n } catch (error: unknown) {\n if (isNotFoundError(error)) return null;\n throw error;\n }\n }\n\n async has(hash: string): Promise<boolean> {\n const client = this.getClient();\n\n try {\n await client.send(\n new HeadObjectCommand({\n Bucket: this.bucket,\n Key: this.toKey(hash),\n }),\n );\n return true;\n } catch (error: unknown) {\n if (isNotFoundError(error)) return false;\n throw error;\n }\n }\n\n async delete(hash: string): Promise<boolean> {\n // Pre-check is intentional: S3 DeleteObject returns 204 regardless of\n // whether the object existed, so we check first for an accurate return.\n // The TOCTOU gap is acceptable for content-addressable blobs.\n const existed = await this.has(hash);\n if (!existed) return false;\n\n const client = this.getClient();\n await client.send(\n new DeleteObjectCommand({\n Bucket: this.bucket,\n Key: this.toKey(hash),\n }),\n );\n return true;\n }\n\n async putMany(entries: StorageBlobEntry[]): Promise<void> {\n if (entries.length === 0) return;\n // S3 doesn't have a batch PUT, so we parallelize individual puts.\n // Content-addressable means duplicate writes are idempotent.\n await Promise.all(entries.map(entry => this.put(entry)));\n }\n\n async getMany(hashes: string[]): Promise<Map<string, StorageBlobEntry>> {\n const result = new Map<string, StorageBlobEntry>();\n if (hashes.length === 0) return result;\n\n // Parallelize individual gets\n const entries = await Promise.all(hashes.map(hash => this.get(hash)));\n for (const entry of entries) {\n if (entry) {\n result.set(entry.hash, entry);\n }\n }\n return result;\n }\n\n async dangerouslyClearAll(): Promise<void> {\n const client = this.getClient();\n\n let continuationToken: string | undefined;\n do {\n const listResponse = await client.send(\n new ListObjectsV2Command({\n Bucket: this.bucket,\n Prefix: this.prefix,\n ContinuationToken: continuationToken,\n }),\n );\n\n const objects = listResponse.Contents;\n if (objects && objects.length > 0) {\n await client.send(\n new DeleteObjectsCommand({\n Bucket: this.bucket,\n Delete: {\n Objects: objects.filter(obj => obj.Key != null).map(obj => ({ Key: obj.Key! })),\n Quiet: true,\n },\n }),\n );\n }\n\n continuationToken = listResponse.IsTruncated ? listResponse.NextContinuationToken : undefined;\n } while (continuationToken);\n }\n}\n\nfunction isNotFoundError(error: unknown): boolean {\n if (!error || typeof error !== 'object' || !('name' in error)) return false;\n const name = (error as { name: string }).name;\n return name === 'NotFound' || name === 'NoSuchKey' || name === '404';\n}\n","/**\n * S3 filesystem provider descriptor for MastraEditor.\n *\n * @example\n * ```typescript\n * import { s3FilesystemProvider } from '@mastra/s3';\n *\n * const editor = new MastraEditor({\n * filesystems: [s3FilesystemProvider],\n * });\n * ```\n */\nimport type { FilesystemProvider, BlobStoreProvider } from '@mastra/core/editor';\nimport { S3BlobStore } from './blob-store';\nimport type { S3BlobStoreOptions } from './blob-store';\nimport { S3Filesystem } from './filesystem';\nimport type { S3FilesystemOptions } from './filesystem';\n\nexport const s3FilesystemProvider: FilesystemProvider<S3FilesystemOptions> = {\n id: 's3',\n name: 'Amazon S3',\n description: 'S3 or S3-compatible storage (AWS, R2, MinIO, DO Spaces)',\n configSchema: {\n type: 'object',\n required: ['bucket', 'region'],\n properties: {\n bucket: { type: 'string', description: 'S3 bucket name' },\n region: { type: 'string', description: 'AWS region (use \"auto\" for R2)' },\n accessKeyId: { type: 'string', description: 'AWS access key ID' },\n secretAccessKey: { type: 'string', description: 'AWS secret access key' },\n sessionToken: { type: 'string', description: 'AWS session token for temporary credentials' },\n endpoint: { type: 'string', description: 'Custom endpoint URL for S3-compatible storage' },\n forcePathStyle: { type: 'boolean', description: 'Force path-style URLs', default: false },\n prefix: { type: 'string', description: 'Key prefix (acts like a subdirectory)' },\n readOnly: { type: 'boolean', description: 'Mount as read-only', default: false },\n },\n },\n createFilesystem: config => new S3Filesystem(config),\n};\n\n/**\n * S3 blob store provider descriptor for MastraEditor.\n *\n * @example\n * ```typescript\n * import { s3BlobStoreProvider } from '@mastra/s3';\n *\n * const editor = new MastraEditor({\n * blobStores: { s3: s3BlobStoreProvider },\n * });\n * ```\n */\nexport const s3BlobStoreProvider: BlobStoreProvider<S3BlobStoreOptions> = {\n id: 's3',\n name: 'Amazon S3 Blob Store',\n description: 'Content-addressable blob storage using S3 or S3-compatible storage (AWS, R2, MinIO, DO Spaces)',\n configSchema: {\n type: 'object',\n required: ['bucket', 'region', 'accessKeyId', 'secretAccessKey'],\n properties: {\n bucket: { type: 'string', description: 'S3 bucket name' },\n region: { type: 'string', description: 'AWS region (use \"auto\" for R2)' },\n accessKeyId: { type: 'string', description: 'AWS access key ID' },\n secretAccessKey: { type: 'string', description: 'AWS secret access key' },\n sessionToken: { type: 'string', description: 'AWS session token for temporary credentials' },\n endpoint: { type: 'string', description: 'Custom endpoint URL for S3-compatible storage' },\n forcePathStyle: { type: 'boolean', description: 'Force path-style URLs', default: false },\n prefix: { type: 'string', description: 'Key prefix for blob objects (default: mastra_skill_blobs/)' },\n },\n },\n createBlobStore: config => new S3BlobStore(config),\n};\n"],"mappings":";;;;;;;AA8DA,MAAM,aAAqC;CAEzC,QAAQ;CACR,OAAO;CACP,aAAa;CACb,SAAS;CACT,QAAQ;CACR,QAAQ;CACR,QAAQ;CACR,QAAQ;CAER,OAAO;CACP,QAAQ;CACR,OAAO;CACP,QAAQ;CACR,QAAQ;CACR,SAAS;CACT,SAAS;CACT,QAAQ;CACR,OAAO;CACP,OAAO;CACP,OAAO;CACP,SAAS;CAET,QAAQ;CACR,QAAQ;CACR,SAAS;CACT,QAAQ;CACR,QAAQ;CACR,SAAS;CACT,QAAQ;CAER,QAAQ;CAER,QAAQ;CACR,OAAO;CACP,QAAQ;AACV;;;;AAKA,SAAS,YAAY,MAAsB;CACzC,MAAM,MAAM,KAAK,YAAY,CAAC,CAAC,MAAM,UAAU,CAAC,GAAG;CACnD,OAAO,MAAO,WAAW,QAAQ,6BAA8B;AACjE;;AAGA,SAASA,kBAAgB,OAAyB;CAChD,IAAI,CAAC,SAAS,OAAO,UAAU,YAAY,EAAE,UAAU,QAAQ,OAAO;CACtE,MAAM,OAAQ,MAA2B;CACzC,OAAO,SAAS,cAAc,SAAS,eAAe,SAAS;AACjE;;AAGA,SAAS,oBAAoB,OAAyB;CACpD,IAAI,CAAC,SAAS,OAAO,UAAU,UAAU,OAAO;CAChD,MAAM,MAAM;CACZ,OAAO,IAAI,SAAS,kBAAkB,IAAI,WAAW,mBAAmB;AAC1E;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AA0GA,SAASC,cAAY,GAAmB;CACtC,IAAI,QAAQ;CACZ,IAAI,MAAM,EAAE;CACZ,OAAO,QAAQ,OAAO,EAAE,WAAW,KAAK;CACxC,OAAO,MAAM,SAAS,EAAE,MAAM,OAAO,KAAK;CAC1C,OAAO,EAAE,MAAM,OAAO,GAAG;AAC3B;AAEA,IAAa,eAAb,cAAkC,iBAAiB;CACjD;CACA,OAAgB;CAChB,WAAoB;CACpB;CAEA,SAAyB;CAGzB;CACA,OAAgC;CAChC;CAEA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CAEA,UAAmC;CAEnC,YAAY,SAA8B;EACxC,MAAM;GAAE,GAAG;GAAS,MAAM;EAAe,CAAC;EAC1C,KAAK,KAAK,QAAQ,MAAM,SAAS,KAAK,IAAI,CAAC,CAAC,SAAS,EAAE,EAAE,GAAG,KAAK,OAAO,CAAC,CAAC,SAAS,EAAE,CAAC,CAAC,MAAM,GAAG,CAAC;EACjG,KAAK,SAAS,QAAQ;EACtB,KAAK,SAAS,QAAQ;EACtB,KAAK,cAAc,QAAQ;EAC3B,KAAK,cAAc,QAAQ;EAC3B,KAAK,kBAAkB,QAAQ;EAC/B,KAAK,eAAe,QAAQ;EAC5B,KAAK,WAAW,QAAQ;EACxB,KAAK,iBAAiB,QAAQ,kBAAkB,CAAC,CAAC,QAAQ;EAE1D,MAAM,gBAAgB,QAAQ,SAASA,cAAY,QAAQ,MAAM,IAAI;EACrE,KAAK,SAAS,gBAAgB,gBAAgB,MAAM;EAGpD,KAAK,OAAO,QAAQ,QAAQ,KAAK,uBAAuB,QAAQ,QAAQ;EACxE,KAAK,cAAc,QAAQ,eAAe,KAAK,sBAAsB,KAAK,IAAI;EAC9E,KAAK,cAAc,QAAQ;EAC3B,KAAK,WAAW,QAAQ;CAC1B;;;;;;;;;;;;;;;;;;;;CAqBA,IAAI,SAAmB;EACrB,OAAO,KAAK,UAAU;CACxB;;;;;;;;;;;CAYA,iBAAgC;EAC9B,MAAM,SAAwB;GAC5B,MAAM;GACN,QAAQ,KAAK;GACb,QAAQ,KAAK;GACb,UAAU,KAAK;EACjB;EAEA,IAAI,KAAK,eAAe,KAAK,iBAAiB;GAC5C,OAAO,cAAc,KAAK;GAC1B,OAAO,kBAAkB,KAAK;GAC9B,IAAI,KAAK,cACP,OAAO,eAAe,KAAK;EAE/B;EAEA,IAAI,KAAK,QACP,OAAO,SAAS,KAAK;EAGvB,IAAI,KAAK,UACP,OAAO,WAAW;EAGpB,OAAO;CACT;;;;CAKA,UAKG;EACD,OAAO;GACL,IAAI,KAAK;GACT,MAAM,KAAK;GACX,UAAU,KAAK;GACf,QAAQ,KAAK;GACb,OAAO,KAAK;GACZ,UAAU,KAAK;GACf,MAAM,KAAK;GACX,UAAU;IACR,QAAQ,KAAK;IACb,QAAQ,KAAK;IACb,GAAI,KAAK,YAAY,EAAE,UAAU,KAAK,SAAS;IAC/C,GAAI,KAAK,UAAU,EAAE,QAAQ,KAAK,OAAO;GAC3C;EACF;CACF;;;;;CAMA,YAAoB,OAAyB;EAC3C,IAAI,oBAAoB,KAAK,GAAG;GAC9B,KAAK,SAAS;GACd,KAAK,QAAQ;EACf;EACA,OAAO;CACT;;;;;CAMA,kBAA0B;EACxB,MAAM,eAAe,KAAK,eAAe;EACzC,MAAM,SAAS,KAAK,WAAW,cAAc;EAC7C,OAAO,GAAG,aAAa,sBAAsB,KAAK,OAAO,KAAK,OAAO;CACvE;;;;CAKA,uBAA+B,UAAmC;EAChE,IAAI,CAAC,UAEH,OAAO;EAIT,IAAI;EACJ,IAAI;GAEF,WAAW,IADK,IAAI,QACP,CAAC,CAAC,SAAS,YAAY;EACtC,QAAQ;GAEN,WAAW,SAAS,YAAY;EAClC;EAGA,IACE,aAAa,8BACb,SAAS,SAAS,2BAA2B,KAC7C,SAAS,SAAS,iBAAiB,GAEnC,OAAO;EAGT,IACE,aAAa,4BACb,SAAS,SAAS,yBAAyB,KAC3C,SAAS,SAAS,iBAAiB,GAEnC,OAAO;EAGT,IACE,aAAa,2BACb,SAAS,SAAS,wBAAwB,KAC1C,SAAS,SAAS,YAAY,GAE9B,OAAO;EAGT,IAAI,SAAS,SAAS,OAAO,GAC3B,OAAO;EAIT,OAAO;CACT;;;;CAKA,sBAA8B,MAA0C;EACtE,QAAQ,MAAR;GACE,KAAK,UACH,OAAO;GACT,KAAK;GACL,KAAK;GACL,KAAK,iBACH,OAAO;GACT,KAAK;GACL,KAAK;GACL,KAAK,wBACH,OAAO;GACT,KAAK;GACL,KAAK,cACH,OAAO;GACT,KAAK,SACH,OAAO;GACT,KAAK,MACH,OAAO;GACT,SAEE;EACJ;CACF;CAEA,YAA8B;EAC5B,IAAI,KAAK,SAAS,OAAO,KAAK;EAE9B,MAAM,uBAAuB,KAAK,eAAe,KAAK;EAEtD,IAAI;EACJ,IAAI,KAAK,aACP,cAAc,KAAK;OACd,IAAI,sBACT,cAAc;GACZ,aAAa,KAAK;GAClB,iBAAiB,KAAK;GACtB,GAAI,KAAK,gBAAgB,EAAE,cAAc,KAAK,aAAa;EAC7D;EAIF,KAAK,UAAU,IAAI,SAAS;GAC1B,QAAQ,KAAK;GACb,GAAI,gBAAgB,KAAA,KAAa,EAAE,YAAY;GAC/C,UAAU,KAAK;GACf,gBAAgB,KAAK;EACvB,CAAC;EAED,OAAO,KAAK;CACd;;;;;CAMA,MAAc,iBAAoC;EAChD,MAAM,KAAK,YAAY;EACvB,OAAO,KAAK,UAAU;CACxB;CAEA,MAAc,MAAsB;EAElC,MAAM,YAAY,KAAK,QAAQ,QAAQ,EAAE,CAAC,CAAC,QAAQ,eAAe,EAAE;EACpE,OAAO,KAAK,SAAS;CACvB;CAMA,MAAM,SAAS,MAAc,SAAiD;EAC5E,MAAM,SAAS,MAAM,KAAK,eAAe;EAEzC,IAAI;GAQF,MAAM,OAAO,OAAM,MAPI,OAAO,KAC5B,IAAI,iBAAiB;IACnB,QAAQ,KAAK;IACb,KAAK,KAAK,MAAM,IAAI;GACtB,CAAC,CACH,EAAA,CAE4B,MAAM,qBAAqB;GACvD,IAAI,CAAC,MAAM,MAAM,IAAI,kBAAkB,IAAI;GAE3C,MAAM,SAAS,OAAO,KAAK,IAAI;GAC/B,IAAI,SAAS,UACX,OAAO,OAAO,SAAS,QAAQ,QAAQ;GAEzC,OAAO;EACT,SAAS,OAAgB;GACvB,IAAID,kBAAgB,KAAK,GACvB,MAAM,IAAI,kBAAkB,IAAI;GAElC,MAAM,KAAK,YAAY,KAAK;EAC9B;CACF;CAEA,MAAM,UAAU,MAAc,SAAsB,SAAuC;EACzF,MAAM,SAAS,MAAM,KAAK,eAAe;EAEzC,IAAI,SAAS,cAAc,SAAU,MAAM,KAAK,OAAO,IAAI,GACzD,MAAM,IAAI,gBAAgB,IAAI;EAGhC,MAAM,OAAO,OAAO,YAAY,WAAW,OAAO,KAAK,SAAS,OAAO,IAAI,OAAO,KAAK,OAAO;EAC9F,MAAM,cAAc,YAAY,IAAI;EAEpC,MAAM,OAAO,KACX,IAAI,iBAAiB;GACnB,QAAQ,KAAK;GACb,KAAK,KAAK,MAAM,IAAI;GACpB,MAAM;GACN,aAAa;EACf,CAAC,CACH;CACF;CAEA,MAAM,WAAW,MAAc,SAAqC;EAElE,IAAI,WAAW;EACf,IAAI;GACF,WAAY,MAAM,KAAK,SAAS,MAAM,EAAE,UAAU,QAAQ,CAAC;EAC7D,SAAS,OAAO;GACd,IAAI,iBAAiB,mBAAmB,CAExC,OACE,MAAM;EAEV;EAEA,MAAM,gBAAgB,OAAO,YAAY,WAAW,UAAU,OAAO,KAAK,OAAO,CAAC,CAAC,SAAS,OAAO;EACnG,MAAM,KAAK,UAAU,MAAM,WAAW,aAAa;CACrD;CAEA,MAAM,WAAW,MAAc,SAAwC;EAGrE,IAAI,MADgB,KAAK,YAAY,IAAI,GAC9B;GACT,MAAM,KAAK,MAAM,MAAM;IAAE,WAAW;IAAM,OAAO,SAAS;GAAM,CAAC;GACjE;EACF;EAEA,MAAM,SAAS,MAAM,KAAK,eAAe;EAEzC,IAAI;GACF,MAAM,OAAO,KACX,IAAI,oBAAoB;IACtB,QAAQ,KAAK;IACb,KAAK,KAAK,MAAM,IAAI;GACtB,CAAC,CACH;EACF,SAAS,OAAgB;GACvB,IAAI,SAAS,OAAO;GACpB,IAAIA,kBAAgB,KAAK,GACvB,MAAM,IAAI,kBAAkB,IAAI;GAElC,MAAM,KAAK,YAAY,KAAK;EAC9B;CACF;CAEA,MAAM,SAAS,KAAa,MAAc,SAAsC;EAC9E,MAAM,SAAS,MAAM,KAAK,eAAe;EAEzC,IAAI,SAAS,cAAc,SAAU,MAAM,KAAK,OAAO,IAAI,GACzD,MAAM,IAAI,gBAAgB,IAAI;EAGhC,IAAI;GACF,MAAM,OAAO,KACX,IAAI,kBAAkB;IACpB,QAAQ,KAAK;IACb,YAAY,GAAG,KAAK,OAAO,GAAG,mBAAmB,KAAK,MAAM,GAAG,CAAC,CAAC,CAAC,QAAQ,QAAQ,GAAG;IACrF,KAAK,KAAK,MAAM,IAAI;GACtB,CAAC,CACH;EACF,SAAS,OAAgB;GACvB,IAAIA,kBAAgB,KAAK,GACvB,MAAM,IAAI,kBAAkB,GAAG;GAEjC,MAAM,KAAK,YAAY,KAAK;EAC9B;CACF;CAEA,MAAM,SAAS,KAAa,MAAc,SAAsC;EAC9E,MAAM,KAAK,SAAS,KAAK,MAAM,OAAO;EACtC,MAAM,KAAK,WAAW,KAAK,EAAE,OAAO,KAAK,CAAC;CAC5C;CAMA,MAAM,MAAM,OAAe,UAAmD,CAG9E;CAEA,MAAM,MAAM,MAAc,SAAwC;EAChE,IAAI,CAAC,SAAS,WAAW;GAGvB,KAAI,MADkB,KAAK,QAAQ,IAAI,EAAA,CAC3B,SAAS,GACnB,MAAM,IAAI,MAAM,wBAAwB,MAAM;GAEhD;EACF;EAGA,MAAM,SAAS,MAAM,KAAK,eAAe;EAEzC,MAAM,SAAS,KAAK,MAAM,IAAI,CAAC,CAAC,QAAQ,OAAO,EAAE,IAAI;EAErD,IAAI;EACJ,GAAG;GACD,MAAM,eAAe,MAAM,OAAO,KAChC,IAAI,qBAAqB;IACvB,QAAQ,KAAK;IACb,QAAQ;IACR,mBAAmB;GACrB,CAAC,CACH;GAEA,IAAI,aAAa,YAAY,aAAa,SAAS,SAAS,GAAG;IAC7D,MAAM,iBAAiB,MAAM,OAAO,KAClC,IAAI,qBAAqB;KACvB,QAAQ,KAAK;KACb,QAAQ,EACN,SAAS,aAAa,SAAS,QAAQ,QAAgC,CAAC,CAAC,IAAI,GAAG,CAAC,CAAC,KAAI,SAAQ,EAC5F,KAAK,IAAI,IACX,EAAE,EACJ;IACF,CAAC,CACH;IACA,IAAI,eAAe,UAAU,eAAe,OAAO,SAAS,GAC1D,MAAM,IAAI,MAAM,oBAAoB,eAAe,OAAO,OAAO,gBAAgB,MAAM;GAE3F;GAEA,oBAAoB,aAAa;EACnC,SAAS;CACX;CAEA,MAAM,QAAQ,MAAc,SAA6C;EACvE,MAAM,SAAS,MAAM,KAAK,eAAe;EAEzC,MAAM,SAAS,KAAK,MAAM,IAAI,CAAC,CAAC,QAAQ,OAAO,EAAE;EACjD,MAAM,eAAe,SAAS,SAAS,MAAM;EAE7C,MAAM,UAAuB,CAAC;EAC9B,MAAM,2BAAW,IAAI,IAAY;EAEjC,IAAI;EACJ,GAAG;GACD,MAAM,WAAW,MAAM,OAAO,KAC5B,IAAI,qBAAqB;IACvB,QAAQ,KAAK;IACb,QAAQ;IACR,WAAW,SAAS,YAAY,KAAA,IAAY;IAC5C,mBAAmB;GACrB,CAAC,CACH;GAGA,IAAI,SAAS,UACX,KAAK,MAAM,OAAO,SAAS,UAAU;IACnC,MAAM,MAAM,IAAI;IAChB,IAAI,CAAC,OAAO,QAAQ,cAAc;IAElC,MAAM,eAAe,IAAI,MAAM,aAAa,MAAM;IAClD,IAAI,CAAC,cAAc;IAGnB,IAAI,aAAa,SAAS,GAAG,GAAG;KAC9B,MAAM,UAAU,aAAa,MAAM,GAAG,EAAE;KACxC,IAAI,CAAC,SAAS,IAAI,OAAO,GAAG;MAC1B,SAAS,IAAI,OAAO;MACpB,QAAQ,KAAK;OAAE,MAAM;OAAS,MAAM;MAAY,CAAC;KACnD;KACA;IACF;IAEA,MAAM,OAAO,SAAS,YAAY,eAAe,aAAa,MAAM,GAAG,CAAC,CAAC;IAGzE,IAAI,CAAC,MAAM;IAGX,IAAI,SAAS,WAEP;SAAA,EADe,MAAM,QAAQ,QAAQ,SAAS,IAAI,QAAQ,YAAY,CAAC,QAAQ,SAAS,EAAA,CAC5E,MAAK,QAAO,KAAK,SAAS,GAAG,CAAC,GAC5C;IAAA;IAIJ,QAAQ,KAAK;KACX;KACA,MAAM;KACN,MAAM,IAAI;IACZ,CAAC;GACH;GAIF,IAAI,SAAS,gBACX,KAAK,MAAM,aAAa,SAAS,gBAAgB;IAC/C,IAAI,CAAC,UAAU,QAAQ;IACvB,MAAM,UAAU,UAAU,OAAO,MAAM,aAAa,MAAM,CAAC,CAAC,QAAQ,OAAO,EAAE;IAC7E,IAAI,WAAW,CAAC,SAAS,IAAI,OAAO,GAAG;KACrC,SAAS,IAAI,OAAO;KACpB,QAAQ,KAAK;MAAE,MAAM;MAAS,MAAM;KAAY,CAAC;IACnD;GACF;GAGF,oBAAoB,SAAS;EAC/B,SAAS;EAET,OAAO;CACT;CAMA,MAAM,OAAO,MAAgC;EAC3C,MAAM,MAAM,KAAK,MAAM,IAAI;EAC3B,IAAI,CAAC,KAAK,OAAO;EAEjB,MAAM,SAAS,MAAM,KAAK,eAAe;EAGzC,IAAI;GACF,MAAM,OAAO,KACX,IAAI,kBAAkB;IACpB,QAAQ,KAAK;IACb,KAAK;GACP,CAAC,CACH;GACA,OAAO;EACT,SAAS,OAAgB;GACvB,IAAI,CAACA,kBAAgB,KAAK,GAAG,MAAM,KAAK,YAAY,KAAK;EAE3D;EAWA,SAAQ,MARyC,OAAO,KACtD,IAAI,qBAAqB;GACvB,QAAQ,KAAK;GACb,QAAQ,IAAI,QAAQ,OAAO,EAAE,IAAI;GACjC,SAAS;EACX,CAAC,CACH,EAAA,CAEiB,UAAU,UAAU,KAAK;CAC5C;CAEA,MAAM,KAAK,MAAiC;EAC1C,MAAM,MAAM,KAAK,MAAM,IAAI;EAG3B,IAAI,CAAC,KACH,OAAO;GACL,MAAM;GACN;GACA,MAAM;GACN,MAAM;GACN,2BAAW,IAAI,KAAK;GACpB,4BAAY,IAAI,KAAK;EACvB;EAGF,MAAM,SAAS,MAAM,KAAK,eAAe;EAEzC,IAAI;GACF,MAAM,WAA4D,MAAM,OAAO,KAC7E,IAAI,kBAAkB;IACpB,QAAQ,KAAK;IACb,KAAK;GACP,CAAC,CACH;GAGA,OAAO;IACL,MAFW,KAAK,MAAM,GAAG,CAAC,CAAC,IAAI,KAAK;IAGpC;IACA,MAAM;IACN,MAAM,SAAS,iBAAiB;IAChC,WAAW,SAAS,gCAAgB,IAAI,KAAK;IAC7C,YAAY,SAAS,gCAAgB,IAAI,KAAK;GAChD;EACF,SAAS,OAAgB;GACvB,IAAI,CAACA,kBAAgB,KAAK,GAAG,MAAM,KAAK,YAAY,KAAK;GAGzD,IAAI,MADgB,KAAK,YAAY,IAAI,GAGvC,OAAO;IACL,MAFW,KAAK,MAAM,GAAG,CAAC,CAAC,OAAO,OAAO,CAAC,CAAC,IAAI,KAAK;IAGpD;IACA,MAAM;IACN,MAAM;IACN,2BAAW,IAAI,KAAK;IACpB,4BAAY,IAAI,KAAK;GACvB;GAEF,MAAM,IAAI,kBAAkB,IAAI;EAClC;CACF;CAEA,MAAM,OAAO,MAAgC;EAC3C,MAAM,MAAM,KAAK,MAAM,IAAI;EAC3B,IAAI,CAAC,KAAK,OAAO;EAEjB,MAAM,SAAS,MAAM,KAAK,eAAe;EAEzC,IAAI;GACF,MAAM,OAAO,KACX,IAAI,kBAAkB;IACpB,QAAQ,KAAK;IACb,KAAK;GACP,CAAC,CACH;GACA,OAAO;EACT,SAAS,OAAgB;GACvB,IAAI,CAACA,kBAAgB,KAAK,GAAG,MAAM,KAAK,YAAY,KAAK;GACzD,OAAO;EACT;CACF;CAEA,MAAM,YAAY,MAAgC;EAChD,MAAM,MAAM,KAAK,MAAM,IAAI;EAC3B,IAAI,CAAC,KAAK,OAAO;EAYjB,SAAQ,OARyC,MAF5B,KAAK,eAAe,EAAA,CAEe,KACtD,IAAI,qBAAqB;GACvB,QAAQ,KAAK;GACb,QAAQ,IAAI,QAAQ,OAAO,EAAE,IAAI;GACjC,SAAS;EACX,CAAC,CACH,EAAA,CAEiB,UAAU,UAAU,KAAK;CAC5C;;;;;CAUA,MAAM,OAAsB;EAE1B,MAAM,SAAS,KAAK,UAAU;EAC9B,IAAI;GACF,MAAM,OAAO,KAAK,IAAI,kBAAkB,EAAE,QAAQ,KAAK,OAAO,CAAC,CAAC;EAClE,SAAS,OAAO;GAEd,MAAM,aAAc,MAAsD,WAAW;GAGrF,MAAM,eAAe,YAAoB;IACvC,MAAM,MAAM,IAAI,MAAM,OAAO;IAC7B,IAAI,YAAY,IAAI,SAAS;IAC7B,OAAO;GACT;GAGA,IAAI,oBAAoB,KAAK,GAC3B,MAAM,YAAY,4BAA4B,KAAK,OAAO,sCAAsC;GAElG,IAAIA,kBAAgB,KAAK,GACvB,MAAM,YAAY,WAAW,KAAK,OAAO,YAAY;GAEvD,MAAM,UAAU,iBAAiB,QAAQ,MAAM,UAAU,OAAO,KAAK;GACrE,IAAI,YACF,MAAM,YAAY,4BAA4B,KAAK,OAAO,UAAU,WAAW,KAAK,SAAS;GAE/F,MAAM;EACR;CACF;;;;;CAMA,MAAM,UAAyB;EAC7B,KAAK,UAAU;CACjB;AACF;;;;ACj3BA,SAAS,YAAY,GAAmB;CACtC,IAAI,QAAQ;CACZ,IAAI,MAAM,EAAE;CACZ,OAAO,QAAQ,OAAO,EAAE,WAAW,KAAK;CACxC,OAAO,MAAM,SAAS,EAAE,MAAM,OAAO,KAAK;CAC1C,OAAO,EAAE,MAAM,OAAO,GAAG;AAC3B;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAsCA,IAAa,cAAb,cAAiC,UAAU;CACzC;CACA;CACA,UAAmC;CAEnC;CACA;CACA;CACA;CACA;CACA;CACA;CAEA,YAAY,SAA6B;EACvC,MAAM;EACN,KAAK,SAAS,QAAQ;EACtB,KAAK,SAAS,QAAQ;EACtB,KAAK,cAAc,QAAQ;EAC3B,KAAK,cAAc,QAAQ;EAC3B,KAAK,kBAAkB,QAAQ;EAC/B,KAAK,eAAe,QAAQ;EAC5B,KAAK,WAAW,QAAQ;EACxB,KAAK,iBAAiB,QAAQ,kBAAkB,CAAC,CAAC,QAAQ;EAC1D,KAAK,SAAS,QAAQ,SAAS,YAAY,QAAQ,MAAM,IAAI,MAAM;CACrE;CAEA,YAA8B;EAC5B,IAAI,KAAK,SAAS,OAAO,KAAK;EAE9B,MAAM,uBAAuB,KAAK,eAAe,KAAK;EAEtD,IAAI;EACJ,IAAI,KAAK,aACP,cAAc,KAAK;OACd,IAAI,sBACT,cAAc;GACZ,aAAa,KAAK;GAClB,iBAAiB,KAAK;GACtB,GAAI,KAAK,gBAAgB,EAAE,cAAc,KAAK,aAAa;EAC7D;EAIF,KAAK,UAAU,IAAI,SAAS;GAC1B,QAAQ,KAAK;GACb,GAAI,gBAAgB,KAAA,KAAa,EAAE,YAAY;GAC/C,UAAU,KAAK;GACf,gBAAgB,KAAK;EACvB,CAAC;EACD,OAAO,KAAK;CACd;CAEA,MAAc,MAAsB;EAClC,OAAO,KAAK,SAAS;CACvB;CAEA,MAAM,OAAsB,CAE5B;CAEA,MAAM,IAAI,OAAwC;EAChD,MAAM,SAAS,KAAK,UAAU;EAC9B,MAAM,MAAM,MAAM,6BAAa,IAAI,KAAK;EAExC,MAAM,OAAO,KACX,IAAI,iBAAiB;GACnB,QAAQ,KAAK;GACb,KAAK,KAAK,MAAM,MAAM,IAAI;GAC1B,MAAM,MAAM;GACZ,aAAa,MAAM,YAAY;GAC/B,UAAU;IACR,MAAM,OAAO,MAAM,IAAI;IACvB,WAAW,IAAI,YAAY;IAC3B,GAAI,MAAM,WAAW,EAAE,UAAU,MAAM,SAAS,IAAI,CAAC;GACvD;EACF,CAAC,CACH;CACF;CAEA,MAAM,IAAI,MAAgD;EACxD,MAAM,SAAS,KAAK,UAAU;EAE9B,IAAI;GACF,MAAM,WAAW,MAAM,OAAO,KAC5B,IAAI,iBAAiB;IACnB,QAAQ,KAAK;IACb,KAAK,KAAK,MAAM,IAAI;GACtB,CAAC,CACH;GAEA,MAAM,OAAO,MAAM,SAAS,MAAM,kBAAkB,OAAO;GAC3D,IAAI,SAAS,KAAA,KAAa,SAAS,MAAM,OAAO;GAEhD,MAAM,WAAW,SAAS,YAAY,CAAC;GACvC,OAAO;IACL;IACA,SAAS;IACT,MAAM,SAAS,QAAQ,OAAO,OAAO,SAAS,IAAI,IAAI,OAAO,WAAW,MAAM,OAAO;IACrF,UAAU,SAAS,YAAY,SAAS,eAAe,KAAA;IACvD,WAAW,SAAS,YAAY,IAAI,KAAK,SAAS,SAAS,oBAAI,IAAI,KAAK;GAC1E;EACF,SAAS,OAAgB;GACvB,IAAI,gBAAgB,KAAK,GAAG,OAAO;GACnC,MAAM;EACR;CACF;CAEA,MAAM,IAAI,MAAgC;EACxC,MAAM,SAAS,KAAK,UAAU;EAE9B,IAAI;GACF,MAAM,OAAO,KACX,IAAI,kBAAkB;IACpB,QAAQ,KAAK;IACb,KAAK,KAAK,MAAM,IAAI;GACtB,CAAC,CACH;GACA,OAAO;EACT,SAAS,OAAgB;GACvB,IAAI,gBAAgB,KAAK,GAAG,OAAO;GACnC,MAAM;EACR;CACF;CAEA,MAAM,OAAO,MAAgC;EAK3C,IAAI,CAAC,MADiB,KAAK,IAAI,IAAI,GACrB,OAAO;EAGrB,MADe,KAAK,UACT,CAAC,CAAC,KACX,IAAI,oBAAoB;GACtB,QAAQ,KAAK;GACb,KAAK,KAAK,MAAM,IAAI;EACtB,CAAC,CACH;EACA,OAAO;CACT;CAEA,MAAM,QAAQ,SAA4C;EACxD,IAAI,QAAQ,WAAW,GAAG;EAG1B,MAAM,QAAQ,IAAI,QAAQ,KAAI,UAAS,KAAK,IAAI,KAAK,CAAC,CAAC;CACzD;CAEA,MAAM,QAAQ,QAA0D;EACtE,MAAM,yBAAS,IAAI,IAA8B;EACjD,IAAI,OAAO,WAAW,GAAG,OAAO;EAGhC,MAAM,UAAU,MAAM,QAAQ,IAAI,OAAO,KAAI,SAAQ,KAAK,IAAI,IAAI,CAAC,CAAC;EACpE,KAAK,MAAM,SAAS,SAClB,IAAI,OACF,OAAO,IAAI,MAAM,MAAM,KAAK;EAGhC,OAAO;CACT;CAEA,MAAM,sBAAqC;EACzC,MAAM,SAAS,KAAK,UAAU;EAE9B,IAAI;EACJ,GAAG;GACD,MAAM,eAAe,MAAM,OAAO,KAChC,IAAI,qBAAqB;IACvB,QAAQ,KAAK;IACb,QAAQ,KAAK;IACb,mBAAmB;GACrB,CAAC,CACH;GAEA,MAAM,UAAU,aAAa;GAC7B,IAAI,WAAW,QAAQ,SAAS,GAC9B,MAAM,OAAO,KACX,IAAI,qBAAqB;IACvB,QAAQ,KAAK;IACb,QAAQ;KACN,SAAS,QAAQ,QAAO,QAAO,IAAI,OAAO,IAAI,CAAC,CAAC,KAAI,SAAQ,EAAE,KAAK,IAAI,IAAK,EAAE;KAC9E,OAAO;IACT;GACF,CAAC,CACH;GAGF,oBAAoB,aAAa,cAAc,aAAa,wBAAwB,KAAA;EACtF,SAAS;CACX;AACF;AAEA,SAAS,gBAAgB,OAAyB;CAChD,IAAI,CAAC,SAAS,OAAO,UAAU,YAAY,EAAE,UAAU,QAAQ,OAAO;CACtE,MAAM,OAAQ,MAA2B;CACzC,OAAO,SAAS,cAAc,SAAS,eAAe,SAAS;AACjE;;;ACxRA,MAAa,uBAAgE;CAC3E,IAAI;CACJ,MAAM;CACN,aAAa;CACb,cAAc;EACZ,MAAM;EACN,UAAU,CAAC,UAAU,QAAQ;EAC7B,YAAY;GACV,QAAQ;IAAE,MAAM;IAAU,aAAa;GAAiB;GACxD,QAAQ;IAAE,MAAM;IAAU,aAAa;GAAiC;GACxE,aAAa;IAAE,MAAM;IAAU,aAAa;GAAoB;GAChE,iBAAiB;IAAE,MAAM;IAAU,aAAa;GAAwB;GACxE,cAAc;IAAE,MAAM;IAAU,aAAa;GAA8C;GAC3F,UAAU;IAAE,MAAM;IAAU,aAAa;GAAgD;GACzF,gBAAgB;IAAE,MAAM;IAAW,aAAa;IAAyB,SAAS;GAAM;GACxF,QAAQ;IAAE,MAAM;IAAU,aAAa;GAAwC;GAC/E,UAAU;IAAE,MAAM;IAAW,aAAa;IAAsB,SAAS;GAAM;EACjF;CACF;CACA,mBAAkB,WAAU,IAAI,aAAa,MAAM;AACrD;;;;;;;;;;;;;AAcA,MAAa,sBAA6D;CACxE,IAAI;CACJ,MAAM;CACN,aAAa;CACb,cAAc;EACZ,MAAM;EACN,UAAU;GAAC;GAAU;GAAU;GAAe;EAAiB;EAC/D,YAAY;GACV,QAAQ;IAAE,MAAM;IAAU,aAAa;GAAiB;GACxD,QAAQ;IAAE,MAAM;IAAU,aAAa;GAAiC;GACxE,aAAa;IAAE,MAAM;IAAU,aAAa;GAAoB;GAChE,iBAAiB;IAAE,MAAM;IAAU,aAAa;GAAwB;GACxE,cAAc;IAAE,MAAM;IAAU,aAAa;GAA8C;GAC3F,UAAU;IAAE,MAAM;IAAU,aAAa;GAAgD;GACzF,gBAAgB;IAAE,MAAM;IAAW,aAAa;IAAyB,SAAS;GAAM;GACxF,QAAQ;IAAE,MAAM;IAAU,aAAa;GAA6D;EACtG;CACF;CACA,kBAAiB,WAAU,IAAI,YAAY,MAAM;AACnD"}
|
|
1
|
+
{"version":3,"file":"index.js","names":["isNotFoundError","trimSlashes"],"sources":["../src/filesystem/index.ts","../src/blob-store/index.ts","../src/provider.ts"],"sourcesContent":["import {\n S3Client,\n GetObjectCommand,\n PutObjectCommand,\n DeleteObjectCommand,\n CopyObjectCommand,\n ListObjectsV2Command,\n DeleteObjectsCommand,\n HeadObjectCommand,\n HeadBucketCommand,\n} from '@aws-sdk/client-s3';\n\nimport type { AwsCredentialIdentity, AwsCredentialIdentityProvider } from '@aws-sdk/types';\n\nimport type {\n FileContent,\n FileStat,\n FileEntry,\n ReadOptions,\n WriteOptions,\n ListOptions,\n RemoveOptions,\n CopyOptions,\n FilesystemMountConfig,\n FilesystemIcon,\n FilesystemInfo,\n ProviderStatus,\n MastraFilesystemOptions,\n} from '@mastra/core/workspace';\nimport { MastraFilesystem, FileNotFoundError, FileExistsError } from '@mastra/core/workspace';\n\n/**\n * S3 mount configuration.\n * Returned by S3Filesystem.getMountConfig() for FUSE mounting in sandboxes.\n */\nexport interface S3MountConfig extends FilesystemMountConfig {\n type: 's3';\n /** S3 bucket name */\n bucket: string;\n /** AWS region (use 'auto' for R2) */\n region?: string;\n /** Optional endpoint for S3-compatible storage (MinIO, R2, etc.) */\n endpoint?: string;\n /** AWS access key ID */\n accessKeyId?: string;\n /** AWS secret access key */\n secretAccessKey?: string;\n /** AWS session token for temporary credentials (SSO, AssumeRole, container credentials, etc.) */\n sessionToken?: string;\n /**\n * Optional prefix (subdirectory) to mount instead of the entire bucket.\n * Uses s3fs `bucket:/prefix` syntax to scope the mount to a specific path.\n * Leading/trailing slashes are normalized automatically.\n */\n prefix?: string;\n /** Mount as read-only */\n readOnly?: boolean;\n}\n\n/**\n * Common MIME types by file extension.\n */\nconst MIME_TYPES: Record<string, string> = {\n // Text\n '.txt': 'text/plain',\n '.md': 'text/markdown',\n '.markdown': 'text/markdown',\n '.html': 'text/html',\n '.htm': 'text/html',\n '.css': 'text/css',\n '.csv': 'text/csv',\n '.xml': 'text/xml',\n // Code\n '.js': 'text/javascript',\n '.mjs': 'text/javascript',\n '.ts': 'text/typescript',\n '.tsx': 'text/typescript',\n '.jsx': 'text/javascript',\n '.json': 'application/json',\n '.yaml': 'text/yaml',\n '.yml': 'text/yaml',\n '.py': 'text/x-python',\n '.rb': 'text/x-ruby',\n '.sh': 'text/x-shellscript',\n '.bash': 'text/x-shellscript',\n // Images\n '.png': 'image/png',\n '.jpg': 'image/jpeg',\n '.jpeg': 'image/jpeg',\n '.gif': 'image/gif',\n '.svg': 'image/svg+xml',\n '.webp': 'image/webp',\n '.ico': 'image/x-icon',\n // Documents\n '.pdf': 'application/pdf',\n // Archives\n '.zip': 'application/zip',\n '.gz': 'application/gzip',\n '.tar': 'application/x-tar',\n};\n\n/**\n * Get MIME type from file path extension.\n */\nfunction getMimeType(path: string): string {\n const ext = path.toLowerCase().match(/\\.[^.]+$/)?.[0];\n return ext ? (MIME_TYPES[ext] ?? 'application/octet-stream') : 'application/octet-stream';\n}\n\n/** Check if an error is a \"not found\" error from the S3 SDK. */\nfunction isNotFoundError(error: unknown): boolean {\n if (!error || typeof error !== 'object' || !('name' in error)) return false;\n const name = (error as { name: string }).name;\n return name === 'NotFound' || name === 'NoSuchKey' || name === '404';\n}\n\n/** Check if an error is an access denied error from the S3 SDK. */\nfunction isAccessDeniedError(error: unknown): boolean {\n if (!error || typeof error !== 'object') return false;\n const err = error as { name?: string; $metadata?: { httpStatusCode?: number } };\n return err.name === 'AccessDenied' || err.$metadata?.httpStatusCode === 403;\n}\n\n/**\n * S3 filesystem provider configuration.\n */\nexport interface S3FilesystemOptions extends MastraFilesystemOptions {\n /** Unique identifier for this filesystem instance */\n id?: string;\n /** S3 bucket name */\n bucket: string;\n /** Human-friendly display name for the UI */\n displayName?: string;\n /** Icon identifier for the UI (defaults to 's3') */\n icon?: FilesystemIcon;\n /** Description shown in tooltips */\n description?: string;\n /** AWS region (use 'auto' for R2) */\n region: string;\n /**\n * AWS credentials or credential provider function.\n * Accepts static credentials or a provider that auto-refreshes\n * (e.g. fromNodeProviderChain() from @aws-sdk/credential-providers).\n * When set, takes precedence over accessKeyId/secretAccessKey/sessionToken.\n * When ALL credential options are omitted, the SDK default credential\n * provider chain is used (env vars, ~/.aws, IMDS, ECS container credentials).\n */\n credentials?: AwsCredentialIdentity | AwsCredentialIdentityProvider;\n /**\n * AWS access key ID.\n * Optional - omit to use the SDK default credential provider chain.\n */\n accessKeyId?: string;\n /**\n * AWS secret access key.\n * Optional - omit to use the SDK default credential provider chain.\n */\n secretAccessKey?: string;\n /**\n * AWS session token for temporary credentials.\n * Required when using SSO, AssumeRole, container credentials, or any other\n * temporary credential provider.\n */\n sessionToken?: string;\n /**\n * Custom endpoint URL for S3-compatible storage.\n * Examples:\n * - Cloudflare R2: 'https://{accountId}.r2.cloudflarestorage.com'\n * - MinIO: 'http://localhost:9000'\n * - DigitalOcean Spaces: 'https://{region}.digitaloceanspaces.com'\n */\n endpoint?: string;\n /** Force path-style URLs (required for some S3-compatible services) */\n forcePathStyle?: boolean;\n /** Optional prefix for all keys (acts like a subdirectory) */\n prefix?: string;\n /** Mount as read-only (blocks write operations, mounts read-only in sandboxes) */\n readOnly?: boolean;\n}\n\n/**\n * S3 filesystem implementation.\n *\n * Stores files in an S3 bucket or S3-compatible storage service.\n * Supports mounting into E2B sandboxes via s3fs-fuse.\n *\n * @example AWS S3\n * ```typescript\n * import { S3Filesystem } from '@mastra/s3';\n *\n * const fs = new S3Filesystem({\n * bucket: 'my-bucket',\n * region: 'us-east-1',\n * accessKeyId: process.env.AWS_ACCESS_KEY_ID!,\n * secretAccessKey: process.env.AWS_SECRET_ACCESS_KEY!,\n * });\n * ```\n *\n * @example Cloudflare R2\n * ```typescript\n * import { S3Filesystem } from '@mastra/s3';\n *\n * const fs = new S3Filesystem({\n * bucket: 'my-bucket',\n * region: 'auto',\n * accessKeyId: process.env.R2_ACCESS_KEY_ID!,\n * secretAccessKey: process.env.R2_SECRET_ACCESS_KEY!,\n * endpoint: `https://${process.env.R2_ACCOUNT_ID}.r2.cloudflarestorage.com`,\n * });\n * ```\n *\n * @example MinIO (local)\n * ```typescript\n * import { S3Filesystem } from '@mastra/s3';\n *\n * const fs = new S3Filesystem({\n * bucket: 'my-bucket',\n * region: 'us-east-1',\n * accessKeyId: 'minioadmin',\n * secretAccessKey: 'minioadmin',\n * endpoint: 'http://localhost:9000',\n * forcePathStyle: true,\n * });\n * ```\n */\n\n/** Trim leading and trailing slashes without regex (avoids polynomial regex on user input). */\nfunction trimSlashes(s: string): string {\n let start = 0;\n let end = s.length;\n while (start < end && s[start] === '/') start++;\n while (end > start && s[end - 1] === '/') end--;\n return s.slice(start, end);\n}\n\nexport class S3Filesystem extends MastraFilesystem {\n readonly id: string;\n readonly name = 'S3Filesystem';\n readonly provider = 's3';\n readonly readOnly?: boolean;\n\n status: ProviderStatus = 'pending';\n\n // Display metadata for UI\n readonly displayName?: string;\n readonly icon: FilesystemIcon = 's3';\n readonly description?: string;\n\n private readonly bucket: string;\n private readonly region: string;\n private readonly credentials?: AwsCredentialIdentity | AwsCredentialIdentityProvider;\n private readonly accessKeyId?: string;\n private readonly secretAccessKey?: string;\n private readonly sessionToken?: string;\n private readonly endpoint?: string;\n private readonly forcePathStyle: boolean;\n private readonly prefix: string;\n\n private _client: S3Client | null = null;\n\n constructor(options: S3FilesystemOptions) {\n super({ ...options, name: 'S3Filesystem' });\n this.id = options.id ?? `s3-fs-${Date.now().toString(36)}-${Math.random().toString(36).slice(2, 8)}`;\n this.bucket = options.bucket;\n this.region = options.region;\n this.credentials = options.credentials;\n this.accessKeyId = options.accessKeyId;\n this.secretAccessKey = options.secretAccessKey;\n this.sessionToken = options.sessionToken;\n this.endpoint = options.endpoint;\n this.forcePathStyle = options.forcePathStyle ?? !!options.endpoint; // Default true for custom endpoints\n // Trim leading/trailing slashes from prefix using iterative approach (avoids polynomial regex)\n const trimmedPrefix = options.prefix ? trimSlashes(options.prefix) : '';\n this.prefix = trimmedPrefix ? trimmedPrefix + '/' : '';\n\n // Display metadata - detect icon first, then derive displayName from it\n this.icon = options.icon ?? this.detectIconFromEndpoint(options.endpoint);\n this.displayName = options.displayName ?? this.getDefaultDisplayName(this.icon);\n this.description = options.description;\n this.readOnly = options.readOnly;\n }\n\n /**\n * Get the underlying S3Client instance for direct access to AWS S3 APIs.\n *\n * Use this when you need to access S3 features not exposed through the\n * WorkspaceFilesystem interface (e.g., presigned URLs, multipart uploads,\n * custom S3 operations, etc.).\n *\n * @example Generate a presigned URL\n * ```typescript\n * import { GetObjectCommand } from '@aws-sdk/client-s3';\n * import { getSignedUrl } from '@aws-sdk/s3-request-presigner';\n *\n * const s3Client = fs.client;\n * const url = await getSignedUrl(s3Client, new GetObjectCommand({\n * Bucket: 'my-bucket',\n * Key: 'my-file.txt',\n * }));\n * ```\n */\n get client(): S3Client {\n return this.getClient();\n }\n\n /**\n * Get mount configuration for E2B sandbox.\n * Returns S3-compatible config that works with s3fs-fuse.\n *\n * Only static `accessKeyId`/`secretAccessKey`/`sessionToken` are included in the\n * returned config. If credentials are provided only via the `credentials` option\n * (provider function), the returned config will have no credentials because FUSE\n * mounts cannot call a provider function. Use static credentials for sandbox\n * mount compatibility.\n */\n getMountConfig(): S3MountConfig {\n const config: S3MountConfig = {\n type: 's3',\n bucket: this.bucket,\n region: this.region,\n endpoint: this.endpoint,\n };\n\n if (this.accessKeyId && this.secretAccessKey) {\n config.accessKeyId = this.accessKeyId;\n config.secretAccessKey = this.secretAccessKey;\n if (this.sessionToken) {\n config.sessionToken = this.sessionToken;\n }\n }\n\n if (this.prefix) {\n config.prefix = this.prefix;\n }\n\n if (this.readOnly) {\n config.readOnly = true;\n }\n\n return config;\n }\n\n /**\n * Get filesystem info for status reporting.\n */\n getInfo(): FilesystemInfo<{\n bucket: string;\n region: string;\n endpoint?: string;\n prefix?: string;\n }> {\n return {\n id: this.id,\n name: this.name,\n provider: this.provider,\n status: this.status,\n error: this.error,\n readOnly: this.readOnly,\n icon: this.icon,\n metadata: {\n bucket: this.bucket,\n region: this.region,\n ...(this.endpoint && { endpoint: this.endpoint }),\n ...(this.prefix && { prefix: this.prefix }),\n },\n };\n }\n\n /**\n * Handle an error, checking for access denied and updating status accordingly.\n * Returns the error for re-throwing.\n */\n private handleError(error: unknown): unknown {\n if (isAccessDeniedError(error)) {\n this.status = 'error';\n this.error = 'Access denied - check credentials and bucket permissions';\n }\n return error;\n }\n\n /**\n * Get instructions describing this S3 filesystem.\n * Used by agents to understand storage semantics.\n */\n getInstructions(): string {\n const providerName = this.displayName || 'S3';\n const access = this.readOnly ? 'Read-only' : 'Persistent';\n return `${providerName} storage in bucket \"${this.bucket}\". ${access} storage - files are retained across sessions.`;\n }\n\n /**\n * Detect the appropriate icon based on the S3 endpoint.\n */\n private detectIconFromEndpoint(endpoint?: string): FilesystemIcon {\n if (!endpoint) {\n // No custom endpoint = AWS S3\n return 'aws-s3';\n }\n\n // Parse hostname from endpoint URL for secure matching\n let hostname: string;\n try {\n const url = new URL(endpoint);\n hostname = url.hostname.toLowerCase();\n } catch {\n // If URL parsing fails, use the endpoint as-is (lowercased)\n hostname = endpoint.toLowerCase();\n }\n\n // Check hostname suffix for known providers (use dot-prefix or exact match to prevent subdomain spoofing)\n if (\n hostname === 'r2.cloudflarestorage.com' ||\n hostname.endsWith('.r2.cloudflarestorage.com') ||\n hostname.endsWith('.cloudflare.com')\n ) {\n return 'r2';\n }\n\n if (\n hostname === 'storage.googleapis.com' ||\n hostname.endsWith('.storage.googleapis.com') ||\n hostname.endsWith('.googleapis.com')\n ) {\n return 'gcs';\n }\n\n if (\n hostname === 'blob.core.windows.net' ||\n hostname.endsWith('.blob.core.windows.net') ||\n hostname.endsWith('.azure.com')\n ) {\n return 'azure';\n }\n\n if (hostname.includes('minio')) {\n return 'minio';\n }\n\n // Generic S3-compatible (DigitalOcean Spaces, etc.)\n return 's3';\n }\n\n /**\n * Get a user-friendly display name based on the icon/provider.\n */\n private getDefaultDisplayName(icon: FilesystemIcon): string | undefined {\n switch (icon) {\n case 'aws-s3':\n return 'AWS S3';\n case 'r2':\n case 'cloudflare':\n case 'cloudflare-r2':\n return 'Cloudflare R2';\n case 'gcs':\n case 'google-cloud':\n case 'google-cloud-storage':\n return 'Google Cloud Storage';\n case 'azure':\n case 'azure-blob':\n return 'Azure Blob';\n case 'minio':\n return 'MinIO';\n case 's3':\n return 'S3';\n default:\n // Unknown icon - don't assume a display name\n return undefined;\n }\n }\n\n private getClient(): S3Client {\n if (this._client) return this._client;\n\n const hasStaticCredentials = this.accessKeyId && this.secretAccessKey;\n\n let credentials: AwsCredentialIdentity | AwsCredentialIdentityProvider | undefined;\n if (this.credentials) {\n credentials = this.credentials;\n } else if (hasStaticCredentials) {\n credentials = {\n accessKeyId: this.accessKeyId!,\n secretAccessKey: this.secretAccessKey!,\n ...(this.sessionToken && { sessionToken: this.sessionToken }),\n };\n }\n // When credentials is undefined, SDK uses its default provider chain\n\n this._client = new S3Client({\n region: this.region,\n ...(credentials !== undefined && { credentials }),\n endpoint: this.endpoint,\n forcePathStyle: this.forcePathStyle,\n });\n\n return this._client;\n }\n\n /**\n * Ensure the filesystem is initialized and return the S3 client.\n * Uses base class ensureReady() for status management, then returns client.\n */\n private async getReadyClient(): Promise<S3Client> {\n await this.ensureReady();\n return this.getClient();\n }\n\n private toKey(path: string): string {\n // Remove leading slashes, then resolve \".\" and \"./\" to empty string (root)\n const cleanPath = path.replace(/^\\/+/, '').replace(/^\\.(?:\\/|$)/, '');\n return this.prefix + cleanPath;\n }\n\n // ---------------------------------------------------------------------------\n // File Operations\n // ---------------------------------------------------------------------------\n\n async readFile(path: string, options?: ReadOptions): Promise<string | Buffer> {\n // A trailing slash means a directory: never return the empty body of a directory marker\n if (path.endsWith('/')) {\n throw new FileNotFoundError(path);\n }\n\n const client = await this.getReadyClient();\n\n try {\n const response = await client.send(\n new GetObjectCommand({\n Bucket: this.bucket,\n Key: this.toKey(path),\n }),\n );\n\n const body = await response.Body?.transformToByteArray();\n if (!body) throw new FileNotFoundError(path);\n\n const buffer = Buffer.from(body);\n if (options?.encoding) {\n return buffer.toString(options.encoding);\n }\n return buffer;\n } catch (error: unknown) {\n if (isNotFoundError(error)) {\n throw new FileNotFoundError(path);\n }\n throw this.handleError(error);\n }\n }\n\n async writeFile(path: string, content: FileContent, options?: WriteOptions): Promise<void> {\n const client = await this.getReadyClient();\n\n if (options?.overwrite === false && (await this.exists(path))) {\n throw new FileExistsError(path);\n }\n\n const body = typeof content === 'string' ? Buffer.from(content, 'utf-8') : Buffer.from(content);\n const contentType = getMimeType(path);\n\n await client.send(\n new PutObjectCommand({\n Bucket: this.bucket,\n Key: this.toKey(path),\n Body: body,\n ContentType: contentType,\n }),\n );\n }\n\n async appendFile(path: string, content: FileContent): Promise<void> {\n // S3 doesn't support append, so read + write\n let existing = '';\n try {\n existing = (await this.readFile(path, { encoding: 'utf-8' })) as string;\n } catch (error) {\n if (error instanceof FileNotFoundError) {\n // File doesn't exist, start fresh\n } else {\n throw error;\n }\n }\n\n const appendContent = typeof content === 'string' ? content : Buffer.from(content).toString('utf-8');\n await this.writeFile(path, existing + appendContent);\n }\n\n async deleteFile(path: string, options?: RemoveOptions): Promise<void> {\n // Check if this is a directory - if so, use rmdir instead\n const isDir = await this.isDirectory(path);\n if (isDir) {\n await this.rmdir(path, { recursive: true, force: options?.force });\n return;\n }\n\n const client = await this.getReadyClient();\n\n try {\n await client.send(\n new DeleteObjectCommand({\n Bucket: this.bucket,\n Key: this.toKey(path),\n }),\n );\n } catch (error: unknown) {\n if (options?.force) return;\n if (isNotFoundError(error)) {\n throw new FileNotFoundError(path);\n }\n throw this.handleError(error);\n }\n }\n\n async copyFile(src: string, dest: string, options?: CopyOptions): Promise<void> {\n // A trailing slash means a directory: never copy a directory marker as a file\n if (src.endsWith('/')) {\n throw new FileNotFoundError(src);\n }\n\n const client = await this.getReadyClient();\n\n if (options?.overwrite === false && (await this.exists(dest))) {\n throw new FileExistsError(dest);\n }\n\n try {\n await client.send(\n new CopyObjectCommand({\n Bucket: this.bucket,\n CopySource: `${this.bucket}/${encodeURIComponent(this.toKey(src)).replace(/%2F/g, '/')}`,\n Key: this.toKey(dest),\n }),\n );\n } catch (error: unknown) {\n if (isNotFoundError(error)) {\n throw new FileNotFoundError(src);\n }\n throw this.handleError(error);\n }\n }\n\n async moveFile(src: string, dest: string, options?: CopyOptions): Promise<void> {\n await this.copyFile(src, dest, options);\n await this.deleteFile(src, { force: true });\n }\n\n // ---------------------------------------------------------------------------\n // Directory Operations\n // ---------------------------------------------------------------------------\n\n async mkdir(path: string, _options?: { recursive?: boolean }): Promise<void> {\n // S3 doesn't have real directories - they're just key prefixes.\n // Write a zero-byte marker object at \"<key>/\" (the convention the S3 console uses)\n // so an empty directory is visible to readdir(), exists() and stat().\n // Parent directories need no marker of their own: the marker key already\n // matches every parent prefix, so nested paths work without `recursive`.\n const key = trimSlashes(this.toKey(path));\n if (!key || key + '/' === this.prefix) return; // Root always exists\n\n const client = await this.getReadyClient();\n\n // A file already occupies this path. A credential that can write but not read\n // cannot probe, so treat a failed probe as \"no file there\" and let the write below\n // report a real problem.\n try {\n await client.send(new HeadObjectCommand({ Bucket: this.bucket, Key: key }));\n throw new FileExistsError(path);\n } catch (error: unknown) {\n if (error instanceof FileExistsError) throw error;\n }\n\n await client.send(\n new PutObjectCommand({\n Bucket: this.bucket,\n Key: key + '/',\n Body: new Uint8Array(0),\n }),\n );\n }\n\n async rmdir(path: string, options?: RemoveOptions): Promise<void> {\n if (!options?.recursive) {\n // Check if directory is empty (the directory marker is not listed as an entry)\n const entries = await this.readdir(path);\n if (entries.length > 0) {\n throw new Error(`Directory not empty: ${path}`);\n }\n // Remove the directory marker so the empty directory disappears\n const key = trimSlashes(this.toKey(path));\n if (key && key + '/' !== this.prefix) {\n const client = await this.getReadyClient();\n await client.send(\n new DeleteObjectCommand({\n Bucket: this.bucket,\n Key: key + '/',\n }),\n );\n }\n return;\n }\n\n // Delete all objects with this prefix\n const client = await this.getReadyClient();\n\n const prefix = this.toKey(path).replace(/\\/$/, '') + '/';\n\n let continuationToken: string | undefined;\n do {\n const listResponse = await client.send(\n new ListObjectsV2Command({\n Bucket: this.bucket,\n Prefix: prefix,\n ContinuationToken: continuationToken,\n }),\n );\n\n if (listResponse.Contents && listResponse.Contents.length > 0) {\n const deleteResponse = await client.send(\n new DeleteObjectsCommand({\n Bucket: this.bucket,\n Delete: {\n Objects: listResponse.Contents.filter((obj): obj is { Key: string } => !!obj.Key).map(obj => ({\n Key: obj.Key,\n })),\n },\n }),\n );\n if (deleteResponse.Errors && deleteResponse.Errors.length > 0) {\n throw new Error(`Failed to delete ${deleteResponse.Errors.length} object(s) in ${path}`);\n }\n }\n\n continuationToken = listResponse.NextContinuationToken;\n } while (continuationToken);\n }\n\n async readdir(path: string, options?: ListOptions): Promise<FileEntry[]> {\n const client = await this.getReadyClient();\n\n const prefix = this.toKey(path).replace(/\\/$/, '');\n const searchPrefix = prefix ? prefix + '/' : '';\n\n const entries: FileEntry[] = [];\n const seenDirs = new Set<string>();\n\n let continuationToken: string | undefined;\n do {\n const response = await client.send(\n new ListObjectsV2Command({\n Bucket: this.bucket,\n Prefix: searchPrefix,\n Delimiter: options?.recursive ? undefined : '/',\n ContinuationToken: continuationToken,\n }),\n );\n\n // Add files\n if (response.Contents) {\n for (const obj of response.Contents) {\n const key = obj.Key;\n if (!key || key === searchPrefix) continue;\n\n const relativePath = key.slice(searchPrefix.length);\n if (!relativePath) continue;\n\n // Skip if this looks like a directory marker\n if (relativePath.endsWith('/')) {\n // A directory is not a file, so an extension filter must exclude it. Only a\n // recursive listing reaches this branch for a nested marker.\n if (options?.recursive && options.extension) continue;\n // A nested marker such as \"a/b/\" belongs to the directory \"a\" in a\n // non-recursive listing; only a recursive listing reports the full path.\n const dirName = options?.recursive ? relativePath.slice(0, -1) : relativePath.split('/')[0]!;\n if (!dirName) continue;\n if (!seenDirs.has(dirName)) {\n seenDirs.add(dirName);\n entries.push({ name: dirName, type: 'directory' });\n }\n continue;\n }\n\n const name = options?.recursive ? relativePath : relativePath.split('/')[0];\n\n // Skip if name is undefined or empty\n if (!name) continue;\n\n // Filter by extension if specified\n if (options?.extension) {\n const extensions = Array.isArray(options.extension) ? options.extension : [options.extension];\n if (!extensions.some(ext => name.endsWith(ext))) {\n continue;\n }\n }\n\n entries.push({\n name,\n type: 'file',\n size: obj.Size,\n });\n }\n }\n\n // Add directories (common prefixes)\n if (response.CommonPrefixes) {\n for (const prefixObj of response.CommonPrefixes) {\n if (!prefixObj.Prefix) continue;\n const dirName = prefixObj.Prefix.slice(searchPrefix.length).replace(/\\/$/, '');\n if (dirName && !seenDirs.has(dirName)) {\n seenDirs.add(dirName);\n entries.push({ name: dirName, type: 'directory' });\n }\n }\n }\n\n continuationToken = response.NextContinuationToken;\n } while (continuationToken);\n\n return entries;\n }\n\n // ---------------------------------------------------------------------------\n // Path Operations\n // ---------------------------------------------------------------------------\n\n async exists(path: string): Promise<boolean> {\n const key = this.toKey(path);\n if (!key) return true; // Root always exists\n\n const client = await this.getReadyClient();\n\n // Check if it's a file\n try {\n await client.send(\n new HeadObjectCommand({\n Bucket: this.bucket,\n Key: key,\n }),\n );\n return true;\n } catch (error: unknown) {\n if (!isNotFoundError(error)) throw this.handleError(error);\n // Not a file, check if it's a \"directory\" (has objects with this prefix)\n }\n\n // Check if it's a directory prefix\n const response: { Contents?: unknown[] } = await client.send(\n new ListObjectsV2Command({\n Bucket: this.bucket,\n Prefix: key.replace(/\\/$/, '') + '/',\n MaxKeys: 1,\n }),\n );\n\n return (response.Contents?.length ?? 0) > 0;\n }\n\n async stat(path: string): Promise<FileStat> {\n // The key never keeps a trailing slash, so a lookup never matches a directory marker\n const key = trimSlashes(this.toKey(path));\n // A trailing slash always means a directory, so never look for a file\n const directoryOnly = path.endsWith('/');\n\n // Root is always a directory\n if (!key) {\n return {\n name: '',\n path,\n type: 'directory',\n size: 0,\n createdAt: new Date(),\n modifiedAt: new Date(),\n };\n }\n\n const name = key.split('/').pop() ?? '';\n\n if (!directoryOnly) {\n const client = await this.getReadyClient();\n\n try {\n const response: { ContentLength?: number; LastModified?: Date } = await client.send(\n new HeadObjectCommand({\n Bucket: this.bucket,\n Key: key,\n }),\n );\n\n return {\n name,\n path,\n type: 'file',\n size: response.ContentLength ?? 0,\n createdAt: response.LastModified ?? new Date(),\n modifiedAt: response.LastModified ?? new Date(),\n };\n } catch (error: unknown) {\n if (!isNotFoundError(error)) throw this.handleError(error);\n }\n }\n\n // Check if it's a directory\n const isDir = await this.isDirectory(path);\n if (isDir) {\n return {\n name,\n path,\n type: 'directory',\n size: 0,\n createdAt: new Date(),\n modifiedAt: new Date(),\n };\n }\n throw new FileNotFoundError(path);\n }\n\n async isFile(path: string): Promise<boolean> {\n // A trailing slash always means a directory\n if (path.endsWith('/')) return false;\n\n // The key never keeps a trailing slash, so a lookup never matches a directory marker\n const key = trimSlashes(this.toKey(path));\n if (!key) return false; // Root is a directory, not a file\n\n const client = await this.getReadyClient();\n\n try {\n await client.send(\n new HeadObjectCommand({\n Bucket: this.bucket,\n Key: key,\n }),\n );\n return true;\n } catch (error: unknown) {\n if (!isNotFoundError(error)) throw this.handleError(error);\n return false;\n }\n }\n\n async isDirectory(path: string): Promise<boolean> {\n const key = this.toKey(path);\n if (!key) return true; // Root is always a directory\n\n const client = await this.getReadyClient();\n\n const response: { Contents?: unknown[] } = await client.send(\n new ListObjectsV2Command({\n Bucket: this.bucket,\n Prefix: key.replace(/\\/$/, '') + '/',\n MaxKeys: 1,\n }),\n );\n\n return (response.Contents?.length ?? 0) > 0;\n }\n\n // ---------------------------------------------------------------------------\n // Lifecycle (overrides base class protected methods)\n // ---------------------------------------------------------------------------\n\n /**\n * Initialize the S3 client.\n * Status management is handled by the base class.\n */\n async init(): Promise<void> {\n // Verify we can access the bucket\n const client = this.getClient();\n try {\n await client.send(new HeadBucketCommand({ Bucket: this.bucket }));\n } catch (error) {\n // Extract httpStatusCode if available\n const statusCode = (error as { $metadata?: { httpStatusCode?: number } }).$metadata?.httpStatusCode;\n\n // Create error with status property for proper HTTP response codes\n const createError = (message: string) => {\n const err = new Error(message) as Error & { status?: number };\n if (statusCode) err.status = statusCode;\n return err;\n };\n\n // Provide better error messages for common S3 errors\n if (isAccessDeniedError(error)) {\n throw createError(`Access denied to bucket \"${this.bucket}\" - check credentials and permissions`);\n }\n if (isNotFoundError(error)) {\n throw createError(`Bucket \"${this.bucket}\" not found`);\n }\n const message = error instanceof Error ? error.message : String(error);\n if (statusCode) {\n throw createError(`Failed to access bucket \"${this.bucket}\" (HTTP ${statusCode}): ${message}`);\n }\n throw error;\n }\n }\n\n /**\n * Clean up the S3 client.\n * Status management is handled by the base class.\n */\n async destroy(): Promise<void> {\n this._client = null;\n }\n}\n","import {\n S3Client,\n GetObjectCommand,\n PutObjectCommand,\n DeleteObjectCommand,\n HeadObjectCommand,\n ListObjectsV2Command,\n DeleteObjectsCommand,\n} from '@aws-sdk/client-s3';\n\nimport type { AwsCredentialIdentity, AwsCredentialIdentityProvider } from '@aws-sdk/types';\n\nimport { BlobStore } from '@mastra/core/storage';\nimport type { StorageBlobEntry } from '@mastra/core/storage';\n\n/**\n * Configuration for S3BlobStore.\n *\n * Compatible with AWS S3, Cloudflare R2, MinIO, DigitalOcean Spaces, etc.\n */\nexport interface S3BlobStoreOptions {\n /** S3 bucket name */\n bucket: string;\n /** AWS region (use 'auto' for R2) */\n region: string;\n /**\n * AWS credentials or credential provider function.\n * Accepts static credentials or a provider that auto-refreshes\n * (e.g. fromNodeProviderChain() from @aws-sdk/credential-providers).\n * When set, takes precedence over accessKeyId/secretAccessKey/sessionToken.\n * When ALL credential options are omitted, the SDK default credential\n * provider chain is used (env vars, ~/.aws, IMDS, ECS container credentials).\n */\n credentials?: AwsCredentialIdentity | AwsCredentialIdentityProvider;\n /** AWS access key ID. Optional - omit to use the SDK default credential provider chain. */\n accessKeyId?: string;\n /** AWS secret access key. Optional - omit to use the SDK default credential provider chain. */\n secretAccessKey?: string;\n /** AWS session token for temporary credentials (SSO, AssumeRole, container credentials, etc.) */\n sessionToken?: string;\n /**\n * Custom endpoint URL for S3-compatible storage.\n * Examples:\n * - Cloudflare R2: 'https://{accountId}.r2.cloudflarestorage.com'\n * - MinIO: 'http://localhost:9000'\n */\n endpoint?: string;\n /** Force path-style URLs (required for some S3-compatible services like MinIO) */\n forcePathStyle?: boolean;\n /**\n * Key prefix for all blob objects.\n * Defaults to 'mastra_skill_blobs/'.\n */\n prefix?: string;\n}\n\n/** Trim leading and trailing slashes. */\nfunction trimSlashes(s: string): string {\n let start = 0;\n let end = s.length;\n while (start < end && s[start] === '/') start++;\n while (end > start && s[end - 1] === '/') end--;\n return s.slice(start, end);\n}\n\n/**\n * S3-backed content-addressable blob store for skill versioning.\n *\n * Each blob is stored as an S3 object keyed by its SHA-256 hash.\n * Metadata (size, mimeType, createdAt) is stored in S3 object user metadata.\n *\n * Since blobs are content-addressable, writes are idempotent — the same hash\n * always maps to the same content, so overwrites are safe and equivalent to\n * a no-op.\n *\n * @example AWS S3\n * ```typescript\n * import { S3BlobStore } from '@mastra/s3';\n *\n * const blobs = new S3BlobStore({\n * bucket: 'my-skill-blobs',\n * region: 'us-east-1',\n * accessKeyId: process.env.AWS_ACCESS_KEY_ID!,\n * secretAccessKey: process.env.AWS_SECRET_ACCESS_KEY!,\n * });\n * ```\n *\n * @example MinIO (local)\n * ```typescript\n * import { S3BlobStore } from '@mastra/s3';\n *\n * const blobs = new S3BlobStore({\n * bucket: 'skill-blobs',\n * region: 'us-east-1',\n * accessKeyId: 'minioadmin',\n * secretAccessKey: 'minioadmin',\n * endpoint: 'http://localhost:9000',\n * forcePathStyle: true,\n * });\n * ```\n */\nexport class S3BlobStore extends BlobStore {\n private readonly bucket: string;\n private readonly prefix: string;\n private _client: S3Client | null = null;\n\n private readonly region: string;\n private readonly credentials?: AwsCredentialIdentity | AwsCredentialIdentityProvider;\n private readonly accessKeyId?: string;\n private readonly secretAccessKey?: string;\n private readonly sessionToken?: string;\n private readonly endpoint?: string;\n private readonly forcePathStyle: boolean;\n\n constructor(options: S3BlobStoreOptions) {\n super();\n this.bucket = options.bucket;\n this.region = options.region;\n this.credentials = options.credentials;\n this.accessKeyId = options.accessKeyId;\n this.secretAccessKey = options.secretAccessKey;\n this.sessionToken = options.sessionToken;\n this.endpoint = options.endpoint;\n this.forcePathStyle = options.forcePathStyle ?? !!options.endpoint;\n this.prefix = options.prefix ? trimSlashes(options.prefix) + '/' : 'mastra_skill_blobs/';\n }\n\n private getClient(): S3Client {\n if (this._client) return this._client;\n\n const hasStaticCredentials = this.accessKeyId && this.secretAccessKey;\n\n let credentials: AwsCredentialIdentity | AwsCredentialIdentityProvider | undefined;\n if (this.credentials) {\n credentials = this.credentials;\n } else if (hasStaticCredentials) {\n credentials = {\n accessKeyId: this.accessKeyId!,\n secretAccessKey: this.secretAccessKey!,\n ...(this.sessionToken && { sessionToken: this.sessionToken }),\n };\n }\n // When credentials is undefined, SDK uses its default provider chain\n\n this._client = new S3Client({\n region: this.region,\n ...(credentials !== undefined && { credentials }),\n endpoint: this.endpoint,\n forcePathStyle: this.forcePathStyle,\n });\n return this._client;\n }\n\n private toKey(hash: string): string {\n return this.prefix + hash;\n }\n\n async init(): Promise<void> {\n // S3 doesn't require table creation — the bucket is expected to exist.\n }\n\n async put(entry: StorageBlobEntry): Promise<void> {\n const client = this.getClient();\n const now = entry.createdAt ?? new Date();\n\n await client.send(\n new PutObjectCommand({\n Bucket: this.bucket,\n Key: this.toKey(entry.hash),\n Body: entry.content,\n ContentType: entry.mimeType ?? 'application/octet-stream',\n Metadata: {\n size: String(entry.size),\n createdat: now.toISOString(),\n ...(entry.mimeType ? { mimetype: entry.mimeType } : {}),\n },\n }),\n );\n }\n\n async get(hash: string): Promise<StorageBlobEntry | null> {\n const client = this.getClient();\n\n try {\n const response = await client.send(\n new GetObjectCommand({\n Bucket: this.bucket,\n Key: this.toKey(hash),\n }),\n );\n\n const body = await response.Body?.transformToString('utf-8');\n if (body === undefined || body === null) return null;\n\n const metadata = response.Metadata ?? {};\n return {\n hash,\n content: body,\n size: metadata.size != null ? Number(metadata.size) : Buffer.byteLength(body, 'utf-8'),\n mimeType: metadata.mimetype || response.ContentType || undefined,\n createdAt: metadata.createdat ? new Date(metadata.createdat) : new Date(),\n };\n } catch (error: unknown) {\n if (isNotFoundError(error)) return null;\n throw error;\n }\n }\n\n async has(hash: string): Promise<boolean> {\n const client = this.getClient();\n\n try {\n await client.send(\n new HeadObjectCommand({\n Bucket: this.bucket,\n Key: this.toKey(hash),\n }),\n );\n return true;\n } catch (error: unknown) {\n if (isNotFoundError(error)) return false;\n throw error;\n }\n }\n\n async delete(hash: string): Promise<boolean> {\n // Pre-check is intentional: S3 DeleteObject returns 204 regardless of\n // whether the object existed, so we check first for an accurate return.\n // The TOCTOU gap is acceptable for content-addressable blobs.\n const existed = await this.has(hash);\n if (!existed) return false;\n\n const client = this.getClient();\n await client.send(\n new DeleteObjectCommand({\n Bucket: this.bucket,\n Key: this.toKey(hash),\n }),\n );\n return true;\n }\n\n async putMany(entries: StorageBlobEntry[]): Promise<void> {\n if (entries.length === 0) return;\n // S3 doesn't have a batch PUT, so we parallelize individual puts.\n // Content-addressable means duplicate writes are idempotent.\n await Promise.all(entries.map(entry => this.put(entry)));\n }\n\n async getMany(hashes: string[]): Promise<Map<string, StorageBlobEntry>> {\n const result = new Map<string, StorageBlobEntry>();\n if (hashes.length === 0) return result;\n\n // Parallelize individual gets\n const entries = await Promise.all(hashes.map(hash => this.get(hash)));\n for (const entry of entries) {\n if (entry) {\n result.set(entry.hash, entry);\n }\n }\n return result;\n }\n\n async dangerouslyClearAll(): Promise<void> {\n const client = this.getClient();\n\n let continuationToken: string | undefined;\n do {\n const listResponse = await client.send(\n new ListObjectsV2Command({\n Bucket: this.bucket,\n Prefix: this.prefix,\n ContinuationToken: continuationToken,\n }),\n );\n\n const objects = listResponse.Contents;\n if (objects && objects.length > 0) {\n await client.send(\n new DeleteObjectsCommand({\n Bucket: this.bucket,\n Delete: {\n Objects: objects.filter(obj => obj.Key != null).map(obj => ({ Key: obj.Key! })),\n Quiet: true,\n },\n }),\n );\n }\n\n continuationToken = listResponse.IsTruncated ? listResponse.NextContinuationToken : undefined;\n } while (continuationToken);\n }\n}\n\nfunction isNotFoundError(error: unknown): boolean {\n if (!error || typeof error !== 'object' || !('name' in error)) return false;\n const name = (error as { name: string }).name;\n return name === 'NotFound' || name === 'NoSuchKey' || name === '404';\n}\n","/**\n * S3 filesystem provider descriptor for MastraEditor.\n *\n * @example\n * ```typescript\n * import { s3FilesystemProvider } from '@mastra/s3';\n *\n * const editor = new MastraEditor({\n * filesystems: [s3FilesystemProvider],\n * });\n * ```\n */\nimport type { FilesystemProvider, BlobStoreProvider } from '@mastra/core/editor';\nimport { S3BlobStore } from './blob-store';\nimport type { S3BlobStoreOptions } from './blob-store';\nimport { S3Filesystem } from './filesystem';\nimport type { S3FilesystemOptions } from './filesystem';\n\nexport const s3FilesystemProvider: FilesystemProvider<S3FilesystemOptions> = {\n id: 's3',\n name: 'Amazon S3',\n description: 'S3 or S3-compatible storage (AWS, R2, MinIO, DO Spaces)',\n configSchema: {\n type: 'object',\n required: ['bucket', 'region'],\n properties: {\n bucket: { type: 'string', description: 'S3 bucket name' },\n region: { type: 'string', description: 'AWS region (use \"auto\" for R2)' },\n accessKeyId: { type: 'string', description: 'AWS access key ID' },\n secretAccessKey: { type: 'string', description: 'AWS secret access key' },\n sessionToken: { type: 'string', description: 'AWS session token for temporary credentials' },\n endpoint: { type: 'string', description: 'Custom endpoint URL for S3-compatible storage' },\n forcePathStyle: { type: 'boolean', description: 'Force path-style URLs', default: false },\n prefix: { type: 'string', description: 'Key prefix (acts like a subdirectory)' },\n readOnly: { type: 'boolean', description: 'Mount as read-only', default: false },\n },\n },\n createFilesystem: config => new S3Filesystem(config),\n};\n\n/**\n * S3 blob store provider descriptor for MastraEditor.\n *\n * @example\n * ```typescript\n * import { s3BlobStoreProvider } from '@mastra/s3';\n *\n * const editor = new MastraEditor({\n * blobStores: { s3: s3BlobStoreProvider },\n * });\n * ```\n */\nexport const s3BlobStoreProvider: BlobStoreProvider<S3BlobStoreOptions> = {\n id: 's3',\n name: 'Amazon S3 Blob Store',\n description: 'Content-addressable blob storage using S3 or S3-compatible storage (AWS, R2, MinIO, DO Spaces)',\n configSchema: {\n type: 'object',\n required: ['bucket', 'region', 'accessKeyId', 'secretAccessKey'],\n properties: {\n bucket: { type: 'string', description: 'S3 bucket name' },\n region: { type: 'string', description: 'AWS region (use \"auto\" for R2)' },\n accessKeyId: { type: 'string', description: 'AWS access key ID' },\n secretAccessKey: { type: 'string', description: 'AWS secret access key' },\n sessionToken: { type: 'string', description: 'AWS session token for temporary credentials' },\n endpoint: { type: 'string', description: 'Custom endpoint URL for S3-compatible storage' },\n forcePathStyle: { type: 'boolean', description: 'Force path-style URLs', default: false },\n prefix: { type: 'string', description: 'Key prefix for blob objects (default: mastra_skill_blobs/)' },\n },\n },\n createBlobStore: config => new S3BlobStore(config),\n};\n"],"mappings":";;;;;;;AA8DA,MAAM,aAAqC;CAEzC,QAAQ;CACR,OAAO;CACP,aAAa;CACb,SAAS;CACT,QAAQ;CACR,QAAQ;CACR,QAAQ;CACR,QAAQ;CAER,OAAO;CACP,QAAQ;CACR,OAAO;CACP,QAAQ;CACR,QAAQ;CACR,SAAS;CACT,SAAS;CACT,QAAQ;CACR,OAAO;CACP,OAAO;CACP,OAAO;CACP,SAAS;CAET,QAAQ;CACR,QAAQ;CACR,SAAS;CACT,QAAQ;CACR,QAAQ;CACR,SAAS;CACT,QAAQ;CAER,QAAQ;CAER,QAAQ;CACR,OAAO;CACP,QAAQ;AACV;;;;AAKA,SAAS,YAAY,MAAsB;CACzC,MAAM,MAAM,KAAK,YAAY,CAAC,CAAC,MAAM,UAAU,CAAC,GAAG;CACnD,OAAO,MAAO,WAAW,QAAQ,6BAA8B;AACjE;;AAGA,SAASA,kBAAgB,OAAyB;CAChD,IAAI,CAAC,SAAS,OAAO,UAAU,YAAY,EAAE,UAAU,QAAQ,OAAO;CACtE,MAAM,OAAQ,MAA2B;CACzC,OAAO,SAAS,cAAc,SAAS,eAAe,SAAS;AACjE;;AAGA,SAAS,oBAAoB,OAAyB;CACpD,IAAI,CAAC,SAAS,OAAO,UAAU,UAAU,OAAO;CAChD,MAAM,MAAM;CACZ,OAAO,IAAI,SAAS,kBAAkB,IAAI,WAAW,mBAAmB;AAC1E;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AA0GA,SAASC,cAAY,GAAmB;CACtC,IAAI,QAAQ;CACZ,IAAI,MAAM,EAAE;CACZ,OAAO,QAAQ,OAAO,EAAE,WAAW,KAAK;CACxC,OAAO,MAAM,SAAS,EAAE,MAAM,OAAO,KAAK;CAC1C,OAAO,EAAE,MAAM,OAAO,GAAG;AAC3B;AAEA,IAAa,eAAb,cAAkC,iBAAiB;CACjD;CACA,OAAgB;CAChB,WAAoB;CACpB;CAEA,SAAyB;CAGzB;CACA,OAAgC;CAChC;CAEA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CAEA,UAAmC;CAEnC,YAAY,SAA8B;EACxC,MAAM;GAAE,GAAG;GAAS,MAAM;EAAe,CAAC;EAC1C,KAAK,KAAK,QAAQ,MAAM,SAAS,KAAK,IAAI,CAAC,CAAC,SAAS,EAAE,EAAE,GAAG,KAAK,OAAO,CAAC,CAAC,SAAS,EAAE,CAAC,CAAC,MAAM,GAAG,CAAC;EACjG,KAAK,SAAS,QAAQ;EACtB,KAAK,SAAS,QAAQ;EACtB,KAAK,cAAc,QAAQ;EAC3B,KAAK,cAAc,QAAQ;EAC3B,KAAK,kBAAkB,QAAQ;EAC/B,KAAK,eAAe,QAAQ;EAC5B,KAAK,WAAW,QAAQ;EACxB,KAAK,iBAAiB,QAAQ,kBAAkB,CAAC,CAAC,QAAQ;EAE1D,MAAM,gBAAgB,QAAQ,SAASA,cAAY,QAAQ,MAAM,IAAI;EACrE,KAAK,SAAS,gBAAgB,gBAAgB,MAAM;EAGpD,KAAK,OAAO,QAAQ,QAAQ,KAAK,uBAAuB,QAAQ,QAAQ;EACxE,KAAK,cAAc,QAAQ,eAAe,KAAK,sBAAsB,KAAK,IAAI;EAC9E,KAAK,cAAc,QAAQ;EAC3B,KAAK,WAAW,QAAQ;CAC1B;;;;;;;;;;;;;;;;;;;;CAqBA,IAAI,SAAmB;EACrB,OAAO,KAAK,UAAU;CACxB;;;;;;;;;;;CAYA,iBAAgC;EAC9B,MAAM,SAAwB;GAC5B,MAAM;GACN,QAAQ,KAAK;GACb,QAAQ,KAAK;GACb,UAAU,KAAK;EACjB;EAEA,IAAI,KAAK,eAAe,KAAK,iBAAiB;GAC5C,OAAO,cAAc,KAAK;GAC1B,OAAO,kBAAkB,KAAK;GAC9B,IAAI,KAAK,cACP,OAAO,eAAe,KAAK;EAE/B;EAEA,IAAI,KAAK,QACP,OAAO,SAAS,KAAK;EAGvB,IAAI,KAAK,UACP,OAAO,WAAW;EAGpB,OAAO;CACT;;;;CAKA,UAKG;EACD,OAAO;GACL,IAAI,KAAK;GACT,MAAM,KAAK;GACX,UAAU,KAAK;GACf,QAAQ,KAAK;GACb,OAAO,KAAK;GACZ,UAAU,KAAK;GACf,MAAM,KAAK;GACX,UAAU;IACR,QAAQ,KAAK;IACb,QAAQ,KAAK;IACb,GAAI,KAAK,YAAY,EAAE,UAAU,KAAK,SAAS;IAC/C,GAAI,KAAK,UAAU,EAAE,QAAQ,KAAK,OAAO;GAC3C;EACF;CACF;;;;;CAMA,YAAoB,OAAyB;EAC3C,IAAI,oBAAoB,KAAK,GAAG;GAC9B,KAAK,SAAS;GACd,KAAK,QAAQ;EACf;EACA,OAAO;CACT;;;;;CAMA,kBAA0B;EACxB,MAAM,eAAe,KAAK,eAAe;EACzC,MAAM,SAAS,KAAK,WAAW,cAAc;EAC7C,OAAO,GAAG,aAAa,sBAAsB,KAAK,OAAO,KAAK,OAAO;CACvE;;;;CAKA,uBAA+B,UAAmC;EAChE,IAAI,CAAC,UAEH,OAAO;EAIT,IAAI;EACJ,IAAI;GAEF,WAAW,IADK,IAAI,QACP,CAAC,CAAC,SAAS,YAAY;EACtC,QAAQ;GAEN,WAAW,SAAS,YAAY;EAClC;EAGA,IACE,aAAa,8BACb,SAAS,SAAS,2BAA2B,KAC7C,SAAS,SAAS,iBAAiB,GAEnC,OAAO;EAGT,IACE,aAAa,4BACb,SAAS,SAAS,yBAAyB,KAC3C,SAAS,SAAS,iBAAiB,GAEnC,OAAO;EAGT,IACE,aAAa,2BACb,SAAS,SAAS,wBAAwB,KAC1C,SAAS,SAAS,YAAY,GAE9B,OAAO;EAGT,IAAI,SAAS,SAAS,OAAO,GAC3B,OAAO;EAIT,OAAO;CACT;;;;CAKA,sBAA8B,MAA0C;EACtE,QAAQ,MAAR;GACE,KAAK,UACH,OAAO;GACT,KAAK;GACL,KAAK;GACL,KAAK,iBACH,OAAO;GACT,KAAK;GACL,KAAK;GACL,KAAK,wBACH,OAAO;GACT,KAAK;GACL,KAAK,cACH,OAAO;GACT,KAAK,SACH,OAAO;GACT,KAAK,MACH,OAAO;GACT,SAEE;EACJ;CACF;CAEA,YAA8B;EAC5B,IAAI,KAAK,SAAS,OAAO,KAAK;EAE9B,MAAM,uBAAuB,KAAK,eAAe,KAAK;EAEtD,IAAI;EACJ,IAAI,KAAK,aACP,cAAc,KAAK;OACd,IAAI,sBACT,cAAc;GACZ,aAAa,KAAK;GAClB,iBAAiB,KAAK;GACtB,GAAI,KAAK,gBAAgB,EAAE,cAAc,KAAK,aAAa;EAC7D;EAIF,KAAK,UAAU,IAAI,SAAS;GAC1B,QAAQ,KAAK;GACb,GAAI,gBAAgB,KAAA,KAAa,EAAE,YAAY;GAC/C,UAAU,KAAK;GACf,gBAAgB,KAAK;EACvB,CAAC;EAED,OAAO,KAAK;CACd;;;;;CAMA,MAAc,iBAAoC;EAChD,MAAM,KAAK,YAAY;EACvB,OAAO,KAAK,UAAU;CACxB;CAEA,MAAc,MAAsB;EAElC,MAAM,YAAY,KAAK,QAAQ,QAAQ,EAAE,CAAC,CAAC,QAAQ,eAAe,EAAE;EACpE,OAAO,KAAK,SAAS;CACvB;CAMA,MAAM,SAAS,MAAc,SAAiD;EAE5E,IAAI,KAAK,SAAS,GAAG,GACnB,MAAM,IAAI,kBAAkB,IAAI;EAGlC,MAAM,SAAS,MAAM,KAAK,eAAe;EAEzC,IAAI;GAQF,MAAM,OAAO,OAAM,MAPI,OAAO,KAC5B,IAAI,iBAAiB;IACnB,QAAQ,KAAK;IACb,KAAK,KAAK,MAAM,IAAI;GACtB,CAAC,CACH,EAAA,CAE4B,MAAM,qBAAqB;GACvD,IAAI,CAAC,MAAM,MAAM,IAAI,kBAAkB,IAAI;GAE3C,MAAM,SAAS,OAAO,KAAK,IAAI;GAC/B,IAAI,SAAS,UACX,OAAO,OAAO,SAAS,QAAQ,QAAQ;GAEzC,OAAO;EACT,SAAS,OAAgB;GACvB,IAAID,kBAAgB,KAAK,GACvB,MAAM,IAAI,kBAAkB,IAAI;GAElC,MAAM,KAAK,YAAY,KAAK;EAC9B;CACF;CAEA,MAAM,UAAU,MAAc,SAAsB,SAAuC;EACzF,MAAM,SAAS,MAAM,KAAK,eAAe;EAEzC,IAAI,SAAS,cAAc,SAAU,MAAM,KAAK,OAAO,IAAI,GACzD,MAAM,IAAI,gBAAgB,IAAI;EAGhC,MAAM,OAAO,OAAO,YAAY,WAAW,OAAO,KAAK,SAAS,OAAO,IAAI,OAAO,KAAK,OAAO;EAC9F,MAAM,cAAc,YAAY,IAAI;EAEpC,MAAM,OAAO,KACX,IAAI,iBAAiB;GACnB,QAAQ,KAAK;GACb,KAAK,KAAK,MAAM,IAAI;GACpB,MAAM;GACN,aAAa;EACf,CAAC,CACH;CACF;CAEA,MAAM,WAAW,MAAc,SAAqC;EAElE,IAAI,WAAW;EACf,IAAI;GACF,WAAY,MAAM,KAAK,SAAS,MAAM,EAAE,UAAU,QAAQ,CAAC;EAC7D,SAAS,OAAO;GACd,IAAI,iBAAiB,mBAAmB,CAExC,OACE,MAAM;EAEV;EAEA,MAAM,gBAAgB,OAAO,YAAY,WAAW,UAAU,OAAO,KAAK,OAAO,CAAC,CAAC,SAAS,OAAO;EACnG,MAAM,KAAK,UAAU,MAAM,WAAW,aAAa;CACrD;CAEA,MAAM,WAAW,MAAc,SAAwC;EAGrE,IAAI,MADgB,KAAK,YAAY,IAAI,GAC9B;GACT,MAAM,KAAK,MAAM,MAAM;IAAE,WAAW;IAAM,OAAO,SAAS;GAAM,CAAC;GACjE;EACF;EAEA,MAAM,SAAS,MAAM,KAAK,eAAe;EAEzC,IAAI;GACF,MAAM,OAAO,KACX,IAAI,oBAAoB;IACtB,QAAQ,KAAK;IACb,KAAK,KAAK,MAAM,IAAI;GACtB,CAAC,CACH;EACF,SAAS,OAAgB;GACvB,IAAI,SAAS,OAAO;GACpB,IAAIA,kBAAgB,KAAK,GACvB,MAAM,IAAI,kBAAkB,IAAI;GAElC,MAAM,KAAK,YAAY,KAAK;EAC9B;CACF;CAEA,MAAM,SAAS,KAAa,MAAc,SAAsC;EAE9E,IAAI,IAAI,SAAS,GAAG,GAClB,MAAM,IAAI,kBAAkB,GAAG;EAGjC,MAAM,SAAS,MAAM,KAAK,eAAe;EAEzC,IAAI,SAAS,cAAc,SAAU,MAAM,KAAK,OAAO,IAAI,GACzD,MAAM,IAAI,gBAAgB,IAAI;EAGhC,IAAI;GACF,MAAM,OAAO,KACX,IAAI,kBAAkB;IACpB,QAAQ,KAAK;IACb,YAAY,GAAG,KAAK,OAAO,GAAG,mBAAmB,KAAK,MAAM,GAAG,CAAC,CAAC,CAAC,QAAQ,QAAQ,GAAG;IACrF,KAAK,KAAK,MAAM,IAAI;GACtB,CAAC,CACH;EACF,SAAS,OAAgB;GACvB,IAAIA,kBAAgB,KAAK,GACvB,MAAM,IAAI,kBAAkB,GAAG;GAEjC,MAAM,KAAK,YAAY,KAAK;EAC9B;CACF;CAEA,MAAM,SAAS,KAAa,MAAc,SAAsC;EAC9E,MAAM,KAAK,SAAS,KAAK,MAAM,OAAO;EACtC,MAAM,KAAK,WAAW,KAAK,EAAE,OAAO,KAAK,CAAC;CAC5C;CAMA,MAAM,MAAM,MAAc,UAAmD;EAM3E,MAAM,MAAMC,cAAY,KAAK,MAAM,IAAI,CAAC;EACxC,IAAI,CAAC,OAAO,MAAM,QAAQ,KAAK,QAAQ;EAEvC,MAAM,SAAS,MAAM,KAAK,eAAe;EAKzC,IAAI;GACF,MAAM,OAAO,KAAK,IAAI,kBAAkB;IAAE,QAAQ,KAAK;IAAQ,KAAK;GAAI,CAAC,CAAC;GAC1E,MAAM,IAAI,gBAAgB,IAAI;EAChC,SAAS,OAAgB;GACvB,IAAI,iBAAiB,iBAAiB,MAAM;EAC9C;EAEA,MAAM,OAAO,KACX,IAAI,iBAAiB;GACnB,QAAQ,KAAK;GACb,KAAK,MAAM;GACX,sBAAM,IAAI,WAAW,CAAC;EACxB,CAAC,CACH;CACF;CAEA,MAAM,MAAM,MAAc,SAAwC;EAChE,IAAI,CAAC,SAAS,WAAW;GAGvB,KAAI,MADkB,KAAK,QAAQ,IAAI,EAAA,CAC3B,SAAS,GACnB,MAAM,IAAI,MAAM,wBAAwB,MAAM;GAGhD,MAAM,MAAMA,cAAY,KAAK,MAAM,IAAI,CAAC;GACxC,IAAI,OAAO,MAAM,QAAQ,KAAK,QAE5B,OAAM,MADe,KAAK,eAAe,EAAA,CAC5B,KACX,IAAI,oBAAoB;IACtB,QAAQ,KAAK;IACb,KAAK,MAAM;GACb,CAAC,CACH;GAEF;EACF;EAGA,MAAM,SAAS,MAAM,KAAK,eAAe;EAEzC,MAAM,SAAS,KAAK,MAAM,IAAI,CAAC,CAAC,QAAQ,OAAO,EAAE,IAAI;EAErD,IAAI;EACJ,GAAG;GACD,MAAM,eAAe,MAAM,OAAO,KAChC,IAAI,qBAAqB;IACvB,QAAQ,KAAK;IACb,QAAQ;IACR,mBAAmB;GACrB,CAAC,CACH;GAEA,IAAI,aAAa,YAAY,aAAa,SAAS,SAAS,GAAG;IAC7D,MAAM,iBAAiB,MAAM,OAAO,KAClC,IAAI,qBAAqB;KACvB,QAAQ,KAAK;KACb,QAAQ,EACN,SAAS,aAAa,SAAS,QAAQ,QAAgC,CAAC,CAAC,IAAI,GAAG,CAAC,CAAC,KAAI,SAAQ,EAC5F,KAAK,IAAI,IACX,EAAE,EACJ;IACF,CAAC,CACH;IACA,IAAI,eAAe,UAAU,eAAe,OAAO,SAAS,GAC1D,MAAM,IAAI,MAAM,oBAAoB,eAAe,OAAO,OAAO,gBAAgB,MAAM;GAE3F;GAEA,oBAAoB,aAAa;EACnC,SAAS;CACX;CAEA,MAAM,QAAQ,MAAc,SAA6C;EACvE,MAAM,SAAS,MAAM,KAAK,eAAe;EAEzC,MAAM,SAAS,KAAK,MAAM,IAAI,CAAC,CAAC,QAAQ,OAAO,EAAE;EACjD,MAAM,eAAe,SAAS,SAAS,MAAM;EAE7C,MAAM,UAAuB,CAAC;EAC9B,MAAM,2BAAW,IAAI,IAAY;EAEjC,IAAI;EACJ,GAAG;GACD,MAAM,WAAW,MAAM,OAAO,KAC5B,IAAI,qBAAqB;IACvB,QAAQ,KAAK;IACb,QAAQ;IACR,WAAW,SAAS,YAAY,KAAA,IAAY;IAC5C,mBAAmB;GACrB,CAAC,CACH;GAGA,IAAI,SAAS,UACX,KAAK,MAAM,OAAO,SAAS,UAAU;IACnC,MAAM,MAAM,IAAI;IAChB,IAAI,CAAC,OAAO,QAAQ,cAAc;IAElC,MAAM,eAAe,IAAI,MAAM,aAAa,MAAM;IAClD,IAAI,CAAC,cAAc;IAGnB,IAAI,aAAa,SAAS,GAAG,GAAG;KAG9B,IAAI,SAAS,aAAa,QAAQ,WAAW;KAG7C,MAAM,UAAU,SAAS,YAAY,aAAa,MAAM,GAAG,EAAE,IAAI,aAAa,MAAM,GAAG,CAAC,CAAC;KACzF,IAAI,CAAC,SAAS;KACd,IAAI,CAAC,SAAS,IAAI,OAAO,GAAG;MAC1B,SAAS,IAAI,OAAO;MACpB,QAAQ,KAAK;OAAE,MAAM;OAAS,MAAM;MAAY,CAAC;KACnD;KACA;IACF;IAEA,MAAM,OAAO,SAAS,YAAY,eAAe,aAAa,MAAM,GAAG,CAAC,CAAC;IAGzE,IAAI,CAAC,MAAM;IAGX,IAAI,SAAS,WAEP;SAAA,EADe,MAAM,QAAQ,QAAQ,SAAS,IAAI,QAAQ,YAAY,CAAC,QAAQ,SAAS,EAAA,CAC5E,MAAK,QAAO,KAAK,SAAS,GAAG,CAAC,GAC5C;IAAA;IAIJ,QAAQ,KAAK;KACX;KACA,MAAM;KACN,MAAM,IAAI;IACZ,CAAC;GACH;GAIF,IAAI,SAAS,gBACX,KAAK,MAAM,aAAa,SAAS,gBAAgB;IAC/C,IAAI,CAAC,UAAU,QAAQ;IACvB,MAAM,UAAU,UAAU,OAAO,MAAM,aAAa,MAAM,CAAC,CAAC,QAAQ,OAAO,EAAE;IAC7E,IAAI,WAAW,CAAC,SAAS,IAAI,OAAO,GAAG;KACrC,SAAS,IAAI,OAAO;KACpB,QAAQ,KAAK;MAAE,MAAM;MAAS,MAAM;KAAY,CAAC;IACnD;GACF;GAGF,oBAAoB,SAAS;EAC/B,SAAS;EAET,OAAO;CACT;CAMA,MAAM,OAAO,MAAgC;EAC3C,MAAM,MAAM,KAAK,MAAM,IAAI;EAC3B,IAAI,CAAC,KAAK,OAAO;EAEjB,MAAM,SAAS,MAAM,KAAK,eAAe;EAGzC,IAAI;GACF,MAAM,OAAO,KACX,IAAI,kBAAkB;IACpB,QAAQ,KAAK;IACb,KAAK;GACP,CAAC,CACH;GACA,OAAO;EACT,SAAS,OAAgB;GACvB,IAAI,CAACD,kBAAgB,KAAK,GAAG,MAAM,KAAK,YAAY,KAAK;EAE3D;EAWA,SAAQ,MARyC,OAAO,KACtD,IAAI,qBAAqB;GACvB,QAAQ,KAAK;GACb,QAAQ,IAAI,QAAQ,OAAO,EAAE,IAAI;GACjC,SAAS;EACX,CAAC,CACH,EAAA,CAEiB,UAAU,UAAU,KAAK;CAC5C;CAEA,MAAM,KAAK,MAAiC;EAE1C,MAAM,MAAMC,cAAY,KAAK,MAAM,IAAI,CAAC;EAExC,MAAM,gBAAgB,KAAK,SAAS,GAAG;EAGvC,IAAI,CAAC,KACH,OAAO;GACL,MAAM;GACN;GACA,MAAM;GACN,MAAM;GACN,2BAAW,IAAI,KAAK;GACpB,4BAAY,IAAI,KAAK;EACvB;EAGF,MAAM,OAAO,IAAI,MAAM,GAAG,CAAC,CAAC,IAAI,KAAK;EAErC,IAAI,CAAC,eAAe;GAClB,MAAM,SAAS,MAAM,KAAK,eAAe;GAEzC,IAAI;IACF,MAAM,WAA4D,MAAM,OAAO,KAC7E,IAAI,kBAAkB;KACpB,QAAQ,KAAK;KACb,KAAK;IACP,CAAC,CACH;IAEA,OAAO;KACL;KACA;KACA,MAAM;KACN,MAAM,SAAS,iBAAiB;KAChC,WAAW,SAAS,gCAAgB,IAAI,KAAK;KAC7C,YAAY,SAAS,gCAAgB,IAAI,KAAK;IAChD;GACF,SAAS,OAAgB;IACvB,IAAI,CAACD,kBAAgB,KAAK,GAAG,MAAM,KAAK,YAAY,KAAK;GAC3D;EACF;EAIA,IAAI,MADgB,KAAK,YAAY,IAAI,GAEvC,OAAO;GACL;GACA;GACA,MAAM;GACN,MAAM;GACN,2BAAW,IAAI,KAAK;GACpB,4BAAY,IAAI,KAAK;EACvB;EAEF,MAAM,IAAI,kBAAkB,IAAI;CAClC;CAEA,MAAM,OAAO,MAAgC;EAE3C,IAAI,KAAK,SAAS,GAAG,GAAG,OAAO;EAG/B,MAAM,MAAMC,cAAY,KAAK,MAAM,IAAI,CAAC;EACxC,IAAI,CAAC,KAAK,OAAO;EAEjB,MAAM,SAAS,MAAM,KAAK,eAAe;EAEzC,IAAI;GACF,MAAM,OAAO,KACX,IAAI,kBAAkB;IACpB,QAAQ,KAAK;IACb,KAAK;GACP,CAAC,CACH;GACA,OAAO;EACT,SAAS,OAAgB;GACvB,IAAI,CAACD,kBAAgB,KAAK,GAAG,MAAM,KAAK,YAAY,KAAK;GACzD,OAAO;EACT;CACF;CAEA,MAAM,YAAY,MAAgC;EAChD,MAAM,MAAM,KAAK,MAAM,IAAI;EAC3B,IAAI,CAAC,KAAK,OAAO;EAYjB,SAAQ,OARyC,MAF5B,KAAK,eAAe,EAAA,CAEe,KACtD,IAAI,qBAAqB;GACvB,QAAQ,KAAK;GACb,QAAQ,IAAI,QAAQ,OAAO,EAAE,IAAI;GACjC,SAAS;EACX,CAAC,CACH,EAAA,CAEiB,UAAU,UAAU,KAAK;CAC5C;;;;;CAUA,MAAM,OAAsB;EAE1B,MAAM,SAAS,KAAK,UAAU;EAC9B,IAAI;GACF,MAAM,OAAO,KAAK,IAAI,kBAAkB,EAAE,QAAQ,KAAK,OAAO,CAAC,CAAC;EAClE,SAAS,OAAO;GAEd,MAAM,aAAc,MAAsD,WAAW;GAGrF,MAAM,eAAe,YAAoB;IACvC,MAAM,MAAM,IAAI,MAAM,OAAO;IAC7B,IAAI,YAAY,IAAI,SAAS;IAC7B,OAAO;GACT;GAGA,IAAI,oBAAoB,KAAK,GAC3B,MAAM,YAAY,4BAA4B,KAAK,OAAO,sCAAsC;GAElG,IAAIA,kBAAgB,KAAK,GACvB,MAAM,YAAY,WAAW,KAAK,OAAO,YAAY;GAEvD,MAAM,UAAU,iBAAiB,QAAQ,MAAM,UAAU,OAAO,KAAK;GACrE,IAAI,YACF,MAAM,YAAY,4BAA4B,KAAK,OAAO,UAAU,WAAW,KAAK,SAAS;GAE/F,MAAM;EACR;CACF;;;;;CAMA,MAAM,UAAyB;EAC7B,KAAK,UAAU;CACjB;AACF;;;;AC/6BA,SAAS,YAAY,GAAmB;CACtC,IAAI,QAAQ;CACZ,IAAI,MAAM,EAAE;CACZ,OAAO,QAAQ,OAAO,EAAE,WAAW,KAAK;CACxC,OAAO,MAAM,SAAS,EAAE,MAAM,OAAO,KAAK;CAC1C,OAAO,EAAE,MAAM,OAAO,GAAG;AAC3B;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAsCA,IAAa,cAAb,cAAiC,UAAU;CACzC;CACA;CACA,UAAmC;CAEnC;CACA;CACA;CACA;CACA;CACA;CACA;CAEA,YAAY,SAA6B;EACvC,MAAM;EACN,KAAK,SAAS,QAAQ;EACtB,KAAK,SAAS,QAAQ;EACtB,KAAK,cAAc,QAAQ;EAC3B,KAAK,cAAc,QAAQ;EAC3B,KAAK,kBAAkB,QAAQ;EAC/B,KAAK,eAAe,QAAQ;EAC5B,KAAK,WAAW,QAAQ;EACxB,KAAK,iBAAiB,QAAQ,kBAAkB,CAAC,CAAC,QAAQ;EAC1D,KAAK,SAAS,QAAQ,SAAS,YAAY,QAAQ,MAAM,IAAI,MAAM;CACrE;CAEA,YAA8B;EAC5B,IAAI,KAAK,SAAS,OAAO,KAAK;EAE9B,MAAM,uBAAuB,KAAK,eAAe,KAAK;EAEtD,IAAI;EACJ,IAAI,KAAK,aACP,cAAc,KAAK;OACd,IAAI,sBACT,cAAc;GACZ,aAAa,KAAK;GAClB,iBAAiB,KAAK;GACtB,GAAI,KAAK,gBAAgB,EAAE,cAAc,KAAK,aAAa;EAC7D;EAIF,KAAK,UAAU,IAAI,SAAS;GAC1B,QAAQ,KAAK;GACb,GAAI,gBAAgB,KAAA,KAAa,EAAE,YAAY;GAC/C,UAAU,KAAK;GACf,gBAAgB,KAAK;EACvB,CAAC;EACD,OAAO,KAAK;CACd;CAEA,MAAc,MAAsB;EAClC,OAAO,KAAK,SAAS;CACvB;CAEA,MAAM,OAAsB,CAE5B;CAEA,MAAM,IAAI,OAAwC;EAChD,MAAM,SAAS,KAAK,UAAU;EAC9B,MAAM,MAAM,MAAM,6BAAa,IAAI,KAAK;EAExC,MAAM,OAAO,KACX,IAAI,iBAAiB;GACnB,QAAQ,KAAK;GACb,KAAK,KAAK,MAAM,MAAM,IAAI;GAC1B,MAAM,MAAM;GACZ,aAAa,MAAM,YAAY;GAC/B,UAAU;IACR,MAAM,OAAO,MAAM,IAAI;IACvB,WAAW,IAAI,YAAY;IAC3B,GAAI,MAAM,WAAW,EAAE,UAAU,MAAM,SAAS,IAAI,CAAC;GACvD;EACF,CAAC,CACH;CACF;CAEA,MAAM,IAAI,MAAgD;EACxD,MAAM,SAAS,KAAK,UAAU;EAE9B,IAAI;GACF,MAAM,WAAW,MAAM,OAAO,KAC5B,IAAI,iBAAiB;IACnB,QAAQ,KAAK;IACb,KAAK,KAAK,MAAM,IAAI;GACtB,CAAC,CACH;GAEA,MAAM,OAAO,MAAM,SAAS,MAAM,kBAAkB,OAAO;GAC3D,IAAI,SAAS,KAAA,KAAa,SAAS,MAAM,OAAO;GAEhD,MAAM,WAAW,SAAS,YAAY,CAAC;GACvC,OAAO;IACL;IACA,SAAS;IACT,MAAM,SAAS,QAAQ,OAAO,OAAO,SAAS,IAAI,IAAI,OAAO,WAAW,MAAM,OAAO;IACrF,UAAU,SAAS,YAAY,SAAS,eAAe,KAAA;IACvD,WAAW,SAAS,YAAY,IAAI,KAAK,SAAS,SAAS,oBAAI,IAAI,KAAK;GAC1E;EACF,SAAS,OAAgB;GACvB,IAAI,gBAAgB,KAAK,GAAG,OAAO;GACnC,MAAM;EACR;CACF;CAEA,MAAM,IAAI,MAAgC;EACxC,MAAM,SAAS,KAAK,UAAU;EAE9B,IAAI;GACF,MAAM,OAAO,KACX,IAAI,kBAAkB;IACpB,QAAQ,KAAK;IACb,KAAK,KAAK,MAAM,IAAI;GACtB,CAAC,CACH;GACA,OAAO;EACT,SAAS,OAAgB;GACvB,IAAI,gBAAgB,KAAK,GAAG,OAAO;GACnC,MAAM;EACR;CACF;CAEA,MAAM,OAAO,MAAgC;EAK3C,IAAI,CAAC,MADiB,KAAK,IAAI,IAAI,GACrB,OAAO;EAGrB,MADe,KAAK,UACT,CAAC,CAAC,KACX,IAAI,oBAAoB;GACtB,QAAQ,KAAK;GACb,KAAK,KAAK,MAAM,IAAI;EACtB,CAAC,CACH;EACA,OAAO;CACT;CAEA,MAAM,QAAQ,SAA4C;EACxD,IAAI,QAAQ,WAAW,GAAG;EAG1B,MAAM,QAAQ,IAAI,QAAQ,KAAI,UAAS,KAAK,IAAI,KAAK,CAAC,CAAC;CACzD;CAEA,MAAM,QAAQ,QAA0D;EACtE,MAAM,yBAAS,IAAI,IAA8B;EACjD,IAAI,OAAO,WAAW,GAAG,OAAO;EAGhC,MAAM,UAAU,MAAM,QAAQ,IAAI,OAAO,KAAI,SAAQ,KAAK,IAAI,IAAI,CAAC,CAAC;EACpE,KAAK,MAAM,SAAS,SAClB,IAAI,OACF,OAAO,IAAI,MAAM,MAAM,KAAK;EAGhC,OAAO;CACT;CAEA,MAAM,sBAAqC;EACzC,MAAM,SAAS,KAAK,UAAU;EAE9B,IAAI;EACJ,GAAG;GACD,MAAM,eAAe,MAAM,OAAO,KAChC,IAAI,qBAAqB;IACvB,QAAQ,KAAK;IACb,QAAQ,KAAK;IACb,mBAAmB;GACrB,CAAC,CACH;GAEA,MAAM,UAAU,aAAa;GAC7B,IAAI,WAAW,QAAQ,SAAS,GAC9B,MAAM,OAAO,KACX,IAAI,qBAAqB;IACvB,QAAQ,KAAK;IACb,QAAQ;KACN,SAAS,QAAQ,QAAO,QAAO,IAAI,OAAO,IAAI,CAAC,CAAC,KAAI,SAAQ,EAAE,KAAK,IAAI,IAAK,EAAE;KAC9E,OAAO;IACT;GACF,CAAC,CACH;GAGF,oBAAoB,aAAa,cAAc,aAAa,wBAAwB,KAAA;EACtF,SAAS;CACX;AACF;AAEA,SAAS,gBAAgB,OAAyB;CAChD,IAAI,CAAC,SAAS,OAAO,UAAU,YAAY,EAAE,UAAU,QAAQ,OAAO;CACtE,MAAM,OAAQ,MAA2B;CACzC,OAAO,SAAS,cAAc,SAAS,eAAe,SAAS;AACjE;;;ACxRA,MAAa,uBAAgE;CAC3E,IAAI;CACJ,MAAM;CACN,aAAa;CACb,cAAc;EACZ,MAAM;EACN,UAAU,CAAC,UAAU,QAAQ;EAC7B,YAAY;GACV,QAAQ;IAAE,MAAM;IAAU,aAAa;GAAiB;GACxD,QAAQ;IAAE,MAAM;IAAU,aAAa;GAAiC;GACxE,aAAa;IAAE,MAAM;IAAU,aAAa;GAAoB;GAChE,iBAAiB;IAAE,MAAM;IAAU,aAAa;GAAwB;GACxE,cAAc;IAAE,MAAM;IAAU,aAAa;GAA8C;GAC3F,UAAU;IAAE,MAAM;IAAU,aAAa;GAAgD;GACzF,gBAAgB;IAAE,MAAM;IAAW,aAAa;IAAyB,SAAS;GAAM;GACxF,QAAQ;IAAE,MAAM;IAAU,aAAa;GAAwC;GAC/E,UAAU;IAAE,MAAM;IAAW,aAAa;IAAsB,SAAS;GAAM;EACjF;CACF;CACA,mBAAkB,WAAU,IAAI,aAAa,MAAM;AACrD;;;;;;;;;;;;;AAcA,MAAa,sBAA6D;CACxE,IAAI;CACJ,MAAM;CACN,aAAa;CACb,cAAc;EACZ,MAAM;EACN,UAAU;GAAC;GAAU;GAAU;GAAe;EAAiB;EAC/D,YAAY;GACV,QAAQ;IAAE,MAAM;IAAU,aAAa;GAAiB;GACxD,QAAQ;IAAE,MAAM;IAAU,aAAa;GAAiC;GACxE,aAAa;IAAE,MAAM;IAAU,aAAa;GAAoB;GAChE,iBAAiB;IAAE,MAAM;IAAU,aAAa;GAAwB;GACxE,cAAc;IAAE,MAAM;IAAU,aAAa;GAA8C;GAC3F,UAAU;IAAE,MAAM;IAAU,aAAa;GAAgD;GACzF,gBAAgB;IAAE,MAAM;IAAW,aAAa;IAAyB,SAAS;GAAM;GACxF,QAAQ;IAAE,MAAM;IAAU,aAAa;GAA6D;EACtG;CACF;CACA,kBAAiB,WAAU,IAAI,YAAY,MAAM;AACnD"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@mastra/s3",
|
|
3
|
-
"version": "0.6.1
|
|
3
|
+
"version": "0.6.1",
|
|
4
4
|
"description": "S3-compatible filesystem provider for Mastra workspaces (AWS S3, Cloudflare R2, MinIO)",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "dist/index.js",
|
|
@@ -31,10 +31,10 @@
|
|
|
31
31
|
"tsdown": "0.22.9",
|
|
32
32
|
"typescript": "^6.0.3",
|
|
33
33
|
"vitest": "4.1.10",
|
|
34
|
-
"@internal/lint": "0.0.
|
|
35
|
-
"@internal/types-builder": "0.0.
|
|
36
|
-
"@
|
|
37
|
-
"@
|
|
34
|
+
"@internal/lint": "0.0.122",
|
|
35
|
+
"@internal/types-builder": "0.0.97",
|
|
36
|
+
"@mastra/core": "1.58.0",
|
|
37
|
+
"@internal/workspace-test-utils": "0.0.66"
|
|
38
38
|
},
|
|
39
39
|
"peerDependencies": {
|
|
40
40
|
"@aws-sdk/types": "^3.0.0",
|