@aigne/afs 1.1.2 → 1.2.0-beta

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (46) hide show
  1. package/CHANGELOG.md +7 -0
  2. package/README.md +135 -111
  3. package/lib/cjs/afs.d.ts +9 -9
  4. package/lib/cjs/afs.js +31 -27
  5. package/lib/cjs/index.d.ts +0 -2
  6. package/lib/cjs/index.js +0 -2
  7. package/lib/cjs/type.d.ts +16 -5
  8. package/lib/dts/afs.d.ts +9 -9
  9. package/lib/dts/index.d.ts +0 -2
  10. package/lib/dts/type.d.ts +16 -5
  11. package/lib/esm/afs.d.ts +9 -9
  12. package/lib/esm/afs.js +31 -27
  13. package/lib/esm/index.d.ts +0 -2
  14. package/lib/esm/index.js +0 -2
  15. package/lib/esm/type.d.ts +16 -5
  16. package/package.json +3 -5
  17. package/lib/cjs/history/index.d.ts +0 -20
  18. package/lib/cjs/history/index.js +0 -47
  19. package/lib/cjs/storage/index.d.ts +0 -24
  20. package/lib/cjs/storage/index.js +0 -72
  21. package/lib/cjs/storage/migrate.d.ts +0 -3
  22. package/lib/cjs/storage/migrate.js +0 -31
  23. package/lib/cjs/storage/migrations/001-init.d.ts +0 -2
  24. package/lib/cjs/storage/migrations/001-init.js +0 -25
  25. package/lib/cjs/storage/models/entries.d.ts +0 -199
  26. package/lib/cjs/storage/models/entries.js +0 -28
  27. package/lib/cjs/storage/type.d.ts +0 -23
  28. package/lib/cjs/storage/type.js +0 -2
  29. package/lib/dts/history/index.d.ts +0 -20
  30. package/lib/dts/storage/index.d.ts +0 -24
  31. package/lib/dts/storage/migrate.d.ts +0 -3
  32. package/lib/dts/storage/migrations/001-init.d.ts +0 -2
  33. package/lib/dts/storage/models/entries.d.ts +0 -199
  34. package/lib/dts/storage/type.d.ts +0 -23
  35. package/lib/esm/history/index.d.ts +0 -20
  36. package/lib/esm/history/index.js +0 -43
  37. package/lib/esm/storage/index.d.ts +0 -24
  38. package/lib/esm/storage/index.js +0 -67
  39. package/lib/esm/storage/migrate.d.ts +0 -3
  40. package/lib/esm/storage/migrate.js +0 -28
  41. package/lib/esm/storage/migrations/001-init.d.ts +0 -2
  42. package/lib/esm/storage/migrations/001-init.js +0 -22
  43. package/lib/esm/storage/models/entries.d.ts +0 -199
  44. package/lib/esm/storage/models/entries.js +0 -23
  45. package/lib/esm/storage/type.d.ts +0 -23
  46. package/lib/esm/storage/type.js +0 -1
package/CHANGELOG.md CHANGED
@@ -1,5 +1,12 @@
1
1
  # Changelog
2
2
 
3
+ ## [1.2.0-beta](https://github.com/AIGNE-io/aigne-framework/compare/afs-v1.1.2...afs-v1.2.0-beta) (2025-11-14)
4
+
5
+
6
+ ### Features
7
+
8
+ * support mount mcp agent into AFS ([#740](https://github.com/AIGNE-io/aigne-framework/issues/740)) ([6d474fc](https://github.com/AIGNE-io/aigne-framework/commit/6d474fc05845a15e2c3e8fa97727b409bdd70945))
9
+
3
10
  ## [1.1.2](https://github.com/AIGNE-io/aigne-framework/compare/afs-v1.1.2-beta...afs-v1.1.2) (2025-11-12)
4
11
 
5
12
 
package/README.md CHANGED
@@ -1,19 +1,17 @@
1
1
  # @aigne/afs
2
2
 
3
- **@aigne/afs** is the core package of the AIGNE File System (AFS), providing a virtual file system abstraction layer that enables AI agents to access various storage backends through a unified, path-based API.
3
+ **@aigne/afs** is the core package of the Agentic File System (AFS), providing a virtual file system abstraction layer that enables AI agents to access various storage backends through a unified, path-based API.
4
4
 
5
5
  ## Overview
6
6
 
7
- AFS Core provides the foundational infrastructure for building virtual file systems that can integrate with different storage backends. It includes the base AFS implementation, storage layer abstraction, and the built-in history module for automatic conversation tracking.
7
+ AFS Core provides the foundational infrastructure for building virtual file systems that can integrate with different storage backends. It includes the base AFS implementation, module mounting system, and event-driven architecture for building modular storage solutions.
8
8
 
9
9
  ## Features
10
10
 
11
- - **Virtual File System**: Hierarchical path-based structure similar to Unix file systems
11
+ - **Virtual File System**: Hierarchical path-based structure with `/modules` root directory
12
12
  - **Module System**: Pluggable architecture for custom storage backends
13
- - **Unified API**: Consistent interface for list, read, write, and search operations
13
+ - **Unified API**: Consistent interface for list, read, write, search, and exec operations
14
14
  - **Event System**: Event-driven architecture for module communication
15
- - **SQLite Storage**: Built-in persistent storage using SQLite
16
- - **History Tracking**: Automatic conversation history recording (AFSHistory module)
17
15
  - **AI Agent Integration**: Seamless integration with AIGNE agents
18
16
 
19
17
  ## Installation
@@ -30,26 +28,34 @@ pnpm add @aigne/afs
30
28
 
31
29
  ```typescript
32
30
  import { AFS } from "@aigne/afs";
31
+ import { AFSHistory } from "@aigne/afs-history";
33
32
 
34
- // Create AFS instance with SQLite storage
35
- const afs = new AFS({
33
+ // Create AFS instance
34
+ const afs = new AFS();
35
+
36
+ // Mount history module (optional)
37
+ afs.mount(new AFSHistory({
36
38
  storage: { url: "file:./memory.sqlite3" }
37
- });
39
+ }));
40
+
41
+ // All modules are mounted under /modules
42
+ // List modules
43
+ const modules = await afs.listModules();
38
44
 
39
- // List entries
40
- const { list } = await afs.list('/');
45
+ // List entries in a module
46
+ const { list } = await afs.list('/modules/history');
41
47
 
42
48
  // Read an entry
43
- const { result } = await afs.read('/history/some-id');
49
+ const { result } = await afs.read('/modules/history/some-id');
44
50
 
45
- // Write an entry
46
- await afs.write('/my-data/notes.txt', {
51
+ // Write an entry (if module supports write)
52
+ await afs.write('/modules/history/notes', {
47
53
  content: 'My notes',
48
54
  summary: 'Personal notes'
49
55
  });
50
56
 
51
57
  // Search for content
52
- const { list: results } = await afs.search('/', 'search query');
58
+ const { list: results } = await afs.search('/modules/history', 'search query');
53
59
  ```
54
60
 
55
61
  ## Core Concepts
@@ -75,19 +81,19 @@ interface AFSEntry {
75
81
 
76
82
  ### Modules
77
83
 
78
- Modules are pluggable components that implement storage backends:
84
+ Modules are pluggable components that implement storage backends. All modules are automatically mounted under the `/modules` path prefix:
79
85
 
80
86
  ```typescript
81
87
  interface AFSModule {
82
- moduleId: string; // Unique module identifier
83
- path: string; // Mount path (e.g., '/history')
88
+ name: string; // Module name (used as mount path)
84
89
  description?: string; // Description for AI agents
85
90
 
86
91
  // Operations (all optional)
87
- list?(path: string, options?: AFSListOptions): Promise<{ list: AFSEntry[] }>;
88
- read?(path: string): Promise<{ result?: AFSEntry }>;
89
- write?(path: string, entry: AFSWriteEntryPayload): Promise<{ result: AFSEntry }>;
90
- search?(path: string, query: string, options?: AFSSearchOptions): Promise<{ list: AFSEntry[] }>;
92
+ list?(path: string, options?: AFSListOptions): Promise<{ list: AFSEntry[]; message?: string }>;
93
+ read?(path: string): Promise<{ result?: AFSEntry; message?: string }>;
94
+ write?(path: string, entry: AFSWriteEntryPayload): Promise<{ result: AFSEntry; message?: string }>;
95
+ search?(path: string, query: string, options?: AFSSearchOptions): Promise<{ list: AFSEntry[]; message?: string }>;
96
+ exec?(path: string, args: Record<string, any>, options: { context: any }): Promise<{ result: Record<string, any> }>;
91
97
 
92
98
  // Lifecycle
93
99
  onMount?(afs: AFSRoot): void;
@@ -95,6 +101,8 @@ interface AFSModule {
95
101
  }
96
102
  ```
97
103
 
104
+ **Mount Path Convention**: When you mount a module with name `"my-module"`, it will be accessible at `/modules/my-module`.
105
+
98
106
  ## API Reference
99
107
 
100
108
  ### AFS Class
@@ -102,21 +110,34 @@ interface AFSModule {
102
110
  #### Constructor
103
111
 
104
112
  ```typescript
105
- new AFS(options: AFSOptions)
113
+ new AFS(options?: AFSOptions)
106
114
  ```
107
115
 
108
116
  Options:
109
- - `storage`: Storage configuration (e.g., `{ url: "file:./memory.sqlite3" }`)
110
- - `historyEnabled`: Enable/disable automatic history tracking (default: `true`)
117
+ - `modules`: Optional array of modules to mount on initialization
111
118
 
112
119
  #### Methods
113
120
 
114
- ##### use(module: AFSModule)
121
+ ##### mount(module: AFSModule)
122
+ ##### mount(path: string, module: AFSModule)
115
123
 
116
- Mount a module at its specified path:
124
+ Mount a module. The module will be accessible under `/modules/{module.name}` or `/modules/{path}`:
117
125
 
118
126
  ```typescript
119
- afs.use(new CustomModule());
127
+ // Mount using module's name
128
+ afs.mount(new CustomModule());
129
+
130
+ // Mount with custom path (advanced usage)
131
+ afs.mount("/custom-path", new CustomModule());
132
+ ```
133
+
134
+ ##### listModules()
135
+
136
+ Get all mounted modules:
137
+
138
+ ```typescript
139
+ const modules = await afs.listModules();
140
+ // Returns: [{ name: string, path: string, description?: string, module: AFSModule }]
120
141
  ```
121
142
 
122
143
  ##### list(path: string, options?: AFSListOptions)
@@ -124,26 +145,20 @@ afs.use(new CustomModule());
124
145
  List entries in a directory:
125
146
 
126
147
  ```typescript
127
- const { list, message } = await afs.list('/history', {
128
- maxDepth: 2,
129
- recursive: true,
130
- limit: 10,
131
- orderBy: [['createdAt', 'desc']]
148
+ const { list, message } = await afs.list('/modules/history', {
149
+ maxDepth: 2
132
150
  });
133
151
  ```
134
152
 
135
153
  Options:
136
- - `maxDepth`: Maximum recursion depth
137
- - `recursive`: Enable recursive listing
138
- - `limit`: Maximum number of results
139
- - `orderBy`: Sort order (array of `[field, direction]` tuples)
154
+ - `maxDepth`: Maximum recursion depth (default: 1)
140
155
 
141
156
  ##### read(path: string)
142
157
 
143
158
  Read a specific entry:
144
159
 
145
160
  ```typescript
146
- const { result, message } = await afs.read('/history/uuid-123');
161
+ const { result, message } = await afs.read('/modules/history/uuid-123');
147
162
  ```
148
163
 
149
164
  ##### write(path: string, content: AFSWriteEntryPayload)
@@ -151,7 +166,7 @@ const { result, message } = await afs.read('/history/uuid-123');
151
166
  Write or update an entry:
152
167
 
153
168
  ```typescript
154
- const { result, message } = await afs.write('/data/file.txt', {
169
+ const { result, message } = await afs.write('/modules/my-module/file.txt', {
155
170
  content: 'Hello, world!',
156
171
  summary: 'Greeting file',
157
172
  metadata: { type: 'greeting' }
@@ -163,9 +178,15 @@ const { result, message } = await afs.write('/data/file.txt', {
163
178
  Search for content:
164
179
 
165
180
  ```typescript
166
- const { list, message } = await afs.search('/history', 'authentication', {
167
- limit: 5
168
- });
181
+ const { list, message } = await afs.search('/modules/history', 'authentication');
182
+ ```
183
+
184
+ ##### exec(path: string, args: Record<string, any>, options: { context: any })
185
+
186
+ Execute a module-specific operation:
187
+
188
+ ```typescript
189
+ const { result } = await afs.exec('/modules/my-module/action', { param: 'value' }, { context });
169
190
  ```
170
191
 
171
192
  ### Events
@@ -173,117 +194,116 @@ const { list, message } = await afs.search('/history', 'authentication', {
173
194
  AFS uses an event system for module communication:
174
195
 
175
196
  ```typescript
176
- afs.on('historyCreated', ({ entry }) => {
177
- console.log('New history entry:', entry);
197
+ // Modules can listen to events
198
+ afs.on('agentSucceed', ({ input, output }) => {
199
+ console.log('Agent succeeded:', input, output);
178
200
  });
201
+
202
+ // Modules can emit custom events
203
+ afs.emit('customEvent', { data: 'value' });
179
204
  ```
180
205
 
181
- Available events:
182
- - `historyCreated`: Emitted when a new history entry is created
206
+ Common events from `AFSRootEvents`:
207
+ - `agentSucceed`: Emitted when an agent successfully completes
208
+ - `agentFail`: Emitted when an agent fails
183
209
 
184
210
  ## Built-in Modules
185
211
 
186
212
  ### AFSHistory
187
213
 
188
- The history module automatically tracks conversation history.
214
+ The history module tracks conversation history. It is available as a separate package: `@aigne/afs-history`.
189
215
 
190
216
  **Features:**
191
- - Automatically records agent interactions
217
+ - Listens to `agentSucceed` events and records agent interactions
192
218
  - Stores input/output pairs with UUID paths
193
- - Enables conversation history injection into agent prompts
194
- - Supports semantic search
219
+ - Supports list and read operations
220
+ - Can be extended with search capabilities
221
+ - Persistent SQLite storage
195
222
 
196
- **Usage:**
223
+ **Installation:**
224
+ ```bash
225
+ npm install @aigne/afs-history
226
+ ```
197
227
 
198
- History is enabled by default. To disable:
228
+ **Usage:**
199
229
 
200
230
  ```typescript
201
- const afs = new AFS({
202
- storage: { url: "file:./memory.sqlite3" },
203
- historyEnabled: false
204
- });
231
+ import { AFS } from "@aigne/afs";
232
+ import { AFSHistory } from "@aigne/afs-history";
233
+
234
+ const afs = new AFS();
235
+ afs.mount(new AFSHistory({
236
+ storage: { url: "file:./memory.sqlite3" }
237
+ }));
238
+
239
+ // History entries are accessible at /modules/history
240
+ const { list } = await afs.list('/modules/history');
205
241
  ```
206
242
 
207
- **Integration with AI Agents:**
243
+ **Configuration:**
244
+ - `storage`: Storage configuration (e.g., `{ url: "file:./memory.sqlite3" }`) or a SharedAFSStorage instance
208
245
 
209
- ```typescript
210
- import { AIAgent, AIGNE } from "@aigne/core";
246
+ **Note:** History is NOT automatically mounted. You must explicitly mount it if needed.
211
247
 
212
- const agent = AIAgent.from({
213
- name: "assistant",
214
- afs: afs,
215
- afsConfig: {
216
- injectHistory: true, // Inject history into prompts
217
- historyWindowSize: 10 // Number of recent entries
218
- }
219
- });
220
- ```
248
+ **Documentation:** See [@aigne/afs-history](../history/README.md) for detailed documentation.
221
249
 
222
250
  ## Creating Custom Modules
223
251
 
224
252
  Create a custom module by implementing the `AFSModule` interface:
225
253
 
226
254
  ```typescript
227
- import { AFSModule, AFSEntry, AFSStorage } from "@aigne/afs";
255
+ import { AFSModule, AFSEntry, AFSListOptions } from "@aigne/afs";
228
256
 
229
257
  export class CustomModule implements AFSModule {
230
- readonly moduleId = "custom-module";
231
- readonly path = "/custom";
258
+ readonly name = "custom-module";
232
259
  readonly description = "My custom storage";
233
260
 
234
- constructor(private storage: AFSStorage) {}
235
-
236
- async list(path: string, options?: AFSListOptions) {
237
- const entries = await this.storage.list(options);
238
- return { list: entries };
261
+ async list(path: string, options?: AFSListOptions): Promise<{ list: AFSEntry[]; message?: string }> {
262
+ // path is the subpath within your module
263
+ // Implement your list logic
264
+ return { list: [] };
239
265
  }
240
266
 
241
- async read(path: string) {
242
- const entry = await this.storage.read(path);
243
- return { result: entry };
267
+ async read(path: string): Promise<{ result?: AFSEntry; message?: string }> {
268
+ // Implement your read logic
269
+ return { result: undefined };
244
270
  }
245
271
 
246
- async write(path: string, content: AFSWriteEntryPayload) {
247
- const entry = await this.storage.create({ ...content, path });
272
+ async write(path: string, content: AFSWriteEntryPayload): Promise<{ result: AFSEntry; message?: string }> {
273
+ // Implement your write logic
274
+ const entry: AFSEntry = { id: 'id', path, ...content };
248
275
  return { result: entry };
249
276
  }
250
277
 
251
- async search(path: string, query: string, options?: AFSSearchOptions) {
252
- const results = await this.storage.list({
253
- where: { content: { contains: query } }
254
- });
255
- return { list: results };
278
+ async search(path: string, query: string, options?: AFSSearchOptions): Promise<{ list: AFSEntry[]; message?: string }> {
279
+ // Implement your search logic
280
+ return { list: [] };
256
281
  }
257
282
 
258
283
  onMount(afs: AFSRoot) {
259
- console.log(`${this.moduleId} mounted at ${this.path}`);
284
+ console.log(`${this.name} mounted`);
260
285
 
261
286
  // Listen to events
262
- afs.on('someEvent', (data) => {
287
+ afs.on('agentSucceed', (data) => {
263
288
  // Handle event
264
289
  });
265
290
  }
266
291
  }
267
292
 
268
- // Use the module
269
- afs.use(new CustomModule());
293
+ // Mount the module
294
+ afs.mount(new CustomModule());
295
+ // Now accessible at /modules/custom-module
270
296
  ```
271
297
 
272
- ## Storage Layer
298
+ ## Module Path Resolution
273
299
 
274
- AFS Core includes a SQLite-based storage layer that modules can use:
300
+ When a module is mounted, AFS handles path resolution automatically:
275
301
 
276
- ```typescript
277
- // Get module-specific storage
278
- const storage = afs.storage(myModule);
279
-
280
- // Storage operations
281
- await storage.create({ path: '/data', content: 'value' });
282
- await storage.read('/data');
283
- await storage.update('/data', { content: 'new value' });
284
- await storage.delete('/data');
285
- await storage.list({ where: { path: { startsWith: '/data' } } });
286
- ```
302
+ 1. Module mounted with name `"my-module"` → accessible at `/modules/my-module`
303
+ 2. When listing `/modules`, AFS shows all mounted modules
304
+ 3. When accessing `/modules/my-module/foo`, the module receives `"/foo"` as the path parameter
305
+
306
+ This allows modules to focus on their internal logic without worrying about mount paths.
287
307
 
288
308
  ## Integration with AI Agents
289
309
 
@@ -293,22 +313,23 @@ When an agent has AFS configured, these tools are automatically registered:
293
313
  - **afs_read**: Read file contents
294
314
  - **afs_write**: Write/create files
295
315
  - **afs_search**: Search for content
316
+ - **afs_exec**: Execute module operations
296
317
 
297
318
  Example:
298
319
 
299
320
  ```typescript
300
321
  import { AIAgent, AIGNE } from "@aigne/core";
301
322
  import { AFS } from "@aigne/afs";
323
+ import { AFSHistory } from "@aigne/afs-history";
302
324
 
303
- const afs = new AFS({ storage: { url: "file:./memory.sqlite3" } });
325
+ const afs = new AFS();
326
+ afs.mount(new AFSHistory({
327
+ storage: { url: "file:./memory.sqlite3" }
328
+ }));
304
329
 
305
330
  const agent = AIAgent.from({
306
331
  name: "assistant",
307
- afs: afs,
308
- afsConfig: {
309
- injectHistory: true,
310
- historyWindowSize: 10
311
- }
332
+ afs: afs
312
333
  });
313
334
 
314
335
  const context = aigne.newContext();
@@ -319,12 +340,15 @@ const result = await context.invoke(agent, {
319
340
 
320
341
  ## Related Packages
321
342
 
322
- - [@aigne/afs-system-fs](../system-fs/README.md) - Local file system module
343
+ - [@aigne/afs-history](../history/README.md) - History tracking module
344
+ - [@aigne/afs-local-fs](../local-fs/README.md) - Local file system module
323
345
  - [@aigne/afs-user-profile-memory](../user-profile-memory/README.md) - User profile memory module
324
346
 
325
347
  ## Examples
326
348
 
327
- See the [AFS examples](../../examples/afs-system-fs) for complete usage examples.
349
+ - [AFS Memory Example](../../examples/afs-memory/README.md) - Conversational memory with user profiles
350
+ - [AFS LocalFS Example](../../examples/afs-local-fs/README.md) - File system access with AI agents
351
+ - [AFS MCP Server Example](../../examples/afs-mcp-server/README.md) - Mount MCP servers as AFS modules
328
352
 
329
353
  ## TypeScript Support
330
354
 
package/lib/cjs/afs.d.ts CHANGED
@@ -1,23 +1,18 @@
1
1
  import { Emitter } from "strict-event-emitter";
2
- import { SharedAFSStorage, type SharedAFSStorageOptions } from "./storage/index.js";
3
- import type { AFSStorage } from "./storage/type.js";
4
2
  import type { AFSEntry, AFSListOptions, AFSModule, AFSRoot, AFSRootEvents, AFSSearchOptions, AFSWriteEntryPayload } from "./type.js";
5
3
  export interface AFSOptions {
6
- storage?: SharedAFSStorage | SharedAFSStorageOptions;
7
4
  modules?: AFSModule[];
8
5
  }
9
6
  export declare class AFS extends Emitter<AFSRootEvents> implements AFSRoot {
10
- moduleId: string;
11
- path: string;
7
+ name: string;
12
8
  constructor(options?: AFSOptions);
13
- private _storage;
14
- storage(module: AFSModule): AFSStorage;
15
9
  private modules;
16
- use(module: AFSModule): this;
10
+ mount(module: AFSModule): this;
17
11
  listModules(): Promise<{
18
- moduleId: string;
12
+ name: string;
19
13
  path: string;
20
14
  description?: string;
15
+ module: AFSModule;
21
16
  }[]>;
22
17
  list(path: string, options?: AFSListOptions): Promise<{
23
18
  list: AFSEntry[];
@@ -36,4 +31,9 @@ export declare class AFS extends Emitter<AFSRootEvents> implements AFSRoot {
36
31
  message?: string;
37
32
  }>;
38
33
  private findModules;
34
+ exec(path: string, args: Record<string, any>, options: {
35
+ context: any;
36
+ }): Promise<{
37
+ result: Record<string, any>;
38
+ }>;
39
39
  }
package/lib/cjs/afs.js CHANGED
@@ -3,38 +3,36 @@ Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.AFS = void 0;
4
4
  const strict_event_emitter_1 = require("strict-event-emitter");
5
5
  const ufo_1 = require("ufo");
6
- const index_js_1 = require("./history/index.js");
7
- const index_js_2 = require("./storage/index.js");
8
6
  const DEFAULT_MAX_DEPTH = 1;
7
+ const MODULES_ROOT_DIR = "/modules";
9
8
  class AFS extends strict_event_emitter_1.Emitter {
10
- moduleId = "AFSRoot";
11
- path = "/";
9
+ name = "AFSRoot";
12
10
  constructor(options) {
13
11
  super();
14
- this._storage =
15
- options?.storage instanceof index_js_2.SharedAFSStorage
16
- ? options.storage
17
- : new index_js_2.SharedAFSStorage(options?.storage);
18
- this.use(new index_js_1.AFSHistory());
19
12
  for (const module of options?.modules ?? []) {
20
- this.use(module);
13
+ this.mount(module);
21
14
  }
22
15
  }
23
- _storage;
24
- storage(module) {
25
- return this._storage.withModule(module);
26
- }
27
16
  modules = new Map();
28
- use(module) {
29
- this.modules.set(module.path, module);
17
+ mount(module) {
18
+ let path = (0, ufo_1.joinURL)("/", module.name);
19
+ if (!/^\/[^/]+$/.test(path)) {
20
+ throw new Error(`Invalid mount path: ${path}. Must start with '/' and contain no other '/'`);
21
+ }
22
+ if (this.modules.has(path)) {
23
+ throw new Error(`Module already mounted at path: ${path}`);
24
+ }
25
+ path = (0, ufo_1.joinURL)(MODULES_ROOT_DIR, path);
26
+ this.modules.set(path, module);
30
27
  module.onMount?.(this);
31
28
  return this;
32
29
  }
33
30
  async listModules() {
34
31
  return Array.from(this.modules.entries()).map(([path, module]) => ({
35
- moduleId: module.moduleId,
36
32
  path,
33
+ name: module.name,
37
34
  description: module.description,
35
+ module,
38
36
  }));
39
37
  }
40
38
  async list(path, options) {
@@ -46,7 +44,7 @@ class AFS extends strict_event_emitter_1.Emitter {
46
44
  const matches = this.findModules(path, options);
47
45
  for (const matched of matches) {
48
46
  const moduleEntry = {
49
- id: matched.module.moduleId,
47
+ id: matched.module.name,
50
48
  path: matched.remainedModulePath,
51
49
  summary: matched.module.description,
52
50
  };
@@ -64,7 +62,7 @@ class AFS extends strict_event_emitter_1.Emitter {
64
62
  if (list.length) {
65
63
  results.push(...list.map((entry) => ({
66
64
  ...entry,
67
- path: (0, ufo_1.joinURL)(matched.module.path, entry.path),
65
+ path: (0, ufo_1.joinURL)(matched.modulePath, entry.path),
68
66
  })));
69
67
  }
70
68
  else {
@@ -74,21 +72,21 @@ class AFS extends strict_event_emitter_1.Emitter {
74
72
  messages.push(message);
75
73
  }
76
74
  catch (error) {
77
- console.error(`Error listing from module at ${matched.module.path}`, error);
75
+ console.error(`Error listing from module at ${matched.modulePath}`, error);
78
76
  }
79
77
  }
80
78
  return { list: results, message: messages.join("; ").trim() || undefined };
81
79
  }
82
80
  async read(path) {
83
81
  const modules = this.findModules(path, { exactMatch: true });
84
- for (const { module, subpath } of modules) {
82
+ for (const { module, modulePath, subpath } of modules) {
85
83
  const res = await module.read?.(subpath);
86
84
  if (res?.result) {
87
85
  return {
88
86
  ...res,
89
87
  result: {
90
88
  ...res.result,
91
- path: (0, ufo_1.joinURL)(module.path, res.result.path),
89
+ path: (0, ufo_1.joinURL)(modulePath, res.result.path),
92
90
  },
93
91
  };
94
92
  }
@@ -104,27 +102,27 @@ class AFS extends strict_event_emitter_1.Emitter {
104
102
  ...res,
105
103
  result: {
106
104
  ...res.result,
107
- path: (0, ufo_1.joinURL)(module.module.path, res.result.path),
105
+ path: (0, ufo_1.joinURL)(module.modulePath, res.result.path),
108
106
  },
109
107
  };
110
108
  }
111
109
  async search(path, query, options) {
112
110
  const results = [];
113
111
  const messages = [];
114
- for (const { module, subpath } of this.findModules(path)) {
112
+ for (const { module, modulePath, subpath } of this.findModules(path)) {
115
113
  if (!module.search)
116
114
  continue;
117
115
  try {
118
116
  const { list, message } = await module.search(subpath, query, options);
119
117
  results.push(...list.map((entry) => ({
120
118
  ...entry,
121
- path: (0, ufo_1.joinURL)(module.path, entry.path),
119
+ path: (0, ufo_1.joinURL)(modulePath, entry.path),
122
120
  })));
123
121
  if (message)
124
122
  messages.push(message);
125
123
  }
126
124
  catch (error) {
127
- console.error(`Error searching in module at ${module.path}`, error);
125
+ console.error(`Error searching in module at ${modulePath}`, error);
128
126
  }
129
127
  }
130
128
  return { list: results, message: messages.join("; ") };
@@ -153,9 +151,15 @@ class AFS extends strict_event_emitter_1.Emitter {
153
151
  }
154
152
  if (newMaxDepth < 0)
155
153
  continue;
156
- matched.push({ module, maxDepth: newMaxDepth, subpath, remainedModulePath });
154
+ matched.push({ module, modulePath, maxDepth: newMaxDepth, subpath, remainedModulePath });
157
155
  }
158
156
  return matched;
159
157
  }
158
+ async exec(path, args, options) {
159
+ const module = this.findModules(path)[0];
160
+ if (!module?.module.exec)
161
+ throw new Error(`No module found for path: ${path}`);
162
+ return await module.module.exec(module.subpath, args, options);
163
+ }
160
164
  }
161
165
  exports.AFS = AFS;
@@ -1,4 +1,2 @@
1
1
  export * from "./afs.js";
2
- export * from "./history/index.js";
3
- export * from "./storage/index.js";
4
2
  export * from "./type.js";
package/lib/cjs/index.js CHANGED
@@ -15,6 +15,4 @@ var __exportStar = (this && this.__exportStar) || function(m, exports) {
15
15
  };
16
16
  Object.defineProperty(exports, "__esModule", { value: true });
17
17
  __exportStar(require("./afs.js"), exports);
18
- __exportStar(require("./history/index.js"), exports);
19
- __exportStar(require("./storage/index.js"), exports);
20
18
  __exportStar(require("./type.js"), exports);