@zqagent/core 0.2.16 → 0.3.6

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 (238) hide show
  1. package/README.md +1 -1
  2. package/README.oss.en.md +15 -2
  3. package/README.oss.md +18 -2
  4. package/README.oss.zh-TW.md +13 -2
  5. package/dist/McpClient-CEQBUPuC.d.cts +145 -0
  6. package/dist/McpClient-CEQBUPuC.d.ts +145 -0
  7. package/dist/{ToolRegistry-C3pTFiaJ.d.cts → ToolRegistry-Cy0fQxrb.d.ts} +2 -111
  8. package/dist/{ToolRegistry-C3pTFiaJ.d.ts → ToolRegistry-DouDvn9E.d.cts} +2 -111
  9. package/dist/{explore-RtZtb-V6.d.ts → TurnEngine-BPKhWuSI.d.cts} +108 -73
  10. package/dist/{explore-BgRhj6SP.d.cts → TurnEngine-DtGEuVLT.d.ts} +108 -73
  11. package/dist/conflictGuard-BBOOIxxK.d.cts +42 -0
  12. package/dist/conflictGuard-BBOOIxxK.d.ts +42 -0
  13. package/dist/index.cjs +5477 -1140
  14. package/dist/index.cjs.map +1 -1
  15. package/dist/index.d.cts +577 -195
  16. package/dist/index.d.ts +577 -195
  17. package/dist/index.js +5305 -1002
  18. package/dist/index.js.map +1 -1
  19. package/dist/internal/crypto/secrets.cjs +25 -0
  20. package/dist/internal/crypto/secrets.cjs.map +1 -0
  21. package/dist/internal/crypto/secrets.d.cts +2 -0
  22. package/dist/internal/crypto/secrets.d.ts +2 -0
  23. package/dist/internal/crypto/secrets.js +3 -0
  24. package/dist/internal/crypto/secrets.js.map +1 -0
  25. package/dist/internal/engine/FileLogHook.cjs +179 -0
  26. package/dist/internal/engine/FileLogHook.cjs.map +1 -0
  27. package/dist/internal/engine/FileLogHook.d.cts +67 -0
  28. package/dist/internal/engine/FileLogHook.d.ts +67 -0
  29. package/dist/internal/engine/FileLogHook.js +144 -0
  30. package/dist/internal/engine/FileLogHook.js.map +1 -0
  31. package/dist/internal/engine/approvalView.cjs +25 -0
  32. package/dist/internal/engine/approvalView.cjs.map +1 -0
  33. package/dist/internal/engine/approvalView.d.cts +1 -0
  34. package/dist/internal/engine/approvalView.d.ts +1 -0
  35. package/dist/internal/engine/approvalView.js +3 -0
  36. package/dist/internal/engine/approvalView.js.map +1 -0
  37. package/dist/internal/engine/limits.cjs +59 -0
  38. package/dist/internal/engine/limits.cjs.map +1 -0
  39. package/dist/internal/engine/limits.d.cts +32 -0
  40. package/dist/internal/engine/limits.d.ts +32 -0
  41. package/dist/internal/engine/limits.js +31 -0
  42. package/dist/internal/engine/limits.js.map +1 -0
  43. package/dist/internal/engine/sessionNaming.cjs +47 -0
  44. package/dist/internal/engine/sessionNaming.cjs.map +1 -0
  45. package/dist/internal/engine/sessionNaming.d.cts +25 -0
  46. package/dist/internal/engine/sessionNaming.d.ts +25 -0
  47. package/dist/internal/engine/sessionNaming.js +20 -0
  48. package/dist/internal/engine/sessionNaming.js.map +1 -0
  49. package/dist/internal/llmConfig.cjs +561 -0
  50. package/dist/internal/llmConfig.cjs.map +1 -0
  51. package/dist/internal/llmConfig.d.cts +75 -0
  52. package/dist/internal/llmConfig.d.ts +75 -0
  53. package/dist/internal/llmConfig.js +535 -0
  54. package/dist/internal/llmConfig.js.map +1 -0
  55. package/dist/internal/mcp/builtin/index.cjs +685 -0
  56. package/dist/internal/mcp/builtin/index.cjs.map +1 -0
  57. package/dist/internal/mcp/builtin/index.d.cts +21 -0
  58. package/dist/internal/mcp/builtin/index.d.ts +21 -0
  59. package/dist/internal/mcp/builtin/index.js +652 -0
  60. package/dist/internal/mcp/builtin/index.js.map +1 -0
  61. package/dist/internal/mcp/config.cjs +278 -0
  62. package/dist/internal/mcp/config.cjs.map +1 -0
  63. package/dist/internal/mcp/config.d.cts +61 -0
  64. package/dist/internal/mcp/config.d.ts +61 -0
  65. package/dist/internal/mcp/config.js +235 -0
  66. package/dist/internal/mcp/config.js.map +1 -0
  67. package/dist/internal/mcp/presets.cjs +184 -0
  68. package/dist/internal/mcp/presets.cjs.map +1 -0
  69. package/dist/internal/mcp/presets.d.cts +39 -0
  70. package/dist/internal/mcp/presets.d.ts +39 -0
  71. package/dist/internal/mcp/presets.js +158 -0
  72. package/dist/internal/mcp/presets.js.map +1 -0
  73. package/dist/internal/node-name.cjs +45 -0
  74. package/dist/internal/node-name.cjs.map +1 -0
  75. package/dist/internal/node-name.d.cts +3 -0
  76. package/dist/internal/node-name.d.ts +3 -0
  77. package/dist/internal/node-name.js +10 -0
  78. package/dist/internal/node-name.js.map +1 -0
  79. package/dist/internal/paths.cjs +63 -0
  80. package/dist/internal/paths.cjs.map +1 -0
  81. package/dist/internal/paths.d.cts +17 -0
  82. package/dist/internal/paths.d.ts +17 -0
  83. package/dist/internal/paths.js +25 -0
  84. package/dist/internal/paths.js.map +1 -0
  85. package/dist/internal/providers/diagnostics.cjs +288 -0
  86. package/dist/internal/providers/diagnostics.cjs.map +1 -0
  87. package/dist/internal/providers/diagnostics.d.cts +103 -0
  88. package/dist/internal/providers/diagnostics.d.ts +103 -0
  89. package/dist/internal/providers/diagnostics.js +250 -0
  90. package/dist/internal/providers/diagnostics.js.map +1 -0
  91. package/dist/internal/providers/models.cjs +699 -0
  92. package/dist/internal/providers/models.cjs.map +1 -0
  93. package/dist/internal/providers/models.d.cts +136 -0
  94. package/dist/internal/providers/models.d.ts +136 -0
  95. package/dist/internal/providers/models.js +654 -0
  96. package/dist/internal/providers/models.js.map +1 -0
  97. package/dist/internal/terminal.cjs +161 -0
  98. package/dist/internal/terminal.cjs.map +1 -0
  99. package/dist/internal/terminal.d.cts +58 -0
  100. package/dist/internal/terminal.d.ts +58 -0
  101. package/dist/internal/terminal.js +128 -0
  102. package/dist/internal/terminal.js.map +1 -0
  103. package/dist/internal/tools/execAsync.cjs +187 -0
  104. package/dist/internal/tools/execAsync.cjs.map +1 -0
  105. package/dist/internal/tools/execAsync.d.cts +30 -0
  106. package/dist/internal/tools/execAsync.d.ts +30 -0
  107. package/dist/internal/tools/execAsync.js +162 -0
  108. package/dist/internal/tools/execAsync.js.map +1 -0
  109. package/dist/internal/tools/git.cjs +935 -0
  110. package/dist/internal/tools/git.cjs.map +1 -0
  111. package/dist/internal/tools/git.d.cts +206 -0
  112. package/dist/internal/tools/git.d.ts +206 -0
  113. package/dist/internal/tools/git.js +883 -0
  114. package/dist/internal/tools/git.js.map +1 -0
  115. package/dist/internal/tools/pathSafety.cjs +99 -0
  116. package/dist/internal/tools/pathSafety.cjs.map +1 -0
  117. package/dist/internal/tools/pathSafety.d.cts +44 -0
  118. package/dist/internal/tools/pathSafety.d.ts +44 -0
  119. package/dist/internal/tools/pathSafety.js +61 -0
  120. package/dist/internal/tools/pathSafety.js.map +1 -0
  121. package/dist/internal/tools/plan.cjs +146 -0
  122. package/dist/internal/tools/plan.cjs.map +1 -0
  123. package/dist/internal/tools/plan.d.cts +48 -0
  124. package/dist/internal/tools/plan.d.ts +48 -0
  125. package/dist/internal/tools/plan.js +117 -0
  126. package/dist/internal/tools/plan.js.map +1 -0
  127. package/dist/internal/tools/ripgrep.cjs +118 -0
  128. package/dist/internal/tools/ripgrep.cjs.map +1 -0
  129. package/dist/internal/tools/ripgrep.d.cts +6 -0
  130. package/dist/internal/tools/ripgrep.d.ts +6 -0
  131. package/dist/internal/tools/ripgrep.js +89 -0
  132. package/dist/internal/tools/ripgrep.js.map +1 -0
  133. package/dist/internal/tools/sandbox.cjs +253 -0
  134. package/dist/internal/tools/sandbox.cjs.map +1 -0
  135. package/dist/internal/tools/sandbox.d.cts +67 -0
  136. package/dist/internal/tools/sandbox.d.ts +67 -0
  137. package/dist/internal/tools/sandbox.js +213 -0
  138. package/dist/internal/tools/sandbox.js.map +1 -0
  139. package/dist/internal/tools/shell.cjs +771 -0
  140. package/dist/internal/tools/shell.cjs.map +1 -0
  141. package/dist/internal/tools/shell.d.cts +47 -0
  142. package/dist/internal/tools/shell.d.ts +47 -0
  143. package/dist/internal/tools/shell.js +731 -0
  144. package/dist/internal/tools/shell.js.map +1 -0
  145. package/dist/internal/tools/shellRuntime.cjs +220 -0
  146. package/dist/internal/tools/shellRuntime.cjs.map +1 -0
  147. package/dist/internal/tools/shellRuntime.d.cts +67 -0
  148. package/dist/internal/tools/shellRuntime.d.ts +67 -0
  149. package/dist/internal/tools/shellRuntime.js +181 -0
  150. package/dist/internal/tools/shellRuntime.js.map +1 -0
  151. package/dist/internal/update/controller.cjs +1035 -0
  152. package/dist/internal/update/controller.cjs.map +1 -0
  153. package/dist/internal/update/controller.d.cts +150 -0
  154. package/dist/internal/update/controller.d.ts +150 -0
  155. package/dist/internal/update/controller.js +991 -0
  156. package/dist/internal/update/controller.js.map +1 -0
  157. package/dist/internal/update/platform.cjs +234 -0
  158. package/dist/internal/update/platform.cjs.map +1 -0
  159. package/dist/internal/update/platform.d.cts +77 -0
  160. package/dist/internal/update/platform.d.ts +77 -0
  161. package/dist/internal/update/platform.js +184 -0
  162. package/dist/internal/update/platform.js.map +1 -0
  163. package/dist/tools/ask.d.cts +3 -1
  164. package/dist/tools/ask.d.ts +3 -1
  165. package/dist/tools/diff.cjs +17 -6
  166. package/dist/tools/diff.cjs.map +1 -1
  167. package/dist/tools/diff.d.cts +5 -2
  168. package/dist/tools/diff.d.ts +5 -2
  169. package/dist/tools/diff.js +17 -6
  170. package/dist/tools/diff.js.map +1 -1
  171. package/dist/tools/directories.d.cts +3 -1
  172. package/dist/tools/directories.d.ts +3 -1
  173. package/dist/tools/explore.cjs +1778 -204
  174. package/dist/tools/explore.cjs.map +1 -1
  175. package/dist/tools/explore.d.cts +82 -2
  176. package/dist/tools/explore.d.ts +82 -2
  177. package/dist/tools/explore.js +1780 -204
  178. package/dist/tools/explore.js.map +1 -1
  179. package/dist/tools/files.cjs +56 -21
  180. package/dist/tools/files.cjs.map +1 -1
  181. package/dist/tools/files.d.cts +6 -3
  182. package/dist/tools/files.d.ts +6 -3
  183. package/dist/tools/files.js +56 -21
  184. package/dist/tools/files.js.map +1 -1
  185. package/dist/tools/git.cjs +34 -25
  186. package/dist/tools/git.cjs.map +1 -1
  187. package/dist/tools/git.d.cts +4 -204
  188. package/dist/tools/git.d.ts +4 -204
  189. package/dist/tools/git.js +34 -25
  190. package/dist/tools/git.js.map +1 -1
  191. package/dist/tools/media.cjs +280 -3
  192. package/dist/tools/media.cjs.map +1 -1
  193. package/dist/tools/media.d.cts +3 -1
  194. package/dist/tools/media.d.ts +3 -1
  195. package/dist/tools/media.js +278 -1
  196. package/dist/tools/media.js.map +1 -1
  197. package/dist/tools/plan.cjs +62 -0
  198. package/dist/tools/plan.cjs.map +1 -1
  199. package/dist/tools/plan.d.cts +4 -17
  200. package/dist/tools/plan.d.ts +4 -17
  201. package/dist/tools/plan.js +59 -0
  202. package/dist/tools/plan.js.map +1 -1
  203. package/dist/tools/search.cjs +3 -0
  204. package/dist/tools/search.cjs.map +1 -1
  205. package/dist/tools/search.d.cts +3 -1
  206. package/dist/tools/search.d.ts +3 -1
  207. package/dist/tools/search.js +3 -0
  208. package/dist/tools/search.js.map +1 -1
  209. package/dist/tools/shell.cjs +76 -11
  210. package/dist/tools/shell.cjs.map +1 -1
  211. package/dist/tools/shell.d.cts +5 -39
  212. package/dist/tools/shell.d.ts +5 -39
  213. package/dist/tools/shell.js +75 -11
  214. package/dist/tools/shell.js.map +1 -1
  215. package/dist/tools/todo.d.cts +3 -1
  216. package/dist/tools/todo.d.ts +3 -1
  217. package/dist/tools/web.cjs +279 -5
  218. package/dist/tools/web.cjs.map +1 -1
  219. package/dist/tools/web.d.cts +3 -1
  220. package/dist/tools/web.d.ts +3 -1
  221. package/dist/tools/web.js +275 -3
  222. package/dist/tools/web.js.map +1 -1
  223. package/dist/types-BYyXWWPW.d.cts +237 -0
  224. package/dist/types-BYyXWWPW.d.ts +237 -0
  225. package/package.json +26 -37
  226. package/bin/zqagent +0 -2
  227. package/dist/cli/index.cjs +0 -24507
  228. package/dist/cli/index.cjs.map +0 -1
  229. package/dist/cli/index.d.cts +0 -2
  230. package/dist/cli/index.d.ts +0 -2
  231. package/dist/cli/index.js +0 -24490
  232. package/dist/cli/index.js.map +0 -1
  233. package/dist/server/index.cjs +0 -19824
  234. package/dist/server/index.cjs.map +0 -1
  235. package/dist/server/index.d.cts +0 -2
  236. package/dist/server/index.d.ts +0 -2
  237. package/dist/server/index.js +0 -19807
  238. package/dist/server/index.js.map +0 -1
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../../../src/mcp/builtin/index.ts","../../../../src/mcp/builtin/ocr.ts","../../../../src/mcp/builtin/ocrEngine.ts","../../../../src/tools/shellRuntime.ts"],"sourcesContent":["/**\n * 内置 MCP 服务器注册表与进程内传输\n *\n * 内置服务器通过 InMemoryTransport 与客户端通信:无需子进程、跨平台、\n * 对 SEA 单文件打包友好。新增内置能力时只需在此登记工厂。\n */\n\nimport type { Server } from \"@modelcontextprotocol/sdk/server/index.js\";\nimport { InMemoryTransport } from \"@modelcontextprotocol/sdk/inMemory.js\";\nimport type { Transport } from \"@modelcontextprotocol/sdk/shared/transport.js\";\nimport type { McpServerConfig } from \"../McpClient.js\";\nimport { createOcrServer } from \"./ocr.js\";\nimport type { McpBuiltinContext } from \"./types.js\";\n\nexport type BuiltinMcpFactory = (ctx: McpBuiltinContext) => Server;\n\nconst FACTORIES: Record<string, BuiltinMcpFactory> = {\n ocr: createOcrServer,\n};\n\n/** 已注册的内置服务器标识 */\nexport function listBuiltinServers(): string[] {\n return Object.keys(FACTORIES);\n}\n\n/**\n * 创建内置服务器的客户端侧传输(已建立连接)。\n * 服务端实例与客户端共享进程,随传输关闭而释放。\n */\nexport async function createBuiltinTransport(\n config: McpServerConfig,\n ctx: McpBuiltinContext,\n): Promise<Transport> {\n const id = config.builtin ?? config.name;\n const factory = FACTORIES[id];\n if (!factory) {\n throw new Error(\n `未知的内置 MCP server \"${id}\"(可用: ${listBuiltinServers().join(\", \")})`,\n );\n }\n const [clientTransport, serverTransport] =\n InMemoryTransport.createLinkedPair();\n const server = factory(ctx);\n await server.connect(serverTransport);\n return clientTransport;\n}\n\nexport type { McpBuiltinContext } from \"./types.js\";\n","/**\n * 内置 OCR MCP 服务器\n *\n * 以标准 MCP server 形式暴露 recognize 工具,经 InMemoryTransport 与\n * McpClient 通信,因此与外部 MCP 服务器共享同一套注入/审批/懒加载逻辑。\n * 工具名固定为 recognize,配合默认 server 名 ocr 得到 `mcp__ocr__recognize`。\n */\n\nimport { Server } from \"@modelcontextprotocol/sdk/server/index.js\";\nimport {\n CallToolRequestSchema,\n ListToolsRequestSchema,\n} from \"@modelcontextprotocol/sdk/types.js\";\nimport { recognizeOcr, type OcrInput } from \"./ocrEngine.js\";\nimport type { McpBuiltinContext } from \"./types.js\";\n\nconst TOOL_NAME = \"recognize\";\n\nconst TOOL_DESCRIPTION = [\n \"从图片、扫描件或 PDF 中提取文字(OCR)。\",\n \"优先传 imagePath(工作区内的文件路径),也支持 imageBase64(可带 data URL 前缀)或 imageUrl。\",\n \"传入 PDF 会逐页识别(maxPages 默认 5,dpi 默认 150)。\",\n \"识别引擎自动选择:macOS Vision / tesseract+pdftoppm / 已配置的多模态模型。\",\n].join(\"\\n\");\n\nconst INPUT_SCHEMA = {\n type: \"object\",\n properties: {\n imagePath: {\n type: \"string\",\n description: \"本地图片或 PDF 路径(相对路径按工作区解析)\",\n },\n imageBase64: {\n type: \"string\",\n description: \"图片的 base64 编码(可带 data URL 前缀)\",\n },\n imageUrl: {\n type: \"string\",\n description: \"远程图片或 PDF 的 URL\",\n },\n maxPages: {\n type: \"number\",\n description: \"PDF 最多识别页数(默认 5,上限 50)\",\n },\n dpi: {\n type: \"number\",\n description: \"PDF 渲染 DPI(默认 150,上限 400)\",\n },\n lang: {\n type: \"string\",\n description: \"tesseract 语言包(默认 chi_sim+eng)\",\n },\n },\n} as const;\n\n/** 创建内置 OCR MCP 服务器 */\nexport function createOcrServer(ctx: McpBuiltinContext = {}): Server {\n const server = new Server(\n { name: \"zqagent-ocr\", version: \"0.1.0\" },\n { capabilities: { tools: {} } },\n );\n\n server.setRequestHandler(ListToolsRequestSchema, async () => ({\n tools: [\n {\n name: TOOL_NAME,\n description: TOOL_DESCRIPTION,\n inputSchema: INPUT_SCHEMA,\n },\n ],\n }));\n\n server.setRequestHandler(CallToolRequestSchema, async (request) => {\n if (request.params.name !== TOOL_NAME) {\n return {\n content: [\n {\n type: \"text\",\n text: JSON.stringify({\n error: `未知工具 \"${request.params.name}\",可用工具:${TOOL_NAME}`,\n }),\n },\n ],\n isError: true,\n };\n }\n\n const args = (request.params.arguments ?? {}) as OcrInput;\n const result = await recognizeOcr(args, ctx);\n if (!result.ok) {\n return {\n content: [\n {\n type: \"text\",\n text: JSON.stringify(\n {\n error: result.error,\n how_to_fix: result.how_to_fix,\n attempts: result.attempts,\n },\n null,\n 2,\n ),\n },\n ],\n isError: true,\n };\n }\n return {\n content: [{ type: \"text\", text: result.text }],\n };\n });\n\n return server;\n}\n","/**\n * 内置 OCR 引擎\n *\n * 识别后端按优先级降级(本机引擎优先,多模态模型兜底):\n * 1. macOS Vision(Swift + PDFKit,免安装,支持 PDF 逐页)\n * 2. tesseract + pdftoppm(Linux/Windows 或非 macOS 环境)\n * 3. 已配置的多模态 LLM(OpenAI 兼容接口,图片走 data URL)\n * 4. 全部不可用 → 返回 error + how_to_fix(不抛异常,模型可读)\n *\n * 所有外部依赖均可通过 OcrRuntimeOverrides 注入,便于单测。\n */\n\nimport fs from \"node:fs\";\nimport os from \"node:os\";\nimport path from \"node:path\";\nimport { execFile } from \"node:child_process\";\nimport OpenAI from \"openai\";\nimport { findExecutable } from \"../../tools/shellRuntime.js\";\nimport type {\n McpBuiltinContext,\n OcrLlmConfig,\n OcrRuntimeOverrides,\n} from \"./types.js\";\n\n/** OCR 输入参数(MCP 工具 recognize 的入参) */\nexport interface OcrInput {\n /** 本地图片/PDF 路径(相对路径按工作区解析) */\n imagePath?: string;\n /** base64(可带 data URL 前缀) */\n imageBase64?: string;\n /** 远程图片/PDF URL */\n imageUrl?: string;\n /** PDF 最多处理页数(默认 5,上限 50) */\n maxPages?: number;\n /** PDF 渲染 DPI(默认 150,上限 400) */\n dpi?: number;\n /** Tesseract 语言(默认 chi_sim+eng) */\n lang?: string;\n}\n\nexport type OcrEngineName = \"vision\" | \"tesseract\" | \"llm\";\n\nexport interface OcrPage {\n page: number;\n text: string;\n}\n\nexport type OcrResult =\n | {\n ok: true;\n text: string;\n engine: OcrEngineName;\n pages: OcrPage[];\n }\n | {\n ok: false;\n error: string;\n how_to_fix: string;\n attempts: { engine: string; error: string }[];\n };\n\nconst DEFAULT_MAX_PAGES = 5;\nconst MAX_MAX_PAGES = 50;\nconst DEFAULT_DPI = 150;\nconst MAX_DPI = 400;\nconst CMD_TIMEOUT_MS = 120_000;\nconst COMPILE_TIMEOUT_MS = 180_000;\nconst MAX_BUFFER = 32 * 1024 * 1024;\n\n/** Vision 引擎 Swift 源码(首次使用时编译并缓存到 .agent/bin) */\nconst VISION_SOURCE = `import Foundation\nimport Vision\nimport AppKit\nimport PDFKit\nimport CoreGraphics\n\nstruct PageResult: Codable { let page: Int; let text: String }\nstruct Output: Codable { let pages: [PageResult] }\n\nfunc recognize(_ image: CGImage) -> String {\n func run(_ langs: [String]?) throws -> [VNRecognizedTextObservation] {\n let request = VNRecognizeTextRequest()\n request.recognitionLevel = .accurate\n request.usesLanguageCorrection = true\n if let langs = langs { request.recognitionLanguages = langs }\n let handler = VNImageRequestHandler(cgImage: image, options: [:])\n try handler.perform([request])\n return request.results ?? []\n }\n let observations = (try? run([\"zh-Hans\", \"zh-Hant\", \"en-US\"])) ?? (try? run(nil)) ?? []\n let lines = observations.compactMap { $0.topCandidates(1).first?.string }\n return lines.joined(separator: \"\\\\n\")\n}\n\nfunc imageFromFile(_ p: String) -> CGImage? {\n guard let src = CGImageSourceCreateWithURL(URL(fileURLWithPath: p) as CFURL, nil) else { return nil }\n return CGImageSourceCreateImageAtIndex(src, 0, nil)\n}\n\nlet args = CommandLine.arguments\nguard args.count >= 2 else {\n FileHandle.standardError.write(\"usage: ocr-vision <file> [maxPages] [dpi]\\\\n\".data(using: .utf8)!)\n exit(2)\n}\nlet file = args[1]\nlet maxPages = args.count > 2 ? (Int(args[2]) ?? 5) : 5\nlet dpi = args.count > 3 ? (Int(args[3]) ?? 150) : 150\n\nvar pages: [PageResult] = []\nif file.lowercased().hasSuffix(\".pdf\"), let doc = PDFDocument(url: URL(fileURLWithPath: file)) {\n let count = min(doc.pageCount, max(1, maxPages))\n let scale = CGFloat(dpi) / 72.0\n for i in 0..<count {\n guard let page = doc.page(at: i) else { continue }\n let bounds = page.bounds(for: .mediaBox)\n let w = Int(bounds.width * scale)\n let h = Int(bounds.height * scale)\n guard w > 0, h > 0 else { continue }\n guard let ctx = CGContext(\n data: nil, width: w, height: h, bitsPerComponent: 8, bytesPerRow: 0,\n space: CGColorSpaceCreateDeviceRGB(),\n bitmapInfo: CGImageAlphaInfo.premultipliedLast.rawValue\n ) else { continue }\n ctx.setFillColor(CGColor(red: 1, green: 1, blue: 1, alpha: 1))\n ctx.fill(CGRect(x: 0, y: 0, width: w, height: h))\n ctx.scaleBy(x: scale, y: scale)\n page.draw(with: .mediaBox, to: ctx)\n guard let image = ctx.makeImage() else { continue }\n pages.append(PageResult(page: i + 1, text: recognize(image)))\n }\n} else if let image = imageFromFile(file) {\n pages.append(PageResult(page: 1, text: recognize(image)))\n} else {\n FileHandle.standardError.write(\"unsupported or unreadable image: \\\\(file)\\\\n\".data(using: .utf8)!)\n exit(3)\n}\n\nlet encoder = JSONEncoder()\nencoder.outputFormatting = [.withoutEscapingSlashes]\nif let data = try? encoder.encode(Output(pages: pages)) {\n FileHandle.standardOutput.write(data)\n} else {\n exit(4)\n}\n`;\n\n/** 默认命令执行器 */\nfunction defaultRun(\n command: string,\n args: string[],\n opts: { timeoutMs?: number; cwd?: string } = {},\n): Promise<{ stdout: string; stderr: string }> {\n return new Promise((resolve, reject) => {\n execFile(\n command,\n args,\n {\n timeout: opts.timeoutMs ?? CMD_TIMEOUT_MS,\n cwd: opts.cwd,\n maxBuffer: MAX_BUFFER,\n encoding: \"utf8\",\n },\n (err, stdout, stderr) => {\n if (err) {\n const e = err as Error;\n e.message = `${path.basename(command)} 执行失败: ${e.message}${\n stderr ? `\\n${stderr.trim()}` : \"\"\n }`;\n reject(e);\n } else {\n resolve({ stdout, stderr });\n }\n },\n );\n });\n}\n\ninterface Runtime {\n platform: NodeJS.Platform;\n env: NodeJS.ProcessEnv;\n workspace: string;\n llm?: OcrLlmConfig;\n overrides: OcrRuntimeOverrides;\n which(name: string): string | null;\n run(\n command: string,\n args: string[],\n opts?: { timeoutMs?: number; cwd?: string },\n ): Promise<{ stdout: string; stderr: string }>;\n fileExists(p: string): boolean;\n}\n\nfunction makeRuntime(ctx: McpBuiltinContext): Runtime {\n const overrides = ctx.ocr ?? {};\n const platform = overrides.platform ?? process.platform;\n const env = overrides.env ?? ctx.env ?? process.env;\n const workspace = ctx.workspace ?? env.WORKSPACE ?? process.cwd();\n const fileExists =\n overrides.fileExists ??\n ((p: string): boolean => {\n try {\n return fs.statSync(p).isFile();\n } catch {\n return false;\n }\n });\n const which =\n overrides.which ??\n ((name: string): string | null =>\n findExecutable(name, { platform, env, fileExists }));\n return {\n platform,\n env,\n workspace,\n llm: ctx.llm,\n overrides,\n which,\n run: overrides.run ?? defaultRun,\n fileExists,\n };\n}\n\nfunction clampInt(\n value: unknown,\n fallback: number,\n min: number,\n max: number,\n): number {\n const n = typeof value === \"number\" ? value : Number(value);\n if (!Number.isFinite(n)) return fallback;\n return Math.min(max, Math.max(min, Math.trunc(n)));\n}\n\nfunction isPdfFile(file: string): boolean {\n if (file.toLowerCase().endsWith(\".pdf\")) return true;\n try {\n const fd = fs.openSync(file, \"r\");\n try {\n const buf = Buffer.alloc(5);\n const read = fs.readSync(fd, buf, 0, 5, 0);\n return read >= 5 && buf.toString(\"latin1\") === \"%PDF-\";\n } finally {\n fs.closeSync(fd);\n }\n } catch {\n return false;\n }\n}\n\nfunction detectMime(file: string): string {\n try {\n const fd = fs.openSync(file, \"r\");\n try {\n const buf = Buffer.alloc(12);\n fs.readSync(fd, buf, 0, 12, 0);\n if (buf[0] === 0x89 && buf[1] === 0x50) return \"image/png\";\n if (buf[0] === 0xff && buf[1] === 0xd8) return \"image/jpeg\";\n if (buf.toString(\"latin1\", 0, 4) === \"RIFF\") return \"image/webp\";\n if (buf.toString(\"latin1\", 0, 4) === \"GIF8\") return \"image/gif\";\n } finally {\n fs.closeSync(fd);\n }\n } catch {\n // 忽略:按扩展名兜底\n }\n const ext = path.extname(file).toLowerCase();\n if (ext === \".jpg\" || ext === \".jpeg\") return \"image/jpeg\";\n if (ext === \".webp\") return \"image/webp\";\n if (ext === \".gif\") return \"image/gif\";\n return \"image/png\";\n}\n\nfunction toDataUrl(file: string): string {\n return `data:${detectMime(file)};base64,${fs.readFileSync(file).toString(\"base64\")}`;\n}\n\nfunction dataUrlToBuffer(value: string): { buffer: Buffer; ext: string } {\n const m = /^data:([^;,]+);base64,([\\s\\S]*)$/.exec(value.trim());\n const mime = m?.[1] ?? \"image/png\";\n const b64 = m?.[2] ?? value.trim();\n const ext =\n mime === \"image/jpeg\"\n ? \".jpg\"\n : mime === \"image/webp\"\n ? \".webp\"\n : mime === \"image/gif\"\n ? \".gif\"\n : mime === \"application/pdf\"\n ? \".pdf\"\n : \".png\";\n return { buffer: Buffer.from(b64, \"base64\"), ext };\n}\n\nfunction naturalCompare(a: string, b: string): number {\n return a.localeCompare(b, undefined, { numeric: true });\n}\n\n/** 把图片 URL 下载到临时文件 */\nasync function downloadToFile(\n url: string,\n dest: string,\n runtime: Runtime,\n): Promise<void> {\n const fetchImpl =\n runtime.overrides.fetchImpl ??\n (globalThis.fetch as unknown as OcrRuntimeOverrides[\"fetchImpl\"]);\n if (!fetchImpl) throw new Error(\"当前运行时不支持 fetch,无法下载 imageUrl\");\n const res = await fetchImpl(url);\n if (!res.ok) throw new Error(`下载图片失败:HTTP ${res.status}`);\n fs.writeFileSync(dest, Buffer.from(await res.arrayBuffer()));\n}\n\ninterface MaterializedInput {\n file: string;\n isPdf: boolean;\n tmpDir: string;\n cleanup(): void;\n}\n\n/** 把三种输入形式统一落成临时文件 */\nasync function materializeInput(\n input: OcrInput,\n runtime: Runtime,\n): Promise<MaterializedInput> {\n const root = runtime.overrides.tmpDir ?? os.tmpdir();\n const tmpDir = fs.mkdtempSync(path.join(root, \"zqagent-ocr-\"));\n const cleanup = (): void => {\n try {\n fs.rmSync(tmpDir, { recursive: true, force: true });\n } catch {\n // 忽略清理失败\n }\n };\n try {\n if (typeof input.imagePath === \"string\" && input.imagePath.trim()) {\n const file = path.isAbsolute(input.imagePath)\n ? input.imagePath\n : path.resolve(runtime.workspace, input.imagePath);\n if (!runtime.fileExists(file) && !fs.existsSync(file))\n throw new Error(`图片不存在:${file}`);\n return { file, isPdf: isPdfFile(file), tmpDir, cleanup };\n }\n if (typeof input.imageBase64 === \"string\" && input.imageBase64.trim()) {\n const { buffer, ext } = dataUrlToBuffer(input.imageBase64);\n const file = path.join(tmpDir, `input${ext}`);\n fs.writeFileSync(file, buffer);\n return { file, isPdf: isPdfFile(file), tmpDir, cleanup };\n }\n if (typeof input.imageUrl === \"string\" && input.imageUrl.trim()) {\n const url = input.imageUrl.trim();\n const ext = path.extname(new URL(url).pathname).toLowerCase() || \".png\";\n const file = path.join(tmpDir, `input${ext}`);\n await downloadToFile(url, file, runtime);\n return { file, isPdf: isPdfFile(file), tmpDir, cleanup };\n }\n throw new Error(\"缺少输入:请提供 imagePath / imageBase64 / imageUrl 之一\");\n } catch (err) {\n cleanup();\n throw err;\n }\n}\n\n/** PDF 转 PNG(依赖 poppler 的 pdftoppm) */\nasync function pdfToImages(\n file: string,\n tmpDir: string,\n input: OcrInput,\n runtime: Runtime,\n): Promise<string[]> {\n const pdftoppm = runtime.which(\"pdftoppm\");\n if (!pdftoppm) {\n throw new Error(\n \"PDF 需要 poppler 的 pdftoppm(macOS 可使用内置 Vision 引擎,无需该依赖)\",\n );\n }\n const pagesDir = path.join(tmpDir, \"pages\");\n fs.mkdirSync(pagesDir, { recursive: true });\n const maxPages = clampInt(\n input.maxPages,\n DEFAULT_MAX_PAGES,\n 1,\n MAX_MAX_PAGES,\n );\n const dpi = clampInt(input.dpi, DEFAULT_DPI, 36, MAX_DPI);\n await runtime.run(\n pdftoppm,\n [\n \"-r\",\n String(dpi),\n \"-png\",\n \"-f\",\n \"1\",\n \"-l\",\n String(maxPages),\n file,\n path.join(pagesDir, \"page\"),\n ],\n { timeoutMs: CMD_TIMEOUT_MS },\n );\n const images = fs\n .readdirSync(pagesDir)\n .filter((f) => /^page.*\\.png$/i.test(f))\n .sort(naturalCompare)\n .map((f) => path.join(pagesDir, f));\n if (images.length === 0) throw new Error(\"PDF 转图片失败(未生成页面)\");\n return images;\n}\n\ninterface VisionBuild {\n binary: string | null;\n error?: string;\n}\n\n// Vision 编译任务按 workspace 缓存:并发 OCR 只编译一次,\n// 失败结果同样缓存,避免长驻进程内每次 OCR 都重复等待编译。\nconst visionBuilds = new Map<string, Promise<VisionBuild>>();\n\n/** 从编译器输出中提取一行可读错误(避免把整页 modulemap 灌给模型) */\nfunction conciseError(err: Error): string {\n const lines = err.message\n .split(\"\\n\")\n .map((l) => l.trim())\n .filter((l) => /error:|fatal error:/i.test(l));\n const pick = lines.length > 0 ? lines[lines.length - 1]! : err.message;\n return pick.slice(0, 500);\n}\n\n/** 确保 Vision 二进制已编译;不可用时返回原因 */\nasync function ensureVisionBinary(runtime: Runtime): Promise<VisionBuild> {\n if (runtime.platform !== \"darwin\")\n return { binary: null, error: \"仅 macOS 可用\" };\n const swiftc = runtime.which(\"swiftc\");\n if (!swiftc)\n return {\n binary: null,\n error: \"未找到 swiftc(需要 Xcode Command Line Tools)\",\n };\n\n const dir = path.join(runtime.workspace, \".agent\", \"bin\");\n const srcPath = path.join(dir, \"ocr-vision.swift\");\n const binPath = path.join(dir, \"ocr-vision\");\n const cacheKey = binPath;\n const cached = visionBuilds.get(cacheKey);\n if (cached) return cached;\n\n const build = (async (): Promise<VisionBuild> => {\n try {\n fs.mkdirSync(dir, { recursive: true });\n let changed = true;\n try {\n changed = fs.readFileSync(srcPath, \"utf8\") !== VISION_SOURCE;\n } catch {\n changed = true;\n }\n if (changed) fs.writeFileSync(srcPath, VISION_SOURCE, \"utf8\");\n if (changed || !runtime.fileExists(binPath)) {\n await runtime.run(swiftc, [\"-O\", \"-o\", binPath, srcPath], {\n timeoutMs: COMPILE_TIMEOUT_MS,\n });\n }\n return runtime.fileExists(binPath)\n ? { binary: binPath }\n : { binary: null, error: \"Vision 编译未生成可执行文件\" };\n } catch (err) {\n // 编译失败(如 CLT 与 SDK 版本不匹配):降级到其它引擎\n return { binary: null, error: `Vision 编译失败: ${conciseError(err as Error)}` };\n }\n })();\n visionBuilds.set(cacheKey, build);\n return build;\n}\n\nasync function runVision(\n binary: string,\n file: string,\n input: OcrInput,\n runtime: Runtime,\n): Promise<OcrPage[]> {\n const maxPages = clampInt(\n input.maxPages,\n DEFAULT_MAX_PAGES,\n 1,\n MAX_MAX_PAGES,\n );\n const dpi = clampInt(input.dpi, DEFAULT_DPI, 36, MAX_DPI);\n const { stdout } = await runtime.run(\n binary,\n [file, String(maxPages), String(dpi)],\n { timeoutMs: CMD_TIMEOUT_MS },\n );\n const parsed = JSON.parse(stdout) as { pages?: OcrPage[] };\n const pages = Array.isArray(parsed.pages) ? parsed.pages : [];\n if (pages.length === 0) throw new Error(\"Vision 未返回识别结果\");\n return pages.map((p) => ({\n page: Number(p.page) || 1,\n text: typeof p.text === \"string\" ? p.text : \"\",\n }));\n}\n\nasync function runTesseract(\n file: string,\n isPdf: boolean,\n input: OcrInput,\n runtime: Runtime,\n tmpDir: string,\n): Promise<OcrPage[]> {\n const tesseract = runtime.which(\"tesseract\");\n if (!tesseract) throw new Error(\"未找到 tesseract\");\n const lang = input.lang ?? runtime.env.OCR_LANG ?? \"chi_sim+eng\";\n const images = isPdf\n ? await pdfToImages(file, tmpDir, input, runtime)\n : [file];\n const pages: OcrPage[] = [];\n for (let i = 0; i < images.length; i += 1) {\n const { stdout } = await runtime.run(\n tesseract,\n [images[i]!, \"stdout\", \"-l\", lang, \"--psm\", \"3\"],\n { timeoutMs: CMD_TIMEOUT_MS },\n );\n pages.push({ page: i + 1, text: stdout.trim() });\n }\n return pages;\n}\n\nfunction resolveLlmConfig(runtime: Runtime): {\n apiKey: string;\n model: string;\n baseURL?: string;\n} | null {\n const llm = runtime.llm;\n const apiKey =\n llm?.apiKey ??\n runtime.env.OPENAI_API_KEY ??\n runtime.env.OCR_API_KEY ??\n \"\";\n const model =\n llm?.model ?? runtime.env.OCR_MODEL ?? runtime.env.OPENAI_MODEL ?? \"\";\n const baseURL =\n llm?.baseURL ?? runtime.env.OPENAI_BASE_URL ?? runtime.env.OCR_BASE_URL;\n if (!apiKey || !model) return null;\n return { apiKey, model, baseURL };\n}\n\nasync function defaultLlmComplete(req: {\n model: string;\n prompt: string;\n imageDataUrl: string;\n baseURL?: string;\n apiKey?: string;\n}): Promise<string> {\n const client = new OpenAI({\n apiKey: req.apiKey ?? \"\",\n ...(req.baseURL ? { baseURL: req.baseURL } : {}),\n });\n const res = await client.chat.completions.create({\n model: req.model,\n messages: [\n {\n role: \"user\",\n content: [\n { type: \"text\", text: req.prompt },\n { type: \"image_url\", image_url: { url: req.imageDataUrl } },\n ],\n },\n ],\n });\n return res.choices?.[0]?.message?.content ?? \"\";\n}\n\nasync function runLlm(\n file: string,\n isPdf: boolean,\n input: OcrInput,\n runtime: Runtime,\n tmpDir: string,\n): Promise<OcrPage[]> {\n const cfg = resolveLlmConfig(runtime);\n if (!cfg) {\n throw new Error(\"未配置多模态模型(缺少 API key 或 model)\");\n }\n const images = isPdf\n ? await pdfToImages(file, tmpDir, input, runtime)\n : [file];\n const prompt =\n \"请逐字提取这张图片中的全部文字,保持原有换行与顺序。只输出文字,不要解释。\";\n const complete = runtime.overrides.llmComplete ?? defaultLlmComplete;\n const pages: OcrPage[] = [];\n for (let i = 0; i < images.length; i += 1) {\n const text = await complete({\n model: cfg.model,\n prompt,\n imageDataUrl: toDataUrl(images[i]!),\n baseURL: cfg.baseURL,\n apiKey: cfg.apiKey,\n });\n pages.push({ page: i + 1, text: text.trim() });\n }\n return pages;\n}\n\nfunction joinPages(pages: OcrPage[]): string {\n if (pages.length === 1) return pages[0]!.text;\n return pages\n .map((p) => `--- page ${p.page} ---\\n${p.text}`)\n .join(\"\\n\\n\");\n}\n\nfunction howToFix(platform: NodeJS.Platform): string {\n if (platform === \"darwin\")\n return [\n \"macOS 可任选其一:\",\n \"1) 安装 Xcode Command Line Tools 以启用内置 Vision:xcode-select --install\",\n \"2) 安装 tesseract 与 poppler:brew install tesseract tesseract-lang poppler\",\n \"3) 用 /connect 配置支持图片输入的多模态模型后重试\",\n ].join(\"\\n\");\n if (platform === \"win32\")\n return [\n \"Windows 可任选其一:\",\n \"1) 安装 tesseract:winget install UB-Mannheim.TesseractOCR\",\n \"2) 安装 poppler(PDF 需要)并加入 PATH\",\n \"3) 用 /connect 配置支持图片输入的多模态模型后重试\",\n ].join(\"\\n\");\n return [\n \"Linux 可任选其一:\",\n \"1) apt-get install -y tesseract-ocr tesseract-ocr-chi-sim poppler-utils\",\n \"2) 用 /connect 配置支持图片输入的多模态模型后重试\",\n ].join(\"\\n\");\n}\n\n/** 引擎探测顺序(OCR_ENGINE 可强制指定) */\nfunction engineOrder(runtime: Runtime): OcrEngineName[] {\n const forced = (runtime.env.OCR_ENGINE ?? \"\").toLowerCase();\n if (forced === \"vision\" || forced === \"tesseract\" || forced === \"llm\")\n return [forced];\n return [\"vision\", \"tesseract\", \"llm\"];\n}\n\n/**\n * 识别图片或 PDF。\n * 不抛异常:失败时返回 { ok:false, error, how_to_fix, attempts }。\n */\nexport async function recognizeOcr(\n input: OcrInput,\n ctx: McpBuiltinContext = {},\n): Promise<OcrResult> {\n const runtime = makeRuntime(ctx);\n let materialized: MaterializedInput;\n try {\n materialized = await materializeInput(input, runtime);\n } catch (err) {\n return {\n ok: false,\n error: (err as Error).message,\n how_to_fix: howToFix(runtime.platform),\n attempts: [],\n };\n }\n\n const attempts: { engine: string; error: string }[] = [];\n try {\n for (const engine of engineOrder(runtime)) {\n try {\n let pages: OcrPage[];\n if (engine === \"vision\") {\n const build = await ensureVisionBinary(runtime);\n if (!build.binary) {\n attempts.push({ engine, error: build.error ?? \"不可用\" });\n continue;\n }\n pages = await runVision(\n build.binary,\n materialized.file,\n input,\n runtime,\n );\n } else if (engine === \"tesseract\") {\n pages = await runTesseract(\n materialized.file,\n materialized.isPdf,\n input,\n runtime,\n materialized.tmpDir,\n );\n } else {\n pages = await runLlm(\n materialized.file,\n materialized.isPdf,\n input,\n runtime,\n materialized.tmpDir,\n );\n }\n if (pages.length === 0) throw new Error(\"未识别到任何页面\");\n return {\n ok: true,\n text: joinPages(pages),\n engine,\n pages,\n };\n } catch (err) {\n attempts.push({ engine, error: (err as Error).message });\n }\n }\n return {\n ok: false,\n error:\n attempts.length > 0\n ? `没有可用的 OCR 引擎: ${attempts\n .map((a) => `${a.engine}: ${a.error}`)\n .join(\"; \")}`\n : \"没有可用的 OCR 引擎\",\n how_to_fix: howToFix(runtime.platform),\n attempts,\n };\n } finally {\n materialized.cleanup();\n }\n}\n","/**\n * shell 运行时探测\n *\n * 决定 run_shell 用哪个 shell 执行命令,解决三件事:\n * - POSIX:沿用 Node 默认 /bin/sh,不改变既有语义;\n * - Windows:cmd.exe 没有 ls/grep/rm/cat,优先挑一个 POSIX 风格的 shell\n * (BusyBox-w32 / Git Bash / PowerShell),都没有时才回退到 %ComSpec%;\n * - 允许用 ZQAGENT_SHELL / ZQAGENT_BUSYBOX 显式覆盖,便于在装了非常规\n * shell(如内网自带的 busybox)的机器上接入。\n *\n * 本模块只做解析,不执行命令;platform / env / 文件探测均可注入,\n * 便于在 CI(Linux)上覆盖 Windows 分支。\n */\nimport * as fs from \"fs\";\nimport * as path from \"path\";\n\n/** shell 家族,决定命令行参数怎么拼 */\nexport type ShellKind = \"sh\" | \"busybox\" | \"powershell\" | \"cmd\" | \"default\";\n\nexport interface ShellSpec {\n kind: ShellKind;\n /** 人类可读标签,用于工具描述 */\n label: string;\n /** 可执行文件路径;null 表示交给 Node 默认(POSIX /bin/sh 或 %ComSpec%) */\n file: string | null;\n /** 拼出实际 argv(含命令本身) */\n buildArgs: (command: string) => string[];\n}\n\nexport interface ResolveShellOptions {\n platform?: NodeJS.Platform;\n env?: NodeJS.ProcessEnv;\n /** 判断文件是否存在,便于测试注入 */\n fileExists?: (p: string) => boolean;\n /** 用于定位「产物同目录」的 busybox,默认 process.execPath */\n execPath?: string;\n}\n\nexport interface ProbeContext {\n platform: NodeJS.Platform;\n env: NodeJS.ProcessEnv;\n fileExists: (p: string) => boolean;\n}\n\n/** Windows 可执行文件后缀(按优先级) */\nconst WINDOWS_SUFFIXES = [\".exe\", \".cmd\", \".bat\", \".com\", \"\"];\n\nfunction pathApi(platform: NodeJS.Platform) {\n return platform === \"win32\" ? path.win32 : path.posix;\n}\n\nfunction defaultFileExists(p: string): boolean {\n try {\n return fs.statSync(p).isFile();\n } catch {\n return false;\n }\n}\n\n/** 按目标平台(而非宿主)拼路径 */\nfunction joinPath(platform: NodeJS.Platform, ...parts: string[]): string {\n return pathApi(platform).join(...parts);\n}\n\n/**\n * 在 PATH 上查找可执行文件(同步,不 spawn 进程)。\n * name 含路径分隔符时按路径直接判定。Windows 会按 WINDOWS_SUFFIXES 补后缀。\n */\nexport function findExecutable(name: string, ctx: ProbeContext): string | null {\n if (name.includes(\"/\") || name.includes(\"\\\\\")) {\n return ctx.fileExists(name) ? name : null;\n }\n const suffixes = ctx.platform === \"win32\" ? WINDOWS_SUFFIXES : [\"\"];\n const delimiter = ctx.platform === \"win32\" ? \";\" : \":\";\n const dirs = (ctx.env.PATH ?? \"\").split(delimiter).filter((d) => d.length > 0);\n for (const dir of dirs) {\n for (const suffix of suffixes) {\n const candidate = joinPath(ctx.platform, dir, name + suffix);\n if (ctx.fileExists(candidate)) return candidate;\n }\n }\n return null;\n}\n\n/** 按文件名推断 shell 家族 */\nfunction kindFromBasename(base: string): ShellKind {\n const b = base.toLowerCase().replace(/\\.(exe|cmd|bat|com)$/, \"\");\n if (b === \"busybox\") return \"busybox\";\n if (b === \"powershell\" || b === \"pwsh\") return \"powershell\";\n if (b === \"cmd\") return \"cmd\";\n // sh/bash/dash/zsh/ash 以及不认识的名字(如 busybox 的 sh.exe 符号链接)都按 sh 处理\n return \"sh\";\n}\n\n/**\n * busybox 按 argv[0] 决定 applet:`busybox.exe` 需要显式给 applet 名\n * (`busybox.exe sh -c \"...\"`),而重命名/硬链接出来的 `sh.exe` 本身就是 ash,\n * 多传一个 \"sh\" 会被当成脚本文件名。\n */\nfunction busyboxArgs(file: string | null, command: string): string[] {\n const base = (file ? path.win32.basename(file) : \"busybox\")\n .toLowerCase()\n .replace(/\\.(exe|cmd|bat|com)$/, \"\");\n return base === \"busybox\" ? [\"sh\", \"-c\", command] : [\"-c\", command];\n}\n\nfunction argsFor(kind: ShellKind, command: string): string[] {\n switch (kind) {\n case \"busybox\":\n return [\"sh\", \"-c\", command];\n case \"powershell\":\n return [\"-NoProfile\", \"-NonInteractive\", \"-Command\", command];\n case \"cmd\":\n return [\"/d\", \"/s\", \"/c\", command];\n default:\n return [\"-c\", command];\n }\n}\n\nfunction makeSpec(kind: ShellKind, file: string | null, label: string): ShellSpec {\n return {\n kind,\n file,\n label,\n buildArgs: (command) =>\n kind === \"busybox\" ? busyboxArgs(file, command) : argsFor(kind, command),\n };\n}\n\nfunction defaultLabelFor(kind: ShellKind): string {\n switch (kind) {\n case \"busybox\":\n return \"busybox ash\";\n case \"powershell\":\n return \"PowerShell\";\n case \"cmd\":\n return \"cmd.exe\";\n case \"default\":\n return \"系统默认 shell\";\n default:\n return \"sh\";\n }\n}\n\n/**\n * 查找 BusyBox-w32。\n * 显式 ZQAGENT_BUSYBOX 优先;否则看 CLI 产物同目录及其 bin/ 子目录\n * (兼容 SEA 单文件、npm 安装与桌面 sidecar 三种布局,其中 sh.exe 是\n * busybox.exe 的重命名副本 —— busybox 按 argv[0] 决定 applet)。\n */\nfunction findBusybox(\n ctx: ProbeContext,\n execPath: string,\n): string | null {\n const explicit = (ctx.env.ZQAGENT_BUSYBOX ?? \"\").trim();\n if (explicit) return ctx.fileExists(explicit) ? explicit : null;\n\n const exeDir = pathApi(ctx.platform).dirname(execPath);\n const candidates = [\n joinPath(ctx.platform, exeDir, \"busybox.exe\"),\n joinPath(ctx.platform, exeDir, \"sh.exe\"),\n joinPath(ctx.platform, exeDir, \"bin\", \"busybox.exe\"),\n joinPath(ctx.platform, exeDir, \"bin\", \"sh.exe\"),\n ];\n for (const candidate of candidates) {\n if (ctx.fileExists(candidate)) return candidate;\n }\n return null;\n}\n\n/** 解析 ZQAGENT_SHELL 的显式取值(关键字或路径) */\nfunction resolveRequested(requested: string, ctx: ProbeContext, execPath: string): ShellSpec {\n const lower = requested.toLowerCase();\n\n if (lower === \"cmd\") {\n const file =\n findExecutable(\"cmd\", ctx) ?? ctx.env.ComSpec ?? ctx.env.COMSPEC ?? \"cmd.exe\";\n return makeSpec(\"cmd\", file, \"cmd.exe\");\n }\n if (lower === \"powershell\" || lower === \"pwsh\") {\n const file =\n findExecutable(lower, ctx) ?? findExecutable(\"powershell\", ctx) ?? `${lower}.exe`;\n return makeSpec(\"powershell\", file, \"PowerShell\");\n }\n if (lower === \"busybox\") {\n const found = findBusybox(ctx, execPath) ?? findExecutable(\"busybox\", ctx);\n return makeSpec(\"busybox\", found ?? requested, \"busybox ash\");\n }\n if ([\"sh\", \"bash\", \"dash\", \"zsh\", \"ash\"].includes(lower)) {\n return makeSpec(\"sh\", findExecutable(lower, ctx) ?? lower, lower);\n }\n\n // 视为路径(可能含空格,如 C:\\Program Files\\Git\\bin\\bash.exe)\n const base = pathApi(ctx.platform).basename(requested);\n const kind = kindFromBasename(base);\n return makeSpec(kind, requested, base || defaultLabelFor(kind));\n}\n\n/**\n * 解析当前应使用的 shell。\n * 返回值 file === null 时表示「不显式指定」,由 Node 走平台默认 shell\n * (POSIX /bin/sh / Windows %ComSpec%),即改造前的行为。\n */\nexport function resolveShellSpec(opts: ResolveShellOptions = {}): ShellSpec {\n const platform = opts.platform ?? process.platform;\n const env = opts.env ?? process.env;\n const execPath = opts.execPath ?? process.execPath;\n const ctx: ProbeContext = {\n platform,\n env,\n fileExists: opts.fileExists ?? defaultFileExists,\n };\n\n const requested = (env.ZQAGENT_SHELL ?? \"\").trim();\n if (requested && requested.toLowerCase() !== \"auto\") {\n return resolveRequested(requested, ctx, execPath);\n }\n\n // 非 Windows:保持 Node 默认(不改变既有行为)\n if (platform !== \"win32\") {\n return makeSpec(\"default\", null, \"/bin/sh\");\n }\n\n // Windows:优先 POSIX 风格 shell —— cmd.exe 缺 ls/grep/rm/cat,模型产出的\n // POSIX 命令在它下面几乎全废。\n const busybox = findBusybox(ctx, execPath);\n if (busybox) return makeSpec(\"busybox\", busybox, \"busybox ash (POSIX)\");\n\n const bash = findExecutable(\"bash\", ctx);\n if (bash) return makeSpec(\"sh\", bash, \"bash (Git for Windows)\");\n\n // PowerShell 排在 cmd 之前:它有 ls/cat/rm/cp/mv/pwd 别名,更接近模型习惯\n const shell = findExecutable(\"pwsh\", ctx) ?? findExecutable(\"powershell\", ctx);\n if (shell) return makeSpec(\"powershell\", shell, \"PowerShell\");\n\n return makeSpec(\"default\", null, env.ComSpec ?? env.COMSPEC ?? \"cmd.exe\");\n}\n\n/**\n * 为命令套上「编码设置 + 退出码传播」外壳,按 shell 家族区分。\n *\n * - PowerShell:先设置控制台输出编码为 UTF-8(同一次调用内即对后续命令生效,\n * 规避 `chcp 65001` 要等下一次调用才生效的问题),命令结束后若存在\n * `$LASTEXITCODE` 就以它作为进程退出码 —— 否则 PowerShell 对 `cmd /c ...`\n * 这类原生命令一律返回 0,工具拿到的 exit_code 不可信。\n * - cmd.exe(含 Windows 下未探测到其它 shell 时的 default):`chcp 65001` 切换\n * 代码页,避免中文被压成 `?`。\n * - POSIX sh / busybox:现代环境默认 UTF-8,不改动。\n */\nexport function wrapCommand(\n kind: ShellKind,\n command: string,\n platform: NodeJS.Platform = process.platform,\n): string {\n if (kind === \"powershell\") {\n return (\n \"$OutputEncoding = [Console]::OutputEncoding = [Text.Encoding]::UTF8; \" +\n command +\n \"\\nif ($null -ne $LASTEXITCODE) { exit $LASTEXITCODE }\"\n );\n }\n if (kind === \"cmd\" || (kind === \"default\" && platform === \"win32\")) {\n return `chcp 65001>nul & ${command}`;\n }\n return command;\n}\n\n/** 生成给模型的运行环境提示,追加到 run_shell 描述里 */\nexport function describeShell(spec: ShellSpec, platform: NodeJS.Platform): string {\n if (spec.kind === \"sh\" || spec.kind === \"busybox\") {\n return `当前平台 ${platform},shell=${spec.label}:请使用 POSIX 风格命令。`;\n }\n if (spec.kind === \"powershell\") {\n return (\n `当前平台 ${platform},shell=PowerShell:ls/cat/rm/cp/mv/pwd 等别名可用,` +\n `也可用 cmdlet;路径分隔符为 \"\\\\\"。`\n );\n }\n if (spec.kind === \"default\" && platform !== \"win32\") {\n return `当前平台 ${platform},shell=${spec.label}:请使用 POSIX 风格命令。`;\n }\n return (\n `当前平台 ${platform},shell=${spec.label}:请使用 Windows 命令` +\n `(dir/type/del/copy/move/findstr),多条命令用 & 或 && 连接;` +\n `不要使用 ls/cat/rm/grep 等 POSIX 命令。`\n );\n}\n\n/**\n * 解析命令里的「切目录」意图,返回目录与剩余命令。\n *\n * 识别 POSIX 的 `cd dir`、PowerShell 的 `Set-Location`/`sl`、cmd 的 `cd /d dir`,\n * 以及 `pushd`/`Push-Location`(只跟踪目标目录;popd 的栈语义不在此处理)。\n * 带 `&&` 时(如 `cd /d D:\\x && dir`)只把切目录部分交给 cwd 跟踪,剩余命令\n * 在新目录下执行 —— 否则目录变更只影响那一个子 shell,无法跨调用保持。\n *\n * target 为 null 时调用方应原样执行命令(未识别到切目录意图)。\n */\nexport function parseCdChain(\n command: string,\n opts: { platform?: NodeJS.Platform } = {},\n): { target: string | null; rest: string | null } {\n const platform = opts.platform ?? process.platform;\n const trimmed = command.trim();\n\n const chain = /^([\\s\\S]*?)\\s*&&\\s*([\\s\\S]+)$/.exec(trimmed);\n const head = (chain ? chain[1] : trimmed).trim();\n const rest = chain ? chain[2].trim() : null;\n\n const m = /^(?:cd|set-location|sl|pushd|push-location)\\b\\s*([\\s\\S]*)$/i.exec(head);\n if (!m) return { target: null, rest: null };\n\n let target = m[1].trim();\n // PowerShell 显式参数与 cmd 的盘符切换\n target = target.replace(/^-(?:path|literalpath)\\s+/i, \"\");\n if (platform === \"win32\") target = target.replace(/^\\/d\\s+/i, \"\");\n // 去引号\n target = target.replace(/^\"(.*)\"$/, \"$1\").replace(/^'(.*)'$/, \"$1\").trim();\n\n // 裸 `cd` / `cd -` 不跟踪(无法确定目标目录)\n if (!target || target === \"-\") return { target: null, rest: null };\n return { target, rest };\n}\n"],"mappings":"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","names":["path","path"]}
@@ -0,0 +1,278 @@
1
+ "use strict";
2
+ var __create = Object.create;
3
+ var __defProp = Object.defineProperty;
4
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
5
+ var __getOwnPropNames = Object.getOwnPropertyNames;
6
+ var __getProtoOf = Object.getPrototypeOf;
7
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
8
+ var __export = (target, all) => {
9
+ for (var name in all)
10
+ __defProp(target, name, { get: all[name], enumerable: true });
11
+ };
12
+ var __copyProps = (to, from, except, desc) => {
13
+ if (from && typeof from === "object" || typeof from === "function") {
14
+ for (let key of __getOwnPropNames(from))
15
+ if (!__hasOwnProp.call(to, key) && key !== except)
16
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
17
+ }
18
+ return to;
19
+ };
20
+ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
21
+ // If the importer is in node compatibility mode or this is not an ESM
22
+ // file that has been converted to a CommonJS file using a Babel-
23
+ // compatible transform (i.e. "__esModule" has not been set), then set
24
+ // "default" to the CommonJS "module.exports" for node compatibility.
25
+ isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
26
+ mod
27
+ ));
28
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
29
+
30
+ // src/mcp/config.ts
31
+ var config_exports = {};
32
+ __export(config_exports, {
33
+ BUILTIN_MCP_CONFIGS: () => BUILTIN_MCP_CONFIGS,
34
+ defaultMcpConfigPath: () => defaultMcpConfigPath,
35
+ loadMcpConfigFile: () => loadMcpConfigFile,
36
+ normalizeMcpConfig: () => normalizeMcpConfig,
37
+ normalizeMcpConfigs: () => normalizeMcpConfigs,
38
+ readMcpConfigFile: () => readMcpConfigFile,
39
+ saveMcpConfigFile: () => saveMcpConfigFile,
40
+ splitCommandLine: () => splitCommandLine,
41
+ withBuiltinMcpConfigs: () => withBuiltinMcpConfigs
42
+ });
43
+ module.exports = __toCommonJS(config_exports);
44
+ var import_node_fs = __toESM(require("fs"), 1);
45
+ var import_node_path = __toESM(require("path"), 1);
46
+ var TRANSPORT_TYPES = /* @__PURE__ */ new Set([
47
+ "stdio",
48
+ "http",
49
+ "sse",
50
+ "builtin"
51
+ ]);
52
+ var BUILTIN_MCP_CONFIGS = [
53
+ { name: "ocr", type: "builtin", builtin: "ocr", enabled: true }
54
+ ];
55
+ function defaultMcpConfigPath(workspace) {
56
+ return import_node_path.default.join(workspace, ".agent", "mcp.json");
57
+ }
58
+ function splitCommandLine(input) {
59
+ const out = [];
60
+ let cur = "";
61
+ let quote = null;
62
+ let escaped = false;
63
+ for (const ch of input) {
64
+ if (escaped) {
65
+ cur += ch;
66
+ escaped = false;
67
+ continue;
68
+ }
69
+ if (ch === "\\" && quote !== "'") {
70
+ escaped = true;
71
+ continue;
72
+ }
73
+ if (quote) {
74
+ if (ch === quote) quote = null;
75
+ else cur += ch;
76
+ continue;
77
+ }
78
+ if (ch === '"' || ch === "'") {
79
+ quote = ch;
80
+ continue;
81
+ }
82
+ if (/\s/.test(ch)) {
83
+ if (cur) {
84
+ out.push(cur);
85
+ cur = "";
86
+ }
87
+ continue;
88
+ }
89
+ cur += ch;
90
+ }
91
+ if (escaped) cur += "\\";
92
+ if (cur) out.push(cur);
93
+ return out;
94
+ }
95
+ function expandEnv(value, env, name, warnings) {
96
+ return value.replace(
97
+ /\$\{(?:env:)?([A-Za-z_][A-Za-z0-9_]*)\}/g,
98
+ (_m, key) => {
99
+ const v = env[key];
100
+ if (v === void 0) {
101
+ warnings.push(`"${name}" \u5F15\u7528\u4E86\u672A\u5B9A\u4E49\u7684\u73AF\u5883\u53D8\u91CF \${${key}}`);
102
+ return "";
103
+ }
104
+ return v;
105
+ }
106
+ );
107
+ }
108
+ function toStringMap(raw, field, name, env, warnings) {
109
+ if (raw === void 0 || raw === null) return void 0;
110
+ if (typeof raw !== "object" || Array.isArray(raw)) {
111
+ warnings.push(`"${name}".${field} \u5E94\u4E3A\u5BF9\u8C61\uFF0C\u5DF2\u5FFD\u7565`);
112
+ return void 0;
113
+ }
114
+ const out = {};
115
+ for (const [k, v] of Object.entries(raw)) {
116
+ if (v === void 0 || v === null) continue;
117
+ if (typeof v === "string") out[k] = expandEnv(v, env, name, warnings);
118
+ else if (typeof v === "number" || typeof v === "boolean")
119
+ out[k] = String(v);
120
+ else warnings.push(`"${name}".${field}.${k} \u4E0D\u662F\u5B57\u7B26\u4E32\uFF0C\u5DF2\u5FFD\u7565`);
121
+ }
122
+ return Object.keys(out).length > 0 ? out : void 0;
123
+ }
124
+ function normalizeMcpConfig(raw, fallbackName, opts = {}, warnings = []) {
125
+ const env = opts.env ?? process.env;
126
+ if (!raw || typeof raw !== "object" || Array.isArray(raw)) {
127
+ warnings.push(`MCP \u914D\u7F6E\u6761\u76EE${fallbackName ? ` "${fallbackName}"` : ""} \u4E0D\u662F\u5BF9\u8C61\uFF0C\u5DF2\u8DF3\u8FC7`);
128
+ return null;
129
+ }
130
+ const o = raw;
131
+ const name = typeof o.name === "string" && o.name.trim() ? o.name.trim() : fallbackName?.trim();
132
+ if (!name) {
133
+ warnings.push("MCP \u914D\u7F6E\u7F3A\u5C11 name\uFF0C\u5DF2\u8DF3\u8FC7");
134
+ return null;
135
+ }
136
+ const rawType = typeof o.type === "string" ? o.type.toLowerCase() : "";
137
+ let type;
138
+ if (TRANSPORT_TYPES.has(rawType)) type = rawType;
139
+ else if (rawType === "streamable-http" || rawType === "streamablehttp")
140
+ type = "http";
141
+ else if (typeof o.builtin === "string") type = "builtin";
142
+ else if (typeof o.url === "string")
143
+ type = /\/sse(?:[?#]|$)/i.test(o.url) ? "sse" : "http";
144
+ else if (o.command !== void 0) type = "stdio";
145
+ if (!type) {
146
+ warnings.push(`MCP "${name}" \u65E0\u6CD5\u63A8\u65AD\u4F20\u8F93\u7C7B\u578B\uFF08\u9700\u8981 builtin/command/url\uFF09`);
147
+ return null;
148
+ }
149
+ const enabled = o.enabled === false || o.disabled === true ? false : true;
150
+ const config = { name, type, enabled };
151
+ if (type === "stdio") {
152
+ const command = [];
153
+ if (Array.isArray(o.command)) {
154
+ for (const part of o.command) {
155
+ if (typeof part === "string") command.push(part);
156
+ else warnings.push(`MCP "${name}" \u7684 command \u542B\u975E\u5B57\u7B26\u4E32\u9879\uFF0C\u5DF2\u5FFD\u7565`);
157
+ }
158
+ } else if (typeof o.command === "string") {
159
+ command.push(...splitCommandLine(o.command));
160
+ }
161
+ if (Array.isArray(o.args)) {
162
+ for (const part of o.args) {
163
+ if (typeof part === "string") command.push(part);
164
+ else warnings.push(`MCP "${name}" \u7684 args \u542B\u975E\u5B57\u7B26\u4E32\u9879\uFF0C\u5DF2\u5FFD\u7565`);
165
+ }
166
+ } else if (typeof o.args === "string") {
167
+ command.push(...splitCommandLine(o.args));
168
+ }
169
+ if (command.length === 0) {
170
+ warnings.push(`MCP "${name}" \u7F3A\u5C11 command\uFF0C\u5DF2\u8DF3\u8FC7`);
171
+ return null;
172
+ }
173
+ config.command = command.map((c) => expandEnv(c, env, name, warnings));
174
+ if (typeof o.cwd === "string" && o.cwd.trim())
175
+ config.cwd = expandEnv(o.cwd, env, name, warnings);
176
+ const stdioEnv = toStringMap(o.env, "env", name, env, warnings);
177
+ if (stdioEnv) config.env = stdioEnv;
178
+ } else if (type === "builtin") {
179
+ config.builtin = typeof o.builtin === "string" && o.builtin.trim() ? o.builtin.trim() : name;
180
+ } else {
181
+ if (typeof o.url !== "string" || !o.url.trim()) {
182
+ warnings.push(`MCP "${name}" \u7F3A\u5C11 url\uFF0C\u5DF2\u8DF3\u8FC7`);
183
+ return null;
184
+ }
185
+ config.url = expandEnv(o.url.trim(), env, name, warnings);
186
+ const headers = toStringMap(o.headers, "headers", name, env, warnings);
187
+ if (headers) config.headers = headers;
188
+ }
189
+ return config;
190
+ }
191
+ function normalizeMcpConfigs(raw, opts = {}) {
192
+ const warnings = [];
193
+ const configs = [];
194
+ const add = (item, fallbackName) => {
195
+ const cfg = normalizeMcpConfig(item, fallbackName, opts, warnings);
196
+ if (!cfg) return;
197
+ const idx = configs.findIndex((c) => c.name === cfg.name);
198
+ if (idx >= 0) {
199
+ warnings.push(`MCP "${cfg.name}" \u91CD\u590D\u51FA\u73B0\uFF0C\u540E\u8005\u8986\u76D6\u524D\u8005`);
200
+ configs[idx] = cfg;
201
+ } else {
202
+ configs.push(cfg);
203
+ }
204
+ };
205
+ if (Array.isArray(raw)) {
206
+ for (const item of raw) add(item);
207
+ } else if (raw && typeof raw === "object") {
208
+ const o = raw;
209
+ const objectMap = (value) => {
210
+ if (!value || typeof value !== "object" || Array.isArray(value))
211
+ return false;
212
+ for (const [k, v] of Object.entries(value))
213
+ add(v, k);
214
+ return true;
215
+ };
216
+ if (Array.isArray(o.servers)) {
217
+ for (const item of o.servers) add(item);
218
+ } else if (Array.isArray(o.mcpServers)) {
219
+ for (const item of o.mcpServers) add(item);
220
+ } else if (objectMap(o.mcpServers)) {
221
+ } else if (objectMap(o.servers)) {
222
+ } else {
223
+ add(o);
224
+ }
225
+ } else {
226
+ warnings.push("MCP \u914D\u7F6E\u5FC5\u987B\u662F JSON \u5BF9\u8C61\u6216\u6570\u7EC4");
227
+ }
228
+ return { configs, warnings };
229
+ }
230
+ function readMcpConfigFile(file, opts = {}) {
231
+ if (!file) return { configs: [], warnings: [] };
232
+ try {
233
+ if (!import_node_fs.default.existsSync(file)) return { configs: [], warnings: [] };
234
+ const raw = JSON.parse(import_node_fs.default.readFileSync(file, "utf8"));
235
+ return normalizeMcpConfigs(raw, opts);
236
+ } catch (err) {
237
+ return {
238
+ configs: [],
239
+ warnings: [`\u8BFB\u53D6 MCP \u914D\u7F6E\u5931\u8D25 ${file}: ${err.message}`]
240
+ };
241
+ }
242
+ }
243
+ function saveMcpConfigFile(file, configs) {
244
+ import_node_fs.default.mkdirSync(import_node_path.default.dirname(file), { recursive: true });
245
+ import_node_fs.default.writeFileSync(
246
+ file,
247
+ JSON.stringify(
248
+ configs.map((c) => JSON.parse(JSON.stringify(c))),
249
+ null,
250
+ 2
251
+ ) + "\n",
252
+ "utf8"
253
+ );
254
+ }
255
+ function withBuiltinMcpConfigs(configs) {
256
+ const existing = new Set(configs.map((c) => c.name));
257
+ const builtin = BUILTIN_MCP_CONFIGS.filter((c) => !existing.has(c.name)).map(
258
+ (c) => ({ ...c })
259
+ );
260
+ return [...configs, ...builtin];
261
+ }
262
+ function loadMcpConfigFile(file, opts = {}) {
263
+ const result = readMcpConfigFile(file, opts);
264
+ return { configs: withBuiltinMcpConfigs(result.configs), warnings: result.warnings };
265
+ }
266
+ // Annotate the CommonJS export names for ESM import in node:
267
+ 0 && (module.exports = {
268
+ BUILTIN_MCP_CONFIGS,
269
+ defaultMcpConfigPath,
270
+ loadMcpConfigFile,
271
+ normalizeMcpConfig,
272
+ normalizeMcpConfigs,
273
+ readMcpConfigFile,
274
+ saveMcpConfigFile,
275
+ splitCommandLine,
276
+ withBuiltinMcpConfigs
277
+ });
278
+ //# sourceMappingURL=config.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../../src/mcp/config.ts"],"sourcesContent":["/**\n * MCP 配置解析与持久化(CLI / server 共用)\n *\n * 支持多种来源格式,避免「拷一份别人的配置进来却静默失效」:\n * - 裸数组:[{ name, type, command|url, ... }]\n * - { servers: [...] } / { servers: { name: {...} } }\n * - { mcpServers: [...] } / { mcpServers: { name: {...} } }(Claude Desktop / Cursor)\n *\n * 单条配置容错:\n * - 省略 type 时按 builtin / url / command 推断\n * - Claude 风格 command(string) + args(string[]) 合并为 command: string[]\n * - env/headers 值允许数字与布尔(转字符串)\n * - 字符串中的 ${VAR} / ${env:VAR} 用进程环境变量展开\n *\n * 内置服务器(如 OCR)默认追加:用户配置中存在同名条目时以用户条目为准,\n * 因此可以用 { name: \"ocr\", enabled: false } 关闭内置能力。\n */\n\nimport fs from \"node:fs\";\nimport path from \"node:path\";\nimport type { McpServerConfig, McpTransportType } from \"./McpClient.js\";\n\n/** 归一化结果 */\nexport interface McpConfigNormalizeResult {\n configs: McpServerConfig[];\n warnings: string[];\n}\n\n/** 归一化选项 */\nexport interface McpConfigNormalizeOptions {\n /** 环境变量(默认 process.env),用于 ${VAR} 展开 */\n env?: NodeJS.ProcessEnv;\n}\n\nconst TRANSPORT_TYPES: ReadonlySet<string> = new Set([\n \"stdio\",\n \"http\",\n \"sse\",\n \"builtin\",\n]);\n\n/** 内置 MCP 服务器默认配置(用户同名配置优先) */\nexport const BUILTIN_MCP_CONFIGS: ReadonlyArray<McpServerConfig> = [\n { name: \"ocr\", type: \"builtin\", builtin: \"ocr\", enabled: true },\n];\n\n/** 工作区默认配置文件路径 */\nexport function defaultMcpConfigPath(workspace: string): string {\n return path.join(workspace, \".agent\", \"mcp.json\");\n}\n\n/**\n * 拆分 shell 风格命令行(支持单/双引号与反斜杠转义)。\n * 用于兼容 `command: \"npx -y some-server\"` 这类单字符串写法。\n */\nexport function splitCommandLine(input: string): string[] {\n const out: string[] = [];\n let cur = \"\";\n let quote: '\"' | \"'\" | null = null;\n let escaped = false;\n for (const ch of input) {\n if (escaped) {\n cur += ch;\n escaped = false;\n continue;\n }\n if (ch === \"\\\\\" && quote !== \"'\") {\n escaped = true;\n continue;\n }\n if (quote) {\n if (ch === quote) quote = null;\n else cur += ch;\n continue;\n }\n if (ch === '\"' || ch === \"'\") {\n quote = ch;\n continue;\n }\n if (/\\s/.test(ch)) {\n if (cur) {\n out.push(cur);\n cur = \"\";\n }\n continue;\n }\n cur += ch;\n }\n if (escaped) cur += \"\\\\\";\n if (cur) out.push(cur);\n return out;\n}\n\nfunction expandEnv(\n value: string,\n env: NodeJS.ProcessEnv,\n name: string,\n warnings: string[],\n): string {\n return value.replace(\n /\\$\\{(?:env:)?([A-Za-z_][A-Za-z0-9_]*)\\}/g,\n (_m, key: string) => {\n const v = env[key];\n if (v === undefined) {\n warnings.push(`\"${name}\" 引用了未定义的环境变量 \\${${key}}`);\n return \"\";\n }\n return v;\n },\n );\n}\n\nfunction toStringMap(\n raw: unknown,\n field: string,\n name: string,\n env: NodeJS.ProcessEnv,\n warnings: string[],\n): Record<string, string> | undefined {\n if (raw === undefined || raw === null) return undefined;\n if (typeof raw !== \"object\" || Array.isArray(raw)) {\n warnings.push(`\"${name}\".${field} 应为对象,已忽略`);\n return undefined;\n }\n const out: Record<string, string> = {};\n for (const [k, v] of Object.entries(raw as Record<string, unknown>)) {\n if (v === undefined || v === null) continue;\n if (typeof v === \"string\") out[k] = expandEnv(v, env, name, warnings);\n else if (typeof v === \"number\" || typeof v === \"boolean\")\n out[k] = String(v);\n else warnings.push(`\"${name}\".${field}.${k} 不是字符串,已忽略`);\n }\n return Object.keys(out).length > 0 ? out : undefined;\n}\n\n/**\n * 归一化单条 MCP 配置。\n * @param raw 原始条目(数组元素或 mcpServers 映射的值)\n * @param fallbackName mcpServers 映射的键名\n * @returns 合法配置;无法解析时返回 null(原因写入 warnings)\n */\nexport function normalizeMcpConfig(\n raw: unknown,\n fallbackName?: string,\n opts: McpConfigNormalizeOptions = {},\n warnings: string[] = [],\n): McpServerConfig | null {\n const env = opts.env ?? process.env;\n if (!raw || typeof raw !== \"object\" || Array.isArray(raw)) {\n warnings.push(`MCP 配置条目${fallbackName ? ` \"${fallbackName}\"` : \"\"} 不是对象,已跳过`);\n return null;\n }\n const o = raw as Record<string, unknown>;\n const name =\n typeof o.name === \"string\" && o.name.trim()\n ? o.name.trim()\n : fallbackName?.trim();\n if (!name) {\n warnings.push(\"MCP 配置缺少 name,已跳过\");\n return null;\n }\n\n const rawType = typeof o.type === \"string\" ? o.type.toLowerCase() : \"\";\n let type: McpTransportType | undefined;\n if (TRANSPORT_TYPES.has(rawType)) type = rawType as McpTransportType;\n else if (rawType === \"streamable-http\" || rawType === \"streamablehttp\")\n type = \"http\";\n else if (typeof o.builtin === \"string\") type = \"builtin\";\n else if (typeof o.url === \"string\")\n type = /\\/sse(?:[?#]|$)/i.test(o.url) ? \"sse\" : \"http\";\n else if (o.command !== undefined) type = \"stdio\";\n\n if (!type) {\n warnings.push(`MCP \"${name}\" 无法推断传输类型(需要 builtin/command/url)`);\n return null;\n }\n\n const enabled =\n o.enabled === false || o.disabled === true ? false : true;\n const config: McpServerConfig = { name, type, enabled };\n\n if (type === \"stdio\") {\n const command: string[] = [];\n if (Array.isArray(o.command)) {\n for (const part of o.command) {\n if (typeof part === \"string\") command.push(part);\n else warnings.push(`MCP \"${name}\" 的 command 含非字符串项,已忽略`);\n }\n } else if (typeof o.command === \"string\") {\n command.push(...splitCommandLine(o.command));\n }\n if (Array.isArray(o.args)) {\n for (const part of o.args) {\n if (typeof part === \"string\") command.push(part);\n else warnings.push(`MCP \"${name}\" 的 args 含非字符串项,已忽略`);\n }\n } else if (typeof o.args === \"string\") {\n command.push(...splitCommandLine(o.args));\n }\n if (command.length === 0) {\n warnings.push(`MCP \"${name}\" 缺少 command,已跳过`);\n return null;\n }\n config.command = command.map((c) => expandEnv(c, env, name, warnings));\n if (typeof o.cwd === \"string\" && o.cwd.trim())\n config.cwd = expandEnv(o.cwd, env, name, warnings);\n const stdioEnv = toStringMap(o.env, \"env\", name, env, warnings);\n if (stdioEnv) config.env = stdioEnv;\n } else if (type === \"builtin\") {\n config.builtin =\n typeof o.builtin === \"string\" && o.builtin.trim()\n ? o.builtin.trim()\n : name;\n } else {\n if (typeof o.url !== \"string\" || !o.url.trim()) {\n warnings.push(`MCP \"${name}\" 缺少 url,已跳过`);\n return null;\n }\n config.url = expandEnv(o.url.trim(), env, name, warnings);\n const headers = toStringMap(o.headers, \"headers\", name, env, warnings);\n if (headers) config.headers = headers;\n }\n\n return config;\n}\n\n/** 归一化任意受支持的配置形状 */\nexport function normalizeMcpConfigs(\n raw: unknown,\n opts: McpConfigNormalizeOptions = {},\n): McpConfigNormalizeResult {\n const warnings: string[] = [];\n const configs: McpServerConfig[] = [];\n\n const add = (item: unknown, fallbackName?: string): void => {\n const cfg = normalizeMcpConfig(item, fallbackName, opts, warnings);\n if (!cfg) return;\n const idx = configs.findIndex((c) => c.name === cfg.name);\n if (idx >= 0) {\n warnings.push(`MCP \"${cfg.name}\" 重复出现,后者覆盖前者`);\n configs[idx] = cfg;\n } else {\n configs.push(cfg);\n }\n };\n\n if (Array.isArray(raw)) {\n for (const item of raw) add(item);\n } else if (raw && typeof raw === \"object\") {\n const o = raw as Record<string, unknown>;\n const objectMap = (value: unknown): boolean => {\n if (!value || typeof value !== \"object\" || Array.isArray(value))\n return false;\n for (const [k, v] of Object.entries(value as Record<string, unknown>))\n add(v, k);\n return true;\n };\n if (Array.isArray(o.servers)) {\n for (const item of o.servers) add(item);\n } else if (Array.isArray(o.mcpServers)) {\n for (const item of o.mcpServers) add(item);\n } else if (objectMap(o.mcpServers)) {\n // Claude Desktop / Cursor: { mcpServers: { name: { command, args } } }\n } else if (objectMap(o.servers)) {\n // { servers: { name: {...} } }\n } else {\n // 单条配置对象\n add(o);\n }\n } else {\n warnings.push(\"MCP 配置必须是 JSON 对象或数组\");\n }\n\n return { configs, warnings };\n}\n\n/**\n * 读取配置文件并归一化。文件不存在时返回空结果(不视为错误)。\n * @param file 配置文件路径;未提供时返回空结果\n */\nexport function readMcpConfigFile(\n file: string | undefined,\n opts: McpConfigNormalizeOptions = {},\n): McpConfigNormalizeResult {\n if (!file) return { configs: [], warnings: [] };\n try {\n if (!fs.existsSync(file)) return { configs: [], warnings: [] };\n const raw: unknown = JSON.parse(fs.readFileSync(file, \"utf8\"));\n return normalizeMcpConfigs(raw, opts);\n } catch (err) {\n return {\n configs: [],\n warnings: [`读取 MCP 配置失败 ${file}: ${(err as Error).message}`],\n };\n }\n}\n\n/** 保存配置(统一写为数组格式,自动创建目录) */\nexport function saveMcpConfigFile(\n file: string,\n configs: McpServerConfig[],\n): void {\n fs.mkdirSync(path.dirname(file), { recursive: true });\n fs.writeFileSync(\n file,\n JSON.stringify(\n configs.map((c) => JSON.parse(JSON.stringify(c)) as McpServerConfig),\n null,\n 2,\n ) + \"\\n\",\n \"utf8\",\n );\n}\n\n/** 追加内置默认配置(同名用户配置优先) */\nexport function withBuiltinMcpConfigs(\n configs: McpServerConfig[],\n): McpServerConfig[] {\n const existing = new Set(configs.map((c) => c.name));\n const builtin = BUILTIN_MCP_CONFIGS.filter((c) => !existing.has(c.name)).map(\n (c) => ({ ...c }),\n );\n return [...configs, ...builtin];\n}\n\n/** 读取配置文件并追加内置默认配置 */\nexport function loadMcpConfigFile(\n file: string | undefined,\n opts: McpConfigNormalizeOptions = {},\n): McpConfigNormalizeResult {\n const result = readMcpConfigFile(file, opts);\n return { configs: withBuiltinMcpConfigs(result.configs), warnings: result.warnings };\n}\n"],"mappings":"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","names":["path","fs"]}
@@ -0,0 +1,61 @@
1
+ import { a as McpServerConfig } from '../../McpClient-CEQBUPuC.cjs';
2
+
3
+ /**
4
+ * MCP 配置解析与持久化(CLI / server 共用)
5
+ *
6
+ * 支持多种来源格式,避免「拷一份别人的配置进来却静默失效」:
7
+ * - 裸数组:[{ name, type, command|url, ... }]
8
+ * - { servers: [...] } / { servers: { name: {...} } }
9
+ * - { mcpServers: [...] } / { mcpServers: { name: {...} } }(Claude Desktop / Cursor)
10
+ *
11
+ * 单条配置容错:
12
+ * - 省略 type 时按 builtin / url / command 推断
13
+ * - Claude 风格 command(string) + args(string[]) 合并为 command: string[]
14
+ * - env/headers 值允许数字与布尔(转字符串)
15
+ * - 字符串中的 ${VAR} / ${env:VAR} 用进程环境变量展开
16
+ *
17
+ * 内置服务器(如 OCR)默认追加:用户配置中存在同名条目时以用户条目为准,
18
+ * 因此可以用 { name: "ocr", enabled: false } 关闭内置能力。
19
+ */
20
+
21
+ /** 归一化结果 */
22
+ interface McpConfigNormalizeResult {
23
+ configs: McpServerConfig[];
24
+ warnings: string[];
25
+ }
26
+ /** 归一化选项 */
27
+ interface McpConfigNormalizeOptions {
28
+ /** 环境变量(默认 process.env),用于 ${VAR} 展开 */
29
+ env?: NodeJS.ProcessEnv;
30
+ }
31
+ /** 内置 MCP 服务器默认配置(用户同名配置优先) */
32
+ declare const BUILTIN_MCP_CONFIGS: ReadonlyArray<McpServerConfig>;
33
+ /** 工作区默认配置文件路径 */
34
+ declare function defaultMcpConfigPath(workspace: string): string;
35
+ /**
36
+ * 拆分 shell 风格命令行(支持单/双引号与反斜杠转义)。
37
+ * 用于兼容 `command: "npx -y some-server"` 这类单字符串写法。
38
+ */
39
+ declare function splitCommandLine(input: string): string[];
40
+ /**
41
+ * 归一化单条 MCP 配置。
42
+ * @param raw 原始条目(数组元素或 mcpServers 映射的值)
43
+ * @param fallbackName mcpServers 映射的键名
44
+ * @returns 合法配置;无法解析时返回 null(原因写入 warnings)
45
+ */
46
+ declare function normalizeMcpConfig(raw: unknown, fallbackName?: string, opts?: McpConfigNormalizeOptions, warnings?: string[]): McpServerConfig | null;
47
+ /** 归一化任意受支持的配置形状 */
48
+ declare function normalizeMcpConfigs(raw: unknown, opts?: McpConfigNormalizeOptions): McpConfigNormalizeResult;
49
+ /**
50
+ * 读取配置文件并归一化。文件不存在时返回空结果(不视为错误)。
51
+ * @param file 配置文件路径;未提供时返回空结果
52
+ */
53
+ declare function readMcpConfigFile(file: string | undefined, opts?: McpConfigNormalizeOptions): McpConfigNormalizeResult;
54
+ /** 保存配置(统一写为数组格式,自动创建目录) */
55
+ declare function saveMcpConfigFile(file: string, configs: McpServerConfig[]): void;
56
+ /** 追加内置默认配置(同名用户配置优先) */
57
+ declare function withBuiltinMcpConfigs(configs: McpServerConfig[]): McpServerConfig[];
58
+ /** 读取配置文件并追加内置默认配置 */
59
+ declare function loadMcpConfigFile(file: string | undefined, opts?: McpConfigNormalizeOptions): McpConfigNormalizeResult;
60
+
61
+ export { BUILTIN_MCP_CONFIGS, type McpConfigNormalizeOptions, type McpConfigNormalizeResult, defaultMcpConfigPath, loadMcpConfigFile, normalizeMcpConfig, normalizeMcpConfigs, readMcpConfigFile, saveMcpConfigFile, splitCommandLine, withBuiltinMcpConfigs };
@@ -0,0 +1,61 @@
1
+ import { a as McpServerConfig } from '../../McpClient-CEQBUPuC.js';
2
+
3
+ /**
4
+ * MCP 配置解析与持久化(CLI / server 共用)
5
+ *
6
+ * 支持多种来源格式,避免「拷一份别人的配置进来却静默失效」:
7
+ * - 裸数组:[{ name, type, command|url, ... }]
8
+ * - { servers: [...] } / { servers: { name: {...} } }
9
+ * - { mcpServers: [...] } / { mcpServers: { name: {...} } }(Claude Desktop / Cursor)
10
+ *
11
+ * 单条配置容错:
12
+ * - 省略 type 时按 builtin / url / command 推断
13
+ * - Claude 风格 command(string) + args(string[]) 合并为 command: string[]
14
+ * - env/headers 值允许数字与布尔(转字符串)
15
+ * - 字符串中的 ${VAR} / ${env:VAR} 用进程环境变量展开
16
+ *
17
+ * 内置服务器(如 OCR)默认追加:用户配置中存在同名条目时以用户条目为准,
18
+ * 因此可以用 { name: "ocr", enabled: false } 关闭内置能力。
19
+ */
20
+
21
+ /** 归一化结果 */
22
+ interface McpConfigNormalizeResult {
23
+ configs: McpServerConfig[];
24
+ warnings: string[];
25
+ }
26
+ /** 归一化选项 */
27
+ interface McpConfigNormalizeOptions {
28
+ /** 环境变量(默认 process.env),用于 ${VAR} 展开 */
29
+ env?: NodeJS.ProcessEnv;
30
+ }
31
+ /** 内置 MCP 服务器默认配置(用户同名配置优先) */
32
+ declare const BUILTIN_MCP_CONFIGS: ReadonlyArray<McpServerConfig>;
33
+ /** 工作区默认配置文件路径 */
34
+ declare function defaultMcpConfigPath(workspace: string): string;
35
+ /**
36
+ * 拆分 shell 风格命令行(支持单/双引号与反斜杠转义)。
37
+ * 用于兼容 `command: "npx -y some-server"` 这类单字符串写法。
38
+ */
39
+ declare function splitCommandLine(input: string): string[];
40
+ /**
41
+ * 归一化单条 MCP 配置。
42
+ * @param raw 原始条目(数组元素或 mcpServers 映射的值)
43
+ * @param fallbackName mcpServers 映射的键名
44
+ * @returns 合法配置;无法解析时返回 null(原因写入 warnings)
45
+ */
46
+ declare function normalizeMcpConfig(raw: unknown, fallbackName?: string, opts?: McpConfigNormalizeOptions, warnings?: string[]): McpServerConfig | null;
47
+ /** 归一化任意受支持的配置形状 */
48
+ declare function normalizeMcpConfigs(raw: unknown, opts?: McpConfigNormalizeOptions): McpConfigNormalizeResult;
49
+ /**
50
+ * 读取配置文件并归一化。文件不存在时返回空结果(不视为错误)。
51
+ * @param file 配置文件路径;未提供时返回空结果
52
+ */
53
+ declare function readMcpConfigFile(file: string | undefined, opts?: McpConfigNormalizeOptions): McpConfigNormalizeResult;
54
+ /** 保存配置(统一写为数组格式,自动创建目录) */
55
+ declare function saveMcpConfigFile(file: string, configs: McpServerConfig[]): void;
56
+ /** 追加内置默认配置(同名用户配置优先) */
57
+ declare function withBuiltinMcpConfigs(configs: McpServerConfig[]): McpServerConfig[];
58
+ /** 读取配置文件并追加内置默认配置 */
59
+ declare function loadMcpConfigFile(file: string | undefined, opts?: McpConfigNormalizeOptions): McpConfigNormalizeResult;
60
+
61
+ export { BUILTIN_MCP_CONFIGS, type McpConfigNormalizeOptions, type McpConfigNormalizeResult, defaultMcpConfigPath, loadMcpConfigFile, normalizeMcpConfig, normalizeMcpConfigs, readMcpConfigFile, saveMcpConfigFile, splitCommandLine, withBuiltinMcpConfigs };