@aigne/afs 1.1.2 → 1.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +9 -0
- package/README.md +135 -111
- package/lib/cjs/afs.d.ts +9 -9
- package/lib/cjs/afs.js +31 -27
- package/lib/cjs/index.d.ts +0 -2
- package/lib/cjs/index.js +0 -2
- package/lib/cjs/type.d.ts +16 -5
- package/lib/dts/afs.d.ts +9 -9
- package/lib/dts/index.d.ts +0 -2
- package/lib/dts/type.d.ts +16 -5
- package/lib/esm/afs.d.ts +9 -9
- package/lib/esm/afs.js +31 -27
- package/lib/esm/index.d.ts +0 -2
- package/lib/esm/index.js +0 -2
- package/lib/esm/type.d.ts +16 -5
- package/package.json +3 -5
- package/lib/cjs/history/index.d.ts +0 -20
- package/lib/cjs/history/index.js +0 -47
- package/lib/cjs/storage/index.d.ts +0 -24
- package/lib/cjs/storage/index.js +0 -72
- package/lib/cjs/storage/migrate.d.ts +0 -3
- package/lib/cjs/storage/migrate.js +0 -31
- package/lib/cjs/storage/migrations/001-init.d.ts +0 -2
- package/lib/cjs/storage/migrations/001-init.js +0 -25
- package/lib/cjs/storage/models/entries.d.ts +0 -199
- package/lib/cjs/storage/models/entries.js +0 -28
- package/lib/cjs/storage/type.d.ts +0 -23
- package/lib/cjs/storage/type.js +0 -2
- package/lib/dts/history/index.d.ts +0 -20
- package/lib/dts/storage/index.d.ts +0 -24
- package/lib/dts/storage/migrate.d.ts +0 -3
- package/lib/dts/storage/migrations/001-init.d.ts +0 -2
- package/lib/dts/storage/models/entries.d.ts +0 -199
- package/lib/dts/storage/type.d.ts +0 -23
- package/lib/esm/history/index.d.ts +0 -20
- package/lib/esm/history/index.js +0 -43
- package/lib/esm/storage/index.d.ts +0 -24
- package/lib/esm/storage/index.js +0 -67
- package/lib/esm/storage/migrate.d.ts +0 -3
- package/lib/esm/storage/migrate.js +0 -28
- package/lib/esm/storage/migrations/001-init.d.ts +0 -2
- package/lib/esm/storage/migrations/001-init.js +0 -22
- package/lib/esm/storage/models/entries.d.ts +0 -199
- package/lib/esm/storage/models/entries.js +0 -23
- package/lib/esm/storage/type.d.ts +0 -23
- package/lib/esm/storage/type.js +0 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,14 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## [1.2.0](https://github.com/AIGNE-io/aigne-framework/compare/afs-v1.2.0-beta...afs-v1.2.0) (2025-11-14)
|
|
4
|
+
|
|
5
|
+
## [1.2.0-beta](https://github.com/AIGNE-io/aigne-framework/compare/afs-v1.1.2...afs-v1.2.0-beta) (2025-11-14)
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
### Features
|
|
9
|
+
|
|
10
|
+
* 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))
|
|
11
|
+
|
|
3
12
|
## [1.1.2](https://github.com/AIGNE-io/aigne-framework/compare/afs-v1.1.2-beta...afs-v1.1.2) (2025-11-12)
|
|
4
13
|
|
|
5
14
|
|
package/README.md
CHANGED
|
@@ -1,19 +1,17 @@
|
|
|
1
1
|
# @aigne/afs
|
|
2
2
|
|
|
3
|
-
**@aigne/afs** is the core package of the
|
|
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,
|
|
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
|
|
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
|
|
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
|
|
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('/
|
|
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
|
-
|
|
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
|
|
113
|
+
new AFS(options?: AFSOptions)
|
|
106
114
|
```
|
|
107
115
|
|
|
108
116
|
Options:
|
|
109
|
-
- `
|
|
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
|
-
#####
|
|
121
|
+
##### mount(module: AFSModule)
|
|
122
|
+
##### mount(path: string, module: AFSModule)
|
|
115
123
|
|
|
116
|
-
Mount a module
|
|
124
|
+
Mount a module. The module will be accessible under `/modules/{module.name}` or `/modules/{path}`:
|
|
117
125
|
|
|
118
126
|
```typescript
|
|
119
|
-
|
|
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('/
|
|
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
|
-
|
|
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
|
-
|
|
177
|
-
|
|
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
|
-
|
|
182
|
-
- `
|
|
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
|
|
214
|
+
The history module tracks conversation history. It is available as a separate package: `@aigne/afs-history`.
|
|
189
215
|
|
|
190
216
|
**Features:**
|
|
191
|
-
-
|
|
217
|
+
- Listens to `agentSucceed` events and records agent interactions
|
|
192
218
|
- Stores input/output pairs with UUID paths
|
|
193
|
-
-
|
|
194
|
-
-
|
|
219
|
+
- Supports list and read operations
|
|
220
|
+
- Can be extended with search capabilities
|
|
221
|
+
- Persistent SQLite storage
|
|
195
222
|
|
|
196
|
-
**
|
|
223
|
+
**Installation:**
|
|
224
|
+
```bash
|
|
225
|
+
npm install @aigne/afs-history
|
|
226
|
+
```
|
|
197
227
|
|
|
198
|
-
|
|
228
|
+
**Usage:**
|
|
199
229
|
|
|
200
230
|
```typescript
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
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
|
-
**
|
|
243
|
+
**Configuration:**
|
|
244
|
+
- `storage`: Storage configuration (e.g., `{ url: "file:./memory.sqlite3" }`) or a SharedAFSStorage instance
|
|
208
245
|
|
|
209
|
-
|
|
210
|
-
import { AIAgent, AIGNE } from "@aigne/core";
|
|
246
|
+
**Note:** History is NOT automatically mounted. You must explicitly mount it if needed.
|
|
211
247
|
|
|
212
|
-
|
|
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,
|
|
255
|
+
import { AFSModule, AFSEntry, AFSListOptions } from "@aigne/afs";
|
|
228
256
|
|
|
229
257
|
export class CustomModule implements AFSModule {
|
|
230
|
-
readonly
|
|
231
|
-
readonly path = "/custom";
|
|
258
|
+
readonly name = "custom-module";
|
|
232
259
|
readonly description = "My custom storage";
|
|
233
260
|
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
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
|
-
|
|
243
|
-
return { result:
|
|
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
|
-
|
|
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
|
-
|
|
253
|
-
|
|
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.
|
|
284
|
+
console.log(`${this.name} mounted`);
|
|
260
285
|
|
|
261
286
|
// Listen to events
|
|
262
|
-
afs.on('
|
|
287
|
+
afs.on('agentSucceed', (data) => {
|
|
263
288
|
// Handle event
|
|
264
289
|
});
|
|
265
290
|
}
|
|
266
291
|
}
|
|
267
292
|
|
|
268
|
-
//
|
|
269
|
-
afs.
|
|
293
|
+
// Mount the module
|
|
294
|
+
afs.mount(new CustomModule());
|
|
295
|
+
// Now accessible at /modules/custom-module
|
|
270
296
|
```
|
|
271
297
|
|
|
272
|
-
##
|
|
298
|
+
## Module Path Resolution
|
|
273
299
|
|
|
274
|
-
|
|
300
|
+
When a module is mounted, AFS handles path resolution automatically:
|
|
275
301
|
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
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(
|
|
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-
|
|
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
|
-
|
|
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
|
-
|
|
11
|
-
path: string;
|
|
7
|
+
name: string;
|
|
12
8
|
constructor(options?: AFSOptions);
|
|
13
|
-
private _storage;
|
|
14
|
-
storage(module: AFSModule): AFSStorage;
|
|
15
9
|
private modules;
|
|
16
|
-
|
|
10
|
+
mount(module: AFSModule): this;
|
|
17
11
|
listModules(): Promise<{
|
|
18
|
-
|
|
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
|
-
|
|
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.
|
|
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
|
-
|
|
29
|
-
|
|
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.
|
|
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.
|
|
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.
|
|
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)(
|
|
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.
|
|
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)(
|
|
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 ${
|
|
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;
|
package/lib/cjs/index.d.ts
CHANGED
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);
|