@aigne/afs-workspace 1.11.0-beta.7
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/LICENSE.md +26 -0
- package/dist/_virtual/_@oxc-project_runtime@0.108.0/helpers/decorate.cjs +11 -0
- package/dist/_virtual/_@oxc-project_runtime@0.108.0/helpers/decorate.mjs +10 -0
- package/dist/config.cjs +70 -0
- package/dist/config.d.cts +9 -0
- package/dist/config.d.cts.map +1 -0
- package/dist/config.d.mts +9 -0
- package/dist/config.d.mts.map +1 -0
- package/dist/config.mjs +68 -0
- package/dist/config.mjs.map +1 -0
- package/dist/index.cjs +579 -0
- package/dist/index.d.cts +104 -0
- package/dist/index.d.cts.map +1 -0
- package/dist/index.d.mts +104 -0
- package/dist/index.d.mts.map +1 -0
- package/dist/index.mjs +578 -0
- package/dist/index.mjs.map +1 -0
- package/package.json +57 -0
package/dist/index.cjs
ADDED
|
@@ -0,0 +1,579 @@
|
|
|
1
|
+
Object.defineProperty(exports, '__esModule', { value: true });
|
|
2
|
+
const require_config = require('./config.cjs');
|
|
3
|
+
const require_decorate = require('./_virtual/_@oxc-project_runtime@0.108.0/helpers/decorate.cjs');
|
|
4
|
+
let node_path = require("node:path");
|
|
5
|
+
let _aigne_afs = require("@aigne/afs");
|
|
6
|
+
let _aigne_afs_provider = require("@aigne/afs/provider");
|
|
7
|
+
let _aigne_afs_provider_registry = require("@aigne/afs-provider-registry");
|
|
8
|
+
let ufo = require("ufo");
|
|
9
|
+
|
|
10
|
+
//#region src/index.ts
|
|
11
|
+
var AFSWorkspace = class extends _aigne_afs_provider.AFSBaseProvider {
|
|
12
|
+
name;
|
|
13
|
+
description;
|
|
14
|
+
accessMode;
|
|
15
|
+
workspacePath;
|
|
16
|
+
registry;
|
|
17
|
+
innerAFS;
|
|
18
|
+
mountStatuses = [];
|
|
19
|
+
initialized = false;
|
|
20
|
+
initPromise;
|
|
21
|
+
constructor(options) {
|
|
22
|
+
super();
|
|
23
|
+
this.workspacePath = (0, node_path.resolve)(options.workspacePath);
|
|
24
|
+
this.registry = options.registry;
|
|
25
|
+
this.name = options.name ?? "workspace";
|
|
26
|
+
this.description = options.description ?? `Workspace: ${this.workspacePath}`;
|
|
27
|
+
this.accessMode = options.accessMode ?? "readwrite";
|
|
28
|
+
this.innerAFS = new _aigne_afs.AFS();
|
|
29
|
+
this.innerAFS.name = `${this.name}-inner`;
|
|
30
|
+
}
|
|
31
|
+
/** Get resolved workspace path */
|
|
32
|
+
getWorkspacePath() {
|
|
33
|
+
return this.workspacePath;
|
|
34
|
+
}
|
|
35
|
+
/**
|
|
36
|
+
* Resolve relative paths in local-scheme URIs against the workspace directory.
|
|
37
|
+
* For example, `sqlite://./sqlite/test.db` becomes `sqlite:///absolute/workspace/path/sqlite/test.db`.
|
|
38
|
+
* Non-local schemes and absolute paths are returned unchanged.
|
|
39
|
+
*/
|
|
40
|
+
resolveURI(uri) {
|
|
41
|
+
const LOCAL_SCHEMES = [
|
|
42
|
+
"fs",
|
|
43
|
+
"sqlite",
|
|
44
|
+
"json",
|
|
45
|
+
"toml",
|
|
46
|
+
"git"
|
|
47
|
+
];
|
|
48
|
+
let parsed;
|
|
49
|
+
try {
|
|
50
|
+
parsed = (0, _aigne_afs_provider_registry.parseURI)(uri);
|
|
51
|
+
} catch {
|
|
52
|
+
return uri;
|
|
53
|
+
}
|
|
54
|
+
if (!LOCAL_SCHEMES.includes(parsed.scheme)) return uri;
|
|
55
|
+
if (parsed.path.startsWith("/")) return uri;
|
|
56
|
+
const absolutePath = (0, node_path.resolve)(this.workspacePath, parsed.path);
|
|
57
|
+
const queryIndex = uri.indexOf("?");
|
|
58
|
+
const query = queryIndex >= 0 ? uri.slice(queryIndex) : "";
|
|
59
|
+
return `${parsed.scheme}://${absolutePath}${query}`;
|
|
60
|
+
}
|
|
61
|
+
/** Initialize workspace: load config, mount sub-providers */
|
|
62
|
+
async initialize() {
|
|
63
|
+
if (this.initialized) return;
|
|
64
|
+
if (this.initPromise) return this.initPromise;
|
|
65
|
+
this.initPromise = this._doInitialize();
|
|
66
|
+
await this.initPromise;
|
|
67
|
+
}
|
|
68
|
+
async _doInitialize() {
|
|
69
|
+
const config = await require_config.loadConfig(this.workspacePath);
|
|
70
|
+
await this.mountSubProviders(config);
|
|
71
|
+
this.initialized = true;
|
|
72
|
+
}
|
|
73
|
+
async ensureInitialized() {
|
|
74
|
+
if (!this.initialized) await this.initialize();
|
|
75
|
+
}
|
|
76
|
+
/** Mount sub-providers from workspace config using tolerant-parallel-mount */
|
|
77
|
+
async mountSubProviders(config) {
|
|
78
|
+
if (config.mounts.length === 0) return;
|
|
79
|
+
const validMounts = [];
|
|
80
|
+
for (const mount of config.mounts) {
|
|
81
|
+
if ((0, _aigne_afs_provider_registry.parseURI)(mount.uri).scheme === "workspace") {
|
|
82
|
+
this.mountStatuses.push({
|
|
83
|
+
path: mount.path,
|
|
84
|
+
uri: mount.uri,
|
|
85
|
+
status: "failed",
|
|
86
|
+
error: "Nested workspace is not supported"
|
|
87
|
+
});
|
|
88
|
+
continue;
|
|
89
|
+
}
|
|
90
|
+
validMounts.push(mount);
|
|
91
|
+
}
|
|
92
|
+
const results = await Promise.allSettled(validMounts.map(async (mount) => {
|
|
93
|
+
const effectiveMount = {
|
|
94
|
+
...mount,
|
|
95
|
+
uri: this.resolveURI(mount.uri)
|
|
96
|
+
};
|
|
97
|
+
if (this.accessMode === "readonly") effectiveMount.access_mode = "readonly";
|
|
98
|
+
return {
|
|
99
|
+
mount,
|
|
100
|
+
provider: await this.registry.createProvider(effectiveMount)
|
|
101
|
+
};
|
|
102
|
+
}));
|
|
103
|
+
let successCount = 0;
|
|
104
|
+
for (let i = 0; i < results.length; i++) {
|
|
105
|
+
const result = results[i];
|
|
106
|
+
const mount = validMounts[i];
|
|
107
|
+
if (result.status === "fulfilled") {
|
|
108
|
+
const { provider } = result.value;
|
|
109
|
+
try {
|
|
110
|
+
await this.innerAFS.mount(provider, mount.path);
|
|
111
|
+
this.mountStatuses.push({
|
|
112
|
+
path: mount.path,
|
|
113
|
+
uri: mount.uri,
|
|
114
|
+
status: "ok"
|
|
115
|
+
});
|
|
116
|
+
successCount++;
|
|
117
|
+
} catch (err) {
|
|
118
|
+
this.mountStatuses.push({
|
|
119
|
+
path: mount.path,
|
|
120
|
+
uri: mount.uri,
|
|
121
|
+
status: "failed",
|
|
122
|
+
error: err instanceof Error ? err.message : String(err)
|
|
123
|
+
});
|
|
124
|
+
}
|
|
125
|
+
} else {
|
|
126
|
+
const reason = result.reason;
|
|
127
|
+
this.mountStatuses.push({
|
|
128
|
+
path: mount.path,
|
|
129
|
+
uri: mount.uri,
|
|
130
|
+
status: "failed",
|
|
131
|
+
error: reason instanceof Error ? reason.message : String(reason)
|
|
132
|
+
});
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
if (successCount === 0 && validMounts.length > 0) throw new Error(`Workspace '${this.name}': all ${validMounts.length} sub-mounts failed to load`);
|
|
136
|
+
}
|
|
137
|
+
async statRoot(_ctx) {
|
|
138
|
+
await this.ensureInitialized();
|
|
139
|
+
return { data: {
|
|
140
|
+
id: "/",
|
|
141
|
+
path: "/",
|
|
142
|
+
meta: {
|
|
143
|
+
kind: "workspace",
|
|
144
|
+
kinds: ["workspace", "afs:node"],
|
|
145
|
+
childrenCount: this.mountStatuses.filter((s) => s.status === "ok").length
|
|
146
|
+
}
|
|
147
|
+
} };
|
|
148
|
+
}
|
|
149
|
+
async readRoot(_ctx) {
|
|
150
|
+
await this.ensureInitialized();
|
|
151
|
+
const okMounts = this.mountStatuses.filter((s) => s.status === "ok").length;
|
|
152
|
+
return {
|
|
153
|
+
id: "/",
|
|
154
|
+
path: "/",
|
|
155
|
+
content: {
|
|
156
|
+
type: "workspace",
|
|
157
|
+
name: this.name,
|
|
158
|
+
description: this.description,
|
|
159
|
+
mountCount: okMounts
|
|
160
|
+
},
|
|
161
|
+
meta: {
|
|
162
|
+
kind: "workspace",
|
|
163
|
+
kinds: ["workspace", "afs:node"],
|
|
164
|
+
childrenCount: okMounts
|
|
165
|
+
}
|
|
166
|
+
};
|
|
167
|
+
}
|
|
168
|
+
async listRoot(_ctx) {
|
|
169
|
+
await this.ensureInitialized();
|
|
170
|
+
const result = await this.innerAFS.list("/");
|
|
171
|
+
for (const entry of result.data) if (entry.meta && !entry.meta.kind) entry.meta.kind = "afs:directory";
|
|
172
|
+
return result;
|
|
173
|
+
}
|
|
174
|
+
async readRootMeta(_ctx) {
|
|
175
|
+
await this.ensureInitialized();
|
|
176
|
+
const okMounts = this.mountStatuses.filter((s) => s.status === "ok").length;
|
|
177
|
+
return {
|
|
178
|
+
id: "/.meta",
|
|
179
|
+
path: "/.meta",
|
|
180
|
+
content: {
|
|
181
|
+
type: "workspace",
|
|
182
|
+
storagePath: this.workspacePath,
|
|
183
|
+
mountCount: this.mountStatuses.length,
|
|
184
|
+
mounts: this.mountStatuses.map((s) => ({
|
|
185
|
+
path: s.path,
|
|
186
|
+
uri: s.uri,
|
|
187
|
+
status: s.status
|
|
188
|
+
}))
|
|
189
|
+
},
|
|
190
|
+
meta: {
|
|
191
|
+
kind: "workspace",
|
|
192
|
+
childrenCount: okMounts
|
|
193
|
+
}
|
|
194
|
+
};
|
|
195
|
+
}
|
|
196
|
+
async readCapabilities(_ctx) {
|
|
197
|
+
await this.ensureInitialized();
|
|
198
|
+
return {
|
|
199
|
+
id: "/.meta/.capabilities",
|
|
200
|
+
path: "/.meta/.capabilities",
|
|
201
|
+
content: {
|
|
202
|
+
schemaVersion: 1,
|
|
203
|
+
provider: this.name,
|
|
204
|
+
description: this.description ?? `Workspace: ${this.workspacePath}`,
|
|
205
|
+
tools: [],
|
|
206
|
+
actions: [],
|
|
207
|
+
operations: this.getOperationsDeclaration()
|
|
208
|
+
},
|
|
209
|
+
meta: { kind: "afs:capabilities" }
|
|
210
|
+
};
|
|
211
|
+
}
|
|
212
|
+
async explainRoot(_ctx) {
|
|
213
|
+
await this.ensureInitialized();
|
|
214
|
+
const lines = [];
|
|
215
|
+
lines.push(`# Workspace: ${this.name}`);
|
|
216
|
+
lines.push("");
|
|
217
|
+
if (this.description) {
|
|
218
|
+
lines.push(this.description);
|
|
219
|
+
lines.push("");
|
|
220
|
+
}
|
|
221
|
+
lines.push("## Mounts");
|
|
222
|
+
lines.push("");
|
|
223
|
+
if (this.mountStatuses.length === 0) lines.push("No mounts configured.");
|
|
224
|
+
else for (const s of this.mountStatuses) {
|
|
225
|
+
const statusIcon = s.status === "ok" ? "[ok]" : "[FAILED]";
|
|
226
|
+
lines.push(`- ${statusIcon} \`${s.path}\` -> \`${s.uri}\``);
|
|
227
|
+
if (s.error) lines.push(` Error: ${s.error}`);
|
|
228
|
+
}
|
|
229
|
+
lines.push("");
|
|
230
|
+
lines.push("## Configuration");
|
|
231
|
+
lines.push("");
|
|
232
|
+
lines.push(`- **Storage Path**: ${this.workspacePath}`);
|
|
233
|
+
lines.push(`- **Config File**: ${require_config.getConfigPath(this.workspacePath)}`);
|
|
234
|
+
lines.push(`- **Access Mode**: ${this.accessMode}`);
|
|
235
|
+
lines.push(`- **Total Mounts**: ${this.mountStatuses.length}`);
|
|
236
|
+
lines.push(`- **Active Mounts**: ${this.mountStatuses.filter((s) => s.status === "ok").length}`);
|
|
237
|
+
return {
|
|
238
|
+
format: "markdown",
|
|
239
|
+
content: lines.join("\n")
|
|
240
|
+
};
|
|
241
|
+
}
|
|
242
|
+
async searchRoot(ctx, query) {
|
|
243
|
+
await this.ensureInitialized();
|
|
244
|
+
const options = ctx.options;
|
|
245
|
+
const result = await this.innerAFS.search("/", query, options);
|
|
246
|
+
if (options?.limit && result.data.length > options.limit) result.data = result.data.slice(0, options.limit);
|
|
247
|
+
return result;
|
|
248
|
+
}
|
|
249
|
+
async listRootActions(_ctx) {
|
|
250
|
+
return { data: [{
|
|
251
|
+
id: "/.actions/add",
|
|
252
|
+
path: "/.actions/add",
|
|
253
|
+
summary: "Add a new mount to the workspace",
|
|
254
|
+
meta: {
|
|
255
|
+
kind: "afs:executable",
|
|
256
|
+
kinds: ["afs:executable", "afs:node"],
|
|
257
|
+
name: "add",
|
|
258
|
+
description: "Add a new sub-provider mount to this workspace",
|
|
259
|
+
inputSchema: {
|
|
260
|
+
type: "object",
|
|
261
|
+
properties: {
|
|
262
|
+
path: {
|
|
263
|
+
type: "string",
|
|
264
|
+
description: "Mount path (must start with /)"
|
|
265
|
+
},
|
|
266
|
+
uri: {
|
|
267
|
+
type: "string",
|
|
268
|
+
description: "Provider URI (e.g. fs:///path, sqlite:///db)"
|
|
269
|
+
},
|
|
270
|
+
description: {
|
|
271
|
+
type: "string",
|
|
272
|
+
description: "Optional mount description"
|
|
273
|
+
},
|
|
274
|
+
access_mode: {
|
|
275
|
+
type: "string",
|
|
276
|
+
description: "readonly or readwrite"
|
|
277
|
+
},
|
|
278
|
+
options: {
|
|
279
|
+
type: "object",
|
|
280
|
+
description: "Provider-specific options"
|
|
281
|
+
}
|
|
282
|
+
},
|
|
283
|
+
required: ["path", "uri"]
|
|
284
|
+
}
|
|
285
|
+
}
|
|
286
|
+
}, {
|
|
287
|
+
id: "/.actions/remove",
|
|
288
|
+
path: "/.actions/remove",
|
|
289
|
+
summary: "Remove a mount from the workspace",
|
|
290
|
+
meta: {
|
|
291
|
+
kind: "afs:executable",
|
|
292
|
+
kinds: ["afs:executable", "afs:node"],
|
|
293
|
+
name: "remove",
|
|
294
|
+
description: "Remove a sub-provider mount from this workspace",
|
|
295
|
+
inputSchema: {
|
|
296
|
+
type: "object",
|
|
297
|
+
properties: {
|
|
298
|
+
path: {
|
|
299
|
+
type: "string",
|
|
300
|
+
description: "Mount path to remove"
|
|
301
|
+
},
|
|
302
|
+
keepData: {
|
|
303
|
+
type: "boolean",
|
|
304
|
+
description: "Keep local data (default: false)"
|
|
305
|
+
}
|
|
306
|
+
},
|
|
307
|
+
required: ["path"]
|
|
308
|
+
}
|
|
309
|
+
}
|
|
310
|
+
}] };
|
|
311
|
+
}
|
|
312
|
+
async handleAdd(_ctx, args) {
|
|
313
|
+
await this.ensureInitialized();
|
|
314
|
+
const path = args.path;
|
|
315
|
+
const uri = args.uri;
|
|
316
|
+
if (!path) return {
|
|
317
|
+
success: false,
|
|
318
|
+
error: {
|
|
319
|
+
code: "VALIDATION_ERROR",
|
|
320
|
+
message: "Missing required parameter: path"
|
|
321
|
+
}
|
|
322
|
+
};
|
|
323
|
+
if (!uri) return {
|
|
324
|
+
success: false,
|
|
325
|
+
error: {
|
|
326
|
+
code: "VALIDATION_ERROR",
|
|
327
|
+
message: "Missing required parameter: uri"
|
|
328
|
+
}
|
|
329
|
+
};
|
|
330
|
+
if (!path.startsWith("/")) return {
|
|
331
|
+
success: false,
|
|
332
|
+
error: {
|
|
333
|
+
code: "VALIDATION_ERROR",
|
|
334
|
+
message: "Path must start with /"
|
|
335
|
+
}
|
|
336
|
+
};
|
|
337
|
+
try {
|
|
338
|
+
if ((0, _aigne_afs_provider_registry.parseURI)(uri).scheme === "workspace") return {
|
|
339
|
+
success: false,
|
|
340
|
+
error: {
|
|
341
|
+
code: "UNSUPPORTED",
|
|
342
|
+
message: "Nested workspace is not supported"
|
|
343
|
+
}
|
|
344
|
+
};
|
|
345
|
+
} catch (err) {
|
|
346
|
+
return {
|
|
347
|
+
success: false,
|
|
348
|
+
error: {
|
|
349
|
+
code: "INVALID_URI",
|
|
350
|
+
message: err instanceof Error ? err.message : String(err)
|
|
351
|
+
}
|
|
352
|
+
};
|
|
353
|
+
}
|
|
354
|
+
const mountConfig = {
|
|
355
|
+
path,
|
|
356
|
+
uri: this.resolveURI(uri),
|
|
357
|
+
description: args.description,
|
|
358
|
+
access_mode: this.accessMode === "readonly" ? "readonly" : args.access_mode,
|
|
359
|
+
options: args.options,
|
|
360
|
+
autoInit: true
|
|
361
|
+
};
|
|
362
|
+
try {
|
|
363
|
+
const provider = await this.registry.createProvider(mountConfig);
|
|
364
|
+
await this.innerAFS.mount(provider, path);
|
|
365
|
+
this.mountStatuses.push({
|
|
366
|
+
path,
|
|
367
|
+
uri,
|
|
368
|
+
status: "ok"
|
|
369
|
+
});
|
|
370
|
+
const { addMountToConfig } = await Promise.resolve().then(() => require("./config.cjs"));
|
|
371
|
+
await addMountToConfig(this.workspacePath, {
|
|
372
|
+
path,
|
|
373
|
+
uri,
|
|
374
|
+
description: mountConfig.description,
|
|
375
|
+
access_mode: mountConfig.access_mode,
|
|
376
|
+
options: mountConfig.options
|
|
377
|
+
});
|
|
378
|
+
return {
|
|
379
|
+
success: true,
|
|
380
|
+
data: {
|
|
381
|
+
path,
|
|
382
|
+
uri,
|
|
383
|
+
message: `Mount '${path}' added successfully`
|
|
384
|
+
}
|
|
385
|
+
};
|
|
386
|
+
} catch (err) {
|
|
387
|
+
return {
|
|
388
|
+
success: false,
|
|
389
|
+
error: {
|
|
390
|
+
code: "ADD_FAILED",
|
|
391
|
+
message: err instanceof Error ? err.message : String(err)
|
|
392
|
+
}
|
|
393
|
+
};
|
|
394
|
+
}
|
|
395
|
+
}
|
|
396
|
+
async handleRemove(_ctx, args) {
|
|
397
|
+
await this.ensureInitialized();
|
|
398
|
+
const path = args.path;
|
|
399
|
+
const keepData = args.keepData === true;
|
|
400
|
+
if (!path) return {
|
|
401
|
+
success: false,
|
|
402
|
+
error: {
|
|
403
|
+
code: "VALIDATION_ERROR",
|
|
404
|
+
message: "Missing required parameter: path"
|
|
405
|
+
}
|
|
406
|
+
};
|
|
407
|
+
const mountIndex = this.mountStatuses.findIndex((s) => s.path === path);
|
|
408
|
+
if (mountIndex === -1) return {
|
|
409
|
+
success: false,
|
|
410
|
+
error: {
|
|
411
|
+
code: "NOT_FOUND",
|
|
412
|
+
message: `Mount '${path}' not found in workspace`
|
|
413
|
+
}
|
|
414
|
+
};
|
|
415
|
+
try {
|
|
416
|
+
this.innerAFS.unmount(path);
|
|
417
|
+
const { removeMountFromConfig } = await Promise.resolve().then(() => require("./config.cjs"));
|
|
418
|
+
const { removed } = await removeMountFromConfig(this.workspacePath, path);
|
|
419
|
+
this.mountStatuses.splice(mountIndex, 1);
|
|
420
|
+
if (!keepData && removed) await this.deleteLocalData(removed.uri);
|
|
421
|
+
return {
|
|
422
|
+
success: true,
|
|
423
|
+
data: {
|
|
424
|
+
path,
|
|
425
|
+
message: `Mount '${path}' removed successfully`
|
|
426
|
+
}
|
|
427
|
+
};
|
|
428
|
+
} catch (err) {
|
|
429
|
+
return {
|
|
430
|
+
success: false,
|
|
431
|
+
error: {
|
|
432
|
+
code: "REMOVE_FAILED",
|
|
433
|
+
message: err instanceof Error ? err.message : String(err)
|
|
434
|
+
}
|
|
435
|
+
};
|
|
436
|
+
}
|
|
437
|
+
}
|
|
438
|
+
/** Delete local data for a URI. Only deletes for local providers. */
|
|
439
|
+
async deleteLocalData(uri) {
|
|
440
|
+
try {
|
|
441
|
+
const parsed = (0, _aigne_afs_provider_registry.parseURI)(uri);
|
|
442
|
+
if (![
|
|
443
|
+
"fs",
|
|
444
|
+
"json",
|
|
445
|
+
"toml",
|
|
446
|
+
"sqlite",
|
|
447
|
+
"git"
|
|
448
|
+
].includes(parsed.scheme)) return;
|
|
449
|
+
const localPath = (0, node_path.resolve)(this.workspacePath, parsed.path);
|
|
450
|
+
if (!localPath.startsWith(this.workspacePath)) return;
|
|
451
|
+
const { rm } = await import("node:fs/promises");
|
|
452
|
+
const { existsSync } = await import("node:fs");
|
|
453
|
+
if (existsSync(localPath)) await rm(localPath, {
|
|
454
|
+
recursive: true,
|
|
455
|
+
force: true
|
|
456
|
+
});
|
|
457
|
+
} catch {}
|
|
458
|
+
}
|
|
459
|
+
/** Check if a path falls under any active mount */
|
|
460
|
+
isPathUnderMount(path) {
|
|
461
|
+
return this.mountStatuses.some((s) => s.status === "ok" && (path === s.path || path.startsWith((0, ufo.joinURL)(s.path, "/"))));
|
|
462
|
+
}
|
|
463
|
+
/** Ensure a path is under an active mount, or throw AFSNotFoundError */
|
|
464
|
+
ensurePathUnderMount(path) {
|
|
465
|
+
if (!this.isPathUnderMount(path)) throw new _aigne_afs.AFSNotFoundError(path);
|
|
466
|
+
}
|
|
467
|
+
/** Re-throw errors from innerAFS delegation. AFS errors pass through; others are wrapped preserving the message */
|
|
468
|
+
rethrowAsNotFound(path, err) {
|
|
469
|
+
if (err instanceof _aigne_afs.AFSError) throw err;
|
|
470
|
+
throw new _aigne_afs.AFSError(`Error at ${path}: ${err instanceof Error ? err.message : String(err)}`, "AFS_INTERNAL_ERROR");
|
|
471
|
+
}
|
|
472
|
+
async statDelegate(ctx) {
|
|
473
|
+
await this.ensureInitialized();
|
|
474
|
+
const fullPath = (0, ufo.joinURL)("/", ctx.params.path);
|
|
475
|
+
this.ensurePathUnderMount(fullPath);
|
|
476
|
+
try {
|
|
477
|
+
const result = await this.innerAFS.stat(fullPath);
|
|
478
|
+
if (result.data) result.data.path = fullPath;
|
|
479
|
+
return result;
|
|
480
|
+
} catch (err) {
|
|
481
|
+
this.rethrowAsNotFound(fullPath, err);
|
|
482
|
+
}
|
|
483
|
+
}
|
|
484
|
+
async listDelegate(ctx) {
|
|
485
|
+
await this.ensureInitialized();
|
|
486
|
+
const fullPath = (0, ufo.joinURL)("/", ctx.params.path);
|
|
487
|
+
this.ensurePathUnderMount(fullPath);
|
|
488
|
+
try {
|
|
489
|
+
return await this.innerAFS.list(fullPath, ctx.options);
|
|
490
|
+
} catch (err) {
|
|
491
|
+
this.rethrowAsNotFound(fullPath, err);
|
|
492
|
+
}
|
|
493
|
+
}
|
|
494
|
+
async readDelegate(ctx) {
|
|
495
|
+
await this.ensureInitialized();
|
|
496
|
+
const fullPath = (0, ufo.joinURL)("/", ctx.params.path);
|
|
497
|
+
this.ensurePathUnderMount(fullPath);
|
|
498
|
+
try {
|
|
499
|
+
const result = await this.innerAFS.read(fullPath, ctx.options);
|
|
500
|
+
if (!result.data) throw new _aigne_afs.AFSNotFoundError(fullPath);
|
|
501
|
+
return result.data;
|
|
502
|
+
} catch (err) {
|
|
503
|
+
this.rethrowAsNotFound(fullPath, err);
|
|
504
|
+
}
|
|
505
|
+
}
|
|
506
|
+
async writeDelegate(ctx, content) {
|
|
507
|
+
await this.ensureInitialized();
|
|
508
|
+
const fullPath = (0, ufo.joinURL)("/", ctx.params.path);
|
|
509
|
+
this.ensurePathUnderMount(fullPath);
|
|
510
|
+
try {
|
|
511
|
+
return await this.innerAFS.write(fullPath, content, ctx.options);
|
|
512
|
+
} catch (err) {
|
|
513
|
+
this.rethrowAsNotFound(fullPath, err);
|
|
514
|
+
}
|
|
515
|
+
}
|
|
516
|
+
async deleteDelegate(ctx) {
|
|
517
|
+
await this.ensureInitialized();
|
|
518
|
+
const fullPath = (0, ufo.joinURL)("/", ctx.params.path);
|
|
519
|
+
this.ensurePathUnderMount(fullPath);
|
|
520
|
+
try {
|
|
521
|
+
return await this.innerAFS.delete(fullPath, ctx.options);
|
|
522
|
+
} catch (err) {
|
|
523
|
+
this.rethrowAsNotFound(fullPath, err);
|
|
524
|
+
}
|
|
525
|
+
}
|
|
526
|
+
async execDelegate(ctx, args) {
|
|
527
|
+
await this.ensureInitialized();
|
|
528
|
+
const fullPath = (0, ufo.joinURL)("/", ctx.params.path);
|
|
529
|
+
this.ensurePathUnderMount(fullPath);
|
|
530
|
+
try {
|
|
531
|
+
return await this.innerAFS.exec(fullPath, args, ctx.options ?? {});
|
|
532
|
+
} catch (err) {
|
|
533
|
+
this.rethrowAsNotFound(fullPath, err);
|
|
534
|
+
}
|
|
535
|
+
}
|
|
536
|
+
async searchDelegate(ctx, query, options) {
|
|
537
|
+
await this.ensureInitialized();
|
|
538
|
+
const fullPath = (0, ufo.joinURL)("/", ctx.params.path);
|
|
539
|
+
this.ensurePathUnderMount(fullPath);
|
|
540
|
+
try {
|
|
541
|
+
return await this.innerAFS.search(fullPath, query, options);
|
|
542
|
+
} catch (err) {
|
|
543
|
+
this.rethrowAsNotFound(fullPath, err);
|
|
544
|
+
}
|
|
545
|
+
}
|
|
546
|
+
async explainDelegate(ctx) {
|
|
547
|
+
await this.ensureInitialized();
|
|
548
|
+
const fullPath = (0, ufo.joinURL)("/", ctx.params.path);
|
|
549
|
+
this.ensurePathUnderMount(fullPath);
|
|
550
|
+
try {
|
|
551
|
+
return await this.innerAFS.explain(fullPath, ctx.options);
|
|
552
|
+
} catch (err) {
|
|
553
|
+
this.rethrowAsNotFound(fullPath, err);
|
|
554
|
+
}
|
|
555
|
+
}
|
|
556
|
+
};
|
|
557
|
+
require_decorate.__decorate([(0, _aigne_afs_provider.Stat)("/")], AFSWorkspace.prototype, "statRoot", null);
|
|
558
|
+
require_decorate.__decorate([(0, _aigne_afs_provider.Read)("/")], AFSWorkspace.prototype, "readRoot", null);
|
|
559
|
+
require_decorate.__decorate([(0, _aigne_afs_provider.List)("/")], AFSWorkspace.prototype, "listRoot", null);
|
|
560
|
+
require_decorate.__decorate([(0, _aigne_afs_provider.Meta)("/")], AFSWorkspace.prototype, "readRootMeta", null);
|
|
561
|
+
require_decorate.__decorate([(0, _aigne_afs_provider.Read)("/.meta/.capabilities")], AFSWorkspace.prototype, "readCapabilities", null);
|
|
562
|
+
require_decorate.__decorate([(0, _aigne_afs_provider.Explain)("/")], AFSWorkspace.prototype, "explainRoot", null);
|
|
563
|
+
require_decorate.__decorate([(0, _aigne_afs_provider.Search)("/")], AFSWorkspace.prototype, "searchRoot", null);
|
|
564
|
+
require_decorate.__decorate([(0, _aigne_afs_provider.Actions)("/")], AFSWorkspace.prototype, "listRootActions", null);
|
|
565
|
+
require_decorate.__decorate([_aigne_afs_provider.Actions.Exec("/", "add")], AFSWorkspace.prototype, "handleAdd", null);
|
|
566
|
+
require_decorate.__decorate([_aigne_afs_provider.Actions.Exec("/", "remove")], AFSWorkspace.prototype, "handleRemove", null);
|
|
567
|
+
require_decorate.__decorate([(0, _aigne_afs_provider.Stat)("/:path+")], AFSWorkspace.prototype, "statDelegate", null);
|
|
568
|
+
require_decorate.__decorate([(0, _aigne_afs_provider.List)("/:path+", { handleDepth: true })], AFSWorkspace.prototype, "listDelegate", null);
|
|
569
|
+
require_decorate.__decorate([(0, _aigne_afs_provider.Read)("/:path+")], AFSWorkspace.prototype, "readDelegate", null);
|
|
570
|
+
require_decorate.__decorate([(0, _aigne_afs_provider.Write)("/:path+")], AFSWorkspace.prototype, "writeDelegate", null);
|
|
571
|
+
require_decorate.__decorate([(0, _aigne_afs_provider.Delete)("/:path+")], AFSWorkspace.prototype, "deleteDelegate", null);
|
|
572
|
+
require_decorate.__decorate([(0, _aigne_afs_provider.Exec)("/:path+")], AFSWorkspace.prototype, "execDelegate", null);
|
|
573
|
+
require_decorate.__decorate([(0, _aigne_afs_provider.Search)("/:path+")], AFSWorkspace.prototype, "searchDelegate", null);
|
|
574
|
+
require_decorate.__decorate([(0, _aigne_afs_provider.Explain)("/:path+")], AFSWorkspace.prototype, "explainDelegate", null);
|
|
575
|
+
var src_default = AFSWorkspace;
|
|
576
|
+
|
|
577
|
+
//#endregion
|
|
578
|
+
exports.AFSWorkspace = AFSWorkspace;
|
|
579
|
+
exports.default = src_default;
|
package/dist/index.d.cts
ADDED
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
import { WorkspaceConfig } from "./config.cjs";
|
|
2
|
+
import * as _aigne_afs0 from "@aigne/afs";
|
|
3
|
+
import { AFSAccessMode, AFSDeleteResult, AFSEntry, AFSExplainResult, AFSListResult, AFSSearchOptions, AFSSearchResult, AFSStatResult, AFSWriteEntryPayload, AFSWriteResult } from "@aigne/afs";
|
|
4
|
+
import { AFSBaseProvider, RouteContext } from "@aigne/afs/provider";
|
|
5
|
+
import { ProviderRegistry } from "@aigne/afs-provider-registry";
|
|
6
|
+
|
|
7
|
+
//#region src/index.d.ts
|
|
8
|
+
interface AFSWorkspaceOptions {
|
|
9
|
+
/** Absolute or relative path to the workspace directory */
|
|
10
|
+
workspacePath: string;
|
|
11
|
+
/** Provider name (used as mount name) */
|
|
12
|
+
name?: string;
|
|
13
|
+
/** Human-readable description */
|
|
14
|
+
description?: string;
|
|
15
|
+
/** Access mode for the workspace. When readonly, all sub-providers are forced readonly. */
|
|
16
|
+
accessMode?: AFSAccessMode;
|
|
17
|
+
/** Provider registry for creating sub-providers (injected to break circular dependency) */
|
|
18
|
+
registry: ProviderRegistry;
|
|
19
|
+
}
|
|
20
|
+
declare class AFSWorkspace extends AFSBaseProvider {
|
|
21
|
+
readonly name: string;
|
|
22
|
+
readonly description?: string;
|
|
23
|
+
readonly accessMode: AFSAccessMode;
|
|
24
|
+
private readonly workspacePath;
|
|
25
|
+
private readonly registry;
|
|
26
|
+
private innerAFS;
|
|
27
|
+
private mountStatuses;
|
|
28
|
+
private initialized;
|
|
29
|
+
private initPromise;
|
|
30
|
+
constructor(options: AFSWorkspaceOptions);
|
|
31
|
+
/** Get resolved workspace path */
|
|
32
|
+
getWorkspacePath(): string;
|
|
33
|
+
/**
|
|
34
|
+
* Resolve relative paths in local-scheme URIs against the workspace directory.
|
|
35
|
+
* For example, `sqlite://./sqlite/test.db` becomes `sqlite:///absolute/workspace/path/sqlite/test.db`.
|
|
36
|
+
* Non-local schemes and absolute paths are returned unchanged.
|
|
37
|
+
*/
|
|
38
|
+
private resolveURI;
|
|
39
|
+
/** Initialize workspace: load config, mount sub-providers */
|
|
40
|
+
private initialize;
|
|
41
|
+
private _doInitialize;
|
|
42
|
+
private ensureInitialized;
|
|
43
|
+
/** Mount sub-providers from workspace config using tolerant-parallel-mount */
|
|
44
|
+
private mountSubProviders;
|
|
45
|
+
statRoot(_ctx: RouteContext): Promise<AFSStatResult>;
|
|
46
|
+
readRoot(_ctx: RouteContext): Promise<AFSEntry>;
|
|
47
|
+
listRoot(_ctx: RouteContext): Promise<AFSListResult>;
|
|
48
|
+
readRootMeta(_ctx: RouteContext): Promise<AFSEntry>;
|
|
49
|
+
readCapabilities(_ctx: RouteContext): Promise<AFSEntry>;
|
|
50
|
+
explainRoot(_ctx: RouteContext): Promise<AFSExplainResult>;
|
|
51
|
+
searchRoot(ctx: RouteContext, query: string): Promise<AFSSearchResult>;
|
|
52
|
+
listRootActions(_ctx: RouteContext): Promise<AFSListResult>;
|
|
53
|
+
handleAdd(_ctx: RouteContext, args: Record<string, unknown>): Promise<{
|
|
54
|
+
success: boolean;
|
|
55
|
+
data?: Record<string, unknown>;
|
|
56
|
+
error?: {
|
|
57
|
+
code: string;
|
|
58
|
+
message: string;
|
|
59
|
+
};
|
|
60
|
+
}>;
|
|
61
|
+
handleRemove(_ctx: RouteContext, args: Record<string, unknown>): Promise<{
|
|
62
|
+
success: boolean;
|
|
63
|
+
data?: Record<string, unknown>;
|
|
64
|
+
error?: {
|
|
65
|
+
code: string;
|
|
66
|
+
message: string;
|
|
67
|
+
};
|
|
68
|
+
}>;
|
|
69
|
+
/** Delete local data for a URI. Only deletes for local providers. */
|
|
70
|
+
private deleteLocalData;
|
|
71
|
+
/** Check if a path falls under any active mount */
|
|
72
|
+
private isPathUnderMount;
|
|
73
|
+
/** Ensure a path is under an active mount, or throw AFSNotFoundError */
|
|
74
|
+
private ensurePathUnderMount;
|
|
75
|
+
/** Re-throw errors from innerAFS delegation. AFS errors pass through; others are wrapped preserving the message */
|
|
76
|
+
private rethrowAsNotFound;
|
|
77
|
+
statDelegate(ctx: RouteContext<{
|
|
78
|
+
path: string;
|
|
79
|
+
}>): Promise<AFSStatResult>;
|
|
80
|
+
listDelegate(ctx: RouteContext<{
|
|
81
|
+
path: string;
|
|
82
|
+
}>): Promise<AFSListResult>;
|
|
83
|
+
readDelegate(ctx: RouteContext<{
|
|
84
|
+
path: string;
|
|
85
|
+
}>): Promise<AFSEntry | undefined>;
|
|
86
|
+
writeDelegate(ctx: RouteContext<{
|
|
87
|
+
path: string;
|
|
88
|
+
}>, content: AFSWriteEntryPayload): Promise<AFSWriteResult>;
|
|
89
|
+
deleteDelegate(ctx: RouteContext<{
|
|
90
|
+
path: string;
|
|
91
|
+
}>): Promise<AFSDeleteResult>;
|
|
92
|
+
execDelegate(ctx: RouteContext<{
|
|
93
|
+
path: string;
|
|
94
|
+
}>, args: Record<string, unknown>): Promise<_aigne_afs0.AFSExecResult>;
|
|
95
|
+
searchDelegate(ctx: RouteContext<{
|
|
96
|
+
path: string;
|
|
97
|
+
}>, query: string, options?: AFSSearchOptions): Promise<AFSSearchResult>;
|
|
98
|
+
explainDelegate(ctx: RouteContext<{
|
|
99
|
+
path: string;
|
|
100
|
+
}>): Promise<AFSExplainResult>;
|
|
101
|
+
}
|
|
102
|
+
//#endregion
|
|
103
|
+
export { AFSWorkspace, AFSWorkspace as default, AFSWorkspaceOptions, type WorkspaceConfig };
|
|
104
|
+
//# sourceMappingURL=index.d.cts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.cts","names":[],"sources":["../src/index.ts"],"mappings":";;;;;;;UAwCiB,mBAAA;;EAEf,aAAA;;EAEA,IAAA;EAJkC;EAMlC,WAAA;EAI0B;EAF1B,UAAA,GAAa,aAAA;EAJb;EAMA,QAAA,EAAU,gBAAA;AAAA;AAAA,cAYC,YAAA,SAAqB,eAAA;EAAA,SACd,IAAA;EAAA,SACA,WAAA;EAAA,SACA,UAAA,EAAY,aAAA;EAAA,iBAEb,aAAA;EAAA,iBACA,QAAA;EAAA,QACT,QAAA;EAAA,QACA,aAAA;EAAA,QACA,WAAA;EAAA,QACA,WAAA;cAEI,OAAA,EAAS,mBAAA;EA6Ie;EAjIpC,gBAAA,CAAA;EAkJ4C;;;;;EAAA,QAzIpC,UAAA;EA2KwC;EAAA,QA1JlC,UAAA;EAAA,QAQA,aAAA;EAAA,QAMA,iBAAA;EAoK8B;EAAA,QA7J9B,iBAAA;EAkFR,QAAA,CAAS,IAAA,EAAM,YAAA,GAAe,OAAA,CAAQ,aAAA;EAiBtC,QAAA,CAAS,IAAA,EAAM,YAAA,GAAe,OAAA,CAAQ,QAAA;EAqBtC,QAAA,CAAS,IAAA,EAAM,YAAA,GAAe,OAAA,CAAQ,aAAA;EAatC,YAAA,CAAa,IAAA,EAAM,YAAA,GAAe,OAAA,CAAQ,QAAA;EAwB1C,gBAAA,CAAiB,IAAA,EAAM,YAAA,GAAe,OAAA,CAAQ,QAAA;EAmB9C,WAAA,CAAY,IAAA,EAAM,YAAA,GAAe,OAAA,CAAQ,gBAAA;EAqCzC,UAAA,CAAW,GAAA,EAAK,YAAA,EAAc,KAAA,WAAgB,OAAA,CAAQ,eAAA;EActD,eAAA,CAAgB,IAAA,EAAM,YAAA,GAAe,OAAA,CAAQ,aAAA;EAoD7C,SAAA,CACJ,IAAA,EAAM,YAAA,EACN,IAAA,EAAM,MAAA,oBACL,OAAA;IACD,OAAA;IACA,IAAA,GAAO,MAAA;IACP,KAAA;MAAU,IAAA;MAAc,OAAA;IAAA;EAAA;EA8FpB,YAAA,CACJ,IAAA,EAAM,YAAA,EACN,IAAA,EAAM,MAAA,oBACL,OAAA;IACD,OAAA;IACA,IAAA,GAAO,MAAA;IACP,KAAA;MAAU,IAAA;MAAc,OAAA;IAAA;EAAA;EAuI+B;EAAA,QAnF3C,eAAA;EAqGH;EAAA,QAxEH,gBAAA;EAyEL;EAAA,QAlEK,oBAAA;EA8E2D;EAAA,QAvE3D,iBAAA;EAWF,YAAA,CAAa,GAAA,EAAK,YAAA;IAAe,IAAA;EAAA,KAAkB,OAAA,CAAQ,aAAA;EAiB3D,YAAA,CAAa,GAAA,EAAK,YAAA;IAAe,IAAA;EAAA,KAAkB,OAAA,CAAQ,aAAA;EAY3D,YAAA,CAAa,GAAA,EAAK,YAAA;IAAe,IAAA;EAAA,KAAkB,OAAA,CAAQ,QAAA;EAgB3D,aAAA,CACJ,GAAA,EAAK,YAAA;IAAe,IAAA;EAAA,IACpB,OAAA,EAAS,oBAAA,GACR,OAAA,CAAQ,cAAA;EAYL,cAAA,CAAe,GAAA,EAAK,YAAA;IAAe,IAAA;EAAA,KAAkB,OAAA,CAAQ,eAAA;EAY7D,YAAA,CAAa,GAAA,EAAK,YAAA;IAAe,IAAA;EAAA,IAAiB,IAAA,EAAM,MAAA,oBAAuB,OAAA,CAAjB,WAAA,CAAiB,aAAA;EAY/E,cAAA,CACJ,GAAA,EAAK,YAAA;IAAe,IAAA;EAAA,IACpB,KAAA,UACA,OAAA,GAAU,gBAAA,GACT,OAAA,CAAQ,eAAA;EAYL,eAAA,CAAgB,GAAA,EAAK,YAAA;IAAe,IAAA;EAAA,KAAkB,OAAA,CAAQ,gBAAA;AAAA"}
|