@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.
- package/dist/_virtual/rolldown_runtime.mjs +7 -0
- package/dist/config/afs-loader.cjs +466 -22
- package/dist/config/afs-loader.d.cts +6 -1
- package/dist/config/afs-loader.d.cts.map +1 -1
- package/dist/config/afs-loader.d.mts +6 -1
- package/dist/config/afs-loader.d.mts.map +1 -1
- package/dist/config/afs-loader.mjs +465 -22
- package/dist/config/afs-loader.mjs.map +1 -1
- package/dist/config/loader.cjs +28 -11
- package/dist/config/loader.mjs +28 -11
- package/dist/config/loader.mjs.map +1 -1
- package/dist/config/mount-commands.cjs +96 -46
- package/dist/config/mount-commands.d.cts +0 -6
- package/dist/config/mount-commands.d.cts.map +1 -1
- package/dist/config/mount-commands.d.mts +0 -6
- package/dist/config/mount-commands.d.mts.map +1 -1
- package/dist/config/mount-commands.mjs +93 -45
- package/dist/config/mount-commands.mjs.map +1 -1
- package/dist/config/schema.cjs +2 -2
- package/dist/config/schema.mjs +2 -2
- package/dist/config/schema.mjs.map +1 -1
- package/dist/core/commands/exec.cjs +6 -3
- package/dist/core/commands/exec.d.cts.map +1 -1
- package/dist/core/commands/exec.d.mts.map +1 -1
- package/dist/core/commands/exec.mjs +6 -3
- package/dist/core/commands/exec.mjs.map +1 -1
- package/dist/core/commands/explain.cjs +1 -1
- package/dist/core/commands/explain.mjs +1 -1
- package/dist/core/commands/mount.cjs +106 -23
- package/dist/core/commands/mount.d.cts +2 -0
- package/dist/core/commands/mount.d.cts.map +1 -1
- package/dist/core/commands/mount.d.mts +2 -0
- package/dist/core/commands/mount.d.mts.map +1 -1
- package/dist/core/commands/mount.mjs +106 -23
- package/dist/core/commands/mount.mjs.map +1 -1
- package/dist/core/commands/serve.cjs +38 -13
- package/dist/core/commands/serve.mjs +38 -13
- package/dist/core/commands/serve.mjs.map +1 -1
- package/dist/core/commands/types.cjs +6 -1
- package/dist/core/commands/types.d.cts.map +1 -1
- package/dist/core/commands/types.d.mts.map +1 -1
- package/dist/core/commands/types.mjs +6 -1
- package/dist/core/commands/types.mjs.map +1 -1
- package/dist/credential/auth-server.cjs +247 -0
- package/dist/credential/auth-server.mjs +247 -0
- package/dist/credential/auth-server.mjs.map +1 -0
- package/dist/credential/cli-auth-context.cjs +86 -0
- package/dist/credential/cli-auth-context.d.mts +1 -0
- package/dist/credential/cli-auth-context.mjs +86 -0
- package/dist/credential/cli-auth-context.mjs.map +1 -0
- package/dist/credential/index.cjs +5 -0
- package/dist/credential/index.d.mts +4 -0
- package/dist/credential/index.mjs +7 -0
- package/dist/credential/mcp-auth-context.cjs +186 -0
- package/dist/credential/mcp-auth-context.d.mts +1 -0
- package/dist/credential/mcp-auth-context.mjs +186 -0
- package/dist/credential/mcp-auth-context.mjs.map +1 -0
- package/dist/credential/resolver.cjs +125 -0
- package/dist/credential/resolver.d.mts +1 -0
- package/dist/credential/resolver.mjs +125 -0
- package/dist/credential/resolver.mjs.map +1 -0
- package/dist/credential/store.cjs +106 -0
- package/dist/credential/store.d.cts +30 -0
- package/dist/credential/store.d.cts.map +1 -0
- package/dist/credential/store.d.mts +30 -0
- package/dist/credential/store.d.mts.map +1 -0
- package/dist/credential/store.mjs +106 -0
- package/dist/credential/store.mjs.map +1 -0
- package/dist/index.cjs +3 -0
- package/dist/index.d.cts +2 -1
- package/dist/index.d.mts +3 -1
- package/dist/index.mjs +3 -1
- package/dist/mcp/http-transport.cjs +22 -3
- package/dist/mcp/http-transport.mjs +22 -3
- package/dist/mcp/http-transport.mjs.map +1 -1
- package/dist/mcp/prompts.cjs +2 -2
- package/dist/mcp/prompts.mjs +2 -2
- package/dist/mcp/prompts.mjs.map +1 -1
- package/dist/mcp/server.cjs +16 -6
- package/dist/mcp/server.mjs +15 -6
- package/dist/mcp/server.mjs.map +1 -1
- package/dist/mcp/tools.cjs +2 -46
- package/dist/mcp/tools.mjs +2 -46
- package/dist/mcp/tools.mjs.map +1 -1
- package/dist/repl.cjs +9 -3
- package/dist/repl.d.cts.map +1 -1
- package/dist/repl.d.mts.map +1 -1
- package/dist/repl.mjs +9 -3
- package/dist/repl.mjs.map +1 -1
- package/package.json +20 -20
package/dist/mcp/prompts.mjs
CHANGED
|
@@ -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
|
}
|
package/dist/mcp/prompts.mjs.map
CHANGED
|
@@ -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
|
|
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"}
|
package/dist/mcp/server.cjs
CHANGED
|
@@ -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
|
|
11
|
-
*
|
|
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
|
|
14
|
-
|
|
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;
|
package/dist/mcp/server.mjs
CHANGED
|
@@ -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
|
|
10
|
-
*
|
|
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
|
|
13
|
-
|
|
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
|
package/dist/mcp/server.mjs.map
CHANGED
|
@@ -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
|
|
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"}
|
package/dist/mcp/tools.cjs
CHANGED
|
@@ -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
|
package/dist/mcp/tools.mjs
CHANGED
|
@@ -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
|
package/dist/mcp/tools.mjs.map
CHANGED
|
@@ -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, {
|
|
354
|
-
|
|
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);
|
package/dist/repl.d.cts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"repl.d.cts","names":[],"sources":["../src/repl.ts"],"mappings":";;;
|
|
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"}
|
package/dist/repl.d.mts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"repl.d.mts","names":[],"sources":["../src/repl.ts"],"mappings":";;;;
|
|
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, {
|
|
353
|
-
|
|
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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"}
|