@aigne/afs-cli 1.11.0-beta.11 → 1.11.0-beta.12

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.
Files changed (154) hide show
  1. package/dist/cli.cjs +3 -2
  2. package/dist/cli.mjs +3 -2
  3. package/dist/cli.mjs.map +1 -1
  4. package/dist/config/afs-loader.cjs +64 -315
  5. package/dist/config/afs-loader.d.cts.map +1 -1
  6. package/dist/config/afs-loader.d.mts +2 -1
  7. package/dist/config/afs-loader.d.mts.map +1 -1
  8. package/dist/config/afs-loader.mjs +59 -310
  9. package/dist/config/afs-loader.mjs.map +1 -1
  10. package/dist/config/credential-helpers.cjs +291 -0
  11. package/dist/config/credential-helpers.d.mts +2 -0
  12. package/dist/config/credential-helpers.mjs +288 -0
  13. package/dist/config/credential-helpers.mjs.map +1 -0
  14. package/dist/config/loader.cjs +3 -1
  15. package/dist/config/loader.mjs +3 -2
  16. package/dist/config/loader.mjs.map +1 -1
  17. package/dist/config/program-install.cjs +276 -0
  18. package/dist/config/program-install.d.mts +1 -0
  19. package/dist/config/program-install.mjs +273 -0
  20. package/dist/config/program-install.mjs.map +1 -0
  21. package/dist/core/commands/connect.cjs +53 -0
  22. package/dist/core/commands/connect.d.mts +2 -0
  23. package/dist/core/commands/connect.mjs +55 -0
  24. package/dist/core/commands/connect.mjs.map +1 -0
  25. package/dist/core/commands/daemon.cjs +207 -0
  26. package/dist/core/commands/daemon.d.mts +2 -0
  27. package/dist/core/commands/daemon.mjs +208 -0
  28. package/dist/core/commands/daemon.mjs.map +1 -0
  29. package/dist/core/commands/explain.cjs +3 -1
  30. package/dist/core/commands/explain.mjs +3 -1
  31. package/dist/core/commands/explain.mjs.map +1 -1
  32. package/dist/core/commands/explore.cjs +47 -12
  33. package/dist/core/commands/explore.mjs +47 -12
  34. package/dist/core/commands/explore.mjs.map +1 -1
  35. package/dist/core/commands/gen-agent-md.cjs +126 -0
  36. package/dist/core/commands/gen-agent-md.d.mts +2 -0
  37. package/dist/core/commands/gen-agent-md.mjs +125 -0
  38. package/dist/core/commands/gen-agent-md.mjs.map +1 -0
  39. package/dist/core/commands/index.cjs +13 -1
  40. package/dist/core/commands/index.d.cts.map +1 -1
  41. package/dist/core/commands/index.d.mts +6 -0
  42. package/dist/core/commands/index.d.mts.map +1 -1
  43. package/dist/core/commands/index.mjs +13 -1
  44. package/dist/core/commands/index.mjs.map +1 -1
  45. package/dist/core/commands/install.cjs +91 -0
  46. package/dist/core/commands/install.d.mts +2 -0
  47. package/dist/core/commands/install.mjs +92 -0
  48. package/dist/core/commands/install.mjs.map +1 -0
  49. package/dist/core/commands/ls.cjs +14 -2
  50. package/dist/core/commands/ls.d.cts +2 -0
  51. package/dist/core/commands/ls.d.cts.map +1 -1
  52. package/dist/core/commands/ls.d.mts +2 -0
  53. package/dist/core/commands/ls.d.mts.map +1 -1
  54. package/dist/core/commands/ls.mjs +14 -2
  55. package/dist/core/commands/ls.mjs.map +1 -1
  56. package/dist/core/commands/mcp-bridge.cjs +201 -0
  57. package/dist/core/commands/mcp-bridge.d.mts +2 -0
  58. package/dist/core/commands/mcp-bridge.mjs +201 -0
  59. package/dist/core/commands/mcp-bridge.mjs.map +1 -0
  60. package/dist/core/commands/read.cjs +20 -7
  61. package/dist/core/commands/read.d.cts +2 -0
  62. package/dist/core/commands/read.d.cts.map +1 -1
  63. package/dist/core/commands/read.d.mts +2 -0
  64. package/dist/core/commands/read.d.mts.map +1 -1
  65. package/dist/core/commands/read.mjs +20 -7
  66. package/dist/core/commands/read.mjs.map +1 -1
  67. package/dist/core/commands/search.cjs +5 -1
  68. package/dist/core/commands/search.mjs +5 -1
  69. package/dist/core/commands/search.mjs.map +1 -1
  70. package/dist/core/commands/stat.mjs.map +1 -1
  71. package/dist/core/commands/types.d.cts +2 -0
  72. package/dist/core/commands/types.d.cts.map +1 -1
  73. package/dist/core/commands/types.d.mts +2 -0
  74. package/dist/core/commands/types.d.mts.map +1 -1
  75. package/dist/core/commands/types.mjs.map +1 -1
  76. package/dist/core/commands/vault.cjs +289 -0
  77. package/dist/core/commands/vault.d.mts +2 -0
  78. package/dist/core/commands/vault.mjs +289 -0
  79. package/dist/core/commands/vault.mjs.map +1 -0
  80. package/dist/core/commands/write.cjs +19 -6
  81. package/dist/core/commands/write.d.cts +2 -1
  82. package/dist/core/commands/write.d.cts.map +1 -1
  83. package/dist/core/commands/write.d.mts +2 -1
  84. package/dist/core/commands/write.d.mts.map +1 -1
  85. package/dist/core/commands/write.mjs +19 -6
  86. package/dist/core/commands/write.mjs.map +1 -1
  87. package/dist/core/executor/index.cjs +95 -19
  88. package/dist/core/executor/index.d.cts +4 -0
  89. package/dist/core/executor/index.d.cts.map +1 -1
  90. package/dist/core/executor/index.d.mts +4 -0
  91. package/dist/core/executor/index.d.mts.map +1 -1
  92. package/dist/core/executor/index.mjs +95 -19
  93. package/dist/core/executor/index.mjs.map +1 -1
  94. package/dist/core/formatters/index.d.mts +1 -0
  95. package/dist/core/formatters/install.cjs +21 -0
  96. package/dist/core/formatters/install.d.mts +1 -0
  97. package/dist/core/formatters/install.mjs +19 -0
  98. package/dist/core/formatters/install.mjs.map +1 -0
  99. package/dist/core/formatters/vault.cjs +36 -0
  100. package/dist/core/formatters/vault.mjs +32 -0
  101. package/dist/core/formatters/vault.mjs.map +1 -0
  102. package/dist/credential/index.d.mts +2 -1
  103. package/dist/credential/mcp-auth-context.cjs +21 -5
  104. package/dist/credential/mcp-auth-context.mjs +21 -5
  105. package/dist/credential/mcp-auth-context.mjs.map +1 -1
  106. package/dist/credential/resolver.cjs +7 -2
  107. package/dist/credential/resolver.mjs +7 -2
  108. package/dist/credential/resolver.mjs.map +1 -1
  109. package/dist/credential/vault-store.d.mts +1 -0
  110. package/dist/daemon/config-manager.cjs +279 -0
  111. package/dist/daemon/config-manager.mjs +279 -0
  112. package/dist/daemon/config-manager.mjs.map +1 -0
  113. package/dist/daemon/manager.cjs +164 -0
  114. package/dist/daemon/manager.mjs +157 -0
  115. package/dist/daemon/manager.mjs.map +1 -0
  116. package/dist/daemon/server.cjs +220 -0
  117. package/dist/daemon/server.mjs +220 -0
  118. package/dist/daemon/server.mjs.map +1 -0
  119. package/dist/mcp/http-transport.cjs +14 -1
  120. package/dist/mcp/http-transport.mjs +14 -1
  121. package/dist/mcp/http-transport.mjs.map +1 -1
  122. package/dist/mcp/server.cjs +4 -2
  123. package/dist/mcp/server.mjs +4 -2
  124. package/dist/mcp/server.mjs.map +1 -1
  125. package/dist/mcp/tools.cjs +62 -12
  126. package/dist/mcp/tools.mjs +62 -12
  127. package/dist/mcp/tools.mjs.map +1 -1
  128. package/dist/program/daemon-integration.cjs +46 -0
  129. package/dist/program/daemon-integration.mjs +45 -0
  130. package/dist/program/daemon-integration.mjs.map +1 -0
  131. package/dist/program/program-manager.cjs +162 -0
  132. package/dist/program/program-manager.mjs +162 -0
  133. package/dist/program/program-manager.mjs.map +1 -0
  134. package/dist/program/trigger-scanner.cjs +148 -0
  135. package/dist/program/trigger-scanner.mjs +148 -0
  136. package/dist/program/trigger-scanner.mjs.map +1 -0
  137. package/dist/providers/vault/dist/_virtual/_@oxc-project_runtime@0.108.0/helpers/decorate.cjs +11 -0
  138. package/dist/providers/vault/dist/_virtual/_@oxc-project_runtime@0.108.0/helpers/decorate.mjs +11 -0
  139. package/dist/providers/vault/dist/_virtual/_@oxc-project_runtime@0.108.0/helpers/decorate.mjs.map +1 -0
  140. package/dist/providers/vault/dist/encrypted-file.cjs +158 -0
  141. package/dist/providers/vault/dist/encrypted-file.mjs +153 -0
  142. package/dist/providers/vault/dist/encrypted-file.mjs.map +1 -0
  143. package/dist/providers/vault/dist/index.cjs +405 -0
  144. package/dist/providers/vault/dist/index.mjs +400 -0
  145. package/dist/providers/vault/dist/index.mjs.map +1 -0
  146. package/dist/providers/vault/dist/key-resolver.cjs +181 -0
  147. package/dist/providers/vault/dist/key-resolver.mjs +180 -0
  148. package/dist/providers/vault/dist/key-resolver.mjs.map +1 -0
  149. package/dist/repl.cjs +105 -14
  150. package/dist/repl.d.cts.map +1 -1
  151. package/dist/repl.d.mts.map +1 -1
  152. package/dist/repl.mjs +105 -14
  153. package/dist/repl.mjs.map +1 -1
  154. package/package.json +29 -22
@@ -1,11 +1,28 @@
1
1
  import { __require } from "../_virtual/rolldown_runtime.mjs";
2
- import { ConfigLoader } from "./loader.mjs";
2
+ import { extractEnvFromURI, mergeStoredCredentials, persistCredentialResult, resolveAndMergeCredentials, resolveCredentialsForMount } from "./credential-helpers.mjs";
3
+ import { AFS_USER_CONFIG_DIR_ENV, CONFIG_DIR_NAME, ConfigLoader } from "./loader.mjs";
3
4
  import { persistMount, unpersistMount, updateMountOptions } from "./mount-commands.mjs";
5
+ import { mkdir } from "node:fs/promises";
6
+ import { homedir } from "node:os";
7
+ import { join } from "node:path";
4
8
  import { AFS, ProviderRegistry } from "@aigne/afs";
5
9
  import { parseURI } from "@aigne/afs/utils/uri";
6
10
 
7
11
  //#region src/config/afs-loader.ts
8
12
  /**
13
+ * AFS Loader - Lazy loading with parallel tolerant mount
14
+ *
15
+ * Provides loadAFS() for on-demand AFS creation with caching.
16
+ * createAFS() uses Promise.allSettled for parallel provider creation + mount,
17
+ * tolerating individual failures while reporting them to stderr.
18
+ *
19
+ * Integrates 4-step credential resolution:
20
+ * 1. Determine missing fields from provider schema
21
+ * 2. Silent resolution (config > env > credential store)
22
+ * 3. Interactive collection (provider auth() or default collect())
23
+ * 4. Unified persistence (sensitive → credentials.toml, non-sensitive → config options)
24
+ */
25
+ /**
9
26
  * Register the workspace:// scheme on a ProviderRegistry.
10
27
  *
11
28
  * Extracted so that both createAFS() and verifyMount() can support
@@ -27,17 +44,26 @@ function registerWorkspaceFactory(registry) {
27
44
  });
28
45
  });
29
46
  }
30
- let cached;
47
+ const cacheMap = /* @__PURE__ */ new Map();
48
+ function cacheKey(cwd) {
49
+ const { resolve: resolve$1 } = __require("node:path");
50
+ return resolve$1(cwd);
51
+ }
31
52
  /**
32
- * Load AFS with caching - first call creates, subsequent calls return cached instance
53
+ * Load AFS with per-cwd caching same cwd returns same instance,
54
+ * different cwd creates a separate instance.
33
55
  */
34
56
  async function loadAFS(cwd, options) {
57
+ const key = cacheKey(cwd);
58
+ const cached = cacheMap.get(key);
35
59
  if (cached) return {
36
60
  afs: cached,
37
- failures: []
61
+ failures: [],
62
+ configMountPaths: [],
63
+ registry: new ProviderRegistry()
38
64
  };
39
65
  const result = await createAFS(cwd, options);
40
- cached = result.afs;
66
+ cacheMap.set(key, result.afs);
41
67
  return result;
42
68
  }
43
69
  /**
@@ -58,8 +84,8 @@ async function createAFS(cwd, options) {
58
84
  const mod = await import("@aigne/afs-workspace");
59
85
  const AFSWorkspace = mod.AFSWorkspace ?? mod.default;
60
86
  if (!AFSWorkspace) throw new Error("workspace:// scheme requires @aigne/afs-workspace package. Install it with: pnpm add @aigne/afs-workspace");
61
- const { resolve } = await import("node:path");
62
- resolve(parsed.body);
87
+ const { resolve: resolve$1 } = await import("node:path");
88
+ resolve$1(parsed.body);
63
89
  return new AFSWorkspace({
64
90
  workspacePath: parsed.body,
65
91
  registry,
@@ -84,6 +110,10 @@ async function createAFS(cwd, options) {
84
110
  ...mount.options
85
111
  });
86
112
  });
113
+ afs.createProviderFromMount = async (mount) => {
114
+ await resolveAndMergeCredentials(mount, authContext, credentialStore, registry);
115
+ return registry.createProvider(mount);
116
+ };
87
117
  afs.loadProvider = async (uri, mountPath, options$1) => {
88
118
  parseURI(uri);
89
119
  const { cleanUri: loadConfigUri, envRecord: loadEnvRecord } = extractEnvFromURI(uri);
@@ -177,14 +207,14 @@ async function createAFS(cwd, options) {
177
207
  };
178
208
  if (config.registry?.enabled !== false) try {
179
209
  const { AFSRegistry } = await import("@aigne/afs-registry");
180
- const officialRegistry = new AFSRegistry(config.registry?.providers?.length ? { providers: config.registry.providers } : { url: "https://raw.githubusercontent.com/ArcBlock/afs-registry/refs/heads/main/providers.json" });
181
- await afs.mount(officialRegistry, "/registry/official");
182
- const internalRegistry = new AFSRegistry();
183
- await afs.mount(internalRegistry, "/registry/internal");
210
+ const registry$1 = new AFSRegistry(config.registry?.providers?.length ? { providers: config.registry.providers } : void 0);
211
+ await afs.mount(registry$1, "/registry");
184
212
  } catch {}
185
213
  if (config.mounts.length === 0) return {
186
214
  afs,
187
- failures: []
215
+ failures: [],
216
+ configMountPaths: [],
217
+ registry
188
218
  };
189
219
  const total = config.mounts.length;
190
220
  let completed = 0;
@@ -232,304 +262,23 @@ async function createAFS(cwd, options) {
232
262
  for (const f of failures) console.warn(` - ${f.path}: ${f.reason}`);
233
263
  }
234
264
  if (succeeded.length === 0 && config.mounts.length > 0) throw new Error("All providers failed to mount");
235
- return {
236
- afs,
237
- failures
238
- };
239
- }
240
- /**
241
- * Extract env query params from MCP URIs for secure credential storage.
242
- *
243
- * MCP servers receive secrets via env vars (e.g., `mcp+stdio://npx?env=API_KEY=sk-xxx`).
244
- * This function extracts env params from the URI so they can be stored in credentials.toml
245
- * instead of being persisted in plaintext in config.toml.
246
- *
247
- * Only applies to MCP schemes (mcp://, mcp+stdio://, mcp+sse://).
248
- * Non-MCP URIs are returned unchanged.
249
- */
250
- function extractEnvFromURI(uri) {
251
- const parsed = parseURI(uri);
252
- const envRecord = {};
253
- if (!parsed.scheme.startsWith("mcp")) return {
254
- cleanUri: uri,
255
- envRecord
256
- };
257
- const envValues = parsed.query.env;
258
- if (!envValues) return {
259
- cleanUri: uri,
260
- envRecord
261
- };
262
- const envList = Array.isArray(envValues) ? envValues : [envValues];
263
- for (const entry of envList) {
264
- const eqIdx = entry.indexOf("=");
265
- if (eqIdx > 0) envRecord[entry.slice(0, eqIdx)] = entry.slice(eqIdx + 1);
266
- }
267
- const queryIndex = uri.indexOf("?");
268
- if (queryIndex < 0) return {
269
- cleanUri: uri,
270
- envRecord
271
- };
272
- const rawQuery = uri.slice(queryIndex + 1);
273
- const params = new URLSearchParams(rawQuery);
274
- params.delete("env");
275
- const newQuery = params.toString();
276
- const base = uri.slice(0, queryIndex);
277
- return {
278
- cleanUri: newQuery ? `${base}?${newQuery}` : base,
279
- envRecord
280
- };
281
- }
282
- /**
283
- * Extract template variables from mount.uri using the manifest's uriTemplate
284
- * and merge them into mount.options so the credential resolver sees them as "known".
285
- *
286
- * Example: cloudflare://d9e5fca3... + template "cloudflare://{accountId}"
287
- * → mount.options.accountId = "d9e5fca3..."
288
- */
289
- function mergeTemplateVarsIntoMount(mount, manifest) {
290
- if (!manifest?.uriTemplate) return;
291
- const { parseTemplate } = __require("@aigne/afs/utils/uri-template");
292
- const parsed = parseURI(mount.uri);
293
- let templateVars;
294
- try {
295
- templateVars = parseTemplate(manifest.uriTemplate, parsed.body);
296
- } catch {
297
- return;
298
- }
299
- for (const [key, value] of Object.entries(templateVars)) if (value !== void 0) {
300
- if (!mount.options) mount.options = {};
301
- if (mount.options[key] === void 0) mount.options[key] = value;
302
- }
303
- }
304
- /**
305
- * After credential resolution, rebuild mount.uri from the template if the
306
- * current URI body is empty/incomplete and resolved options can fill template vars.
307
- *
308
- * Example: mount.uri = "cloudflare://" (empty body), mount.options.accountId = "abc"
309
- * → mount.uri = "cloudflare://abc"
310
- */
311
- function rebuildURIFromTemplate(mount, manifest) {
312
- if (!manifest?.uriTemplate) return;
313
- const { buildURI, getTemplateVariableNames } = __require("@aigne/afs/utils/uri-template");
314
- const varNames = getTemplateVariableNames(manifest.uriTemplate);
315
- if (varNames.length === 0) return;
316
- const parsed = parseURI(mount.uri);
317
- const { parseTemplate } = __require("@aigne/afs/utils/uri-template");
318
- let existingVars;
319
- try {
320
- existingVars = parseTemplate(manifest.uriTemplate, parsed.body);
321
- } catch {
322
- existingVars = {};
323
- }
324
- const allVars = { ...existingVars };
325
- for (const name of varNames) if (!allVars[name] && mount.options?.[name] != null) allVars[name] = String(mount.options[name]);
326
- try {
327
- const newURI = buildURI(manifest.uriTemplate, allVars);
328
- if (newURI !== mount.uri) mount.uri = newURI;
329
- } catch {}
330
- }
331
- /**
332
- * Attempt credential resolution for a mount, merging resolved values into mount.options.
333
- *
334
- * Returns the credential result if any fields were collected interactively,
335
- * or null if no interactive collection was needed (or no schema/authContext).
336
- *
337
- * This function mutates mount.options and mount.auth/mount.token when credentials
338
- * are resolved, so the subsequent registry.createProvider(mount) receives complete values.
339
- */
340
- async function resolveAndMergeCredentials(mount, authContext, credentialStore, registry, opts) {
341
- const info = await registry.getProviderInfo(mount.uri);
342
- const schema = info?.schema ?? null;
343
- const providerAuth = info?.auth;
344
- if (!schema) return null;
345
- mergeTemplateVarsIntoMount(mount, info?.manifest);
346
- const { getSensitiveFields } = await import("@aigne/afs/utils/schema");
347
- const sensitiveFieldsInSchema = getSensitiveFields(schema);
348
- const schemaProps = schema.properties ?? {};
349
- const hasEnvFields = Object.values(schemaProps).some((p) => Array.isArray(p?.env));
350
- if (sensitiveFieldsInSchema.length === 0 && !hasEnvFields && !providerAuth) return null;
351
- const { resolveCredentials } = await import("../credential/resolver.mjs");
352
- const result = await resolveCredentials({
353
- mount,
354
- schema,
355
- authContext,
356
- credentialStore,
357
- providerAuth,
358
- forceCollect: opts?.forceCollect
359
- });
360
- if (!result) {
361
- const fieldNames = Object.keys(schema.properties ?? {});
362
- const known = /* @__PURE__ */ new Set();
363
- if (mount.auth !== void 0) known.add("auth");
364
- if (mount.token !== void 0) known.add("token");
365
- if (mount.options) for (const k of Object.keys(mount.options)) known.add(k);
366
- const missing = fieldNames.filter((f) => !known.has(f));
367
- const fieldList = missing.length > 0 ? missing.join(", ") : fieldNames.join(", ");
368
- throw new Error(`Missing credentials: ${fieldList}. Retry with them as args, e.g. { "uri": "${mount.uri}", "path": "${mount.path}", ${missing.map((f) => `"${f}": "..."`).join(", ")} }`);
369
- }
370
- if (Object.keys(result.values).length > 0) {
371
- if (result.values.token !== void 0 && mount.token === void 0) mount.token = String(result.values.token);
372
- if (result.values.auth !== void 0 && mount.auth === void 0) mount.auth = String(result.values.auth);
373
- const opts$1 = mount.options ?? {};
374
- for (const [key, value] of Object.entries(result.values)) if (key !== "token" && key !== "auth" && opts$1[key] === void 0) opts$1[key] = value;
375
- if (Object.keys(opts$1).length > 0) mount.options = opts$1;
376
- }
377
- rebuildURIFromTemplate(mount, info?.manifest);
378
- return result.collected ? result : null;
379
- }
380
- /**
381
- * Persist credential resolution result (sensitive → credentials.toml, non-sensitive → config).
382
- */
383
- async function persistCredentialResult(mount, result) {
384
- if (Object.keys(result.sensitive).length > 0) try {
385
- const { createCredentialStore } = await import("../credential/store.mjs");
386
- await createCredentialStore().set(mount.uri, result.sensitive);
387
- } catch (err) {
388
- const msg = err instanceof Error ? err.message : String(err);
389
- console.warn(`[mount] credential persistence failed: ${msg}`);
390
- }
391
- }
392
- /**
393
- * Load stored credentials and merge into mount options during startup.
394
- *
395
- * Credentials are keyed by URI (the resource identity), not mount path.
396
- */
397
- async function mergeStoredCredentials(mounts, _mountSources, store) {
398
- for (const mount of mounts) try {
399
- const stored = await store.get(mount.uri);
400
- if (stored) {
401
- const opts = mount.options ?? {};
402
- for (const [key, value] of Object.entries(stored)) if (key.startsWith("env:")) {
403
- if (!opts.env) opts.env = {};
404
- opts.env[key.slice(4)] = value;
405
- } else if (opts[key] === void 0) opts[key] = value;
406
- if (Object.keys(opts).length > 0) mount.options = opts;
407
- }
408
- } catch {}
409
- }
410
- /**
411
- * Resolve and persist credentials for a mount configuration.
412
- *
413
- * Used by `mount add` CLI command to trigger credential collection
414
- * at add-time rather than deferring to AFS creation.
415
- *
416
- * - Gets provider schema via registry
417
- * - Runs 4-step credential resolution
418
- * - Persists sensitive values to credentials.toml
419
- * - Returns non-sensitive values for caller to update config
420
- *
421
- * Returns null if no schema found or no credentials needed.
422
- * Throws if user cancels collection when credentials are required.
423
- */
424
- async function resolveCredentialsForMount(options) {
425
- const { uri, mountPath, authContext, credentialStore, extraOptions, sensitiveArgs } = options;
426
- const { cleanUri: configUri, envRecord } = extractEnvFromURI(uri);
427
- const hasExtractedEnv = Object.keys(envRecord).length > 0;
428
- const info = await (options.registry ?? new ProviderRegistry()).getProviderInfo(uri);
429
- let schema = info?.schema ?? null;
430
- const providerAuth = info?.auth;
431
- if (!schema && extraOptions && Object.keys(extraOptions).length > 0) {
432
- const { buildAdHocSchema } = await import("@aigne/afs/utils/schema");
433
- schema = buildAdHocSchema(extraOptions, sensitiveArgs ?? []);
434
- }
435
- const envOnlyResult = () => {
436
- if (!hasExtractedEnv) return null;
437
- return {
438
- collected: false,
439
- nonSensitive: {},
440
- allValues: {},
441
- persistCredentials: async () => {
442
- const toStore = {};
443
- for (const [key, val] of Object.entries(envRecord)) toStore[`env:${key}`] = val;
444
- if (credentialStore) try {
445
- await credentialStore.set(configUri, toStore);
446
- } catch (err) {
447
- const msg = err instanceof Error ? err.message : String(err);
448
- console.warn(`[mount add] credential persistence failed: ${msg}`);
449
- }
450
- },
451
- sensitiveFields: [],
452
- configUri
453
- };
454
- };
455
- if (!schema) return envOnlyResult();
456
- const properties = schema.properties;
457
- if (!properties || Object.keys(properties).length === 0) return envOnlyResult();
458
- if (sensitiveArgs && sensitiveArgs.length > 0) {
459
- const mergedProps = { ...properties };
460
- let changed = false;
461
- for (const field of sensitiveArgs) if (mergedProps[field]) {
462
- mergedProps[field] = {
463
- ...mergedProps[field],
464
- sensitive: true
465
- };
466
- changed = true;
467
- } else if (extraOptions?.[field] !== void 0) {
468
- const value = extraOptions[field];
469
- mergedProps[field] = {
470
- type: typeof value === "number" ? "number" : typeof value === "boolean" ? "boolean" : "string",
471
- sensitive: true
472
- };
473
- changed = true;
474
- }
475
- if (changed) schema = {
476
- ...schema,
477
- properties: mergedProps
478
- };
479
- }
480
- const { getSensitiveFields } = await import("@aigne/afs/utils/schema");
481
- const sensitiveFieldsInSchema = getSensitiveFields(schema);
482
- const schemaProps = schema.properties;
483
- const hasEnvFields = Object.values(schemaProps).some((p) => Array.isArray(p?.env));
484
- if (sensitiveFieldsInSchema.length === 0 && !hasEnvFields && !extraOptions) return envOnlyResult();
485
- const mount = {
486
- uri,
487
- path: mountPath
488
- };
489
- if (extraOptions && Object.keys(extraOptions).length > 0) mount.options = {
490
- ...mount.options ?? {},
491
- ...extraOptions
492
- };
493
- mergeTemplateVarsIntoMount(mount, info?.manifest);
494
- const { resolveCredentials } = await import("../credential/resolver.mjs");
495
- const result = await resolveCredentials({
496
- mount,
497
- schema,
498
- authContext,
499
- credentialStore,
500
- providerAuth,
501
- forceCollect: options.forceCollect
502
- });
503
- if (!result) {
504
- const fieldNames = Object.keys(properties);
505
- throw new Error(`Missing credentials: ${fieldNames.join(", ")}. Retry with them as args, e.g. { "uri": "${uri}", "path": "${mountPath}", ${fieldNames.map((f) => `"${f}": "..."`).join(", ")} }`);
506
- }
507
- const sensitiveFieldSet = new Set(sensitiveFieldsInSchema);
508
- const flatSensitive = {};
509
- for (const field of sensitiveFieldsInSchema) {
510
- const val = result.values[field];
511
- if (val === void 0) continue;
512
- if (field === "env" && typeof val === "object" && val !== null) for (const [envKey, envVal] of Object.entries(val)) flatSensitive[`env:${envKey}`] = String(envVal);
513
- else flatSensitive[field] = String(val);
265
+ {
266
+ const dataDir = join(process.env[AFS_USER_CONFIG_DIR_ENV] ?? join(homedir(), CONFIG_DIR_NAME), "data");
267
+ try {
268
+ await mkdir(dataDir, { recursive: true });
269
+ const dataProvider = await registry.createProvider({
270
+ uri: `fs://${dataDir}`,
271
+ path: "/.data",
272
+ access_mode: "readwrite"
273
+ });
274
+ await afs.mount(dataProvider, "/.data");
275
+ } catch {}
514
276
  }
515
- const nonSensitive = result.collected ? result.nonSensitive : extraOptions ? Object.fromEntries(Object.entries(extraOptions).filter(([k]) => !sensitiveFieldSet.has(k))) : {};
516
- const persistCredentials = async () => {
517
- const toStore = { ...flatSensitive };
518
- for (const [key, val] of Object.entries(envRecord)) toStore[`env:${key}`] = val;
519
- if (Object.keys(toStore).length > 0 && credentialStore) try {
520
- await credentialStore.set(configUri, toStore);
521
- } catch (err) {
522
- const msg = err instanceof Error ? err.message : String(err);
523
- console.warn(`[mount add] credential persistence failed: ${msg}`);
524
- }
525
- };
526
277
  return {
527
- collected: result.collected,
528
- nonSensitive,
529
- allValues: result.values,
530
- persistCredentials,
531
- sensitiveFields: sensitiveFieldsInSchema,
532
- configUri: hasExtractedEnv ? configUri : void 0
278
+ afs,
279
+ failures,
280
+ configMountPaths: succeeded,
281
+ registry
533
282
  };
534
283
  }
535
284
  /**
@@ -565,8 +314,8 @@ async function verifyMount(uri, mountPath, options) {
565
314
  const msg = err instanceof Error ? err.message : String(err);
566
315
  throw new Error(`Mount verification failed: could not reach provider at ${uri}. Error: ${msg}. Check your URI and credentials.`);
567
316
  } finally {
568
- if (typeof provider.close === "function") try {
569
- await provider.close();
317
+ try {
318
+ await provider.close?.();
570
319
  } catch {}
571
320
  }
572
321
  }
@@ -1 +1 @@
1
- {"version":3,"file":"afs-loader.mjs","names":["options","opts","AFS"],"sources":["../../src/config/afs-loader.ts"],"sourcesContent":["/**\n * AFS Loader - Lazy loading with parallel tolerant mount\n *\n * Provides loadAFS() for on-demand AFS creation with caching.\n * createAFS() uses Promise.allSettled for parallel provider creation + mount,\n * tolerating individual failures while reporting them to stderr.\n *\n * Integrates 4-step credential resolution:\n * 1. Determine missing fields from provider schema\n * 2. Silent resolution (config > env > credential store)\n * 3. Interactive collection (provider auth() or default collect())\n * 4. Unified persistence (sensitive → credentials.toml, non-sensitive → config options)\n */\n\nimport type { AFSModule, AuthContext, MountConfig } from \"@aigne/afs\";\nimport { AFS, ProviderRegistry } from \"@aigne/afs\";\nimport { parseURI } from \"@aigne/afs/utils/uri\";\nimport type { CredentialStore } from \"../credential/store.js\";\nimport { ConfigLoader } from \"./loader.js\";\nimport {\n type ConfigMountEntry,\n type PersistScope,\n persistMount,\n unpersistMount,\n updateMountOptions,\n} from \"./mount-commands.js\";\n\n// ─── Workspace Factory Helper ─────────────────────────────────────────────\n\n/**\n * Register the workspace:// scheme on a ProviderRegistry.\n *\n * Extracted so that both createAFS() and verifyMount() can support\n * workspace URIs. createAFS() overrides this with a richer variant\n * that includes credential resolution.\n */\nfunction registerWorkspaceFactory(registry: import(\"@aigne/afs\").ProviderRegistry): void {\n registry.register(\"workspace\", async (mount, parsed) => {\n const mod = await import(\"@aigne/afs-workspace\" as string);\n const AFSWorkspace = mod.AFSWorkspace ?? mod.default;\n if (!AFSWorkspace) {\n throw new Error(\n \"workspace:// scheme requires @aigne/afs-workspace package. Install it with: pnpm add @aigne/afs-workspace\",\n );\n }\n return new AFSWorkspace({\n workspacePath: parsed.body,\n registry,\n name: mount.path.slice(1).replace(/\\//g, \"-\") || \"workspace\",\n description: mount.description,\n accessMode: mount.access_mode,\n ...mount.options,\n });\n });\n}\n\n// ─── Types ────────────────────────────────────────────────────────────────\n\nexport interface MountProgressEvent {\n total: number;\n completed: number;\n failed: number;\n}\n\nexport interface MountFailure {\n path: string;\n reason: string;\n}\n\nexport interface CreateAFSResult {\n afs: AFS;\n failures: MountFailure[];\n}\n\nexport interface CreateAFSOptions {\n onProgress?: (event: MountProgressEvent) => void;\n /** Auth context for interactive credential collection (CLI or MCP) */\n authContext?: AuthContext;\n /** Credential store for reading/writing credentials */\n credentialStore?: CredentialStore;\n}\n\n// ─── Cache ────────────────────────────────────────────────────────────────\n\nlet cached: AFS | undefined;\n\n/**\n * Reset the cached AFS instance (for testing)\n */\nexport function resetAFSCache(): void {\n cached = undefined;\n}\n\n/**\n * Load AFS with caching - first call creates, subsequent calls return cached instance\n */\nexport async function loadAFS(cwd: string, options?: CreateAFSOptions): Promise<CreateAFSResult> {\n if (cached) return { afs: cached, failures: [] };\n const result = await createAFS(cwd, options);\n cached = result.afs;\n return result;\n}\n\n/**\n * Create AFS instance from config with parallel tolerant mount\n *\n * - All providers are created and mounted in parallel via Promise.allSettled\n * - Individual failures are logged to stderr and skipped (unless onProgress is provided)\n * - If ALL providers fail, throws an error\n * - If no mounts configured, returns empty AFS (no error)\n */\nexport async function createAFS(cwd: string, options?: CreateAFSOptions): Promise<CreateAFSResult> {\n const loader = new ConfigLoader();\n const { config, mountSources } = await loader.loadWithSources(cwd);\n const afs = new AFS();\n const authContext = options?.authContext;\n const credentialStore = options?.credentialStore;\n\n // Create registry — auto-loads built-in providers via manifest-driven resolution.\n // Only workspace needs explicit registration (to break circular deps + inject credential callback).\n const registry = new ProviderRegistry();\n registry.register(\"workspace\", async (mount, parsed) => {\n const mod = await import(\"@aigne/afs-workspace\" as string);\n const AFSWorkspace = mod.AFSWorkspace ?? mod.default;\n if (!AFSWorkspace) {\n throw new Error(\n \"workspace:// scheme requires @aigne/afs-workspace package. Install it with: pnpm add @aigne/afs-workspace\",\n );\n }\n const { resolve } = await import(\"node:path\");\n const _workspacePath = resolve(parsed.body);\n return new AFSWorkspace({\n workspacePath: parsed.body,\n registry,\n createProvider: async (subMount: MountConfig) => {\n const credResult = await resolveAndMergeCredentials(\n subMount,\n authContext,\n credentialStore,\n registry,\n );\n // Create provider with resolved credentials in mount.options\n const provider = await registry.createProvider(subMount);\n // Persist sensitive credentials to credential store\n if (credResult) {\n await persistCredentialResult(subMount, credResult);\n // Strip sensitive values from mount.options (keep only non-sensitive for config)\n const opts = subMount.options ?? {};\n for (const key of Object.keys(credResult.sensitive)) {\n delete opts[key];\n }\n subMount.options = Object.keys(opts).length > 0 ? opts : undefined;\n // Merge non-sensitive resolved values back\n if (Object.keys(credResult.nonSensitive).length > 0) {\n subMount.options = { ...(subMount.options ?? {}), ...credResult.nonSensitive };\n }\n }\n return provider;\n },\n name: mount.path.slice(1).replace(/\\//g, \"-\") || \"workspace\",\n description: mount.description,\n accessMode: mount.access_mode,\n ...mount.options,\n });\n });\n\n // ── Inject loadProvider ──\n // Allows agents to mount new providers at runtime via /.actions/mount\n afs.loadProvider = async (uri: string, mountPath: string, options?: Record<string, unknown>) => {\n // Validate URI format before passing to factory\n parseURI(uri);\n\n // Extract env query params from MCP URIs for secure credential storage\n const { cleanUri: loadConfigUri, envRecord: loadEnvRecord } = extractEnvFromURI(uri);\n const hasLoadEnv = Object.keys(loadEnvRecord).length > 0;\n\n // Extract known mount-level fields; pass remaining as provider-specific options\n const { accessMode, auth, description, scope, ...providerOptions } = options ?? {};\n\n // Inject extracted env values into provider options\n if (hasLoadEnv) {\n const existingEnv = (providerOptions.env as Record<string, string>) ?? {};\n providerOptions.env = { ...existingEnv, ...loadEnvRecord };\n }\n\n const mount: MountConfig = {\n uri,\n path: mountPath,\n access_mode: (accessMode as \"readonly\" | \"readwrite\") ?? undefined,\n auth: (auth as string) ?? undefined,\n description: (description as string) ?? undefined,\n options: Object.keys(providerOptions).length > 0 ? providerOptions : undefined,\n };\n\n const persistScope = (scope as PersistScope) || \"cwd\";\n let credResult = await resolveAndMergeCredentials(\n mount,\n authContext,\n credentialStore,\n registry,\n );\n\n try {\n const provider = await registry.createProvider(mount);\n await afs.mount(provider, mountPath);\n } catch (mountError) {\n // Health check failed with silently resolved credentials →\n // retry once with forced interactive collection so user can fix values\n if (credResult === null && authContext) {\n // credResult === null means all fields resolved silently (no interactive collection)\n // Rebuild mount config from scratch for retry\n const retryProviderOptions = { ...providerOptions };\n const retryMount: MountConfig = {\n uri,\n path: mountPath,\n access_mode: (accessMode as \"readonly\" | \"readwrite\") ?? undefined,\n auth: (auth as string) ?? undefined,\n description: (description as string) ?? undefined,\n options: Object.keys(retryProviderOptions).length > 0 ? retryProviderOptions : undefined,\n };\n credResult = await resolveAndMergeCredentials(\n retryMount,\n authContext,\n credentialStore,\n registry,\n { forceCollect: true },\n );\n const retryProvider = await registry.createProvider(retryMount);\n await afs.mount(retryProvider, mountPath);\n // Update mount reference for persistence below\n Object.assign(mount, retryMount);\n } else {\n throw mountError;\n }\n }\n\n // Persist credentials if any were collected\n if (credResult) {\n await persistCredentialResult(mount, credResult);\n }\n\n // Persist extracted MCP env values to credentials.toml\n if (hasLoadEnv && credentialStore) {\n try {\n const envCreds: Record<string, string> = {};\n for (const [k, v] of Object.entries(loadEnvRecord)) {\n envCreds[`env:${k}`] = v;\n }\n // Merge with any existing sensitive values from credential resolution\n const existing = credResult ? { ...credResult.sensitive } : {};\n await credentialStore.set(loadConfigUri, { ...existing, ...envCreds });\n } catch (err) {\n const msg = err instanceof Error ? err.message : String(err);\n console.warn(`[mount] env credential persistence failed: ${msg}`);\n }\n }\n\n // Always persist to config after successful mount\n try {\n // Use configUri (env stripped) for config.toml — env values are in credentials.toml\n const entry: ConfigMountEntry = {\n path: mountPath,\n uri: hasLoadEnv ? loadConfigUri : mount.uri,\n };\n if (description) entry.description = description as string;\n if (accessMode) entry.access_mode = accessMode as \"readonly\" | \"readwrite\";\n if (auth) entry.auth = auth as string;\n const mergedOptions = { ...providerOptions, ...credResult?.nonSensitive };\n // Strip env from config options — it's in credentials.toml\n if (hasLoadEnv) delete mergedOptions.env;\n if (Object.keys(mergedOptions).length > 0) entry.options = mergedOptions;\n await persistMount(cwd, entry, persistScope);\n } catch (err) {\n const msg = err instanceof Error ? err.message : String(err);\n console.warn(`[mount] config persistence failed: ${msg}`);\n }\n };\n\n // ── Inject unloadProvider ──\n // Removes mount config when provider is unmounted via /.actions/unmount\n afs.unloadProvider = async (mountPath: string, options?: Record<string, unknown>) => {\n try {\n await unpersistMount(cwd, mountPath, (options?.scope as PersistScope) || undefined);\n } catch (err) {\n const msg = err instanceof Error ? err.message : String(err);\n console.warn(`[unmount] config removal failed: ${msg}`);\n }\n };\n\n // ── Inject updateProviderConfig ──\n // Allows providers to persist option changes back to their config file\n afs.updateProviderConfig = async (mountPath: string, optionUpdates: Record<string, unknown>) => {\n try {\n const key = `:${mountPath}`;\n const configDir = mountSources.get(key);\n if (!configDir) return;\n await updateMountOptions(configDir, mountPath, optionUpdates);\n } catch (err) {\n const msg = err instanceof Error ? err.message : String(err);\n console.warn(`[updateProviderConfig] failed: ${msg}`);\n }\n };\n\n // ── Auto-mount Registry Providers (tolerant — silent on failure) ──\n if (config.registry?.enabled !== false) {\n try {\n const { AFSRegistry } = await import(\"@aigne/afs-registry\");\n\n // /registry/official — external providers from remote registry\n const officialRegistry = new AFSRegistry(\n config.registry?.providers?.length\n ? { providers: config.registry.providers as any[] }\n : {\n url: \"https://raw.githubusercontent.com/ArcBlock/afs-registry/refs/heads/main/providers.json\",\n },\n );\n await afs.mount(officialRegistry, \"/registry/official\");\n\n // /registry/internal — built-in providers bundled with CLI\n const internalRegistry = new AFSRegistry();\n await afs.mount(internalRegistry, \"/registry/internal\");\n } catch {\n // Silent degradation — registry is optional\n }\n }\n\n if (config.mounts.length === 0) {\n return { afs, failures: [] };\n }\n\n const total = config.mounts.length;\n let completed = 0;\n let failedCount = 0;\n\n options?.onProgress?.({ total, completed: 0, failed: 0 });\n\n // Load stored credentials keyed by URI\n if (credentialStore && mountSources.size > 0) {\n await mergeStoredCredentials(config.mounts, mountSources, credentialStore);\n }\n\n const results = await Promise.allSettled(\n config.mounts.map(async (mount) => {\n const provider = await registry.createProvider(mount);\n await afs.mount(provider, mount.path, { namespace: mount.namespace ?? null });\n completed++;\n options?.onProgress?.({ total, completed, failed: failedCount });\n return mount.path;\n }),\n );\n\n const failures: MountFailure[] = [];\n const succeeded: string[] = [];\n\n for (let i = 0; i < results.length; i++) {\n const result = results[i]!;\n if (result.status === \"fulfilled\") {\n succeeded.push(result.value);\n } else {\n const reason = result.reason;\n const msg = reason instanceof Error ? reason.message : String(reason);\n failures.push({ path: config.mounts[i]!.path, reason: msg });\n failedCount++;\n completed++;\n options?.onProgress?.({ total, completed, failed: failedCount });\n }\n }\n\n // When onProgress is provided, the caller handles display; otherwise log to stderr\n if (!options?.onProgress && failures.length > 0) {\n console.warn(`[mount] ${succeeded.length} succeeded, ${failures.length} failed:`);\n for (const f of failures) {\n console.warn(` - ${f.path}: ${f.reason}`);\n }\n }\n\n if (succeeded.length === 0 && config.mounts.length > 0) {\n throw new Error(\"All providers failed to mount\");\n }\n\n return { afs, failures };\n}\n\n// ─── Credential Resolution Helpers ─────────────────────────────────────────\n\n/**\n * Extract env query params from MCP URIs for secure credential storage.\n *\n * MCP servers receive secrets via env vars (e.g., `mcp+stdio://npx?env=API_KEY=sk-xxx`).\n * This function extracts env params from the URI so they can be stored in credentials.toml\n * instead of being persisted in plaintext in config.toml.\n *\n * Only applies to MCP schemes (mcp://, mcp+stdio://, mcp+sse://).\n * Non-MCP URIs are returned unchanged.\n */\nexport function extractEnvFromURI(uri: string): {\n cleanUri: string;\n envRecord: Record<string, string>;\n} {\n const parsed = parseURI(uri);\n const envRecord: Record<string, string> = {};\n\n // Only extract env for MCP schemes\n if (!parsed.scheme.startsWith(\"mcp\")) {\n return { cleanUri: uri, envRecord };\n }\n\n const envValues = parsed.query.env;\n if (!envValues) return { cleanUri: uri, envRecord };\n\n // Parse env=KEY=VALUE format (split on first = only)\n const envList = Array.isArray(envValues) ? envValues : [envValues];\n for (const entry of envList) {\n const eqIdx = entry.indexOf(\"=\");\n if (eqIdx > 0) {\n envRecord[entry.slice(0, eqIdx)] = entry.slice(eqIdx + 1);\n }\n }\n\n // Rebuild URI without env params, preserving other query params\n const queryIndex = uri.indexOf(\"?\");\n if (queryIndex < 0) return { cleanUri: uri, envRecord };\n\n const rawQuery = uri.slice(queryIndex + 1);\n const params = new URLSearchParams(rawQuery);\n params.delete(\"env\");\n const newQuery = params.toString();\n const base = uri.slice(0, queryIndex);\n const cleanUri = newQuery ? `${base}?${newQuery}` : base;\n\n return { cleanUri, envRecord };\n}\n\n/**\n * Extract template variables from mount.uri using the manifest's uriTemplate\n * and merge them into mount.options so the credential resolver sees them as \"known\".\n *\n * Example: cloudflare://d9e5fca3... + template \"cloudflare://{accountId}\"\n * → mount.options.accountId = \"d9e5fca3...\"\n */\nfunction mergeTemplateVarsIntoMount(\n mount: MountConfig,\n manifest: import(\"@aigne/afs\").ProviderManifest | null | undefined,\n): void {\n if (!manifest?.uriTemplate) return;\n const { parseTemplate } =\n require(\"@aigne/afs/utils/uri-template\") as typeof import(\"@aigne/afs/utils/uri-template\");\n const parsed = parseURI(mount.uri);\n // Gracefully handle empty/partial URI bodies — template vars may not be\n // extractable yet (e.g. when the mount action was called without --uri and\n // without providing all required template parameters as args).\n let templateVars: Record<string, string | undefined>;\n try {\n templateVars = parseTemplate(manifest.uriTemplate, parsed.body);\n } catch {\n return; // Body doesn't match template yet — nothing to merge\n }\n for (const [key, value] of Object.entries(templateVars)) {\n if (value !== undefined) {\n if (!mount.options) mount.options = {};\n if (mount.options[key] === undefined) {\n mount.options[key] = value;\n }\n }\n }\n}\n\n/**\n * After credential resolution, rebuild mount.uri from the template if the\n * current URI body is empty/incomplete and resolved options can fill template vars.\n *\n * Example: mount.uri = \"cloudflare://\" (empty body), mount.options.accountId = \"abc\"\n * → mount.uri = \"cloudflare://abc\"\n */\nfunction rebuildURIFromTemplate(\n mount: MountConfig,\n manifest: import(\"@aigne/afs\").ProviderManifest | null | undefined,\n): void {\n if (!manifest?.uriTemplate) return;\n const { buildURI, getTemplateVariableNames } =\n require(\"@aigne/afs/utils/uri-template\") as typeof import(\"@aigne/afs/utils/uri-template\");\n const varNames = getTemplateVariableNames(manifest.uriTemplate);\n if (varNames.length === 0) return;\n\n // Check if any template vars are missing from the current URI body\n const parsed = parseURI(mount.uri);\n const { parseTemplate } =\n require(\"@aigne/afs/utils/uri-template\") as typeof import(\"@aigne/afs/utils/uri-template\");\n let existingVars: Record<string, string | undefined>;\n try {\n existingVars = parseTemplate(manifest.uriTemplate, parsed.body);\n } catch {\n existingVars = {}; // Body incomplete\n }\n\n // Merge: existing URI vars + mount.options (options fill in gaps)\n const allVars: Record<string, string | undefined> = { ...existingVars };\n for (const name of varNames) {\n if (!allVars[name] && mount.options?.[name] != null) {\n allVars[name] = String(mount.options[name]);\n }\n }\n\n // Rebuild if we can fill more vars than the current URI has\n try {\n const newURI = buildURI(manifest.uriTemplate, allVars);\n if (newURI !== mount.uri) {\n mount.uri = newURI;\n }\n } catch {\n // Still can't build a complete URI — that's OK, createProvider will handle it\n }\n}\n\n/**\n * Attempt credential resolution for a mount, merging resolved values into mount.options.\n *\n * Returns the credential result if any fields were collected interactively,\n * or null if no interactive collection was needed (or no schema/authContext).\n *\n * This function mutates mount.options and mount.auth/mount.token when credentials\n * are resolved, so the subsequent registry.createProvider(mount) receives complete values.\n */\nasync function resolveAndMergeCredentials(\n mount: MountConfig,\n authContext: AuthContext | undefined,\n credentialStore: CredentialStore | undefined,\n registry: ProviderRegistry,\n opts?: { forceCollect?: boolean },\n): Promise<import(\"../credential/resolver.js\").ResolveCredentialsResult | null> {\n // Get provider schema and auth method via registry\n const info = await registry.getProviderInfo(mount.uri);\n const schema = info?.schema ?? null;\n const providerAuth = info?.auth;\n\n if (!schema) return null;\n\n // Extract template variables from URI and merge into mount.options\n // so they appear as \"known\" values in the resolver (skipped in form)\n mergeTemplateVarsIntoMount(mount, info?.manifest);\n\n // Skip interactive credential resolution when schema has no sensitive or env-annotated\n // fields. Non-sensitive fields (like command for MCP, localPath for FS) are URI/config\n // parameters, not credentials — they should not trigger a browser form.\n const { getSensitiveFields } = await import(\"@aigne/afs/utils/schema\");\n const sensitiveFieldsInSchema = getSensitiveFields(schema);\n const schemaProps = (schema as any).properties ?? {};\n const hasEnvFields = Object.values(schemaProps).some((p: any) => Array.isArray(p?.env));\n if (sensitiveFieldsInSchema.length === 0 && !hasEnvFields && !providerAuth) return null;\n\n const { resolveCredentials } = await import(\"../credential/resolver.js\");\n\n const result = await resolveCredentials({\n mount,\n schema,\n authContext,\n credentialStore,\n providerAuth,\n forceCollect: opts?.forceCollect,\n });\n\n if (!result) {\n // User declined/cancelled credential collection — abort mount\n const fieldNames = Object.keys((schema as any).properties ?? {});\n const known = new Set<string>();\n if (mount.auth !== undefined) known.add(\"auth\");\n if (mount.token !== undefined) known.add(\"token\");\n if (mount.options) {\n for (const k of Object.keys(mount.options)) known.add(k);\n }\n const missing = fieldNames.filter((f) => !known.has(f));\n const fieldList = missing.length > 0 ? missing.join(\", \") : fieldNames.join(\", \");\n throw new Error(\n `Missing credentials: ${fieldList}. ` +\n `Retry with them as args, e.g. { \"uri\": \"${mount.uri}\", \"path\": \"${mount.path}\", ${missing.map((f) => `\"${f}\": \"...\"`).join(\", \")} }`,\n );\n }\n\n // Merge resolved values into mount config for provider creation\n if (Object.keys(result.values).length > 0) {\n // Map sensitive credential fields to mount.auth/token if applicable\n if (result.values.token !== undefined && mount.token === undefined) {\n mount.token = String(result.values.token);\n }\n if (result.values.auth !== undefined && mount.auth === undefined) {\n mount.auth = String(result.values.auth);\n }\n\n // Merge remaining resolved values into mount.options\n const opts = mount.options ?? {};\n for (const [key, value] of Object.entries(result.values)) {\n if (key !== \"token\" && key !== \"auth\" && opts[key] === undefined) {\n opts[key] = value;\n }\n }\n if (Object.keys(opts).length > 0) {\n mount.options = opts;\n }\n }\n\n // Rebuild URI if credential resolution filled in template variables\n // (e.g. accountId collected via form → cloudflare:// becomes cloudflare://abc)\n rebuildURIFromTemplate(mount, info?.manifest);\n\n return result.collected ? result : null;\n}\n\n/**\n * Persist credential resolution result (sensitive → credentials.toml, non-sensitive → config).\n */\nasync function persistCredentialResult(\n mount: MountConfig,\n result: import(\"../credential/resolver.js\").ResolveCredentialsResult,\n): Promise<void> {\n if (Object.keys(result.sensitive).length > 0) {\n try {\n const { createCredentialStore } = await import(\"../credential/store.js\");\n const store = createCredentialStore();\n await store.set(mount.uri, result.sensitive);\n } catch (err) {\n const msg = err instanceof Error ? err.message : String(err);\n console.warn(`[mount] credential persistence failed: ${msg}`);\n }\n }\n}\n\n/**\n * Load stored credentials and merge into mount options during startup.\n *\n * Credentials are keyed by URI (the resource identity), not mount path.\n */\nasync function mergeStoredCredentials(\n mounts: MountConfig[],\n _mountSources: Map<string, string>,\n store: CredentialStore,\n): Promise<void> {\n for (const mount of mounts) {\n try {\n const stored = await store.get(mount.uri);\n if (stored) {\n const opts = mount.options ?? {};\n for (const [key, value] of Object.entries(stored)) {\n if (key.startsWith(\"env:\")) {\n // Reconstruct env Record from flattened env:KEY credential entries\n if (!opts.env) opts.env = {};\n (opts.env as Record<string, string>)[key.slice(4)] = value;\n } else if (opts[key] === undefined) {\n opts[key] = value;\n }\n }\n if (Object.keys(opts).length > 0) {\n mount.options = opts;\n }\n }\n } catch {\n // Credential lookup failure is non-fatal\n }\n }\n}\n\n// ─── Exported Credential Resolution for CLI mount add ───────────────────────\n\nexport interface ResolveCredentialsForMountOptions {\n cwd: string;\n uri: string;\n mountPath: string;\n authContext?: AuthContext;\n credentialStore?: CredentialStore;\n /** Extra key-value options provided by the user (e.g., --host, --password) */\n extraOptions?: Record<string, unknown>;\n /** Field names that should be treated as sensitive (for ad-hoc schema) */\n sensitiveArgs?: string[];\n /** Registry instance for resolving provider schema/auth. If not provided, one is created. */\n registry?: ProviderRegistry;\n /** Force interactive collection even when all fields are silently resolved.\n * Used for retry after health-check failure with stale env/store values. */\n forceCollect?: boolean;\n}\n\nexport interface ResolveCredentialsForMountResult {\n /** Whether interactive collection was performed */\n collected: boolean;\n /** Non-sensitive values to persist in config.toml options */\n nonSensitive: Record<string, unknown>;\n /** All resolved values (for mount verification before persisting) */\n allValues: Record<string, unknown>;\n /** Persist sensitive credentials to credentials.toml.\n * Call AFTER health check succeeds — never before. */\n persistCredentials: () => Promise<void>;\n /** Sensitive field names — caller should strip these from config options */\n sensitiveFields: string[];\n /** URI with env params stripped (for MCP URIs). Use for config.toml persistence.\n * Undefined when no env extraction was performed. */\n configUri?: string;\n}\n\n/**\n * Resolve and persist credentials for a mount configuration.\n *\n * Used by `mount add` CLI command to trigger credential collection\n * at add-time rather than deferring to AFS creation.\n *\n * - Gets provider schema via registry\n * - Runs 4-step credential resolution\n * - Persists sensitive values to credentials.toml\n * - Returns non-sensitive values for caller to update config\n *\n * Returns null if no schema found or no credentials needed.\n * Throws if user cancels collection when credentials are required.\n */\nexport async function resolveCredentialsForMount(\n options: ResolveCredentialsForMountOptions,\n): Promise<ResolveCredentialsForMountResult | null> {\n const { uri, mountPath, authContext, credentialStore, extraOptions, sensitiveArgs } = options;\n\n // Extract env query params from MCP URIs for secure credential storage\n const { cleanUri: configUri, envRecord } = extractEnvFromURI(uri);\n const hasExtractedEnv = Object.keys(envRecord).length > 0;\n\n // Get provider schema and auth via registry\n const registry = options.registry ?? new ProviderRegistry();\n const info = await registry.getProviderInfo(uri);\n let schema = info?.schema ?? null;\n const providerAuth = info?.auth;\n\n // Fallback: build ad-hoc schema from extraOptions + sensitiveArgs\n if (!schema && extraOptions && Object.keys(extraOptions).length > 0) {\n const { buildAdHocSchema } = await import(\"@aigne/afs/utils/schema\");\n schema = buildAdHocSchema(extraOptions, sensitiveArgs ?? []);\n }\n // Helper: return env-only result when no schema-based credential resolution is needed\n // but env values were extracted from MCP URI and need credential storage\n const envOnlyResult = (): ResolveCredentialsForMountResult | null => {\n if (!hasExtractedEnv) return null;\n return {\n collected: false,\n nonSensitive: {},\n allValues: {},\n persistCredentials: async () => {\n const toStore: Record<string, string> = {};\n for (const [key, val] of Object.entries(envRecord)) {\n toStore[`env:${key}`] = val;\n }\n if (credentialStore) {\n try {\n await credentialStore.set(configUri, toStore);\n } catch (err) {\n const msg = err instanceof Error ? err.message : String(err);\n console.warn(`[mount add] credential persistence failed: ${msg}`);\n }\n }\n },\n sensitiveFields: [],\n configUri,\n };\n };\n\n if (!schema) return envOnlyResult();\n\n const properties = (schema as any).properties;\n if (!properties || Object.keys(properties).length === 0) return envOnlyResult();\n\n // Augment schema with sensitiveArgs — mark existing fields as sensitive\n // and add new fields not in provider schema (from extraOptions)\n if (sensitiveArgs && sensitiveArgs.length > 0) {\n const mergedProps = { ...properties };\n let changed = false;\n for (const field of sensitiveArgs) {\n if (mergedProps[field]) {\n // Existing field → mark as sensitive\n mergedProps[field] = { ...mergedProps[field], sensitive: true };\n changed = true;\n } else if (extraOptions?.[field] !== undefined) {\n // New field not in provider schema → add with inferred type\n const value = extraOptions[field];\n mergedProps[field] = {\n type:\n typeof value === \"number\"\n ? \"number\"\n : typeof value === \"boolean\"\n ? \"boolean\"\n : \"string\",\n sensitive: true,\n };\n changed = true;\n }\n }\n if (changed) {\n schema = { ...schema, properties: mergedProps } as typeof schema;\n }\n }\n\n // Skip credential resolution if no fields need credential handling.\n // A field needs credential handling if it has `sensitive: true` (needs secure storage)\n // or `env` metadata (can be resolved from environment variables).\n // Plain required fields (like localPath for fs://) are URI parameters, not credentials.\n const { getSensitiveFields } = await import(\"@aigne/afs/utils/schema\");\n const sensitiveFieldsInSchema = getSensitiveFields(schema);\n const schemaProps = (schema as any).properties;\n const hasEnvFields = Object.values(schemaProps).some((p: any) => Array.isArray(p?.env));\n if (sensitiveFieldsInSchema.length === 0 && !hasEnvFields && !extraOptions)\n return envOnlyResult();\n\n // Build mount config for resolver\n const mount: MountConfig = { uri, path: mountPath };\n\n // Merge extraOptions into mount.options so resolver sees them as known values\n if (extraOptions && Object.keys(extraOptions).length > 0) {\n mount.options = { ...(mount.options ?? {}), ...extraOptions };\n }\n\n // Extract template variables from URI and merge into mount.options\n mergeTemplateVarsIntoMount(mount, info?.manifest);\n\n // Resolve credentials — schema comes from manifest (user-facing fields only)\n const { resolveCredentials } = await import(\"../credential/resolver.js\");\n\n const result = await resolveCredentials({\n mount,\n schema,\n authContext,\n credentialStore,\n providerAuth,\n forceCollect: options.forceCollect,\n });\n\n if (!result) {\n // User declined — include field names for actionable error\n const fieldNames = Object.keys(properties);\n throw new Error(\n `Missing credentials: ${fieldNames.join(\", \")}. ` +\n `Retry with them as args, e.g. { \"uri\": \"${uri}\", \"path\": \"${mountPath}\", ${fieldNames.map((f) => `\"${f}\": \"...\"`).join(\", \")} }`,\n );\n }\n\n // Compute sensitive fields from (possibly augmented) schema\n const sensitiveFieldSet = new Set(sensitiveFieldsInSchema);\n\n // Build flattened sensitive values for credential store.\n // Handles env object → env:KEY flattening for flat credential storage.\n const flatSensitive: Record<string, string> = {};\n for (const field of sensitiveFieldsInSchema) {\n const val = result.values[field];\n if (val === undefined) continue;\n if (field === \"env\" && typeof val === \"object\" && val !== null) {\n for (const [envKey, envVal] of Object.entries(val as Record<string, string>)) {\n flatSensitive[`env:${envKey}`] = String(envVal);\n }\n } else {\n flatSensitive[field] = String(val);\n }\n }\n\n // Non-sensitive values for config.toml options\n const nonSensitive: Record<string, unknown> = result.collected\n ? result.nonSensitive\n : extraOptions\n ? Object.fromEntries(Object.entries(extraOptions).filter(([k]) => !sensitiveFieldSet.has(k)))\n : {};\n\n // Build deferred persistence callback — caller invokes AFTER health check succeeds\n // Merges schema-sensitive values with extracted env values from MCP URIs\n const persistCredentials = async () => {\n const toStore = { ...flatSensitive };\n // Merge env values extracted from MCP URI (env:KEY format)\n for (const [key, val] of Object.entries(envRecord)) {\n toStore[`env:${key}`] = val;\n }\n if (Object.keys(toStore).length > 0 && credentialStore) {\n try {\n // Use configUri (env stripped) as credential key so reload can match\n await credentialStore.set(configUri, toStore);\n } catch (err) {\n const msg = err instanceof Error ? err.message : String(err);\n console.warn(`[mount add] credential persistence failed: ${msg}`);\n }\n }\n };\n\n return {\n collected: result.collected,\n nonSensitive,\n allValues: result.values,\n persistCredentials,\n sensitiveFields: sensitiveFieldsInSchema,\n configUri: hasExtractedEnv ? configUri : undefined,\n };\n}\n\n// ─── Mount Verification ───────────────────────────────────────────────────────\n\n/**\n * Verify that a mount configuration produces a working provider.\n *\n * Creates the provider and mounts it on a temporary AFS instance,\n * which triggers the built-in checkProviderOnMount (stat + data validation + list).\n * Throws if the mount check fails.\n *\n * @param uri - Provider URI\n * @param mountPath - Mount path\n * @param options - Merged options (non-sensitive + sensitive) for provider creation\n */\nexport async function verifyMount(\n uri: string,\n mountPath: string,\n options?: Record<string, unknown>,\n): Promise<void> {\n const mount: MountConfig = {\n uri,\n path: mountPath,\n options: options && Object.keys(options).length > 0 ? options : undefined,\n };\n\n const registry = new ProviderRegistry();\n registerWorkspaceFactory(registry);\n let provider: AFSModule;\n try {\n provider = await registry.createProvider(mount);\n } catch (err) {\n const msg = err instanceof Error ? err.message : String(err);\n throw new Error(`Mount verification failed (provider creation): ${msg}`);\n }\n\n // Mount on a temporary AFS to trigger the real checkProviderOnMount\n try {\n const { AFS } = await import(\"@aigne/afs\");\n const tempAFS = new AFS();\n await tempAFS.mount(provider, mountPath);\n } catch (err) {\n const msg = err instanceof Error ? err.message : String(err);\n throw new Error(\n `Mount verification failed: could not reach provider at ${uri}. ` +\n `Error: ${msg}. Check your URI and credentials.`,\n );\n } finally {\n // Clean up provider resources (e.g., MCP process)\n if (typeof (provider as any).close === \"function\") {\n try {\n await (provider as any).close();\n } catch {\n // ignore cleanup errors\n }\n }\n }\n}\n"],"mappings":";;;;;;;;;;;;;;AAoCA,SAAS,yBAAyB,UAAuD;AACvF,UAAS,SAAS,aAAa,OAAO,OAAO,WAAW;EACtD,MAAM,MAAM,MAAM,OAAO;EACzB,MAAM,eAAe,IAAI,gBAAgB,IAAI;AAC7C,MAAI,CAAC,aACH,OAAM,IAAI,MACR,4GACD;AAEH,SAAO,IAAI,aAAa;GACtB,eAAe,OAAO;GACtB;GACA,MAAM,MAAM,KAAK,MAAM,EAAE,CAAC,QAAQ,OAAO,IAAI,IAAI;GACjD,aAAa,MAAM;GACnB,YAAY,MAAM;GAClB,GAAG,MAAM;GACV,CAAC;GACF;;AA+BJ,IAAI;;;;AAYJ,eAAsB,QAAQ,KAAa,SAAsD;AAC/F,KAAI,OAAQ,QAAO;EAAE,KAAK;EAAQ,UAAU,EAAE;EAAE;CAChD,MAAM,SAAS,MAAM,UAAU,KAAK,QAAQ;AAC5C,UAAS,OAAO;AAChB,QAAO;;;;;;;;;;AAWT,eAAsB,UAAU,KAAa,SAAsD;CAEjG,MAAM,EAAE,QAAQ,iBAAiB,MADlB,IAAI,cAAc,CACa,gBAAgB,IAAI;CAClE,MAAM,MAAM,IAAI,KAAK;CACrB,MAAM,cAAc,SAAS;CAC7B,MAAM,kBAAkB,SAAS;CAIjC,MAAM,WAAW,IAAI,kBAAkB;AACvC,UAAS,SAAS,aAAa,OAAO,OAAO,WAAW;EACtD,MAAM,MAAM,MAAM,OAAO;EACzB,MAAM,eAAe,IAAI,gBAAgB,IAAI;AAC7C,MAAI,CAAC,aACH,OAAM,IAAI,MACR,4GACD;EAEH,MAAM,EAAE,YAAY,MAAM,OAAO;AACV,UAAQ,OAAO,KAAK;AAC3C,SAAO,IAAI,aAAa;GACtB,eAAe,OAAO;GACtB;GACA,gBAAgB,OAAO,aAA0B;IAC/C,MAAM,aAAa,MAAM,2BACvB,UACA,aACA,iBACA,SACD;IAED,MAAM,WAAW,MAAM,SAAS,eAAe,SAAS;AAExD,QAAI,YAAY;AACd,WAAM,wBAAwB,UAAU,WAAW;KAEnD,MAAM,OAAO,SAAS,WAAW,EAAE;AACnC,UAAK,MAAM,OAAO,OAAO,KAAK,WAAW,UAAU,CACjD,QAAO,KAAK;AAEd,cAAS,UAAU,OAAO,KAAK,KAAK,CAAC,SAAS,IAAI,OAAO;AAEzD,SAAI,OAAO,KAAK,WAAW,aAAa,CAAC,SAAS,EAChD,UAAS,UAAU;MAAE,GAAI,SAAS,WAAW,EAAE;MAAG,GAAG,WAAW;MAAc;;AAGlF,WAAO;;GAET,MAAM,MAAM,KAAK,MAAM,EAAE,CAAC,QAAQ,OAAO,IAAI,IAAI;GACjD,aAAa,MAAM;GACnB,YAAY,MAAM;GAClB,GAAG,MAAM;GACV,CAAC;GACF;AAIF,KAAI,eAAe,OAAO,KAAa,WAAmB,cAAsC;AAE9F,WAAS,IAAI;EAGb,MAAM,EAAE,UAAU,eAAe,WAAW,kBAAkB,kBAAkB,IAAI;EACpF,MAAM,aAAa,OAAO,KAAK,cAAc,CAAC,SAAS;EAGvD,MAAM,EAAE,YAAY,MAAM,aAAa,OAAO,GAAG,oBAAoBA,aAAW,EAAE;AAGlF,MAAI,WAEF,iBAAgB,MAAM;GAAE,GADH,gBAAgB,OAAkC,EAAE;GACjC,GAAG;GAAe;EAG5D,MAAM,QAAqB;GACzB;GACA,MAAM;GACN,aAAc,cAA2C;GACzD,MAAO,QAAmB;GAC1B,aAAc,eAA0B;GACxC,SAAS,OAAO,KAAK,gBAAgB,CAAC,SAAS,IAAI,kBAAkB;GACtE;EAED,MAAM,eAAgB,SAA0B;EAChD,IAAI,aAAa,MAAM,2BACrB,OACA,aACA,iBACA,SACD;AAED,MAAI;GACF,MAAM,WAAW,MAAM,SAAS,eAAe,MAAM;AACrD,SAAM,IAAI,MAAM,UAAU,UAAU;WAC7B,YAAY;AAGnB,OAAI,eAAe,QAAQ,aAAa;IAGtC,MAAM,uBAAuB,EAAE,GAAG,iBAAiB;IACnD,MAAM,aAA0B;KAC9B;KACA,MAAM;KACN,aAAc,cAA2C;KACzD,MAAO,QAAmB;KAC1B,aAAc,eAA0B;KACxC,SAAS,OAAO,KAAK,qBAAqB,CAAC,SAAS,IAAI,uBAAuB;KAChF;AACD,iBAAa,MAAM,2BACjB,YACA,aACA,iBACA,UACA,EAAE,cAAc,MAAM,CACvB;IACD,MAAM,gBAAgB,MAAM,SAAS,eAAe,WAAW;AAC/D,UAAM,IAAI,MAAM,eAAe,UAAU;AAEzC,WAAO,OAAO,OAAO,WAAW;SAEhC,OAAM;;AAKV,MAAI,WACF,OAAM,wBAAwB,OAAO,WAAW;AAIlD,MAAI,cAAc,gBAChB,KAAI;GACF,MAAM,WAAmC,EAAE;AAC3C,QAAK,MAAM,CAAC,GAAG,MAAM,OAAO,QAAQ,cAAc,CAChD,UAAS,OAAO,OAAO;GAGzB,MAAM,WAAW,aAAa,EAAE,GAAG,WAAW,WAAW,GAAG,EAAE;AAC9D,SAAM,gBAAgB,IAAI,eAAe;IAAE,GAAG;IAAU,GAAG;IAAU,CAAC;WAC/D,KAAK;GACZ,MAAM,MAAM,eAAe,QAAQ,IAAI,UAAU,OAAO,IAAI;AAC5D,WAAQ,KAAK,8CAA8C,MAAM;;AAKrE,MAAI;GAEF,MAAM,QAA0B;IAC9B,MAAM;IACN,KAAK,aAAa,gBAAgB,MAAM;IACzC;AACD,OAAI,YAAa,OAAM,cAAc;AACrC,OAAI,WAAY,OAAM,cAAc;AACpC,OAAI,KAAM,OAAM,OAAO;GACvB,MAAM,gBAAgB;IAAE,GAAG;IAAiB,GAAG,YAAY;IAAc;AAEzE,OAAI,WAAY,QAAO,cAAc;AACrC,OAAI,OAAO,KAAK,cAAc,CAAC,SAAS,EAAG,OAAM,UAAU;AAC3D,SAAM,aAAa,KAAK,OAAO,aAAa;WACrC,KAAK;GACZ,MAAM,MAAM,eAAe,QAAQ,IAAI,UAAU,OAAO,IAAI;AAC5D,WAAQ,KAAK,sCAAsC,MAAM;;;AAM7D,KAAI,iBAAiB,OAAO,WAAmB,cAAsC;AACnF,MAAI;AACF,SAAM,eAAe,KAAK,WAAYA,WAAS,SAA0B,OAAU;WAC5E,KAAK;GACZ,MAAM,MAAM,eAAe,QAAQ,IAAI,UAAU,OAAO,IAAI;AAC5D,WAAQ,KAAK,oCAAoC,MAAM;;;AAM3D,KAAI,uBAAuB,OAAO,WAAmB,kBAA2C;AAC9F,MAAI;GACF,MAAM,MAAM,IAAI;GAChB,MAAM,YAAY,aAAa,IAAI,IAAI;AACvC,OAAI,CAAC,UAAW;AAChB,SAAM,mBAAmB,WAAW,WAAW,cAAc;WACtD,KAAK;GACZ,MAAM,MAAM,eAAe,QAAQ,IAAI,UAAU,OAAO,IAAI;AAC5D,WAAQ,KAAK,kCAAkC,MAAM;;;AAKzD,KAAI,OAAO,UAAU,YAAY,MAC/B,KAAI;EACF,MAAM,EAAE,gBAAgB,MAAM,OAAO;EAGrC,MAAM,mBAAmB,IAAI,YAC3B,OAAO,UAAU,WAAW,SACxB,EAAE,WAAW,OAAO,SAAS,WAAoB,GACjD,EACE,KAAK,0FACN,CACN;AACD,QAAM,IAAI,MAAM,kBAAkB,qBAAqB;EAGvD,MAAM,mBAAmB,IAAI,aAAa;AAC1C,QAAM,IAAI,MAAM,kBAAkB,qBAAqB;SACjD;AAKV,KAAI,OAAO,OAAO,WAAW,EAC3B,QAAO;EAAE;EAAK,UAAU,EAAE;EAAE;CAG9B,MAAM,QAAQ,OAAO,OAAO;CAC5B,IAAI,YAAY;CAChB,IAAI,cAAc;AAElB,UAAS,aAAa;EAAE;EAAO,WAAW;EAAG,QAAQ;EAAG,CAAC;AAGzD,KAAI,mBAAmB,aAAa,OAAO,EACzC,OAAM,uBAAuB,OAAO,QAAQ,cAAc,gBAAgB;CAG5E,MAAM,UAAU,MAAM,QAAQ,WAC5B,OAAO,OAAO,IAAI,OAAO,UAAU;EACjC,MAAM,WAAW,MAAM,SAAS,eAAe,MAAM;AACrD,QAAM,IAAI,MAAM,UAAU,MAAM,MAAM,EAAE,WAAW,MAAM,aAAa,MAAM,CAAC;AAC7E;AACA,WAAS,aAAa;GAAE;GAAO;GAAW,QAAQ;GAAa,CAAC;AAChE,SAAO,MAAM;GACb,CACH;CAED,MAAM,WAA2B,EAAE;CACnC,MAAM,YAAsB,EAAE;AAE9B,MAAK,IAAI,IAAI,GAAG,IAAI,QAAQ,QAAQ,KAAK;EACvC,MAAM,SAAS,QAAQ;AACvB,MAAI,OAAO,WAAW,YACpB,WAAU,KAAK,OAAO,MAAM;OACvB;GACL,MAAM,SAAS,OAAO;GACtB,MAAM,MAAM,kBAAkB,QAAQ,OAAO,UAAU,OAAO,OAAO;AACrE,YAAS,KAAK;IAAE,MAAM,OAAO,OAAO,GAAI;IAAM,QAAQ;IAAK,CAAC;AAC5D;AACA;AACA,YAAS,aAAa;IAAE;IAAO;IAAW,QAAQ;IAAa,CAAC;;;AAKpE,KAAI,CAAC,SAAS,cAAc,SAAS,SAAS,GAAG;AAC/C,UAAQ,KAAK,WAAW,UAAU,OAAO,cAAc,SAAS,OAAO,UAAU;AACjF,OAAK,MAAM,KAAK,SACd,SAAQ,KAAK,OAAO,EAAE,KAAK,IAAI,EAAE,SAAS;;AAI9C,KAAI,UAAU,WAAW,KAAK,OAAO,OAAO,SAAS,EACnD,OAAM,IAAI,MAAM,gCAAgC;AAGlD,QAAO;EAAE;EAAK;EAAU;;;;;;;;;;;;AAe1B,SAAgB,kBAAkB,KAGhC;CACA,MAAM,SAAS,SAAS,IAAI;CAC5B,MAAM,YAAoC,EAAE;AAG5C,KAAI,CAAC,OAAO,OAAO,WAAW,MAAM,CAClC,QAAO;EAAE,UAAU;EAAK;EAAW;CAGrC,MAAM,YAAY,OAAO,MAAM;AAC/B,KAAI,CAAC,UAAW,QAAO;EAAE,UAAU;EAAK;EAAW;CAGnD,MAAM,UAAU,MAAM,QAAQ,UAAU,GAAG,YAAY,CAAC,UAAU;AAClE,MAAK,MAAM,SAAS,SAAS;EAC3B,MAAM,QAAQ,MAAM,QAAQ,IAAI;AAChC,MAAI,QAAQ,EACV,WAAU,MAAM,MAAM,GAAG,MAAM,IAAI,MAAM,MAAM,QAAQ,EAAE;;CAK7D,MAAM,aAAa,IAAI,QAAQ,IAAI;AACnC,KAAI,aAAa,EAAG,QAAO;EAAE,UAAU;EAAK;EAAW;CAEvD,MAAM,WAAW,IAAI,MAAM,aAAa,EAAE;CAC1C,MAAM,SAAS,IAAI,gBAAgB,SAAS;AAC5C,QAAO,OAAO,MAAM;CACpB,MAAM,WAAW,OAAO,UAAU;CAClC,MAAM,OAAO,IAAI,MAAM,GAAG,WAAW;AAGrC,QAAO;EAAE,UAFQ,WAAW,GAAG,KAAK,GAAG,aAAa;EAEjC;EAAW;;;;;;;;;AAUhC,SAAS,2BACP,OACA,UACM;AACN,KAAI,CAAC,UAAU,YAAa;CAC5B,MAAM,EAAE,4BACE,gCAAgC;CAC1C,MAAM,SAAS,SAAS,MAAM,IAAI;CAIlC,IAAI;AACJ,KAAI;AACF,iBAAe,cAAc,SAAS,aAAa,OAAO,KAAK;SACzD;AACN;;AAEF,MAAK,MAAM,CAAC,KAAK,UAAU,OAAO,QAAQ,aAAa,CACrD,KAAI,UAAU,QAAW;AACvB,MAAI,CAAC,MAAM,QAAS,OAAM,UAAU,EAAE;AACtC,MAAI,MAAM,QAAQ,SAAS,OACzB,OAAM,QAAQ,OAAO;;;;;;;;;;AAa7B,SAAS,uBACP,OACA,UACM;AACN,KAAI,CAAC,UAAU,YAAa;CAC5B,MAAM,EAAE,UAAU,uCACR,gCAAgC;CAC1C,MAAM,WAAW,yBAAyB,SAAS,YAAY;AAC/D,KAAI,SAAS,WAAW,EAAG;CAG3B,MAAM,SAAS,SAAS,MAAM,IAAI;CAClC,MAAM,EAAE,4BACE,gCAAgC;CAC1C,IAAI;AACJ,KAAI;AACF,iBAAe,cAAc,SAAS,aAAa,OAAO,KAAK;SACzD;AACN,iBAAe,EAAE;;CAInB,MAAM,UAA8C,EAAE,GAAG,cAAc;AACvE,MAAK,MAAM,QAAQ,SACjB,KAAI,CAAC,QAAQ,SAAS,MAAM,UAAU,SAAS,KAC7C,SAAQ,QAAQ,OAAO,MAAM,QAAQ,MAAM;AAK/C,KAAI;EACF,MAAM,SAAS,SAAS,SAAS,aAAa,QAAQ;AACtD,MAAI,WAAW,MAAM,IACnB,OAAM,MAAM;SAER;;;;;;;;;;;AAcV,eAAe,2BACb,OACA,aACA,iBACA,UACA,MAC8E;CAE9E,MAAM,OAAO,MAAM,SAAS,gBAAgB,MAAM,IAAI;CACtD,MAAM,SAAS,MAAM,UAAU;CAC/B,MAAM,eAAe,MAAM;AAE3B,KAAI,CAAC,OAAQ,QAAO;AAIpB,4BAA2B,OAAO,MAAM,SAAS;CAKjD,MAAM,EAAE,uBAAuB,MAAM,OAAO;CAC5C,MAAM,0BAA0B,mBAAmB,OAAO;CAC1D,MAAM,cAAe,OAAe,cAAc,EAAE;CACpD,MAAM,eAAe,OAAO,OAAO,YAAY,CAAC,MAAM,MAAW,MAAM,QAAQ,GAAG,IAAI,CAAC;AACvF,KAAI,wBAAwB,WAAW,KAAK,CAAC,gBAAgB,CAAC,aAAc,QAAO;CAEnF,MAAM,EAAE,uBAAuB,MAAM,OAAO;CAE5C,MAAM,SAAS,MAAM,mBAAmB;EACtC;EACA;EACA;EACA;EACA;EACA,cAAc,MAAM;EACrB,CAAC;AAEF,KAAI,CAAC,QAAQ;EAEX,MAAM,aAAa,OAAO,KAAM,OAAe,cAAc,EAAE,CAAC;EAChE,MAAM,wBAAQ,IAAI,KAAa;AAC/B,MAAI,MAAM,SAAS,OAAW,OAAM,IAAI,OAAO;AAC/C,MAAI,MAAM,UAAU,OAAW,OAAM,IAAI,QAAQ;AACjD,MAAI,MAAM,QACR,MAAK,MAAM,KAAK,OAAO,KAAK,MAAM,QAAQ,CAAE,OAAM,IAAI,EAAE;EAE1D,MAAM,UAAU,WAAW,QAAQ,MAAM,CAAC,MAAM,IAAI,EAAE,CAAC;EACvD,MAAM,YAAY,QAAQ,SAAS,IAAI,QAAQ,KAAK,KAAK,GAAG,WAAW,KAAK,KAAK;AACjF,QAAM,IAAI,MACR,wBAAwB,UAAU,4CACW,MAAM,IAAI,cAAc,MAAM,KAAK,KAAK,QAAQ,KAAK,MAAM,IAAI,EAAE,UAAU,CAAC,KAAK,KAAK,CAAC,IACrI;;AAIH,KAAI,OAAO,KAAK,OAAO,OAAO,CAAC,SAAS,GAAG;AAEzC,MAAI,OAAO,OAAO,UAAU,UAAa,MAAM,UAAU,OACvD,OAAM,QAAQ,OAAO,OAAO,OAAO,MAAM;AAE3C,MAAI,OAAO,OAAO,SAAS,UAAa,MAAM,SAAS,OACrD,OAAM,OAAO,OAAO,OAAO,OAAO,KAAK;EAIzC,MAAMC,SAAO,MAAM,WAAW,EAAE;AAChC,OAAK,MAAM,CAAC,KAAK,UAAU,OAAO,QAAQ,OAAO,OAAO,CACtD,KAAI,QAAQ,WAAW,QAAQ,UAAUA,OAAK,SAAS,OACrD,QAAK,OAAO;AAGhB,MAAI,OAAO,KAAKA,OAAK,CAAC,SAAS,EAC7B,OAAM,UAAUA;;AAMpB,wBAAuB,OAAO,MAAM,SAAS;AAE7C,QAAO,OAAO,YAAY,SAAS;;;;;AAMrC,eAAe,wBACb,OACA,QACe;AACf,KAAI,OAAO,KAAK,OAAO,UAAU,CAAC,SAAS,EACzC,KAAI;EACF,MAAM,EAAE,0BAA0B,MAAM,OAAO;AAE/C,QADc,uBAAuB,CACzB,IAAI,MAAM,KAAK,OAAO,UAAU;UACrC,KAAK;EACZ,MAAM,MAAM,eAAe,QAAQ,IAAI,UAAU,OAAO,IAAI;AAC5D,UAAQ,KAAK,0CAA0C,MAAM;;;;;;;;AAUnE,eAAe,uBACb,QACA,eACA,OACe;AACf,MAAK,MAAM,SAAS,OAClB,KAAI;EACF,MAAM,SAAS,MAAM,MAAM,IAAI,MAAM,IAAI;AACzC,MAAI,QAAQ;GACV,MAAM,OAAO,MAAM,WAAW,EAAE;AAChC,QAAK,MAAM,CAAC,KAAK,UAAU,OAAO,QAAQ,OAAO,CAC/C,KAAI,IAAI,WAAW,OAAO,EAAE;AAE1B,QAAI,CAAC,KAAK,IAAK,MAAK,MAAM,EAAE;AAC5B,IAAC,KAAK,IAA+B,IAAI,MAAM,EAAE,IAAI;cAC5C,KAAK,SAAS,OACvB,MAAK,OAAO;AAGhB,OAAI,OAAO,KAAK,KAAK,CAAC,SAAS,EAC7B,OAAM,UAAU;;SAGd;;;;;;;;;;;;;;;;AAwDZ,eAAsB,2BACpB,SACkD;CAClD,MAAM,EAAE,KAAK,WAAW,aAAa,iBAAiB,cAAc,kBAAkB;CAGtF,MAAM,EAAE,UAAU,WAAW,cAAc,kBAAkB,IAAI;CACjE,MAAM,kBAAkB,OAAO,KAAK,UAAU,CAAC,SAAS;CAIxD,MAAM,OAAO,OADI,QAAQ,YAAY,IAAI,kBAAkB,EAC/B,gBAAgB,IAAI;CAChD,IAAI,SAAS,MAAM,UAAU;CAC7B,MAAM,eAAe,MAAM;AAG3B,KAAI,CAAC,UAAU,gBAAgB,OAAO,KAAK,aAAa,CAAC,SAAS,GAAG;EACnE,MAAM,EAAE,qBAAqB,MAAM,OAAO;AAC1C,WAAS,iBAAiB,cAAc,iBAAiB,EAAE,CAAC;;CAI9D,MAAM,sBAA+D;AACnE,MAAI,CAAC,gBAAiB,QAAO;AAC7B,SAAO;GACL,WAAW;GACX,cAAc,EAAE;GAChB,WAAW,EAAE;GACb,oBAAoB,YAAY;IAC9B,MAAM,UAAkC,EAAE;AAC1C,SAAK,MAAM,CAAC,KAAK,QAAQ,OAAO,QAAQ,UAAU,CAChD,SAAQ,OAAO,SAAS;AAE1B,QAAI,gBACF,KAAI;AACF,WAAM,gBAAgB,IAAI,WAAW,QAAQ;aACtC,KAAK;KACZ,MAAM,MAAM,eAAe,QAAQ,IAAI,UAAU,OAAO,IAAI;AAC5D,aAAQ,KAAK,8CAA8C,MAAM;;;GAIvE,iBAAiB,EAAE;GACnB;GACD;;AAGH,KAAI,CAAC,OAAQ,QAAO,eAAe;CAEnC,MAAM,aAAc,OAAe;AACnC,KAAI,CAAC,cAAc,OAAO,KAAK,WAAW,CAAC,WAAW,EAAG,QAAO,eAAe;AAI/E,KAAI,iBAAiB,cAAc,SAAS,GAAG;EAC7C,MAAM,cAAc,EAAE,GAAG,YAAY;EACrC,IAAI,UAAU;AACd,OAAK,MAAM,SAAS,cAClB,KAAI,YAAY,QAAQ;AAEtB,eAAY,SAAS;IAAE,GAAG,YAAY;IAAQ,WAAW;IAAM;AAC/D,aAAU;aACD,eAAe,WAAW,QAAW;GAE9C,MAAM,QAAQ,aAAa;AAC3B,eAAY,SAAS;IACnB,MACE,OAAO,UAAU,WACb,WACA,OAAO,UAAU,YACf,YACA;IACR,WAAW;IACZ;AACD,aAAU;;AAGd,MAAI,QACF,UAAS;GAAE,GAAG;GAAQ,YAAY;GAAa;;CAQnD,MAAM,EAAE,uBAAuB,MAAM,OAAO;CAC5C,MAAM,0BAA0B,mBAAmB,OAAO;CAC1D,MAAM,cAAe,OAAe;CACpC,MAAM,eAAe,OAAO,OAAO,YAAY,CAAC,MAAM,MAAW,MAAM,QAAQ,GAAG,IAAI,CAAC;AACvF,KAAI,wBAAwB,WAAW,KAAK,CAAC,gBAAgB,CAAC,aAC5D,QAAO,eAAe;CAGxB,MAAM,QAAqB;EAAE;EAAK,MAAM;EAAW;AAGnD,KAAI,gBAAgB,OAAO,KAAK,aAAa,CAAC,SAAS,EACrD,OAAM,UAAU;EAAE,GAAI,MAAM,WAAW,EAAE;EAAG,GAAG;EAAc;AAI/D,4BAA2B,OAAO,MAAM,SAAS;CAGjD,MAAM,EAAE,uBAAuB,MAAM,OAAO;CAE5C,MAAM,SAAS,MAAM,mBAAmB;EACtC;EACA;EACA;EACA;EACA;EACA,cAAc,QAAQ;EACvB,CAAC;AAEF,KAAI,CAAC,QAAQ;EAEX,MAAM,aAAa,OAAO,KAAK,WAAW;AAC1C,QAAM,IAAI,MACR,wBAAwB,WAAW,KAAK,KAAK,CAAC,4CACD,IAAI,cAAc,UAAU,KAAK,WAAW,KAAK,MAAM,IAAI,EAAE,UAAU,CAAC,KAAK,KAAK,CAAC,IACjI;;CAIH,MAAM,oBAAoB,IAAI,IAAI,wBAAwB;CAI1D,MAAM,gBAAwC,EAAE;AAChD,MAAK,MAAM,SAAS,yBAAyB;EAC3C,MAAM,MAAM,OAAO,OAAO;AAC1B,MAAI,QAAQ,OAAW;AACvB,MAAI,UAAU,SAAS,OAAO,QAAQ,YAAY,QAAQ,KACxD,MAAK,MAAM,CAAC,QAAQ,WAAW,OAAO,QAAQ,IAA8B,CAC1E,eAAc,OAAO,YAAY,OAAO,OAAO;MAGjD,eAAc,SAAS,OAAO,IAAI;;CAKtC,MAAM,eAAwC,OAAO,YACjD,OAAO,eACP,eACE,OAAO,YAAY,OAAO,QAAQ,aAAa,CAAC,QAAQ,CAAC,OAAO,CAAC,kBAAkB,IAAI,EAAE,CAAC,CAAC,GAC3F,EAAE;CAIR,MAAM,qBAAqB,YAAY;EACrC,MAAM,UAAU,EAAE,GAAG,eAAe;AAEpC,OAAK,MAAM,CAAC,KAAK,QAAQ,OAAO,QAAQ,UAAU,CAChD,SAAQ,OAAO,SAAS;AAE1B,MAAI,OAAO,KAAK,QAAQ,CAAC,SAAS,KAAK,gBACrC,KAAI;AAEF,SAAM,gBAAgB,IAAI,WAAW,QAAQ;WACtC,KAAK;GACZ,MAAM,MAAM,eAAe,QAAQ,IAAI,UAAU,OAAO,IAAI;AAC5D,WAAQ,KAAK,8CAA8C,MAAM;;;AAKvE,QAAO;EACL,WAAW,OAAO;EAClB;EACA,WAAW,OAAO;EAClB;EACA,iBAAiB;EACjB,WAAW,kBAAkB,YAAY;EAC1C;;;;;;;;;;;;;AAgBH,eAAsB,YACpB,KACA,WACA,SACe;CACf,MAAM,QAAqB;EACzB;EACA,MAAM;EACN,SAAS,WAAW,OAAO,KAAK,QAAQ,CAAC,SAAS,IAAI,UAAU;EACjE;CAED,MAAM,WAAW,IAAI,kBAAkB;AACvC,0BAAyB,SAAS;CAClC,IAAI;AACJ,KAAI;AACF,aAAW,MAAM,SAAS,eAAe,MAAM;UACxC,KAAK;EACZ,MAAM,MAAM,eAAe,QAAQ,IAAI,UAAU,OAAO,IAAI;AAC5D,QAAM,IAAI,MAAM,kDAAkD,MAAM;;AAI1E,KAAI;EACF,MAAM,EAAE,eAAQ,MAAM,OAAO;AAE7B,QADgB,IAAIC,OAAK,CACX,MAAM,UAAU,UAAU;UACjC,KAAK;EACZ,MAAM,MAAM,eAAe,QAAQ,IAAI,UAAU,OAAO,IAAI;AAC5D,QAAM,IAAI,MACR,0DAA0D,IAAI,WAClD,IAAI,mCACjB;WACO;AAER,MAAI,OAAQ,SAAiB,UAAU,WACrC,KAAI;AACF,SAAO,SAAiB,OAAO;UACzB"}
1
+ {"version":3,"file":"afs-loader.mjs","names":["resolve","options","registry","AFS"],"sources":["../../src/config/afs-loader.ts"],"sourcesContent":["/**\n * AFS Loader - Lazy loading with parallel tolerant mount\n *\n * Provides loadAFS() for on-demand AFS creation with caching.\n * createAFS() uses Promise.allSettled for parallel provider creation + mount,\n * tolerating individual failures while reporting them to stderr.\n *\n * Integrates 4-step credential resolution:\n * 1. Determine missing fields from provider schema\n * 2. Silent resolution (config > env > credential store)\n * 3. Interactive collection (provider auth() or default collect())\n * 4. Unified persistence (sensitive → credentials.toml, non-sensitive → config options)\n */\n\nimport { mkdir } from \"node:fs/promises\";\nimport { homedir } from \"node:os\";\nimport { join } from \"node:path\";\nimport type { AFSModule, AuthContext, MountConfig } from \"@aigne/afs\";\nimport { AFS, ProviderRegistry } from \"@aigne/afs\";\nimport { parseURI } from \"@aigne/afs/utils/uri\";\nimport type { CredentialStore } from \"../credential/store.js\";\nimport {\n extractEnvFromURI,\n mergeStoredCredentials,\n persistCredentialResult,\n type ResolveCredentialsForMountOptions,\n type ResolveCredentialsForMountResult,\n resolveAndMergeCredentials,\n resolveCredentialsForMount,\n} from \"./credential-helpers.js\";\nimport { AFS_USER_CONFIG_DIR_ENV, CONFIG_DIR_NAME, ConfigLoader } from \"./loader.js\";\nimport {\n type ConfigMountEntry,\n type PersistScope,\n persistMount,\n unpersistMount,\n updateMountOptions,\n} from \"./mount-commands.js\";\n\n// Re-export credential helpers for backward compatibility\nexport {\n extractEnvFromURI,\n resolveCredentialsForMount,\n type ResolveCredentialsForMountOptions,\n type ResolveCredentialsForMountResult,\n};\n\n// ─── Workspace Factory Helper ─────────────────────────────────────────────\n\n/**\n * Register the workspace:// scheme on a ProviderRegistry.\n *\n * Extracted so that both createAFS() and verifyMount() can support\n * workspace URIs. createAFS() overrides this with a richer variant\n * that includes credential resolution.\n */\nfunction registerWorkspaceFactory(registry: import(\"@aigne/afs\").ProviderRegistry): void {\n registry.register(\"workspace\", async (mount, parsed) => {\n const mod = await import(\"@aigne/afs-workspace\" as string);\n const AFSWorkspace = mod.AFSWorkspace ?? mod.default;\n if (!AFSWorkspace) {\n throw new Error(\n \"workspace:// scheme requires @aigne/afs-workspace package. Install it with: pnpm add @aigne/afs-workspace\",\n );\n }\n return new AFSWorkspace({\n workspacePath: parsed.body,\n registry,\n name: mount.path.slice(1).replace(/\\//g, \"-\") || \"workspace\",\n description: mount.description,\n accessMode: mount.access_mode,\n ...mount.options,\n });\n });\n}\n\n// ─── Types ────────────────────────────────────────────────────────────────\n\nexport interface MountProgressEvent {\n total: number;\n completed: number;\n failed: number;\n}\n\nexport interface MountFailure {\n path: string;\n reason: string;\n}\n\nexport interface CreateAFSResult {\n afs: AFS;\n failures: MountFailure[];\n /** Mount paths that were successfully loaded from config.toml (excludes code-managed mounts). */\n configMountPaths: string[];\n /** Provider registry with all registered factories (workspace, etc.). */\n registry: import(\"@aigne/afs\").ProviderRegistry;\n}\n\nexport interface CreateAFSOptions {\n onProgress?: (event: MountProgressEvent) => void;\n /** Auth context for interactive credential collection (CLI or MCP) */\n authContext?: AuthContext;\n /** Credential store for reading/writing credentials */\n credentialStore?: CredentialStore;\n}\n\n// ─── Cache ────────────────────────────────────────────────────────────────\n\nconst cacheMap = new Map<string, AFS>();\n\nfunction cacheKey(cwd: string): string {\n const { resolve } = require(\"node:path\") as typeof import(\"node:path\");\n return resolve(cwd);\n}\n\n/**\n * Reset all cached AFS instances (for testing)\n */\nexport function resetAFSCache(): void {\n cacheMap.clear();\n}\n\n/**\n * Load AFS with per-cwd caching — same cwd returns same instance,\n * different cwd creates a separate instance.\n */\nexport async function loadAFS(cwd: string, options?: CreateAFSOptions): Promise<CreateAFSResult> {\n const key = cacheKey(cwd);\n const cached = cacheMap.get(key);\n if (cached)\n return { afs: cached, failures: [], configMountPaths: [], registry: new ProviderRegistry() };\n const result = await createAFS(cwd, options);\n cacheMap.set(key, result.afs);\n return result;\n}\n\n/**\n * Create AFS instance from config with parallel tolerant mount\n *\n * - All providers are created and mounted in parallel via Promise.allSettled\n * - Individual failures are logged to stderr and skipped (unless onProgress is provided)\n * - If ALL providers fail, throws an error\n * - If no mounts configured, returns empty AFS (no error)\n */\nexport async function createAFS(cwd: string, options?: CreateAFSOptions): Promise<CreateAFSResult> {\n const loader = new ConfigLoader();\n const { config, mountSources } = await loader.loadWithSources(cwd);\n const afs = new AFS();\n const authContext = options?.authContext;\n const credentialStore = options?.credentialStore;\n\n // Create registry — auto-loads built-in providers via manifest-driven resolution.\n // Only workspace needs explicit registration (to break circular deps + inject credential callback).\n const registry = new ProviderRegistry();\n registry.register(\"workspace\", async (mount, parsed) => {\n const mod = await import(\"@aigne/afs-workspace\" as string);\n const AFSWorkspace = mod.AFSWorkspace ?? mod.default;\n if (!AFSWorkspace) {\n throw new Error(\n \"workspace:// scheme requires @aigne/afs-workspace package. Install it with: pnpm add @aigne/afs-workspace\",\n );\n }\n const { resolve } = await import(\"node:path\");\n const _workspacePath = resolve(parsed.body);\n return new AFSWorkspace({\n workspacePath: parsed.body,\n registry,\n createProvider: async (subMount: MountConfig) => {\n const credResult = await resolveAndMergeCredentials(\n subMount,\n authContext,\n credentialStore,\n registry,\n );\n // Create provider with resolved credentials in mount.options\n const provider = await registry.createProvider(subMount);\n // Persist sensitive credentials to credential store\n if (credResult) {\n await persistCredentialResult(subMount, credResult);\n // Strip sensitive values from mount.options (keep only non-sensitive for config)\n const opts = subMount.options ?? {};\n for (const key of Object.keys(credResult.sensitive)) {\n delete opts[key];\n }\n subMount.options = Object.keys(opts).length > 0 ? opts : undefined;\n // Merge non-sensitive resolved values back\n if (Object.keys(credResult.nonSensitive).length > 0) {\n subMount.options = { ...(subMount.options ?? {}), ...credResult.nonSensitive };\n }\n }\n return provider;\n },\n name: mount.path.slice(1).replace(/\\//g, \"-\") || \"workspace\",\n description: mount.description,\n accessMode: mount.access_mode,\n ...mount.options,\n });\n });\n\n // ── Inject provider factory for program mount fallback ──\n // Handles credential resolution + registry creation for program mounts\n // when shared mount URI isn't found in host AFS.\n afs.createProviderFromMount = async (mount) => {\n await resolveAndMergeCredentials(mount, authContext, credentialStore, registry);\n return registry.createProvider(mount);\n };\n\n // ── Inject loadProvider ──\n // Allows agents to mount new providers at runtime via /.actions/mount\n afs.loadProvider = async (uri: string, mountPath: string, options?: Record<string, unknown>) => {\n // Validate URI format before passing to factory\n parseURI(uri);\n\n // Extract env query params from MCP URIs for secure credential storage\n const { cleanUri: loadConfigUri, envRecord: loadEnvRecord } = extractEnvFromURI(uri);\n const hasLoadEnv = Object.keys(loadEnvRecord).length > 0;\n\n // Extract known mount-level fields; pass remaining as provider-specific options\n const { accessMode, auth, description, scope, ...providerOptions } = options ?? {};\n\n // Inject extracted env values into provider options\n if (hasLoadEnv) {\n const existingEnv = (providerOptions.env as Record<string, string>) ?? {};\n providerOptions.env = { ...existingEnv, ...loadEnvRecord };\n }\n\n const mount: MountConfig = {\n uri,\n path: mountPath,\n access_mode: (accessMode as \"readonly\" | \"readwrite\") ?? undefined,\n auth: (auth as string) ?? undefined,\n description: (description as string) ?? undefined,\n options: Object.keys(providerOptions).length > 0 ? providerOptions : undefined,\n };\n\n const persistScope = (scope as PersistScope) || \"cwd\";\n let credResult = await resolveAndMergeCredentials(\n mount,\n authContext,\n credentialStore,\n registry,\n );\n\n try {\n const provider = await registry.createProvider(mount);\n await afs.mount(provider, mountPath);\n } catch (mountError) {\n // Health check failed with silently resolved credentials →\n // retry once with forced interactive collection so user can fix values\n if (credResult === null && authContext) {\n // credResult === null means all fields resolved silently (no interactive collection)\n // Rebuild mount config from scratch for retry\n const retryProviderOptions = { ...providerOptions };\n const retryMount: MountConfig = {\n uri,\n path: mountPath,\n access_mode: (accessMode as \"readonly\" | \"readwrite\") ?? undefined,\n auth: (auth as string) ?? undefined,\n description: (description as string) ?? undefined,\n options: Object.keys(retryProviderOptions).length > 0 ? retryProviderOptions : undefined,\n };\n credResult = await resolveAndMergeCredentials(\n retryMount,\n authContext,\n credentialStore,\n registry,\n { forceCollect: true },\n );\n const retryProvider = await registry.createProvider(retryMount);\n await afs.mount(retryProvider, mountPath);\n // Update mount reference for persistence below\n Object.assign(mount, retryMount);\n } else {\n throw mountError;\n }\n }\n\n // Persist credentials if any were collected\n if (credResult) {\n await persistCredentialResult(mount, credResult);\n }\n\n // Persist extracted MCP env values to credentials.toml\n if (hasLoadEnv && credentialStore) {\n try {\n const envCreds: Record<string, string> = {};\n for (const [k, v] of Object.entries(loadEnvRecord)) {\n envCreds[`env:${k}`] = v;\n }\n // Merge with any existing sensitive values from credential resolution\n const existing = credResult ? { ...credResult.sensitive } : {};\n await credentialStore.set(loadConfigUri, { ...existing, ...envCreds });\n } catch (err) {\n const msg = err instanceof Error ? err.message : String(err);\n console.warn(`[mount] env credential persistence failed: ${msg}`);\n }\n }\n\n // Always persist to config after successful mount\n try {\n // Use configUri (env stripped) for config.toml — env values are in credentials.toml\n const entry: ConfigMountEntry = {\n path: mountPath,\n uri: hasLoadEnv ? loadConfigUri : mount.uri,\n };\n if (description) entry.description = description as string;\n if (accessMode) entry.access_mode = accessMode as \"readonly\" | \"readwrite\";\n if (auth) entry.auth = auth as string;\n const mergedOptions = { ...providerOptions, ...credResult?.nonSensitive };\n // Strip env from config options — it's in credentials.toml\n if (hasLoadEnv) delete mergedOptions.env;\n if (Object.keys(mergedOptions).length > 0) entry.options = mergedOptions;\n await persistMount(cwd, entry, persistScope);\n } catch (err) {\n const msg = err instanceof Error ? err.message : String(err);\n console.warn(`[mount] config persistence failed: ${msg}`);\n }\n };\n\n // ── Inject unloadProvider ──\n // Removes mount config when provider is unmounted via /.actions/unmount\n afs.unloadProvider = async (mountPath: string, options?: Record<string, unknown>) => {\n try {\n await unpersistMount(cwd, mountPath, (options?.scope as PersistScope) || undefined);\n } catch (err) {\n const msg = err instanceof Error ? err.message : String(err);\n console.warn(`[unmount] config removal failed: ${msg}`);\n }\n };\n\n // ── Inject updateProviderConfig ──\n // Allows providers to persist option changes back to their config file\n afs.updateProviderConfig = async (mountPath: string, optionUpdates: Record<string, unknown>) => {\n try {\n const key = `:${mountPath}`;\n const configDir = mountSources.get(key);\n if (!configDir) return;\n await updateMountOptions(configDir, mountPath, optionUpdates);\n } catch (err) {\n const msg = err instanceof Error ? err.message : String(err);\n console.warn(`[updateProviderConfig] failed: ${msg}`);\n }\n };\n\n // ── Auto-mount Registry (tolerant — silent on failure) ──\n // Scans locally installed @aigne/afs-* packages at runtime.\n if (config.registry?.enabled !== false) {\n try {\n const { AFSRegistry } = await import(\"@aigne/afs-registry\");\n const registry = new AFSRegistry(\n config.registry?.providers?.length\n ? { providers: config.registry.providers as any[] }\n : undefined,\n );\n await afs.mount(registry, \"/registry\");\n } catch {\n // Silent degradation — registry is optional\n }\n }\n\n if (config.mounts.length === 0) {\n return { afs, failures: [], configMountPaths: [], registry };\n }\n\n const total = config.mounts.length;\n let completed = 0;\n let failedCount = 0;\n\n options?.onProgress?.({ total, completed: 0, failed: 0 });\n\n // Load stored credentials keyed by URI\n if (credentialStore && mountSources.size > 0) {\n await mergeStoredCredentials(config.mounts, mountSources, credentialStore);\n }\n\n const results = await Promise.allSettled(\n config.mounts.map(async (mount) => {\n const provider = await registry.createProvider(mount);\n await afs.mount(provider, mount.path, { namespace: mount.namespace ?? null });\n completed++;\n options?.onProgress?.({ total, completed, failed: failedCount });\n return mount.path;\n }),\n );\n\n const failures: MountFailure[] = [];\n const succeeded: string[] = [];\n\n for (let i = 0; i < results.length; i++) {\n const result = results[i]!;\n if (result.status === \"fulfilled\") {\n succeeded.push(result.value);\n } else {\n const reason = result.reason;\n const msg = reason instanceof Error ? reason.message : String(reason);\n failures.push({ path: config.mounts[i]!.path, reason: msg });\n failedCount++;\n completed++;\n options?.onProgress?.({ total, completed, failed: failedCount });\n }\n }\n\n // When onProgress is provided, the caller handles display; otherwise log to stderr\n if (!options?.onProgress && failures.length > 0) {\n console.warn(`[mount] ${succeeded.length} succeeded, ${failures.length} failed:`);\n for (const f of failures) {\n console.warn(` - ${f.path}: ${f.reason}`);\n }\n }\n\n if (succeeded.length === 0 && config.mounts.length > 0) {\n throw new Error(\"All providers failed to mount\");\n }\n\n // Auto-mount program data directory from user's home ~/.afs-config/data/\n // Always use user-level config dir so program data persists across projects\n {\n const userConfigDir = process.env[AFS_USER_CONFIG_DIR_ENV] ?? join(homedir(), CONFIG_DIR_NAME);\n const dataDir = join(userConfigDir, \"data\");\n try {\n await mkdir(dataDir, { recursive: true });\n const dataProvider = await registry.createProvider({\n uri: `fs://${dataDir}`,\n path: \"/.data\",\n access_mode: \"readwrite\",\n });\n await afs.mount(dataProvider, \"/.data\");\n } catch {\n // Non-critical: program data mount is optional\n }\n }\n\n return { afs, failures, configMountPaths: succeeded, registry };\n}\n\n// ─── Mount Verification ───────────────────────────────────────────────────────\n\n/**\n * Verify that a mount configuration produces a working provider.\n *\n * Creates the provider and mounts it on a temporary AFS instance,\n * which triggers the built-in checkProviderOnMount (stat + data validation + list).\n * Throws if the mount check fails.\n *\n * @param uri - Provider URI\n * @param mountPath - Mount path\n * @param options - Merged options (non-sensitive + sensitive) for provider creation\n */\nexport async function verifyMount(\n uri: string,\n mountPath: string,\n options?: Record<string, unknown>,\n): Promise<void> {\n const mount: MountConfig = {\n uri,\n path: mountPath,\n options: options && Object.keys(options).length > 0 ? options : undefined,\n };\n\n const registry = new ProviderRegistry();\n registerWorkspaceFactory(registry);\n let provider: AFSModule;\n try {\n provider = await registry.createProvider(mount);\n } catch (err) {\n const msg = err instanceof Error ? err.message : String(err);\n throw new Error(`Mount verification failed (provider creation): ${msg}`);\n }\n\n // Mount on a temporary AFS to trigger the real checkProviderOnMount\n try {\n const { AFS } = await import(\"@aigne/afs\");\n const tempAFS = new AFS();\n await tempAFS.mount(provider, mountPath);\n } catch (err) {\n const msg = err instanceof Error ? err.message : String(err);\n throw new Error(\n `Mount verification failed: could not reach provider at ${uri}. ` +\n `Error: ${msg}. Check your URI and credentials.`,\n );\n } finally {\n // Clean up provider resources (e.g., MCP process)\n try {\n await provider.close?.();\n } catch {\n // ignore cleanup errors\n }\n }\n}\n"],"mappings":"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"}