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

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 (157) hide show
  1. package/dist/cli.cjs +3 -2
  2. package/dist/cli.mjs +3 -2
  3. package/dist/cli.mjs.map +1 -1
  4. package/dist/config/afs-loader.cjs +36 -315
  5. package/dist/config/afs-loader.d.cts.map +1 -1
  6. package/dist/config/afs-loader.d.mts +2 -1
  7. package/dist/config/afs-loader.d.mts.map +1 -1
  8. package/dist/config/afs-loader.mjs +28 -307
  9. package/dist/config/afs-loader.mjs.map +1 -1
  10. package/dist/config/credential-helpers.cjs +303 -0
  11. package/dist/config/credential-helpers.d.mts +2 -0
  12. package/dist/config/credential-helpers.mjs +300 -0
  13. package/dist/config/credential-helpers.mjs.map +1 -0
  14. package/dist/config/loader.cjs +3 -1
  15. package/dist/config/loader.mjs +3 -2
  16. package/dist/config/loader.mjs.map +1 -1
  17. package/dist/config/program-install.cjs +450 -0
  18. package/dist/config/program-install.d.mts +1 -0
  19. package/dist/config/program-install.mjs +444 -0
  20. package/dist/config/program-install.mjs.map +1 -0
  21. package/dist/core/commands/connect.cjs +53 -0
  22. package/dist/core/commands/connect.d.mts +2 -0
  23. package/dist/core/commands/connect.mjs +55 -0
  24. package/dist/core/commands/connect.mjs.map +1 -0
  25. package/dist/core/commands/daemon.cjs +211 -0
  26. package/dist/core/commands/daemon.d.mts +2 -0
  27. package/dist/core/commands/daemon.mjs +212 -0
  28. package/dist/core/commands/daemon.mjs.map +1 -0
  29. package/dist/core/commands/explain.cjs +3 -1
  30. package/dist/core/commands/explain.mjs +3 -1
  31. package/dist/core/commands/explain.mjs.map +1 -1
  32. package/dist/core/commands/explore.cjs +47 -12
  33. package/dist/core/commands/explore.mjs +47 -12
  34. package/dist/core/commands/explore.mjs.map +1 -1
  35. package/dist/core/commands/gen-agent-md.cjs +126 -0
  36. package/dist/core/commands/gen-agent-md.d.mts +2 -0
  37. package/dist/core/commands/gen-agent-md.mjs +125 -0
  38. package/dist/core/commands/gen-agent-md.mjs.map +1 -0
  39. package/dist/core/commands/index.cjs +13 -1
  40. package/dist/core/commands/index.d.cts.map +1 -1
  41. package/dist/core/commands/index.d.mts +6 -0
  42. package/dist/core/commands/index.d.mts.map +1 -1
  43. package/dist/core/commands/index.mjs +13 -1
  44. package/dist/core/commands/index.mjs.map +1 -1
  45. package/dist/core/commands/install.cjs +139 -0
  46. package/dist/core/commands/install.d.mts +2 -0
  47. package/dist/core/commands/install.mjs +140 -0
  48. package/dist/core/commands/install.mjs.map +1 -0
  49. package/dist/core/commands/ls.cjs +14 -2
  50. package/dist/core/commands/ls.d.cts +2 -0
  51. package/dist/core/commands/ls.d.cts.map +1 -1
  52. package/dist/core/commands/ls.d.mts +2 -0
  53. package/dist/core/commands/ls.d.mts.map +1 -1
  54. package/dist/core/commands/ls.mjs +14 -2
  55. package/dist/core/commands/ls.mjs.map +1 -1
  56. package/dist/core/commands/mcp-bridge.cjs +201 -0
  57. package/dist/core/commands/mcp-bridge.d.mts +2 -0
  58. package/dist/core/commands/mcp-bridge.mjs +201 -0
  59. package/dist/core/commands/mcp-bridge.mjs.map +1 -0
  60. package/dist/core/commands/read.cjs +20 -7
  61. package/dist/core/commands/read.d.cts +2 -0
  62. package/dist/core/commands/read.d.cts.map +1 -1
  63. package/dist/core/commands/read.d.mts +2 -0
  64. package/dist/core/commands/read.d.mts.map +1 -1
  65. package/dist/core/commands/read.mjs +20 -7
  66. package/dist/core/commands/read.mjs.map +1 -1
  67. package/dist/core/commands/search.cjs +5 -1
  68. package/dist/core/commands/search.mjs +5 -1
  69. package/dist/core/commands/search.mjs.map +1 -1
  70. package/dist/core/commands/stat.mjs.map +1 -1
  71. package/dist/core/commands/types.d.cts +2 -0
  72. package/dist/core/commands/types.d.cts.map +1 -1
  73. package/dist/core/commands/types.d.mts +2 -0
  74. package/dist/core/commands/types.d.mts.map +1 -1
  75. package/dist/core/commands/types.mjs.map +1 -1
  76. package/dist/core/commands/vault.cjs +289 -0
  77. package/dist/core/commands/vault.d.mts +2 -0
  78. package/dist/core/commands/vault.mjs +289 -0
  79. package/dist/core/commands/vault.mjs.map +1 -0
  80. package/dist/core/commands/write.cjs +19 -6
  81. package/dist/core/commands/write.d.cts +2 -1
  82. package/dist/core/commands/write.d.cts.map +1 -1
  83. package/dist/core/commands/write.d.mts +2 -1
  84. package/dist/core/commands/write.d.mts.map +1 -1
  85. package/dist/core/commands/write.mjs +19 -6
  86. package/dist/core/commands/write.mjs.map +1 -1
  87. package/dist/core/executor/index.cjs +95 -19
  88. package/dist/core/executor/index.d.cts +4 -0
  89. package/dist/core/executor/index.d.cts.map +1 -1
  90. package/dist/core/executor/index.d.mts +4 -0
  91. package/dist/core/executor/index.d.mts.map +1 -1
  92. package/dist/core/executor/index.mjs +95 -19
  93. package/dist/core/executor/index.mjs.map +1 -1
  94. package/dist/core/formatters/index.d.mts +1 -0
  95. package/dist/core/formatters/install.cjs +40 -0
  96. package/dist/core/formatters/install.d.mts +1 -0
  97. package/dist/core/formatters/install.mjs +36 -0
  98. package/dist/core/formatters/install.mjs.map +1 -0
  99. package/dist/core/formatters/vault.cjs +36 -0
  100. package/dist/core/formatters/vault.mjs +32 -0
  101. package/dist/core/formatters/vault.mjs.map +1 -0
  102. package/dist/credential/auth-server.cjs +22 -4
  103. package/dist/credential/auth-server.mjs +22 -4
  104. package/dist/credential/auth-server.mjs.map +1 -1
  105. package/dist/credential/index.d.mts +2 -1
  106. package/dist/credential/mcp-auth-context.cjs +21 -5
  107. package/dist/credential/mcp-auth-context.mjs +21 -5
  108. package/dist/credential/mcp-auth-context.mjs.map +1 -1
  109. package/dist/credential/resolver.cjs +11 -3
  110. package/dist/credential/resolver.mjs +11 -3
  111. package/dist/credential/resolver.mjs.map +1 -1
  112. package/dist/credential/vault-store.d.mts +1 -0
  113. package/dist/daemon/config-manager.cjs +279 -0
  114. package/dist/daemon/config-manager.mjs +279 -0
  115. package/dist/daemon/config-manager.mjs.map +1 -0
  116. package/dist/daemon/manager.cjs +164 -0
  117. package/dist/daemon/manager.mjs +157 -0
  118. package/dist/daemon/manager.mjs.map +1 -0
  119. package/dist/daemon/server.cjs +220 -0
  120. package/dist/daemon/server.mjs +220 -0
  121. package/dist/daemon/server.mjs.map +1 -0
  122. package/dist/mcp/http-transport.cjs +14 -1
  123. package/dist/mcp/http-transport.mjs +14 -1
  124. package/dist/mcp/http-transport.mjs.map +1 -1
  125. package/dist/mcp/server.cjs +4 -2
  126. package/dist/mcp/server.mjs +4 -2
  127. package/dist/mcp/server.mjs.map +1 -1
  128. package/dist/mcp/tools.cjs +62 -12
  129. package/dist/mcp/tools.mjs +62 -12
  130. package/dist/mcp/tools.mjs.map +1 -1
  131. package/dist/program/daemon-integration.cjs +46 -0
  132. package/dist/program/daemon-integration.mjs +45 -0
  133. package/dist/program/daemon-integration.mjs.map +1 -0
  134. package/dist/program/program-manager.cjs +166 -0
  135. package/dist/program/program-manager.mjs +166 -0
  136. package/dist/program/program-manager.mjs.map +1 -0
  137. package/dist/program/trigger-scanner.cjs +148 -0
  138. package/dist/program/trigger-scanner.mjs +148 -0
  139. package/dist/program/trigger-scanner.mjs.map +1 -0
  140. package/dist/providers/vault/dist/_virtual/_@oxc-project_runtime@0.108.0/helpers/decorate.cjs +11 -0
  141. package/dist/providers/vault/dist/_virtual/_@oxc-project_runtime@0.108.0/helpers/decorate.mjs +11 -0
  142. package/dist/providers/vault/dist/_virtual/_@oxc-project_runtime@0.108.0/helpers/decorate.mjs.map +1 -0
  143. package/dist/providers/vault/dist/encrypted-file.cjs +158 -0
  144. package/dist/providers/vault/dist/encrypted-file.mjs +153 -0
  145. package/dist/providers/vault/dist/encrypted-file.mjs.map +1 -0
  146. package/dist/providers/vault/dist/index.cjs +405 -0
  147. package/dist/providers/vault/dist/index.mjs +400 -0
  148. package/dist/providers/vault/dist/index.mjs.map +1 -0
  149. package/dist/providers/vault/dist/key-resolver.cjs +181 -0
  150. package/dist/providers/vault/dist/key-resolver.mjs +180 -0
  151. package/dist/providers/vault/dist/key-resolver.mjs.map +1 -0
  152. package/dist/repl.cjs +109 -14
  153. package/dist/repl.d.cts.map +1 -1
  154. package/dist/repl.d.mts.map +1 -1
  155. package/dist/repl.mjs +109 -14
  156. package/dist/repl.mjs.map +1 -1
  157. package/package.json +27 -20
@@ -0,0 +1,220 @@
1
+ const require_rolldown_runtime = require('../_virtual/rolldown_runtime.cjs');
2
+ let node_crypto = require("node:crypto");
3
+ let node_path = require("node:path");
4
+ let _aigne_afs_http = require("@aigne/afs-http");
5
+ let _aigne_afs_explorer = require("@aigne/afs-explorer");
6
+ let _modelcontextprotocol_sdk_server_streamableHttp_js = require("@modelcontextprotocol/sdk/server/streamableHttp.js");
7
+
8
+ //#region src/daemon/server.ts
9
+ /**
10
+ * Daemon Server
11
+ *
12
+ * Unified HTTP server that serves:
13
+ * - Web Explorer UI (static files + WebSocket JSON-RPC)
14
+ * - HTTP REST API on /afs/* (backward compatible)
15
+ * - MCP Streamable HTTP on /mcp (NEW)
16
+ *
17
+ * Uses ExplorerWSServer with httpMiddleware for REST API and MCP routing.
18
+ */
19
+ /**
20
+ * Create an AFSModule wrapper around AFS for the REST API handler.
21
+ */
22
+ function createAFSModule(afs) {
23
+ return {
24
+ name: "afs-daemon",
25
+ accessMode: "readwrite",
26
+ async list(path, options) {
27
+ return afs.list(path, options);
28
+ },
29
+ async read(path, options) {
30
+ return afs.read(path, options);
31
+ },
32
+ async write(path, content, options) {
33
+ return afs.write(path, content, options);
34
+ },
35
+ async delete(path, options) {
36
+ return afs.delete(path, options);
37
+ },
38
+ async search(path, query, options) {
39
+ return afs.search(path, query, options);
40
+ },
41
+ async stat(path, options) {
42
+ return afs.stat(path, options);
43
+ },
44
+ async explain(path, options) {
45
+ return afs.explain(path, options);
46
+ },
47
+ async exec(path, args, options) {
48
+ return afs.exec(path, args, options);
49
+ },
50
+ async rename(oldPath, newPath, options) {
51
+ return afs.rename(oldPath, newPath, options);
52
+ }
53
+ };
54
+ }
55
+ /**
56
+ * Start the daemon server.
57
+ *
58
+ * Routes (via ExplorerWSServer with httpMiddleware):
59
+ * - GET/POST/DELETE /mcp → MCP Streamable HTTP
60
+ * - POST /afs/* → HTTP REST API
61
+ * - GET /api/read → AFS content proxy (handled by ExplorerWSServer)
62
+ * - WS /ws → JSON-RPC (handled by ExplorerWSServer)
63
+ * - GET /* → Explorer UI static files (handled by ExplorerWSServer)
64
+ */
65
+ async function startDaemonServer(options) {
66
+ const { afs, host = "localhost", apiPath = "/afs", configManager } = options;
67
+ const port = options.port || 4900;
68
+ let webRoot = options.webRoot;
69
+ if (!webRoot) try {
70
+ const { createRequire } = await import("node:module");
71
+ webRoot = (0, node_path.resolve)(createRequire(require("url").pathToFileURL(__filename).href).resolve("@aigne/afs-explorer/package.json"), "..", "web");
72
+ } catch {}
73
+ const restHandler = (0, _aigne_afs_http.createAFSHttpHandler)({ module: createAFSModule(afs) });
74
+ const { createMcpServerInstance, createAFSMcpServer } = await Promise.resolve().then(() => require("../mcp/server.cjs"));
75
+ /** Create a fresh McpServer wired to the AFS instance (one per session). */
76
+ const createSessionMcpServer = () => {
77
+ const srv = createMcpServerInstance();
78
+ createAFSMcpServer({
79
+ afs,
80
+ server: srv
81
+ });
82
+ return srv;
83
+ };
84
+ const mcpSessions = /* @__PURE__ */ new Map();
85
+ const logTs = () => (/* @__PURE__ */ new Date()).toISOString();
86
+ const httpMiddleware = async (req, res) => {
87
+ const url = new URL(req.url || "/", `http://${req.headers.host}`);
88
+ if (url.pathname === "/mcp") return handleMcpRequest(req, res, createSessionMcpServer, mcpSessions);
89
+ if (url.pathname.startsWith("/api/programs") && options.programManager) {
90
+ const { handleProgramsAPI } = await Promise.resolve().then(() => require("../program/daemon-integration.cjs"));
91
+ const headers$1 = new Headers();
92
+ for (const [key, value] of Object.entries(req.headers)) if (value) headers$1.append(key, Array.isArray(value) ? value.join(", ") : value);
93
+ const chunks$1 = [];
94
+ for await (const chunk of req) chunks$1.push(chunk);
95
+ const body$1 = Buffer.concat(chunks$1);
96
+ const request$1 = new Request(`http://${req.headers.host}${req.url}`, {
97
+ method: req.method,
98
+ headers: headers$1,
99
+ body: body$1.length > 0 ? body$1 : void 0
100
+ });
101
+ try {
102
+ const response = await handleProgramsAPI(request$1, options.programManager);
103
+ res.writeHead(response.status, { "Content-Type": response.headers.get("Content-Type") || "application/json" });
104
+ res.end(await response.text());
105
+ } catch (err) {
106
+ console.error(`[${logTs()}] Programs API error ${req.method} ${req.url}: ${err instanceof Error ? err.stack || err.message : err}`);
107
+ res.writeHead(500, { "Content-Type": "application/json" });
108
+ res.end(JSON.stringify({ error: "Internal server error" }));
109
+ }
110
+ return true;
111
+ }
112
+ if (!url.pathname.startsWith(apiPath)) return false;
113
+ const headers = new Headers();
114
+ for (const [key, value] of Object.entries(req.headers)) if (value) headers.append(key, Array.isArray(value) ? value.join(", ") : value);
115
+ const chunks = [];
116
+ for await (const chunk of req) chunks.push(chunk);
117
+ const body = Buffer.concat(chunks);
118
+ const request = new Request(`http://${req.headers.host}${req.url}`, {
119
+ method: req.method,
120
+ headers,
121
+ body: body.length > 0 ? body : void 0
122
+ });
123
+ try {
124
+ const response = await restHandler(request);
125
+ res.writeHead(response.status, { "Content-Type": response.headers.get("Content-Type") || "application/json" });
126
+ const responseBody = await response.text();
127
+ res.end(responseBody);
128
+ } catch (err) {
129
+ console.error(`[${logTs()}] REST error ${req.method} ${req.url}: ${err instanceof Error ? err.stack || err.message : err}`);
130
+ res.writeHead(500, { "Content-Type": "application/json" });
131
+ res.end(JSON.stringify({
132
+ code: 5,
133
+ error: "Internal server error"
134
+ }));
135
+ }
136
+ return true;
137
+ };
138
+ const explorerServer = new _aigne_afs_explorer.ExplorerWSServer(afs, {
139
+ port,
140
+ host,
141
+ webRoot,
142
+ configManager,
143
+ httpMiddleware
144
+ });
145
+ const info = await explorerServer.start();
146
+ return {
147
+ port: info.port,
148
+ url: info.url,
149
+ mcpUrl: `http://${host}:${info.port}/mcp`,
150
+ server: explorerServer,
151
+ stop: () => explorerServer.stop()
152
+ };
153
+ }
154
+ /**
155
+ * Handle MCP Streamable HTTP requests on /mcp.
156
+ *
157
+ * Each client session gets its own McpServer + StreamableHTTPServerTransport pair.
158
+ * The MCP SDK's McpServer only supports one transport at a time, so we create
159
+ * a dedicated instance per session rather than sharing one across connections.
160
+ */
161
+ async function handleMcpRequest(req, res, createMcpServer, sessions) {
162
+ res.setHeader("Access-Control-Allow-Origin", "*");
163
+ res.setHeader("Access-Control-Allow-Methods", "GET, POST, DELETE, OPTIONS");
164
+ res.setHeader("Access-Control-Allow-Headers", "Content-Type, mcp-session-id");
165
+ if (req.method === "OPTIONS") {
166
+ res.writeHead(204);
167
+ res.end();
168
+ return true;
169
+ }
170
+ if (req.method !== "GET" && req.method !== "POST" && req.method !== "DELETE") {
171
+ res.writeHead(405, { "Content-Type": "application/json" });
172
+ res.end(JSON.stringify({ error: "Method not allowed" }));
173
+ return true;
174
+ }
175
+ const sessionId = req.headers["mcp-session-id"];
176
+ let session;
177
+ if (sessionId) session = sessions.get(sessionId);
178
+ if (!session) {
179
+ const transport = new _modelcontextprotocol_sdk_server_streamableHttp_js.StreamableHTTPServerTransport({
180
+ sessionIdGenerator: () => (0, node_crypto.randomUUID)(),
181
+ onsessioninitialized: (sid) => {
182
+ sessions.set(sid, session);
183
+ }
184
+ });
185
+ transport.onclose = () => {
186
+ const sid = transport.sessionId;
187
+ if (sid) sessions.delete(sid);
188
+ };
189
+ const server = createMcpServer();
190
+ try {
191
+ await server.connect(transport);
192
+ } catch (err) {
193
+ const ts = (/* @__PURE__ */ new Date()).toISOString();
194
+ console.error(`[${ts}] MCP connect error: ${err instanceof Error ? err.stack || err.message : err}`);
195
+ if (!res.headersSent) {
196
+ res.writeHead(500, { "Content-Type": "application/json" });
197
+ res.end(JSON.stringify({ error: "MCP session initialization failed" }));
198
+ }
199
+ return true;
200
+ }
201
+ session = {
202
+ transport,
203
+ server
204
+ };
205
+ }
206
+ try {
207
+ await session.transport.handleRequest(req, res);
208
+ } catch (err) {
209
+ const ts = (/* @__PURE__ */ new Date()).toISOString();
210
+ console.error(`[${ts}] MCP error ${req.method} /mcp: ${err instanceof Error ? err.stack || err.message : err}`);
211
+ if (!res.headersSent) {
212
+ res.writeHead(500, { "Content-Type": "application/json" });
213
+ res.end(JSON.stringify({ error: "Internal server error" }));
214
+ }
215
+ }
216
+ return true;
217
+ }
218
+
219
+ //#endregion
220
+ exports.startDaemonServer = startDaemonServer;
@@ -0,0 +1,220 @@
1
+ import { randomUUID } from "node:crypto";
2
+ import { resolve } from "node:path";
3
+ import { createAFSHttpHandler } from "@aigne/afs-http";
4
+ import { ExplorerWSServer } from "@aigne/afs-explorer";
5
+ import { StreamableHTTPServerTransport } from "@modelcontextprotocol/sdk/server/streamableHttp.js";
6
+
7
+ //#region src/daemon/server.ts
8
+ /**
9
+ * Daemon Server
10
+ *
11
+ * Unified HTTP server that serves:
12
+ * - Web Explorer UI (static files + WebSocket JSON-RPC)
13
+ * - HTTP REST API on /afs/* (backward compatible)
14
+ * - MCP Streamable HTTP on /mcp (NEW)
15
+ *
16
+ * Uses ExplorerWSServer with httpMiddleware for REST API and MCP routing.
17
+ */
18
+ /**
19
+ * Create an AFSModule wrapper around AFS for the REST API handler.
20
+ */
21
+ function createAFSModule(afs) {
22
+ return {
23
+ name: "afs-daemon",
24
+ accessMode: "readwrite",
25
+ async list(path, options) {
26
+ return afs.list(path, options);
27
+ },
28
+ async read(path, options) {
29
+ return afs.read(path, options);
30
+ },
31
+ async write(path, content, options) {
32
+ return afs.write(path, content, options);
33
+ },
34
+ async delete(path, options) {
35
+ return afs.delete(path, options);
36
+ },
37
+ async search(path, query, options) {
38
+ return afs.search(path, query, options);
39
+ },
40
+ async stat(path, options) {
41
+ return afs.stat(path, options);
42
+ },
43
+ async explain(path, options) {
44
+ return afs.explain(path, options);
45
+ },
46
+ async exec(path, args, options) {
47
+ return afs.exec(path, args, options);
48
+ },
49
+ async rename(oldPath, newPath, options) {
50
+ return afs.rename(oldPath, newPath, options);
51
+ }
52
+ };
53
+ }
54
+ /**
55
+ * Start the daemon server.
56
+ *
57
+ * Routes (via ExplorerWSServer with httpMiddleware):
58
+ * - GET/POST/DELETE /mcp → MCP Streamable HTTP
59
+ * - POST /afs/* → HTTP REST API
60
+ * - GET /api/read → AFS content proxy (handled by ExplorerWSServer)
61
+ * - WS /ws → JSON-RPC (handled by ExplorerWSServer)
62
+ * - GET /* → Explorer UI static files (handled by ExplorerWSServer)
63
+ */
64
+ async function startDaemonServer(options) {
65
+ const { afs, host = "localhost", apiPath = "/afs", configManager } = options;
66
+ const port = options.port || 4900;
67
+ let webRoot = options.webRoot;
68
+ if (!webRoot) try {
69
+ const { createRequire } = await import("node:module");
70
+ webRoot = resolve(createRequire(import.meta.url).resolve("@aigne/afs-explorer/package.json"), "..", "web");
71
+ } catch {}
72
+ const restHandler = createAFSHttpHandler({ module: createAFSModule(afs) });
73
+ const { createMcpServerInstance, createAFSMcpServer } = await import("../mcp/server.mjs");
74
+ /** Create a fresh McpServer wired to the AFS instance (one per session). */
75
+ const createSessionMcpServer = () => {
76
+ const srv = createMcpServerInstance();
77
+ createAFSMcpServer({
78
+ afs,
79
+ server: srv
80
+ });
81
+ return srv;
82
+ };
83
+ const mcpSessions = /* @__PURE__ */ new Map();
84
+ const logTs = () => (/* @__PURE__ */ new Date()).toISOString();
85
+ const httpMiddleware = async (req, res) => {
86
+ const url = new URL(req.url || "/", `http://${req.headers.host}`);
87
+ if (url.pathname === "/mcp") return handleMcpRequest(req, res, createSessionMcpServer, mcpSessions);
88
+ if (url.pathname.startsWith("/api/programs") && options.programManager) {
89
+ const { handleProgramsAPI } = await import("../program/daemon-integration.mjs");
90
+ const headers$1 = new Headers();
91
+ for (const [key, value] of Object.entries(req.headers)) if (value) headers$1.append(key, Array.isArray(value) ? value.join(", ") : value);
92
+ const chunks$1 = [];
93
+ for await (const chunk of req) chunks$1.push(chunk);
94
+ const body$1 = Buffer.concat(chunks$1);
95
+ const request$1 = new Request(`http://${req.headers.host}${req.url}`, {
96
+ method: req.method,
97
+ headers: headers$1,
98
+ body: body$1.length > 0 ? body$1 : void 0
99
+ });
100
+ try {
101
+ const response = await handleProgramsAPI(request$1, options.programManager);
102
+ res.writeHead(response.status, { "Content-Type": response.headers.get("Content-Type") || "application/json" });
103
+ res.end(await response.text());
104
+ } catch (err) {
105
+ console.error(`[${logTs()}] Programs API error ${req.method} ${req.url}: ${err instanceof Error ? err.stack || err.message : err}`);
106
+ res.writeHead(500, { "Content-Type": "application/json" });
107
+ res.end(JSON.stringify({ error: "Internal server error" }));
108
+ }
109
+ return true;
110
+ }
111
+ if (!url.pathname.startsWith(apiPath)) return false;
112
+ const headers = new Headers();
113
+ for (const [key, value] of Object.entries(req.headers)) if (value) headers.append(key, Array.isArray(value) ? value.join(", ") : value);
114
+ const chunks = [];
115
+ for await (const chunk of req) chunks.push(chunk);
116
+ const body = Buffer.concat(chunks);
117
+ const request = new Request(`http://${req.headers.host}${req.url}`, {
118
+ method: req.method,
119
+ headers,
120
+ body: body.length > 0 ? body : void 0
121
+ });
122
+ try {
123
+ const response = await restHandler(request);
124
+ res.writeHead(response.status, { "Content-Type": response.headers.get("Content-Type") || "application/json" });
125
+ const responseBody = await response.text();
126
+ res.end(responseBody);
127
+ } catch (err) {
128
+ console.error(`[${logTs()}] REST error ${req.method} ${req.url}: ${err instanceof Error ? err.stack || err.message : err}`);
129
+ res.writeHead(500, { "Content-Type": "application/json" });
130
+ res.end(JSON.stringify({
131
+ code: 5,
132
+ error: "Internal server error"
133
+ }));
134
+ }
135
+ return true;
136
+ };
137
+ const explorerServer = new ExplorerWSServer(afs, {
138
+ port,
139
+ host,
140
+ webRoot,
141
+ configManager,
142
+ httpMiddleware
143
+ });
144
+ const info = await explorerServer.start();
145
+ return {
146
+ port: info.port,
147
+ url: info.url,
148
+ mcpUrl: `http://${host}:${info.port}/mcp`,
149
+ server: explorerServer,
150
+ stop: () => explorerServer.stop()
151
+ };
152
+ }
153
+ /**
154
+ * Handle MCP Streamable HTTP requests on /mcp.
155
+ *
156
+ * Each client session gets its own McpServer + StreamableHTTPServerTransport pair.
157
+ * The MCP SDK's McpServer only supports one transport at a time, so we create
158
+ * a dedicated instance per session rather than sharing one across connections.
159
+ */
160
+ async function handleMcpRequest(req, res, createMcpServer, sessions) {
161
+ res.setHeader("Access-Control-Allow-Origin", "*");
162
+ res.setHeader("Access-Control-Allow-Methods", "GET, POST, DELETE, OPTIONS");
163
+ res.setHeader("Access-Control-Allow-Headers", "Content-Type, mcp-session-id");
164
+ if (req.method === "OPTIONS") {
165
+ res.writeHead(204);
166
+ res.end();
167
+ return true;
168
+ }
169
+ if (req.method !== "GET" && req.method !== "POST" && req.method !== "DELETE") {
170
+ res.writeHead(405, { "Content-Type": "application/json" });
171
+ res.end(JSON.stringify({ error: "Method not allowed" }));
172
+ return true;
173
+ }
174
+ const sessionId = req.headers["mcp-session-id"];
175
+ let session;
176
+ if (sessionId) session = sessions.get(sessionId);
177
+ if (!session) {
178
+ const transport = new StreamableHTTPServerTransport({
179
+ sessionIdGenerator: () => randomUUID(),
180
+ onsessioninitialized: (sid) => {
181
+ sessions.set(sid, session);
182
+ }
183
+ });
184
+ transport.onclose = () => {
185
+ const sid = transport.sessionId;
186
+ if (sid) sessions.delete(sid);
187
+ };
188
+ const server = createMcpServer();
189
+ try {
190
+ await server.connect(transport);
191
+ } catch (err) {
192
+ const ts = (/* @__PURE__ */ new Date()).toISOString();
193
+ console.error(`[${ts}] MCP connect error: ${err instanceof Error ? err.stack || err.message : err}`);
194
+ if (!res.headersSent) {
195
+ res.writeHead(500, { "Content-Type": "application/json" });
196
+ res.end(JSON.stringify({ error: "MCP session initialization failed" }));
197
+ }
198
+ return true;
199
+ }
200
+ session = {
201
+ transport,
202
+ server
203
+ };
204
+ }
205
+ try {
206
+ await session.transport.handleRequest(req, res);
207
+ } catch (err) {
208
+ const ts = (/* @__PURE__ */ new Date()).toISOString();
209
+ console.error(`[${ts}] MCP error ${req.method} /mcp: ${err instanceof Error ? err.stack || err.message : err}`);
210
+ if (!res.headersSent) {
211
+ res.writeHead(500, { "Content-Type": "application/json" });
212
+ res.end(JSON.stringify({ error: "Internal server error" }));
213
+ }
214
+ }
215
+ return true;
216
+ }
217
+
218
+ //#endregion
219
+ export { startDaemonServer };
220
+ //# sourceMappingURL=server.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"server.mjs","names":["headers","chunks","body","request"],"sources":["../../src/daemon/server.ts"],"sourcesContent":["/**\n * Daemon Server\n *\n * Unified HTTP server that serves:\n * - Web Explorer UI (static files + WebSocket JSON-RPC)\n * - HTTP REST API on /afs/* (backward compatible)\n * - MCP Streamable HTTP on /mcp (NEW)\n *\n * Uses ExplorerWSServer with httpMiddleware for REST API and MCP routing.\n */\n\nimport { randomUUID } from \"node:crypto\";\nimport type { IncomingMessage, ServerResponse } from \"node:http\";\nimport { resolve } from \"node:path\";\nimport type { AFS, AFSModule } from \"@aigne/afs\";\nimport { type ConfigManager, ExplorerWSServer } from \"@aigne/afs-explorer\";\nimport { createAFSHttpHandler } from \"@aigne/afs-http\";\nimport type { McpServer } from \"@modelcontextprotocol/sdk/server/mcp.js\";\nimport { StreamableHTTPServerTransport } from \"@modelcontextprotocol/sdk/server/streamableHttp.js\";\n\nexport interface DaemonServerOptions {\n afs: AFS;\n port?: number;\n host?: string;\n /** REST API base path (default: \"/afs\") */\n apiPath?: string;\n /** Path to web/ directory for Explorer UI. */\n webRoot?: string;\n /** Optional ConfigManager for mount management RPCs. */\n configManager?: ConfigManager;\n /** Optional ProgramManager for /api/programs/* routes. */\n programManager?: import(\"../program/program-manager.js\").ProgramManager;\n}\n\nexport interface DaemonServerInfo {\n port: number;\n url: string;\n mcpUrl: string;\n server: ExplorerWSServer;\n stop: () => void;\n}\n\n/**\n * Create an AFSModule wrapper around AFS for the REST API handler.\n */\nfunction createAFSModule(afs: AFS): AFSModule {\n return {\n name: \"afs-daemon\",\n accessMode: \"readwrite\",\n async list(path, options) {\n return afs.list(path, options);\n },\n async read(path, options) {\n return afs.read(path, options);\n },\n async write(path, content, options) {\n return afs.write(path, content, options);\n },\n async delete(path, options) {\n return afs.delete(path, options);\n },\n async search(path, query, options) {\n return afs.search(path, query, options);\n },\n async stat(path, options) {\n return afs.stat(path, options);\n },\n async explain(path, options) {\n return afs.explain(path, options);\n },\n async exec(path, args, options) {\n return afs.exec(path, args, options);\n },\n async rename(oldPath, newPath, options) {\n return afs.rename(oldPath, newPath, options);\n },\n };\n}\n\n/**\n * Start the daemon server.\n *\n * Routes (via ExplorerWSServer with httpMiddleware):\n * - GET/POST/DELETE /mcp → MCP Streamable HTTP\n * - POST /afs/* → HTTP REST API\n * - GET /api/read → AFS content proxy (handled by ExplorerWSServer)\n * - WS /ws → JSON-RPC (handled by ExplorerWSServer)\n * - GET /* → Explorer UI static files (handled by ExplorerWSServer)\n */\nexport async function startDaemonServer(options: DaemonServerOptions): Promise<DaemonServerInfo> {\n const { afs, host = \"localhost\", apiPath = \"/afs\", configManager } = options;\n const port = options.port || 4900;\n\n // Resolve web root for Explorer assets\n let webRoot = options.webRoot;\n if (!webRoot) {\n try {\n const { createRequire } = await import(\"node:module\");\n const req = createRequire(import.meta.url);\n const explorerPkg = req.resolve(\"@aigne/afs-explorer/package.json\");\n webRoot = resolve(explorerPkg, \"..\", \"web\");\n } catch {\n // Fallback — embedded assets may be used\n }\n }\n\n // Create REST API handler for backward compatibility\n const module = createAFSModule(afs);\n const restHandler = createAFSHttpHandler({ module });\n\n // MCP: per-session server + transport\n const { createMcpServerInstance, createAFSMcpServer } = await import(\"../mcp/server.js\");\n\n /** Create a fresh McpServer wired to the AFS instance (one per session). */\n const createSessionMcpServer = () => {\n const srv = createMcpServerInstance();\n createAFSMcpServer({ afs, server: srv });\n return srv;\n };\n\n // Per-session state: transport + its dedicated McpServer\n const mcpSessions = new Map<\n string,\n { transport: StreamableHTTPServerTransport; server: McpServer }\n >();\n\n const logTs = () => new Date().toISOString();\n\n // HTTP middleware: intercept MCP, Programs API, and REST API requests before static file serving\n const httpMiddleware = async (req: IncomingMessage, res: ServerResponse): Promise<boolean> => {\n const url = new URL(req.url || \"/\", `http://${req.headers.host}`);\n\n // --- MCP Streamable HTTP on /mcp ---\n if (url.pathname === \"/mcp\") {\n return handleMcpRequest(req, res, createSessionMcpServer, mcpSessions);\n }\n\n // --- Programs API on /api/programs/* ---\n if (url.pathname.startsWith(\"/api/programs\") && options.programManager) {\n const { handleProgramsAPI } = await import(\"../program/daemon-integration.js\");\n const headers = new Headers();\n for (const [key, value] of Object.entries(req.headers)) {\n if (value) headers.append(key, Array.isArray(value) ? value.join(\", \") : value);\n }\n const chunks: Buffer[] = [];\n for await (const chunk of req) chunks.push(chunk as Buffer);\n const body = Buffer.concat(chunks);\n const request = new Request(`http://${req.headers.host}${req.url}`, {\n method: req.method,\n headers,\n body: body.length > 0 ? body : undefined,\n });\n try {\n const response = await handleProgramsAPI(request, options.programManager);\n res.writeHead(response.status, {\n \"Content-Type\": response.headers.get(\"Content-Type\") || \"application/json\",\n });\n res.end(await response.text());\n } catch (err) {\n console.error(\n `[${logTs()}] Programs API error ${req.method} ${req.url}: ${err instanceof Error ? err.stack || err.message : err}`,\n );\n res.writeHead(500, { \"Content-Type\": \"application/json\" });\n res.end(JSON.stringify({ error: \"Internal server error\" }));\n }\n return true;\n }\n\n // --- REST API on /afs/* ---\n if (!url.pathname.startsWith(apiPath)) return false;\n\n // Convert Node.js request to Web Standard Request\n const headers = new Headers();\n for (const [key, value] of Object.entries(req.headers)) {\n if (value) {\n headers.append(key, Array.isArray(value) ? value.join(\", \") : value);\n }\n }\n\n const chunks: Buffer[] = [];\n for await (const chunk of req) {\n chunks.push(chunk as Buffer);\n }\n const body = Buffer.concat(chunks);\n\n const request = new Request(`http://${req.headers.host}${req.url}`, {\n method: req.method,\n headers,\n body: body.length > 0 ? body : undefined,\n });\n\n try {\n const response = await restHandler(request);\n res.writeHead(response.status, {\n \"Content-Type\": response.headers.get(\"Content-Type\") || \"application/json\",\n });\n const responseBody = await response.text();\n res.end(responseBody);\n } catch (err) {\n console.error(\n `[${logTs()}] REST error ${req.method} ${req.url}: ${err instanceof Error ? err.stack || err.message : err}`,\n );\n res.writeHead(500, { \"Content-Type\": \"application/json\" });\n res.end(JSON.stringify({ code: 5, error: \"Internal server error\" }));\n }\n\n return true;\n };\n\n const explorerServer = new ExplorerWSServer(afs, {\n port,\n host,\n webRoot,\n configManager,\n httpMiddleware,\n });\n\n const info = await explorerServer.start();\n\n return {\n port: info.port,\n url: info.url,\n mcpUrl: `http://${host}:${info.port}/mcp`,\n server: explorerServer,\n stop: () => explorerServer.stop(),\n };\n}\n\ninterface McpSession {\n transport: StreamableHTTPServerTransport;\n server: McpServer;\n}\n\n/**\n * Handle MCP Streamable HTTP requests on /mcp.\n *\n * Each client session gets its own McpServer + StreamableHTTPServerTransport pair.\n * The MCP SDK's McpServer only supports one transport at a time, so we create\n * a dedicated instance per session rather than sharing one across connections.\n */\nasync function handleMcpRequest(\n req: IncomingMessage,\n res: ServerResponse,\n createMcpServer: () => McpServer,\n sessions: Map<string, McpSession>,\n): Promise<true> {\n // CORS\n res.setHeader(\"Access-Control-Allow-Origin\", \"*\");\n res.setHeader(\"Access-Control-Allow-Methods\", \"GET, POST, DELETE, OPTIONS\");\n res.setHeader(\"Access-Control-Allow-Headers\", \"Content-Type, mcp-session-id\");\n\n if (req.method === \"OPTIONS\") {\n res.writeHead(204);\n res.end();\n return true;\n }\n\n // Only GET, POST, DELETE are valid MCP methods\n if (req.method !== \"GET\" && req.method !== \"POST\" && req.method !== \"DELETE\") {\n res.writeHead(405, { \"Content-Type\": \"application/json\" });\n res.end(JSON.stringify({ error: \"Method not allowed\" }));\n return true;\n }\n\n // Look up existing session or create new one\n const sessionId = req.headers[\"mcp-session-id\"] as string | undefined;\n let session: McpSession | undefined;\n\n if (sessionId) {\n session = sessions.get(sessionId);\n }\n\n if (!session) {\n const transport = new StreamableHTTPServerTransport({\n sessionIdGenerator: () => randomUUID(),\n onsessioninitialized: (sid) => {\n sessions.set(sid, session!);\n },\n });\n\n transport.onclose = () => {\n const sid = transport.sessionId;\n if (sid) sessions.delete(sid);\n };\n\n const server = createMcpServer();\n\n try {\n await server.connect(transport);\n } catch (err) {\n const ts = new Date().toISOString();\n console.error(\n `[${ts}] MCP connect error: ${err instanceof Error ? err.stack || err.message : err}`,\n );\n if (!res.headersSent) {\n res.writeHead(500, { \"Content-Type\": \"application/json\" });\n res.end(JSON.stringify({ error: \"MCP session initialization failed\" }));\n }\n return true;\n }\n\n session = { transport, server };\n }\n\n try {\n await session.transport.handleRequest(req, res);\n } catch (err) {\n const ts = new Date().toISOString();\n console.error(\n `[${ts}] MCP error ${req.method} /mcp: ${err instanceof Error ? err.stack || err.message : err}`,\n );\n if (!res.headersSent) {\n res.writeHead(500, { \"Content-Type\": \"application/json\" });\n res.end(JSON.stringify({ error: \"Internal server error\" }));\n }\n }\n\n return true;\n}\n"],"mappings":"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"}
@@ -43,7 +43,20 @@ async function startMcpHttpServer(options) {
43
43
  }
44
44
  const sessionId = req.headers["mcp-session-id"];
45
45
  let transport;
46
- if (sessionId) transport = sessions.get(sessionId);
46
+ if (sessionId) {
47
+ transport = sessions.get(sessionId);
48
+ if (!transport) {
49
+ res.writeHead(404, { "Content-Type": "application/json" });
50
+ res.end(JSON.stringify({
51
+ jsonrpc: "2.0",
52
+ error: {
53
+ code: -32e3,
54
+ message: "Session not found"
55
+ }
56
+ }));
57
+ return;
58
+ }
59
+ }
47
60
  if (!transport) {
48
61
  transport = new _modelcontextprotocol_sdk_server_streamableHttp_js.StreamableHTTPServerTransport({
49
62
  sessionIdGenerator: () => (0, node_crypto.randomUUID)(),
@@ -42,7 +42,20 @@ async function startMcpHttpServer(options) {
42
42
  }
43
43
  const sessionId = req.headers["mcp-session-id"];
44
44
  let transport;
45
- if (sessionId) transport = sessions.get(sessionId);
45
+ if (sessionId) {
46
+ transport = sessions.get(sessionId);
47
+ if (!transport) {
48
+ res.writeHead(404, { "Content-Type": "application/json" });
49
+ res.end(JSON.stringify({
50
+ jsonrpc: "2.0",
51
+ error: {
52
+ code: -32e3,
53
+ message: "Session not found"
54
+ }
55
+ }));
56
+ return;
57
+ }
58
+ }
46
59
  if (!transport) {
47
60
  transport = new StreamableHTTPServerTransport({
48
61
  sessionIdGenerator: () => randomUUID(),
@@ -1 +1 @@
1
- {"version":3,"file":"http-transport.mjs","names":[],"sources":["../../src/mcp/http-transport.ts"],"sourcesContent":["/**\n * MCP HTTP Transport\n *\n * Starts an HTTP server that serves MCP over Streamable HTTP transport.\n * Creates a new transport per client session to support reconnection.\n */\n\nimport { randomUUID } from \"node:crypto\";\nimport type { IncomingMessage, Server, ServerResponse } from \"node:http\";\nimport { createServer } from \"node:http\";\nimport type { McpServer } from \"@modelcontextprotocol/sdk/server/mcp.js\";\nimport { StreamableHTTPServerTransport } from \"@modelcontextprotocol/sdk/server/streamableHttp.js\";\n\nexport interface StartMcpHttpServerOptions {\n mcpServer: McpServer;\n host: string;\n port: number;\n cors: boolean;\n}\n\nexport interface StartMcpHttpServerResult {\n httpServer: Server;\n port: number;\n url: string;\n}\n\n/**\n * Start MCP server over Streamable HTTP transport.\n *\n * Creates a new transport per client session so that reconnections\n * and multiple clients are supported.\n */\nexport async function startMcpHttpServer(\n options: StartMcpHttpServerOptions,\n): Promise<StartMcpHttpServerResult> {\n const { mcpServer, host, port, cors } = options;\n\n // Per-session transport map\n const sessions = new Map<string, StreamableHTTPServerTransport>();\n\n const httpServer = createServer(async (req: IncomingMessage, res: ServerResponse) => {\n // Strip version headers to avoid data leaks\n res.removeHeader(\"X-Powered-By\");\n\n // CORS handling\n if (cors) {\n res.setHeader(\"Access-Control-Allow-Origin\", \"*\");\n res.setHeader(\"Access-Control-Allow-Methods\", \"GET, POST, DELETE, OPTIONS\");\n res.setHeader(\"Access-Control-Allow-Headers\", \"Content-Type, mcp-session-id\");\n\n if (req.method === \"OPTIONS\") {\n res.writeHead(204);\n res.end();\n return;\n }\n }\n\n const url = new URL(req.url || \"/\", `http://${req.headers.host}`);\n\n // Only handle /mcp path\n if (url.pathname !== \"/mcp\") {\n res.writeHead(404, { \"Content-Type\": \"application/json\" });\n res.end(JSON.stringify({ error: \"Not found\" }));\n return;\n }\n\n // Only allow GET, POST, DELETE (MCP protocol methods)\n if (req.method !== \"GET\" && req.method !== \"POST\" && req.method !== \"DELETE\") {\n res.writeHead(405, { \"Content-Type\": \"application/json\" });\n res.end(JSON.stringify({ error: \"Method not allowed\" }));\n return;\n }\n\n // Look up existing session or create new one for initialization requests\n const sessionId = req.headers[\"mcp-session-id\"] as string | undefined;\n let transport: StreamableHTTPServerTransport | undefined;\n\n if (sessionId) {\n transport = sessions.get(sessionId);\n }\n\n if (!transport) {\n // New session — create a new transport\n transport = new StreamableHTTPServerTransport({\n sessionIdGenerator: () => randomUUID(),\n onsessioninitialized: (sid) => {\n sessions.set(sid, transport!);\n },\n });\n\n transport.onclose = () => {\n const sid = transport!.sessionId;\n if (sid) sessions.delete(sid);\n };\n\n await mcpServer.connect(transport);\n }\n\n try {\n await transport.handleRequest(req, res);\n } catch {\n if (!res.headersSent) {\n res.writeHead(500, { \"Content-Type\": \"application/json\" });\n res.end(JSON.stringify({ error: \"Internal server error\" }));\n }\n }\n });\n\n // Start listening\n const actualPort = await new Promise<number>((resolve, reject) => {\n httpServer.on(\"error\", reject);\n httpServer.listen(port, host, () => {\n const addr = httpServer.address();\n if (addr && typeof addr === \"object\") {\n resolve(addr.port);\n } else {\n resolve(port);\n }\n });\n });\n\n const url = `http://${host}:${actualPort}/mcp`;\n\n // Print listening info to stderr (stdout reserved for protocol)\n console.error(`MCP HTTP server listening on ${url}`);\n\n return { httpServer, port: actualPort, url };\n}\n"],"mappings":";;;;;;;;;;;;;;;;;AAgCA,eAAsB,mBACpB,SACmC;CACnC,MAAM,EAAE,WAAW,MAAM,MAAM,SAAS;CAGxC,MAAM,2BAAW,IAAI,KAA4C;CAEjE,MAAM,aAAa,aAAa,OAAO,KAAsB,QAAwB;AAEnF,MAAI,aAAa,eAAe;AAGhC,MAAI,MAAM;AACR,OAAI,UAAU,+BAA+B,IAAI;AACjD,OAAI,UAAU,gCAAgC,6BAA6B;AAC3E,OAAI,UAAU,gCAAgC,+BAA+B;AAE7E,OAAI,IAAI,WAAW,WAAW;AAC5B,QAAI,UAAU,IAAI;AAClB,QAAI,KAAK;AACT;;;AAOJ,MAHY,IAAI,IAAI,IAAI,OAAO,KAAK,UAAU,IAAI,QAAQ,OAAO,CAGzD,aAAa,QAAQ;AAC3B,OAAI,UAAU,KAAK,EAAE,gBAAgB,oBAAoB,CAAC;AAC1D,OAAI,IAAI,KAAK,UAAU,EAAE,OAAO,aAAa,CAAC,CAAC;AAC/C;;AAIF,MAAI,IAAI,WAAW,SAAS,IAAI,WAAW,UAAU,IAAI,WAAW,UAAU;AAC5E,OAAI,UAAU,KAAK,EAAE,gBAAgB,oBAAoB,CAAC;AAC1D,OAAI,IAAI,KAAK,UAAU,EAAE,OAAO,sBAAsB,CAAC,CAAC;AACxD;;EAIF,MAAM,YAAY,IAAI,QAAQ;EAC9B,IAAI;AAEJ,MAAI,UACF,aAAY,SAAS,IAAI,UAAU;AAGrC,MAAI,CAAC,WAAW;AAEd,eAAY,IAAI,8BAA8B;IAC5C,0BAA0B,YAAY;IACtC,uBAAuB,QAAQ;AAC7B,cAAS,IAAI,KAAK,UAAW;;IAEhC,CAAC;AAEF,aAAU,gBAAgB;IACxB,MAAM,MAAM,UAAW;AACvB,QAAI,IAAK,UAAS,OAAO,IAAI;;AAG/B,SAAM,UAAU,QAAQ,UAAU;;AAGpC,MAAI;AACF,SAAM,UAAU,cAAc,KAAK,IAAI;UACjC;AACN,OAAI,CAAC,IAAI,aAAa;AACpB,QAAI,UAAU,KAAK,EAAE,gBAAgB,oBAAoB,CAAC;AAC1D,QAAI,IAAI,KAAK,UAAU,EAAE,OAAO,yBAAyB,CAAC,CAAC;;;GAG/D;CAGF,MAAM,aAAa,MAAM,IAAI,SAAiB,SAAS,WAAW;AAChE,aAAW,GAAG,SAAS,OAAO;AAC9B,aAAW,OAAO,MAAM,YAAY;GAClC,MAAM,OAAO,WAAW,SAAS;AACjC,OAAI,QAAQ,OAAO,SAAS,SAC1B,SAAQ,KAAK,KAAK;OAElB,SAAQ,KAAK;IAEf;GACF;CAEF,MAAM,MAAM,UAAU,KAAK,GAAG,WAAW;AAGzC,SAAQ,MAAM,gCAAgC,MAAM;AAEpD,QAAO;EAAE;EAAY,MAAM;EAAY;EAAK"}
1
+ {"version":3,"file":"http-transport.mjs","names":[],"sources":["../../src/mcp/http-transport.ts"],"sourcesContent":["/**\n * MCP HTTP Transport\n *\n * Starts an HTTP server that serves MCP over Streamable HTTP transport.\n * Creates a new transport per client session to support reconnection.\n */\n\nimport { randomUUID } from \"node:crypto\";\nimport type { IncomingMessage, Server, ServerResponse } from \"node:http\";\nimport { createServer } from \"node:http\";\nimport type { McpServer } from \"@modelcontextprotocol/sdk/server/mcp.js\";\nimport { StreamableHTTPServerTransport } from \"@modelcontextprotocol/sdk/server/streamableHttp.js\";\n\nexport interface StartMcpHttpServerOptions {\n mcpServer: McpServer;\n host: string;\n port: number;\n cors: boolean;\n}\n\nexport interface StartMcpHttpServerResult {\n httpServer: Server;\n port: number;\n url: string;\n}\n\n/**\n * Start MCP server over Streamable HTTP transport.\n *\n * Creates a new transport per client session so that reconnections\n * and multiple clients are supported.\n */\nexport async function startMcpHttpServer(\n options: StartMcpHttpServerOptions,\n): Promise<StartMcpHttpServerResult> {\n const { mcpServer, host, port, cors } = options;\n\n // Per-session transport map\n const sessions = new Map<string, StreamableHTTPServerTransport>();\n\n const httpServer = createServer(async (req: IncomingMessage, res: ServerResponse) => {\n // Strip version headers to avoid data leaks\n res.removeHeader(\"X-Powered-By\");\n\n // CORS handling\n if (cors) {\n res.setHeader(\"Access-Control-Allow-Origin\", \"*\");\n res.setHeader(\"Access-Control-Allow-Methods\", \"GET, POST, DELETE, OPTIONS\");\n res.setHeader(\"Access-Control-Allow-Headers\", \"Content-Type, mcp-session-id\");\n\n if (req.method === \"OPTIONS\") {\n res.writeHead(204);\n res.end();\n return;\n }\n }\n\n const url = new URL(req.url || \"/\", `http://${req.headers.host}`);\n\n // Only handle /mcp path\n if (url.pathname !== \"/mcp\") {\n res.writeHead(404, { \"Content-Type\": \"application/json\" });\n res.end(JSON.stringify({ error: \"Not found\" }));\n return;\n }\n\n // Only allow GET, POST, DELETE (MCP protocol methods)\n if (req.method !== \"GET\" && req.method !== \"POST\" && req.method !== \"DELETE\") {\n res.writeHead(405, { \"Content-Type\": \"application/json\" });\n res.end(JSON.stringify({ error: \"Method not allowed\" }));\n return;\n }\n\n // Look up existing session or create new one for initialization requests\n const sessionId = req.headers[\"mcp-session-id\"] as string | undefined;\n let transport: StreamableHTTPServerTransport | undefined;\n\n if (sessionId) {\n transport = sessions.get(sessionId);\n if (!transport) {\n // Stale/unknown session — tell client to re-initialize (MCP spec)\n res.writeHead(404, { \"Content-Type\": \"application/json\" });\n res.end(\n JSON.stringify({ jsonrpc: \"2.0\", error: { code: -32000, message: \"Session not found\" } }),\n );\n return;\n }\n }\n\n if (!transport) {\n // No session ID new client, create a fresh transport\n transport = new StreamableHTTPServerTransport({\n sessionIdGenerator: () => randomUUID(),\n onsessioninitialized: (sid) => {\n sessions.set(sid, transport!);\n },\n });\n\n transport.onclose = () => {\n const sid = transport!.sessionId;\n if (sid) sessions.delete(sid);\n };\n\n await mcpServer.connect(transport);\n }\n\n try {\n await transport.handleRequest(req, res);\n } catch {\n if (!res.headersSent) {\n res.writeHead(500, { \"Content-Type\": \"application/json\" });\n res.end(JSON.stringify({ error: \"Internal server error\" }));\n }\n }\n });\n\n // Start listening\n const actualPort = await new Promise<number>((resolve, reject) => {\n httpServer.on(\"error\", reject);\n httpServer.listen(port, host, () => {\n const addr = httpServer.address();\n if (addr && typeof addr === \"object\") {\n resolve(addr.port);\n } else {\n resolve(port);\n }\n });\n });\n\n const url = `http://${host}:${actualPort}/mcp`;\n\n // Print listening info to stderr (stdout reserved for protocol)\n console.error(`MCP HTTP server listening on ${url}`);\n\n return { httpServer, port: actualPort, url };\n}\n"],"mappings":";;;;;;;;;;;;;;;;;AAgCA,eAAsB,mBACpB,SACmC;CACnC,MAAM,EAAE,WAAW,MAAM,MAAM,SAAS;CAGxC,MAAM,2BAAW,IAAI,KAA4C;CAEjE,MAAM,aAAa,aAAa,OAAO,KAAsB,QAAwB;AAEnF,MAAI,aAAa,eAAe;AAGhC,MAAI,MAAM;AACR,OAAI,UAAU,+BAA+B,IAAI;AACjD,OAAI,UAAU,gCAAgC,6BAA6B;AAC3E,OAAI,UAAU,gCAAgC,+BAA+B;AAE7E,OAAI,IAAI,WAAW,WAAW;AAC5B,QAAI,UAAU,IAAI;AAClB,QAAI,KAAK;AACT;;;AAOJ,MAHY,IAAI,IAAI,IAAI,OAAO,KAAK,UAAU,IAAI,QAAQ,OAAO,CAGzD,aAAa,QAAQ;AAC3B,OAAI,UAAU,KAAK,EAAE,gBAAgB,oBAAoB,CAAC;AAC1D,OAAI,IAAI,KAAK,UAAU,EAAE,OAAO,aAAa,CAAC,CAAC;AAC/C;;AAIF,MAAI,IAAI,WAAW,SAAS,IAAI,WAAW,UAAU,IAAI,WAAW,UAAU;AAC5E,OAAI,UAAU,KAAK,EAAE,gBAAgB,oBAAoB,CAAC;AAC1D,OAAI,IAAI,KAAK,UAAU,EAAE,OAAO,sBAAsB,CAAC,CAAC;AACxD;;EAIF,MAAM,YAAY,IAAI,QAAQ;EAC9B,IAAI;AAEJ,MAAI,WAAW;AACb,eAAY,SAAS,IAAI,UAAU;AACnC,OAAI,CAAC,WAAW;AAEd,QAAI,UAAU,KAAK,EAAE,gBAAgB,oBAAoB,CAAC;AAC1D,QAAI,IACF,KAAK,UAAU;KAAE,SAAS;KAAO,OAAO;MAAE,MAAM;MAAQ,SAAS;MAAqB;KAAE,CAAC,CAC1F;AACD;;;AAIJ,MAAI,CAAC,WAAW;AAEd,eAAY,IAAI,8BAA8B;IAC5C,0BAA0B,YAAY;IACtC,uBAAuB,QAAQ;AAC7B,cAAS,IAAI,KAAK,UAAW;;IAEhC,CAAC;AAEF,aAAU,gBAAgB;IACxB,MAAM,MAAM,UAAW;AACvB,QAAI,IAAK,UAAS,OAAO,IAAI;;AAG/B,SAAM,UAAU,QAAQ,UAAU;;AAGpC,MAAI;AACF,SAAM,UAAU,cAAc,KAAK,IAAI;UACjC;AACN,OAAI,CAAC,IAAI,aAAa;AACpB,QAAI,UAAU,KAAK,EAAE,gBAAgB,oBAAoB,CAAC;AAC1D,QAAI,IAAI,KAAK,UAAU,EAAE,OAAO,yBAAyB,CAAC,CAAC;;;GAG/D;CAGF,MAAM,aAAa,MAAM,IAAI,SAAiB,SAAS,WAAW;AAChE,aAAW,GAAG,SAAS,OAAO;AAC9B,aAAW,OAAO,MAAM,YAAY;GAClC,MAAM,OAAO,WAAW,SAAS;AACjC,OAAI,QAAQ,OAAO,SAAS,SAC1B,SAAQ,KAAK,KAAK;OAElB,SAAQ,KAAK;IAEf;GACF;CAEF,MAAM,MAAM,UAAU,KAAK,GAAG,WAAW;AAGzC,SAAQ,MAAM,gCAAgC,MAAM;AAEpD,QAAO;EAAE;EAAY,MAAM;EAAY;EAAK"}
@@ -17,7 +17,8 @@ function createMcpServerInstance() {
17
17
  }, { capabilities: {
18
18
  tools: {},
19
19
  resources: {},
20
- prompts: {}
20
+ prompts: {},
21
+ logging: {}
21
22
  } });
22
23
  }
23
24
  /**
@@ -26,7 +27,8 @@ function createMcpServerInstance() {
26
27
  */
27
28
  function buildInstructions(afs) {
28
29
  const lines = [];
29
- lines.push("AFS (Agentic File System) is a virtual filesystem that unifies different data sources into a single namespace.");
30
+ lines.push("AFS (Agentic File System) is a virtual filesystem developed by ArcBlock that gives AI agents a unified, path-based interface to any data source.");
31
+ lines.push("Inspired by Unix and Plan 9's \"everything is a file\", AFS extends the idea to \"everything is context\" — databases, APIs, smart home devices, and cloud services all become files and directories that agents can read, write, search, and act on.");
30
32
  lines.push("All data sources are accessed through a consistent path-based API with standard operations: read, list, stat, explain, search, write, delete, exec.");
31
33
  lines.push("");
32
34
  lines.push("## Mounted Providers");
@@ -16,7 +16,8 @@ function createMcpServerInstance() {
16
16
  }, { capabilities: {
17
17
  tools: {},
18
18
  resources: {},
19
- prompts: {}
19
+ prompts: {},
20
+ logging: {}
20
21
  } });
21
22
  }
22
23
  /**
@@ -25,7 +26,8 @@ function createMcpServerInstance() {
25
26
  */
26
27
  function buildInstructions(afs) {
27
28
  const lines = [];
28
- lines.push("AFS (Agentic File System) is a virtual filesystem that unifies different data sources into a single namespace.");
29
+ lines.push("AFS (Agentic File System) is a virtual filesystem developed by ArcBlock that gives AI agents a unified, path-based interface to any data source.");
30
+ lines.push("Inspired by Unix and Plan 9's \"everything is a file\", AFS extends the idea to \"everything is context\" — databases, APIs, smart home devices, and cloud services all become files and directories that agents can read, write, search, and act on.");
29
31
  lines.push("All data sources are accessed through a consistent path-based API with standard operations: read, list, stat, explain, search, write, delete, exec.");
30
32
  lines.push("");
31
33
  lines.push("## Mounted Providers");
@@ -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 /** 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 * Build MCP instructions string from AFS instance.\n * Includes base AFS description + dynamically mounted provider details.\n */\nfunction buildInstructions(afs: AFS): string {\n const lines: string[] = [];\n\n lines.push(\n \"AFS (Agentic File System) is a virtual filesystem that unifies different data sources into a single namespace.\",\n );\n lines.push(\n \"All data sources are accessed through a consistent path-based API with standard operations: read, list, stat, explain, search, write, delete, exec.\",\n );\n lines.push(\"\");\n lines.push(\"## Mounted Providers\");\n lines.push(\"\");\n\n const mounts = afs.getMounts(null);\n if (mounts.length === 0) {\n lines.push(\"No providers currently mounted.\");\n } else {\n for (const m of mounts) {\n const desc = m.module.description;\n lines.push(`### ${m.module.name} (${m.path})`);\n if (desc) {\n for (const line of desc.split(\"\\n\")) {\n lines.push(line);\n }\n }\n lines.push(\"\");\n }\n }\n\n lines.push(\"## Usage\");\n lines.push(\"\");\n lines.push(\"- Use `afs_list` to browse directories and discover content\");\n lines.push(\"- Use `afs_read` to read file/node content\");\n lines.push(\"- Use `afs_stat` to get metadata without content\");\n lines.push(\"- Use `afs_explain` to get documentation for any path\");\n lines.push(\"- Use `afs_search` to find content by pattern\");\n lines.push(\"- Use `afs_write` to create or update content\");\n lines.push(\"- Use `afs_delete` to remove files or nodes\");\n lines.push(\"- Use `afs_exec` to execute actions (e.g., `/.actions/mount`)\");\n lines.push(\"\");\n lines.push(\n \"Start with `afs_list /` to see all mounted providers, or `afs_explain /` for a complete overview.\",\n );\n\n return lines.join(\"\\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 // Set instructions dynamically — includes mounted provider info\n // Access the underlying Server's private _instructions field\n (server.server as unknown as Record<string, unknown>)._instructions = buildInstructions(afs);\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;;;;;;AAOH,SAAS,kBAAkB,KAAkB;CAC3C,MAAM,QAAkB,EAAE;AAE1B,OAAM,KACJ,iHACD;AACD,OAAM,KACJ,sJACD;AACD,OAAM,KAAK,GAAG;AACd,OAAM,KAAK,uBAAuB;AAClC,OAAM,KAAK,GAAG;CAEd,MAAM,SAAS,IAAI,UAAU,KAAK;AAClC,KAAI,OAAO,WAAW,EACpB,OAAM,KAAK,kCAAkC;KAE7C,MAAK,MAAM,KAAK,QAAQ;EACtB,MAAM,OAAO,EAAE,OAAO;AACtB,QAAM,KAAK,OAAO,EAAE,OAAO,KAAK,IAAI,EAAE,KAAK,GAAG;AAC9C,MAAI,KACF,MAAK,MAAM,QAAQ,KAAK,MAAM,KAAK,CACjC,OAAM,KAAK,KAAK;AAGpB,QAAM,KAAK,GAAG;;AAIlB,OAAM,KAAK,WAAW;AACtB,OAAM,KAAK,GAAG;AACd,OAAM,KAAK,8DAA8D;AACzE,OAAM,KAAK,6CAA6C;AACxD,OAAM,KAAK,mDAAmD;AAC9D,OAAM,KAAK,wDAAwD;AACnE,OAAM,KAAK,gDAAgD;AAC3D,OAAM,KAAK,gDAAgD;AAC3D,OAAM,KAAK,8CAA8C;AACzD,OAAM,KAAK,gEAAgE;AAC3E,OAAM,KAAK,GAAG;AACd,OAAM,KACJ,oGACD;AAED,QAAO,MAAM,KAAK,KAAK;;;;;;;;AASzB,SAAgB,mBAAmB,SAAwD;CACzF,MAAM,EAAE,QAAQ;CAChB,MAAM,SAAS,QAAQ,UAAU,yBAAyB;AAI1D,CAAC,OAAO,OAA8C,gBAAgB,kBAAkB,IAAI;AAE5F,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 logging: {},\n },\n },\n );\n}\n\n/**\n * Build MCP instructions string from AFS instance.\n * Includes base AFS description + dynamically mounted provider details.\n */\nfunction buildInstructions(afs: AFS): string {\n const lines: string[] = [];\n\n lines.push(\n \"AFS (Agentic File System) is a virtual filesystem developed by ArcBlock that gives AI agents a unified, path-based interface to any data source.\",\n );\n lines.push(\n 'Inspired by Unix and Plan 9\\'s \"everything is a file\", AFS extends the idea to \"everything is context\" — databases, APIs, smart home devices, and cloud services all become files and directories that agents can read, write, search, and act on.',\n );\n lines.push(\n \"All data sources are accessed through a consistent path-based API with standard operations: read, list, stat, explain, search, write, delete, exec.\",\n );\n lines.push(\"\");\n lines.push(\"## Mounted Providers\");\n lines.push(\"\");\n\n const mounts = afs.getMounts(null);\n if (mounts.length === 0) {\n lines.push(\"No providers currently mounted.\");\n } else {\n for (const m of mounts) {\n const desc = m.module.description;\n lines.push(`### ${m.module.name} (${m.path})`);\n if (desc) {\n for (const line of desc.split(\"\\n\")) {\n lines.push(line);\n }\n }\n lines.push(\"\");\n }\n }\n\n lines.push(\"## Usage\");\n lines.push(\"\");\n lines.push(\"- Use `afs_list` to browse directories and discover content\");\n lines.push(\"- Use `afs_read` to read file/node content\");\n lines.push(\"- Use `afs_stat` to get metadata without content\");\n lines.push(\"- Use `afs_explain` to get documentation for any path\");\n lines.push(\"- Use `afs_search` to find content by pattern\");\n lines.push(\"- Use `afs_write` to create or update content\");\n lines.push(\"- Use `afs_delete` to remove files or nodes\");\n lines.push(\"- Use `afs_exec` to execute actions (e.g., `/.actions/mount`)\");\n lines.push(\"\");\n lines.push(\n \"Start with `afs_list /` to see all mounted providers, or `afs_explain /` for a complete overview.\",\n );\n\n return lines.join(\"\\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 // Set instructions dynamically — includes mounted provider info\n // Access the underlying Server's private _instructions field\n (server.server as unknown as Record<string, unknown>)._instructions = buildInstructions(afs);\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;EACX,SAAS,EAAE;EACZ,EACF,CACF;;;;;;AAOH,SAAS,kBAAkB,KAAkB;CAC3C,MAAM,QAAkB,EAAE;AAE1B,OAAM,KACJ,mJACD;AACD,OAAM,KACJ,wPACD;AACD,OAAM,KACJ,sJACD;AACD,OAAM,KAAK,GAAG;AACd,OAAM,KAAK,uBAAuB;AAClC,OAAM,KAAK,GAAG;CAEd,MAAM,SAAS,IAAI,UAAU,KAAK;AAClC,KAAI,OAAO,WAAW,EACpB,OAAM,KAAK,kCAAkC;KAE7C,MAAK,MAAM,KAAK,QAAQ;EACtB,MAAM,OAAO,EAAE,OAAO;AACtB,QAAM,KAAK,OAAO,EAAE,OAAO,KAAK,IAAI,EAAE,KAAK,GAAG;AAC9C,MAAI,KACF,MAAK,MAAM,QAAQ,KAAK,MAAM,KAAK,CACjC,OAAM,KAAK,KAAK;AAGpB,QAAM,KAAK,GAAG;;AAIlB,OAAM,KAAK,WAAW;AACtB,OAAM,KAAK,GAAG;AACd,OAAM,KAAK,8DAA8D;AACzE,OAAM,KAAK,6CAA6C;AACxD,OAAM,KAAK,mDAAmD;AAC9D,OAAM,KAAK,wDAAwD;AACnE,OAAM,KAAK,gDAAgD;AAC3D,OAAM,KAAK,gDAAgD;AAC3D,OAAM,KAAK,8CAA8C;AACzD,OAAM,KAAK,gEAAgE;AAC3E,OAAM,KAAK,GAAG;AACd,OAAM,KACJ,oGACD;AAED,QAAO,MAAM,KAAK,KAAK;;;;;;;;AASzB,SAAgB,mBAAmB,SAAwD;CACzF,MAAM,EAAE,QAAQ;CAChB,MAAM,SAAS,QAAQ,UAAU,yBAAyB;AAI1D,CAAC,OAAO,OAA8C,gBAAgB,kBAAkB,IAAI;AAE5F,eAAc,QAAQ,IAAI;AAC1B,mBAAkB,QAAQ,IAAI;AAC9B,iBAAgB,QAAQ,IAAI;AAE5B,QAAO,EAAE,QAAQ"}