@aigne/afs-cli 1.11.0-beta.7 → 1.11.0-beta.8

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 (90) hide show
  1. package/dist/_virtual/rolldown_runtime.mjs +7 -0
  2. package/dist/config/afs-loader.cjs +466 -22
  3. package/dist/config/afs-loader.d.cts +6 -1
  4. package/dist/config/afs-loader.d.cts.map +1 -1
  5. package/dist/config/afs-loader.d.mts +6 -1
  6. package/dist/config/afs-loader.d.mts.map +1 -1
  7. package/dist/config/afs-loader.mjs +465 -22
  8. package/dist/config/afs-loader.mjs.map +1 -1
  9. package/dist/config/loader.cjs +28 -11
  10. package/dist/config/loader.mjs +28 -11
  11. package/dist/config/loader.mjs.map +1 -1
  12. package/dist/config/mount-commands.cjs +96 -46
  13. package/dist/config/mount-commands.d.cts +0 -6
  14. package/dist/config/mount-commands.d.cts.map +1 -1
  15. package/dist/config/mount-commands.d.mts +0 -6
  16. package/dist/config/mount-commands.d.mts.map +1 -1
  17. package/dist/config/mount-commands.mjs +93 -45
  18. package/dist/config/mount-commands.mjs.map +1 -1
  19. package/dist/config/schema.cjs +2 -2
  20. package/dist/config/schema.mjs +2 -2
  21. package/dist/config/schema.mjs.map +1 -1
  22. package/dist/core/commands/exec.cjs +6 -3
  23. package/dist/core/commands/exec.d.cts.map +1 -1
  24. package/dist/core/commands/exec.d.mts.map +1 -1
  25. package/dist/core/commands/exec.mjs +6 -3
  26. package/dist/core/commands/exec.mjs.map +1 -1
  27. package/dist/core/commands/explain.cjs +1 -1
  28. package/dist/core/commands/explain.mjs +1 -1
  29. package/dist/core/commands/mount.cjs +106 -23
  30. package/dist/core/commands/mount.d.cts +2 -0
  31. package/dist/core/commands/mount.d.cts.map +1 -1
  32. package/dist/core/commands/mount.d.mts +2 -0
  33. package/dist/core/commands/mount.d.mts.map +1 -1
  34. package/dist/core/commands/mount.mjs +106 -23
  35. package/dist/core/commands/mount.mjs.map +1 -1
  36. package/dist/core/commands/serve.cjs +38 -13
  37. package/dist/core/commands/serve.mjs +38 -13
  38. package/dist/core/commands/serve.mjs.map +1 -1
  39. package/dist/core/commands/types.cjs +6 -1
  40. package/dist/core/commands/types.d.cts.map +1 -1
  41. package/dist/core/commands/types.d.mts.map +1 -1
  42. package/dist/core/commands/types.mjs +6 -1
  43. package/dist/core/commands/types.mjs.map +1 -1
  44. package/dist/credential/auth-server.cjs +247 -0
  45. package/dist/credential/auth-server.mjs +247 -0
  46. package/dist/credential/auth-server.mjs.map +1 -0
  47. package/dist/credential/cli-auth-context.cjs +86 -0
  48. package/dist/credential/cli-auth-context.d.mts +1 -0
  49. package/dist/credential/cli-auth-context.mjs +86 -0
  50. package/dist/credential/cli-auth-context.mjs.map +1 -0
  51. package/dist/credential/index.cjs +5 -0
  52. package/dist/credential/index.d.mts +4 -0
  53. package/dist/credential/index.mjs +7 -0
  54. package/dist/credential/mcp-auth-context.cjs +186 -0
  55. package/dist/credential/mcp-auth-context.d.mts +1 -0
  56. package/dist/credential/mcp-auth-context.mjs +186 -0
  57. package/dist/credential/mcp-auth-context.mjs.map +1 -0
  58. package/dist/credential/resolver.cjs +125 -0
  59. package/dist/credential/resolver.d.mts +1 -0
  60. package/dist/credential/resolver.mjs +125 -0
  61. package/dist/credential/resolver.mjs.map +1 -0
  62. package/dist/credential/store.cjs +106 -0
  63. package/dist/credential/store.d.cts +30 -0
  64. package/dist/credential/store.d.cts.map +1 -0
  65. package/dist/credential/store.d.mts +30 -0
  66. package/dist/credential/store.d.mts.map +1 -0
  67. package/dist/credential/store.mjs +106 -0
  68. package/dist/credential/store.mjs.map +1 -0
  69. package/dist/index.cjs +3 -0
  70. package/dist/index.d.cts +2 -1
  71. package/dist/index.d.mts +3 -1
  72. package/dist/index.mjs +3 -1
  73. package/dist/mcp/http-transport.cjs +22 -3
  74. package/dist/mcp/http-transport.mjs +22 -3
  75. package/dist/mcp/http-transport.mjs.map +1 -1
  76. package/dist/mcp/prompts.cjs +2 -2
  77. package/dist/mcp/prompts.mjs +2 -2
  78. package/dist/mcp/prompts.mjs.map +1 -1
  79. package/dist/mcp/server.cjs +16 -6
  80. package/dist/mcp/server.mjs +15 -6
  81. package/dist/mcp/server.mjs.map +1 -1
  82. package/dist/mcp/tools.cjs +2 -46
  83. package/dist/mcp/tools.mjs +2 -46
  84. package/dist/mcp/tools.mjs.map +1 -1
  85. package/dist/repl.cjs +9 -3
  86. package/dist/repl.d.cts.map +1 -1
  87. package/dist/repl.d.mts.map +1 -1
  88. package/dist/repl.mjs +9 -3
  89. package/dist/repl.mjs.map +1 -1
  90. package/package.json +20 -20
@@ -27,8 +27,6 @@ Use these tools to navigate and interact with the mounted providers:
27
27
  - **afs_write** — Write content to a path (if the provider supports it).
28
28
  - **afs_delete** — Delete a file or directory.
29
29
  - **afs_exec** — Execute an action. Use \`afs_list {path}/.actions\` to discover available actions.
30
- - **afs_mount** — Dynamically mount a new provider (e.g., \`fs:///path\`, \`sqlite:///db.sqlite\`).
31
- - **afs_unmount** — Unmount a provider.
32
30
 
33
31
  ## Getting Started
34
32
 
@@ -36,6 +34,8 @@ Use these tools to navigate and interact with the mounted providers:
36
34
  2. Pick a mount and run \`afs_list /mount-path\` to explore its contents
37
35
  3. Use \`afs_read\` to view files, \`afs_stat\` for metadata
38
36
  4. Use \`afs_list {path}/.actions\` to discover executable actions
37
+ 5. Mount new providers: \`afs_exec /.actions/mount {uri, path}\` or via registry \`afs_exec /registry/.../.actions/mount\`
38
+ 6. Unmount providers: \`afs_exec /.actions/unmount {path}\`
39
39
 
40
40
  ${mountList.length > 0 ? `## Current Mounts\n\n${mountList.join("\n")}` : "## Current Mounts\n\nNo providers are currently mounted."}`
41
41
  }
@@ -1 +1 @@
1
- {"version":3,"file":"prompts.mjs","names":[],"sources":["../../src/mcp/prompts.ts"],"sourcesContent":["/**\n * AFS MCP Prompts Registration\n *\n * Registers the \"explore\" prompt that provides a usage guide with dynamic mount info.\n */\n\nimport type { AFS } from \"@aigne/afs\";\nimport type { McpServer } from \"@modelcontextprotocol/sdk/server/mcp.js\";\n\n/**\n * Register MCP prompts on the server.\n */\nexport function registerPrompts(server: McpServer, afs: AFS): void {\n server.prompt(\n \"explore\",\n \"Explore the AFS virtual filesystem and learn how to use it\",\n async () => {\n const mounts = afs.getMounts();\n const mountList = mounts.map((m) => {\n const mode = m.module.accessMode ?? \"readonly\";\n return ` - ${m.path} (${m.module.name}, ${mode})`;\n });\n\n const mountSection =\n mountList.length > 0\n ? `## Current Mounts\\n\\n${mountList.join(\"\\n\")}`\n : \"## Current Mounts\\n\\nNo providers are currently mounted.\";\n\n const guide = `# AFS Explorer Guide\n\nAFS (Agentic File System) provides a unified interface to access various data sources.\nUse these tools to navigate and interact with the mounted providers:\n\n## Available Tools\n\n- **afs_list** — List directory contents at a path. Start with \\`afs_list /\\` to see all mount points.\n- **afs_read** — Read the content of a file or entry.\n- **afs_stat** — Get metadata (size, type, children count) for any path.\n- **afs_search** — Search for content within a path subtree.\n- **afs_explain** — Get a human-readable explanation of a path or topic.\n- **afs_write** — Write content to a path (if the provider supports it).\n- **afs_delete** — Delete a file or directory.\n- **afs_exec** — Execute an action. Use \\`afs_list {path}/.actions\\` to discover available actions.\n- **afs_mount** — Dynamically mount a new provider (e.g., \\`fs:///path\\`, \\`sqlite:///db.sqlite\\`).\n- **afs_unmount** — Unmount a provider.\n\n## Getting Started\n\n1. Run \\`afs_list /\\` to see all mount points\n2. Pick a mount and run \\`afs_list /mount-path\\` to explore its contents\n3. Use \\`afs_read\\` to view files, \\`afs_stat\\` for metadata\n4. Use \\`afs_list {path}/.actions\\` to discover executable actions\n\n${mountSection}`;\n\n return {\n messages: [\n {\n role: \"user\" as const,\n content: { type: \"text\" as const, text: guide },\n },\n ],\n };\n },\n );\n}\n"],"mappings":";;;;AAYA,SAAgB,gBAAgB,QAAmB,KAAgB;AACjE,QAAO,OACL,WACA,8DACA,YAAY;EAEV,MAAM,YADS,IAAI,WAAW,CACL,KAAK,MAAM;GAClC,MAAM,OAAO,EAAE,OAAO,cAAc;AACpC,UAAO,OAAO,EAAE,KAAK,IAAI,EAAE,OAAO,KAAK,IAAI,KAAK;IAChD;AAkCF,SAAO,EACL,UAAU,CACR;GACE,MAAM;GACN,SAAS;IAAE,MAAM;IAAiB,MA/B1B;;;;;;;;;;;;;;;;;;;;;;;;;EAJZ,UAAU,SAAS,IACf,wBAAwB,UAAU,KAAK,KAAK,KAC5C;IAiC+C;GAChD,CACF,EACF;GAEJ"}
1
+ {"version":3,"file":"prompts.mjs","names":[],"sources":["../../src/mcp/prompts.ts"],"sourcesContent":["/**\n * AFS MCP Prompts Registration\n *\n * Registers the \"explore\" prompt that provides a usage guide with dynamic mount info.\n */\n\nimport type { AFS } from \"@aigne/afs\";\nimport type { McpServer } from \"@modelcontextprotocol/sdk/server/mcp.js\";\n\n/**\n * Register MCP prompts on the server.\n */\nexport function registerPrompts(server: McpServer, afs: AFS): void {\n server.prompt(\n \"explore\",\n \"Explore the AFS virtual filesystem and learn how to use it\",\n async () => {\n const mounts = afs.getMounts();\n const mountList = mounts.map((m) => {\n const mode = m.module.accessMode ?? \"readonly\";\n return ` - ${m.path} (${m.module.name}, ${mode})`;\n });\n\n const mountSection =\n mountList.length > 0\n ? `## Current Mounts\\n\\n${mountList.join(\"\\n\")}`\n : \"## Current Mounts\\n\\nNo providers are currently mounted.\";\n\n const guide = `# AFS Explorer Guide\n\nAFS (Agentic File System) provides a unified interface to access various data sources.\nUse these tools to navigate and interact with the mounted providers:\n\n## Available Tools\n\n- **afs_list** — List directory contents at a path. Start with \\`afs_list /\\` to see all mount points.\n- **afs_read** — Read the content of a file or entry.\n- **afs_stat** — Get metadata (size, type, children count) for any path.\n- **afs_search** — Search for content within a path subtree.\n- **afs_explain** — Get a human-readable explanation of a path or topic.\n- **afs_write** — Write content to a path (if the provider supports it).\n- **afs_delete** — Delete a file or directory.\n- **afs_exec** — Execute an action. Use \\`afs_list {path}/.actions\\` to discover available actions.\n\n## Getting Started\n\n1. Run \\`afs_list /\\` to see all mount points\n2. Pick a mount and run \\`afs_list /mount-path\\` to explore its contents\n3. Use \\`afs_read\\` to view files, \\`afs_stat\\` for metadata\n4. Use \\`afs_list {path}/.actions\\` to discover executable actions\n5. Mount new providers: \\`afs_exec /.actions/mount {uri, path}\\` or via registry \\`afs_exec /registry/.../.actions/mount\\`\n6. Unmount providers: \\`afs_exec /.actions/unmount {path}\\`\n\n${mountSection}`;\n\n return {\n messages: [\n {\n role: \"user\" as const,\n content: { type: \"text\" as const, text: guide },\n },\n ],\n };\n },\n );\n}\n"],"mappings":";;;;AAYA,SAAgB,gBAAgB,QAAmB,KAAgB;AACjE,QAAO,OACL,WACA,8DACA,YAAY;EAEV,MAAM,YADS,IAAI,WAAW,CACL,KAAK,MAAM;GAClC,MAAM,OAAO,EAAE,OAAO,cAAc;AACpC,UAAO,OAAO,EAAE,KAAK,IAAI,EAAE,OAAO,KAAK,IAAI,KAAK;IAChD;AAkCF,SAAO,EACL,UAAU,CACR;GACE,MAAM;GACN,SAAS;IAAE,MAAM;IAAiB,MA/B1B;;;;;;;;;;;;;;;;;;;;;;;;;EAJZ,UAAU,SAAS,IACf,wBAAwB,UAAU,KAAK,KAAK,KAC5C;IAiC+C;GAChD,CACF,EACF;GAEJ"}
@@ -7,12 +7,11 @@ let _modelcontextprotocol_sdk_server_mcp_js = require("@modelcontextprotocol/sdk
7
7
 
8
8
  //#region src/mcp/server.ts
9
9
  /**
10
- * Create an MCP Server that exposes AFS operations as tools,
11
- * resources, and prompts.
10
+ * Create a new McpServer instance with AFS capabilities.
11
+ * Useful when you need the server before AFS is created (e.g. for MCP auth context).
12
12
  */
13
- function createAFSMcpServer(options) {
14
- const { afs } = options;
15
- const server = new _modelcontextprotocol_sdk_server_mcp_js.McpServer({
13
+ function createMcpServerInstance() {
14
+ return new _modelcontextprotocol_sdk_server_mcp_js.McpServer({
16
15
  name: "afs",
17
16
  version: require_version.VERSION
18
17
  }, { capabilities: {
@@ -20,6 +19,16 @@ function createAFSMcpServer(options) {
20
19
  resources: {},
21
20
  prompts: {}
22
21
  } });
22
+ }
23
+ /**
24
+ * Create an MCP Server that exposes AFS operations as tools,
25
+ * resources, and prompts.
26
+ *
27
+ * If `options.server` is provided, registers on that server instead of creating a new one.
28
+ */
29
+ function createAFSMcpServer(options) {
30
+ const { afs } = options;
31
+ const server = options.server ?? createMcpServerInstance();
23
32
  require_tools.registerTools(server, afs);
24
33
  require_resources.registerResources(server, afs);
25
34
  require_prompts.registerPrompts(server, afs);
@@ -27,4 +36,5 @@ function createAFSMcpServer(options) {
27
36
  }
28
37
 
29
38
  //#endregion
30
- exports.createAFSMcpServer = createAFSMcpServer;
39
+ exports.createAFSMcpServer = createAFSMcpServer;
40
+ exports.createMcpServerInstance = createMcpServerInstance;
@@ -6,12 +6,11 @@ import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
6
6
 
7
7
  //#region src/mcp/server.ts
8
8
  /**
9
- * Create an MCP Server that exposes AFS operations as tools,
10
- * resources, and prompts.
9
+ * Create a new McpServer instance with AFS capabilities.
10
+ * Useful when you need the server before AFS is created (e.g. for MCP auth context).
11
11
  */
12
- function createAFSMcpServer(options) {
13
- const { afs } = options;
14
- const server = new McpServer({
12
+ function createMcpServerInstance() {
13
+ return new McpServer({
15
14
  name: "afs",
16
15
  version: VERSION
17
16
  }, { capabilities: {
@@ -19,6 +18,16 @@ function createAFSMcpServer(options) {
19
18
  resources: {},
20
19
  prompts: {}
21
20
  } });
21
+ }
22
+ /**
23
+ * Create an MCP Server that exposes AFS operations as tools,
24
+ * resources, and prompts.
25
+ *
26
+ * If `options.server` is provided, registers on that server instead of creating a new one.
27
+ */
28
+ function createAFSMcpServer(options) {
29
+ const { afs } = options;
30
+ const server = options.server ?? createMcpServerInstance();
22
31
  registerTools(server, afs);
23
32
  registerResources(server, afs);
24
33
  registerPrompts(server, afs);
@@ -26,5 +35,5 @@ function createAFSMcpServer(options) {
26
35
  }
27
36
 
28
37
  //#endregion
29
- export { createAFSMcpServer };
38
+ export { createAFSMcpServer, createMcpServerInstance };
30
39
  //# sourceMappingURL=server.mjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"server.mjs","names":[],"sources":["../../src/mcp/server.ts"],"sourcesContent":["/**\n * AFS MCP Server\n *\n * Creates an MCP Server that exposes AFS operations as MCP tools,\n * resources, and prompts.\n */\n\nimport type { AFS } from \"@aigne/afs\";\nimport { McpServer } from \"@modelcontextprotocol/sdk/server/mcp.js\";\nimport { VERSION } from \"../version.js\";\nimport { registerPrompts } from \"./prompts.js\";\nimport { registerResources } from \"./resources.js\";\nimport { registerTools } from \"./tools.js\";\n\nexport interface CreateAFSMcpServerOptions {\n /** AFS instance to expose */\n afs: AFS;\n}\n\nexport interface AFSMcpServerResult {\n /** The MCP server instance */\n server: McpServer;\n}\n\n/**\n * Create an MCP Server that exposes AFS operations as tools,\n * resources, and prompts.\n */\nexport function createAFSMcpServer(options: CreateAFSMcpServerOptions): AFSMcpServerResult {\n const { afs } = options;\n\n const server = new McpServer(\n { name: \"afs\", version: VERSION },\n {\n capabilities: {\n tools: {},\n resources: {},\n prompts: {},\n },\n },\n );\n\n registerTools(server, afs);\n registerResources(server, afs);\n registerPrompts(server, afs);\n\n return { server };\n}\n"],"mappings":";;;;;;;;;;;AA4BA,SAAgB,mBAAmB,SAAwD;CACzF,MAAM,EAAE,QAAQ;CAEhB,MAAM,SAAS,IAAI,UACjB;EAAE,MAAM;EAAO,SAAS;EAAS,EACjC,EACE,cAAc;EACZ,OAAO,EAAE;EACT,WAAW,EAAE;EACb,SAAS,EAAE;EACZ,EACF,CACF;AAED,eAAc,QAAQ,IAAI;AAC1B,mBAAkB,QAAQ,IAAI;AAC9B,iBAAgB,QAAQ,IAAI;AAE5B,QAAO,EAAE,QAAQ"}
1
+ {"version":3,"file":"server.mjs","names":[],"sources":["../../src/mcp/server.ts"],"sourcesContent":["/**\n * AFS MCP Server\n *\n * Creates an MCP Server that exposes AFS operations as MCP tools,\n * resources, and prompts.\n */\n\nimport type { AFS } from \"@aigne/afs\";\nimport { McpServer } from \"@modelcontextprotocol/sdk/server/mcp.js\";\nimport { VERSION } from \"../version.js\";\nimport { registerPrompts } from \"./prompts.js\";\nimport { registerResources } from \"./resources.js\";\nimport { registerTools } from \"./tools.js\";\n\nexport interface CreateAFSMcpServerOptions {\n /** AFS instance to expose */\n afs: AFS;\n /** Pre-created McpServer instance. If not provided, a new one is created. */\n server?: McpServer;\n}\n\nexport interface AFSMcpServerResult {\n /** The MCP server instance */\n server: McpServer;\n}\n\n/**\n * Create a new McpServer instance with AFS capabilities.\n * Useful when you need the server before AFS is created (e.g. for MCP auth context).\n */\nexport function createMcpServerInstance(): McpServer {\n return new McpServer(\n { name: \"afs\", version: VERSION },\n {\n capabilities: {\n tools: {},\n resources: {},\n prompts: {},\n },\n },\n );\n}\n\n/**\n * Create an MCP Server that exposes AFS operations as tools,\n * resources, and prompts.\n *\n * If `options.server` is provided, registers on that server instead of creating a new one.\n */\nexport function createAFSMcpServer(options: CreateAFSMcpServerOptions): AFSMcpServerResult {\n const { afs } = options;\n const server = options.server ?? createMcpServerInstance();\n\n registerTools(server, afs);\n registerResources(server, afs);\n registerPrompts(server, afs);\n\n return { server };\n}\n"],"mappings":";;;;;;;;;;;AA8BA,SAAgB,0BAAqC;AACnD,QAAO,IAAI,UACT;EAAE,MAAM;EAAO,SAAS;EAAS,EACjC,EACE,cAAc;EACZ,OAAO,EAAE;EACT,WAAW,EAAE;EACb,SAAS,EAAE;EACZ,EACF,CACF;;;;;;;;AASH,SAAgB,mBAAmB,SAAwD;CACzF,MAAM,EAAE,QAAQ;CAChB,MAAM,SAAS,QAAQ,UAAU,yBAAyB;AAE1D,eAAc,QAAQ,IAAI;AAC1B,mBAAkB,QAAQ,IAAI;AAC9B,iBAAgB,QAAQ,IAAI;AAE5B,QAAO,EAAE,QAAQ"}
@@ -6,7 +6,6 @@ const require_read = require('../core/formatters/read.cjs');
6
6
  const require_stat = require('../core/formatters/stat.cjs');
7
7
  const require_write = require('../core/formatters/write.cjs');
8
8
  const require_path_utils = require('../path-utils.cjs');
9
- let _aigne_afs_provider_registry = require("@aigne/afs-provider-registry");
10
9
  let zod = require("zod");
11
10
 
12
11
  //#region src/mcp/tools.ts
@@ -79,7 +78,7 @@ function registerTools(server, afs) {
79
78
  });
80
79
  server.tool("afs_write", "Write content to an AFS path", {
81
80
  path: zod.z.string().describe("AFS path to write to"),
82
- content: zod.z.union([zod.z.string(), zod.z.record(zod.z.unknown())]).describe("Content to write (string or JSON object)")
81
+ content: zod.z.union([zod.z.string(), zod.z.record(zod.z.string(), zod.z.unknown())]).describe("Content to write (string or JSON object)")
83
82
  }, async ({ path, content }) => {
84
83
  try {
85
84
  const canonicalPath = require_path_utils.cliPathToCanonical(path);
@@ -114,7 +113,7 @@ function registerTools(server, afs) {
114
113
  });
115
114
  server.tool("afs_exec", "Execute an action. Use afs_list on {path}/.actions to discover available actions and their inputSchema.", {
116
115
  path: zod.z.string().describe("Action path, e.g. /modules/sqlite/users/.actions/insert"),
117
- args: zod.z.record(zod.z.unknown()).optional().describe("Action arguments matching the action's inputSchema")
116
+ args: zod.z.record(zod.z.string(), zod.z.unknown()).optional().describe("Action arguments matching the action's inputSchema")
118
117
  }, async ({ path, args }) => {
119
118
  try {
120
119
  const canonicalPath = require_path_utils.cliPathToCanonical(path);
@@ -147,49 +146,6 @@ function registerTools(server, afs) {
147
146
  return errorResult(error);
148
147
  }
149
148
  });
150
- server.tool("afs_mount", "Mount a new provider at the specified path", {
151
- uri: zod.z.string().describe("Provider URI, e.g. fs:///path, sqlite:///db.sqlite"),
152
- path: zod.z.string().describe("Mount path in AFS, e.g. /modules/my-db"),
153
- description: zod.z.string().optional().describe("Human-readable description"),
154
- auth: zod.z.string().optional().describe("Authentication token for the provider")
155
- }, async ({ uri, path, description, auth }) => {
156
- try {
157
- const provider = await (0, _aigne_afs_provider_registry.createProviderRegistry)().createProvider({
158
- uri,
159
- path,
160
- description,
161
- auth
162
- });
163
- await afs.mount(provider, path);
164
- server.sendResourceListChanged();
165
- return textResult(`Mounted ${uri} at ${path}`);
166
- } catch (error) {
167
- let safeMessage = (error instanceof Error ? error.message : String(error)).split("\n")[0] || "Mount failed";
168
- if (auth) safeMessage = safeMessage.replaceAll(auth, "***");
169
- return {
170
- isError: true,
171
- content: [{
172
- type: "text",
173
- text: safeMessage
174
- }]
175
- };
176
- }
177
- });
178
- server.tool("afs_unmount", "Unmount a provider at the specified path", { path: zod.z.string().describe("Mount path to unmount, e.g. /modules/my-db") }, async ({ path }) => {
179
- try {
180
- if (!afs.unmount(path)) return {
181
- isError: true,
182
- content: [{
183
- type: "text",
184
- text: `No provider mounted at ${path}`
185
- }]
186
- };
187
- server.sendResourceListChanged();
188
- return textResult(`Unmounted ${path}`);
189
- } catch (error) {
190
- return errorResult(error);
191
- }
192
- });
193
149
  }
194
150
 
195
151
  //#endregion
@@ -5,7 +5,6 @@ import { formatReadOutput } from "../core/formatters/read.mjs";
5
5
  import { formatStatOutput } from "../core/formatters/stat.mjs";
6
6
  import { formatWriteOutput } from "../core/formatters/write.mjs";
7
7
  import { cliPathToCanonical } from "../path-utils.mjs";
8
- import { createProviderRegistry } from "@aigne/afs-provider-registry";
9
8
  import { z } from "zod";
10
9
 
11
10
  //#region src/mcp/tools.ts
@@ -78,7 +77,7 @@ function registerTools(server, afs) {
78
77
  });
79
78
  server.tool("afs_write", "Write content to an AFS path", {
80
79
  path: z.string().describe("AFS path to write to"),
81
- content: z.union([z.string(), z.record(z.unknown())]).describe("Content to write (string or JSON object)")
80
+ content: z.union([z.string(), z.record(z.string(), z.unknown())]).describe("Content to write (string or JSON object)")
82
81
  }, async ({ path, content }) => {
83
82
  try {
84
83
  const canonicalPath = cliPathToCanonical(path);
@@ -113,7 +112,7 @@ function registerTools(server, afs) {
113
112
  });
114
113
  server.tool("afs_exec", "Execute an action. Use afs_list on {path}/.actions to discover available actions and their inputSchema.", {
115
114
  path: z.string().describe("Action path, e.g. /modules/sqlite/users/.actions/insert"),
116
- args: z.record(z.unknown()).optional().describe("Action arguments matching the action's inputSchema")
115
+ args: z.record(z.string(), z.unknown()).optional().describe("Action arguments matching the action's inputSchema")
117
116
  }, async ({ path, args }) => {
118
117
  try {
119
118
  const canonicalPath = cliPathToCanonical(path);
@@ -146,49 +145,6 @@ function registerTools(server, afs) {
146
145
  return errorResult(error);
147
146
  }
148
147
  });
149
- server.tool("afs_mount", "Mount a new provider at the specified path", {
150
- uri: z.string().describe("Provider URI, e.g. fs:///path, sqlite:///db.sqlite"),
151
- path: z.string().describe("Mount path in AFS, e.g. /modules/my-db"),
152
- description: z.string().optional().describe("Human-readable description"),
153
- auth: z.string().optional().describe("Authentication token for the provider")
154
- }, async ({ uri, path, description, auth }) => {
155
- try {
156
- const provider = await createProviderRegistry().createProvider({
157
- uri,
158
- path,
159
- description,
160
- auth
161
- });
162
- await afs.mount(provider, path);
163
- server.sendResourceListChanged();
164
- return textResult(`Mounted ${uri} at ${path}`);
165
- } catch (error) {
166
- let safeMessage = (error instanceof Error ? error.message : String(error)).split("\n")[0] || "Mount failed";
167
- if (auth) safeMessage = safeMessage.replaceAll(auth, "***");
168
- return {
169
- isError: true,
170
- content: [{
171
- type: "text",
172
- text: safeMessage
173
- }]
174
- };
175
- }
176
- });
177
- server.tool("afs_unmount", "Unmount a provider at the specified path", { path: z.string().describe("Mount path to unmount, e.g. /modules/my-db") }, async ({ path }) => {
178
- try {
179
- if (!afs.unmount(path)) return {
180
- isError: true,
181
- content: [{
182
- type: "text",
183
- text: `No provider mounted at ${path}`
184
- }]
185
- };
186
- server.sendResourceListChanged();
187
- return textResult(`Unmounted ${path}`);
188
- } catch (error) {
189
- return errorResult(error);
190
- }
191
- });
192
148
  }
193
149
 
194
150
  //#endregion
@@ -1 +1 @@
1
- {"version":3,"file":"tools.mjs","names":[],"sources":["../../src/mcp/tools.ts"],"sourcesContent":["/**\n * AFS MCP Tools Registration\n *\n * Registers all 10 AFS operations as MCP tools.\n */\n\nimport type { AFS } from \"@aigne/afs\";\nimport { createProviderRegistry } from \"@aigne/afs-provider-registry\";\nimport type { McpServer } from \"@modelcontextprotocol/sdk/server/mcp.js\";\nimport { z } from \"zod\";\nimport { formatDeleteOutput } from \"../core/formatters/delete.js\";\nimport { formatExecOutput } from \"../core/formatters/exec.js\";\nimport { formatLsOutput } from \"../core/formatters/ls.js\";\nimport { formatReadOutput } from \"../core/formatters/read.js\";\nimport { formatStatOutput } from \"../core/formatters/stat.js\";\nimport { formatWriteOutput } from \"../core/formatters/write.js\";\nimport { cliPathToCanonical } from \"../path-utils.js\";\n\n/**\n * Create a safe error result for MCP tools.\n * Strips stack traces and internal details from error messages.\n */\nfunction errorResult(error: unknown) {\n const message = error instanceof Error ? error.message : String(error);\n const safeMessage = message.split(\"\\n\")[0] || \"Operation failed\";\n return {\n isError: true as const,\n content: [{ type: \"text\" as const, text: safeMessage }],\n };\n}\n\n/**\n * Create a text result for MCP tools.\n */\nfunction textResult(text: string) {\n return {\n content: [{ type: \"text\" as const, text }],\n };\n}\n\n/**\n * Register all AFS tools on the MCP server.\n */\nexport function registerTools(server: McpServer, afs: AFS): void {\n // afs_read - Read content at a path\n server.tool(\n \"afs_read\",\n \"Read content at an AFS path\",\n { path: z.string().describe(\"AFS path to read, e.g. /modules/fs/README.md or $afs:ns/path\") },\n async ({ path }) => {\n try {\n const canonicalPath = cliPathToCanonical(path);\n const result = await afs.read(canonicalPath);\n if (!result.data) {\n return {\n isError: true as const,\n content: [{ type: \"text\" as const, text: result.message || \"Not found\" }],\n };\n }\n const formatted = formatReadOutput(result, \"llm\", { path });\n return textResult(formatted);\n } catch (error) {\n return errorResult(error);\n }\n },\n );\n\n // afs_list - List directory contents\n server.tool(\n \"afs_list\",\n \"List directory contents at an AFS path\",\n {\n path: z.string().describe(\"AFS path to list\"),\n depth: z.number().optional().describe(\"Recursion depth, default 1\"),\n pattern: z.string().optional().describe(\"Filter pattern, e.g. *.md\"),\n limit: z.number().optional().describe(\"Max entries to return\"),\n },\n async ({ path, depth, pattern, limit }) => {\n try {\n const canonicalPath = cliPathToCanonical(path);\n const result = await afs.list(canonicalPath, {\n maxDepth: depth ?? 1,\n pattern,\n limit,\n });\n if (result.data.length === 0 && result.message) {\n return {\n isError: true as const,\n content: [{ type: \"text\" as const, text: result.message }],\n };\n }\n const formatted = formatLsOutput(result, \"llm\", { path });\n return textResult(formatted);\n } catch (error) {\n return errorResult(error);\n }\n },\n );\n\n // afs_write - Write content to a path\n server.tool(\n \"afs_write\",\n \"Write content to an AFS path\",\n {\n path: z.string().describe(\"AFS path to write to\"),\n content: z\n .union([z.string(), z.record(z.unknown())])\n .describe(\"Content to write (string or JSON object)\"),\n },\n async ({ path, content }) => {\n try {\n const canonicalPath = cliPathToCanonical(path);\n const result = await afs.write(canonicalPath, { content });\n const formatted = formatWriteOutput(result, \"llm\");\n return textResult(formatted);\n } catch (error) {\n return errorResult(error);\n }\n },\n );\n\n // afs_delete - Delete a path\n server.tool(\n \"afs_delete\",\n \"Delete a file or directory at an AFS path\",\n {\n path: z.string().describe(\"AFS path to delete\"),\n recursive: z.boolean().optional().describe(\"Delete recursively\"),\n },\n async ({ path, recursive }) => {\n try {\n const canonicalPath = cliPathToCanonical(path);\n const result = await afs.delete(canonicalPath, { recursive });\n const formatted = formatDeleteOutput(result, \"llm\", { path });\n return textResult(formatted);\n } catch (error) {\n return errorResult(error);\n }\n },\n );\n\n // afs_search - Search for content\n server.tool(\n \"afs_search\",\n \"Search for content within an AFS path\",\n {\n path: z.string().describe(\"Base path to search from\"),\n query: z.string().describe(\"Search query\"),\n pattern: z.string().optional().describe(\"File pattern filter\"),\n limit: z.number().optional().describe(\"Max results\"),\n },\n async ({ path, query, limit }) => {\n try {\n const canonicalPath = cliPathToCanonical(path);\n const result = await afs.search(canonicalPath, query, { limit });\n // Search results use list formatter\n const formatted = formatLsOutput(result as any, \"llm\", { path });\n return textResult(formatted);\n } catch (error) {\n return errorResult(error);\n }\n },\n );\n\n // afs_exec - Execute an action\n server.tool(\n \"afs_exec\",\n \"Execute an action. Use afs_list on {path}/.actions to discover available actions and their inputSchema.\",\n {\n path: z.string().describe(\"Action path, e.g. /modules/sqlite/users/.actions/insert\"),\n args: z\n .record(z.unknown())\n .optional()\n .describe(\"Action arguments matching the action's inputSchema\"),\n },\n async ({ path, args }) => {\n try {\n const canonicalPath = cliPathToCanonical(path);\n const result = await afs.exec(canonicalPath, args ?? {});\n const formatted = formatExecOutput(result, \"llm\", { path });\n return textResult(formatted);\n } catch (error) {\n return errorResult(error);\n }\n },\n );\n\n // afs_stat - Get path metadata\n server.tool(\n \"afs_stat\",\n \"Get metadata for an AFS path\",\n { path: z.string().describe(\"AFS path to stat\") },\n async ({ path }) => {\n try {\n const canonicalPath = cliPathToCanonical(path);\n const result = await afs.stat(canonicalPath);\n if (!result.data) {\n return {\n isError: true as const,\n content: [{ type: \"text\" as const, text: result.message || \"Not found\" }],\n };\n }\n const formatted = formatStatOutput(result, \"llm\");\n return textResult(formatted);\n } catch (error) {\n return errorResult(error);\n }\n },\n );\n\n // afs_explain - Get human-readable explanation\n server.tool(\n \"afs_explain\",\n \"Get a human-readable explanation for an AFS path or topic\",\n { path: z.string().describe(\"AFS path or topic to explain\") },\n async ({ path }) => {\n try {\n const canonicalPath = cliPathToCanonical(path);\n const result = await afs.explain(canonicalPath);\n // explain returns { format, content } directly\n return textResult(result.content);\n } catch (error) {\n return errorResult(error);\n }\n },\n );\n\n // afs_mount - Dynamically mount a provider\n server.tool(\n \"afs_mount\",\n \"Mount a new provider at the specified path\",\n {\n uri: z.string().describe(\"Provider URI, e.g. fs:///path, sqlite:///db.sqlite\"),\n path: z.string().describe(\"Mount path in AFS, e.g. /modules/my-db\"),\n description: z.string().optional().describe(\"Human-readable description\"),\n auth: z.string().optional().describe(\"Authentication token for the provider\"),\n },\n async ({ uri, path, description, auth }) => {\n try {\n const registry = createProviderRegistry();\n const provider = await registry.createProvider({\n uri,\n path,\n description,\n auth,\n });\n await afs.mount(provider, path);\n server.sendResourceListChanged();\n return textResult(`Mounted ${uri} at ${path}`);\n } catch (error) {\n // Strip auth tokens from error messages\n const message = error instanceof Error ? error.message : String(error);\n let safeMessage = message.split(\"\\n\")[0] || \"Mount failed\";\n if (auth) {\n safeMessage = safeMessage.replaceAll(auth, \"***\");\n }\n return {\n isError: true as const,\n content: [{ type: \"text\" as const, text: safeMessage }],\n };\n }\n },\n );\n\n // afs_unmount - Unmount a provider\n server.tool(\n \"afs_unmount\",\n \"Unmount a provider at the specified path\",\n { path: z.string().describe(\"Mount path to unmount, e.g. /modules/my-db\") },\n async ({ path }) => {\n try {\n const success = afs.unmount(path);\n if (!success) {\n return {\n isError: true as const,\n content: [{ type: \"text\" as const, text: `No provider mounted at ${path}` }],\n };\n }\n server.sendResourceListChanged();\n return textResult(`Unmounted ${path}`);\n } catch (error) {\n return errorResult(error);\n }\n },\n );\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"tools.mjs","names":[],"sources":["../../src/mcp/tools.ts"],"sourcesContent":["/**\n * AFS MCP Tools Registration\n *\n * Registers all 10 AFS operations as MCP tools.\n */\n\nimport type { AFS } from \"@aigne/afs\";\nimport type { McpServer } from \"@modelcontextprotocol/sdk/server/mcp.js\";\nimport { z } from \"zod\";\nimport { formatDeleteOutput } from \"../core/formatters/delete.js\";\nimport { formatExecOutput } from \"../core/formatters/exec.js\";\nimport { formatLsOutput } from \"../core/formatters/ls.js\";\nimport { formatReadOutput } from \"../core/formatters/read.js\";\nimport { formatStatOutput } from \"../core/formatters/stat.js\";\nimport { formatWriteOutput } from \"../core/formatters/write.js\";\nimport { cliPathToCanonical } from \"../path-utils.js\";\n\n/**\n * Create a safe error result for MCP tools.\n * Strips stack traces and internal details from error messages.\n */\nfunction errorResult(error: unknown) {\n const message = error instanceof Error ? error.message : String(error);\n const safeMessage = message.split(\"\\n\")[0] || \"Operation failed\";\n return {\n isError: true as const,\n content: [{ type: \"text\" as const, text: safeMessage }],\n };\n}\n\n/**\n * Create a text result for MCP tools.\n */\nfunction textResult(text: string) {\n return {\n content: [{ type: \"text\" as const, text }],\n };\n}\n\n/**\n * Register all AFS tools on the MCP server.\n */\nexport function registerTools(server: McpServer, afs: AFS): void {\n // afs_read - Read content at a path\n server.tool(\n \"afs_read\",\n \"Read content at an AFS path\",\n { path: z.string().describe(\"AFS path to read, e.g. /modules/fs/README.md or $afs:ns/path\") },\n async ({ path }) => {\n try {\n const canonicalPath = cliPathToCanonical(path);\n const result = await afs.read(canonicalPath);\n if (!result.data) {\n return {\n isError: true as const,\n content: [{ type: \"text\" as const, text: result.message || \"Not found\" }],\n };\n }\n const formatted = formatReadOutput(result, \"llm\", { path });\n return textResult(formatted);\n } catch (error) {\n return errorResult(error);\n }\n },\n );\n\n // afs_list - List directory contents\n server.tool(\n \"afs_list\",\n \"List directory contents at an AFS path\",\n {\n path: z.string().describe(\"AFS path to list\"),\n depth: z.number().optional().describe(\"Recursion depth, default 1\"),\n pattern: z.string().optional().describe(\"Filter pattern, e.g. *.md\"),\n limit: z.number().optional().describe(\"Max entries to return\"),\n },\n async ({ path, depth, pattern, limit }) => {\n try {\n const canonicalPath = cliPathToCanonical(path);\n const result = await afs.list(canonicalPath, {\n maxDepth: depth ?? 1,\n pattern,\n limit,\n });\n if (result.data.length === 0 && result.message) {\n return {\n isError: true as const,\n content: [{ type: \"text\" as const, text: result.message }],\n };\n }\n const formatted = formatLsOutput(result, \"llm\", { path });\n return textResult(formatted);\n } catch (error) {\n return errorResult(error);\n }\n },\n );\n\n // afs_write - Write content to a path\n server.tool(\n \"afs_write\",\n \"Write content to an AFS path\",\n {\n path: z.string().describe(\"AFS path to write to\"),\n content: z\n .union([z.string(), z.record(z.string(), z.unknown())])\n .describe(\"Content to write (string or JSON object)\"),\n },\n async ({ path, content }) => {\n try {\n const canonicalPath = cliPathToCanonical(path);\n const result = await afs.write(canonicalPath, { content });\n const formatted = formatWriteOutput(result, \"llm\");\n return textResult(formatted);\n } catch (error) {\n return errorResult(error);\n }\n },\n );\n\n // afs_delete - Delete a path\n server.tool(\n \"afs_delete\",\n \"Delete a file or directory at an AFS path\",\n {\n path: z.string().describe(\"AFS path to delete\"),\n recursive: z.boolean().optional().describe(\"Delete recursively\"),\n },\n async ({ path, recursive }) => {\n try {\n const canonicalPath = cliPathToCanonical(path);\n const result = await afs.delete(canonicalPath, { recursive });\n const formatted = formatDeleteOutput(result, \"llm\", { path });\n return textResult(formatted);\n } catch (error) {\n return errorResult(error);\n }\n },\n );\n\n // afs_search - Search for content\n server.tool(\n \"afs_search\",\n \"Search for content within an AFS path\",\n {\n path: z.string().describe(\"Base path to search from\"),\n query: z.string().describe(\"Search query\"),\n pattern: z.string().optional().describe(\"File pattern filter\"),\n limit: z.number().optional().describe(\"Max results\"),\n },\n async ({ path, query, limit }) => {\n try {\n const canonicalPath = cliPathToCanonical(path);\n const result = await afs.search(canonicalPath, query, { limit });\n // Search results use list formatter\n const formatted = formatLsOutput(result as any, \"llm\", { path });\n return textResult(formatted);\n } catch (error) {\n return errorResult(error);\n }\n },\n );\n\n // afs_exec - Execute an action\n server.tool(\n \"afs_exec\",\n \"Execute an action. Use afs_list on {path}/.actions to discover available actions and their inputSchema.\",\n {\n path: z.string().describe(\"Action path, e.g. /modules/sqlite/users/.actions/insert\"),\n args: z\n .record(z.string(), z.unknown())\n .optional()\n .describe(\"Action arguments matching the action's inputSchema\"),\n },\n async ({ path, args }) => {\n try {\n const canonicalPath = cliPathToCanonical(path);\n const result = await afs.exec(canonicalPath, args ?? {});\n const formatted = formatExecOutput(result, \"llm\", { path });\n return textResult(formatted);\n } catch (error) {\n return errorResult(error);\n }\n },\n );\n\n // afs_stat - Get path metadata\n server.tool(\n \"afs_stat\",\n \"Get metadata for an AFS path\",\n { path: z.string().describe(\"AFS path to stat\") },\n async ({ path }) => {\n try {\n const canonicalPath = cliPathToCanonical(path);\n const result = await afs.stat(canonicalPath);\n if (!result.data) {\n return {\n isError: true as const,\n content: [{ type: \"text\" as const, text: result.message || \"Not found\" }],\n };\n }\n const formatted = formatStatOutput(result, \"llm\");\n return textResult(formatted);\n } catch (error) {\n return errorResult(error);\n }\n },\n );\n\n // afs_explain - Get human-readable explanation\n server.tool(\n \"afs_explain\",\n \"Get a human-readable explanation for an AFS path or topic\",\n { path: z.string().describe(\"AFS path or topic to explain\") },\n async ({ path }) => {\n try {\n const canonicalPath = cliPathToCanonical(path);\n const result = await afs.explain(canonicalPath);\n // explain returns { format, content } directly\n return textResult(result.content);\n } catch (error) {\n return errorResult(error);\n }\n },\n );\n}\n"],"mappings":"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"}
package/dist/repl.cjs CHANGED
@@ -1,4 +1,6 @@
1
1
  const require_rolldown_runtime = require('./_virtual/rolldown_runtime.cjs');
2
+ const require_cli_auth_context = require('./credential/cli-auth-context.cjs');
3
+ const require_store = require('./credential/store.cjs');
2
4
  const require_afs_loader = require('./config/afs-loader.cjs');
3
5
  const require_index = require('./core/executor/index.cjs');
4
6
  let node_path = require("node:path");
@@ -350,9 +352,13 @@ async function startRepl(options) {
350
352
  const spinner = createSpinner();
351
353
  spinner.start("Mounting providers...");
352
354
  let failures = [];
353
- const { afs, failures: mountFailures } = await require_afs_loader.loadAFS(cwd, { onProgress({ total, completed, failed }) {
354
- spinner.update(`Mounting providers... (${completed}/${total}${failed > 0 ? `, ${failed} failed` : ""})`);
355
- } });
355
+ const { afs, failures: mountFailures } = await require_afs_loader.loadAFS(cwd, {
356
+ authContext: require_cli_auth_context.createCLIAuthContext(),
357
+ credentialStore: require_store.createCredentialStore(),
358
+ onProgress({ total, completed, failed }) {
359
+ spinner.update(`Mounting providers... (${completed}/${total}${failed > 0 ? `, ${failed} failed` : ""})`);
360
+ }
361
+ });
356
362
  failures = mountFailures;
357
363
  if (extraProviders) for (const { provider, mountPath } of extraProviders) try {
358
364
  await afs.mount(provider, mountPath);
@@ -1 +1 @@
1
- {"version":3,"file":"repl.d.cts","names":[],"sources":["../src/repl.ts"],"mappings":";;;iBA+hBsB,SAAA,CAAU,OAAA;EAC9B,GAAA;EACA,OAAA;EACA,MAAA,SAAe,OAAA;EAEf,cAAA,GAAiB,KAAA;IAAQ,QAAA,EAAU,SAAA;IAAW,SAAA;EAAA;AAAA,IAC5C,OAAA"}
1
+ {"version":3,"file":"repl.d.cts","names":[],"sources":["../src/repl.ts"],"mappings":";;;iBAiiBsB,SAAA,CAAU,OAAA;EAC9B,GAAA;EACA,OAAA;EACA,MAAA,SAAe,OAAA;EAEf,cAAA,GAAiB,KAAA;IAAQ,QAAA,EAAU,SAAA;IAAW,SAAA;EAAA;AAAA,IAC5C,OAAA"}
@@ -1 +1 @@
1
- {"version":3,"file":"repl.d.mts","names":[],"sources":["../src/repl.ts"],"mappings":";;;;iBA+hBsB,SAAA,CAAU,OAAA;EAC9B,GAAA;EACA,OAAA;EACA,MAAA,SAAe,OAAA;EAEf,cAAA,GAAiB,KAAA;IAAQ,QAAA,EAAU,SAAA;IAAW,SAAA;EAAA;AAAA,IAC5C,OAAA"}
1
+ {"version":3,"file":"repl.d.mts","names":[],"sources":["../src/repl.ts"],"mappings":";;;;iBAiiBsB,SAAA,CAAU,OAAA;EAC9B,GAAA;EACA,OAAA;EACA,MAAA,SAAe,OAAA;EAEf,cAAA,GAAiB,KAAA;IAAQ,QAAA,EAAU,SAAA;IAAW,SAAA;EAAA;AAAA,IAC5C,OAAA"}
package/dist/repl.mjs CHANGED
@@ -1,3 +1,5 @@
1
+ import { createCLIAuthContext } from "./credential/cli-auth-context.mjs";
2
+ import { createCredentialStore } from "./credential/store.mjs";
1
3
  import { loadAFS } from "./config/afs-loader.mjs";
2
4
  import { AFSCommandExecutor } from "./core/executor/index.mjs";
3
5
  import { basename } from "node:path";
@@ -349,9 +351,13 @@ async function startRepl(options) {
349
351
  const spinner = createSpinner();
350
352
  spinner.start("Mounting providers...");
351
353
  let failures = [];
352
- const { afs, failures: mountFailures } = await loadAFS(cwd, { onProgress({ total, completed, failed }) {
353
- spinner.update(`Mounting providers... (${completed}/${total}${failed > 0 ? `, ${failed} failed` : ""})`);
354
- } });
354
+ const { afs, failures: mountFailures } = await loadAFS(cwd, {
355
+ authContext: createCLIAuthContext(),
356
+ credentialStore: createCredentialStore(),
357
+ onProgress({ total, completed, failed }) {
358
+ spinner.update(`Mounting providers... (${completed}/${total}${failed > 0 ? `, ${failed} failed` : ""})`);
359
+ }
360
+ });
355
361
  failures = mountFailures;
356
362
  if (extraProviders) for (const { provider, mountPath } of extraProviders) try {
357
363
  await afs.mount(provider, mountPath);
package/dist/repl.mjs.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"repl.mjs","names":["dirname","ns","resolved","ctx","resolve"],"sources":["../src/repl.ts"],"sourcesContent":["/**\n * AFS CLI Interactive REPL\n *\n * Single-file REPL implementation for `afs -i` / `afs --interactive`.\n * Provides: REPL loop, cd/pwd, Tab completion, explore TUI integration.\n */\n\nimport { basename } from \"node:path\";\nimport { createInterface, type Interface } from \"node:readline\";\nimport type { AFS, AFSEntry, AFSModule } from \"@aigne/afs\";\nimport { joinURL } from \"ufo\";\nimport { loadAFS, type MountFailure } from \"./config/afs-loader.js\";\nimport { AFSCommandExecutor } from \"./core/executor/index.js\";\n\n// ─── Types ───────────────────────────────────────────────────────────────\n\nexport interface ReplOptions {\n executor: AFSCommandExecutor;\n afs: AFS;\n version: string;\n}\n\nexport interface ReplContext {\n executor: AFSCommandExecutor;\n afs: AFS;\n version: string;\n currentPath: string;\n currentNamespace: string | null;\n completionCache: Map<string, AFSEntry[]>;\n}\n\nexport interface BuiltinResult {\n output?: string;\n exit?: boolean;\n}\n\n// ─── Constants ───────────────────────────────────────────────────────────\n\nconst BUILTIN_COMMANDS = [\"cd\", \"pwd\", \"help\", \"exit\", \"quit\"];\n\n/** Commands that default to current path when no path argument is given in REPL */\nconst DEFAULT_PATH_COMMANDS = new Set([\"ls\", \"read\", \"stat\", \"explain\", \"explore\"]);\n\nconst ALL_COMMANDS = [\n \"ls\",\n \"read\",\n \"write\",\n \"delete\",\n \"stat\",\n \"exec\",\n \"explain\",\n \"mount\",\n \"explore\",\n \"cd\",\n \"pwd\",\n \"help\",\n \"exit\",\n];\n\n// ─── Context ─────────────────────────────────────────────────────────────\n\nexport function createReplContext(options: ReplOptions): ReplContext {\n return {\n executor: options.executor,\n afs: options.afs,\n version: options.version,\n currentPath: \"/\",\n currentNamespace: null,\n completionCache: new Map(),\n };\n}\n\n// ─── Prompt ──────────────────────────────────────────────────────────────\n\nexport function getPrompt(ctx: ReplContext): string {\n const dirname = ctx.currentPath === \"/\" ? \"/\" : basename(ctx.currentPath);\n const nsPrefix = ctx.currentNamespace ? `${ctx.currentNamespace}:` : \"\";\n return `afs ${nsPrefix}${dirname}> `;\n}\n\n// ─── Banner ──────────────────────────────────────────────────────────────\n\nexport function getBanner(ctx: ReplContext): string {\n const mountCount = ctx.afs.getMounts().length;\n const plural = mountCount === 1 ? \"provider\" : \"providers\";\n return `AFS Interactive Shell v${ctx.version} — ${mountCount} ${plural} mounted. Type \"help\" for commands.`;\n}\n\n// ─── Builtin Commands ────────────────────────────────────────────────────\n\nexport function isBuiltinCommand(input: string): boolean {\n const trimmed = input.trim();\n if (!trimmed) return false;\n const cmd = trimmed.split(/\\s+/)[0]!;\n return BUILTIN_COMMANDS.includes(cmd);\n}\n\nexport async function handleBuiltinCommand(\n input: string,\n ctx: ReplContext,\n): Promise<BuiltinResult | null> {\n const trimmed = input.trim();\n if (!trimmed) return null;\n\n const parts = trimmed.split(/\\s+/);\n const cmd = parts[0]!;\n\n switch (cmd) {\n case \"exit\":\n case \"quit\":\n return { exit: true, output: \"Bye!\" };\n\n case \"help\":\n return { output: formatHelp() };\n\n case \"pwd\":\n return handlePwd(ctx);\n\n case \"cd\":\n return handleCd(parts.slice(1).join(\" \"), ctx);\n\n default:\n return null;\n }\n}\n\nfunction formatHelp(): string {\n const lines = [\n \"Available commands:\",\n \"\",\n \" AFS Commands:\",\n \" ls [path] List directory contents (default: current dir)\",\n \" read [path] Read file content (default: current node)\",\n \" write <path> [content] Write content to file\",\n \" delete <path> Delete file or directory\",\n \" stat [path] Get file or directory info (default: current node)\",\n \" exec <action> Execute an action\",\n \" explain [topic] Explain AFS concepts or paths (default: current path)\",\n \" mount Mount management\",\n \" explore [path] Interactive TUI explorer (default: current dir)\",\n \"\",\n \" REPL Commands:\",\n \" cd [path] Change working directory\",\n \" pwd Print working directory\",\n \" help Show this help\",\n \" exit / quit Exit REPL\",\n \"\",\n \" Tips:\",\n \" - Commands work with or without 'afs' prefix\",\n \" - Use Tab for command and path completion\",\n \" - Ctrl+D to exit\",\n ];\n return lines.join(\"\\n\");\n}\n\n// ─── cd / pwd ────────────────────────────────────────────────────────────\n\nfunction handlePwd(ctx: ReplContext): BuiltinResult {\n if (ctx.currentNamespace) {\n return { output: `@${ctx.currentNamespace}${ctx.currentPath}` };\n }\n return { output: ctx.currentPath };\n}\n\nasync function handleCd(target: string, ctx: ReplContext): Promise<BuiltinResult> {\n if (!target || target.trim() === \"\") {\n // cd with no args → go to root\n ctx.currentPath = \"/\";\n return {};\n }\n\n const trimmed = target.trim();\n\n // Handle @default → reset namespace\n if (trimmed === \"@default\") {\n ctx.currentNamespace = null;\n ctx.currentPath = \"/\";\n return {};\n }\n\n // Handle @namespace syntax\n if (trimmed.startsWith(\"@\")) {\n const rest = trimmed.slice(1);\n const slashIdx = rest.indexOf(\"/\");\n if (slashIdx === -1) {\n // @namespace only → switch to namespace root\n const ns = rest;\n if (!ns) return { output: \"cd: invalid namespace\" };\n // Validate by calling stat on namespace root\n try {\n const canonicalPath = `$afs:${ns}/`;\n await ctx.afs.stat(canonicalPath);\n ctx.currentNamespace = ns;\n ctx.currentPath = \"/\";\n return {};\n } catch {\n return { output: `cd: no such path: @${ns}/` };\n }\n }\n const ns = rest.slice(0, slashIdx);\n const path = rest.slice(slashIdx) || \"/\";\n if (!ns) return { output: \"cd: invalid namespace\" };\n try {\n const canonicalPath = `$afs:${ns}${path}`;\n await ctx.afs.stat(canonicalPath);\n ctx.currentNamespace = ns;\n ctx.currentPath = path;\n return {};\n } catch {\n return { output: `cd: no such path: @${ns}${path}` };\n }\n }\n\n // Handle absolute path\n if (trimmed.startsWith(\"/\")) {\n const resolved = normalizePath(trimmed);\n try {\n const canonicalPath = ctx.currentNamespace\n ? `$afs:${ctx.currentNamespace}${resolved}`\n : resolved;\n await ctx.afs.stat(canonicalPath);\n ctx.currentPath = resolved;\n return {};\n } catch {\n return { output: `cd: no such path: ${trimmed}` };\n }\n }\n\n // Handle relative path (including ..)\n const resolved = normalizePath(joinURL(ctx.currentPath, trimmed));\n try {\n const canonicalPath = ctx.currentNamespace\n ? `$afs:${ctx.currentNamespace}${resolved}`\n : resolved;\n await ctx.afs.stat(canonicalPath);\n ctx.currentPath = resolved;\n return {};\n } catch {\n return { output: `cd: no such path: ${trimmed}` };\n }\n}\n\nfunction normalizePath(path: string): string {\n // Split into segments, resolve .., remove empty segments\n const parts = path.split(\"/\").filter(Boolean);\n const resolved: string[] = [];\n for (const part of parts) {\n if (part === \"..\") {\n resolved.pop();\n } else if (part !== \".\") {\n resolved.push(part);\n }\n }\n return `/${resolved.join(\"/\")}`;\n}\n\n// ─── Argv Path Preprocessing ─────────────────────────────────────────────\n\nexport function resolveArgvPath(input: string, ctx: ReplContext): string[] {\n const tokens = tokenize(input);\n if (tokens.length === 0) return tokens;\n\n // Find the command name (skip optional 'afs' prefix)\n let cmdIdx = 0;\n if (tokens[0] === \"afs\" && tokens.length > 1) cmdIdx = 1;\n const cmd = tokens[cmdIdx];\n if (!cmd) return tokens;\n\n // Find first positional argument (non-option) after the command\n let argIdx = -1;\n for (let i = cmdIdx + 1; i < tokens.length; i++) {\n const token = tokens[i]!;\n if (!token.startsWith(\"-\")) {\n // For \"mount\" command, subcommands like \"list\", \"add\", \"remove\" are not paths\n if (\n cmd === \"mount\" &&\n (token === \"list\" || token === \"ls\" || token === \"add\" || token === \"remove\")\n ) {\n continue;\n }\n argIdx = i;\n break;\n }\n // Skip --option value pairs\n if (token.startsWith(\"--\") && !token.includes(\"=\") && i + 1 < tokens.length) {\n i++; // skip the value\n }\n }\n\n if (argIdx === -1) {\n // No positional arg found — inject current path for safe commands\n if (DEFAULT_PATH_COMMANDS.has(cmd)) {\n const currentFullPath = ctx.currentNamespace\n ? `@${ctx.currentNamespace}${ctx.currentPath}`\n : ctx.currentPath;\n tokens.push(currentFullPath);\n }\n return tokens;\n }\n\n const arg = tokens[argIdx]!;\n\n // Don't resolve if already absolute, canonical, or namespace-prefixed\n if (arg.startsWith(\"/\") || arg.startsWith(\"@\") || arg.startsWith(\"$afs\")) {\n return tokens;\n }\n\n // Resolve relative path\n const resolved = normalizePath(joinURL(ctx.currentPath, arg));\n\n // Add namespace prefix if in a non-default namespace\n const finalPath = ctx.currentNamespace ? `@${ctx.currentNamespace}${resolved}` : resolved;\n\n // Replace the token in the original input\n tokens[argIdx] = finalPath;\n return tokens;\n}\n\nfunction tokenize(input: string): string[] {\n const tokens: string[] = [];\n let current = \"\";\n let inQuote = false;\n let quoteChar = \"\";\n\n for (let i = 0; i < input.length; i++) {\n const char = input[i]!;\n if (inQuote) {\n if (char === quoteChar) {\n inQuote = false;\n } else {\n current += char;\n }\n } else if (char === '\"' || char === \"'\") {\n inQuote = true;\n quoteChar = char;\n } else if (char === \" \" || char === \"\\t\") {\n if (current) {\n tokens.push(current);\n current = \"\";\n }\n } else {\n current += char;\n }\n }\n if (current) tokens.push(current);\n return tokens;\n}\n\n// ─── Explore Detection ───────────────────────────────────────────────────\n\nexport function isExploreCommand(cmd: string): boolean {\n const normalized = cmd.replace(/^afs\\s+/, \"\").trim();\n return normalized === \"explore\" || normalized.startsWith(\"explore \");\n}\n\nfunction parseExplorePath(cmd: string, ctx: ReplContext): string {\n const normalized = cmd.replace(/^afs\\s+/, \"\").trim();\n const parts = normalized.split(/\\s+/);\n const path = parts[1];\n if (!path) {\n // Use current working directory\n if (ctx.currentNamespace) {\n return `$afs:${ctx.currentNamespace}${ctx.currentPath}`;\n }\n return ctx.currentPath;\n }\n // If relative path, resolve it\n if (!path.startsWith(\"/\") && !path.startsWith(\"@\") && !path.startsWith(\"$afs\")) {\n const resolved = normalizePath(joinURL(ctx.currentPath, path));\n if (ctx.currentNamespace) {\n return `$afs:${ctx.currentNamespace}${resolved}`;\n }\n return resolved;\n }\n return path;\n}\n\n// ─── Tab Completion ──────────────────────────────────────────────────────\n\nexport function createCompleter(ctx: ReplContext) {\n return function completer(\n line: string,\n callback: (err: Error | null, result: [string[], string]) => void,\n ): void {\n const trimmed = line.trimStart();\n const tokens = trimmed.split(/\\s+/);\n\n // Command completion: first token\n if (tokens.length <= 1) {\n const partial = tokens[0] || \"\";\n const matches = ALL_COMMANDS.filter((c) => c.startsWith(partial));\n callback(null, [matches, partial]);\n return;\n }\n\n // Path completion: after command name\n // Find the last token as the path being completed\n const lastToken = tokens[tokens.length - 1]!;\n\n // Don't complete option flags\n if (lastToken.startsWith(\"-\")) {\n callback(null, [[], lastToken]);\n return;\n }\n\n // Determine parent dir and prefix for completion\n let parentDir: string;\n let prefix: string;\n\n if (lastToken.includes(\"/\")) {\n const lastSlash = lastToken.lastIndexOf(\"/\");\n const parentPart = lastToken.slice(0, lastSlash + 1) || \"/\";\n prefix = lastToken.slice(lastSlash + 1);\n // Resolve parent relative to current path\n if (\n parentPart.startsWith(\"/\") ||\n parentPart.startsWith(\"@\") ||\n parentPart.startsWith(\"$afs\")\n ) {\n parentDir = parentPart;\n } else {\n parentDir = joinURL(ctx.currentPath, parentPart);\n }\n } else {\n parentDir = ctx.currentPath;\n prefix = lastToken;\n }\n\n // Add namespace if needed\n const queryPath =\n ctx.currentNamespace && !parentDir.startsWith(\"@\") && !parentDir.startsWith(\"$afs\")\n ? `$afs:${ctx.currentNamespace}${parentDir}`\n : parentDir;\n\n // Check cache\n const cacheKey = queryPath;\n const cached = ctx.completionCache.get(cacheKey);\n if (cached) {\n const completions = buildCompletions(cached, prefix, lastToken);\n callback(null, [completions, lastToken]);\n return;\n }\n\n // Query AFS\n ctx.afs\n .list(queryPath)\n .then((result) => {\n const entries = result.data || [];\n ctx.completionCache.set(cacheKey, entries);\n const completions = buildCompletions(entries, prefix, lastToken);\n callback(null, [completions, lastToken]);\n })\n .catch(() => {\n callback(null, [[], lastToken]);\n });\n };\n}\n\nfunction buildCompletions(entries: AFSEntry[], prefix: string, lastToken: string): string[] {\n const matches = entries.filter((e) => {\n const name = basename(e.path);\n return name.startsWith(prefix);\n });\n\n return matches.map((e) => {\n const name = basename(e.path);\n const isDir = typeof e.meta?.childrenCount === \"number\";\n const suffix = isDir ? \"/\" : \" \";\n // Build the completion to replace lastToken\n if (lastToken.includes(\"/\")) {\n const lastSlash = lastToken.lastIndexOf(\"/\");\n return lastToken.slice(0, lastSlash + 1) + name + suffix;\n }\n return name + suffix;\n });\n}\n\n// ─── Blessed Cleanup ─────────────────────────────────────────────────────\n\nfunction cleanupStdinAfterBlessed(): void {\n const stdin = process.stdin as NodeJS.ReadStream & {\n _blessedInput?: unknown;\n _keypressHandler?: unknown;\n _dataHandler?: unknown;\n _keypressDecoder?: unknown;\n _kpiListener?: unknown;\n _readableState?: { flowing: unknown; reading: boolean };\n setRawMode?: (mode: boolean) => void;\n };\n\n stdin.removeAllListeners(\"keypress\");\n stdin.removeAllListeners(\"data\");\n\n delete stdin._blessedInput;\n delete stdin._keypressHandler;\n delete stdin._dataHandler;\n delete stdin._keypressDecoder;\n delete stdin._kpiListener;\n\n if (stdin.setRawMode) {\n stdin.setRawMode(false);\n }\n\n if (stdin._readableState) {\n stdin._readableState.flowing = null;\n stdin._readableState.reading = false;\n }\n}\n\n// ─── Spinner ──────────────────────────────────────────────────────────────\n\nconst SPINNER_FRAMES = [\"⠋\", \"⠙\", \"⠹\", \"⠸\", \"⠼\", \"⠴\", \"⠦\", \"⠧\", \"⠇\", \"⠏\"];\nconst SPINNER_INTERVAL_MS = 80;\n\nfunction createSpinner() {\n let frameIdx = 0;\n let text = \"\";\n let timer: ReturnType<typeof setInterval> | null = null;\n\n return {\n start(initialText: string) {\n text = initialText;\n timer = setInterval(() => {\n const frame = SPINNER_FRAMES[frameIdx % SPINNER_FRAMES.length]!;\n process.stderr.write(`\\r${frame} ${text}`);\n frameIdx++;\n }, SPINNER_INTERVAL_MS);\n },\n update(newText: string) {\n text = newText;\n },\n stop() {\n if (timer) {\n clearInterval(timer);\n timer = null;\n }\n process.stderr.write(\"\\r\\x1b[K\");\n },\n };\n}\n\n// ─── Main REPL ───────────────────────────────────────────────────────────\n\nexport async function startRepl(options: {\n cwd: string;\n version: string;\n onExit?: () => Promise<void>;\n /** Pre-created providers to mount directly (e.g. mock-based providers that can't be recreated from URI) */\n extraProviders?: Array<{ provider: AFSModule; mountPath: string }>;\n}): Promise<void> {\n const { cwd, version, onExit, extraProviders } = options;\n\n // Load AFS with progress spinner\n const spinner = createSpinner();\n spinner.start(\"Mounting providers...\");\n\n let failures: MountFailure[] = [];\n const { afs, failures: mountFailures } = await loadAFS(cwd, {\n onProgress({ total, completed, failed }) {\n spinner.update(\n `Mounting providers... (${completed}/${total}${failed > 0 ? `, ${failed} failed` : \"\"})`,\n );\n },\n });\n failures = mountFailures;\n\n // Mount extra providers (mock-based or pre-created)\n if (extraProviders) {\n for (const { provider, mountPath } of extraProviders) {\n try {\n await afs.mount(provider, mountPath);\n } catch (e: unknown) {\n const msg = e instanceof Error ? e.message : String(e);\n failures.push({ path: mountPath, reason: msg });\n }\n }\n }\n\n spinner.stop();\n\n // Print failure warnings if any\n if (failures.length > 0) {\n const noun = failures.length === 1 ? \"mount\" : \"mounts\";\n console.warn(`⚠ ${failures.length} ${noun} failed:`);\n for (const f of failures) {\n console.warn(` - ${f.path}: ${f.reason}`);\n }\n }\n\n // Create executor\n const executor = new AFSCommandExecutor(afs, {\n cwd,\n tty: true,\n version,\n });\n\n const ctx = createReplContext({ executor, afs, version });\n\n // Print banner\n console.log(getBanner(ctx));\n\n let closed = false;\n let rl: Interface;\n let originalDataHandler: ((...args: unknown[]) => void) | null = null;\n\n function startReplLoop() {\n const stdin = process.stdin as NodeJS.ReadStream & {\n _readableState?: { flowing: unknown; reading: boolean };\n };\n\n if (stdin._readableState) {\n stdin._readableState.flowing = null;\n stdin._readableState.reading = false;\n }\n\n rl = createInterface({\n input: process.stdin,\n output: process.stdout,\n prompt: getPrompt(ctx),\n historySize: 100,\n completer: createCompleter(ctx),\n });\n\n // Save the original data handler from first successful createInterface\n if (!originalDataHandler && stdin.listenerCount(\"data\") > 0) {\n const listeners = stdin.listeners(\"data\") as ((...args: unknown[]) => void)[];\n originalDataHandler = listeners[0] ?? null;\n }\n\n // If createInterface failed to attach data listener, reattach the saved one\n if (stdin.listenerCount(\"data\") === 0 && originalDataHandler) {\n stdin.on(\"data\", originalDataHandler);\n stdin.resume();\n }\n\n rl.on(\"line\", async (line: string) => {\n const trimmed = line.trim();\n\n if (!trimmed) {\n if (!closed) rl.prompt();\n return;\n }\n\n // Check explore command (needs special handling for blessed TUI)\n if (isExploreCommand(trimmed)) {\n await handleExplore(trimmed, ctx);\n return;\n }\n\n // Check builtin commands\n const builtinResult = await handleBuiltinCommand(trimmed, ctx);\n if (builtinResult) {\n if (builtinResult.output) {\n console.log(builtinResult.output);\n }\n if (builtinResult.exit) {\n closed = true;\n rl.close();\n return;\n }\n if (!closed) {\n rl.setPrompt(getPrompt(ctx));\n rl.prompt();\n }\n return;\n }\n\n // Resolve relative paths in argv\n const resolved = resolveArgvPath(trimmed, ctx);\n\n // Execute via AFSCommandExecutor\n try {\n const result = await executor.execute(resolved);\n if (result.formatted) {\n console.log(result.formatted);\n }\n if (!result.success && result.error) {\n if (!result.formatted.includes(result.error.message)) {\n console.error(result.error.message);\n }\n }\n } catch (e: unknown) {\n const msg = e instanceof Error ? e.message : String(e);\n console.error(`Error: ${msg}`);\n }\n\n // Clear completion cache after each command\n ctx.completionCache.clear();\n\n if (!closed) rl.prompt();\n });\n\n rl.on(\"close\", () => {\n if (!closed) {\n closed = true;\n console.log(\"\\nBye!\");\n }\n });\n\n rl.prompt();\n }\n\n async function handleExplore(trimmed: string, ctx: ReplContext) {\n // Remove listeners before close\n rl.removeAllListeners(\"line\");\n rl.removeAllListeners(\"close\");\n rl.close();\n\n cleanupStdinAfterBlessed();\n\n try {\n const { createExplorerScreen } = await import(\"./explorer/screen.js\");\n const startPath = parseExplorePath(trimmed, ctx);\n await createExplorerScreen({\n afs: ctx.afs,\n startPath,\n version: ctx.version,\n onExit: () => {},\n });\n } catch {\n // ignore errors from explore\n }\n\n await new Promise((resolve) => setTimeout(resolve, 100));\n\n cleanupStdinAfterBlessed();\n\n process.stdout.write(\"\\x1b[?1049l\"); // Exit alternate screen\n process.stdout.write(\"\\x1b[?25h\"); // Show cursor\n\n console.log(\"\");\n\n if (!closed) {\n startReplLoop();\n }\n }\n\n startReplLoop();\n\n // Return promise that resolves when REPL exits\n return new Promise((resolve) => {\n const checkClosed = setInterval(() => {\n if (closed) {\n clearInterval(checkClosed);\n if (onExit) {\n onExit()\n .catch(() => {})\n .then(resolve);\n } else {\n resolve();\n }\n }\n }, 100);\n });\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"repl.mjs","names":["dirname","ns","resolved","ctx","resolve"],"sources":["../src/repl.ts"],"sourcesContent":["/**\n * AFS CLI Interactive REPL\n *\n * Single-file REPL implementation for `afs -i` / `afs --interactive`.\n * Provides: REPL loop, cd/pwd, Tab completion, explore TUI integration.\n */\n\nimport { basename } from \"node:path\";\nimport { createInterface, type Interface } from \"node:readline\";\nimport type { AFS, AFSEntry, AFSModule } from \"@aigne/afs\";\nimport { joinURL } from \"ufo\";\nimport { loadAFS, type MountFailure } from \"./config/afs-loader.js\";\nimport { AFSCommandExecutor } from \"./core/executor/index.js\";\nimport { createCLIAuthContext } from \"./credential/cli-auth-context.js\";\nimport { createCredentialStore } from \"./credential/store.js\";\n\n// ─── Types ───────────────────────────────────────────────────────────────\n\nexport interface ReplOptions {\n executor: AFSCommandExecutor;\n afs: AFS;\n version: string;\n}\n\nexport interface ReplContext {\n executor: AFSCommandExecutor;\n afs: AFS;\n version: string;\n currentPath: string;\n currentNamespace: string | null;\n completionCache: Map<string, AFSEntry[]>;\n}\n\nexport interface BuiltinResult {\n output?: string;\n exit?: boolean;\n}\n\n// ─── Constants ───────────────────────────────────────────────────────────\n\nconst BUILTIN_COMMANDS = [\"cd\", \"pwd\", \"help\", \"exit\", \"quit\"];\n\n/** Commands that default to current path when no path argument is given in REPL */\nconst DEFAULT_PATH_COMMANDS = new Set([\"ls\", \"read\", \"stat\", \"explain\", \"explore\"]);\n\nconst ALL_COMMANDS = [\n \"ls\",\n \"read\",\n \"write\",\n \"delete\",\n \"stat\",\n \"exec\",\n \"explain\",\n \"mount\",\n \"explore\",\n \"cd\",\n \"pwd\",\n \"help\",\n \"exit\",\n];\n\n// ─── Context ─────────────────────────────────────────────────────────────\n\nexport function createReplContext(options: ReplOptions): ReplContext {\n return {\n executor: options.executor,\n afs: options.afs,\n version: options.version,\n currentPath: \"/\",\n currentNamespace: null,\n completionCache: new Map(),\n };\n}\n\n// ─── Prompt ──────────────────────────────────────────────────────────────\n\nexport function getPrompt(ctx: ReplContext): string {\n const dirname = ctx.currentPath === \"/\" ? \"/\" : basename(ctx.currentPath);\n const nsPrefix = ctx.currentNamespace ? `${ctx.currentNamespace}:` : \"\";\n return `afs ${nsPrefix}${dirname}> `;\n}\n\n// ─── Banner ──────────────────────────────────────────────────────────────\n\nexport function getBanner(ctx: ReplContext): string {\n const mountCount = ctx.afs.getMounts().length;\n const plural = mountCount === 1 ? \"provider\" : \"providers\";\n return `AFS Interactive Shell v${ctx.version} — ${mountCount} ${plural} mounted. Type \"help\" for commands.`;\n}\n\n// ─── Builtin Commands ────────────────────────────────────────────────────\n\nexport function isBuiltinCommand(input: string): boolean {\n const trimmed = input.trim();\n if (!trimmed) return false;\n const cmd = trimmed.split(/\\s+/)[0]!;\n return BUILTIN_COMMANDS.includes(cmd);\n}\n\nexport async function handleBuiltinCommand(\n input: string,\n ctx: ReplContext,\n): Promise<BuiltinResult | null> {\n const trimmed = input.trim();\n if (!trimmed) return null;\n\n const parts = trimmed.split(/\\s+/);\n const cmd = parts[0]!;\n\n switch (cmd) {\n case \"exit\":\n case \"quit\":\n return { exit: true, output: \"Bye!\" };\n\n case \"help\":\n return { output: formatHelp() };\n\n case \"pwd\":\n return handlePwd(ctx);\n\n case \"cd\":\n return handleCd(parts.slice(1).join(\" \"), ctx);\n\n default:\n return null;\n }\n}\n\nfunction formatHelp(): string {\n const lines = [\n \"Available commands:\",\n \"\",\n \" AFS Commands:\",\n \" ls [path] List directory contents (default: current dir)\",\n \" read [path] Read file content (default: current node)\",\n \" write <path> [content] Write content to file\",\n \" delete <path> Delete file or directory\",\n \" stat [path] Get file or directory info (default: current node)\",\n \" exec <action> Execute an action\",\n \" explain [topic] Explain AFS concepts or paths (default: current path)\",\n \" mount Mount management\",\n \" explore [path] Interactive TUI explorer (default: current dir)\",\n \"\",\n \" REPL Commands:\",\n \" cd [path] Change working directory\",\n \" pwd Print working directory\",\n \" help Show this help\",\n \" exit / quit Exit REPL\",\n \"\",\n \" Tips:\",\n \" - Commands work with or without 'afs' prefix\",\n \" - Use Tab for command and path completion\",\n \" - Ctrl+D to exit\",\n ];\n return lines.join(\"\\n\");\n}\n\n// ─── cd / pwd ────────────────────────────────────────────────────────────\n\nfunction handlePwd(ctx: ReplContext): BuiltinResult {\n if (ctx.currentNamespace) {\n return { output: `@${ctx.currentNamespace}${ctx.currentPath}` };\n }\n return { output: ctx.currentPath };\n}\n\nasync function handleCd(target: string, ctx: ReplContext): Promise<BuiltinResult> {\n if (!target || target.trim() === \"\") {\n // cd with no args → go to root\n ctx.currentPath = \"/\";\n return {};\n }\n\n const trimmed = target.trim();\n\n // Handle @default → reset namespace\n if (trimmed === \"@default\") {\n ctx.currentNamespace = null;\n ctx.currentPath = \"/\";\n return {};\n }\n\n // Handle @namespace syntax\n if (trimmed.startsWith(\"@\")) {\n const rest = trimmed.slice(1);\n const slashIdx = rest.indexOf(\"/\");\n if (slashIdx === -1) {\n // @namespace only → switch to namespace root\n const ns = rest;\n if (!ns) return { output: \"cd: invalid namespace\" };\n // Validate by calling stat on namespace root\n try {\n const canonicalPath = `$afs:${ns}/`;\n await ctx.afs.stat(canonicalPath);\n ctx.currentNamespace = ns;\n ctx.currentPath = \"/\";\n return {};\n } catch {\n return { output: `cd: no such path: @${ns}/` };\n }\n }\n const ns = rest.slice(0, slashIdx);\n const path = rest.slice(slashIdx) || \"/\";\n if (!ns) return { output: \"cd: invalid namespace\" };\n try {\n const canonicalPath = `$afs:${ns}${path}`;\n await ctx.afs.stat(canonicalPath);\n ctx.currentNamespace = ns;\n ctx.currentPath = path;\n return {};\n } catch {\n return { output: `cd: no such path: @${ns}${path}` };\n }\n }\n\n // Handle absolute path\n if (trimmed.startsWith(\"/\")) {\n const resolved = normalizePath(trimmed);\n try {\n const canonicalPath = ctx.currentNamespace\n ? `$afs:${ctx.currentNamespace}${resolved}`\n : resolved;\n await ctx.afs.stat(canonicalPath);\n ctx.currentPath = resolved;\n return {};\n } catch {\n return { output: `cd: no such path: ${trimmed}` };\n }\n }\n\n // Handle relative path (including ..)\n const resolved = normalizePath(joinURL(ctx.currentPath, trimmed));\n try {\n const canonicalPath = ctx.currentNamespace\n ? `$afs:${ctx.currentNamespace}${resolved}`\n : resolved;\n await ctx.afs.stat(canonicalPath);\n ctx.currentPath = resolved;\n return {};\n } catch {\n return { output: `cd: no such path: ${trimmed}` };\n }\n}\n\nfunction normalizePath(path: string): string {\n // Split into segments, resolve .., remove empty segments\n const parts = path.split(\"/\").filter(Boolean);\n const resolved: string[] = [];\n for (const part of parts) {\n if (part === \"..\") {\n resolved.pop();\n } else if (part !== \".\") {\n resolved.push(part);\n }\n }\n return `/${resolved.join(\"/\")}`;\n}\n\n// ─── Argv Path Preprocessing ─────────────────────────────────────────────\n\nexport function resolveArgvPath(input: string, ctx: ReplContext): string[] {\n const tokens = tokenize(input);\n if (tokens.length === 0) return tokens;\n\n // Find the command name (skip optional 'afs' prefix)\n let cmdIdx = 0;\n if (tokens[0] === \"afs\" && tokens.length > 1) cmdIdx = 1;\n const cmd = tokens[cmdIdx];\n if (!cmd) return tokens;\n\n // Find first positional argument (non-option) after the command\n let argIdx = -1;\n for (let i = cmdIdx + 1; i < tokens.length; i++) {\n const token = tokens[i]!;\n if (!token.startsWith(\"-\")) {\n // For \"mount\" command, subcommands like \"list\", \"add\", \"remove\" are not paths\n if (\n cmd === \"mount\" &&\n (token === \"list\" || token === \"ls\" || token === \"add\" || token === \"remove\")\n ) {\n continue;\n }\n argIdx = i;\n break;\n }\n // Skip --option value pairs\n if (token.startsWith(\"--\") && !token.includes(\"=\") && i + 1 < tokens.length) {\n i++; // skip the value\n }\n }\n\n if (argIdx === -1) {\n // No positional arg found — inject current path for safe commands\n if (DEFAULT_PATH_COMMANDS.has(cmd)) {\n const currentFullPath = ctx.currentNamespace\n ? `@${ctx.currentNamespace}${ctx.currentPath}`\n : ctx.currentPath;\n tokens.push(currentFullPath);\n }\n return tokens;\n }\n\n const arg = tokens[argIdx]!;\n\n // Don't resolve if already absolute, canonical, or namespace-prefixed\n if (arg.startsWith(\"/\") || arg.startsWith(\"@\") || arg.startsWith(\"$afs\")) {\n return tokens;\n }\n\n // Resolve relative path\n const resolved = normalizePath(joinURL(ctx.currentPath, arg));\n\n // Add namespace prefix if in a non-default namespace\n const finalPath = ctx.currentNamespace ? `@${ctx.currentNamespace}${resolved}` : resolved;\n\n // Replace the token in the original input\n tokens[argIdx] = finalPath;\n return tokens;\n}\n\nfunction tokenize(input: string): string[] {\n const tokens: string[] = [];\n let current = \"\";\n let inQuote = false;\n let quoteChar = \"\";\n\n for (let i = 0; i < input.length; i++) {\n const char = input[i]!;\n if (inQuote) {\n if (char === quoteChar) {\n inQuote = false;\n } else {\n current += char;\n }\n } else if (char === '\"' || char === \"'\") {\n inQuote = true;\n quoteChar = char;\n } else if (char === \" \" || char === \"\\t\") {\n if (current) {\n tokens.push(current);\n current = \"\";\n }\n } else {\n current += char;\n }\n }\n if (current) tokens.push(current);\n return tokens;\n}\n\n// ─── Explore Detection ───────────────────────────────────────────────────\n\nexport function isExploreCommand(cmd: string): boolean {\n const normalized = cmd.replace(/^afs\\s+/, \"\").trim();\n return normalized === \"explore\" || normalized.startsWith(\"explore \");\n}\n\nfunction parseExplorePath(cmd: string, ctx: ReplContext): string {\n const normalized = cmd.replace(/^afs\\s+/, \"\").trim();\n const parts = normalized.split(/\\s+/);\n const path = parts[1];\n if (!path) {\n // Use current working directory\n if (ctx.currentNamespace) {\n return `$afs:${ctx.currentNamespace}${ctx.currentPath}`;\n }\n return ctx.currentPath;\n }\n // If relative path, resolve it\n if (!path.startsWith(\"/\") && !path.startsWith(\"@\") && !path.startsWith(\"$afs\")) {\n const resolved = normalizePath(joinURL(ctx.currentPath, path));\n if (ctx.currentNamespace) {\n return `$afs:${ctx.currentNamespace}${resolved}`;\n }\n return resolved;\n }\n return path;\n}\n\n// ─── Tab Completion ──────────────────────────────────────────────────────\n\nexport function createCompleter(ctx: ReplContext) {\n return function completer(\n line: string,\n callback: (err: Error | null, result: [string[], string]) => void,\n ): void {\n const trimmed = line.trimStart();\n const tokens = trimmed.split(/\\s+/);\n\n // Command completion: first token\n if (tokens.length <= 1) {\n const partial = tokens[0] || \"\";\n const matches = ALL_COMMANDS.filter((c) => c.startsWith(partial));\n callback(null, [matches, partial]);\n return;\n }\n\n // Path completion: after command name\n // Find the last token as the path being completed\n const lastToken = tokens[tokens.length - 1]!;\n\n // Don't complete option flags\n if (lastToken.startsWith(\"-\")) {\n callback(null, [[], lastToken]);\n return;\n }\n\n // Determine parent dir and prefix for completion\n let parentDir: string;\n let prefix: string;\n\n if (lastToken.includes(\"/\")) {\n const lastSlash = lastToken.lastIndexOf(\"/\");\n const parentPart = lastToken.slice(0, lastSlash + 1) || \"/\";\n prefix = lastToken.slice(lastSlash + 1);\n // Resolve parent relative to current path\n if (\n parentPart.startsWith(\"/\") ||\n parentPart.startsWith(\"@\") ||\n parentPart.startsWith(\"$afs\")\n ) {\n parentDir = parentPart;\n } else {\n parentDir = joinURL(ctx.currentPath, parentPart);\n }\n } else {\n parentDir = ctx.currentPath;\n prefix = lastToken;\n }\n\n // Add namespace if needed\n const queryPath =\n ctx.currentNamespace && !parentDir.startsWith(\"@\") && !parentDir.startsWith(\"$afs\")\n ? `$afs:${ctx.currentNamespace}${parentDir}`\n : parentDir;\n\n // Check cache\n const cacheKey = queryPath;\n const cached = ctx.completionCache.get(cacheKey);\n if (cached) {\n const completions = buildCompletions(cached, prefix, lastToken);\n callback(null, [completions, lastToken]);\n return;\n }\n\n // Query AFS\n ctx.afs\n .list(queryPath)\n .then((result) => {\n const entries = result.data || [];\n ctx.completionCache.set(cacheKey, entries);\n const completions = buildCompletions(entries, prefix, lastToken);\n callback(null, [completions, lastToken]);\n })\n .catch(() => {\n callback(null, [[], lastToken]);\n });\n };\n}\n\nfunction buildCompletions(entries: AFSEntry[], prefix: string, lastToken: string): string[] {\n const matches = entries.filter((e) => {\n const name = basename(e.path);\n return name.startsWith(prefix);\n });\n\n return matches.map((e) => {\n const name = basename(e.path);\n const isDir = typeof e.meta?.childrenCount === \"number\";\n const suffix = isDir ? \"/\" : \" \";\n // Build the completion to replace lastToken\n if (lastToken.includes(\"/\")) {\n const lastSlash = lastToken.lastIndexOf(\"/\");\n return lastToken.slice(0, lastSlash + 1) + name + suffix;\n }\n return name + suffix;\n });\n}\n\n// ─── Blessed Cleanup ─────────────────────────────────────────────────────\n\nfunction cleanupStdinAfterBlessed(): void {\n const stdin = process.stdin as NodeJS.ReadStream & {\n _blessedInput?: unknown;\n _keypressHandler?: unknown;\n _dataHandler?: unknown;\n _keypressDecoder?: unknown;\n _kpiListener?: unknown;\n _readableState?: { flowing: unknown; reading: boolean };\n setRawMode?: (mode: boolean) => void;\n };\n\n stdin.removeAllListeners(\"keypress\");\n stdin.removeAllListeners(\"data\");\n\n delete stdin._blessedInput;\n delete stdin._keypressHandler;\n delete stdin._dataHandler;\n delete stdin._keypressDecoder;\n delete stdin._kpiListener;\n\n if (stdin.setRawMode) {\n stdin.setRawMode(false);\n }\n\n if (stdin._readableState) {\n stdin._readableState.flowing = null;\n stdin._readableState.reading = false;\n }\n}\n\n// ─── Spinner ──────────────────────────────────────────────────────────────\n\nconst SPINNER_FRAMES = [\"⠋\", \"⠙\", \"⠹\", \"⠸\", \"⠼\", \"⠴\", \"⠦\", \"⠧\", \"⠇\", \"⠏\"];\nconst SPINNER_INTERVAL_MS = 80;\n\nfunction createSpinner() {\n let frameIdx = 0;\n let text = \"\";\n let timer: ReturnType<typeof setInterval> | null = null;\n\n return {\n start(initialText: string) {\n text = initialText;\n timer = setInterval(() => {\n const frame = SPINNER_FRAMES[frameIdx % SPINNER_FRAMES.length]!;\n process.stderr.write(`\\r${frame} ${text}`);\n frameIdx++;\n }, SPINNER_INTERVAL_MS);\n },\n update(newText: string) {\n text = newText;\n },\n stop() {\n if (timer) {\n clearInterval(timer);\n timer = null;\n }\n process.stderr.write(\"\\r\\x1b[K\");\n },\n };\n}\n\n// ─── Main REPL ───────────────────────────────────────────────────────────\n\nexport async function startRepl(options: {\n cwd: string;\n version: string;\n onExit?: () => Promise<void>;\n /** Pre-created providers to mount directly (e.g. mock-based providers that can't be recreated from URI) */\n extraProviders?: Array<{ provider: AFSModule; mountPath: string }>;\n}): Promise<void> {\n const { cwd, version, onExit, extraProviders } = options;\n\n // Load AFS with progress spinner\n const spinner = createSpinner();\n spinner.start(\"Mounting providers...\");\n\n let failures: MountFailure[] = [];\n const { afs, failures: mountFailures } = await loadAFS(cwd, {\n authContext: createCLIAuthContext(),\n credentialStore: createCredentialStore(),\n onProgress({ total, completed, failed }) {\n spinner.update(\n `Mounting providers... (${completed}/${total}${failed > 0 ? `, ${failed} failed` : \"\"})`,\n );\n },\n });\n failures = mountFailures;\n\n // Mount extra providers (mock-based or pre-created)\n if (extraProviders) {\n for (const { provider, mountPath } of extraProviders) {\n try {\n await afs.mount(provider, mountPath);\n } catch (e: unknown) {\n const msg = e instanceof Error ? e.message : String(e);\n failures.push({ path: mountPath, reason: msg });\n }\n }\n }\n\n spinner.stop();\n\n // Print failure warnings if any\n if (failures.length > 0) {\n const noun = failures.length === 1 ? \"mount\" : \"mounts\";\n console.warn(`⚠ ${failures.length} ${noun} failed:`);\n for (const f of failures) {\n console.warn(` - ${f.path}: ${f.reason}`);\n }\n }\n\n // Create executor\n const executor = new AFSCommandExecutor(afs, {\n cwd,\n tty: true,\n version,\n });\n\n const ctx = createReplContext({ executor, afs, version });\n\n // Print banner\n console.log(getBanner(ctx));\n\n let closed = false;\n let rl: Interface;\n let originalDataHandler: ((...args: unknown[]) => void) | null = null;\n\n function startReplLoop() {\n const stdin = process.stdin as NodeJS.ReadStream & {\n _readableState?: { flowing: unknown; reading: boolean };\n };\n\n if (stdin._readableState) {\n stdin._readableState.flowing = null;\n stdin._readableState.reading = false;\n }\n\n rl = createInterface({\n input: process.stdin,\n output: process.stdout,\n prompt: getPrompt(ctx),\n historySize: 100,\n completer: createCompleter(ctx),\n });\n\n // Save the original data handler from first successful createInterface\n if (!originalDataHandler && stdin.listenerCount(\"data\") > 0) {\n const listeners = stdin.listeners(\"data\") as ((...args: unknown[]) => void)[];\n originalDataHandler = listeners[0] ?? null;\n }\n\n // If createInterface failed to attach data listener, reattach the saved one\n if (stdin.listenerCount(\"data\") === 0 && originalDataHandler) {\n stdin.on(\"data\", originalDataHandler);\n stdin.resume();\n }\n\n rl.on(\"line\", async (line: string) => {\n const trimmed = line.trim();\n\n if (!trimmed) {\n if (!closed) rl.prompt();\n return;\n }\n\n // Check explore command (needs special handling for blessed TUI)\n if (isExploreCommand(trimmed)) {\n await handleExplore(trimmed, ctx);\n return;\n }\n\n // Check builtin commands\n const builtinResult = await handleBuiltinCommand(trimmed, ctx);\n if (builtinResult) {\n if (builtinResult.output) {\n console.log(builtinResult.output);\n }\n if (builtinResult.exit) {\n closed = true;\n rl.close();\n return;\n }\n if (!closed) {\n rl.setPrompt(getPrompt(ctx));\n rl.prompt();\n }\n return;\n }\n\n // Resolve relative paths in argv\n const resolved = resolveArgvPath(trimmed, ctx);\n\n // Execute via AFSCommandExecutor\n try {\n const result = await executor.execute(resolved);\n if (result.formatted) {\n console.log(result.formatted);\n }\n if (!result.success && result.error) {\n if (!result.formatted.includes(result.error.message)) {\n console.error(result.error.message);\n }\n }\n } catch (e: unknown) {\n const msg = e instanceof Error ? e.message : String(e);\n console.error(`Error: ${msg}`);\n }\n\n // Clear completion cache after each command\n ctx.completionCache.clear();\n\n if (!closed) rl.prompt();\n });\n\n rl.on(\"close\", () => {\n if (!closed) {\n closed = true;\n console.log(\"\\nBye!\");\n }\n });\n\n rl.prompt();\n }\n\n async function handleExplore(trimmed: string, ctx: ReplContext) {\n // Remove listeners before close\n rl.removeAllListeners(\"line\");\n rl.removeAllListeners(\"close\");\n rl.close();\n\n cleanupStdinAfterBlessed();\n\n try {\n const { createExplorerScreen } = await import(\"./explorer/screen.js\");\n const startPath = parseExplorePath(trimmed, ctx);\n await createExplorerScreen({\n afs: ctx.afs,\n startPath,\n version: ctx.version,\n onExit: () => {},\n });\n } catch {\n // ignore errors from explore\n }\n\n await new Promise((resolve) => setTimeout(resolve, 100));\n\n cleanupStdinAfterBlessed();\n\n process.stdout.write(\"\\x1b[?1049l\"); // Exit alternate screen\n process.stdout.write(\"\\x1b[?25h\"); // Show cursor\n\n console.log(\"\");\n\n if (!closed) {\n startReplLoop();\n }\n }\n\n startReplLoop();\n\n // Return promise that resolves when REPL exits\n return new Promise((resolve) => {\n const checkClosed = setInterval(() => {\n if (closed) {\n clearInterval(checkClosed);\n if (onExit) {\n onExit()\n .catch(() => {})\n .then(resolve);\n } else {\n resolve();\n }\n }\n }, 100);\n });\n}\n"],"mappings":"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"}