@aigne/afs 1.3.0 → 1.4.0-beta.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -1,5 +1,24 @@
1
1
  # Changelog
2
2
 
3
+ ## [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)
4
+
5
+
6
+ ### Bug Fixes
7
+
8
+ * bump version ([70d217c](https://github.com/AIGNE-io/aigne-framework/commit/70d217c8360dd0dda7f5f17011c4e92ec836e801))
9
+
10
+ ## [1.4.0-beta](https://github.com/AIGNE-io/aigne-framework/compare/afs-v1.3.0...afs-v1.4.0-beta) (2025-12-17)
11
+
12
+
13
+ ### Features
14
+
15
+ * **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))
16
+
17
+
18
+ ### Bug Fixes
19
+
20
+ * **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))
21
+
3
22
  ## [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
23
 
5
24
  ## [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,17 @@ 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 buildTreeView;
45
32
  }
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,59 @@ 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 === "tree") {
165
+ const valid = zod_1.z.array(type_js_1.afsEntrySchema).safeParse(deduped);
166
+ if (valid.data)
167
+ formatted = this.buildTreeView(valid.data);
168
+ else
169
+ throw new Error("Tree format requires entries to be AFSEntry objects");
170
+ }
171
+ else if (typeof format === "object" && typeof format.invoke === "function") {
172
+ formatted = await format.invoke({ data: deduped }, options).then((res) => res.data);
173
+ }
174
+ return { data: formatted };
175
+ }
176
+ async _select(path, query, select, options) {
177
+ const { data } = await select.invoke({ path, query }, options);
178
+ const results = (await Promise.all(data.map((p) => this.read(p).then((res) => res.data)))).filter((i) => !!i);
179
+ return { data: results };
180
+ }
181
+ async _search(path, query, options) {
128
182
  const results = [];
129
183
  const messages = [];
130
184
  for (const { module, modulePath, subpath } of this.findModules(path)) {
131
185
  if (!module.search)
132
186
  continue;
133
187
  try {
134
- const { list, message } = await module.search(subpath, query, options);
135
- results.push(...list.map((entry) => ({
188
+ const { data, message } = await module.search(subpath, query, options);
189
+ results.push(...data.map((entry) => ({
136
190
  ...entry,
137
191
  path: (0, ufo_1.joinURL)(modulePath, entry.path),
138
192
  })));
@@ -143,7 +197,7 @@ class AFS extends strict_event_emitter_1.Emitter {
143
197
  console.error(`Error searching in module at ${modulePath}`, error);
144
198
  }
145
199
  }
146
- return { list: results, message: messages.join("; ") };
200
+ return { data: results, message: messages.join("; ") };
147
201
  }
148
202
  findModules(path, options) {
149
203
  const maxDepth = Math.max(options?.maxDepth ?? DEFAULT_MAX_DEPTH, 1);
@@ -179,5 +233,50 @@ class AFS extends strict_event_emitter_1.Emitter {
179
233
  throw new Error(`No module found for path: ${path}`);
180
234
  return await module.module.exec(module.subpath, args, options);
181
235
  }
236
+ buildTreeView(entries) {
237
+ const tree = {};
238
+ const entryMap = new Map();
239
+ for (const entry of entries) {
240
+ entryMap.set(entry.path, entry);
241
+ const parts = entry.path.split("/").filter(Boolean);
242
+ let current = tree;
243
+ for (const part of parts) {
244
+ if (!current[part]) {
245
+ current[part] = {};
246
+ }
247
+ current = current[part];
248
+ }
249
+ }
250
+ const renderTree = (node, prefix = "", currentPath = "") => {
251
+ let result = "";
252
+ const keys = Object.keys(node);
253
+ keys.forEach((key, index) => {
254
+ const isLast = index === keys.length - 1;
255
+ const fullPath = currentPath ? `${currentPath}/${key}` : `/${key}`;
256
+ const entry = entryMap.get(fullPath);
257
+ // Build metadata suffix
258
+ const metadataParts = [];
259
+ // Children count
260
+ const childrenCount = entry?.metadata?.childrenCount;
261
+ if (typeof childrenCount === "number") {
262
+ metadataParts.push(`${childrenCount} items`);
263
+ }
264
+ // Children truncated
265
+ if (entry?.metadata?.childrenTruncated) {
266
+ metadataParts.push("truncated");
267
+ }
268
+ // Executable
269
+ if (entry?.metadata?.execute) {
270
+ metadataParts.push("executable");
271
+ }
272
+ const metadataSuffix = metadataParts.length > 0 ? ` [${metadataParts.join(", ")}]` : "";
273
+ result += `${prefix}${isLast ? "└── " : "├── "}${key}${metadataSuffix}`;
274
+ result += `\n`;
275
+ result += renderTree(node[key], `${prefix}${isLast ? " " : "│ "}`, fullPath);
276
+ });
277
+ return result;
278
+ };
279
+ return renderTree(tree);
280
+ }
182
281
  }
183
282
  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" | "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,17 @@ 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 buildTreeView;
45
32
  }
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" | "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,17 @@ 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 buildTreeView;
45
32
  }
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,59 @@ 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 === "tree") {
162
+ const valid = z.array(afsEntrySchema).safeParse(deduped);
163
+ if (valid.data)
164
+ formatted = this.buildTreeView(valid.data);
165
+ else
166
+ throw new Error("Tree format requires entries to be AFSEntry objects");
167
+ }
168
+ else if (typeof format === "object" && typeof format.invoke === "function") {
169
+ formatted = await format.invoke({ data: deduped }, options).then((res) => res.data);
170
+ }
171
+ return { data: formatted };
172
+ }
173
+ async _select(path, query, select, options) {
174
+ const { data } = await select.invoke({ path, query }, options);
175
+ const results = (await Promise.all(data.map((p) => this.read(p).then((res) => res.data)))).filter((i) => !!i);
176
+ return { data: results };
177
+ }
178
+ async _search(path, query, options) {
125
179
  const results = [];
126
180
  const messages = [];
127
181
  for (const { module, modulePath, subpath } of this.findModules(path)) {
128
182
  if (!module.search)
129
183
  continue;
130
184
  try {
131
- const { list, message } = await module.search(subpath, query, options);
132
- results.push(...list.map((entry) => ({
185
+ const { data, message } = await module.search(subpath, query, options);
186
+ results.push(...data.map((entry) => ({
133
187
  ...entry,
134
188
  path: joinURL(modulePath, entry.path),
135
189
  })));
@@ -140,7 +194,7 @@ export class AFS extends Emitter {
140
194
  console.error(`Error searching in module at ${modulePath}`, error);
141
195
  }
142
196
  }
143
- return { list: results, message: messages.join("; ") };
197
+ return { data: results, message: messages.join("; ") };
144
198
  }
145
199
  findModules(path, options) {
146
200
  const maxDepth = Math.max(options?.maxDepth ?? DEFAULT_MAX_DEPTH, 1);
@@ -176,4 +230,49 @@ export class AFS extends Emitter {
176
230
  throw new Error(`No module found for path: ${path}`);
177
231
  return await module.module.exec(module.subpath, args, options);
178
232
  }
233
+ buildTreeView(entries) {
234
+ const tree = {};
235
+ const entryMap = new Map();
236
+ for (const entry of entries) {
237
+ entryMap.set(entry.path, entry);
238
+ const parts = entry.path.split("/").filter(Boolean);
239
+ let current = tree;
240
+ for (const part of parts) {
241
+ if (!current[part]) {
242
+ current[part] = {};
243
+ }
244
+ current = current[part];
245
+ }
246
+ }
247
+ const renderTree = (node, prefix = "", currentPath = "") => {
248
+ let result = "";
249
+ const keys = Object.keys(node);
250
+ keys.forEach((key, index) => {
251
+ const isLast = index === keys.length - 1;
252
+ const fullPath = currentPath ? `${currentPath}/${key}` : `/${key}`;
253
+ const entry = entryMap.get(fullPath);
254
+ // Build metadata suffix
255
+ const metadataParts = [];
256
+ // Children count
257
+ const childrenCount = entry?.metadata?.childrenCount;
258
+ if (typeof childrenCount === "number") {
259
+ metadataParts.push(`${childrenCount} items`);
260
+ }
261
+ // Children truncated
262
+ if (entry?.metadata?.childrenTruncated) {
263
+ metadataParts.push("truncated");
264
+ }
265
+ // Executable
266
+ if (entry?.metadata?.execute) {
267
+ metadataParts.push("executable");
268
+ }
269
+ const metadataSuffix = metadataParts.length > 0 ? ` [${metadataParts.join(", ")}]` : "";
270
+ result += `${prefix}${isLast ? "└── " : "├── "}${key}${metadataSuffix}`;
271
+ result += `\n`;
272
+ result += renderTree(node[key], `${prefix}${isLast ? " " : "│ "}`, fullPath);
273
+ });
274
+ return result;
275
+ };
276
+ return renderTree(tree);
277
+ }
179
278
  }
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" | "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.1",
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",