@aigne/afs 1.3.0 → 1.4.0-beta.2

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 CHANGED
@@ -1,5 +1,31 @@
1
1
  # Changelog
2
2
 
3
+ ## [1.4.0-beta.2](https://github.com/AIGNE-io/aigne-framework/compare/afs-v1.4.0-beta.1...afs-v1.4.0-beta.2) (2025-12-19)
4
+
5
+
6
+ ### Bug Fixes
7
+
8
+ * **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))
9
+
10
+ ## [1.4.0-beta.1](https://github.com/AIGNE-io/aigne-framework/compare/afs-v1.4.0-beta...afs-v1.4.0-beta.1) (2025-12-17)
11
+
12
+
13
+ ### Bug Fixes
14
+
15
+ * bump version ([70d217c](https://github.com/AIGNE-io/aigne-framework/commit/70d217c8360dd0dda7f5f17011c4e92ec836e801))
16
+
17
+ ## [1.4.0-beta](https://github.com/AIGNE-io/aigne-framework/compare/afs-v1.3.0...afs-v1.4.0-beta) (2025-12-17)
18
+
19
+
20
+ ### Features
21
+
22
+ * **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))
23
+
24
+
25
+ ### Bug Fixes
26
+
27
+ * **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))
28
+
3
29
  ## [1.3.0](https://github.com/AIGNE-io/aigne-framework/compare/afs-v1.3.0-beta.3...afs-v1.3.0) (2025-12-12)
4
30
 
5
31
  ## [1.3.0-beta.3](https://github.com/AIGNE-io/aigne-framework/compare/afs-v1.3.0-beta.2...afs-v1.3.0-beta.3) (2025-12-11)
package/lib/cjs/afs.d.ts CHANGED
@@ -1,9 +1,11 @@
1
1
  import { Emitter } from "strict-event-emitter";
2
- import type { AFSDeleteOptions, AFSEntry, AFSListOptions, AFSModule, AFSRenameOptions, AFSRoot, AFSRootEvents, AFSSearchOptions, AFSWriteEntryPayload, AFSWriteOptions } from "./type.js";
2
+ import { type AFSContext, type AFSDeleteOptions, type AFSDeleteResult, type AFSExecOptions, type AFSExecResult, type AFSModule, type AFSReadOptions, type AFSReadResult, type AFSRenameOptions, type AFSRenameResult, type AFSRoot, type AFSRootEvents, type AFSRootListOptions, type AFSRootListResult, type AFSRootSearchOptions, type AFSRootSearchResult, type AFSWriteEntryPayload, type AFSWriteOptions, type AFSWriteResult } from "./type.js";
3
3
  export interface AFSOptions {
4
4
  modules?: AFSModule[];
5
+ context?: AFSContext;
5
6
  }
6
7
  export declare class AFS extends Emitter<AFSRootEvents> implements AFSRoot {
8
+ options: AFSOptions;
7
9
  name: string;
8
10
  constructor(options?: AFSOptions);
9
11
  private modules;
@@ -14,32 +16,19 @@ export declare class AFS extends Emitter<AFSRootEvents> implements AFSRoot {
14
16
  description?: string;
15
17
  module: AFSModule;
16
18
  }[]>;
17
- list(path: string, options?: AFSListOptions): Promise<{
18
- list: AFSEntry[];
19
- message?: string;
20
- }>;
21
- read(path: string): Promise<{
22
- result?: AFSEntry;
23
- message?: string;
24
- }>;
25
- write(path: string, content: AFSWriteEntryPayload, options?: AFSWriteOptions): Promise<{
26
- result: AFSEntry;
27
- message?: string;
28
- }>;
29
- delete(path: string, options?: AFSDeleteOptions): Promise<{
30
- message?: string;
31
- }>;
32
- rename(oldPath: string, newPath: string, options?: AFSRenameOptions): Promise<{
33
- message?: string;
34
- }>;
35
- search(path: string, query: string, options?: AFSSearchOptions): Promise<{
36
- list: AFSEntry[];
37
- message?: string;
38
- }>;
19
+ list(path: string, options?: AFSRootListOptions): Promise<AFSRootListResult>;
20
+ private _list;
21
+ read(path: string, _options?: AFSReadOptions): Promise<AFSReadResult>;
22
+ write(path: string, content: AFSWriteEntryPayload, options?: AFSWriteOptions): Promise<AFSWriteResult>;
23
+ delete(path: string, options?: AFSDeleteOptions): Promise<AFSDeleteResult>;
24
+ rename(oldPath: string, newPath: string, options?: AFSRenameOptions): Promise<AFSRenameResult>;
25
+ search(path: string, query: string, options?: AFSRootSearchOptions): Promise<AFSRootSearchResult>;
26
+ private processWithPreset;
27
+ private _select;
28
+ private _search;
39
29
  private findModules;
40
- exec(path: string, args: Record<string, any>, options: {
41
- context: any;
42
- }): Promise<{
43
- result: Record<string, any>;
44
- }>;
30
+ exec(path: string, args: Record<string, any>, options: AFSExecOptions): Promise<AFSExecResult>;
31
+ private buildSimpleListView;
32
+ private buildTreeView;
33
+ private buildMetadataSuffix;
45
34
  }
package/lib/cjs/afs.js CHANGED
@@ -3,12 +3,16 @@ 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 zod_1 = require("zod");
7
+ const type_js_1 = require("./type.js");
6
8
  const DEFAULT_MAX_DEPTH = 1;
7
9
  const MODULES_ROOT_DIR = "/modules";
8
10
  class AFS extends strict_event_emitter_1.Emitter {
11
+ options;
9
12
  name = "AFSRoot";
10
- constructor(options) {
13
+ constructor(options = {}) {
11
14
  super();
15
+ this.options = options;
12
16
  for (const module of options?.modules ?? []) {
13
17
  this.mount(module);
14
18
  }
@@ -35,12 +39,20 @@ class AFS extends strict_event_emitter_1.Emitter {
35
39
  module,
36
40
  }));
37
41
  }
38
- async list(path, options) {
39
- const maxDepth = options?.maxDepth ?? DEFAULT_MAX_DEPTH;
40
- if (!(maxDepth >= 0))
41
- throw new Error(`Invalid maxDepth: ${maxDepth}`);
42
+ async list(path, options = {}) {
43
+ let preset;
44
+ if (options.preset) {
45
+ preset = this.options?.context?.list?.presets?.[options.preset];
46
+ if (!preset)
47
+ throw new Error(`Preset not found: ${options.preset}`);
48
+ }
49
+ return await this.processWithPreset(path, undefined, preset, {
50
+ ...options,
51
+ defaultSelect: () => this._list(path, options),
52
+ });
53
+ }
54
+ async _list(path, options = {}) {
42
55
  const results = [];
43
- const messages = [];
44
56
  const matches = this.findModules(path, options);
45
57
  for (const matched of matches) {
46
58
  const moduleEntry = {
@@ -55,12 +67,12 @@ class AFS extends strict_event_emitter_1.Emitter {
55
67
  if (!matched.module.list)
56
68
  continue;
57
69
  try {
58
- const { list, message } = await matched.module.list(matched.subpath, {
70
+ const { data } = await matched.module.list(matched.subpath, {
59
71
  ...options,
60
72
  maxDepth: matched.maxDepth,
61
73
  });
62
- if (list.length) {
63
- results.push(...list.map((entry) => ({
74
+ if (data.length) {
75
+ results.push(...data.map((entry) => ({
64
76
  ...entry,
65
77
  path: (0, ufo_1.joinURL)(matched.modulePath, entry.path),
66
78
  })));
@@ -68,30 +80,28 @@ class AFS extends strict_event_emitter_1.Emitter {
68
80
  else {
69
81
  results.push(moduleEntry);
70
82
  }
71
- if (message)
72
- messages.push(message);
73
83
  }
74
84
  catch (error) {
75
85
  console.error(`Error listing from module at ${matched.modulePath}`, error);
76
86
  }
77
87
  }
78
- return { list: results, message: messages.join("; ").trim() || undefined };
88
+ return { data: results };
79
89
  }
80
- async read(path) {
90
+ async read(path, _options) {
81
91
  const modules = this.findModules(path, { exactMatch: true });
82
92
  for (const { module, modulePath, subpath } of modules) {
83
93
  const res = await module.read?.(subpath);
84
- if (res?.result) {
94
+ if (res?.data) {
85
95
  return {
86
96
  ...res,
87
- result: {
88
- ...res.result,
89
- path: (0, ufo_1.joinURL)(modulePath, res.result.path),
97
+ data: {
98
+ ...res.data,
99
+ path: (0, ufo_1.joinURL)(modulePath, res.data.path),
90
100
  },
91
101
  };
92
102
  }
93
103
  }
94
- return { result: undefined, message: "File not found" };
104
+ return { data: undefined, message: "File not found" };
95
105
  }
96
106
  async write(path, content, options) {
97
107
  const module = this.findModules(path, { exactMatch: true })[0];
@@ -100,9 +110,9 @@ class AFS extends strict_event_emitter_1.Emitter {
100
110
  const res = await module.module.write(module.subpath, content, options);
101
111
  return {
102
112
  ...res,
103
- result: {
104
- ...res.result,
105
- path: (0, ufo_1.joinURL)(module.modulePath, res.result.path),
113
+ data: {
114
+ ...res.data,
115
+ path: (0, ufo_1.joinURL)(module.modulePath, res.data.path),
106
116
  },
107
117
  };
108
118
  }
@@ -124,15 +134,61 @@ class AFS extends strict_event_emitter_1.Emitter {
124
134
  }
125
135
  return await oldModule.module.rename(oldModule.subpath, newModule.subpath, options);
126
136
  }
127
- async search(path, query, options) {
137
+ async search(path, query, options = {}) {
138
+ let preset;
139
+ if (options.preset) {
140
+ preset = this.options?.context?.search?.presets?.[options.preset];
141
+ if (!preset)
142
+ throw new Error(`Preset not found: ${options.preset}`);
143
+ }
144
+ return await this.processWithPreset(path, query, preset, {
145
+ ...options,
146
+ defaultSelect: () => this._search(path, query, options),
147
+ });
148
+ }
149
+ async processWithPreset(path, query, preset, options) {
150
+ const select = options.select || preset?.select;
151
+ const per = options.per || preset?.per;
152
+ const dedupe = options.dedupe || preset?.dedupe;
153
+ const format = options.format || preset?.format;
154
+ const entries = select
155
+ ? (await this._select(path, query, select, options)).data
156
+ : (await options.defaultSelect()).data;
157
+ const mapped = per
158
+ ? await Promise.all(entries.map((data) => per.invoke({ data }, options).then((res) => res.data)))
159
+ : entries;
160
+ const deduped = dedupe
161
+ ? await dedupe.invoke({ data: mapped }, options).then((res) => res.data)
162
+ : mapped;
163
+ let formatted = deduped;
164
+ if (format === "simple-list" || format === "tree") {
165
+ const valid = zod_1.z.array(type_js_1.afsEntrySchema).safeParse(deduped);
166
+ if (!valid.data)
167
+ throw new Error("Tree format requires entries to be AFSEntry objects");
168
+ if (format === "tree")
169
+ formatted = this.buildTreeView(valid.data);
170
+ else if (format === "simple-list")
171
+ formatted = this.buildSimpleListView(valid.data);
172
+ }
173
+ else if (typeof format === "object" && typeof format.invoke === "function") {
174
+ formatted = await format.invoke({ data: deduped }, options).then((res) => res.data);
175
+ }
176
+ return { data: formatted };
177
+ }
178
+ async _select(path, query, select, options) {
179
+ const { data } = await select.invoke({ path, query }, options);
180
+ const results = (await Promise.all(data.map((p) => this.read(p).then((res) => res.data)))).filter((i) => !!i);
181
+ return { data: results };
182
+ }
183
+ async _search(path, query, options) {
128
184
  const results = [];
129
185
  const messages = [];
130
186
  for (const { module, modulePath, subpath } of this.findModules(path)) {
131
187
  if (!module.search)
132
188
  continue;
133
189
  try {
134
- const { list, message } = await module.search(subpath, query, options);
135
- results.push(...list.map((entry) => ({
190
+ const { data, message } = await module.search(subpath, query, options);
191
+ results.push(...data.map((entry) => ({
136
192
  ...entry,
137
193
  path: (0, ufo_1.joinURL)(modulePath, entry.path),
138
194
  })));
@@ -143,7 +199,7 @@ class AFS extends strict_event_emitter_1.Emitter {
143
199
  console.error(`Error searching in module at ${modulePath}`, error);
144
200
  }
145
201
  }
146
- return { list: results, message: messages.join("; ") };
202
+ return { data: results, message: messages.join("; ") };
147
203
  }
148
204
  findModules(path, options) {
149
205
  const maxDepth = Math.max(options?.maxDepth ?? DEFAULT_MAX_DEPTH, 1);
@@ -179,5 +235,56 @@ class AFS extends strict_event_emitter_1.Emitter {
179
235
  throw new Error(`No module found for path: ${path}`);
180
236
  return await module.module.exec(module.subpath, args, options);
181
237
  }
238
+ buildSimpleListView(entries) {
239
+ return entries.map((entry) => `${entry.path}${this.buildMetadataSuffix(entry)}`);
240
+ }
241
+ buildTreeView(entries) {
242
+ const tree = {};
243
+ const entryMap = new Map();
244
+ for (const entry of entries) {
245
+ entryMap.set(entry.path, entry);
246
+ const parts = entry.path.split("/").filter(Boolean);
247
+ let current = tree;
248
+ for (const part of parts) {
249
+ if (!current[part]) {
250
+ current[part] = {};
251
+ }
252
+ current = current[part];
253
+ }
254
+ }
255
+ const renderTree = (node, prefix = "", currentPath = "") => {
256
+ let result = "";
257
+ const keys = Object.keys(node);
258
+ keys.forEach((key, index) => {
259
+ const isLast = index === keys.length - 1;
260
+ const fullPath = currentPath ? `${currentPath}/${key}` : `/${key}`;
261
+ const entry = entryMap.get(fullPath);
262
+ result += `${prefix}${isLast ? "└── " : "├── "}${key}${entry ? this.buildMetadataSuffix(entry) : ""}`;
263
+ result += `\n`;
264
+ result += renderTree(node[key], `${prefix}${isLast ? " " : "│ "}`, fullPath);
265
+ });
266
+ return result;
267
+ };
268
+ return renderTree(tree);
269
+ }
270
+ buildMetadataSuffix(entry) {
271
+ // Build metadata suffix
272
+ const metadataParts = [];
273
+ // Children count
274
+ const childrenCount = entry?.metadata?.childrenCount;
275
+ if (typeof childrenCount === "number") {
276
+ metadataParts.push(`${childrenCount} items`);
277
+ }
278
+ // Children truncated
279
+ if (entry?.metadata?.childrenTruncated) {
280
+ metadataParts.push("truncated");
281
+ }
282
+ // Executable
283
+ if (entry?.metadata?.execute) {
284
+ metadataParts.push("executable");
285
+ }
286
+ const metadataSuffix = metadataParts.length > 0 ? ` [${metadataParts.join(", ")}]` : "";
287
+ return metadataSuffix;
288
+ }
182
289
  }
183
290
  exports.AFS = AFS;
package/lib/cjs/type.d.ts CHANGED
@@ -1,60 +1,85 @@
1
1
  import type { Emitter } from "strict-event-emitter";
2
+ import { type ZodType } from "zod";
2
3
  export interface AFSListOptions {
3
4
  filter?: {
4
5
  userId?: string;
5
6
  sessionId?: string;
6
7
  };
7
- recursive?: boolean;
8
8
  maxDepth?: number;
9
9
  limit?: number;
10
10
  orderBy?: [string, "asc" | "desc"][];
11
+ maxChildren?: number;
12
+ onOverflow?: "truncate";
13
+ /**
14
+ * Whether to disable .gitignore files when listing files.
15
+ * @default false
16
+ */
17
+ disableGitignore?: boolean;
18
+ context?: any;
19
+ }
20
+ export interface AFSListResult {
21
+ data: AFSEntry[];
22
+ message?: string;
23
+ context?: any;
11
24
  }
12
25
  export interface AFSSearchOptions {
13
26
  limit?: number;
14
27
  caseSensitive?: boolean;
28
+ context?: any;
29
+ }
30
+ export interface AFSSearchResult {
31
+ data: AFSEntry[];
32
+ message?: string;
33
+ }
34
+ export interface AFSReadOptions {
35
+ context?: any;
36
+ }
37
+ export interface AFSReadResult {
38
+ data?: AFSEntry;
39
+ message?: string;
15
40
  }
16
41
  export interface AFSDeleteOptions {
17
42
  recursive?: boolean;
43
+ context?: any;
44
+ }
45
+ export interface AFSDeleteResult {
46
+ message?: string;
18
47
  }
19
48
  export interface AFSRenameOptions {
20
49
  overwrite?: boolean;
50
+ context?: any;
51
+ }
52
+ export interface AFSRenameResult {
53
+ message?: string;
21
54
  }
22
55
  export interface AFSWriteOptions {
23
56
  append?: boolean;
57
+ context?: any;
58
+ }
59
+ export interface AFSWriteResult {
60
+ data: AFSEntry;
61
+ message?: string;
62
+ context?: any;
24
63
  }
25
64
  export interface AFSWriteEntryPayload extends Omit<AFSEntry, "id" | "path"> {
26
65
  }
66
+ export interface AFSExecOptions {
67
+ context: any;
68
+ }
69
+ export interface AFSExecResult {
70
+ data: Record<string, any>;
71
+ }
27
72
  export interface AFSModule {
28
73
  readonly name: string;
29
74
  readonly description?: string;
30
75
  onMount?(root: AFSRoot): void;
31
- list?(path: string, options?: AFSListOptions): Promise<{
32
- list: AFSEntry[];
33
- message?: string;
34
- }>;
35
- read?(path: string): Promise<{
36
- result?: AFSEntry;
37
- message?: string;
38
- }>;
39
- write?(path: string, content: AFSWriteEntryPayload, options?: AFSWriteOptions): Promise<{
40
- result: AFSEntry;
41
- message?: string;
42
- }>;
43
- delete?(path: string, options?: AFSDeleteOptions): Promise<{
44
- message?: string;
45
- }>;
46
- rename?(oldPath: string, newPath: string, options?: AFSRenameOptions): Promise<{
47
- message?: string;
48
- }>;
49
- search?(path: string, query: string, options?: AFSSearchOptions): Promise<{
50
- list: AFSEntry[];
51
- message?: string;
52
- }>;
53
- exec?(path: string, args: Record<string, any>, options: {
54
- context: any;
55
- }): Promise<{
56
- result: Record<string, any>;
57
- }>;
76
+ list?(path: string, options?: AFSListOptions): Promise<AFSListResult>;
77
+ read?(path: string, options?: AFSReadOptions): Promise<AFSReadResult>;
78
+ write?(path: string, content: AFSWriteEntryPayload, options?: AFSWriteOptions): Promise<AFSWriteResult>;
79
+ delete?(path: string, options?: AFSDeleteOptions): Promise<AFSDeleteResult>;
80
+ rename?(oldPath: string, newPath: string, options?: AFSRenameOptions): Promise<AFSRenameResult>;
81
+ search?(path: string, query: string, options?: AFSSearchOptions): Promise<AFSSearchResult>;
82
+ exec?(path: string, args: Record<string, any>, options: AFSExecOptions): Promise<AFSExecResult>;
58
83
  }
59
84
  export type AFSRootEvents = {
60
85
  agentSucceed: [{
@@ -65,7 +90,21 @@ export type AFSRootEvents = {
65
90
  entry: AFSEntry;
66
91
  }];
67
92
  };
93
+ export interface AFSRootListOptions extends AFSListOptions, AFSContextPreset {
94
+ preset?: string;
95
+ }
96
+ export interface AFSRootListResult extends Omit<AFSListResult, "data"> {
97
+ data: any;
98
+ }
99
+ export interface AFSRootSearchOptions extends AFSSearchOptions, AFSContextPreset {
100
+ preset?: string;
101
+ }
102
+ export interface AFSRootSearchResult extends Omit<AFSSearchResult, "data"> {
103
+ data: any;
104
+ }
68
105
  export interface AFSRoot extends Emitter<AFSRootEvents>, AFSModule {
106
+ list(path: string, options?: AFSRootListOptions): Promise<AFSRootListResult>;
107
+ search(path: string, query: string, options: AFSRootSearchOptions): Promise<AFSRootSearchResult>;
69
108
  }
70
109
  export interface AFSEntryMetadata extends Record<string, any> {
71
110
  execute?: {
@@ -75,6 +114,7 @@ export interface AFSEntryMetadata extends Record<string, any> {
75
114
  outputSchema?: Record<string, any>;
76
115
  };
77
116
  childrenCount?: number;
117
+ childrenTruncated?: boolean;
78
118
  }
79
119
  export interface AFSEntry<T = any> {
80
120
  id: string;
@@ -89,3 +129,42 @@ export interface AFSEntry<T = any> {
89
129
  linkTo?: string | null;
90
130
  content?: T;
91
131
  }
132
+ export declare const afsEntrySchema: ZodType<AFSEntry>;
133
+ export interface AFSContextPreset {
134
+ /**
135
+ * The view template for presenting the search results.
136
+ */
137
+ view?: string;
138
+ select?: AFSContextPresetOptionAgent<{
139
+ path: string;
140
+ query?: string;
141
+ }, {
142
+ data: string[];
143
+ }>;
144
+ per?: AFSContextPresetOptionAgent<{
145
+ data: AFSEntry;
146
+ }, {
147
+ data: unknown;
148
+ }>;
149
+ dedupe?: AFSContextPresetOptionAgent<{
150
+ data: unknown[];
151
+ }, {
152
+ data: unknown;
153
+ }>;
154
+ format?: "default" | "simple-list" | "tree" | AFSContextPresetOptionAgent<{
155
+ data: unknown;
156
+ }, {
157
+ data: unknown;
158
+ }>;
159
+ }
160
+ export interface AFSContextPresetOptionAgent<I = any, O = any> {
161
+ invoke(input: I, options?: any): Promise<O>;
162
+ }
163
+ export interface AFSContext {
164
+ search?: {
165
+ presets?: Record<string, AFSContextPreset>;
166
+ };
167
+ list?: {
168
+ presets?: Record<string, AFSContextPreset>;
169
+ };
170
+ }
package/lib/cjs/type.js CHANGED
@@ -1,2 +1,17 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.afsEntrySchema = void 0;
4
+ const zod_1 = require("zod");
5
+ exports.afsEntrySchema = zod_1.z.object({
6
+ id: zod_1.z.string(),
7
+ createdAt: zod_1.z.date().optional(),
8
+ updatedAt: zod_1.z.date().optional(),
9
+ path: zod_1.z.string(),
10
+ userId: zod_1.z.string().nullable().optional(),
11
+ sessionId: zod_1.z.string().nullable().optional(),
12
+ summary: zod_1.z.string().nullable().optional(),
13
+ description: zod_1.z.string().nullable().optional(),
14
+ metadata: zod_1.z.record(zod_1.z.any()).nullable().optional(),
15
+ linkTo: zod_1.z.string().nullable().optional(),
16
+ content: zod_1.z.any().optional(),
17
+ });
package/lib/dts/afs.d.ts CHANGED
@@ -1,9 +1,11 @@
1
1
  import { Emitter } from "strict-event-emitter";
2
- import type { AFSDeleteOptions, AFSEntry, AFSListOptions, AFSModule, AFSRenameOptions, AFSRoot, AFSRootEvents, AFSSearchOptions, AFSWriteEntryPayload, AFSWriteOptions } from "./type.js";
2
+ import { type AFSContext, type AFSDeleteOptions, type AFSDeleteResult, type AFSExecOptions, type AFSExecResult, type AFSModule, type AFSReadOptions, type AFSReadResult, type AFSRenameOptions, type AFSRenameResult, type AFSRoot, type AFSRootEvents, type AFSRootListOptions, type AFSRootListResult, type AFSRootSearchOptions, type AFSRootSearchResult, type AFSWriteEntryPayload, type AFSWriteOptions, type AFSWriteResult } from "./type.js";
3
3
  export interface AFSOptions {
4
4
  modules?: AFSModule[];
5
+ context?: AFSContext;
5
6
  }
6
7
  export declare class AFS extends Emitter<AFSRootEvents> implements AFSRoot {
8
+ options: AFSOptions;
7
9
  name: string;
8
10
  constructor(options?: AFSOptions);
9
11
  private modules;
@@ -14,32 +16,19 @@ export declare class AFS extends Emitter<AFSRootEvents> implements AFSRoot {
14
16
  description?: string;
15
17
  module: AFSModule;
16
18
  }[]>;
17
- list(path: string, options?: AFSListOptions): Promise<{
18
- list: AFSEntry[];
19
- message?: string;
20
- }>;
21
- read(path: string): Promise<{
22
- result?: AFSEntry;
23
- message?: string;
24
- }>;
25
- write(path: string, content: AFSWriteEntryPayload, options?: AFSWriteOptions): Promise<{
26
- result: AFSEntry;
27
- message?: string;
28
- }>;
29
- delete(path: string, options?: AFSDeleteOptions): Promise<{
30
- message?: string;
31
- }>;
32
- rename(oldPath: string, newPath: string, options?: AFSRenameOptions): Promise<{
33
- message?: string;
34
- }>;
35
- search(path: string, query: string, options?: AFSSearchOptions): Promise<{
36
- list: AFSEntry[];
37
- message?: string;
38
- }>;
19
+ list(path: string, options?: AFSRootListOptions): Promise<AFSRootListResult>;
20
+ private _list;
21
+ read(path: string, _options?: AFSReadOptions): Promise<AFSReadResult>;
22
+ write(path: string, content: AFSWriteEntryPayload, options?: AFSWriteOptions): Promise<AFSWriteResult>;
23
+ delete(path: string, options?: AFSDeleteOptions): Promise<AFSDeleteResult>;
24
+ rename(oldPath: string, newPath: string, options?: AFSRenameOptions): Promise<AFSRenameResult>;
25
+ search(path: string, query: string, options?: AFSRootSearchOptions): Promise<AFSRootSearchResult>;
26
+ private processWithPreset;
27
+ private _select;
28
+ private _search;
39
29
  private findModules;
40
- exec(path: string, args: Record<string, any>, options: {
41
- context: any;
42
- }): Promise<{
43
- result: Record<string, any>;
44
- }>;
30
+ exec(path: string, args: Record<string, any>, options: AFSExecOptions): Promise<AFSExecResult>;
31
+ private buildSimpleListView;
32
+ private buildTreeView;
33
+ private buildMetadataSuffix;
45
34
  }
package/lib/dts/type.d.ts CHANGED
@@ -1,60 +1,85 @@
1
1
  import type { Emitter } from "strict-event-emitter";
2
+ import { type ZodType } from "zod";
2
3
  export interface AFSListOptions {
3
4
  filter?: {
4
5
  userId?: string;
5
6
  sessionId?: string;
6
7
  };
7
- recursive?: boolean;
8
8
  maxDepth?: number;
9
9
  limit?: number;
10
10
  orderBy?: [string, "asc" | "desc"][];
11
+ maxChildren?: number;
12
+ onOverflow?: "truncate";
13
+ /**
14
+ * Whether to disable .gitignore files when listing files.
15
+ * @default false
16
+ */
17
+ disableGitignore?: boolean;
18
+ context?: any;
19
+ }
20
+ export interface AFSListResult {
21
+ data: AFSEntry[];
22
+ message?: string;
23
+ context?: any;
11
24
  }
12
25
  export interface AFSSearchOptions {
13
26
  limit?: number;
14
27
  caseSensitive?: boolean;
28
+ context?: any;
29
+ }
30
+ export interface AFSSearchResult {
31
+ data: AFSEntry[];
32
+ message?: string;
33
+ }
34
+ export interface AFSReadOptions {
35
+ context?: any;
36
+ }
37
+ export interface AFSReadResult {
38
+ data?: AFSEntry;
39
+ message?: string;
15
40
  }
16
41
  export interface AFSDeleteOptions {
17
42
  recursive?: boolean;
43
+ context?: any;
44
+ }
45
+ export interface AFSDeleteResult {
46
+ message?: string;
18
47
  }
19
48
  export interface AFSRenameOptions {
20
49
  overwrite?: boolean;
50
+ context?: any;
51
+ }
52
+ export interface AFSRenameResult {
53
+ message?: string;
21
54
  }
22
55
  export interface AFSWriteOptions {
23
56
  append?: boolean;
57
+ context?: any;
58
+ }
59
+ export interface AFSWriteResult {
60
+ data: AFSEntry;
61
+ message?: string;
62
+ context?: any;
24
63
  }
25
64
  export interface AFSWriteEntryPayload extends Omit<AFSEntry, "id" | "path"> {
26
65
  }
66
+ export interface AFSExecOptions {
67
+ context: any;
68
+ }
69
+ export interface AFSExecResult {
70
+ data: Record<string, any>;
71
+ }
27
72
  export interface AFSModule {
28
73
  readonly name: string;
29
74
  readonly description?: string;
30
75
  onMount?(root: AFSRoot): void;
31
- list?(path: string, options?: AFSListOptions): Promise<{
32
- list: AFSEntry[];
33
- message?: string;
34
- }>;
35
- read?(path: string): Promise<{
36
- result?: AFSEntry;
37
- message?: string;
38
- }>;
39
- write?(path: string, content: AFSWriteEntryPayload, options?: AFSWriteOptions): Promise<{
40
- result: AFSEntry;
41
- message?: string;
42
- }>;
43
- delete?(path: string, options?: AFSDeleteOptions): Promise<{
44
- message?: string;
45
- }>;
46
- rename?(oldPath: string, newPath: string, options?: AFSRenameOptions): Promise<{
47
- message?: string;
48
- }>;
49
- search?(path: string, query: string, options?: AFSSearchOptions): Promise<{
50
- list: AFSEntry[];
51
- message?: string;
52
- }>;
53
- exec?(path: string, args: Record<string, any>, options: {
54
- context: any;
55
- }): Promise<{
56
- result: Record<string, any>;
57
- }>;
76
+ list?(path: string, options?: AFSListOptions): Promise<AFSListResult>;
77
+ read?(path: string, options?: AFSReadOptions): Promise<AFSReadResult>;
78
+ write?(path: string, content: AFSWriteEntryPayload, options?: AFSWriteOptions): Promise<AFSWriteResult>;
79
+ delete?(path: string, options?: AFSDeleteOptions): Promise<AFSDeleteResult>;
80
+ rename?(oldPath: string, newPath: string, options?: AFSRenameOptions): Promise<AFSRenameResult>;
81
+ search?(path: string, query: string, options?: AFSSearchOptions): Promise<AFSSearchResult>;
82
+ exec?(path: string, args: Record<string, any>, options: AFSExecOptions): Promise<AFSExecResult>;
58
83
  }
59
84
  export type AFSRootEvents = {
60
85
  agentSucceed: [{
@@ -65,7 +90,21 @@ export type AFSRootEvents = {
65
90
  entry: AFSEntry;
66
91
  }];
67
92
  };
93
+ export interface AFSRootListOptions extends AFSListOptions, AFSContextPreset {
94
+ preset?: string;
95
+ }
96
+ export interface AFSRootListResult extends Omit<AFSListResult, "data"> {
97
+ data: any;
98
+ }
99
+ export interface AFSRootSearchOptions extends AFSSearchOptions, AFSContextPreset {
100
+ preset?: string;
101
+ }
102
+ export interface AFSRootSearchResult extends Omit<AFSSearchResult, "data"> {
103
+ data: any;
104
+ }
68
105
  export interface AFSRoot extends Emitter<AFSRootEvents>, AFSModule {
106
+ list(path: string, options?: AFSRootListOptions): Promise<AFSRootListResult>;
107
+ search(path: string, query: string, options: AFSRootSearchOptions): Promise<AFSRootSearchResult>;
69
108
  }
70
109
  export interface AFSEntryMetadata extends Record<string, any> {
71
110
  execute?: {
@@ -75,6 +114,7 @@ export interface AFSEntryMetadata extends Record<string, any> {
75
114
  outputSchema?: Record<string, any>;
76
115
  };
77
116
  childrenCount?: number;
117
+ childrenTruncated?: boolean;
78
118
  }
79
119
  export interface AFSEntry<T = any> {
80
120
  id: string;
@@ -89,3 +129,42 @@ export interface AFSEntry<T = any> {
89
129
  linkTo?: string | null;
90
130
  content?: T;
91
131
  }
132
+ export declare const afsEntrySchema: ZodType<AFSEntry>;
133
+ export interface AFSContextPreset {
134
+ /**
135
+ * The view template for presenting the search results.
136
+ */
137
+ view?: string;
138
+ select?: AFSContextPresetOptionAgent<{
139
+ path: string;
140
+ query?: string;
141
+ }, {
142
+ data: string[];
143
+ }>;
144
+ per?: AFSContextPresetOptionAgent<{
145
+ data: AFSEntry;
146
+ }, {
147
+ data: unknown;
148
+ }>;
149
+ dedupe?: AFSContextPresetOptionAgent<{
150
+ data: unknown[];
151
+ }, {
152
+ data: unknown;
153
+ }>;
154
+ format?: "default" | "simple-list" | "tree" | AFSContextPresetOptionAgent<{
155
+ data: unknown;
156
+ }, {
157
+ data: unknown;
158
+ }>;
159
+ }
160
+ export interface AFSContextPresetOptionAgent<I = any, O = any> {
161
+ invoke(input: I, options?: any): Promise<O>;
162
+ }
163
+ export interface AFSContext {
164
+ search?: {
165
+ presets?: Record<string, AFSContextPreset>;
166
+ };
167
+ list?: {
168
+ presets?: Record<string, AFSContextPreset>;
169
+ };
170
+ }
package/lib/esm/afs.d.ts CHANGED
@@ -1,9 +1,11 @@
1
1
  import { Emitter } from "strict-event-emitter";
2
- import type { AFSDeleteOptions, AFSEntry, AFSListOptions, AFSModule, AFSRenameOptions, AFSRoot, AFSRootEvents, AFSSearchOptions, AFSWriteEntryPayload, AFSWriteOptions } from "./type.js";
2
+ import { type AFSContext, type AFSDeleteOptions, type AFSDeleteResult, type AFSExecOptions, type AFSExecResult, type AFSModule, type AFSReadOptions, type AFSReadResult, type AFSRenameOptions, type AFSRenameResult, type AFSRoot, type AFSRootEvents, type AFSRootListOptions, type AFSRootListResult, type AFSRootSearchOptions, type AFSRootSearchResult, type AFSWriteEntryPayload, type AFSWriteOptions, type AFSWriteResult } from "./type.js";
3
3
  export interface AFSOptions {
4
4
  modules?: AFSModule[];
5
+ context?: AFSContext;
5
6
  }
6
7
  export declare class AFS extends Emitter<AFSRootEvents> implements AFSRoot {
8
+ options: AFSOptions;
7
9
  name: string;
8
10
  constructor(options?: AFSOptions);
9
11
  private modules;
@@ -14,32 +16,19 @@ export declare class AFS extends Emitter<AFSRootEvents> implements AFSRoot {
14
16
  description?: string;
15
17
  module: AFSModule;
16
18
  }[]>;
17
- list(path: string, options?: AFSListOptions): Promise<{
18
- list: AFSEntry[];
19
- message?: string;
20
- }>;
21
- read(path: string): Promise<{
22
- result?: AFSEntry;
23
- message?: string;
24
- }>;
25
- write(path: string, content: AFSWriteEntryPayload, options?: AFSWriteOptions): Promise<{
26
- result: AFSEntry;
27
- message?: string;
28
- }>;
29
- delete(path: string, options?: AFSDeleteOptions): Promise<{
30
- message?: string;
31
- }>;
32
- rename(oldPath: string, newPath: string, options?: AFSRenameOptions): Promise<{
33
- message?: string;
34
- }>;
35
- search(path: string, query: string, options?: AFSSearchOptions): Promise<{
36
- list: AFSEntry[];
37
- message?: string;
38
- }>;
19
+ list(path: string, options?: AFSRootListOptions): Promise<AFSRootListResult>;
20
+ private _list;
21
+ read(path: string, _options?: AFSReadOptions): Promise<AFSReadResult>;
22
+ write(path: string, content: AFSWriteEntryPayload, options?: AFSWriteOptions): Promise<AFSWriteResult>;
23
+ delete(path: string, options?: AFSDeleteOptions): Promise<AFSDeleteResult>;
24
+ rename(oldPath: string, newPath: string, options?: AFSRenameOptions): Promise<AFSRenameResult>;
25
+ search(path: string, query: string, options?: AFSRootSearchOptions): Promise<AFSRootSearchResult>;
26
+ private processWithPreset;
27
+ private _select;
28
+ private _search;
39
29
  private findModules;
40
- exec(path: string, args: Record<string, any>, options: {
41
- context: any;
42
- }): Promise<{
43
- result: Record<string, any>;
44
- }>;
30
+ exec(path: string, args: Record<string, any>, options: AFSExecOptions): Promise<AFSExecResult>;
31
+ private buildSimpleListView;
32
+ private buildTreeView;
33
+ private buildMetadataSuffix;
45
34
  }
package/lib/esm/afs.js CHANGED
@@ -1,11 +1,15 @@
1
1
  import { Emitter } from "strict-event-emitter";
2
2
  import { joinURL } from "ufo";
3
+ import { z } from "zod";
4
+ import { afsEntrySchema, } from "./type.js";
3
5
  const DEFAULT_MAX_DEPTH = 1;
4
6
  const MODULES_ROOT_DIR = "/modules";
5
7
  export class AFS extends Emitter {
8
+ options;
6
9
  name = "AFSRoot";
7
- constructor(options) {
10
+ constructor(options = {}) {
8
11
  super();
12
+ this.options = options;
9
13
  for (const module of options?.modules ?? []) {
10
14
  this.mount(module);
11
15
  }
@@ -32,12 +36,20 @@ export class AFS extends Emitter {
32
36
  module,
33
37
  }));
34
38
  }
35
- async list(path, options) {
36
- const maxDepth = options?.maxDepth ?? DEFAULT_MAX_DEPTH;
37
- if (!(maxDepth >= 0))
38
- throw new Error(`Invalid maxDepth: ${maxDepth}`);
39
+ async list(path, options = {}) {
40
+ let preset;
41
+ if (options.preset) {
42
+ preset = this.options?.context?.list?.presets?.[options.preset];
43
+ if (!preset)
44
+ throw new Error(`Preset not found: ${options.preset}`);
45
+ }
46
+ return await this.processWithPreset(path, undefined, preset, {
47
+ ...options,
48
+ defaultSelect: () => this._list(path, options),
49
+ });
50
+ }
51
+ async _list(path, options = {}) {
39
52
  const results = [];
40
- const messages = [];
41
53
  const matches = this.findModules(path, options);
42
54
  for (const matched of matches) {
43
55
  const moduleEntry = {
@@ -52,12 +64,12 @@ export class AFS extends Emitter {
52
64
  if (!matched.module.list)
53
65
  continue;
54
66
  try {
55
- const { list, message } = await matched.module.list(matched.subpath, {
67
+ const { data } = await matched.module.list(matched.subpath, {
56
68
  ...options,
57
69
  maxDepth: matched.maxDepth,
58
70
  });
59
- if (list.length) {
60
- results.push(...list.map((entry) => ({
71
+ if (data.length) {
72
+ results.push(...data.map((entry) => ({
61
73
  ...entry,
62
74
  path: joinURL(matched.modulePath, entry.path),
63
75
  })));
@@ -65,30 +77,28 @@ export class AFS extends Emitter {
65
77
  else {
66
78
  results.push(moduleEntry);
67
79
  }
68
- if (message)
69
- messages.push(message);
70
80
  }
71
81
  catch (error) {
72
82
  console.error(`Error listing from module at ${matched.modulePath}`, error);
73
83
  }
74
84
  }
75
- return { list: results, message: messages.join("; ").trim() || undefined };
85
+ return { data: results };
76
86
  }
77
- async read(path) {
87
+ async read(path, _options) {
78
88
  const modules = this.findModules(path, { exactMatch: true });
79
89
  for (const { module, modulePath, subpath } of modules) {
80
90
  const res = await module.read?.(subpath);
81
- if (res?.result) {
91
+ if (res?.data) {
82
92
  return {
83
93
  ...res,
84
- result: {
85
- ...res.result,
86
- path: joinURL(modulePath, res.result.path),
94
+ data: {
95
+ ...res.data,
96
+ path: joinURL(modulePath, res.data.path),
87
97
  },
88
98
  };
89
99
  }
90
100
  }
91
- return { result: undefined, message: "File not found" };
101
+ return { data: undefined, message: "File not found" };
92
102
  }
93
103
  async write(path, content, options) {
94
104
  const module = this.findModules(path, { exactMatch: true })[0];
@@ -97,9 +107,9 @@ export class AFS extends Emitter {
97
107
  const res = await module.module.write(module.subpath, content, options);
98
108
  return {
99
109
  ...res,
100
- result: {
101
- ...res.result,
102
- path: joinURL(module.modulePath, res.result.path),
110
+ data: {
111
+ ...res.data,
112
+ path: joinURL(module.modulePath, res.data.path),
103
113
  },
104
114
  };
105
115
  }
@@ -121,15 +131,61 @@ export class AFS extends Emitter {
121
131
  }
122
132
  return await oldModule.module.rename(oldModule.subpath, newModule.subpath, options);
123
133
  }
124
- async search(path, query, options) {
134
+ async search(path, query, options = {}) {
135
+ let preset;
136
+ if (options.preset) {
137
+ preset = this.options?.context?.search?.presets?.[options.preset];
138
+ if (!preset)
139
+ throw new Error(`Preset not found: ${options.preset}`);
140
+ }
141
+ return await this.processWithPreset(path, query, preset, {
142
+ ...options,
143
+ defaultSelect: () => this._search(path, query, options),
144
+ });
145
+ }
146
+ async processWithPreset(path, query, preset, options) {
147
+ const select = options.select || preset?.select;
148
+ const per = options.per || preset?.per;
149
+ const dedupe = options.dedupe || preset?.dedupe;
150
+ const format = options.format || preset?.format;
151
+ const entries = select
152
+ ? (await this._select(path, query, select, options)).data
153
+ : (await options.defaultSelect()).data;
154
+ const mapped = per
155
+ ? await Promise.all(entries.map((data) => per.invoke({ data }, options).then((res) => res.data)))
156
+ : entries;
157
+ const deduped = dedupe
158
+ ? await dedupe.invoke({ data: mapped }, options).then((res) => res.data)
159
+ : mapped;
160
+ let formatted = deduped;
161
+ if (format === "simple-list" || format === "tree") {
162
+ const valid = z.array(afsEntrySchema).safeParse(deduped);
163
+ if (!valid.data)
164
+ throw new Error("Tree format requires entries to be AFSEntry objects");
165
+ if (format === "tree")
166
+ formatted = this.buildTreeView(valid.data);
167
+ else if (format === "simple-list")
168
+ formatted = this.buildSimpleListView(valid.data);
169
+ }
170
+ else if (typeof format === "object" && typeof format.invoke === "function") {
171
+ formatted = await format.invoke({ data: deduped }, options).then((res) => res.data);
172
+ }
173
+ return { data: formatted };
174
+ }
175
+ async _select(path, query, select, options) {
176
+ const { data } = await select.invoke({ path, query }, options);
177
+ const results = (await Promise.all(data.map((p) => this.read(p).then((res) => res.data)))).filter((i) => !!i);
178
+ return { data: results };
179
+ }
180
+ async _search(path, query, options) {
125
181
  const results = [];
126
182
  const messages = [];
127
183
  for (const { module, modulePath, subpath } of this.findModules(path)) {
128
184
  if (!module.search)
129
185
  continue;
130
186
  try {
131
- const { list, message } = await module.search(subpath, query, options);
132
- results.push(...list.map((entry) => ({
187
+ const { data, message } = await module.search(subpath, query, options);
188
+ results.push(...data.map((entry) => ({
133
189
  ...entry,
134
190
  path: joinURL(modulePath, entry.path),
135
191
  })));
@@ -140,7 +196,7 @@ export class AFS extends Emitter {
140
196
  console.error(`Error searching in module at ${modulePath}`, error);
141
197
  }
142
198
  }
143
- return { list: results, message: messages.join("; ") };
199
+ return { data: results, message: messages.join("; ") };
144
200
  }
145
201
  findModules(path, options) {
146
202
  const maxDepth = Math.max(options?.maxDepth ?? DEFAULT_MAX_DEPTH, 1);
@@ -176,4 +232,55 @@ export class AFS extends Emitter {
176
232
  throw new Error(`No module found for path: ${path}`);
177
233
  return await module.module.exec(module.subpath, args, options);
178
234
  }
235
+ buildSimpleListView(entries) {
236
+ return entries.map((entry) => `${entry.path}${this.buildMetadataSuffix(entry)}`);
237
+ }
238
+ buildTreeView(entries) {
239
+ const tree = {};
240
+ const entryMap = new Map();
241
+ for (const entry of entries) {
242
+ entryMap.set(entry.path, entry);
243
+ const parts = entry.path.split("/").filter(Boolean);
244
+ let current = tree;
245
+ for (const part of parts) {
246
+ if (!current[part]) {
247
+ current[part] = {};
248
+ }
249
+ current = current[part];
250
+ }
251
+ }
252
+ const renderTree = (node, prefix = "", currentPath = "") => {
253
+ let result = "";
254
+ const keys = Object.keys(node);
255
+ keys.forEach((key, index) => {
256
+ const isLast = index === keys.length - 1;
257
+ const fullPath = currentPath ? `${currentPath}/${key}` : `/${key}`;
258
+ const entry = entryMap.get(fullPath);
259
+ result += `${prefix}${isLast ? "└── " : "├── "}${key}${entry ? this.buildMetadataSuffix(entry) : ""}`;
260
+ result += `\n`;
261
+ result += renderTree(node[key], `${prefix}${isLast ? " " : "│ "}`, fullPath);
262
+ });
263
+ return result;
264
+ };
265
+ return renderTree(tree);
266
+ }
267
+ buildMetadataSuffix(entry) {
268
+ // Build metadata suffix
269
+ const metadataParts = [];
270
+ // Children count
271
+ const childrenCount = entry?.metadata?.childrenCount;
272
+ if (typeof childrenCount === "number") {
273
+ metadataParts.push(`${childrenCount} items`);
274
+ }
275
+ // Children truncated
276
+ if (entry?.metadata?.childrenTruncated) {
277
+ metadataParts.push("truncated");
278
+ }
279
+ // Executable
280
+ if (entry?.metadata?.execute) {
281
+ metadataParts.push("executable");
282
+ }
283
+ const metadataSuffix = metadataParts.length > 0 ? ` [${metadataParts.join(", ")}]` : "";
284
+ return metadataSuffix;
285
+ }
179
286
  }
package/lib/esm/type.d.ts CHANGED
@@ -1,60 +1,85 @@
1
1
  import type { Emitter } from "strict-event-emitter";
2
+ import { type ZodType } from "zod";
2
3
  export interface AFSListOptions {
3
4
  filter?: {
4
5
  userId?: string;
5
6
  sessionId?: string;
6
7
  };
7
- recursive?: boolean;
8
8
  maxDepth?: number;
9
9
  limit?: number;
10
10
  orderBy?: [string, "asc" | "desc"][];
11
+ maxChildren?: number;
12
+ onOverflow?: "truncate";
13
+ /**
14
+ * Whether to disable .gitignore files when listing files.
15
+ * @default false
16
+ */
17
+ disableGitignore?: boolean;
18
+ context?: any;
19
+ }
20
+ export interface AFSListResult {
21
+ data: AFSEntry[];
22
+ message?: string;
23
+ context?: any;
11
24
  }
12
25
  export interface AFSSearchOptions {
13
26
  limit?: number;
14
27
  caseSensitive?: boolean;
28
+ context?: any;
29
+ }
30
+ export interface AFSSearchResult {
31
+ data: AFSEntry[];
32
+ message?: string;
33
+ }
34
+ export interface AFSReadOptions {
35
+ context?: any;
36
+ }
37
+ export interface AFSReadResult {
38
+ data?: AFSEntry;
39
+ message?: string;
15
40
  }
16
41
  export interface AFSDeleteOptions {
17
42
  recursive?: boolean;
43
+ context?: any;
44
+ }
45
+ export interface AFSDeleteResult {
46
+ message?: string;
18
47
  }
19
48
  export interface AFSRenameOptions {
20
49
  overwrite?: boolean;
50
+ context?: any;
51
+ }
52
+ export interface AFSRenameResult {
53
+ message?: string;
21
54
  }
22
55
  export interface AFSWriteOptions {
23
56
  append?: boolean;
57
+ context?: any;
58
+ }
59
+ export interface AFSWriteResult {
60
+ data: AFSEntry;
61
+ message?: string;
62
+ context?: any;
24
63
  }
25
64
  export interface AFSWriteEntryPayload extends Omit<AFSEntry, "id" | "path"> {
26
65
  }
66
+ export interface AFSExecOptions {
67
+ context: any;
68
+ }
69
+ export interface AFSExecResult {
70
+ data: Record<string, any>;
71
+ }
27
72
  export interface AFSModule {
28
73
  readonly name: string;
29
74
  readonly description?: string;
30
75
  onMount?(root: AFSRoot): void;
31
- list?(path: string, options?: AFSListOptions): Promise<{
32
- list: AFSEntry[];
33
- message?: string;
34
- }>;
35
- read?(path: string): Promise<{
36
- result?: AFSEntry;
37
- message?: string;
38
- }>;
39
- write?(path: string, content: AFSWriteEntryPayload, options?: AFSWriteOptions): Promise<{
40
- result: AFSEntry;
41
- message?: string;
42
- }>;
43
- delete?(path: string, options?: AFSDeleteOptions): Promise<{
44
- message?: string;
45
- }>;
46
- rename?(oldPath: string, newPath: string, options?: AFSRenameOptions): Promise<{
47
- message?: string;
48
- }>;
49
- search?(path: string, query: string, options?: AFSSearchOptions): Promise<{
50
- list: AFSEntry[];
51
- message?: string;
52
- }>;
53
- exec?(path: string, args: Record<string, any>, options: {
54
- context: any;
55
- }): Promise<{
56
- result: Record<string, any>;
57
- }>;
76
+ list?(path: string, options?: AFSListOptions): Promise<AFSListResult>;
77
+ read?(path: string, options?: AFSReadOptions): Promise<AFSReadResult>;
78
+ write?(path: string, content: AFSWriteEntryPayload, options?: AFSWriteOptions): Promise<AFSWriteResult>;
79
+ delete?(path: string, options?: AFSDeleteOptions): Promise<AFSDeleteResult>;
80
+ rename?(oldPath: string, newPath: string, options?: AFSRenameOptions): Promise<AFSRenameResult>;
81
+ search?(path: string, query: string, options?: AFSSearchOptions): Promise<AFSSearchResult>;
82
+ exec?(path: string, args: Record<string, any>, options: AFSExecOptions): Promise<AFSExecResult>;
58
83
  }
59
84
  export type AFSRootEvents = {
60
85
  agentSucceed: [{
@@ -65,7 +90,21 @@ export type AFSRootEvents = {
65
90
  entry: AFSEntry;
66
91
  }];
67
92
  };
93
+ export interface AFSRootListOptions extends AFSListOptions, AFSContextPreset {
94
+ preset?: string;
95
+ }
96
+ export interface AFSRootListResult extends Omit<AFSListResult, "data"> {
97
+ data: any;
98
+ }
99
+ export interface AFSRootSearchOptions extends AFSSearchOptions, AFSContextPreset {
100
+ preset?: string;
101
+ }
102
+ export interface AFSRootSearchResult extends Omit<AFSSearchResult, "data"> {
103
+ data: any;
104
+ }
68
105
  export interface AFSRoot extends Emitter<AFSRootEvents>, AFSModule {
106
+ list(path: string, options?: AFSRootListOptions): Promise<AFSRootListResult>;
107
+ search(path: string, query: string, options: AFSRootSearchOptions): Promise<AFSRootSearchResult>;
69
108
  }
70
109
  export interface AFSEntryMetadata extends Record<string, any> {
71
110
  execute?: {
@@ -75,6 +114,7 @@ export interface AFSEntryMetadata extends Record<string, any> {
75
114
  outputSchema?: Record<string, any>;
76
115
  };
77
116
  childrenCount?: number;
117
+ childrenTruncated?: boolean;
78
118
  }
79
119
  export interface AFSEntry<T = any> {
80
120
  id: string;
@@ -89,3 +129,42 @@ export interface AFSEntry<T = any> {
89
129
  linkTo?: string | null;
90
130
  content?: T;
91
131
  }
132
+ export declare const afsEntrySchema: ZodType<AFSEntry>;
133
+ export interface AFSContextPreset {
134
+ /**
135
+ * The view template for presenting the search results.
136
+ */
137
+ view?: string;
138
+ select?: AFSContextPresetOptionAgent<{
139
+ path: string;
140
+ query?: string;
141
+ }, {
142
+ data: string[];
143
+ }>;
144
+ per?: AFSContextPresetOptionAgent<{
145
+ data: AFSEntry;
146
+ }, {
147
+ data: unknown;
148
+ }>;
149
+ dedupe?: AFSContextPresetOptionAgent<{
150
+ data: unknown[];
151
+ }, {
152
+ data: unknown;
153
+ }>;
154
+ format?: "default" | "simple-list" | "tree" | AFSContextPresetOptionAgent<{
155
+ data: unknown;
156
+ }, {
157
+ data: unknown;
158
+ }>;
159
+ }
160
+ export interface AFSContextPresetOptionAgent<I = any, O = any> {
161
+ invoke(input: I, options?: any): Promise<O>;
162
+ }
163
+ export interface AFSContext {
164
+ search?: {
165
+ presets?: Record<string, AFSContextPreset>;
166
+ };
167
+ list?: {
168
+ presets?: Record<string, AFSContextPreset>;
169
+ };
170
+ }
package/lib/esm/type.js CHANGED
@@ -1 +1,14 @@
1
- export {};
1
+ import { z } from "zod";
2
+ export const afsEntrySchema = z.object({
3
+ id: z.string(),
4
+ createdAt: z.date().optional(),
5
+ updatedAt: z.date().optional(),
6
+ path: z.string(),
7
+ userId: z.string().nullable().optional(),
8
+ sessionId: z.string().nullable().optional(),
9
+ summary: z.string().nullable().optional(),
10
+ description: z.string().nullable().optional(),
11
+ metadata: z.record(z.any()).nullable().optional(),
12
+ linkTo: z.string().nullable().optional(),
13
+ content: z.any().optional(),
14
+ });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@aigne/afs",
3
- "version": "1.3.0",
3
+ "version": "1.4.0-beta.2",
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"
@@ -48,7 +48,8 @@
48
48
  },
49
49
  "dependencies": {
50
50
  "strict-event-emitter": "^0.5.1",
51
- "ufo": "^1.6.1"
51
+ "ufo": "^1.6.1",
52
+ "zod": "^3.25.67"
52
53
  },
53
54
  "devDependencies": {
54
55
  "@types/bun": "^1.2.22",