@aigne/afs-registry 1.11.0-beta.7 → 1.11.0-beta.9

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/dist/index.mjs CHANGED
@@ -1,6 +1,519 @@
1
- import { AFSBaseProvider, AFSNotFoundError, Actions, Explain, List, Meta, Read, Search, Stat } from "@aigne/afs";
1
+ import { AFSBaseProvider, AFSNotFoundError, AFSValidationError, Actions, Explain, List, Meta, Read, Search, Stat } from "@aigne/afs";
2
+ import { buildURI, getTemplateVariableNames } from "@aigne/afs/utils/uri-template";
2
3
  import { joinURL } from "ufo";
3
4
 
5
+ //#region schema/providers.json
6
+ var providers = [
7
+ {
8
+ "name": "fs",
9
+ "description": "Mount a local filesystem directory",
10
+ "category": "storage",
11
+ "uriTemplate": "fs://{localPath+}",
12
+ "schema": {
13
+ "type": "object",
14
+ "properties": { "localPath": {
15
+ "type": "string",
16
+ "description": "Local directory path"
17
+ } },
18
+ "required": ["localPath"]
19
+ },
20
+ "tags": ["local", "filesystem"],
21
+ "useCases": [
22
+ "source code access",
23
+ "local data files",
24
+ "project navigation"
25
+ ]
26
+ },
27
+ {
28
+ "name": "git",
29
+ "description": "Mount a local Git repository",
30
+ "category": "version-control",
31
+ "uriTemplate": "git://{localPath+}",
32
+ "schema": {
33
+ "type": "object",
34
+ "properties": {
35
+ "localPath": {
36
+ "type": "string",
37
+ "description": "Local git repository path"
38
+ },
39
+ "branch": {
40
+ "type": "string",
41
+ "description": "Branch name (default: current branch)"
42
+ },
43
+ "remoteUrl": {
44
+ "type": "string",
45
+ "description": "Remote URL for cloning"
46
+ }
47
+ },
48
+ "required": ["localPath"]
49
+ },
50
+ "tags": ["git", "version-control"],
51
+ "useCases": [
52
+ "code review",
53
+ "version history",
54
+ "branch comparison"
55
+ ]
56
+ },
57
+ {
58
+ "name": "sqlite",
59
+ "description": "Mount a SQLite database as a virtual filesystem",
60
+ "category": "database",
61
+ "uriTemplate": "sqlite://{localPath+}",
62
+ "schema": {
63
+ "type": "object",
64
+ "properties": { "localPath": {
65
+ "type": "string",
66
+ "description": "Path to SQLite database file"
67
+ } },
68
+ "required": ["localPath"]
69
+ },
70
+ "tags": ["sqlite", "database"],
71
+ "useCases": [
72
+ "structured data",
73
+ "application state",
74
+ "data analysis"
75
+ ]
76
+ },
77
+ {
78
+ "name": "json",
79
+ "description": "Mount a JSON or YAML file as a virtual filesystem",
80
+ "category": "structured-data",
81
+ "uriTemplate": "json://{localPath+}",
82
+ "schema": {
83
+ "type": "object",
84
+ "properties": { "localPath": {
85
+ "type": "string",
86
+ "description": "Path to JSON or YAML file"
87
+ } },
88
+ "required": ["localPath"]
89
+ },
90
+ "tags": [
91
+ "json",
92
+ "yaml",
93
+ "structured-data"
94
+ ],
95
+ "useCases": [
96
+ "configuration files",
97
+ "data inspection",
98
+ "API response exploration"
99
+ ]
100
+ },
101
+ {
102
+ "name": "toml",
103
+ "description": "Mount a TOML file as a virtual filesystem",
104
+ "category": "structured-data",
105
+ "uriTemplate": "toml://{localPath+}",
106
+ "schema": {
107
+ "type": "object",
108
+ "properties": { "localPath": {
109
+ "type": "string",
110
+ "description": "Path to TOML file"
111
+ } },
112
+ "required": ["localPath"]
113
+ },
114
+ "tags": ["toml", "structured-data"],
115
+ "useCases": ["configuration files", "Cargo.toml inspection"]
116
+ },
117
+ {
118
+ "name": "sandbox",
119
+ "description": "Sandboxed JavaScript execution environment for LLM-generated code",
120
+ "category": "compute",
121
+ "uriTemplate": "sandbox://",
122
+ "schema": {
123
+ "type": "object",
124
+ "properties": {}
125
+ },
126
+ "tags": [
127
+ "sandbox",
128
+ "compute",
129
+ "javascript"
130
+ ],
131
+ "useCases": [
132
+ "code execution",
133
+ "data transformation",
134
+ "scripting"
135
+ ]
136
+ },
137
+ {
138
+ "name": "workspace",
139
+ "description": "Workspace container with sub-mounts",
140
+ "category": "composite",
141
+ "uriTemplate": "workspace://{localPath+}",
142
+ "schema": {
143
+ "type": "object",
144
+ "properties": { "localPath": {
145
+ "type": "string",
146
+ "description": "Workspace directory path"
147
+ } },
148
+ "required": ["localPath"]
149
+ },
150
+ "tags": ["workspace", "composite"],
151
+ "useCases": [
152
+ "project organization",
153
+ "multi-source access",
154
+ "monorepo navigation"
155
+ ]
156
+ },
157
+ {
158
+ "name": "github",
159
+ "description": "GitHub Issues, Pull Requests, and Actions",
160
+ "category": "integration",
161
+ "uriTemplate": "github://{owner}/{repo}",
162
+ "schema": {
163
+ "type": "object",
164
+ "properties": {
165
+ "owner": {
166
+ "type": "string",
167
+ "description": "GitHub organization or user"
168
+ },
169
+ "repo": {
170
+ "type": "string",
171
+ "description": "Repository name"
172
+ },
173
+ "token": {
174
+ "type": "string",
175
+ "description": "GitHub personal access token"
176
+ }
177
+ },
178
+ "required": ["owner", "repo"]
179
+ },
180
+ "tags": [
181
+ "github",
182
+ "git",
183
+ "integration"
184
+ ],
185
+ "useCases": [
186
+ "issue tracking",
187
+ "PR review",
188
+ "CI/CD monitoring"
189
+ ]
190
+ },
191
+ {
192
+ "name": "http",
193
+ "description": "Remote AFS server via HTTP",
194
+ "category": "integration",
195
+ "uriTemplate": "http://{host+}",
196
+ "schema": {
197
+ "type": "object",
198
+ "properties": {
199
+ "host": {
200
+ "type": "string",
201
+ "description": "Server host and path"
202
+ },
203
+ "token": {
204
+ "type": "string",
205
+ "description": "Authentication token"
206
+ }
207
+ },
208
+ "required": ["host"]
209
+ },
210
+ "tags": [
211
+ "http",
212
+ "remote",
213
+ "integration"
214
+ ]
215
+ },
216
+ {
217
+ "name": "https",
218
+ "description": "Remote AFS server via HTTPS",
219
+ "category": "integration",
220
+ "uriTemplate": "https://{host+}",
221
+ "schema": {
222
+ "type": "object",
223
+ "properties": {
224
+ "host": {
225
+ "type": "string",
226
+ "description": "Server host and path"
227
+ },
228
+ "token": {
229
+ "type": "string",
230
+ "description": "Authentication token"
231
+ }
232
+ },
233
+ "required": ["host"]
234
+ },
235
+ "tags": [
236
+ "https",
237
+ "remote",
238
+ "integration"
239
+ ]
240
+ },
241
+ {
242
+ "name": "mcp-stdio",
243
+ "description": "MCP server via stdio transport",
244
+ "category": "integration",
245
+ "uriTemplate": "mcp+stdio://{command+}",
246
+ "schema": {
247
+ "type": "object",
248
+ "properties": {
249
+ "command": {
250
+ "type": "string",
251
+ "description": "Command to run the MCP server"
252
+ },
253
+ "args": {
254
+ "type": "array",
255
+ "items": { "type": "string" },
256
+ "description": "Command arguments"
257
+ },
258
+ "env": {
259
+ "type": "object",
260
+ "description": "Environment variables"
261
+ }
262
+ },
263
+ "required": ["command"]
264
+ },
265
+ "tags": [
266
+ "mcp",
267
+ "stdio",
268
+ "integration"
269
+ ]
270
+ },
271
+ {
272
+ "name": "mcp-http",
273
+ "description": "MCP server via HTTP transport",
274
+ "category": "integration",
275
+ "uriTemplate": "mcp+http://{url+}",
276
+ "schema": {
277
+ "type": "object",
278
+ "properties": { "url": {
279
+ "type": "string",
280
+ "description": "HTTP URL of the MCP server"
281
+ } },
282
+ "required": ["url"]
283
+ },
284
+ "tags": [
285
+ "mcp",
286
+ "http",
287
+ "integration"
288
+ ]
289
+ },
290
+ {
291
+ "name": "mcp-sse",
292
+ "description": "MCP server via SSE transport",
293
+ "category": "integration",
294
+ "uriTemplate": "mcp+sse://{url+}",
295
+ "schema": {
296
+ "type": "object",
297
+ "properties": { "url": {
298
+ "type": "string",
299
+ "description": "SSE URL of the MCP server"
300
+ } },
301
+ "required": ["url"]
302
+ },
303
+ "tags": [
304
+ "mcp",
305
+ "sse",
306
+ "integration"
307
+ ]
308
+ },
309
+ {
310
+ "name": "s3",
311
+ "description": "AWS S3 and S3-compatible object storage",
312
+ "category": "cloud-storage",
313
+ "uriTemplate": "s3://{bucket}/{prefix+?}",
314
+ "schema": {
315
+ "type": "object",
316
+ "properties": {
317
+ "bucket": {
318
+ "type": "string",
319
+ "description": "S3 bucket name"
320
+ },
321
+ "prefix": {
322
+ "type": "string",
323
+ "description": "Object key prefix"
324
+ },
325
+ "region": {
326
+ "type": "string",
327
+ "description": "AWS region"
328
+ },
329
+ "accessKeyId": {
330
+ "type": "string",
331
+ "description": "AWS access key ID"
332
+ },
333
+ "secretAccessKey": {
334
+ "type": "string",
335
+ "description": "AWS secret access key"
336
+ },
337
+ "endpoint": {
338
+ "type": "string",
339
+ "description": "Custom S3 endpoint URL"
340
+ },
341
+ "profile": {
342
+ "type": "string",
343
+ "description": "AWS CLI profile name"
344
+ }
345
+ },
346
+ "required": ["bucket"]
347
+ },
348
+ "tags": [
349
+ "aws",
350
+ "s3",
351
+ "cloud",
352
+ "storage"
353
+ ],
354
+ "useCases": [
355
+ "cloud file storage",
356
+ "data lake access",
357
+ "backup management"
358
+ ]
359
+ },
360
+ {
361
+ "name": "gcs",
362
+ "description": "Google Cloud Storage",
363
+ "category": "cloud-storage",
364
+ "uriTemplate": "gcs://{bucket}/{prefix+?}",
365
+ "schema": {
366
+ "type": "object",
367
+ "properties": {
368
+ "bucket": {
369
+ "type": "string",
370
+ "description": "GCS bucket name"
371
+ },
372
+ "prefix": {
373
+ "type": "string",
374
+ "description": "Object key prefix"
375
+ },
376
+ "projectId": {
377
+ "type": "string",
378
+ "description": "Google Cloud project ID"
379
+ },
380
+ "keyFilename": {
381
+ "type": "string",
382
+ "description": "Path to service account key file"
383
+ }
384
+ },
385
+ "required": ["bucket"]
386
+ },
387
+ "tags": [
388
+ "gcp",
389
+ "gcs",
390
+ "cloud",
391
+ "storage"
392
+ ],
393
+ "useCases": [
394
+ "cloud file storage",
395
+ "data lake access",
396
+ "ML training data"
397
+ ]
398
+ },
399
+ {
400
+ "name": "ec2",
401
+ "description": "AWS EC2 compute instances",
402
+ "category": "cloud-compute",
403
+ "uriTemplate": "ec2://{region?}",
404
+ "schema": {
405
+ "type": "object",
406
+ "properties": {
407
+ "region": {
408
+ "type": "string",
409
+ "description": "AWS region"
410
+ },
411
+ "profile": {
412
+ "type": "string",
413
+ "description": "AWS CLI profile name"
414
+ },
415
+ "endpoint": {
416
+ "type": "string",
417
+ "description": "Custom EC2 endpoint URL"
418
+ }
419
+ }
420
+ },
421
+ "tags": [
422
+ "aws",
423
+ "ec2",
424
+ "cloud",
425
+ "compute"
426
+ ],
427
+ "useCases": [
428
+ "instance management",
429
+ "cloud operations",
430
+ "infrastructure monitoring"
431
+ ]
432
+ },
433
+ {
434
+ "name": "gce",
435
+ "description": "Google Compute Engine instances",
436
+ "category": "cloud-compute",
437
+ "uriTemplate": "gce://{project}/{zone}",
438
+ "schema": {
439
+ "type": "object",
440
+ "properties": {
441
+ "project": {
442
+ "type": "string",
443
+ "description": "Google Cloud project ID"
444
+ },
445
+ "zone": {
446
+ "type": "string",
447
+ "description": "Compute zone"
448
+ },
449
+ "keyFilename": {
450
+ "type": "string",
451
+ "description": "Path to service account key file"
452
+ }
453
+ },
454
+ "required": ["project", "zone"]
455
+ },
456
+ "tags": [
457
+ "gcp",
458
+ "gce",
459
+ "cloud",
460
+ "compute"
461
+ ],
462
+ "useCases": [
463
+ "instance management",
464
+ "cloud operations",
465
+ "infrastructure monitoring"
466
+ ]
467
+ },
468
+ {
469
+ "name": "dns",
470
+ "description": "DNS zone management",
471
+ "category": "cloud-dns",
472
+ "uriTemplate": "dns://{zone}",
473
+ "schema": {
474
+ "type": "object",
475
+ "properties": { "zone": {
476
+ "type": "string",
477
+ "description": "DNS zone name"
478
+ } },
479
+ "required": ["zone"]
480
+ },
481
+ "tags": ["dns", "cloud"],
482
+ "useCases": ["DNS record management", "domain configuration"]
483
+ },
484
+ {
485
+ "name": "cloudflare",
486
+ "description": "Cloudflare Workers, KV, and Pages",
487
+ "category": "cloud-platform",
488
+ "uriTemplate": "cloudflare://{accountId}",
489
+ "schema": {
490
+ "type": "object",
491
+ "properties": {
492
+ "accountId": {
493
+ "type": "string",
494
+ "description": "Cloudflare account ID"
495
+ },
496
+ "apiToken": {
497
+ "type": "string",
498
+ "description": "Cloudflare API token"
499
+ }
500
+ }
501
+ },
502
+ "tags": [
503
+ "cloudflare",
504
+ "cloud",
505
+ "workers",
506
+ "kv"
507
+ ],
508
+ "useCases": [
509
+ "edge computing",
510
+ "KV storage",
511
+ "CDN management"
512
+ ]
513
+ }
514
+ ];
515
+
516
+ //#endregion
4
517
  //#region \0@oxc-project+runtime@0.108.0/helpers/decorate.js
5
518
  function __decorate(decorators, target, key, desc) {
6
519
  var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
@@ -30,15 +543,72 @@ var AFSRegistry = class extends AFSBaseProvider {
30
543
  description = "AFS Provider Registry — discover and mount providers";
31
544
  accessMode = "readwrite";
32
545
  providers;
546
+ remoteUrl;
547
+ fetchPromise;
33
548
  loadProviderFn;
34
- constructor(options) {
549
+ constructor(options = {}) {
35
550
  super();
36
- this.providers = JSON.parse(JSON.stringify(options.providers));
551
+ if (options.providers && options.providers.length > 0) this.providers = JSON.parse(JSON.stringify(options.providers));
552
+ else {
553
+ this.providers = JSON.parse(JSON.stringify(providers));
554
+ this.remoteUrl = options.url;
555
+ if (this.remoteUrl) this.fetchPromise = this.fetchRemoteProviders(this.remoteUrl);
556
+ }
557
+ }
558
+ async fetchRemoteProviders(url) {
559
+ try {
560
+ const response = await fetch(url);
561
+ if (!response.ok) return;
562
+ const data = await response.json();
563
+ if (Array.isArray(data?.providers) && data.providers.length > 0) this.providers = data.providers.map((p) => {
564
+ const entry = { ...p };
565
+ if (!entry.uriTemplate && entry.uri) {
566
+ entry.uriTemplate = entry.uri;
567
+ delete entry.uri;
568
+ }
569
+ if (!entry.useCases && entry.use_cases) {
570
+ entry.useCases = entry.use_cases;
571
+ delete entry.use_cases;
572
+ }
573
+ delete entry.capabilities_summary;
574
+ return entry;
575
+ });
576
+ } catch {}
577
+ }
578
+ async ensureLoaded() {
579
+ if (this.fetchPromise) {
580
+ await this.fetchPromise;
581
+ this.fetchPromise = void 0;
582
+ }
37
583
  }
38
584
  onMount(root) {
39
585
  this.loadProviderFn = root.loadProvider;
40
586
  }
41
- get byCategory() {
587
+ async list(path, options) {
588
+ await this.ensureLoaded();
589
+ return super.list(path, options);
590
+ }
591
+ async read(path, options) {
592
+ await this.ensureLoaded();
593
+ return super.read(path, options);
594
+ }
595
+ async stat(path, options) {
596
+ await this.ensureLoaded();
597
+ return super.stat(path, options);
598
+ }
599
+ async search(path, query, options) {
600
+ await this.ensureLoaded();
601
+ return super.search(path, query, options);
602
+ }
603
+ async exec(path, args, options) {
604
+ await this.ensureLoaded();
605
+ return super.exec(path, args, options);
606
+ }
607
+ async explain(path, options) {
608
+ await this.ensureLoaded();
609
+ return super.explain(path, options);
610
+ }
611
+ buildByCategory() {
42
612
  const map = /* @__PURE__ */ new Map();
43
613
  for (const p of this.providers) {
44
614
  const list = map.get(p.category) || [];
@@ -47,7 +617,7 @@ var AFSRegistry = class extends AFSBaseProvider {
47
617
  }
48
618
  return map;
49
619
  }
50
- get byTag() {
620
+ buildByTag() {
51
621
  const map = /* @__PURE__ */ new Map();
52
622
  for (const p of this.providers) for (const tag of p.tags || []) {
53
623
  const list = map.get(tag) || [];
@@ -74,7 +644,7 @@ var AFSRegistry = class extends AFSBaseProvider {
74
644
  path: "/by-category",
75
645
  meta: {
76
646
  kind: "afs:directory",
77
- childrenCount: this.byCategory.size
647
+ childrenCount: this.buildByCategory().size
78
648
  }
79
649
  },
80
650
  {
@@ -82,7 +652,7 @@ var AFSRegistry = class extends AFSBaseProvider {
82
652
  path: "/by-tag",
83
653
  meta: {
84
654
  kind: "afs:directory",
85
- childrenCount: this.byTag.size
655
+ childrenCount: this.buildByTag().size
86
656
  }
87
657
  }
88
658
  ] };
@@ -113,19 +683,19 @@ var AFSRegistry = class extends AFSBaseProvider {
113
683
  }] };
114
684
  }
115
685
  async listCategories(_ctx) {
116
- return { data: Array.from(this.byCategory.entries()).map(([cat, providers]) => ({
686
+ return { data: Array.from(this.buildByCategory().entries()).map(([cat, providers$1]) => ({
117
687
  id: cat,
118
688
  path: joinURL("/by-category", cat),
119
689
  meta: {
120
690
  kind: "afs:directory",
121
- childrenCount: providers.length
691
+ childrenCount: providers$1.length
122
692
  }
123
693
  })) };
124
694
  }
125
695
  async listCategoryProviders(ctx) {
126
- const providers = this.byCategory.get(ctx.params.category);
127
- if (!providers) throw new AFSNotFoundError(ctx.path);
128
- return { data: providers.map((p) => ({
696
+ const providers$1 = this.buildByCategory().get(ctx.params.category);
697
+ if (!providers$1) throw new AFSNotFoundError(ctx.path);
698
+ return { data: providers$1.map((p) => ({
129
699
  id: p.name,
130
700
  path: joinURL("/by-category", ctx.params.category, p.name),
131
701
  summary: p.description,
@@ -136,23 +706,23 @@ var AFSRegistry = class extends AFSBaseProvider {
136
706
  })) };
137
707
  }
138
708
  async listTags(_ctx) {
139
- return { data: Array.from(this.byTag.entries()).map(([tag, providers]) => ({
709
+ return { data: Array.from(this.buildByTag().entries()).map(([tag, providers$1]) => ({
140
710
  id: tag,
141
711
  path: joinURL("/by-tag", tag),
142
712
  meta: {
143
713
  kind: "afs:directory",
144
- childrenCount: providers.length
714
+ childrenCount: providers$1.length
145
715
  }
146
716
  })) };
147
717
  }
148
718
  async listCategoryProviderChildren(ctx) {
149
- if (!this.byCategory.get(ctx.params.category)?.find((p) => p.name === ctx.params.name)) throw new AFSNotFoundError(ctx.path);
719
+ if (!this.buildByCategory().get(ctx.params.category)?.find((p) => p.name === ctx.params.name)) throw new AFSNotFoundError(ctx.path);
150
720
  return { data: [] };
151
721
  }
152
722
  async listTagProviders(ctx) {
153
- const providers = this.byTag.get(ctx.params.tag);
154
- if (!providers) throw new AFSNotFoundError(ctx.path);
155
- return { data: providers.map((p) => ({
723
+ const providers$1 = this.buildByTag().get(ctx.params.tag);
724
+ if (!providers$1) throw new AFSNotFoundError(ctx.path);
725
+ return { data: providers$1.map((p) => ({
156
726
  id: p.name,
157
727
  path: joinURL("/by-tag", ctx.params.tag, p.name),
158
728
  summary: p.description,
@@ -160,7 +730,7 @@ var AFSRegistry = class extends AFSBaseProvider {
160
730
  })) };
161
731
  }
162
732
  async listTagProviderChildren(ctx) {
163
- if (!this.byTag.get(ctx.params.tag)?.find((p) => p.name === ctx.params.name)) throw new AFSNotFoundError(ctx.path);
733
+ if (!this.buildByTag().get(ctx.params.tag)?.find((p) => p.name === ctx.params.name)) throw new AFSNotFoundError(ctx.path);
164
734
  return { data: [] };
165
735
  }
166
736
  async listFileNode(ctx) {
@@ -231,14 +801,21 @@ var AFSRegistry = class extends AFSBaseProvider {
231
801
  provider.description,
232
802
  "",
233
803
  `**Category:** ${provider.category}`,
234
- `**Scheme:** \`${provider.scheme}://\``,
235
- "",
236
- `## Capabilities`,
237
- "",
238
- provider.capabilities_summary
804
+ `**URI Template:** \`${provider.uriTemplate}\``
239
805
  ];
806
+ if (provider.schema) {
807
+ const props = provider.schema.properties;
808
+ const required = new Set(provider.schema.required ?? []);
809
+ if (props) {
810
+ lines.push("", "## Configuration", "");
811
+ for (const [key, val] of Object.entries(props)) {
812
+ const req = required.has(key) ? " **(required)**" : "";
813
+ lines.push(`- \`${key}\` (${val.type ?? "string"})${req} — ${val.description ?? ""}`);
814
+ }
815
+ }
816
+ }
240
817
  if (provider.tags?.length) lines.push("", `**Tags:** ${provider.tags.join(", ")}`);
241
- if (provider.use_cases?.length) lines.push("", `## Use Cases`, "", ...provider.use_cases.map((u) => `- ${u}`));
818
+ if (provider.useCases?.length) lines.push("", `## Use Cases`, "", ...provider.useCases.map((u) => `- ${u}`));
242
819
  lines.push("");
243
820
  return {
244
821
  id: "README.md",
@@ -253,32 +830,32 @@ var AFSRegistry = class extends AFSBaseProvider {
253
830
  path: "/by-category",
254
831
  content: {
255
832
  type: "directory",
256
- count: this.byCategory.size
833
+ count: this.buildByCategory().size
257
834
  },
258
835
  meta: {
259
836
  kind: "afs:directory",
260
- childrenCount: this.byCategory.size
837
+ childrenCount: this.buildByCategory().size
261
838
  }
262
839
  };
263
840
  }
264
841
  async readCategory(ctx) {
265
- const providers = this.byCategory.get(ctx.params.category);
266
- if (!providers) throw new AFSNotFoundError(ctx.path);
842
+ const providers$1 = this.buildByCategory().get(ctx.params.category);
843
+ if (!providers$1) throw new AFSNotFoundError(ctx.path);
267
844
  return {
268
845
  id: ctx.params.category,
269
846
  path: joinURL("/by-category", ctx.params.category),
270
847
  content: {
271
848
  type: "category",
272
- count: providers.length
849
+ count: providers$1.length
273
850
  },
274
851
  meta: {
275
852
  kind: "afs:directory",
276
- childrenCount: providers.length
853
+ childrenCount: providers$1.length
277
854
  }
278
855
  };
279
856
  }
280
857
  async readCategoryProvider(ctx) {
281
- const provider = this.byCategory.get(ctx.params.category)?.find((p) => p.name === ctx.params.name);
858
+ const provider = this.buildByCategory().get(ctx.params.category)?.find((p) => p.name === ctx.params.name);
282
859
  if (!provider) throw new AFSNotFoundError(ctx.path);
283
860
  return {
284
861
  id: provider.name,
@@ -297,32 +874,32 @@ var AFSRegistry = class extends AFSBaseProvider {
297
874
  path: "/by-tag",
298
875
  content: {
299
876
  type: "directory",
300
- count: this.byTag.size
877
+ count: this.buildByTag().size
301
878
  },
302
879
  meta: {
303
880
  kind: "afs:directory",
304
- childrenCount: this.byTag.size
881
+ childrenCount: this.buildByTag().size
305
882
  }
306
883
  };
307
884
  }
308
885
  async readTag(ctx) {
309
- const providers = this.byTag.get(ctx.params.tag);
310
- if (!providers) throw new AFSNotFoundError(ctx.path);
886
+ const providers$1 = this.buildByTag().get(ctx.params.tag);
887
+ if (!providers$1) throw new AFSNotFoundError(ctx.path);
311
888
  return {
312
889
  id: ctx.params.tag,
313
890
  path: joinURL("/by-tag", ctx.params.tag),
314
891
  content: {
315
892
  type: "tag",
316
- count: providers.length
893
+ count: providers$1.length
317
894
  },
318
895
  meta: {
319
896
  kind: "afs:directory",
320
- childrenCount: providers.length
897
+ childrenCount: providers$1.length
321
898
  }
322
899
  };
323
900
  }
324
901
  async readTagProvider(ctx) {
325
- const provider = this.byTag.get(ctx.params.tag)?.find((p) => p.name === ctx.params.name);
902
+ const provider = this.buildByTag().get(ctx.params.tag)?.find((p) => p.name === ctx.params.name);
326
903
  if (!provider) throw new AFSNotFoundError(ctx.path);
327
904
  return {
328
905
  id: provider.name,
@@ -358,8 +935,8 @@ var AFSRegistry = class extends AFSBaseProvider {
358
935
  path: "/.meta",
359
936
  content: {
360
937
  providerCount: this.providers.length,
361
- categories: Array.from(this.byCategory.keys()),
362
- tags: Array.from(this.byTag.keys())
938
+ categories: Array.from(this.buildByCategory().keys()),
939
+ tags: Array.from(this.buildByTag().keys())
363
940
  },
364
941
  meta: {
365
942
  kind: "registry:root",
@@ -387,7 +964,7 @@ var AFSRegistry = class extends AFSBaseProvider {
387
964
  content: {
388
965
  name: provider.name,
389
966
  category: provider.category,
390
- scheme: provider.scheme
967
+ uriTemplate: provider.uriTemplate
391
968
  },
392
969
  meta: {
393
970
  kind: "registry:provider",
@@ -419,31 +996,31 @@ var AFSRegistry = class extends AFSBaseProvider {
419
996
  return {
420
997
  id: ".meta",
421
998
  path: "/by-category/.meta",
422
- content: { count: this.byCategory.size },
999
+ content: { count: this.buildByCategory().size },
423
1000
  meta: {
424
1001
  kind: "afs:directory",
425
- childrenCount: this.byCategory.size
1002
+ childrenCount: this.buildByCategory().size
426
1003
  }
427
1004
  };
428
1005
  }
429
1006
  async categoryMeta(ctx) {
430
- const providers = this.byCategory.get(ctx.params.category);
431
- if (!providers) throw new AFSNotFoundError(ctx.path);
1007
+ const providers$1 = this.buildByCategory().get(ctx.params.category);
1008
+ if (!providers$1) throw new AFSNotFoundError(ctx.path);
432
1009
  return {
433
1010
  id: ".meta",
434
1011
  path: joinURL("/by-category", ctx.params.category, ".meta"),
435
1012
  content: {
436
1013
  category: ctx.params.category,
437
- count: providers.length
1014
+ count: providers$1.length
438
1015
  },
439
1016
  meta: {
440
1017
  kind: "afs:directory",
441
- childrenCount: providers.length
1018
+ childrenCount: providers$1.length
442
1019
  }
443
1020
  };
444
1021
  }
445
1022
  async categoryProviderMeta(ctx) {
446
- const provider = this.byCategory.get(ctx.params.category)?.find((p) => p.name === ctx.params.name);
1023
+ const provider = this.buildByCategory().get(ctx.params.category)?.find((p) => p.name === ctx.params.name);
447
1024
  if (!provider) throw new AFSNotFoundError(ctx.path);
448
1025
  return {
449
1026
  id: ".meta",
@@ -456,31 +1033,31 @@ var AFSRegistry = class extends AFSBaseProvider {
456
1033
  return {
457
1034
  id: ".meta",
458
1035
  path: "/by-tag/.meta",
459
- content: { count: this.byTag.size },
1036
+ content: { count: this.buildByTag().size },
460
1037
  meta: {
461
1038
  kind: "afs:directory",
462
- childrenCount: this.byTag.size
1039
+ childrenCount: this.buildByTag().size
463
1040
  }
464
1041
  };
465
1042
  }
466
1043
  async tagMeta(ctx) {
467
- const providers = this.byTag.get(ctx.params.tag);
468
- if (!providers) throw new AFSNotFoundError(ctx.path);
1044
+ const providers$1 = this.buildByTag().get(ctx.params.tag);
1045
+ if (!providers$1) throw new AFSNotFoundError(ctx.path);
469
1046
  return {
470
1047
  id: ".meta",
471
1048
  path: joinURL("/by-tag", ctx.params.tag, ".meta"),
472
1049
  content: {
473
1050
  tag: ctx.params.tag,
474
- count: providers.length
1051
+ count: providers$1.length
475
1052
  },
476
1053
  meta: {
477
1054
  kind: "afs:directory",
478
- childrenCount: providers.length
1055
+ childrenCount: providers$1.length
479
1056
  }
480
1057
  };
481
1058
  }
482
1059
  async tagProviderMeta(ctx) {
483
- const provider = this.byTag.get(ctx.params.tag)?.find((p) => p.name === ctx.params.name);
1060
+ const provider = this.buildByTag().get(ctx.params.tag)?.find((p) => p.name === ctx.params.name);
484
1061
  if (!provider) throw new AFSNotFoundError(ctx.path);
485
1062
  return {
486
1063
  id: ".meta",
@@ -519,9 +1096,57 @@ var AFSRegistry = class extends AFSBaseProvider {
519
1096
  }
520
1097
  };
521
1098
  }
1099
+ /**
1100
+ * Build mount action inputSchema for a provider
1101
+ */
1102
+ buildMountSchema(provider) {
1103
+ return {
1104
+ type: "object",
1105
+ properties: {
1106
+ path: {
1107
+ type: "string",
1108
+ description: "Mount path"
1109
+ },
1110
+ uri: {
1111
+ type: "string",
1112
+ description: `Full provider URI (overrides template). Template: ${provider.uriTemplate}`
1113
+ },
1114
+ accessMode: {
1115
+ type: "string",
1116
+ enum: ["readonly", "readwrite"],
1117
+ description: "Access mode (default: readonly)"
1118
+ },
1119
+ auth: {
1120
+ type: "string",
1121
+ description: "Authentication token"
1122
+ },
1123
+ description: {
1124
+ type: "string",
1125
+ description: "Human-readable description"
1126
+ },
1127
+ scope: {
1128
+ type: "string",
1129
+ enum: [
1130
+ "cwd",
1131
+ "project",
1132
+ "user"
1133
+ ],
1134
+ description: "Where to persist the mount config. 'cwd' (default) = current directory, 'project' = project root (.git), 'user' = user home"
1135
+ },
1136
+ sensitiveArgs: {
1137
+ type: "array",
1138
+ items: { type: "string" },
1139
+ description: "Field names that should be treated as sensitive credentials (stored in credentials.toml instead of config.toml)"
1140
+ },
1141
+ ...provider.schema?.properties ?? {}
1142
+ },
1143
+ required: ["path", ...provider.schema?.required ?? []]
1144
+ };
1145
+ }
522
1146
  async listProviderActions(ctx) {
523
1147
  const provider = this.findProvider(ctx.params.name);
524
1148
  if (!provider) throw new AFSNotFoundError(ctx.path);
1149
+ const mountSchema = this.buildMountSchema(provider);
525
1150
  return { data: [{
526
1151
  id: "mount",
527
1152
  path: joinURL("/providers", provider.name, ".actions", "mount"),
@@ -532,30 +1157,52 @@ var AFSRegistry = class extends AFSBaseProvider {
532
1157
  actions: [{
533
1158
  name: "mount",
534
1159
  description: `Mount the ${provider.name} provider`,
535
- inputSchema: {
536
- type: "object",
537
- properties: {
538
- path: {
539
- type: "string",
540
- description: "Mount path"
541
- },
542
- uri: {
543
- type: "string",
544
- description: `Provider URI (default: ${provider.scheme}:///)`
545
- }
546
- },
547
- required: ["path"]
548
- }
1160
+ inputSchema: mountSchema
549
1161
  }]
550
1162
  }] };
551
1163
  }
1164
+ async readMountAction(ctx) {
1165
+ const provider = this.findProvider(ctx.params.name);
1166
+ if (!provider) throw new AFSNotFoundError(ctx.path);
1167
+ const mountSchema = this.buildMountSchema(provider);
1168
+ return {
1169
+ id: "mount",
1170
+ path: joinURL("/providers", provider.name, ".actions", "mount"),
1171
+ content: {
1172
+ description: `Mount the ${provider.name} provider`,
1173
+ inputSchema: mountSchema
1174
+ },
1175
+ meta: {
1176
+ kind: "afs:executable",
1177
+ kinds: ["afs:executable", "afs:node"],
1178
+ description: `Mount the ${provider.name} provider`,
1179
+ inputSchema: mountSchema
1180
+ }
1181
+ };
1182
+ }
552
1183
  async mountProvider(ctx, args) {
553
1184
  const provider = this.findProvider(ctx.params.name);
554
1185
  if (!provider) throw new AFSNotFoundError(ctx.path);
555
1186
  if (!this.loadProviderFn) throw new Error("loadProvider not configured — Registry was not mounted on an AFS instance");
1187
+ if (typeof args.path !== "string" || args.path.trim() === "") throw new AFSValidationError("Input validation failed: path: must be a non-empty string");
556
1188
  const mountPath = args.path;
557
- const uri = args.uri || `${provider.scheme}:///`;
558
- await this.loadProviderFn(uri, mountPath);
1189
+ let uri = args.uri;
1190
+ if (!uri) {
1191
+ const varNames = getTemplateVariableNames(provider.uriTemplate);
1192
+ const vars = {};
1193
+ for (const name of varNames) {
1194
+ const value = args[name];
1195
+ vars[name] = value != null ? String(value) : void 0;
1196
+ }
1197
+ try {
1198
+ uri = buildURI(provider.uriTemplate, vars);
1199
+ } catch {
1200
+ uri = `${provider.uriTemplate.split("://")[0]}://`;
1201
+ }
1202
+ }
1203
+ const { path: _path, uri: _uri, sensitiveArgs, ...options } = args;
1204
+ if (Array.isArray(sensitiveArgs) && sensitiveArgs.length > 0) options._sensitiveArgs = sensitiveArgs;
1205
+ await this.loadProviderFn(uri, mountPath, Object.keys(options).length > 0 ? options : void 0);
559
1206
  return {
560
1207
  success: true,
561
1208
  data: {
@@ -567,7 +1214,7 @@ var AFSRegistry = class extends AFSBaseProvider {
567
1214
  }
568
1215
  async searchRoot(_ctx, query, options) {
569
1216
  const q = query.toLowerCase();
570
- let matched = this.providers.filter((p) => p.name.toLowerCase().includes(q) || p.description.toLowerCase().includes(q) || p.category.toLowerCase().includes(q) || (p.tags || []).some((t) => t.toLowerCase().includes(q)) || p.capabilities_summary.toLowerCase().includes(q));
1217
+ let matched = this.providers.filter((p) => p.name.toLowerCase().includes(q) || p.description.toLowerCase().includes(q) || p.category.toLowerCase().includes(q) || p.uriTemplate.toLowerCase().includes(q) || (p.tags || []).some((t) => t.toLowerCase().includes(q)) || (p.useCases || []).some((u) => u.toLowerCase().includes(q)));
571
1218
  if (options?.limit !== void 0 && matched.length > options.limit) matched = matched.slice(0, options.limit);
572
1219
  return { data: matched.map((p) => ({
573
1220
  id: p.name,
@@ -638,6 +1285,7 @@ __decorate([Meta("/by-tag/:tag/:name")], AFSRegistry.prototype, "tagProviderMeta
638
1285
  __decorate([Actions("/")], AFSRegistry.prototype, "listRootActions", null);
639
1286
  __decorate([Actions.Exec("/", "search")], AFSRegistry.prototype, "searchProviders", null);
640
1287
  __decorate([Actions("/providers/:name")], AFSRegistry.prototype, "listProviderActions", null);
1288
+ __decorate([Read("/providers/:name/.actions/mount")], AFSRegistry.prototype, "readMountAction", null);
641
1289
  __decorate([Actions.Exec("/providers/:name", "mount")], AFSRegistry.prototype, "mountProvider", null);
642
1290
  __decorate([Search("/")], AFSRegistry.prototype, "searchRoot", null);
643
1291
  __decorate([Explain("/:path*")], AFSRegistry.prototype, "explainPath", null);