@aigne/afs 1.4.0-beta.8 → 1.4.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 +70 -0
- package/lib/cjs/afs.d.ts +5 -0
- package/lib/cjs/afs.js +52 -18
- package/lib/cjs/error.d.ts +13 -0
- package/lib/cjs/error.js +25 -0
- package/lib/cjs/index.d.ts +1 -0
- package/lib/cjs/index.js +1 -0
- package/lib/cjs/type.d.ts +69 -17
- package/lib/cjs/type.js +9 -1
- package/lib/dts/afs.d.ts +5 -0
- package/lib/dts/error.d.ts +13 -0
- package/lib/dts/index.d.ts +1 -0
- package/lib/dts/type.d.ts +69 -17
- package/lib/esm/afs.d.ts +5 -0
- package/lib/esm/afs.js +52 -18
- package/lib/esm/error.d.ts +13 -0
- package/lib/esm/error.js +20 -0
- package/lib/esm/index.d.ts +1 -0
- package/lib/esm/index.js +1 -0
- package/lib/esm/type.d.ts +69 -17
- package/lib/esm/type.js +8 -0
- package/package.json +3 -2
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,75 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## [1.4.0](https://github.com/AIGNE-io/aigne-framework/compare/afs-v1.4.0-beta.11...afs-v1.4.0) (2026-01-16)
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
### Dependencies
|
|
7
|
+
|
|
8
|
+
* The following workspace dependencies were updated
|
|
9
|
+
* dependencies
|
|
10
|
+
* @aigne/platform-helpers bumped to 0.6.7
|
|
11
|
+
|
|
12
|
+
## [1.4.0-beta.11](https://github.com/AIGNE-io/aigne-framework/compare/afs-v1.4.0-beta.10...afs-v1.4.0-beta.11) (2026-01-16)
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
### Features
|
|
16
|
+
|
|
17
|
+
* add Agent Skill support ([#787](https://github.com/AIGNE-io/aigne-framework/issues/787)) ([f04fbe7](https://github.com/AIGNE-io/aigne-framework/commit/f04fbe76ec24cf3c59c74adf92d87b0c3784a8f7))
|
|
18
|
+
* add session compact support for AIAgent ([#863](https://github.com/AIGNE-io/aigne-framework/issues/863)) ([9010918](https://github.com/AIGNE-io/aigne-framework/commit/9010918cd3f18b02b5c60ddc9ed5c34b568d0b28))
|
|
19
|
+
* **afs,bash:** add physical path mapping for AFS modules in bash execution ([#881](https://github.com/AIGNE-io/aigne-framework/issues/881)) ([50dbda2](https://github.com/AIGNE-io/aigne-framework/commit/50dbda224bd666d951494d2449779830d8db57fc))
|
|
20
|
+
* **afs:** add basic AFS(AIGNE File System) support ([#505](https://github.com/AIGNE-io/aigne-framework/issues/505)) ([ac2a18a](https://github.com/AIGNE-io/aigne-framework/commit/ac2a18a82470a2f31c466f329386525eb1cdab6d))
|
|
21
|
+
* **afs:** add edit/delete/rename methods for AFS ([#820](https://github.com/AIGNE-io/aigne-framework/issues/820)) ([68cb508](https://github.com/AIGNE-io/aigne-framework/commit/68cb508d1cfc9c516d56303018139f1e567f897e))
|
|
22
|
+
* **afs:** add module access control and schema validation support ([#904](https://github.com/AIGNE-io/aigne-framework/issues/904)) ([d0b279a](https://github.com/AIGNE-io/aigne-framework/commit/d0b279aac07ebe2bcc1fd4148498fc3f6bbcd561))
|
|
23
|
+
* **afs:** add module system fs for afs ([#594](https://github.com/AIGNE-io/aigne-framework/issues/594)) ([83c7b65](https://github.com/AIGNE-io/aigne-framework/commit/83c7b6555d21c606a5005eb05f6686882fb8ffa3))
|
|
24
|
+
* **afs:** improve list behavior to always include current path ([cb91f80](https://github.com/AIGNE-io/aigne-framework/commit/cb91f80c6ea3aa6e93dde26b6feeea8689fceb48))
|
|
25
|
+
* **afs:** support expand context into prompt template by call `$afs.xxx` ([#830](https://github.com/AIGNE-io/aigne-framework/issues/830)) ([5616acd](https://github.com/AIGNE-io/aigne-framework/commit/5616acd6ea257c91aa0b766608f45c5ce17f0345))
|
|
26
|
+
* **core:** add session history support ([#858](https://github.com/AIGNE-io/aigne-framework/issues/858)) ([28a070e](https://github.com/AIGNE-io/aigne-framework/commit/28a070ed33b821d1fd344b899706d817ca992b9f))
|
|
27
|
+
* support define agent by third library & orchestrator agent refactor ([#799](https://github.com/AIGNE-io/aigne-framework/issues/799)) ([7264b11](https://github.com/AIGNE-io/aigne-framework/commit/7264b11ab6eed787e928367f09aa08d254968d40))
|
|
28
|
+
* 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))
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
### Bug Fixes
|
|
32
|
+
|
|
33
|
+
* **afs:** add case-sensitive option for search with case-insensitive default ([#814](https://github.com/AIGNE-io/aigne-framework/issues/814)) ([9dc9446](https://github.com/AIGNE-io/aigne-framework/commit/9dc944635104fc311e7756b4bde0a20275cfe8ec))
|
|
34
|
+
* **afs:** check module existence on normalized path ([#793](https://github.com/AIGNE-io/aigne-framework/issues/793)) ([0c991bf](https://github.com/AIGNE-io/aigne-framework/commit/0c991bf0caa948ce62948986ce885b5a98437689))
|
|
35
|
+
* **afs:** improve module path resolution and depth handling ([#659](https://github.com/AIGNE-io/aigne-framework/issues/659)) ([c609d4f](https://github.com/AIGNE-io/aigne-framework/commit/c609d4fc9614123afcf4b8f86b3382a613ace417))
|
|
36
|
+
* **afs:** read method should not throw not found error ([#835](https://github.com/AIGNE-io/aigne-framework/issues/835)) ([ebfdfc1](https://github.com/AIGNE-io/aigne-framework/commit/ebfdfc1cdba23efd23ac2ad4621e3f046990fd8b))
|
|
37
|
+
* **afs:** show gitignored files with marker instead of filtering ([c2bdea1](https://github.com/AIGNE-io/aigne-framework/commit/c2bdea155f47c9420f2fe810cdfed79ef70ef899))
|
|
38
|
+
* **afs:** throw errors instead of logging in AFS module operations ([#874](https://github.com/AIGNE-io/aigne-framework/issues/874)) ([f0cc1c4](https://github.com/AIGNE-io/aigne-framework/commit/f0cc1c4056f8b95b631d595892bb12eb75da4b9f))
|
|
39
|
+
* **afs:** use simple-list instead of tree as default type ([#839](https://github.com/AIGNE-io/aigne-framework/issues/839)) ([65a9a40](https://github.com/AIGNE-io/aigne-framework/commit/65a9a4054b3bdad6f7e40357299ef3dc48f7c3e4))
|
|
40
|
+
* bump version ([696560f](https://github.com/AIGNE-io/aigne-framework/commit/696560fa2673eddcb4d00ac0523fbbbde7273cb3))
|
|
41
|
+
* bump version ([70d217c](https://github.com/AIGNE-io/aigne-framework/commit/70d217c8360dd0dda7f5f17011c4e92ec836e801))
|
|
42
|
+
* bump version ([af04b69](https://github.com/AIGNE-io/aigne-framework/commit/af04b6931951afa35d52065430acc7fef4b10087))
|
|
43
|
+
* bump version ([ba7ad18](https://github.com/AIGNE-io/aigne-framework/commit/ba7ad184fcf32b49bf0507a3cb638d20fb00690d))
|
|
44
|
+
* bump version ([93a1c10](https://github.com/AIGNE-io/aigne-framework/commit/93a1c10cf35f88eaafe91092481f5d087bd5b3a9))
|
|
45
|
+
* improve test coverage tracking and reporting ([#903](https://github.com/AIGNE-io/aigne-framework/issues/903)) ([031144e](https://github.com/AIGNE-io/aigne-framework/commit/031144e74f29e882cffe52ffda8f7a18c76ace7f))
|
|
46
|
+
|
|
47
|
+
## [1.4.0-beta.10](https://github.com/AIGNE-io/aigne-framework/compare/afs-v1.4.0-beta.9...afs-v1.4.0-beta.10) (2026-01-16)
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
### Features
|
|
51
|
+
|
|
52
|
+
* **afs:** improve list behavior to always include current path ([cb91f80](https://github.com/AIGNE-io/aigne-framework/commit/cb91f80c6ea3aa6e93dde26b6feeea8689fceb48))
|
|
53
|
+
|
|
54
|
+
## [1.4.0-beta.9](https://github.com/AIGNE-io/aigne-framework/compare/afs-v1.4.0-beta.8...afs-v1.4.0-beta.9) (2026-01-14)
|
|
55
|
+
|
|
56
|
+
|
|
57
|
+
### Features
|
|
58
|
+
|
|
59
|
+
* **afs:** add module access control and schema validation support ([#904](https://github.com/AIGNE-io/aigne-framework/issues/904)) ([d0b279a](https://github.com/AIGNE-io/aigne-framework/commit/d0b279aac07ebe2bcc1fd4148498fc3f6bbcd561))
|
|
60
|
+
|
|
61
|
+
|
|
62
|
+
### Bug Fixes
|
|
63
|
+
|
|
64
|
+
* improve test coverage tracking and reporting ([#903](https://github.com/AIGNE-io/aigne-framework/issues/903)) ([031144e](https://github.com/AIGNE-io/aigne-framework/commit/031144e74f29e882cffe52ffda8f7a18c76ace7f))
|
|
65
|
+
|
|
66
|
+
|
|
67
|
+
### Dependencies
|
|
68
|
+
|
|
69
|
+
* The following workspace dependencies were updated
|
|
70
|
+
* dependencies
|
|
71
|
+
* @aigne/platform-helpers bumped to 0.6.7-beta.2
|
|
72
|
+
|
|
3
73
|
## [1.4.0-beta.8](https://github.com/AIGNE-io/aigne-framework/compare/afs-v1.4.0-beta.7...afs-v1.4.0-beta.8) (2026-01-12)
|
|
4
74
|
|
|
5
75
|
|
package/lib/cjs/afs.d.ts
CHANGED
|
@@ -9,6 +9,11 @@ export declare class AFS extends Emitter<AFSRootEvents> implements AFSRoot {
|
|
|
9
9
|
name: string;
|
|
10
10
|
constructor(options?: AFSOptions);
|
|
11
11
|
private modules;
|
|
12
|
+
/**
|
|
13
|
+
* Check if write operations are allowed for the given module.
|
|
14
|
+
* Throws AFSReadonlyError if not allowed.
|
|
15
|
+
*/
|
|
16
|
+
private checkWritePermission;
|
|
12
17
|
mount(module: AFSModule): this;
|
|
13
18
|
listModules(): Promise<{
|
|
14
19
|
name: string;
|
package/lib/cjs/afs.js
CHANGED
|
@@ -6,6 +6,7 @@ const uuid_1 = require("@aigne/uuid");
|
|
|
6
6
|
const strict_event_emitter_1 = require("strict-event-emitter");
|
|
7
7
|
const ufo_1 = require("ufo");
|
|
8
8
|
const zod_1 = require("zod");
|
|
9
|
+
const error_js_1 = require("./error.js");
|
|
9
10
|
const type_js_1 = require("./type.js");
|
|
10
11
|
const DEFAULT_MAX_DEPTH = 1;
|
|
11
12
|
const MODULES_ROOT_DIR = "/modules";
|
|
@@ -20,12 +21,22 @@ class AFS extends strict_event_emitter_1.Emitter {
|
|
|
20
21
|
}
|
|
21
22
|
}
|
|
22
23
|
modules = new Map();
|
|
24
|
+
/**
|
|
25
|
+
* Check if write operations are allowed for the given module.
|
|
26
|
+
* Throws AFSReadonlyError if not allowed.
|
|
27
|
+
*/
|
|
28
|
+
checkWritePermission(module, operation, path) {
|
|
29
|
+
// Module-level readonly (undefined means readonly by default)
|
|
30
|
+
if (module.accessMode !== "readwrite") {
|
|
31
|
+
throw new error_js_1.AFSReadonlyError(`Module '${module.name}' is readonly, cannot perform ${operation} to ${path}`);
|
|
32
|
+
}
|
|
33
|
+
}
|
|
23
34
|
mount(module) {
|
|
24
|
-
|
|
25
|
-
if (
|
|
26
|
-
throw new Error(`Invalid
|
|
35
|
+
// Validate module name (should not contain '/')
|
|
36
|
+
if (module.name.includes("/")) {
|
|
37
|
+
throw new Error(`Invalid module name: ${module.name}. Module name must not contain '/'`);
|
|
27
38
|
}
|
|
28
|
-
path = (0, ufo_1.joinURL)(MODULES_ROOT_DIR,
|
|
39
|
+
const path = (0, ufo_1.joinURL)(MODULES_ROOT_DIR, module.name);
|
|
29
40
|
if (this.modules.has(path)) {
|
|
30
41
|
throw new Error(`Module already mounted at path: ${path}`);
|
|
31
42
|
}
|
|
@@ -55,14 +66,36 @@ class AFS extends strict_event_emitter_1.Emitter {
|
|
|
55
66
|
}
|
|
56
67
|
async _list(path, options = {}) {
|
|
57
68
|
const results = [];
|
|
69
|
+
// Special case: listing root "/" should return /modules directory
|
|
70
|
+
if (path === "/" && this.modules.size > 0) {
|
|
71
|
+
const maxDepth = options?.maxDepth ?? DEFAULT_MAX_DEPTH;
|
|
72
|
+
// Always include /modules directory first
|
|
73
|
+
results.push({
|
|
74
|
+
id: "modules",
|
|
75
|
+
path: MODULES_ROOT_DIR,
|
|
76
|
+
summary: "All mounted modules",
|
|
77
|
+
});
|
|
78
|
+
if (maxDepth === 1) {
|
|
79
|
+
// Only show /modules directory
|
|
80
|
+
return { data: results };
|
|
81
|
+
}
|
|
82
|
+
// For maxDepth > 1, also get children of /modules with reduced depth
|
|
83
|
+
const childrenResult = await this._list(MODULES_ROOT_DIR, {
|
|
84
|
+
...options,
|
|
85
|
+
maxDepth: maxDepth - 1,
|
|
86
|
+
});
|
|
87
|
+
results.push(...childrenResult.data);
|
|
88
|
+
return { data: results };
|
|
89
|
+
}
|
|
58
90
|
const matches = this.findModules(path, options);
|
|
59
91
|
for (const matched of matches) {
|
|
60
|
-
const moduleEntry = {
|
|
61
|
-
id: matched.module.name,
|
|
62
|
-
path: matched.remainedModulePath,
|
|
63
|
-
summary: matched.module.description,
|
|
64
|
-
};
|
|
65
92
|
if (matched.maxDepth === 0) {
|
|
93
|
+
// When maxDepth is 0, show the module entry
|
|
94
|
+
const moduleEntry = {
|
|
95
|
+
id: matched.module.name,
|
|
96
|
+
path: matched.modulePath,
|
|
97
|
+
summary: matched.module.description,
|
|
98
|
+
};
|
|
66
99
|
results.push(moduleEntry);
|
|
67
100
|
continue;
|
|
68
101
|
}
|
|
@@ -73,15 +106,13 @@ class AFS extends strict_event_emitter_1.Emitter {
|
|
|
73
106
|
...options,
|
|
74
107
|
maxDepth: matched.maxDepth,
|
|
75
108
|
});
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
results.push(moduleEntry);
|
|
84
|
-
}
|
|
109
|
+
const children = data.map((entry) => ({
|
|
110
|
+
...entry,
|
|
111
|
+
path: (0, ufo_1.joinURL)(matched.modulePath, entry.path),
|
|
112
|
+
}));
|
|
113
|
+
// Always include all nodes (including the current path itself)
|
|
114
|
+
// This ensures consistent behavior across all listing scenarios
|
|
115
|
+
results.push(...children);
|
|
85
116
|
}
|
|
86
117
|
catch (error) {
|
|
87
118
|
throw new Error(`Error listing from module at ${matched.modulePath}: ${error.message}`);
|
|
@@ -109,6 +140,7 @@ class AFS extends strict_event_emitter_1.Emitter {
|
|
|
109
140
|
const module = this.findModules(path, { exactMatch: true })[0];
|
|
110
141
|
if (!module?.module.write)
|
|
111
142
|
throw new Error(`No module found for path: ${path}`);
|
|
143
|
+
this.checkWritePermission(module.module, "write", path);
|
|
112
144
|
const res = await module.module.write(module.subpath, content, options);
|
|
113
145
|
return {
|
|
114
146
|
...res,
|
|
@@ -122,6 +154,7 @@ class AFS extends strict_event_emitter_1.Emitter {
|
|
|
122
154
|
const module = this.findModules(path, { exactMatch: true })[0];
|
|
123
155
|
if (!module?.module.delete)
|
|
124
156
|
throw new Error(`No module found for path: ${path}`);
|
|
157
|
+
this.checkWritePermission(module.module, "delete", path);
|
|
125
158
|
return await module.module.delete(module.subpath, options);
|
|
126
159
|
}
|
|
127
160
|
async rename(oldPath, newPath, options) {
|
|
@@ -134,6 +167,7 @@ class AFS extends strict_event_emitter_1.Emitter {
|
|
|
134
167
|
if (!oldModule.module.rename) {
|
|
135
168
|
throw new Error(`Module does not support rename operation: ${oldModule.modulePath}`);
|
|
136
169
|
}
|
|
170
|
+
this.checkWritePermission(oldModule.module, "rename", oldPath);
|
|
137
171
|
return await oldModule.module.rename(oldModule.subpath, newModule.subpath, options);
|
|
138
172
|
}
|
|
139
173
|
async search(path, query, options = {}) {
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Base error class for all AFS errors.
|
|
3
|
+
*/
|
|
4
|
+
export declare class AFSError extends Error {
|
|
5
|
+
readonly code: string;
|
|
6
|
+
constructor(message: string, code: string);
|
|
7
|
+
}
|
|
8
|
+
/**
|
|
9
|
+
* Error thrown when attempting write operations on a readonly AFS or module.
|
|
10
|
+
*/
|
|
11
|
+
export declare class AFSReadonlyError extends AFSError {
|
|
12
|
+
constructor(message: string);
|
|
13
|
+
}
|
package/lib/cjs/error.js
ADDED
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.AFSReadonlyError = exports.AFSError = void 0;
|
|
4
|
+
/**
|
|
5
|
+
* Base error class for all AFS errors.
|
|
6
|
+
*/
|
|
7
|
+
class AFSError extends Error {
|
|
8
|
+
code;
|
|
9
|
+
constructor(message, code) {
|
|
10
|
+
super(message);
|
|
11
|
+
this.name = "AFSError";
|
|
12
|
+
this.code = code;
|
|
13
|
+
}
|
|
14
|
+
}
|
|
15
|
+
exports.AFSError = AFSError;
|
|
16
|
+
/**
|
|
17
|
+
* Error thrown when attempting write operations on a readonly AFS or module.
|
|
18
|
+
*/
|
|
19
|
+
class AFSReadonlyError extends AFSError {
|
|
20
|
+
constructor(message) {
|
|
21
|
+
super(message, "AFS_READONLY");
|
|
22
|
+
this.name = "AFSReadonlyError";
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
exports.AFSReadonlyError = AFSReadonlyError;
|
package/lib/cjs/index.d.ts
CHANGED
package/lib/cjs/index.js
CHANGED
|
@@ -15,4 +15,5 @@ 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("./error.js"), exports);
|
|
18
19
|
__exportStar(require("./type.js"), exports);
|
package/lib/cjs/type.d.ts
CHANGED
|
@@ -1,6 +1,20 @@
|
|
|
1
1
|
import type { Emitter } from "strict-event-emitter";
|
|
2
|
-
import { type ZodType } from "zod";
|
|
3
|
-
|
|
2
|
+
import { type ZodType, z } from "zod";
|
|
3
|
+
/**
|
|
4
|
+
* Access mode for AFS modules and root.
|
|
5
|
+
* - "readonly": Only read operations are allowed (list, read, search)
|
|
6
|
+
* - "readwrite": All operations are allowed
|
|
7
|
+
*/
|
|
8
|
+
export type AFSAccessMode = "readonly" | "readwrite";
|
|
9
|
+
/**
|
|
10
|
+
* Zod schema for access mode validation.
|
|
11
|
+
* Can be reused across modules that support access mode configuration.
|
|
12
|
+
*/
|
|
13
|
+
export declare const accessModeSchema: z.ZodOptional<z.ZodEnum<["readonly", "readwrite"]>>;
|
|
14
|
+
export interface AFSOperationOptions {
|
|
15
|
+
context?: any;
|
|
16
|
+
}
|
|
17
|
+
export interface AFSListOptions extends AFSOperationOptions {
|
|
4
18
|
filter?: {
|
|
5
19
|
agentId?: string;
|
|
6
20
|
userId?: string;
|
|
@@ -23,47 +37,40 @@ export interface AFSListOptions {
|
|
|
23
37
|
* Examples: "*.ts", "**\/*.js", "src/**\/*.{ts,tsx}"
|
|
24
38
|
*/
|
|
25
39
|
pattern?: string;
|
|
26
|
-
context?: any;
|
|
27
40
|
}
|
|
28
41
|
export interface AFSListResult {
|
|
29
42
|
data: AFSEntry[];
|
|
30
43
|
message?: string;
|
|
31
|
-
context?: any;
|
|
32
44
|
}
|
|
33
|
-
export interface AFSSearchOptions {
|
|
45
|
+
export interface AFSSearchOptions extends AFSOperationOptions {
|
|
34
46
|
limit?: number;
|
|
35
47
|
caseSensitive?: boolean;
|
|
36
|
-
context?: any;
|
|
37
48
|
}
|
|
38
49
|
export interface AFSSearchResult {
|
|
39
50
|
data: AFSEntry[];
|
|
40
51
|
message?: string;
|
|
41
52
|
}
|
|
42
|
-
export interface AFSReadOptions {
|
|
53
|
+
export interface AFSReadOptions extends AFSOperationOptions {
|
|
43
54
|
filter?: AFSListOptions["filter"];
|
|
44
|
-
context?: any;
|
|
45
55
|
}
|
|
46
56
|
export interface AFSReadResult {
|
|
47
57
|
data?: AFSEntry;
|
|
48
58
|
message?: string;
|
|
49
59
|
}
|
|
50
|
-
export interface AFSDeleteOptions {
|
|
60
|
+
export interface AFSDeleteOptions extends AFSOperationOptions {
|
|
51
61
|
recursive?: boolean;
|
|
52
|
-
context?: any;
|
|
53
62
|
}
|
|
54
63
|
export interface AFSDeleteResult {
|
|
55
64
|
message?: string;
|
|
56
65
|
}
|
|
57
|
-
export interface AFSRenameOptions {
|
|
66
|
+
export interface AFSRenameOptions extends AFSOperationOptions {
|
|
58
67
|
overwrite?: boolean;
|
|
59
|
-
context?: any;
|
|
60
68
|
}
|
|
61
69
|
export interface AFSRenameResult {
|
|
62
70
|
message?: string;
|
|
63
71
|
}
|
|
64
|
-
export interface AFSWriteOptions {
|
|
72
|
+
export interface AFSWriteOptions extends AFSOperationOptions {
|
|
65
73
|
append?: boolean;
|
|
66
|
-
context?: any;
|
|
67
74
|
}
|
|
68
75
|
export interface AFSWriteResult {
|
|
69
76
|
data: AFSEntry;
|
|
@@ -72,8 +79,7 @@ export interface AFSWriteResult {
|
|
|
72
79
|
}
|
|
73
80
|
export interface AFSWriteEntryPayload extends Omit<AFSEntry, "id" | "path"> {
|
|
74
81
|
}
|
|
75
|
-
export interface AFSExecOptions {
|
|
76
|
-
context: any;
|
|
82
|
+
export interface AFSExecOptions extends AFSOperationOptions {
|
|
77
83
|
}
|
|
78
84
|
export interface AFSExecResult {
|
|
79
85
|
data: Record<string, any>;
|
|
@@ -81,6 +87,19 @@ export interface AFSExecResult {
|
|
|
81
87
|
export interface AFSModule {
|
|
82
88
|
readonly name: string;
|
|
83
89
|
readonly description?: string;
|
|
90
|
+
/**
|
|
91
|
+
* Access mode for this module.
|
|
92
|
+
* - "readonly": Only read operations are allowed
|
|
93
|
+
* - "readwrite": All operations are allowed
|
|
94
|
+
* Default behavior is implementation-specific.
|
|
95
|
+
*/
|
|
96
|
+
readonly accessMode?: AFSAccessMode;
|
|
97
|
+
/**
|
|
98
|
+
* Enable automatic agent skill scanning for this module.
|
|
99
|
+
* When set to true, the system will scan this module for agent skills.
|
|
100
|
+
* @default false
|
|
101
|
+
*/
|
|
102
|
+
readonly agentSkills?: boolean;
|
|
84
103
|
onMount?(root: AFSRoot): void;
|
|
85
104
|
symlinkToPhysical?(path: string): Promise<void>;
|
|
86
105
|
list?(path: string, options?: AFSListOptions): Promise<AFSListResult>;
|
|
@@ -91,6 +110,39 @@ export interface AFSModule {
|
|
|
91
110
|
search?(path: string, query: string, options?: AFSSearchOptions): Promise<AFSSearchResult>;
|
|
92
111
|
exec?(path: string, args: Record<string, any>, options: AFSExecOptions): Promise<AFSExecResult>;
|
|
93
112
|
}
|
|
113
|
+
/**
|
|
114
|
+
* Parameters for loading a module from configuration.
|
|
115
|
+
*/
|
|
116
|
+
export interface AFSModuleLoadParams {
|
|
117
|
+
/** Path to the configuration file */
|
|
118
|
+
filepath: string;
|
|
119
|
+
/** Parsed configuration object */
|
|
120
|
+
parsed?: object;
|
|
121
|
+
}
|
|
122
|
+
/**
|
|
123
|
+
* Interface for module classes that support schema validation and loading from configuration.
|
|
124
|
+
* This describes the static part of a module class.
|
|
125
|
+
*
|
|
126
|
+
* @example
|
|
127
|
+
* ```typescript
|
|
128
|
+
* class MyModule implements AFSModule {
|
|
129
|
+
* static schema() { return mySchema; }
|
|
130
|
+
* static async load(params: AFSModuleLoadParams) { ... }
|
|
131
|
+
* // ...
|
|
132
|
+
* }
|
|
133
|
+
*
|
|
134
|
+
* // Type check
|
|
135
|
+
* const _check: AFSModuleClass<MyModule, MyModuleOptions> = MyModule;
|
|
136
|
+
* ```
|
|
137
|
+
*/
|
|
138
|
+
export interface AFSModuleClass<T extends AFSModule = AFSModule, O extends object = object> {
|
|
139
|
+
/** Returns the Zod schema for validating module configuration */
|
|
140
|
+
schema(): ZodType<O>;
|
|
141
|
+
/** Loads a module instance from configuration file path and parsed config */
|
|
142
|
+
load(params: AFSModuleLoadParams): Promise<T>;
|
|
143
|
+
/** Constructor */
|
|
144
|
+
new (options: O): T;
|
|
145
|
+
}
|
|
94
146
|
export type AFSRootEvents = {
|
|
95
147
|
agentSucceed: [
|
|
96
148
|
{
|
|
@@ -104,7 +156,7 @@ export type AFSRootEvents = {
|
|
|
104
156
|
];
|
|
105
157
|
historyCreated: [{
|
|
106
158
|
entry: AFSEntry;
|
|
107
|
-
}];
|
|
159
|
+
}, options: AFSOperationOptions];
|
|
108
160
|
};
|
|
109
161
|
export interface AFSRootListOptions extends AFSListOptions, AFSContextPreset {
|
|
110
162
|
preset?: string;
|
package/lib/cjs/type.js
CHANGED
|
@@ -1,7 +1,15 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.afsEntrySchema = void 0;
|
|
3
|
+
exports.afsEntrySchema = exports.accessModeSchema = void 0;
|
|
4
4
|
const zod_1 = require("zod");
|
|
5
|
+
/**
|
|
6
|
+
* Zod schema for access mode validation.
|
|
7
|
+
* Can be reused across modules that support access mode configuration.
|
|
8
|
+
*/
|
|
9
|
+
exports.accessModeSchema = zod_1.z
|
|
10
|
+
.enum(["readonly", "readwrite"])
|
|
11
|
+
.describe("Access mode for this module")
|
|
12
|
+
.optional();
|
|
5
13
|
exports.afsEntrySchema = zod_1.z.object({
|
|
6
14
|
id: zod_1.z.string(),
|
|
7
15
|
createdAt: zod_1.z.date().optional(),
|
package/lib/dts/afs.d.ts
CHANGED
|
@@ -9,6 +9,11 @@ export declare class AFS extends Emitter<AFSRootEvents> implements AFSRoot {
|
|
|
9
9
|
name: string;
|
|
10
10
|
constructor(options?: AFSOptions);
|
|
11
11
|
private modules;
|
|
12
|
+
/**
|
|
13
|
+
* Check if write operations are allowed for the given module.
|
|
14
|
+
* Throws AFSReadonlyError if not allowed.
|
|
15
|
+
*/
|
|
16
|
+
private checkWritePermission;
|
|
12
17
|
mount(module: AFSModule): this;
|
|
13
18
|
listModules(): Promise<{
|
|
14
19
|
name: string;
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Base error class for all AFS errors.
|
|
3
|
+
*/
|
|
4
|
+
export declare class AFSError extends Error {
|
|
5
|
+
readonly code: string;
|
|
6
|
+
constructor(message: string, code: string);
|
|
7
|
+
}
|
|
8
|
+
/**
|
|
9
|
+
* Error thrown when attempting write operations on a readonly AFS or module.
|
|
10
|
+
*/
|
|
11
|
+
export declare class AFSReadonlyError extends AFSError {
|
|
12
|
+
constructor(message: string);
|
|
13
|
+
}
|
package/lib/dts/index.d.ts
CHANGED
package/lib/dts/type.d.ts
CHANGED
|
@@ -1,6 +1,20 @@
|
|
|
1
1
|
import type { Emitter } from "strict-event-emitter";
|
|
2
|
-
import { type ZodType } from "zod";
|
|
3
|
-
|
|
2
|
+
import { type ZodType, z } from "zod";
|
|
3
|
+
/**
|
|
4
|
+
* Access mode for AFS modules and root.
|
|
5
|
+
* - "readonly": Only read operations are allowed (list, read, search)
|
|
6
|
+
* - "readwrite": All operations are allowed
|
|
7
|
+
*/
|
|
8
|
+
export type AFSAccessMode = "readonly" | "readwrite";
|
|
9
|
+
/**
|
|
10
|
+
* Zod schema for access mode validation.
|
|
11
|
+
* Can be reused across modules that support access mode configuration.
|
|
12
|
+
*/
|
|
13
|
+
export declare const accessModeSchema: z.ZodOptional<z.ZodEnum<["readonly", "readwrite"]>>;
|
|
14
|
+
export interface AFSOperationOptions {
|
|
15
|
+
context?: any;
|
|
16
|
+
}
|
|
17
|
+
export interface AFSListOptions extends AFSOperationOptions {
|
|
4
18
|
filter?: {
|
|
5
19
|
agentId?: string;
|
|
6
20
|
userId?: string;
|
|
@@ -23,47 +37,40 @@ export interface AFSListOptions {
|
|
|
23
37
|
* Examples: "*.ts", "**\/*.js", "src/**\/*.{ts,tsx}"
|
|
24
38
|
*/
|
|
25
39
|
pattern?: string;
|
|
26
|
-
context?: any;
|
|
27
40
|
}
|
|
28
41
|
export interface AFSListResult {
|
|
29
42
|
data: AFSEntry[];
|
|
30
43
|
message?: string;
|
|
31
|
-
context?: any;
|
|
32
44
|
}
|
|
33
|
-
export interface AFSSearchOptions {
|
|
45
|
+
export interface AFSSearchOptions extends AFSOperationOptions {
|
|
34
46
|
limit?: number;
|
|
35
47
|
caseSensitive?: boolean;
|
|
36
|
-
context?: any;
|
|
37
48
|
}
|
|
38
49
|
export interface AFSSearchResult {
|
|
39
50
|
data: AFSEntry[];
|
|
40
51
|
message?: string;
|
|
41
52
|
}
|
|
42
|
-
export interface AFSReadOptions {
|
|
53
|
+
export interface AFSReadOptions extends AFSOperationOptions {
|
|
43
54
|
filter?: AFSListOptions["filter"];
|
|
44
|
-
context?: any;
|
|
45
55
|
}
|
|
46
56
|
export interface AFSReadResult {
|
|
47
57
|
data?: AFSEntry;
|
|
48
58
|
message?: string;
|
|
49
59
|
}
|
|
50
|
-
export interface AFSDeleteOptions {
|
|
60
|
+
export interface AFSDeleteOptions extends AFSOperationOptions {
|
|
51
61
|
recursive?: boolean;
|
|
52
|
-
context?: any;
|
|
53
62
|
}
|
|
54
63
|
export interface AFSDeleteResult {
|
|
55
64
|
message?: string;
|
|
56
65
|
}
|
|
57
|
-
export interface AFSRenameOptions {
|
|
66
|
+
export interface AFSRenameOptions extends AFSOperationOptions {
|
|
58
67
|
overwrite?: boolean;
|
|
59
|
-
context?: any;
|
|
60
68
|
}
|
|
61
69
|
export interface AFSRenameResult {
|
|
62
70
|
message?: string;
|
|
63
71
|
}
|
|
64
|
-
export interface AFSWriteOptions {
|
|
72
|
+
export interface AFSWriteOptions extends AFSOperationOptions {
|
|
65
73
|
append?: boolean;
|
|
66
|
-
context?: any;
|
|
67
74
|
}
|
|
68
75
|
export interface AFSWriteResult {
|
|
69
76
|
data: AFSEntry;
|
|
@@ -72,8 +79,7 @@ export interface AFSWriteResult {
|
|
|
72
79
|
}
|
|
73
80
|
export interface AFSWriteEntryPayload extends Omit<AFSEntry, "id" | "path"> {
|
|
74
81
|
}
|
|
75
|
-
export interface AFSExecOptions {
|
|
76
|
-
context: any;
|
|
82
|
+
export interface AFSExecOptions extends AFSOperationOptions {
|
|
77
83
|
}
|
|
78
84
|
export interface AFSExecResult {
|
|
79
85
|
data: Record<string, any>;
|
|
@@ -81,6 +87,19 @@ export interface AFSExecResult {
|
|
|
81
87
|
export interface AFSModule {
|
|
82
88
|
readonly name: string;
|
|
83
89
|
readonly description?: string;
|
|
90
|
+
/**
|
|
91
|
+
* Access mode for this module.
|
|
92
|
+
* - "readonly": Only read operations are allowed
|
|
93
|
+
* - "readwrite": All operations are allowed
|
|
94
|
+
* Default behavior is implementation-specific.
|
|
95
|
+
*/
|
|
96
|
+
readonly accessMode?: AFSAccessMode;
|
|
97
|
+
/**
|
|
98
|
+
* Enable automatic agent skill scanning for this module.
|
|
99
|
+
* When set to true, the system will scan this module for agent skills.
|
|
100
|
+
* @default false
|
|
101
|
+
*/
|
|
102
|
+
readonly agentSkills?: boolean;
|
|
84
103
|
onMount?(root: AFSRoot): void;
|
|
85
104
|
symlinkToPhysical?(path: string): Promise<void>;
|
|
86
105
|
list?(path: string, options?: AFSListOptions): Promise<AFSListResult>;
|
|
@@ -91,6 +110,39 @@ export interface AFSModule {
|
|
|
91
110
|
search?(path: string, query: string, options?: AFSSearchOptions): Promise<AFSSearchResult>;
|
|
92
111
|
exec?(path: string, args: Record<string, any>, options: AFSExecOptions): Promise<AFSExecResult>;
|
|
93
112
|
}
|
|
113
|
+
/**
|
|
114
|
+
* Parameters for loading a module from configuration.
|
|
115
|
+
*/
|
|
116
|
+
export interface AFSModuleLoadParams {
|
|
117
|
+
/** Path to the configuration file */
|
|
118
|
+
filepath: string;
|
|
119
|
+
/** Parsed configuration object */
|
|
120
|
+
parsed?: object;
|
|
121
|
+
}
|
|
122
|
+
/**
|
|
123
|
+
* Interface for module classes that support schema validation and loading from configuration.
|
|
124
|
+
* This describes the static part of a module class.
|
|
125
|
+
*
|
|
126
|
+
* @example
|
|
127
|
+
* ```typescript
|
|
128
|
+
* class MyModule implements AFSModule {
|
|
129
|
+
* static schema() { return mySchema; }
|
|
130
|
+
* static async load(params: AFSModuleLoadParams) { ... }
|
|
131
|
+
* // ...
|
|
132
|
+
* }
|
|
133
|
+
*
|
|
134
|
+
* // Type check
|
|
135
|
+
* const _check: AFSModuleClass<MyModule, MyModuleOptions> = MyModule;
|
|
136
|
+
* ```
|
|
137
|
+
*/
|
|
138
|
+
export interface AFSModuleClass<T extends AFSModule = AFSModule, O extends object = object> {
|
|
139
|
+
/** Returns the Zod schema for validating module configuration */
|
|
140
|
+
schema(): ZodType<O>;
|
|
141
|
+
/** Loads a module instance from configuration file path and parsed config */
|
|
142
|
+
load(params: AFSModuleLoadParams): Promise<T>;
|
|
143
|
+
/** Constructor */
|
|
144
|
+
new (options: O): T;
|
|
145
|
+
}
|
|
94
146
|
export type AFSRootEvents = {
|
|
95
147
|
agentSucceed: [
|
|
96
148
|
{
|
|
@@ -104,7 +156,7 @@ export type AFSRootEvents = {
|
|
|
104
156
|
];
|
|
105
157
|
historyCreated: [{
|
|
106
158
|
entry: AFSEntry;
|
|
107
|
-
}];
|
|
159
|
+
}, options: AFSOperationOptions];
|
|
108
160
|
};
|
|
109
161
|
export interface AFSRootListOptions extends AFSListOptions, AFSContextPreset {
|
|
110
162
|
preset?: string;
|
package/lib/esm/afs.d.ts
CHANGED
|
@@ -9,6 +9,11 @@ export declare class AFS extends Emitter<AFSRootEvents> implements AFSRoot {
|
|
|
9
9
|
name: string;
|
|
10
10
|
constructor(options?: AFSOptions);
|
|
11
11
|
private modules;
|
|
12
|
+
/**
|
|
13
|
+
* Check if write operations are allowed for the given module.
|
|
14
|
+
* Throws AFSReadonlyError if not allowed.
|
|
15
|
+
*/
|
|
16
|
+
private checkWritePermission;
|
|
12
17
|
mount(module: AFSModule): this;
|
|
13
18
|
listModules(): Promise<{
|
|
14
19
|
name: string;
|
package/lib/esm/afs.js
CHANGED
|
@@ -3,6 +3,7 @@ import { v7 } from "@aigne/uuid";
|
|
|
3
3
|
import { Emitter } from "strict-event-emitter";
|
|
4
4
|
import { joinURL } from "ufo";
|
|
5
5
|
import { z } from "zod";
|
|
6
|
+
import { AFSReadonlyError } from "./error.js";
|
|
6
7
|
import { afsEntrySchema, } from "./type.js";
|
|
7
8
|
const DEFAULT_MAX_DEPTH = 1;
|
|
8
9
|
const MODULES_ROOT_DIR = "/modules";
|
|
@@ -17,12 +18,22 @@ export class AFS extends Emitter {
|
|
|
17
18
|
}
|
|
18
19
|
}
|
|
19
20
|
modules = new Map();
|
|
21
|
+
/**
|
|
22
|
+
* Check if write operations are allowed for the given module.
|
|
23
|
+
* Throws AFSReadonlyError if not allowed.
|
|
24
|
+
*/
|
|
25
|
+
checkWritePermission(module, operation, path) {
|
|
26
|
+
// Module-level readonly (undefined means readonly by default)
|
|
27
|
+
if (module.accessMode !== "readwrite") {
|
|
28
|
+
throw new AFSReadonlyError(`Module '${module.name}' is readonly, cannot perform ${operation} to ${path}`);
|
|
29
|
+
}
|
|
30
|
+
}
|
|
20
31
|
mount(module) {
|
|
21
|
-
|
|
22
|
-
if (
|
|
23
|
-
throw new Error(`Invalid
|
|
32
|
+
// Validate module name (should not contain '/')
|
|
33
|
+
if (module.name.includes("/")) {
|
|
34
|
+
throw new Error(`Invalid module name: ${module.name}. Module name must not contain '/'`);
|
|
24
35
|
}
|
|
25
|
-
path = joinURL(MODULES_ROOT_DIR,
|
|
36
|
+
const path = joinURL(MODULES_ROOT_DIR, module.name);
|
|
26
37
|
if (this.modules.has(path)) {
|
|
27
38
|
throw new Error(`Module already mounted at path: ${path}`);
|
|
28
39
|
}
|
|
@@ -52,14 +63,36 @@ export class AFS extends Emitter {
|
|
|
52
63
|
}
|
|
53
64
|
async _list(path, options = {}) {
|
|
54
65
|
const results = [];
|
|
66
|
+
// Special case: listing root "/" should return /modules directory
|
|
67
|
+
if (path === "/" && this.modules.size > 0) {
|
|
68
|
+
const maxDepth = options?.maxDepth ?? DEFAULT_MAX_DEPTH;
|
|
69
|
+
// Always include /modules directory first
|
|
70
|
+
results.push({
|
|
71
|
+
id: "modules",
|
|
72
|
+
path: MODULES_ROOT_DIR,
|
|
73
|
+
summary: "All mounted modules",
|
|
74
|
+
});
|
|
75
|
+
if (maxDepth === 1) {
|
|
76
|
+
// Only show /modules directory
|
|
77
|
+
return { data: results };
|
|
78
|
+
}
|
|
79
|
+
// For maxDepth > 1, also get children of /modules with reduced depth
|
|
80
|
+
const childrenResult = await this._list(MODULES_ROOT_DIR, {
|
|
81
|
+
...options,
|
|
82
|
+
maxDepth: maxDepth - 1,
|
|
83
|
+
});
|
|
84
|
+
results.push(...childrenResult.data);
|
|
85
|
+
return { data: results };
|
|
86
|
+
}
|
|
55
87
|
const matches = this.findModules(path, options);
|
|
56
88
|
for (const matched of matches) {
|
|
57
|
-
const moduleEntry = {
|
|
58
|
-
id: matched.module.name,
|
|
59
|
-
path: matched.remainedModulePath,
|
|
60
|
-
summary: matched.module.description,
|
|
61
|
-
};
|
|
62
89
|
if (matched.maxDepth === 0) {
|
|
90
|
+
// When maxDepth is 0, show the module entry
|
|
91
|
+
const moduleEntry = {
|
|
92
|
+
id: matched.module.name,
|
|
93
|
+
path: matched.modulePath,
|
|
94
|
+
summary: matched.module.description,
|
|
95
|
+
};
|
|
63
96
|
results.push(moduleEntry);
|
|
64
97
|
continue;
|
|
65
98
|
}
|
|
@@ -70,15 +103,13 @@ export class AFS extends Emitter {
|
|
|
70
103
|
...options,
|
|
71
104
|
maxDepth: matched.maxDepth,
|
|
72
105
|
});
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
results.push(moduleEntry);
|
|
81
|
-
}
|
|
106
|
+
const children = data.map((entry) => ({
|
|
107
|
+
...entry,
|
|
108
|
+
path: joinURL(matched.modulePath, entry.path),
|
|
109
|
+
}));
|
|
110
|
+
// Always include all nodes (including the current path itself)
|
|
111
|
+
// This ensures consistent behavior across all listing scenarios
|
|
112
|
+
results.push(...children);
|
|
82
113
|
}
|
|
83
114
|
catch (error) {
|
|
84
115
|
throw new Error(`Error listing from module at ${matched.modulePath}: ${error.message}`);
|
|
@@ -106,6 +137,7 @@ export class AFS extends Emitter {
|
|
|
106
137
|
const module = this.findModules(path, { exactMatch: true })[0];
|
|
107
138
|
if (!module?.module.write)
|
|
108
139
|
throw new Error(`No module found for path: ${path}`);
|
|
140
|
+
this.checkWritePermission(module.module, "write", path);
|
|
109
141
|
const res = await module.module.write(module.subpath, content, options);
|
|
110
142
|
return {
|
|
111
143
|
...res,
|
|
@@ -119,6 +151,7 @@ export class AFS extends Emitter {
|
|
|
119
151
|
const module = this.findModules(path, { exactMatch: true })[0];
|
|
120
152
|
if (!module?.module.delete)
|
|
121
153
|
throw new Error(`No module found for path: ${path}`);
|
|
154
|
+
this.checkWritePermission(module.module, "delete", path);
|
|
122
155
|
return await module.module.delete(module.subpath, options);
|
|
123
156
|
}
|
|
124
157
|
async rename(oldPath, newPath, options) {
|
|
@@ -131,6 +164,7 @@ export class AFS extends Emitter {
|
|
|
131
164
|
if (!oldModule.module.rename) {
|
|
132
165
|
throw new Error(`Module does not support rename operation: ${oldModule.modulePath}`);
|
|
133
166
|
}
|
|
167
|
+
this.checkWritePermission(oldModule.module, "rename", oldPath);
|
|
134
168
|
return await oldModule.module.rename(oldModule.subpath, newModule.subpath, options);
|
|
135
169
|
}
|
|
136
170
|
async search(path, query, options = {}) {
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Base error class for all AFS errors.
|
|
3
|
+
*/
|
|
4
|
+
export declare class AFSError extends Error {
|
|
5
|
+
readonly code: string;
|
|
6
|
+
constructor(message: string, code: string);
|
|
7
|
+
}
|
|
8
|
+
/**
|
|
9
|
+
* Error thrown when attempting write operations on a readonly AFS or module.
|
|
10
|
+
*/
|
|
11
|
+
export declare class AFSReadonlyError extends AFSError {
|
|
12
|
+
constructor(message: string);
|
|
13
|
+
}
|
package/lib/esm/error.js
ADDED
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Base error class for all AFS errors.
|
|
3
|
+
*/
|
|
4
|
+
export class AFSError extends Error {
|
|
5
|
+
code;
|
|
6
|
+
constructor(message, code) {
|
|
7
|
+
super(message);
|
|
8
|
+
this.name = "AFSError";
|
|
9
|
+
this.code = code;
|
|
10
|
+
}
|
|
11
|
+
}
|
|
12
|
+
/**
|
|
13
|
+
* Error thrown when attempting write operations on a readonly AFS or module.
|
|
14
|
+
*/
|
|
15
|
+
export class AFSReadonlyError extends AFSError {
|
|
16
|
+
constructor(message) {
|
|
17
|
+
super(message, "AFS_READONLY");
|
|
18
|
+
this.name = "AFSReadonlyError";
|
|
19
|
+
}
|
|
20
|
+
}
|
package/lib/esm/index.d.ts
CHANGED
package/lib/esm/index.js
CHANGED
package/lib/esm/type.d.ts
CHANGED
|
@@ -1,6 +1,20 @@
|
|
|
1
1
|
import type { Emitter } from "strict-event-emitter";
|
|
2
|
-
import { type ZodType } from "zod";
|
|
3
|
-
|
|
2
|
+
import { type ZodType, z } from "zod";
|
|
3
|
+
/**
|
|
4
|
+
* Access mode for AFS modules and root.
|
|
5
|
+
* - "readonly": Only read operations are allowed (list, read, search)
|
|
6
|
+
* - "readwrite": All operations are allowed
|
|
7
|
+
*/
|
|
8
|
+
export type AFSAccessMode = "readonly" | "readwrite";
|
|
9
|
+
/**
|
|
10
|
+
* Zod schema for access mode validation.
|
|
11
|
+
* Can be reused across modules that support access mode configuration.
|
|
12
|
+
*/
|
|
13
|
+
export declare const accessModeSchema: z.ZodOptional<z.ZodEnum<["readonly", "readwrite"]>>;
|
|
14
|
+
export interface AFSOperationOptions {
|
|
15
|
+
context?: any;
|
|
16
|
+
}
|
|
17
|
+
export interface AFSListOptions extends AFSOperationOptions {
|
|
4
18
|
filter?: {
|
|
5
19
|
agentId?: string;
|
|
6
20
|
userId?: string;
|
|
@@ -23,47 +37,40 @@ export interface AFSListOptions {
|
|
|
23
37
|
* Examples: "*.ts", "**\/*.js", "src/**\/*.{ts,tsx}"
|
|
24
38
|
*/
|
|
25
39
|
pattern?: string;
|
|
26
|
-
context?: any;
|
|
27
40
|
}
|
|
28
41
|
export interface AFSListResult {
|
|
29
42
|
data: AFSEntry[];
|
|
30
43
|
message?: string;
|
|
31
|
-
context?: any;
|
|
32
44
|
}
|
|
33
|
-
export interface AFSSearchOptions {
|
|
45
|
+
export interface AFSSearchOptions extends AFSOperationOptions {
|
|
34
46
|
limit?: number;
|
|
35
47
|
caseSensitive?: boolean;
|
|
36
|
-
context?: any;
|
|
37
48
|
}
|
|
38
49
|
export interface AFSSearchResult {
|
|
39
50
|
data: AFSEntry[];
|
|
40
51
|
message?: string;
|
|
41
52
|
}
|
|
42
|
-
export interface AFSReadOptions {
|
|
53
|
+
export interface AFSReadOptions extends AFSOperationOptions {
|
|
43
54
|
filter?: AFSListOptions["filter"];
|
|
44
|
-
context?: any;
|
|
45
55
|
}
|
|
46
56
|
export interface AFSReadResult {
|
|
47
57
|
data?: AFSEntry;
|
|
48
58
|
message?: string;
|
|
49
59
|
}
|
|
50
|
-
export interface AFSDeleteOptions {
|
|
60
|
+
export interface AFSDeleteOptions extends AFSOperationOptions {
|
|
51
61
|
recursive?: boolean;
|
|
52
|
-
context?: any;
|
|
53
62
|
}
|
|
54
63
|
export interface AFSDeleteResult {
|
|
55
64
|
message?: string;
|
|
56
65
|
}
|
|
57
|
-
export interface AFSRenameOptions {
|
|
66
|
+
export interface AFSRenameOptions extends AFSOperationOptions {
|
|
58
67
|
overwrite?: boolean;
|
|
59
|
-
context?: any;
|
|
60
68
|
}
|
|
61
69
|
export interface AFSRenameResult {
|
|
62
70
|
message?: string;
|
|
63
71
|
}
|
|
64
|
-
export interface AFSWriteOptions {
|
|
72
|
+
export interface AFSWriteOptions extends AFSOperationOptions {
|
|
65
73
|
append?: boolean;
|
|
66
|
-
context?: any;
|
|
67
74
|
}
|
|
68
75
|
export interface AFSWriteResult {
|
|
69
76
|
data: AFSEntry;
|
|
@@ -72,8 +79,7 @@ export interface AFSWriteResult {
|
|
|
72
79
|
}
|
|
73
80
|
export interface AFSWriteEntryPayload extends Omit<AFSEntry, "id" | "path"> {
|
|
74
81
|
}
|
|
75
|
-
export interface AFSExecOptions {
|
|
76
|
-
context: any;
|
|
82
|
+
export interface AFSExecOptions extends AFSOperationOptions {
|
|
77
83
|
}
|
|
78
84
|
export interface AFSExecResult {
|
|
79
85
|
data: Record<string, any>;
|
|
@@ -81,6 +87,19 @@ export interface AFSExecResult {
|
|
|
81
87
|
export interface AFSModule {
|
|
82
88
|
readonly name: string;
|
|
83
89
|
readonly description?: string;
|
|
90
|
+
/**
|
|
91
|
+
* Access mode for this module.
|
|
92
|
+
* - "readonly": Only read operations are allowed
|
|
93
|
+
* - "readwrite": All operations are allowed
|
|
94
|
+
* Default behavior is implementation-specific.
|
|
95
|
+
*/
|
|
96
|
+
readonly accessMode?: AFSAccessMode;
|
|
97
|
+
/**
|
|
98
|
+
* Enable automatic agent skill scanning for this module.
|
|
99
|
+
* When set to true, the system will scan this module for agent skills.
|
|
100
|
+
* @default false
|
|
101
|
+
*/
|
|
102
|
+
readonly agentSkills?: boolean;
|
|
84
103
|
onMount?(root: AFSRoot): void;
|
|
85
104
|
symlinkToPhysical?(path: string): Promise<void>;
|
|
86
105
|
list?(path: string, options?: AFSListOptions): Promise<AFSListResult>;
|
|
@@ -91,6 +110,39 @@ export interface AFSModule {
|
|
|
91
110
|
search?(path: string, query: string, options?: AFSSearchOptions): Promise<AFSSearchResult>;
|
|
92
111
|
exec?(path: string, args: Record<string, any>, options: AFSExecOptions): Promise<AFSExecResult>;
|
|
93
112
|
}
|
|
113
|
+
/**
|
|
114
|
+
* Parameters for loading a module from configuration.
|
|
115
|
+
*/
|
|
116
|
+
export interface AFSModuleLoadParams {
|
|
117
|
+
/** Path to the configuration file */
|
|
118
|
+
filepath: string;
|
|
119
|
+
/** Parsed configuration object */
|
|
120
|
+
parsed?: object;
|
|
121
|
+
}
|
|
122
|
+
/**
|
|
123
|
+
* Interface for module classes that support schema validation and loading from configuration.
|
|
124
|
+
* This describes the static part of a module class.
|
|
125
|
+
*
|
|
126
|
+
* @example
|
|
127
|
+
* ```typescript
|
|
128
|
+
* class MyModule implements AFSModule {
|
|
129
|
+
* static schema() { return mySchema; }
|
|
130
|
+
* static async load(params: AFSModuleLoadParams) { ... }
|
|
131
|
+
* // ...
|
|
132
|
+
* }
|
|
133
|
+
*
|
|
134
|
+
* // Type check
|
|
135
|
+
* const _check: AFSModuleClass<MyModule, MyModuleOptions> = MyModule;
|
|
136
|
+
* ```
|
|
137
|
+
*/
|
|
138
|
+
export interface AFSModuleClass<T extends AFSModule = AFSModule, O extends object = object> {
|
|
139
|
+
/** Returns the Zod schema for validating module configuration */
|
|
140
|
+
schema(): ZodType<O>;
|
|
141
|
+
/** Loads a module instance from configuration file path and parsed config */
|
|
142
|
+
load(params: AFSModuleLoadParams): Promise<T>;
|
|
143
|
+
/** Constructor */
|
|
144
|
+
new (options: O): T;
|
|
145
|
+
}
|
|
94
146
|
export type AFSRootEvents = {
|
|
95
147
|
agentSucceed: [
|
|
96
148
|
{
|
|
@@ -104,7 +156,7 @@ export type AFSRootEvents = {
|
|
|
104
156
|
];
|
|
105
157
|
historyCreated: [{
|
|
106
158
|
entry: AFSEntry;
|
|
107
|
-
}];
|
|
159
|
+
}, options: AFSOperationOptions];
|
|
108
160
|
};
|
|
109
161
|
export interface AFSRootListOptions extends AFSListOptions, AFSContextPreset {
|
|
110
162
|
preset?: string;
|
package/lib/esm/type.js
CHANGED
|
@@ -1,4 +1,12 @@
|
|
|
1
1
|
import { z } from "zod";
|
|
2
|
+
/**
|
|
3
|
+
* Zod schema for access mode validation.
|
|
4
|
+
* Can be reused across modules that support access mode configuration.
|
|
5
|
+
*/
|
|
6
|
+
export const accessModeSchema = z
|
|
7
|
+
.enum(["readonly", "readwrite"])
|
|
8
|
+
.describe("Access mode for this module")
|
|
9
|
+
.optional();
|
|
2
10
|
export const afsEntrySchema = z.object({
|
|
3
11
|
id: z.string(),
|
|
4
12
|
createdAt: z.date().optional(),
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@aigne/afs",
|
|
3
|
-
"version": "1.4.0
|
|
3
|
+
"version": "1.4.0",
|
|
4
4
|
"description": "Agentic File System (AFS) is a virtual file system that supports various storage backends and provides a unified API for file operations.",
|
|
5
5
|
"publishConfig": {
|
|
6
6
|
"access": "public"
|
|
@@ -50,8 +50,9 @@
|
|
|
50
50
|
"@aigne/uuid": "^13.0.1",
|
|
51
51
|
"strict-event-emitter": "^0.5.1",
|
|
52
52
|
"ufo": "^1.6.1",
|
|
53
|
+
"yaml": "^2.8.1",
|
|
53
54
|
"zod": "^3.25.67",
|
|
54
|
-
"@aigne/platform-helpers": "^0.6.7
|
|
55
|
+
"@aigne/platform-helpers": "^0.6.7"
|
|
55
56
|
},
|
|
56
57
|
"devDependencies": {
|
|
57
58
|
"@types/bun": "^1.2.22",
|