foliko 1.1.92 → 2.0.0

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 (212) hide show
  1. package/.claude/settings.local.json +2 -1
  2. package/CLAUDE.md +56 -30
  3. package/REFACTORING_PLAN.md +645 -0
  4. package/docs/architecture.md +131 -0
  5. package/docs/migration.md +57 -0
  6. package/docs/public-api.md +138 -0
  7. package/docs/usage.md +385 -0
  8. package/examples/ambient-example.js +20 -137
  9. package/examples/basic.js +21 -48
  10. package/examples/bootstrap.js +16 -74
  11. package/examples/mcp-example.js +6 -29
  12. package/examples/skill-example.js +6 -19
  13. package/examples/workflow.js +8 -56
  14. package/package.json +8 -4
  15. package/plugins/README.md +49 -0
  16. package/plugins/{ambient-agent → ambient}/EventWatcher.js +1 -1
  17. package/plugins/{ambient-agent → ambient}/ExplorerLoop.js +3 -3
  18. package/plugins/{ambient-agent → ambient}/GoalManager.js +2 -2
  19. package/plugins/ambient/README.md +14 -0
  20. package/plugins/{ambient-agent → ambient}/Reflector.js +1 -1
  21. package/plugins/{ambient-agent → ambient}/StateStore.js +1 -1
  22. package/plugins/{ambient-agent → ambient}/index.js +2 -2
  23. package/plugins/{ai-plugin.js → core/ai/index.js} +14 -30
  24. package/plugins/{audit-plugin.js → core/audit/index.js} +3 -30
  25. package/plugins/{coordinator-plugin.js → core/coordinator/index.js} +3 -35
  26. package/plugins/core/default/bootstrap.js +202 -0
  27. package/plugins/core/default/config.js +220 -0
  28. package/plugins/core/default/index.js +58 -0
  29. package/plugins/core/mcp/index.js +1 -0
  30. package/plugins/{python-plugin-loader.js → core/python-loader/index.js} +7 -187
  31. package/plugins/{rules-plugin.js → core/rules/index.js} +121 -64
  32. package/plugins/{scheduler-plugin.js → core/scheduler/index.js} +12 -114
  33. package/plugins/{session-plugin.js → core/session/index.js} +9 -73
  34. package/{src/capabilities/skill-manager.js → plugins/core/skill-manager/index.js} +64 -18
  35. package/plugins/{storage-plugin.js → core/storage/index.js} +5 -29
  36. package/plugins/{subagent-plugin.js → core/sub-agent/index.js} +10 -171
  37. package/plugins/{think-plugin.js → core/think/index.js} +24 -91
  38. package/{src/capabilities/workflow-engine.js → plugins/core/workflow/index.js} +87 -85
  39. package/plugins/default-plugins.js +6 -720
  40. package/plugins/{data-splitter-plugin.js → executors/data-splitter/index.js} +9 -83
  41. package/plugins/{extension-executor-plugin.js → executors/extension/index.js} +13 -97
  42. package/plugins/{python-executor-plugin.js → executors/python/index.js} +6 -31
  43. package/plugins/{shell-executor-plugin.js → executors/shell/index.js} +2 -5
  44. package/plugins/install/README.md +9 -0
  45. package/plugins/{install-plugin.js → install/index.js} +3 -3
  46. package/plugins/{file-system-plugin.js → io/file-system/index.js} +34 -236
  47. package/plugins/{web-plugin.js → io/web/index.js} +11 -113
  48. package/plugins/memory/README.md +13 -0
  49. package/plugins/{memory-plugin.js → memory/index.js} +4 -18
  50. package/plugins/messaging/email/README.md +19 -0
  51. package/plugins/{email → messaging/email}/index.js +2 -2
  52. package/plugins/{feishu-plugin.js → messaging/feishu/index.js} +3 -3
  53. package/plugins/{qq-plugin.js → messaging/qq/index.js} +5 -16
  54. package/plugins/{telegram-plugin.js → messaging/telegram/index.js} +3 -3
  55. package/plugins/{weixin-plugin.js → messaging/weixin/index.js} +15 -15
  56. package/plugins/{plugin-manager-plugin.js → plugin-manager/index.js} +36 -180
  57. package/plugins/{tools-plugin.js → tools/index.js} +68 -116
  58. package/plugins/trading/README.md +15 -0
  59. package/plugins/{gate-trading.js → trading/index.js} +8 -8
  60. package/{examples → sandbox}/test-concurrent-chat.js +2 -2
  61. package/{examples → sandbox}/test-long-chat.js +2 -2
  62. package/{examples → sandbox}/test-session-chat.js +2 -2
  63. package/{examples → sandbox}/test-web-plugin.js +1 -1
  64. package/{examples → sandbox}/test-weixin-feishu.js +2 -2
  65. package/src/agent/base.js +56 -0
  66. package/src/{core/agent-chat.js → agent/chat.js} +11 -11
  67. package/src/{core/coordinator-manager.js → agent/coordinator.js} +3 -3
  68. package/src/agent/index.js +111 -0
  69. package/src/agent/main.js +337 -0
  70. package/src/agent/prompt.js +78 -0
  71. package/src/agent/sub.js +198 -0
  72. package/src/agent/worker.js +104 -0
  73. package/{cli/bin/foliko.js → src/cli/bin.js} +1 -1
  74. package/{cli/src → src/cli}/commands/chat.js +25 -21
  75. package/{cli/src → src/cli}/index.js +1 -0
  76. package/{cli/src → src/cli}/ui/chat-ui-old.js +40 -178
  77. package/{cli/src → src/cli}/ui/chat-ui.js +3 -3
  78. package/{cli/src → src/cli}/ui/components/footer-bar.js +1 -1
  79. package/src/{core → common}/constants.js +3 -0
  80. package/src/common/errors.js +402 -0
  81. package/src/{utils → common}/logger.js +33 -0
  82. package/src/{utils/chat-queue.js → common/queue.js} +2 -2
  83. package/src/config/plugin-config.js +50 -0
  84. package/src/context/agent.js +32 -0
  85. package/src/context/compaction-prompts.js +170 -0
  86. package/src/context/compaction-utils.js +191 -0
  87. package/src/context/compressor.js +413 -0
  88. package/src/context/index.js +9 -0
  89. package/src/{core/context-manager.js → context/manager.js} +1 -1
  90. package/src/context/request.js +50 -0
  91. package/src/context/session.js +33 -0
  92. package/src/context/storage.js +30 -0
  93. package/src/executors/mcp-client.js +153 -0
  94. package/src/executors/mcp-desc.js +236 -0
  95. package/src/executors/mcp-executor.js +91 -956
  96. package/src/{core → framework}/command-registry.js +1 -1
  97. package/src/framework/framework.js +300 -0
  98. package/src/framework/index.js +18 -0
  99. package/src/framework/lifecycle.js +203 -0
  100. package/src/framework/loader.js +78 -0
  101. package/src/framework/registry.js +86 -0
  102. package/src/{core/ui-extension-context.js → framework/ui-extension.js} +1 -1
  103. package/src/index.js +130 -15
  104. package/src/llm/index.js +26 -0
  105. package/src/llm/provider.js +212 -0
  106. package/src/llm/registry.js +11 -0
  107. package/src/{core/token-counter.js → llm/tokens.js} +4 -37
  108. package/src/{core/plugin-base.js → plugin/base.js} +10 -136
  109. package/src/plugin/index.js +14 -0
  110. package/src/plugin/loader.js +101 -0
  111. package/src/plugin/manager.js +261 -0
  112. package/src/{core → session}/branch-summary-auto.js +2 -2
  113. package/src/{core/chat-session.js → session/chat.js} +2 -2
  114. package/src/session/index.js +7 -0
  115. package/src/{core/session-manager.js → session/session.js} +2 -2
  116. package/src/session/ttl.js +92 -0
  117. package/src/{core/jsonl-storage.js → storage/jsonl.js} +1 -1
  118. package/src/tool/executor.js +85 -0
  119. package/src/tool/index.js +15 -0
  120. package/src/tool/registry.js +143 -0
  121. package/src/{core/tool-router.js → tool/router.js} +17 -124
  122. package/src/tool/schema.js +108 -0
  123. package/src/utils/data-splitter.js +1 -1
  124. package/src/utils/download.js +1 -1
  125. package/src/utils/index.js +6 -6
  126. package/src/utils/message-validator.js +1 -1
  127. package/tests/core/context-storage.test.js +46 -0
  128. package/tests/core/llm.test.js +54 -0
  129. package/tests/core/plugin.test.js +42 -0
  130. package/tests/core/tool.test.js +60 -0
  131. package/tests/setup.js +10 -0
  132. package/tests/smoke.test.js +58 -0
  133. package/vitest.config.js +9 -0
  134. package/cli/src/daemon.js +0 -149
  135. package/docs/CONTEXT_DESIGN.md +0 -1596
  136. package/docs/ai-sdk-optimization.md +0 -655
  137. package/docs/features.md +0 -120
  138. package/docs/qq-bot.md +0 -976
  139. package/docs/quick-reference.md +0 -160
  140. package/docs/user-manual.md +0 -1391
  141. package/images/geometric_shapes.jpg +0 -0
  142. package/images/sunset_mountain_lake.jpg +0 -0
  143. package/skills/poster-guide/SKILL.md +0 -792
  144. package/src/capabilities/index.js +0 -11
  145. package/src/core/agent.js +0 -808
  146. package/src/core/context-compressor.js +0 -959
  147. package/src/core/enhanced-context-compressor.js +0 -210
  148. package/src/core/framework.js +0 -1422
  149. package/src/core/index.js +0 -30
  150. package/src/core/plugin-manager.js +0 -961
  151. package/src/core/provider-registry.js +0 -159
  152. package/src/core/provider.js +0 -156
  153. package/src/core/request-context.js +0 -98
  154. package/src/core/subagent.js +0 -442
  155. package/src/core/system-prompt-builder.js +0 -120
  156. package/src/core/tool-executor.js +0 -202
  157. package/src/core/tool-registry.js +0 -517
  158. package/src/core/worker-agent.js +0 -192
  159. package/src/executors/executor-base.js +0 -58
  160. package/src/utils/error-boundary.js +0 -363
  161. package/src/utils/error.js +0 -374
  162. package/system.md +0 -1645
  163. package/website_v2/README.md +0 -57
  164. package/website_v2/SPEC.md +0 -1
  165. package/website_v2/docs/api.html +0 -128
  166. package/website_v2/docs/configuration.html +0 -147
  167. package/website_v2/docs/plugin-development.html +0 -129
  168. package/website_v2/docs/project-structure.html +0 -89
  169. package/website_v2/docs/skill-development.html +0 -85
  170. package/website_v2/index.html +0 -489
  171. package/website_v2/scripts/main.js +0 -93
  172. package/website_v2/styles/animations.css +0 -8
  173. package/website_v2/styles/docs.css +0 -83
  174. package/website_v2/styles/main.css +0 -417
  175. package/xhs_auth.json +0 -268
  176. package//346/265/267/346/212/245/346/217/222/344/273/266.md +0 -621
  177. /package/plugins/{ambient-agent → ambient}/constants.js +0 -0
  178. /package/plugins/{email → messaging/email}/constants.js +0 -0
  179. /package/plugins/{email → messaging/email}/handlers.js +0 -0
  180. /package/plugins/{email → messaging/email}/monitor.js +0 -0
  181. /package/plugins/{email → messaging/email}/parser.js +0 -0
  182. /package/plugins/{email → messaging/email}/reply.js +0 -0
  183. /package/plugins/{email → messaging/email}/utils.js +0 -0
  184. /package/{examples → sandbox}/test-chat.js +0 -0
  185. /package/{examples → sandbox}/test-mcp.js +0 -0
  186. /package/{examples → sandbox}/test-reload.js +0 -0
  187. /package/{examples → sandbox}/test-telegram.js +0 -0
  188. /package/{examples → sandbox}/test-tg-bot.js +0 -0
  189. /package/{examples → sandbox}/test-tg-simple.js +0 -0
  190. /package/{examples → sandbox}/test-tg.js +0 -0
  191. /package/{examples → sandbox}/test-think.js +0 -0
  192. /package/src/{core/sub-agent-config.js → agent/sub-config.js} +0 -0
  193. /package/{cli/src → src/cli}/commands/daemon.js +0 -0
  194. /package/{cli/src → src/cli}/commands/list.js +0 -0
  195. /package/{cli/src → src/cli}/commands/plugin.js +0 -0
  196. /package/{cli/src → src/cli}/ui/components/agent-mention-provider.js +0 -0
  197. /package/{cli/src → src/cli}/ui/components/chained-autocomplete-provider.js +0 -0
  198. /package/{cli/src → src/cli}/ui/components/message-bubble.js +0 -0
  199. /package/{cli/src → src/cli}/ui/components/status-bar.js +0 -0
  200. /package/{cli/src → src/cli}/utils/ansi.js +0 -0
  201. /package/{cli/src → src/cli}/utils/config.js +0 -0
  202. /package/{cli/src → src/cli}/utils/markdown.js +0 -0
  203. /package/{cli/src → src/cli}/utils/plugin-config.js +0 -0
  204. /package/{cli/src → src/cli}/utils/render-diff.js +0 -0
  205. /package/src/{utils/circuit-breaker.js → common/circuit.js} +0 -0
  206. /package/src/{utils/edit-diff.js → common/diff.js} +0 -0
  207. /package/src/{utils/event-emitter.js → common/events.js} +0 -0
  208. /package/src/{utils → common}/id.js +0 -0
  209. /package/src/{utils → common}/retry.js +0 -0
  210. /package/src/{core/notification-manager.js → notification/manager.js} +0 -0
  211. /package/src/{core/session-entry.js → session/entry.js} +0 -0
  212. /package/src/{core/storage-manager.js → storage/manager.js} +0 -0
@@ -0,0 +1,33 @@
1
+ 'use strict';
2
+
3
+ /**
4
+ * SessionContext - 会话级上下文
5
+ * 管理会话范围内的状态和元数据
6
+ */
7
+
8
+ class SessionContext {
9
+ constructor(options = {}) {
10
+ this.sessionId = options.sessionId;
11
+ this.userId = options.userId || null;
12
+ this.metadata = options.metadata || {};
13
+ this.createdAt = Date.now();
14
+ this._data = new Map();
15
+ }
16
+
17
+ set(key, value) { this._data.set(key, value); }
18
+ get(key) { return this._data.get(key); }
19
+ has(key) { return this._data.has(key); }
20
+ delete(key) { return this._data.delete(key); }
21
+ clear() { this._data.clear(); }
22
+
23
+ toJSON() {
24
+ return {
25
+ sessionId: this.sessionId,
26
+ userId: this.userId,
27
+ metadata: this.metadata,
28
+ createdAt: this.createdAt,
29
+ };
30
+ }
31
+ }
32
+
33
+ module.exports = { SessionContext };
@@ -0,0 +1,30 @@
1
+ 'use strict';
2
+
3
+ /**
4
+ * ContextStorage - 集中管理所有 AsyncLocalStorage 实例
5
+ * 提供 Request / Session / Agent 三层上下文隔离
6
+ */
7
+
8
+ const { AsyncLocalStorage } = require('async_hooks');
9
+
10
+ class ContextStorage {
11
+ constructor() {
12
+ this._request = new AsyncLocalStorage();
13
+ this._session = new AsyncLocalStorage();
14
+ this._agent = new AsyncLocalStorage();
15
+ }
16
+
17
+ // === Request Context ===
18
+ runRequest(ctx, fn) { return this._request.run(ctx, fn); }
19
+ getRequest() { return this._request.getStore(); }
20
+
21
+ // === Session Context ===
22
+ runSession(ctx, fn) { return this._session.run(ctx, fn); }
23
+ getSession() { return this._session.getStore(); }
24
+
25
+ // === Agent Context ===
26
+ runAgent(ctx, fn) { return this._agent.run(ctx, fn); }
27
+ getAgent() { return this._agent.getStore(); }
28
+ }
29
+
30
+ module.exports = new ContextStorage();
@@ -0,0 +1,153 @@
1
+ 'use strict';
2
+
3
+ /**
4
+ * MCP Client Wrapper
5
+ * 管理 MCP 服务器连接和工具调用
6
+ */
7
+
8
+ const { spawn } = require('child_process');
9
+ const fs = require('fs');
10
+ const path = require('path');
11
+ const { createMCPClient } = require('@ai-sdk/mcp');
12
+ const { StdioClientTransport } = require('@modelcontextprotocol/sdk/client/stdio.js');
13
+ const { logger } = require('../common/logger');
14
+
15
+ const log = logger.child('MCPClient');
16
+
17
+ class MCPClientWrapper {
18
+ constructor(options = {}) {
19
+ this.serverName = options.serverName || 'unknown';
20
+ this.command = options.command;
21
+ this.args = options.args || [];
22
+ this.env = options.env || {};
23
+ this.timeout = options.timeout || 30000;
24
+ this.priority = 11;
25
+ this.client = null;
26
+ this.connected = false;
27
+ this.tools = [];
28
+ }
29
+
30
+ async connect() {
31
+ if (this.connected) return;
32
+
33
+ const controller = new AbortController();
34
+ const timeoutMs = this.timeout || 30000;
35
+ const timeoutId = setTimeout(() => controller.abort(), timeoutMs);
36
+
37
+ try {
38
+ let createMCPClientFn;
39
+ try {
40
+ const mcpModule = require('@ai-sdk/mcp');
41
+ createMCPClientFn = mcpModule.createMCPClient || mcpModule.experimental_createMCPClient;
42
+ } catch (err) {
43
+ throw new Error('@ai-sdk/mcp not installed. Please run: npm install @ai-sdk/mcp');
44
+ }
45
+
46
+ if (!createMCPClientFn) {
47
+ throw new Error('createMCPClient not available');
48
+ }
49
+
50
+ const { which, shellResolve } = this._resolveCommand(this.command);
51
+
52
+ try {
53
+ const { Experimental_StdioMCPTransport } = require('@ai-sdk/mcp/mcp-stdio');
54
+ const transport = new Experimental_StdioMCPTransport({
55
+ command: which || this.command,
56
+ args: shellResolve ? [shellResolve, ...this.args] : this.args,
57
+ env: { ...process.env, ...this.env },
58
+ });
59
+ this.client = await createMCPClientFn({
60
+ transport,
61
+ signal: controller.signal,
62
+ });
63
+ } catch (transportErr) {
64
+ log.error(` Transport error:`, transportErr.message);
65
+ throw transportErr;
66
+ }
67
+
68
+ if (typeof this.client.tools === 'function') {
69
+ const toolsObject = await this.client.tools();
70
+ this.tools = Object.entries(toolsObject)
71
+ .filter(([, tool]) => tool != null)
72
+ .map(([name, tool]) => ({ ...tool, name }));
73
+ } else if (this.client.tools && typeof this.client.tools.list === 'function') {
74
+ const listResult = await this.client.tools.list();
75
+ this.tools = listResult.tools || [];
76
+ }
77
+
78
+ this.connected = true;
79
+ } catch (err) {
80
+ if (controller.signal.aborted) {
81
+ log.error(` Connection timeout (${timeoutMs}ms) for ${this.serverName}`);
82
+ throw new Error(`连接超时 (${timeoutMs}ms): ${this.serverName}`);
83
+ }
84
+ log.error(` Failed to connect to ${this.serverName}:`, err.message);
85
+ } finally {
86
+ clearTimeout(timeoutId);
87
+ }
88
+ }
89
+
90
+ _resolveCommand(cmd) {
91
+ const isWindows = process.platform === 'win32';
92
+ const ext = isWindows ? '.cmd' : '';
93
+ const withExt = cmd + ext;
94
+
95
+ try {
96
+ const result = spawn.sync(withExt, ['--version'], {
97
+ stdio: 'ignore',
98
+ timeout: 2000,
99
+ });
100
+ if (result.error?.code !== 'ENOENT') {
101
+ return { which: withExt, shellResolve: null };
102
+ }
103
+ } catch (e) { /* ignore */ }
104
+
105
+ const globalCommands = isWindows
106
+ ? [
107
+ path.join(process.env.APPDATA || '', 'npm', withExt),
108
+ path.join(process.env.PROGRAMDATA || 'C:\\ProgramData', 'npm', withExt),
109
+ ]
110
+ : ['/usr/local/bin/' + cmd, '/usr/bin/' + cmd];
111
+
112
+ for (const cmdPath of globalCommands) {
113
+ try {
114
+ if (fs.existsSync(cmdPath)) {
115
+ return { which: cmdPath, shellResolve: null };
116
+ }
117
+ } catch (e) { /* ignore */ }
118
+ }
119
+
120
+ return { which: cmd, shellResolve: null };
121
+ }
122
+
123
+ async callTool(toolName, args) {
124
+ if (!this.connected) await this.connect();
125
+ if (!this.client) throw new Error('MCP client not initialized');
126
+
127
+ let toolsObject;
128
+ if (typeof this.client.tools === 'function') {
129
+ toolsObject = await this.client.tools();
130
+ } else if (this.client.tools && typeof this.client.tools.list === 'function') {
131
+ const listResult = await this.client.tools.list();
132
+ toolsObject = listResult.tools || {};
133
+ } else {
134
+ throw new Error('Cannot get tools from MCP client');
135
+ }
136
+
137
+ const tool = toolsObject[toolName];
138
+ if (!tool) throw new Error(`Tool not found: ${toolName}`);
139
+
140
+ return await tool.execute(args);
141
+ }
142
+
143
+ async disconnect() {
144
+ if (this.client) {
145
+ try { await this.client.destroy(); } catch (e) { /* ignore */ }
146
+ this.client = null;
147
+ this.connected = false;
148
+ this.tools = [];
149
+ }
150
+ }
151
+ }
152
+
153
+ module.exports = { MCPClientWrapper };
@@ -0,0 +1,236 @@
1
+ 'use strict';
2
+
3
+ /**
4
+ * MCP 描述构建和 Schema 格式化
5
+ * 为 Agent 系统提示词构建 MCP 工具描述,及参数 schema 的 Markdown 文档生成
6
+ */
7
+
8
+ const { z } = require('zod');
9
+ const { zodSchemaToMarkdown } = require('@chnak/zod-to-markdown');
10
+ const { jsonSchemaToZod } = require('../tool/schema');
11
+ const { logger } = require('../common/logger');
12
+
13
+ const log = logger.child('MCPDesc');
14
+
15
+ /**
16
+ * 从 inputSchema 中提取实际 schema
17
+ */
18
+ function extractSchema(inputSchema) {
19
+ if (!inputSchema) return {};
20
+ if (inputSchema.jsonSchema) return inputSchema.jsonSchema;
21
+ if (inputSchema.inputSchema) return inputSchema.inputSchema;
22
+ return inputSchema;
23
+ }
24
+
25
+ /**
26
+ * 构建 MCP 服务器描述文本
27
+ * @param {Map} clients - MCP 客户端 Map
28
+ */
29
+ function buildMCPServersDescription(clients) {
30
+ if (clients.size === 0) return '';
31
+
32
+ let desc = '## 【MCP Servers】 工具合集\n\n';
33
+ desc += 'MCP 服务器工具已注册为 `服务器_工具名` 格式(如 github_search)。\n';
34
+ desc += '通过 `ext_call` 调用:\n\n';
35
+
36
+ for (const [serverName, info] of clients) {
37
+ desc += `### ${serverName}\n\n`;
38
+ for (const tool of info.tools) {
39
+ const fullName = `${serverName}_${tool.name}`;
40
+ desc += `- **${fullName}**: ${tool.description || '无描述'}\n`;
41
+ desc += bindMcpParamsDesc(tool.inputSchema);
42
+ }
43
+ desc += '\n';
44
+ }
45
+
46
+ desc += '**调用格式:**\n';
47
+ desc += '```\next_call({ plugin: "mcp", tool: "服务器_工具名", args: {参数} })\n';
48
+ desc += '```\n';
49
+ return desc.trim();
50
+ }
51
+
52
+ /**
53
+ * 为单个 MCP 工具生成参数描述 Markdown
54
+ */
55
+ function bindMcpParamsDesc(rawSchema) {
56
+ let desc = '';
57
+ const schema = extractSchema(rawSchema);
58
+ if (!schema) return desc;
59
+
60
+ try {
61
+ let schemaResult = null;
62
+ let zodSchemaForFallback = null;
63
+
64
+ const isZodSchema =
65
+ typeof schema.shape === 'function' || (schema._def && schema._def.typeName);
66
+ if (isZodSchema) {
67
+ schemaResult = zodSchemaToMarkdown(schema);
68
+ zodSchemaForFallback = schema;
69
+ } else if (schema.jsonSchema) {
70
+ const zodSchema = jsonSchemaToZod(schema.jsonSchema || schema);
71
+ if (zodSchema) {
72
+ schemaResult = zodSchemaToMarkdown(zodSchema);
73
+ zodSchemaForFallback = zodSchema;
74
+ }
75
+ } else if (schema.properties) {
76
+ const zodSchema = jsonSchemaToZod(schema);
77
+ if (zodSchema) {
78
+ schemaResult = zodSchemaToMarkdown(zodSchema);
79
+ zodSchemaForFallback = zodSchema;
80
+ }
81
+ }
82
+
83
+ if (schemaResult) {
84
+ const hasUnparsedTypes = /\|.*\bZod\w+\b.*\|/.test(schemaResult);
85
+ const hasOnlyType =
86
+ /^-\s+\w+:\s+.+\n?$/.test(schemaResult.trim()) && !schemaResult.includes('`');
87
+ if (hasUnparsedTypes || hasOnlyType) {
88
+ desc += fallbackParamsDesc(zodSchemaForFallback || schema);
89
+ } else {
90
+ desc += `**参数:**\n\n${schemaResult}\n\n`;
91
+ }
92
+ }
93
+ } catch (e) {
94
+ try { desc += fallbackParamsDesc(zodSchemaForFallback || schema); } catch (e2) { /* ignore */ }
95
+ }
96
+ return desc;
97
+ }
98
+
99
+ /**
100
+ * 回退的参数描述(当 zodSchemaToMarkdown 失败时)
101
+ */
102
+ function fallbackParamsDesc(schema) {
103
+ const props = schema.properties || {};
104
+ const required = schema.required || [];
105
+
106
+ if (Object.keys(props).length > 0) {
107
+ let desc = '**参数:**\n\n';
108
+ for (const [key, prop] of Object.entries(props)) {
109
+ const isRequired = required.includes(key);
110
+ const type = prop.type || 'any';
111
+ const desc_text = prop.description || '';
112
+ desc += `- \`${key}\`${isRequired ? ' (必填)' : ''}: ${type} ${desc_text}\n`;
113
+ }
114
+ return desc + '\n';
115
+ }
116
+
117
+ if (typeof schema.shape === 'function' || (schema._def && schema._def.shape)) {
118
+ const shape = typeof schema.shape === 'function' ? schema.shape() : schema._def.shape();
119
+ if (shape && typeof shape === 'object') {
120
+ let desc = '**参数:**\n\n';
121
+ for (const [key, field] of Object.entries(shape)) {
122
+ const typeInfo = extractZodFieldType(field);
123
+ const isOptional = typeInfo.isOptional ? ' (可选)' : ' (必填)';
124
+ desc += `- \`${key}\`${isOptional}: ${typeInfo.type}\n`;
125
+ }
126
+ return desc + '\n';
127
+ }
128
+ }
129
+
130
+ return '';
131
+ }
132
+
133
+ /**
134
+ * 从 Zod schema 字段提取类型信息
135
+ */
136
+ function extractZodFieldType(field) {
137
+ if (!field) return { type: 'unknown', isOptional: false, description: '' };
138
+
139
+ const typeName = field._def?.typeName || field.constructor?.name || '';
140
+ const description = field.description || field._def?.description || '';
141
+
142
+ switch (typeName) {
143
+ case 'ZodOptional': {
144
+ const inner = field._def?.innerType;
145
+ if (!inner) return { type: 'optional', isOptional: true, description };
146
+ const innerInfo = extractZodFieldType(inner);
147
+ return { ...innerInfo, isOptional: true };
148
+ }
149
+ case 'ZodArray': {
150
+ const inner = field._def?.type;
151
+ if (!inner) return { type: 'array', isOptional: false, description };
152
+ const innerInfo = extractZodFieldType(inner);
153
+ return { type: `${innerInfo.type}[]`, isOptional: false, description };
154
+ }
155
+ case 'ZodObject': {
156
+ const shapeFn = field._def?.shape;
157
+ if (typeof shapeFn !== 'function') return { type: 'object', isOptional: false, description };
158
+ const shape = shapeFn();
159
+ if (!shape) return { type: 'object', isOptional: false, description };
160
+ return { type: `object{${Object.keys(shape).join(', ')}}`, isOptional: false, description };
161
+ }
162
+ case 'ZodString':
163
+ return { type: 'string', isOptional: false, description };
164
+ case 'ZodNumber':
165
+ return { type: 'number', isOptional: false, description };
166
+ case 'ZodBoolean':
167
+ return { type: 'boolean', isOptional: false, description };
168
+ case 'ZodEnum': {
169
+ const values = field._def?.values;
170
+ return values
171
+ ? { type: values.map((v) => `"${v}"`).join(' | '), isOptional: false, description }
172
+ : { type: 'enum', isOptional: false, description };
173
+ }
174
+ case 'ZodUnion': {
175
+ const options = field._def?.options;
176
+ return options
177
+ ? { type: `union(${options.map((opt) => extractZodFieldType(opt).type).join(' | ')})`, isOptional: false, description }
178
+ : { type: 'union', isOptional: false, description };
179
+ }
180
+ case 'ZodLiteral':
181
+ return { type: JSON.stringify(field._def?.value), isOptional: false, description };
182
+ case 'ZodRecord': {
183
+ const keyType = extractZodFieldType(field._def?.keyType);
184
+ const valueType = extractZodFieldType(field._def?.valueType);
185
+ return { type: `record<${keyType?.type || 'any'}, ${valueType?.type || 'any'}>`, isOptional: false, description };
186
+ }
187
+ case 'ZodMap': {
188
+ const keyType = extractZodFieldType(field._def?.keyType);
189
+ const valueType = extractZodFieldType(field._def?.valueType);
190
+ return { type: `map<${keyType?.type || 'any'}, ${valueType?.type || 'any'}>`, isOptional: false, description };
191
+ }
192
+ case 'ZodSet': {
193
+ const valueType = extractZodFieldType(field._def?.valueType);
194
+ return { type: `set<${valueType?.type || 'any'}>`, isOptional: false, description };
195
+ }
196
+ case 'ZodAny':
197
+ return { type: 'any', isOptional: false, description };
198
+ case 'ZodUnknown':
199
+ return { type: 'unknown', isOptional: false, description };
200
+ case 'ZodNever':
201
+ return { type: 'never', isOptional: false, description };
202
+ case 'ZodNull':
203
+ return { type: 'null', isOptional: false, description };
204
+ case 'ZodUndefined':
205
+ return { type: 'undefined', isOptional: false, description };
206
+ case 'ZodDate':
207
+ return { type: 'date', isOptional: false, description };
208
+ case 'ZodBigInt':
209
+ return { type: 'bigint', isOptional: false, description };
210
+ case 'ZodFunction':
211
+ return { type: 'function', isOptional: false, description };
212
+ case 'ZodLazy':
213
+ return { type: 'lazy', isOptional: false, description };
214
+ case 'ZodNativeEnum':
215
+ return { type: 'nativeEnum', isOptional: false, description };
216
+ case 'ZodDefault': {
217
+ const inner = field._def?.innerType;
218
+ if (!inner) return { type: 'default', isOptional: false, description };
219
+ return extractZodFieldType(inner);
220
+ }
221
+ case 'ZodEffects':
222
+ return { type: 'effects', isOptional: false, description };
223
+ case 'ZodPipeline':
224
+ return { type: 'pipeline', isOptional: false, description };
225
+ default:
226
+ return { type: typeName.replace('Zod', '').toLowerCase() || 'any', isOptional: false, description };
227
+ }
228
+ }
229
+
230
+ module.exports = {
231
+ extractSchema,
232
+ buildMCPServersDescription,
233
+ bindMcpParamsDesc,
234
+ fallbackParamsDesc,
235
+ extractZodFieldType,
236
+ };