reasonix 0.32.0 → 0.33.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cli/chat-Q5ZCVIOO.js +39 -0
- package/dist/cli/chunk-2AWTGJ2C.js +110 -0
- package/dist/cli/chunk-2AWTGJ2C.js.map +1 -0
- package/dist/cli/chunk-3Q3C4W66.js +30 -0
- package/dist/cli/chunk-3Q3C4W66.js.map +1 -0
- package/dist/cli/chunk-4DCHFFEY.js +149 -0
- package/dist/cli/chunk-4DCHFFEY.js.map +1 -0
- package/dist/cli/chunk-5X7LZJDE.js +36 -0
- package/dist/cli/chunk-5X7LZJDE.js.map +1 -0
- package/dist/cli/chunk-63KAV5DX.js +106 -0
- package/dist/cli/chunk-63KAV5DX.js.map +1 -0
- package/dist/cli/chunk-6TMHAK5D.js +576 -0
- package/dist/cli/chunk-6TMHAK5D.js.map +1 -0
- package/dist/cli/chunk-APPB3ZPQ.js +43 -0
- package/dist/cli/chunk-APPB3ZPQ.js.map +1 -0
- package/dist/cli/chunk-BQNUJJN7.js +42 -0
- package/dist/cli/chunk-BQNUJJN7.js.map +1 -0
- package/dist/cli/chunk-CPOV2O73.js +39 -0
- package/dist/cli/chunk-CPOV2O73.js.map +1 -0
- package/dist/cli/chunk-D5DKXIP5.js +368 -0
- package/dist/cli/chunk-D5DKXIP5.js.map +1 -0
- package/dist/cli/chunk-DFP4YSVM.js +247 -0
- package/dist/cli/chunk-DFP4YSVM.js.map +1 -0
- package/dist/cli/chunk-DULSP7JH.js +410 -0
- package/dist/cli/chunk-DULSP7JH.js.map +1 -0
- package/dist/cli/chunk-FM57FNPJ.js +46 -0
- package/dist/cli/chunk-FM57FNPJ.js.map +1 -0
- package/dist/cli/chunk-FWGEHRB7.js +54 -0
- package/dist/cli/chunk-FWGEHRB7.js.map +1 -0
- package/dist/cli/chunk-FXGQ5NHE.js +513 -0
- package/dist/cli/chunk-FXGQ5NHE.js.map +1 -0
- package/dist/cli/chunk-G3XNWSFN.js +53 -0
- package/dist/cli/chunk-G3XNWSFN.js.map +1 -0
- package/dist/cli/chunk-I6YIAK6C.js +757 -0
- package/dist/cli/chunk-I6YIAK6C.js.map +1 -0
- package/dist/cli/chunk-J5VLP23S.js +94 -0
- package/dist/cli/chunk-J5VLP23S.js.map +1 -0
- package/dist/cli/chunk-KMWKGPFZ.js +303 -0
- package/dist/cli/chunk-KMWKGPFZ.js.map +1 -0
- package/dist/cli/chunk-MDHVWCJ4.js +14965 -0
- package/dist/cli/chunk-MDHVWCJ4.js.map +1 -0
- package/dist/cli/chunk-MHDNZXJJ.js +48 -0
- package/dist/cli/chunk-MHDNZXJJ.js.map +1 -0
- package/dist/cli/chunk-ORM6PK57.js +140 -0
- package/dist/cli/chunk-ORM6PK57.js.map +1 -0
- package/dist/cli/chunk-Q6YFXW7H.js +4986 -0
- package/dist/cli/chunk-Q6YFXW7H.js.map +1 -0
- package/dist/cli/chunk-QGE6AF76.js +1467 -0
- package/dist/cli/chunk-QGE6AF76.js.map +1 -0
- package/dist/cli/chunk-RFX7TYVV.js +28 -0
- package/dist/cli/chunk-RFX7TYVV.js.map +1 -0
- package/dist/cli/chunk-RZILUXUC.js +940 -0
- package/dist/cli/chunk-RZILUXUC.js.map +1 -0
- package/dist/cli/chunk-SDE5U32Z.js +535 -0
- package/dist/cli/chunk-SDE5U32Z.js.map +1 -0
- package/dist/cli/chunk-SOZE7V7V.js +340 -0
- package/dist/cli/chunk-SOZE7V7V.js.map +1 -0
- package/dist/cli/chunk-U3V2ZQ5J.js +479 -0
- package/dist/cli/chunk-U3V2ZQ5J.js.map +1 -0
- package/dist/cli/chunk-W4LDFAZ6.js +1544 -0
- package/dist/cli/chunk-W4LDFAZ6.js.map +1 -0
- package/dist/cli/chunk-WBDE4IRI.js +208 -0
- package/dist/cli/chunk-WBDE4IRI.js.map +1 -0
- package/dist/cli/chunk-XHQIK7B6.js +189 -0
- package/dist/cli/chunk-XHQIK7B6.js.map +1 -0
- package/dist/cli/chunk-XJLZ4HKU.js +307 -0
- package/dist/cli/chunk-XJLZ4HKU.js.map +1 -0
- package/dist/cli/chunk-ZPTSJGX5.js +88 -0
- package/dist/cli/chunk-ZPTSJGX5.js.map +1 -0
- package/dist/cli/chunk-ZTLZO42A.js +231 -0
- package/dist/cli/chunk-ZTLZO42A.js.map +1 -0
- package/dist/cli/code-DLR77NPZ.js +151 -0
- package/dist/cli/code-DLR77NPZ.js.map +1 -0
- package/dist/cli/commands-JWT2MWVH.js +352 -0
- package/dist/cli/commands-JWT2MWVH.js.map +1 -0
- package/dist/cli/commit-RPZBOZS2.js +288 -0
- package/dist/cli/commit-RPZBOZS2.js.map +1 -0
- package/dist/cli/diff-NTEHCSDW.js +145 -0
- package/dist/cli/diff-NTEHCSDW.js.map +1 -0
- package/dist/cli/doctor-3TGB2NZN.js +19 -0
- package/dist/cli/doctor-3TGB2NZN.js.map +1 -0
- package/dist/cli/events-P27CX7LN.js +338 -0
- package/dist/cli/events-P27CX7LN.js.map +1 -0
- package/dist/cli/index.js +80 -33693
- package/dist/cli/index.js.map +1 -1
- package/dist/cli/mcp-ARTNQ24O.js +266 -0
- package/dist/cli/mcp-ARTNQ24O.js.map +1 -0
- package/dist/cli/mcp-browse-HLO2ENDL.js +163 -0
- package/dist/cli/mcp-browse-HLO2ENDL.js.map +1 -0
- package/dist/cli/mcp-inspect-T2HBR22P.js +103 -0
- package/dist/cli/mcp-inspect-T2HBR22P.js.map +1 -0
- package/dist/cli/{prompt-XHICFAYN.js → prompt-V47QKSAR.js} +3 -2
- package/dist/cli/prompt-V47QKSAR.js.map +1 -0
- package/dist/cli/prune-sessions-ERL6B4G5.js +42 -0
- package/dist/cli/prune-sessions-ERL6B4G5.js.map +1 -0
- package/dist/cli/replay-Q43DSMG6.js +273 -0
- package/dist/cli/replay-Q43DSMG6.js.map +1 -0
- package/dist/cli/run-JMEOTQCG.js +215 -0
- package/dist/cli/run-JMEOTQCG.js.map +1 -0
- package/dist/cli/server-SYC3OVOP.js +2967 -0
- package/dist/cli/server-SYC3OVOP.js.map +1 -0
- package/dist/cli/sessions-MOJAALJI.js +102 -0
- package/dist/cli/sessions-MOJAALJI.js.map +1 -0
- package/dist/cli/setup-CCJZAWTY.js +404 -0
- package/dist/cli/setup-CCJZAWTY.js.map +1 -0
- package/dist/cli/stats-5RJCATCE.js +12 -0
- package/dist/cli/stats-5RJCATCE.js.map +1 -0
- package/dist/cli/update-4TJWRUIN.js +90 -0
- package/dist/cli/update-4TJWRUIN.js.map +1 -0
- package/dist/cli/version-3MYFE4G6.js +29 -0
- package/dist/cli/version-3MYFE4G6.js.map +1 -0
- package/dist/index.d.ts +13 -2
- package/dist/index.js +493 -89
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
- package/dist/cli/chunk-VWFJNLIK.js +0 -1031
- package/dist/cli/chunk-VWFJNLIK.js.map +0 -1
- /package/dist/cli/{prompt-XHICFAYN.js.map → chat-Q5ZCVIOO.js.map} +0 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../src/mcp/types.ts","../../src/mcp/client.ts","../../src/mcp/stdio.ts","../../src/mcp/sse.ts","../../src/mcp/streamable-http.ts","../../src/mcp/shell-split.ts","../../src/mcp/spec.ts","../../src/mcp/inspect.ts"],"sourcesContent":["/** MCP types (spec 2024-11-05). Stdio wire format is NDJSON — one JSON-RPC message per line, no Content-Length framing. */\n\nexport type JsonRpcId = string | number;\n\nexport interface JsonRpcRequest<P = unknown> {\n jsonrpc: \"2.0\";\n id: JsonRpcId;\n method: string;\n params?: P;\n}\n\nexport interface JsonRpcNotification<P = unknown> {\n jsonrpc: \"2.0\";\n method: string;\n params?: P;\n}\n\nexport interface JsonRpcSuccess<R = unknown> {\n jsonrpc: \"2.0\";\n id: JsonRpcId;\n result: R;\n}\n\nexport interface JsonRpcError {\n jsonrpc: \"2.0\";\n id: JsonRpcId | null;\n error: {\n /** JSON-RPC standard codes: -32700 parse, -32600 invalid request, -32601 method not found, -32602 invalid params, -32603 internal. MCP also defines its own range. */\n code: number;\n message: string;\n data?: unknown;\n };\n}\n\nexport type JsonRpcResponse<R = unknown> = JsonRpcSuccess<R> | JsonRpcError;\n\nexport type JsonRpcMessage = JsonRpcRequest | JsonRpcNotification | JsonRpcSuccess | JsonRpcError;\n\nexport interface McpClientInfo {\n name: string;\n version: string;\n}\n\nexport interface McpClientCapabilities {\n /** Empty object advertises support without any optional sub-features. */\n tools?: Record<string, never>;\n /** Advertised when the client can consume `resources/list` + `resources/read`. */\n resources?: Record<string, never>;\n /** Advertised when the client can consume `prompts/list` + `prompts/get`. */\n prompts?: Record<string, never>;\n // sampling would go here — deferred.\n}\n\nexport interface InitializeParams {\n protocolVersion: string;\n capabilities: McpClientCapabilities;\n clientInfo: McpClientInfo;\n}\n\nexport interface InitializeResult {\n protocolVersion: string;\n serverInfo: { name: string; version: string };\n capabilities: {\n tools?: { listChanged?: boolean };\n resources?: unknown;\n prompts?: unknown;\n };\n instructions?: string;\n}\n\nexport interface McpToolSchema {\n /** JSON Schema — compatible with Reasonix's tools.ts JSONSchema shape. */\n type?: string;\n properties?: Record<string, unknown>;\n required?: string[];\n [extra: string]: unknown;\n}\n\nexport interface McpTool {\n name: string;\n description?: string;\n /** MCP calls this `inputSchema`. Reasonix's `parameters` field is the same concept. */\n inputSchema: McpToolSchema;\n}\n\nexport interface ListToolsResult {\n tools: McpTool[];\n nextCursor?: string;\n}\n\nexport interface CallToolParams {\n name: string;\n arguments?: Record<string, unknown>;\n _meta?: { progressToken?: string | number };\n}\n\nexport interface ProgressNotificationParams {\n progressToken: string | number;\n progress: number;\n total?: number;\n message?: string;\n}\n\n/** Values a `ProgressHandler` receives — `progressToken` is already matched away. */\nexport interface McpProgressInfo {\n progress: number;\n total?: number;\n message?: string;\n}\n\nexport type McpProgressHandler = (info: McpProgressInfo) => void;\n\nexport interface McpContentBlockText {\n type: \"text\";\n text: string;\n}\n\nexport interface McpContentBlockImage {\n type: \"image\";\n data: string;\n mimeType: string;\n}\n\n/** MCP result content is an array of typed blocks. Reasonix consumes only text for now — image blocks get stringified with a placeholder. */\nexport type McpContentBlock = McpContentBlockText | McpContentBlockImage;\n\nexport interface CallToolResult {\n content: McpContentBlock[];\n /** True = tool raised an error; the content describes it. */\n isError?: boolean;\n}\n\nexport interface McpResource {\n uri: string;\n name: string;\n description?: string;\n /** Hint for the content type (e.g. \"text/markdown\"). Purely informational. */\n mimeType?: string;\n}\n\nexport interface ListResourcesParams {\n /** Pagination cursor from a previous listResources response. */\n cursor?: string;\n}\n\nexport interface ListResourcesResult {\n resources: McpResource[];\n nextCursor?: string;\n}\n\nexport interface ReadResourceParams {\n uri: string;\n}\n\n/** Server populates exactly one of `text` (UTF-8) or `blob` (base64) per entry. */\nexport interface McpResourceContentsText {\n uri: string;\n mimeType?: string;\n text: string;\n}\n\nexport interface McpResourceContentsBlob {\n uri: string;\n mimeType?: string;\n blob: string;\n}\n\nexport type McpResourceContents = McpResourceContentsText | McpResourceContentsBlob;\n\nexport interface ReadResourceResult {\n contents: McpResourceContents[];\n}\n\nexport interface McpPromptArgument {\n name: string;\n description?: string;\n required?: boolean;\n}\n\nexport interface McpPrompt {\n name: string;\n description?: string;\n arguments?: McpPromptArgument[];\n}\n\nexport interface ListPromptsParams {\n cursor?: string;\n}\n\nexport interface ListPromptsResult {\n prompts: McpPrompt[];\n nextCursor?: string;\n}\n\nexport interface GetPromptParams {\n name: string;\n arguments?: Record<string, string>;\n}\n\nexport interface McpPromptMessage {\n role: \"user\" | \"assistant\";\n content: McpContentBlock | McpPromptResourceBlock;\n}\n\nexport interface McpPromptResourceBlock {\n type: \"resource\";\n resource: McpResourceContents;\n}\n\nexport interface GetPromptResult {\n description?: string;\n messages: McpPromptMessage[];\n}\n\n/** Current MCP protocol version Reasonix is coded against. */\nexport const MCP_PROTOCOL_VERSION = \"2024-11-05\";\n\n/** Type guard — success vs error response. */\nexport function isJsonRpcError(msg: JsonRpcResponse): msg is JsonRpcError {\n return \"error\" in msg;\n}\n","import { VERSION } from \"../version.js\";\nimport type { McpTransport } from \"./stdio.js\";\nimport {\n type CallToolParams,\n type CallToolResult,\n type GetPromptParams,\n type GetPromptResult,\n type InitializeParams,\n type InitializeResult,\n type JsonRpcId,\n type JsonRpcMessage,\n type JsonRpcRequest,\n type JsonRpcResponse,\n type ListPromptsParams,\n type ListPromptsResult,\n type ListResourcesParams,\n type ListResourcesResult,\n type ListToolsResult,\n MCP_PROTOCOL_VERSION,\n type McpClientInfo,\n type McpProgressHandler,\n type ProgressNotificationParams,\n type ReadResourceParams,\n type ReadResourceResult,\n isJsonRpcError,\n} from \"./types.js\";\n\nexport interface McpClientOptions {\n transport: McpTransport;\n clientInfo?: McpClientInfo;\n /** Per-request timeout. Default 60s. */\n requestTimeoutMs?: number;\n}\n\ninterface PendingRequest {\n resolve: (value: unknown) => void;\n reject: (err: Error) => void;\n timeout: NodeJS.Timeout;\n}\n\nexport class McpClient {\n private readonly transport: McpTransport;\n private readonly clientInfo: McpClientInfo;\n private readonly requestTimeoutMs: number;\n private readonly pending = new Map<JsonRpcId, PendingRequest>();\n private nextId = 1;\n private readerStarted = false;\n private initialized = false;\n private _serverCapabilities: InitializeResult[\"capabilities\"] = {};\n private _serverInfo: InitializeResult[\"serverInfo\"] = { name: \"\", version: \"\" };\n private _protocolVersion = \"\";\n private _instructions: string | undefined;\n // Progress-token → handler for notifications/progress routing. Tokens\n // are minted per call when the caller supplies an onProgress\n // callback; cleared when the final response lands (or the pending\n // request rejects). No leaks — the `try/finally` in callTool\n // guarantees cleanup even on timeout.\n private readonly progressHandlers = new Map<string | number, McpProgressHandler>();\n private nextProgressToken = 1;\n\n constructor(opts: McpClientOptions) {\n this.transport = opts.transport;\n this.clientInfo = opts.clientInfo ?? { name: \"reasonix\", version: VERSION };\n this.requestTimeoutMs = opts.requestTimeoutMs ?? 60_000;\n }\n\n /** Server's advertised capabilities, available after initialize(). */\n get serverCapabilities(): InitializeResult[\"capabilities\"] {\n return this._serverCapabilities;\n }\n\n /** Server's self-reported name + version, available after initialize(). */\n get serverInfo(): InitializeResult[\"serverInfo\"] {\n return this._serverInfo;\n }\n\n /** Protocol version the server agreed to during the handshake. */\n get protocolVersion(): string {\n return this._protocolVersion;\n }\n\n /** Optional free-form instructions the server provides at handshake. */\n get serverInstructions(): string | undefined {\n return this._instructions;\n }\n\n /** Compliant servers reject other methods until this completes. */\n async initialize(): Promise<InitializeResult> {\n if (this.initialized) throw new Error(\"MCP client already initialized\");\n this.startReaderIfNeeded();\n const result = await this.request<InitializeResult>(\"initialize\", {\n protocolVersion: MCP_PROTOCOL_VERSION,\n // Advertise every method the client can consume so servers know\n // they can send listChanged notifications etc. Sub-feature flags\n // (e.g. `resources.subscribe`) are omitted — we don't implement\n // those yet and the empty object means \"method-level support, no\n // sub-features.\"\n capabilities: { tools: {}, resources: {}, prompts: {} },\n clientInfo: this.clientInfo,\n } satisfies InitializeParams);\n this._serverCapabilities = result.capabilities ?? {};\n this._serverInfo = result.serverInfo ?? { name: \"\", version: \"\" };\n this._protocolVersion = result.protocolVersion ?? \"\";\n this._instructions = result.instructions;\n // Per spec: client sends notifications/initialized after receiving the\n // initialize response. Only then is the connection live for other\n // methods.\n await this.transport.send({\n jsonrpc: \"2.0\",\n method: \"notifications/initialized\",\n });\n this.initialized = true;\n return result;\n }\n\n /** List tools the server exposes. */\n async listTools(): Promise<ListToolsResult> {\n this.assertInitialized();\n return this.request<ListToolsResult>(\"tools/list\", {});\n }\n\n /** Abort sends `notifications/cancelled` and rejects immediately; late server responses are dropped. */\n async callTool(\n name: string,\n args?: Record<string, unknown>,\n opts: { onProgress?: McpProgressHandler; signal?: AbortSignal } = {},\n ): Promise<CallToolResult> {\n this.assertInitialized();\n const params: CallToolParams = { name, arguments: args ?? {} };\n let token: number | undefined;\n if (opts.onProgress) {\n token = this.nextProgressToken++;\n this.progressHandlers.set(token, opts.onProgress);\n params._meta = { progressToken: token };\n }\n try {\n return await this.request<CallToolResult>(\"tools/call\", params, opts.signal);\n } finally {\n if (token !== undefined) this.progressHandlers.delete(token);\n }\n }\n\n /** Throws on method-not-found; callers should gate on `serverCapabilities.resources` first. */\n async listResources(cursor?: string): Promise<ListResourcesResult> {\n this.assertInitialized();\n return this.request<ListResourcesResult>(\"resources/list\", {\n ...(cursor ? { cursor } : {}),\n } satisfies ListResourcesParams);\n }\n\n /** Read the contents of a resource by URI. */\n async readResource(uri: string): Promise<ReadResourceResult> {\n this.assertInitialized();\n return this.request<ReadResourceResult>(\"resources/read\", {\n uri,\n } satisfies ReadResourceParams);\n }\n\n /** List prompt templates the server exposes. */\n async listPrompts(cursor?: string): Promise<ListPromptsResult> {\n this.assertInitialized();\n return this.request<ListPromptsResult>(\"prompts/list\", {\n ...(cursor ? { cursor } : {}),\n } satisfies ListPromptsParams);\n }\n\n async getPrompt(name: string, args?: Record<string, string>): Promise<GetPromptResult> {\n this.assertInitialized();\n return this.request<GetPromptResult>(\"prompts/get\", {\n name,\n ...(args ? { arguments: args } : {}),\n } satisfies GetPromptParams);\n }\n\n /** Close the transport and reject any outstanding requests. */\n async close(): Promise<void> {\n for (const [, pending] of this.pending) {\n clearTimeout(pending.timeout);\n pending.reject(new Error(\"MCP client closed\"));\n }\n this.pending.clear();\n await this.transport.close();\n }\n\n private assertInitialized(): void {\n if (!this.initialized) throw new Error(\"MCP client not initialized — call initialize() first\");\n }\n\n private async request<R>(method: string, params: unknown, signal?: AbortSignal): Promise<R> {\n const id = this.nextId++;\n const frame: JsonRpcRequest = { jsonrpc: \"2.0\", id, method, params };\n let abortHandler: (() => void) | null = null;\n const promise = new Promise<R>((resolve, reject) => {\n const timeout = setTimeout(() => {\n this.pending.delete(id);\n if (abortHandler && signal) signal.removeEventListener(\"abort\", abortHandler);\n reject(\n new Error(`MCP request ${method} (id=${id}) timed out after ${this.requestTimeoutMs}ms`),\n );\n }, this.requestTimeoutMs);\n this.pending.set(id, {\n resolve: resolve as (value: unknown) => void,\n reject,\n timeout,\n });\n // Wire up cancellation: when signal fires, send an MCP cancellation\n // notification to the server (so it can stop whatever it was doing)\n // and reject the caller immediately — no need to wait for the\n // subprocess to finish its in-flight work. Late responses from the\n // server are dropped by `dispatch` because the id is gone from\n // `pending`.\n if (signal) {\n if (signal.aborted) {\n this.pending.delete(id);\n clearTimeout(timeout);\n reject(new Error(`MCP request ${method} (id=${id}) aborted before send`));\n return;\n }\n abortHandler = () => {\n this.pending.delete(id);\n clearTimeout(timeout);\n void this.transport\n .send({\n jsonrpc: \"2.0\",\n method: \"notifications/cancelled\",\n params: { requestId: id, reason: \"aborted by user\" },\n })\n .catch(() => {\n // Transport may already be closing — swallow; we still\n // reject the caller below so they unblock.\n });\n reject(new Error(`MCP request ${method} (id=${id}) aborted by user`));\n };\n signal.addEventListener(\"abort\", abortHandler, { once: true });\n }\n });\n promise.catch(() => undefined);\n try {\n await Promise.race([this.transport.send(frame), promise.then(() => undefined)]);\n } catch (err) {\n const pending = this.pending.get(id);\n if (pending) clearTimeout(pending.timeout);\n this.pending.delete(id);\n if (abortHandler && signal) signal.removeEventListener(\"abort\", abortHandler);\n throw err;\n }\n try {\n return await promise;\n } finally {\n if (abortHandler && signal) signal.removeEventListener(\"abort\", abortHandler);\n }\n }\n\n private startReaderIfNeeded(): void {\n if (this.readerStarted) return;\n this.readerStarted = true;\n // Fire-and-forget: the reader runs for the lifetime of the client.\n void this.readLoop();\n }\n\n private async readLoop(): Promise<void> {\n try {\n for await (const msg of this.transport.messages()) {\n this.dispatch(msg);\n }\n } catch (err) {\n // Surface as rejections on all pending requests so nobody hangs.\n for (const [, pending] of this.pending) {\n clearTimeout(pending.timeout);\n pending.reject(err as Error);\n }\n this.pending.clear();\n }\n }\n\n private dispatch(msg: JsonRpcMessage): void {\n // Notifications (no `id`): route by method. Progress notifications\n // go to the per-call handler if one was registered; everything\n // else is dropped silently (we don't yet handle tools/list_changed\n // or resources/list_changed).\n if (!(\"id\" in msg) || msg.id === null || msg.id === undefined) {\n if (\"method\" in msg && msg.method === \"notifications/progress\") {\n const p = msg.params as ProgressNotificationParams | undefined;\n if (!p || p.progressToken === undefined) return;\n const handler = this.progressHandlers.get(p.progressToken);\n if (!handler) return; // late notification after the call resolved\n handler({ progress: p.progress, total: p.total, message: p.message });\n }\n return;\n }\n if (!(\"result\" in msg) && !(\"error\" in msg)) return; // it's a request from server\n const pending = this.pending.get(msg.id);\n if (!pending) return; // late response after timeout; drop\n this.pending.delete(msg.id);\n clearTimeout(pending.timeout);\n const resp = msg as JsonRpcResponse;\n if (isJsonRpcError(resp)) {\n pending.reject(new Error(`MCP ${resp.error.code}: ${resp.error.message}`));\n } else {\n pending.resolve(resp.result);\n }\n }\n}\n","/** MCP stdio = newline-delimited JSON-RPC; transport iface lets tests fake it without spawning. */\n\nimport { type ChildProcess, spawn } from \"node:child_process\";\nimport type { JsonRpcMessage } from \"./types.js\";\n\nexport interface McpTransport {\n /** Send one JSON-RPC message. Resolves when the bytes are accepted. */\n send(message: JsonRpcMessage): Promise<void>;\n /** Async iterator over incoming messages. Ends when the connection closes. */\n messages(): AsyncIterableIterator<JsonRpcMessage>;\n /** Close the underlying resource (kill child process, close streams). */\n close(): Promise<void>;\n}\n\nexport interface StdioTransportOptions {\n /** Argv to spawn. First element is the command. */\n command: string;\n args?: string[];\n /** Env overlay — merged over process.env unless replaceEnv=true. */\n env?: Record<string, string>;\n /** When true, only the env above is visible to the child. Default false. */\n replaceEnv?: boolean;\n /** CWD for the child. Default: process.cwd(). */\n cwd?: string;\n /** Default true on win32 to resolve `.cmd`/`.bat` wrappers (npx.cmd etc.). */\n shell?: boolean;\n}\n\nexport class StdioTransport implements McpTransport {\n private readonly child: ChildProcess;\n private readonly queue: JsonRpcMessage[] = [];\n private readonly waiters: Array<(m: JsonRpcMessage | null) => void> = [];\n private closed = false;\n private stdoutBuffer = \"\";\n\n constructor(opts: StdioTransportOptions) {\n const env = opts.replaceEnv ? { ...(opts.env ?? {}) } : { ...process.env, ...(opts.env ?? {}) };\n // Windows wraps binaries as .cmd/.bat shims (npx.cmd, pnpm.cmd, …).\n // child_process.spawn without shell:true can't resolve them, which\n // breaks `--mcp \"npx -y some-server\"` — the most common MCP setup.\n // Default shell:true on win32 and leave POSIX alone.\n const shell = opts.shell ?? process.platform === \"win32\";\n\n if (shell) {\n // Node's shell:true + args[] triggers DEP0190 because it concatenates\n // with spaces and doesn't quote args — unsafe if an arg contains\n // shell metacharacters. We build a single command line ourselves,\n // quoting ONLY the args (command stays bare so the shell's PATH /\n // PATHEXT lookup finds `npx` → `npx.cmd` on Windows).\n const line = [\n opts.command,\n ...(opts.args ?? []).map((a) => quoteArg(a, process.platform === \"win32\")),\n ].join(\" \");\n this.child = spawn(line, [], {\n env,\n cwd: opts.cwd,\n stdio: [\"pipe\", \"pipe\", \"inherit\"],\n shell: true,\n });\n } else {\n this.child = spawn(opts.command, opts.args ?? [], {\n env,\n cwd: opts.cwd,\n stdio: [\"pipe\", \"pipe\", \"inherit\"],\n });\n }\n this.child.stdout!.setEncoding(\"utf8\");\n this.child.stdout!.on(\"data\", (chunk: string) => this.onStdout(chunk));\n this.child.on(\"close\", () => this.onClose());\n this.child.on(\"error\", (err) => {\n // Surface spawn errors as a synthetic JsonRpcError so callers don't\n // hang on a stream that never emits anything.\n this.push({\n jsonrpc: \"2.0\",\n id: null,\n error: { code: -32000, message: `transport error: ${err.message}` },\n });\n });\n }\n\n async send(message: JsonRpcMessage): Promise<void> {\n if (this.closed) throw new Error(\"MCP transport is closed\");\n return new Promise((resolve, reject) => {\n const line = `${JSON.stringify(message)}\\n`;\n this.child.stdin!.write(line, \"utf8\", (err) => {\n if (err) reject(err);\n else resolve();\n });\n });\n }\n\n async *messages(): AsyncIterableIterator<JsonRpcMessage> {\n while (true) {\n if (this.queue.length > 0) {\n yield this.queue.shift()!;\n continue;\n }\n if (this.closed) return;\n const next = await new Promise<JsonRpcMessage | null>((resolve) => {\n this.waiters.push(resolve);\n });\n if (next === null) return; // closed while we were waiting\n yield next;\n }\n }\n\n async close(): Promise<void> {\n if (this.closed) return;\n this.closed = true;\n // Signal any pending waiters.\n while (this.waiters.length > 0) this.waiters.shift()!(null);\n try {\n this.child.stdin!.end();\n } catch {\n /* already ended */\n }\n if (this.child.exitCode === null && !this.child.killed) {\n // child.kill(\"SIGTERM\") throws EINVAL on Windows; plain kill()\n // can also throw on failed spawns. Swallow both.\n try {\n this.child.kill(process.platform === \"win32\" ? undefined : \"SIGTERM\");\n } catch {\n /* already exited or unsignallable */\n }\n }\n }\n\n /** Parse incoming stdout chunks into NDJSON messages. */\n private onStdout(chunk: string): void {\n this.stdoutBuffer += chunk;\n let newlineIdx: number;\n // biome-ignore lint/suspicious/noAssignInExpressions: idiomatic loop shape\n while ((newlineIdx = this.stdoutBuffer.indexOf(\"\\n\")) !== -1) {\n const line = this.stdoutBuffer.slice(0, newlineIdx).trim();\n this.stdoutBuffer = this.stdoutBuffer.slice(newlineIdx + 1);\n if (!line) continue;\n try {\n const msg = JSON.parse(line) as JsonRpcMessage;\n this.push(msg);\n } catch {\n // Malformed lines are dropped — some servers emit startup banners\n // before the JSON-RPC loop begins. We surface the noise to stderr\n // via the inherited stderr stream, not our event queue.\n }\n }\n }\n\n private onClose(): void {\n this.closed = true;\n while (this.waiters.length > 0) this.waiters.shift()!(null);\n }\n\n private push(msg: JsonRpcMessage): void {\n const waiter = this.waiters.shift();\n if (waiter) waiter(msg);\n else this.queue.push(msg);\n }\n}\n\nfunction quoteArg(s: string, windows: boolean): string {\n if (!windows) {\n // POSIX: single-quote, escape single quotes.\n return `'${s.replace(/'/g, \"'\\\\''\")}'`;\n }\n // cmd.exe: double-quote, escape internal quotes by doubling.\n return `\"${s.replace(/\"/g, '\"\"')}\"`;\n}\n","/** MCP HTTP+SSE transport (spec 2024-11-05) — POST endpoint URL arrives as the first `event: endpoint` SSE frame. */\n\nimport { createParser } from \"eventsource-parser\";\nimport type { McpTransport } from \"./stdio.js\";\nimport type { JsonRpcMessage } from \"./types.js\";\n\nexport interface SseTransportOptions {\n /** SSE endpoint URL, e.g. `https://mcp.example.com/sse`. */\n url: string;\n /** Extra headers sent on both the SSE GET and the JSON-RPC POSTs (e.g. `Authorization`). */\n headers?: Record<string, string>;\n}\n\nexport class SseTransport implements McpTransport {\n private readonly url: string;\n private readonly headers: Record<string, string>;\n private readonly queue: JsonRpcMessage[] = [];\n private readonly waiters: Array<(m: JsonRpcMessage | null) => void> = [];\n private readonly controller = new AbortController();\n private closed = false;\n private postUrl: string | null = null;\n private readonly endpointReady: Promise<string>;\n private resolveEndpoint!: (url: string) => void;\n private rejectEndpoint!: (err: Error) => void;\n\n constructor(opts: SseTransportOptions) {\n this.url = opts.url;\n this.headers = opts.headers ?? {};\n this.endpointReady = new Promise<string>((resolve, reject) => {\n this.resolveEndpoint = resolve;\n this.rejectEndpoint = reject;\n });\n // Swallow unhandled-rejection noise if nobody ever calls send().\n this.endpointReady.catch(() => undefined);\n void this.runStream();\n }\n\n async send(message: JsonRpcMessage): Promise<void> {\n if (this.closed) throw new Error(\"MCP SSE transport is closed\");\n const postUrl = await this.endpointReady;\n const res = await fetch(postUrl, {\n method: \"POST\",\n headers: { \"content-type\": \"application/json\", ...this.headers },\n body: JSON.stringify(message),\n signal: this.controller.signal,\n });\n // Drain body so the socket returns to the pool even if the server\n // elected to write one. We explicitly don't parse it — responses\n // arrive on the SSE channel.\n await res.arrayBuffer().catch(() => undefined);\n if (!res.ok) {\n throw new Error(`MCP SSE POST ${postUrl} failed: ${res.status} ${res.statusText}`);\n }\n }\n\n async *messages(): AsyncIterableIterator<JsonRpcMessage> {\n while (true) {\n if (this.queue.length > 0) {\n yield this.queue.shift()!;\n continue;\n }\n if (this.closed) return;\n const next = await new Promise<JsonRpcMessage | null>((resolve) => {\n this.waiters.push(resolve);\n });\n if (next === null) return;\n yield next;\n }\n }\n\n async close(): Promise<void> {\n if (this.closed) return;\n this.closed = true;\n while (this.waiters.length > 0) this.waiters.shift()!(null);\n // Reject any still-pending send() that was waiting for the endpoint.\n this.rejectEndpoint(new Error(\"MCP SSE transport closed before endpoint was ready\"));\n try {\n this.controller.abort();\n } catch {\n /* already aborted */\n }\n }\n\n private async runStream(): Promise<void> {\n let res: Response;\n try {\n res = await fetch(this.url, {\n method: \"GET\",\n headers: { accept: \"text/event-stream\", ...this.headers },\n signal: this.controller.signal,\n });\n } catch (err) {\n this.failHandshake(`SSE connect to ${this.url} failed: ${(err as Error).message}`);\n return;\n }\n if (!res.ok || !res.body) {\n // Drain body to free the socket before giving up.\n await res.body?.cancel().catch(() => undefined);\n this.failHandshake(`SSE handshake ${this.url} → ${res.status} ${res.statusText}`);\n return;\n }\n\n const parser = createParser({\n onEvent: (ev) => this.handleEvent(ev.event ?? \"message\", ev.data),\n });\n const decoder = new TextDecoder();\n try {\n for await (const chunk of res.body as AsyncIterable<Uint8Array>) {\n parser.feed(decoder.decode(chunk, { stream: true }));\n }\n } catch (err) {\n if (!this.closed) {\n this.pushError(`SSE stream error: ${(err as Error).message}`);\n }\n } finally {\n this.markClosed();\n }\n }\n\n private handleEvent(type: string, data: string): void {\n if (type === \"endpoint\") {\n if (this.postUrl) return; // ignore repeat announcements\n try {\n this.postUrl = new URL(data, this.url).toString();\n this.resolveEndpoint(this.postUrl);\n } catch (err) {\n this.failHandshake(`SSE endpoint event had bad URL \"${data}\": ${(err as Error).message}`);\n }\n return;\n }\n if (type === \"message\") {\n try {\n const parsed = JSON.parse(data) as JsonRpcMessage;\n this.pushMessage(parsed);\n } catch {\n // Malformed JSON-RPC on an SSE frame — drop it, same as stdio.\n }\n return;\n }\n // Unknown event types (server pings, custom extensions) — ignore.\n }\n\n private failHandshake(reason: string): void {\n this.rejectEndpoint(new Error(reason));\n this.pushError(reason);\n this.markClosed();\n }\n\n private pushMessage(msg: JsonRpcMessage): void {\n const waiter = this.waiters.shift();\n if (waiter) waiter(msg);\n else this.queue.push(msg);\n }\n\n private pushError(message: string): void {\n this.pushMessage({\n jsonrpc: \"2.0\",\n id: null,\n error: { code: -32000, message },\n });\n }\n\n private markClosed(): void {\n if (this.closed) return;\n this.closed = true;\n while (this.waiters.length > 0) this.waiters.shift()!(null);\n }\n}\n","/** MCP Streamable HTTP transport (2025-03-26) — POST-only; no long-lived GET stream, no Last-Event-ID resume. */\n\nimport { createParser } from \"eventsource-parser\";\nimport type { McpTransport } from \"./stdio.js\";\nimport type { JsonRpcMessage } from \"./types.js\";\n\nexport interface StreamableHttpTransportOptions {\n /** Streamable HTTP endpoint URL, e.g. `https://mcp.example.com/mcp`. */\n url: string;\n /** Extra headers sent on every request (e.g. `Authorization`). */\n headers?: Record<string, string>;\n}\n\nconst SESSION_HEADER = \"mcp-session-id\";\n\nexport class StreamableHttpTransport implements McpTransport {\n private readonly url: string;\n private readonly extraHeaders: Record<string, string>;\n private readonly queue: JsonRpcMessage[] = [];\n private readonly waiters: Array<(m: JsonRpcMessage | null) => void> = [];\n private readonly controller = new AbortController();\n /** Session id minted by server on (typically) the initialize response. */\n private sessionId: string | null = null;\n private closed = false;\n /** Background SSE read-loops kicked off by send(); awaited on close(). */\n private readonly streams = new Set<Promise<void>>();\n\n constructor(opts: StreamableHttpTransportOptions) {\n this.url = opts.url;\n this.extraHeaders = opts.headers ?? {};\n }\n\n async send(message: JsonRpcMessage): Promise<void> {\n if (this.closed) throw new Error(\"MCP Streamable HTTP transport is closed\");\n const headers: Record<string, string> = {\n \"content-type\": \"application/json\",\n // Both accepted — server picks. application/json first signals a\n // mild preference for the simpler shape when the response is a\n // single message.\n accept: \"application/json, text/event-stream\",\n ...this.extraHeaders,\n };\n if (this.sessionId !== null) headers[\"mcp-session-id\"] = this.sessionId;\n\n let res: Response;\n try {\n res = await fetch(this.url, {\n method: \"POST\",\n headers,\n body: JSON.stringify(message),\n signal: this.controller.signal,\n });\n } catch (err) {\n throw new Error(`MCP Streamable HTTP POST ${this.url} failed: ${(err as Error).message}`);\n }\n\n // Capture session id the first time the server hands one out.\n const serverSessionId = res.headers.get(SESSION_HEADER);\n if (serverSessionId && this.sessionId === null) {\n this.sessionId = serverSessionId;\n }\n\n if (res.status === 404 && this.sessionId !== null) {\n // Session expired / unknown to the server. Surface as an error so\n // McpClient can recreate; drain the body so the socket goes back\n // to the pool.\n await res.body?.cancel().catch(() => undefined);\n throw new Error(\n `MCP Streamable HTTP session expired (server returned 404 with Mcp-Session-Id \"${this.sessionId}\"). Reinitialize the client.`,\n );\n }\n\n if (!res.ok) {\n const body = await res.text().catch(() => \"\");\n throw new Error(\n `MCP Streamable HTTP POST ${this.url} → ${res.status} ${res.statusText}${body ? `: ${body}` : \"\"}`,\n );\n }\n\n // 202 Accepted: request was a notification or pure ack — no body.\n if (res.status === 202) {\n await res.body?.cancel().catch(() => undefined);\n return;\n }\n\n const ct = (res.headers.get(\"content-type\") ?? \"\").toLowerCase();\n if (ct.includes(\"application/json\")) {\n let parsed: unknown;\n try {\n parsed = await res.json();\n } catch (err) {\n throw new Error(`MCP Streamable HTTP body wasn't valid JSON: ${(err as Error).message}`);\n }\n if (Array.isArray(parsed)) {\n for (const item of parsed) this.pushMessage(item as JsonRpcMessage);\n } else {\n this.pushMessage(parsed as JsonRpcMessage);\n }\n return;\n }\n\n if (ct.includes(\"text/event-stream\")) {\n // Stream may carry multiple events (progress notifications +\n // the eventual response). Read it concurrently with subsequent\n // sends — return as soon as the stream is wired so callers can\n // pipeline more requests.\n if (!res.body) {\n throw new Error(\"MCP Streamable HTTP SSE response had no body\");\n }\n const stream = this.consumeStream(res.body as AsyncIterable<Uint8Array>);\n this.streams.add(stream);\n stream.finally(() => this.streams.delete(stream));\n return;\n }\n\n // Unknown content type — drain and treat as a no-op rather than\n // hanging. Servers that want to extend the protocol should not\n // wedge older clients with an unexpected MIME.\n await res.body?.cancel().catch(() => undefined);\n }\n\n async *messages(): AsyncIterableIterator<JsonRpcMessage> {\n while (true) {\n if (this.queue.length > 0) {\n yield this.queue.shift()!;\n continue;\n }\n if (this.closed) return;\n const next = await new Promise<JsonRpcMessage | null>((resolve) => {\n this.waiters.push(resolve);\n });\n if (next === null) return;\n yield next;\n }\n }\n\n async close(): Promise<void> {\n if (this.closed) return;\n this.closed = true;\n while (this.waiters.length > 0) this.waiters.shift()!(null);\n try {\n this.controller.abort();\n } catch {\n /* already aborted */\n }\n // Wait for any in-flight SSE streams to wind down so a subsequent\n // process.exit() doesn't trip on a hanging socket. Cap at \"done\";\n // controller.abort() above unblocks them.\n await Promise.allSettled(Array.from(this.streams));\n }\n\n /** Visible for tests — confirm session header round-trip. */\n getSessionId(): string | null {\n return this.sessionId;\n }\n\n private async consumeStream(body: AsyncIterable<Uint8Array>): Promise<void> {\n const parser = createParser({\n onEvent: (ev) => {\n // Per spec, server-side events use the `message` event type\n // (default if `event:` line is missing). Other event types\n // (server pings, custom extensions) we silently ignore.\n const type = ev.event ?? \"message\";\n if (type !== \"message\") return;\n try {\n const parsed = JSON.parse(ev.data) as JsonRpcMessage;\n this.pushMessage(parsed);\n } catch {\n /* malformed JSON — drop, mirror SSE behavior */\n }\n },\n });\n const decoder = new TextDecoder();\n try {\n for await (const chunk of body) {\n if (this.closed) break;\n parser.feed(decoder.decode(chunk, { stream: true }));\n }\n } catch (err) {\n if (!this.closed) {\n this.pushMessage({\n jsonrpc: \"2.0\",\n id: null,\n error: {\n code: -32000,\n message: `Streamable HTTP stream error: ${(err as Error).message}`,\n },\n });\n }\n }\n }\n\n private pushMessage(msg: JsonRpcMessage): void {\n const waiter = this.waiters.shift();\n if (waiter) waiter(msg);\n else this.queue.push(msg);\n }\n}\n","/** Quote-aware argv split for `--mcp`; throws on unterminated quotes. NOT a full shell parser. */\nexport function shellSplit(input: string): string[] {\n const tokens: string[] = [];\n let cur = \"\";\n let quote: '\"' | \"'\" | null = null;\n let i = 0;\n const s = input;\n\n while (i < s.length) {\n const ch = s[i]!;\n\n if (quote) {\n if (ch === quote) {\n quote = null;\n i++;\n continue;\n }\n // backslash escapes inside double quotes only\n if (ch === \"\\\\\" && quote === '\"' && i + 1 < s.length) {\n cur += s[i + 1];\n i += 2;\n continue;\n }\n cur += ch;\n i++;\n continue;\n }\n\n if (ch === '\"' || ch === \"'\") {\n quote = ch as '\"' | \"'\";\n i++;\n continue;\n }\n\n // Backslash escape ONLY applies inside double quotes (handled above).\n // Outside quotes, backslashes pass through literally — otherwise\n // Windows paths like `C:\\path\\to\\exe` get mangled. POSIX users who\n // want to escape a space outside quotes can use single quotes instead.\n\n if (ch === \" \" || ch === \"\\t\") {\n if (cur.length > 0) {\n tokens.push(cur);\n cur = \"\";\n }\n i++;\n continue;\n }\n\n cur += ch;\n i++;\n }\n\n if (quote) {\n throw new Error(\n `shellSplit: unterminated ${quote === '\"' ? \"double\" : \"single\"} quote in input`,\n );\n }\n if (cur.length > 0) tokens.push(cur);\n return tokens;\n}\n","/** Plain http:// stays HTTP+SSE for back-compat; Streamable HTTP is opt-in via the `streamable+` URL prefix. */\n\nimport { shellSplit } from \"./shell-split.js\";\n\nexport interface StdioMcpSpec {\n transport: \"stdio\";\n /** Namespace prefix applied to each registered tool, or null if anonymous. */\n name: string | null;\n /** Argv[0]. */\n command: string;\n /** Remaining argv. */\n args: string[];\n}\n\nexport interface SseMcpSpec {\n transport: \"sse\";\n name: string | null;\n /** Fully qualified SSE endpoint URL. */\n url: string;\n}\n\nexport interface StreamableHttpMcpSpec {\n transport: \"streamable-http\";\n name: string | null;\n /** Fully qualified Streamable HTTP endpoint URL (no `streamable+` prefix). */\n url: string;\n}\n\nexport type McpSpec = StdioMcpSpec | SseMcpSpec | StreamableHttpMcpSpec;\n\nconst NAME_PREFIX = /^([a-zA-Z_][a-zA-Z0-9_-]*)=(.*)$/;\nconst HTTP_URL = /^https?:\\/\\//i;\nconst STREAMABLE_PREFIX = /^streamable\\+(https?:\\/\\/.+)$/i;\n\nexport function parseMcpSpec(input: string): McpSpec {\n const trimmed = input.trim();\n if (!trimmed) {\n throw new Error(\"empty MCP spec\");\n }\n\n const nameMatch = NAME_PREFIX.exec(trimmed);\n const name = nameMatch ? nameMatch[1]! : null;\n const body = (nameMatch ? nameMatch[2]! : trimmed).trim();\n\n if (!body) {\n throw new Error(`MCP spec has name but no command: ${input}`);\n }\n\n const streamMatch = STREAMABLE_PREFIX.exec(body);\n if (streamMatch) {\n return { transport: \"streamable-http\", name, url: streamMatch[1]! };\n }\n\n if (HTTP_URL.test(body)) {\n return { transport: \"sse\", name, url: body };\n }\n\n const argv = shellSplit(body);\n if (argv.length === 0) {\n throw new Error(`MCP spec has name but no command: ${input}`);\n }\n const [command, ...args] = argv;\n return { transport: \"stdio\", name, command: command!, args };\n}\n","/** Unsupported list methods surface as `{supported:false}` instead of throwing — minimal servers still get a clean report. */\n\nimport type { McpClient } from \"./client.js\";\nimport type { McpPrompt, McpResource, McpTool } from \"./types.js\";\n\nexport interface InspectionReport {\n protocolVersion: string;\n serverInfo: { name: string; version: string };\n capabilities: Record<string, unknown>;\n instructions?: string;\n tools: SectionResult<McpTool>;\n resources: SectionResult<McpResource>;\n prompts: SectionResult<McpPrompt>;\n /** Wall-clock for the three list calls combined; surfaced as the server's \"p95-ish\" latency in the browser. */\n elapsedMs: number;\n}\n\nexport type SectionResult<T> =\n | { supported: true; items: T[] }\n | { supported: false; reason: string };\n\n/** Caller owns initialize() / close() — keeps this pure so tests can feed a FakeMcpTransport. */\nexport async function inspectMcpServer(client: McpClient): Promise<InspectionReport> {\n const t0 = Date.now();\n // Always try all three listings — some servers omit capability flags but still serve the methods.\n const tools = await trySection<McpTool>(() => client.listTools().then((r) => r.tools));\n const resources = await trySection<McpResource>(() =>\n client.listResources().then((r) => r.resources),\n );\n const prompts = await trySection<McpPrompt>(() => client.listPrompts().then((r) => r.prompts));\n\n return {\n protocolVersion: client.protocolVersion || \"(unknown)\",\n serverInfo: client.serverInfo,\n capabilities: client.serverCapabilities ?? {},\n instructions: client.serverInstructions,\n tools,\n resources,\n prompts,\n elapsedMs: Date.now() - t0,\n };\n}\n\nasync function trySection<T>(load: () => Promise<T[]>): Promise<SectionResult<T>> {\n try {\n const items = await load();\n return { supported: true, items };\n } catch (err) {\n const msg = (err as Error).message ?? String(err);\n // -32601 is JSON-RPC \"method not found\" — the canonical response\n // from a server that doesn't implement this family. Treat it as\n // \"not supported\" rather than a hard error, so the CLI can render\n // a clean summary instead of aborting on the first missing method.\n if (/-32601/.test(msg) || /method not found/i.test(msg)) {\n return { supported: false, reason: \"method not found (-32601)\" };\n }\n return { supported: false, reason: msg };\n }\n}\n"],"mappings":"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","names":["createParser"]}
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import {
|
|
3
|
+
indexCompatible,
|
|
4
|
+
querySemantic
|
|
5
|
+
} from "./chunk-RZILUXUC.js";
|
|
6
|
+
|
|
7
|
+
// src/index/semantic/tool.ts
|
|
8
|
+
async function registerSemanticSearchTool(registry, opts) {
|
|
9
|
+
if (!await indexCompatible(opts.root, { provider: opts.provider, model: opts.model }))
|
|
10
|
+
return false;
|
|
11
|
+
const defaultTopK = opts.defaultTopK ?? 8;
|
|
12
|
+
const defaultMinScore = opts.defaultMinScore ?? 0.3;
|
|
13
|
+
registry.register({
|
|
14
|
+
name: "semantic_search",
|
|
15
|
+
description: "FIRST CHOICE for descriptive queries. Use this BEFORE search_content (grep) when the user describes WHAT code does ('where do we handle X', 'which file owns Y', 'how does Z work', 'find the logic that \u2026'). Returns ranked snippets ordered by semantic relevance \u2014 finds the right file even when your description shares no words with the code. Falls back to search_content / search_files only for: exact identifiers, regex patterns, or counting occurrences of a known token. If your first instinct is grep on a paraphrased question, you are wrong \u2014 try semantic_search first.",
|
|
16
|
+
readOnly: true,
|
|
17
|
+
parallelSafe: true,
|
|
18
|
+
parameters: {
|
|
19
|
+
type: "object",
|
|
20
|
+
properties: {
|
|
21
|
+
query: {
|
|
22
|
+
type: "string",
|
|
23
|
+
description: "Natural-language description, phrased as a question or noun phrase: 'where do we validate the session cookie?' / 'retry backoff logic' / 'code that prevents user changes from immediately landing on disk'. Do NOT pass exact identifiers \u2014 those are search_content's job."
|
|
24
|
+
},
|
|
25
|
+
topK: {
|
|
26
|
+
type: "integer",
|
|
27
|
+
description: `Number of snippets to return (1..16). Default ${defaultTopK}.`
|
|
28
|
+
},
|
|
29
|
+
minScore: {
|
|
30
|
+
type: "number",
|
|
31
|
+
description: `Drop snippets with cosine score below this (0..1). Default ${defaultMinScore}. Raise for stricter matches; lower if the index is small.`
|
|
32
|
+
}
|
|
33
|
+
},
|
|
34
|
+
required: ["query"]
|
|
35
|
+
},
|
|
36
|
+
fn: async (args, ctx) => {
|
|
37
|
+
const hits = await querySemantic(opts.root, args.query, {
|
|
38
|
+
topK: args.topK ?? defaultTopK,
|
|
39
|
+
minScore: args.minScore ?? defaultMinScore,
|
|
40
|
+
provider: opts.provider,
|
|
41
|
+
baseUrl: opts.baseUrl,
|
|
42
|
+
apiKey: opts.apiKey,
|
|
43
|
+
model: opts.model,
|
|
44
|
+
extraBody: opts.extraBody,
|
|
45
|
+
signal: ctx?.signal
|
|
46
|
+
});
|
|
47
|
+
if (hits === null) {
|
|
48
|
+
return "No semantic index found for this project. Run `reasonix index` to build one.";
|
|
49
|
+
}
|
|
50
|
+
if (hits.length === 0) {
|
|
51
|
+
return `query: ${args.query}
|
|
52
|
+
|
|
53
|
+
no matches above the score threshold (${args.minScore ?? defaultMinScore}).`;
|
|
54
|
+
}
|
|
55
|
+
return formatHits(args.query, hits);
|
|
56
|
+
}
|
|
57
|
+
});
|
|
58
|
+
return true;
|
|
59
|
+
}
|
|
60
|
+
function formatHits(query, hits) {
|
|
61
|
+
const lines = [`query: ${query}`, `
|
|
62
|
+
results (${hits.length}):`];
|
|
63
|
+
hits.forEach((h, i) => {
|
|
64
|
+
const { entry, score } = h;
|
|
65
|
+
lines.push(
|
|
66
|
+
`
|
|
67
|
+
${i + 1}. ${entry.path}:${entry.startLine}-${entry.endLine} (score ${score.toFixed(3)})`
|
|
68
|
+
);
|
|
69
|
+
const preview = entry.text.split("\n").slice(0, 8).join("\n");
|
|
70
|
+
lines.push(indentBlock(preview, " "));
|
|
71
|
+
if (entry.text.split("\n").length > 8) {
|
|
72
|
+
lines.push(
|
|
73
|
+
` \u2026(${entry.text.split("\n").length - 8} more lines \u2014 read_file ${entry.path}:${entry.startLine} for the full chunk)`
|
|
74
|
+
);
|
|
75
|
+
}
|
|
76
|
+
});
|
|
77
|
+
return lines.join("\n");
|
|
78
|
+
}
|
|
79
|
+
function indentBlock(text, prefix) {
|
|
80
|
+
return text.split("\n").map((l) => prefix + l).join("\n");
|
|
81
|
+
}
|
|
82
|
+
async function bootstrapSemanticSearchInCodeMode(registry, rootDir, opts = {}) {
|
|
83
|
+
if (await indexCompatible(rootDir, { provider: opts.provider, model: opts.model })) {
|
|
84
|
+
await registerSemanticSearchTool(registry, { ...opts, root: rootDir });
|
|
85
|
+
return { enabled: true };
|
|
86
|
+
}
|
|
87
|
+
return { enabled: false };
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
export {
|
|
91
|
+
registerSemanticSearchTool,
|
|
92
|
+
bootstrapSemanticSearchInCodeMode
|
|
93
|
+
};
|
|
94
|
+
//# sourceMappingURL=chunk-J5VLP23S.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../src/index/semantic/tool.ts"],"sourcesContent":["import type { ToolRegistry } from \"../../tools.js\";\nimport { indexCompatible, indexExists, querySemantic } from \"./builder.js\";\nimport type { SearchHit } from \"./store.js\";\n\ntype SemanticToolOptions = {\n provider?: \"ollama\" | \"openai-compat\";\n baseUrl?: string;\n apiKey?: string;\n model?: string;\n extraBody?: Record<string, unknown>;\n timeoutMs?: number;\n root: string;\n defaultTopK?: number;\n defaultMinScore?: number;\n};\n\nexport async function registerSemanticSearchTool(\n registry: ToolRegistry,\n opts: SemanticToolOptions,\n): Promise<boolean> {\n if (!(await indexCompatible(opts.root, { provider: opts.provider, model: opts.model })))\n return false;\n const defaultTopK = opts.defaultTopK ?? 8;\n const defaultMinScore = opts.defaultMinScore ?? 0.3;\n\n registry.register({\n name: \"semantic_search\",\n description:\n \"FIRST CHOICE for descriptive queries. Use this BEFORE search_content (grep) when the user describes WHAT code does ('where do we handle X', 'which file owns Y', 'how does Z work', 'find the logic that …'). Returns ranked snippets ordered by semantic relevance — finds the right file even when your description shares no words with the code. Falls back to search_content / search_files only for: exact identifiers, regex patterns, or counting occurrences of a known token. If your first instinct is grep on a paraphrased question, you are wrong — try semantic_search first.\",\n readOnly: true,\n parallelSafe: true,\n parameters: {\n type: \"object\",\n properties: {\n query: {\n type: \"string\",\n description:\n \"Natural-language description, phrased as a question or noun phrase: 'where do we validate the session cookie?' / 'retry backoff logic' / 'code that prevents user changes from immediately landing on disk'. Do NOT pass exact identifiers — those are search_content's job.\",\n },\n topK: {\n type: \"integer\",\n description: `Number of snippets to return (1..16). Default ${defaultTopK}.`,\n },\n minScore: {\n type: \"number\",\n description: `Drop snippets with cosine score below this (0..1). Default ${defaultMinScore}. Raise for stricter matches; lower if the index is small.`,\n },\n },\n required: [\"query\"],\n },\n fn: async (args: { query: string; topK?: number; minScore?: number }, ctx) => {\n const hits = await querySemantic(opts.root, args.query, {\n topK: args.topK ?? defaultTopK,\n minScore: args.minScore ?? defaultMinScore,\n provider: opts.provider,\n baseUrl: opts.baseUrl,\n apiKey: opts.apiKey,\n model: opts.model,\n extraBody: opts.extraBody,\n signal: ctx?.signal,\n });\n if (hits === null) {\n return \"No semantic index found for this project. Run `reasonix index` to build one.\";\n }\n if (hits.length === 0) {\n return `query: ${args.query}\\n\\nno matches above the score threshold (${args.minScore ?? defaultMinScore}).`;\n }\n return formatHits(args.query, hits);\n },\n });\n return true;\n}\n\nexport function formatHits(query: string, hits: readonly SearchHit[]): string {\n const lines: string[] = [`query: ${query}`, `\\nresults (${hits.length}):`];\n hits.forEach((h, i) => {\n const { entry, score } = h;\n lines.push(\n `\\n${i + 1}. ${entry.path}:${entry.startLine}-${entry.endLine} (score ${score.toFixed(3)})`,\n );\n // Cap each snippet so a 60-line chunk doesn't dominate the\n // model's context. The full chunk is still discoverable via\n // read_file once the model picks the most relevant hit.\n const preview = entry.text.split(\"\\n\").slice(0, 8).join(\"\\n\");\n lines.push(indentBlock(preview, \" \"));\n if (entry.text.split(\"\\n\").length > 8) {\n lines.push(\n ` …(${entry.text.split(\"\\n\").length - 8} more lines — read_file ${entry.path}:${entry.startLine} for the full chunk)`,\n );\n }\n });\n return lines.join(\"\\n\");\n}\n\nfunction indentBlock(text: string, prefix: string): string {\n return text\n .split(\"\\n\")\n .map((l) => prefix + l)\n .join(\"\\n\");\n}\n\n/** Silent: register if index exists, else skip — no Ollama probe, no setup prompt. */\nexport async function bootstrapSemanticSearchInCodeMode(\n registry: ToolRegistry,\n rootDir: string,\n opts: Omit<SemanticToolOptions, \"root\" | \"defaultTopK\" | \"defaultMinScore\"> = {},\n): Promise<{ enabled: boolean }> {\n if (await indexCompatible(rootDir, { provider: opts.provider, model: opts.model })) {\n await registerSemanticSearchTool(registry, { ...opts, root: rootDir });\n return { enabled: true };\n }\n return { enabled: false };\n}\n"],"mappings":";;;;;;;AAgBA,eAAsB,2BACpB,UACA,MACkB;AAClB,MAAI,CAAE,MAAM,gBAAgB,KAAK,MAAM,EAAE,UAAU,KAAK,UAAU,OAAO,KAAK,MAAM,CAAC;AACnF,WAAO;AACT,QAAM,cAAc,KAAK,eAAe;AACxC,QAAM,kBAAkB,KAAK,mBAAmB;AAEhD,WAAS,SAAS;AAAA,IAChB,MAAM;AAAA,IACN,aACE;AAAA,IACF,UAAU;AAAA,IACV,cAAc;AAAA,IACd,YAAY;AAAA,MACV,MAAM;AAAA,MACN,YAAY;AAAA,QACV,OAAO;AAAA,UACL,MAAM;AAAA,UACN,aACE;AAAA,QACJ;AAAA,QACA,MAAM;AAAA,UACJ,MAAM;AAAA,UACN,aAAa,iDAAiD,WAAW;AAAA,QAC3E;AAAA,QACA,UAAU;AAAA,UACR,MAAM;AAAA,UACN,aAAa,8DAA8D,eAAe;AAAA,QAC5F;AAAA,MACF;AAAA,MACA,UAAU,CAAC,OAAO;AAAA,IACpB;AAAA,IACA,IAAI,OAAO,MAA2D,QAAQ;AAC5E,YAAM,OAAO,MAAM,cAAc,KAAK,MAAM,KAAK,OAAO;AAAA,QACtD,MAAM,KAAK,QAAQ;AAAA,QACnB,UAAU,KAAK,YAAY;AAAA,QAC3B,UAAU,KAAK;AAAA,QACf,SAAS,KAAK;AAAA,QACd,QAAQ,KAAK;AAAA,QACb,OAAO,KAAK;AAAA,QACZ,WAAW,KAAK;AAAA,QAChB,QAAQ,KAAK;AAAA,MACf,CAAC;AACD,UAAI,SAAS,MAAM;AACjB,eAAO;AAAA,MACT;AACA,UAAI,KAAK,WAAW,GAAG;AACrB,eAAO,UAAU,KAAK,KAAK;AAAA;AAAA,wCAA6C,KAAK,YAAY,eAAe;AAAA,MAC1G;AACA,aAAO,WAAW,KAAK,OAAO,IAAI;AAAA,IACpC;AAAA,EACF,CAAC;AACD,SAAO;AACT;AAEO,SAAS,WAAW,OAAe,MAAoC;AAC5E,QAAM,QAAkB,CAAC,UAAU,KAAK,IAAI;AAAA,WAAc,KAAK,MAAM,IAAI;AACzE,OAAK,QAAQ,CAAC,GAAG,MAAM;AACrB,UAAM,EAAE,OAAO,MAAM,IAAI;AACzB,UAAM;AAAA,MACJ;AAAA,EAAK,IAAI,CAAC,KAAK,MAAM,IAAI,IAAI,MAAM,SAAS,IAAI,MAAM,OAAO,YAAY,MAAM,QAAQ,CAAC,CAAC;AAAA,IAC3F;AAIA,UAAM,UAAU,MAAM,KAAK,MAAM,IAAI,EAAE,MAAM,GAAG,CAAC,EAAE,KAAK,IAAI;AAC5D,UAAM,KAAK,YAAY,SAAS,KAAK,CAAC;AACtC,QAAI,MAAM,KAAK,MAAM,IAAI,EAAE,SAAS,GAAG;AACrC,YAAM;AAAA,QACJ,aAAQ,MAAM,KAAK,MAAM,IAAI,EAAE,SAAS,CAAC,gCAA2B,MAAM,IAAI,IAAI,MAAM,SAAS;AAAA,MACnG;AAAA,IACF;AAAA,EACF,CAAC;AACD,SAAO,MAAM,KAAK,IAAI;AACxB;AAEA,SAAS,YAAY,MAAc,QAAwB;AACzD,SAAO,KACJ,MAAM,IAAI,EACV,IAAI,CAAC,MAAM,SAAS,CAAC,EACrB,KAAK,IAAI;AACd;AAGA,eAAsB,kCACpB,UACA,SACA,OAA8E,CAAC,GAChD;AAC/B,MAAI,MAAM,gBAAgB,SAAS,EAAE,UAAU,KAAK,UAAU,OAAO,KAAK,MAAM,CAAC,GAAG;AAClF,UAAM,2BAA2B,UAAU,EAAE,GAAG,MAAM,MAAM,QAAQ,CAAC;AACrE,WAAO,EAAE,SAAS,KAAK;AAAA,EACzB;AACA,SAAO,EAAE,SAAS,MAAM;AAC1B;","names":[]}
|
|
@@ -0,0 +1,303 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
// src/client.ts
|
|
4
|
+
import { createParser } from "eventsource-parser";
|
|
5
|
+
|
|
6
|
+
// src/retry.ts
|
|
7
|
+
var DEFAULT_RETRYABLE_STATUSES = [408, 429, 500, 502, 503, 504];
|
|
8
|
+
async function fetchWithRetry(fetchFn, url, init, opts = {}) {
|
|
9
|
+
const maxAttempts = opts.maxAttempts ?? 4;
|
|
10
|
+
const initial = opts.initialBackoffMs ?? 500;
|
|
11
|
+
const cap = opts.maxBackoffMs ?? 1e4;
|
|
12
|
+
const retryable = new Set(opts.retryableStatuses ?? DEFAULT_RETRYABLE_STATUSES);
|
|
13
|
+
let lastError;
|
|
14
|
+
for (let attempt = 0; attempt < maxAttempts; attempt++) {
|
|
15
|
+
if (opts.signal?.aborted) throw new Error("aborted");
|
|
16
|
+
try {
|
|
17
|
+
const resp = await fetchFn(url, init);
|
|
18
|
+
if (resp.ok || !retryable.has(resp.status)) return resp;
|
|
19
|
+
if (attempt === maxAttempts - 1) return resp;
|
|
20
|
+
await resp.text().catch(() => void 0);
|
|
21
|
+
const waitMs = computeWait(attempt, initial, cap, resp.headers.get("Retry-After"));
|
|
22
|
+
opts.onRetry?.({ attempt: attempt + 1, reason: `http ${resp.status}`, waitMs });
|
|
23
|
+
await sleep(waitMs, opts.signal);
|
|
24
|
+
} catch (err) {
|
|
25
|
+
lastError = err;
|
|
26
|
+
if (isAbortError(err) || opts.signal?.aborted) throw err;
|
|
27
|
+
if (attempt === maxAttempts - 1) throw err;
|
|
28
|
+
const waitMs = computeWait(attempt, initial, cap, null);
|
|
29
|
+
opts.onRetry?.({
|
|
30
|
+
attempt: attempt + 1,
|
|
31
|
+
reason: `network: ${messageOf(err)}`,
|
|
32
|
+
waitMs
|
|
33
|
+
});
|
|
34
|
+
await sleep(waitMs, opts.signal);
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
throw lastError ?? new Error("fetchWithRetry: loop exited unexpectedly");
|
|
38
|
+
}
|
|
39
|
+
function computeWait(attempt, initial, cap, retryAfter) {
|
|
40
|
+
if (retryAfter) {
|
|
41
|
+
const seconds = Number.parseFloat(retryAfter);
|
|
42
|
+
if (Number.isFinite(seconds) && seconds > 0) {
|
|
43
|
+
return Math.min(seconds * 1e3, cap);
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
const exp = initial * 2 ** attempt;
|
|
47
|
+
const jitter = exp * (0.75 + Math.random() * 0.5);
|
|
48
|
+
return Math.min(Math.max(jitter, 0), cap);
|
|
49
|
+
}
|
|
50
|
+
function sleep(ms, signal) {
|
|
51
|
+
if (ms <= 0) return Promise.resolve();
|
|
52
|
+
return new Promise((resolve, reject) => {
|
|
53
|
+
const timer = setTimeout(resolve, ms);
|
|
54
|
+
if (signal) {
|
|
55
|
+
const onAbort = () => {
|
|
56
|
+
clearTimeout(timer);
|
|
57
|
+
reject(new Error("aborted"));
|
|
58
|
+
};
|
|
59
|
+
if (signal.aborted) onAbort();
|
|
60
|
+
else signal.addEventListener("abort", onAbort, { once: true });
|
|
61
|
+
}
|
|
62
|
+
});
|
|
63
|
+
}
|
|
64
|
+
function isAbortError(err) {
|
|
65
|
+
if (!err || typeof err !== "object") return false;
|
|
66
|
+
const name = err.name;
|
|
67
|
+
return name === "AbortError";
|
|
68
|
+
}
|
|
69
|
+
function messageOf(err) {
|
|
70
|
+
if (err instanceof Error) return err.message;
|
|
71
|
+
try {
|
|
72
|
+
return String(err);
|
|
73
|
+
} catch {
|
|
74
|
+
return "unknown error";
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
// src/client.ts
|
|
79
|
+
var Usage = class _Usage {
|
|
80
|
+
constructor(promptTokens = 0, completionTokens = 0, totalTokens = 0, promptCacheHitTokens = 0, promptCacheMissTokens = 0) {
|
|
81
|
+
this.promptTokens = promptTokens;
|
|
82
|
+
this.completionTokens = completionTokens;
|
|
83
|
+
this.totalTokens = totalTokens;
|
|
84
|
+
this.promptCacheHitTokens = promptCacheHitTokens;
|
|
85
|
+
this.promptCacheMissTokens = promptCacheMissTokens;
|
|
86
|
+
}
|
|
87
|
+
promptTokens;
|
|
88
|
+
completionTokens;
|
|
89
|
+
totalTokens;
|
|
90
|
+
promptCacheHitTokens;
|
|
91
|
+
promptCacheMissTokens;
|
|
92
|
+
get cacheHitRatio() {
|
|
93
|
+
const denom = this.promptCacheHitTokens + this.promptCacheMissTokens;
|
|
94
|
+
return denom > 0 ? this.promptCacheHitTokens / denom : 0;
|
|
95
|
+
}
|
|
96
|
+
static fromApi(raw) {
|
|
97
|
+
const u = raw ?? {};
|
|
98
|
+
return new _Usage(
|
|
99
|
+
u.prompt_tokens ?? 0,
|
|
100
|
+
u.completion_tokens ?? 0,
|
|
101
|
+
u.total_tokens ?? 0,
|
|
102
|
+
u.prompt_cache_hit_tokens ?? 0,
|
|
103
|
+
u.prompt_cache_miss_tokens ?? 0
|
|
104
|
+
);
|
|
105
|
+
}
|
|
106
|
+
};
|
|
107
|
+
var DeepSeekClient = class {
|
|
108
|
+
apiKey;
|
|
109
|
+
baseUrl;
|
|
110
|
+
timeoutMs;
|
|
111
|
+
retry;
|
|
112
|
+
_fetch;
|
|
113
|
+
constructor(opts = {}) {
|
|
114
|
+
const apiKey = opts.apiKey ?? process.env.DEEPSEEK_API_KEY;
|
|
115
|
+
if (!apiKey) {
|
|
116
|
+
throw new Error(
|
|
117
|
+
"DEEPSEEK_API_KEY is not set. Put it in .env or pass apiKey to DeepSeekClient."
|
|
118
|
+
);
|
|
119
|
+
}
|
|
120
|
+
this.apiKey = apiKey;
|
|
121
|
+
let url = opts.baseUrl ?? process.env.DEEPSEEK_BASE_URL ?? "https://api.deepseek.com";
|
|
122
|
+
while (url.endsWith("/")) url = url.slice(0, -1);
|
|
123
|
+
this.baseUrl = url;
|
|
124
|
+
this.timeoutMs = opts.timeoutMs ?? 66e4;
|
|
125
|
+
this._fetch = opts.fetch ?? globalThis.fetch.bind(globalThis);
|
|
126
|
+
this.retry = opts.retry ?? {};
|
|
127
|
+
}
|
|
128
|
+
buildPayload(opts, stream) {
|
|
129
|
+
const payload = {
|
|
130
|
+
model: opts.model,
|
|
131
|
+
messages: opts.messages,
|
|
132
|
+
stream
|
|
133
|
+
};
|
|
134
|
+
if (opts.tools?.length) payload.tools = opts.tools;
|
|
135
|
+
if (opts.temperature !== void 0) payload.temperature = opts.temperature;
|
|
136
|
+
if (opts.maxTokens !== void 0) payload.max_tokens = opts.maxTokens;
|
|
137
|
+
if (opts.responseFormat) payload.response_format = opts.responseFormat;
|
|
138
|
+
if (opts.thinking) {
|
|
139
|
+
payload.extra_body = { thinking: { type: opts.thinking } };
|
|
140
|
+
}
|
|
141
|
+
if (opts.reasoningEffort) {
|
|
142
|
+
payload.reasoning_effort = opts.reasoningEffort;
|
|
143
|
+
}
|
|
144
|
+
return payload;
|
|
145
|
+
}
|
|
146
|
+
/** Returns null on failure so callers can degrade — session must keep working without balance UI. */
|
|
147
|
+
async getBalance(opts = {}) {
|
|
148
|
+
try {
|
|
149
|
+
const resp = await this._fetch(`${this.baseUrl}/user/balance`, {
|
|
150
|
+
method: "GET",
|
|
151
|
+
headers: { Authorization: `Bearer ${this.apiKey}` },
|
|
152
|
+
signal: opts.signal
|
|
153
|
+
});
|
|
154
|
+
if (!resp.ok) return null;
|
|
155
|
+
const data = await resp.json();
|
|
156
|
+
if (!data || !Array.isArray(data.balance_infos)) return null;
|
|
157
|
+
return data;
|
|
158
|
+
} catch {
|
|
159
|
+
return null;
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
/** Returns null on failure — callers fall back to a hardcoded model hint. */
|
|
163
|
+
async listModels(opts = {}) {
|
|
164
|
+
try {
|
|
165
|
+
const resp = await this._fetch(`${this.baseUrl}/models`, {
|
|
166
|
+
method: "GET",
|
|
167
|
+
headers: { Authorization: `Bearer ${this.apiKey}` },
|
|
168
|
+
signal: opts.signal
|
|
169
|
+
});
|
|
170
|
+
if (!resp.ok) return null;
|
|
171
|
+
const data = await resp.json();
|
|
172
|
+
if (!data || !Array.isArray(data.data)) return null;
|
|
173
|
+
return data;
|
|
174
|
+
} catch {
|
|
175
|
+
return null;
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
async chat(opts) {
|
|
179
|
+
const ctrl = new AbortController();
|
|
180
|
+
const timer = setTimeout(() => ctrl.abort(), this.timeoutMs);
|
|
181
|
+
const signal = opts.signal ?? ctrl.signal;
|
|
182
|
+
try {
|
|
183
|
+
const resp = await fetchWithRetry(
|
|
184
|
+
this._fetch,
|
|
185
|
+
`${this.baseUrl}/chat/completions`,
|
|
186
|
+
{
|
|
187
|
+
method: "POST",
|
|
188
|
+
headers: {
|
|
189
|
+
Authorization: `Bearer ${this.apiKey}`,
|
|
190
|
+
"Content-Type": "application/json"
|
|
191
|
+
},
|
|
192
|
+
body: JSON.stringify(this.buildPayload(opts, false)),
|
|
193
|
+
signal
|
|
194
|
+
},
|
|
195
|
+
{ ...this.retry, signal }
|
|
196
|
+
);
|
|
197
|
+
if (!resp.ok) {
|
|
198
|
+
throw new Error(`DeepSeek ${resp.status}: ${await resp.text()}`);
|
|
199
|
+
}
|
|
200
|
+
const data = await resp.json();
|
|
201
|
+
const choice = data.choices?.[0]?.message ?? {};
|
|
202
|
+
return {
|
|
203
|
+
content: choice.content ?? "",
|
|
204
|
+
reasoningContent: choice.reasoning_content ?? null,
|
|
205
|
+
toolCalls: choice.tool_calls ?? [],
|
|
206
|
+
usage: Usage.fromApi(data.usage),
|
|
207
|
+
raw: data
|
|
208
|
+
};
|
|
209
|
+
} finally {
|
|
210
|
+
clearTimeout(timer);
|
|
211
|
+
}
|
|
212
|
+
}
|
|
213
|
+
async *stream(opts) {
|
|
214
|
+
const ctrl = new AbortController();
|
|
215
|
+
const timer = setTimeout(() => ctrl.abort(), this.timeoutMs);
|
|
216
|
+
const signal = opts.signal ?? ctrl.signal;
|
|
217
|
+
let resp;
|
|
218
|
+
try {
|
|
219
|
+
resp = await fetchWithRetry(
|
|
220
|
+
this._fetch,
|
|
221
|
+
`${this.baseUrl}/chat/completions`,
|
|
222
|
+
{
|
|
223
|
+
method: "POST",
|
|
224
|
+
headers: {
|
|
225
|
+
Authorization: `Bearer ${this.apiKey}`,
|
|
226
|
+
"Content-Type": "application/json",
|
|
227
|
+
Accept: "text/event-stream"
|
|
228
|
+
},
|
|
229
|
+
body: JSON.stringify(this.buildPayload(opts, true)),
|
|
230
|
+
signal
|
|
231
|
+
},
|
|
232
|
+
{ ...this.retry, signal }
|
|
233
|
+
);
|
|
234
|
+
} catch (err) {
|
|
235
|
+
clearTimeout(timer);
|
|
236
|
+
throw err;
|
|
237
|
+
}
|
|
238
|
+
if (!resp.ok || !resp.body) {
|
|
239
|
+
clearTimeout(timer);
|
|
240
|
+
throw new Error(`DeepSeek ${resp.status}: ${await resp.text().catch(() => "")}`);
|
|
241
|
+
}
|
|
242
|
+
const queue = [];
|
|
243
|
+
let done = false;
|
|
244
|
+
const parser = createParser({
|
|
245
|
+
onEvent: (ev) => {
|
|
246
|
+
if (!ev.data || ev.data === "[DONE]") {
|
|
247
|
+
done = true;
|
|
248
|
+
return;
|
|
249
|
+
}
|
|
250
|
+
try {
|
|
251
|
+
const json = JSON.parse(ev.data);
|
|
252
|
+
const delta = json.choices?.[0]?.delta ?? {};
|
|
253
|
+
const finishReason = json.choices?.[0]?.finish_reason ?? void 0;
|
|
254
|
+
const chunk = { raw: json, finishReason };
|
|
255
|
+
if (typeof delta.content === "string" && delta.content.length > 0) {
|
|
256
|
+
chunk.contentDelta = delta.content;
|
|
257
|
+
}
|
|
258
|
+
if (typeof delta.reasoning_content === "string" && delta.reasoning_content.length > 0) {
|
|
259
|
+
chunk.reasoningDelta = delta.reasoning_content;
|
|
260
|
+
}
|
|
261
|
+
if (Array.isArray(delta.tool_calls) && delta.tool_calls.length > 0) {
|
|
262
|
+
const tc = delta.tool_calls[0];
|
|
263
|
+
chunk.toolCallDelta = {
|
|
264
|
+
index: tc.index ?? 0,
|
|
265
|
+
id: tc.id,
|
|
266
|
+
name: tc.function?.name,
|
|
267
|
+
argumentsDelta: tc.function?.arguments
|
|
268
|
+
};
|
|
269
|
+
}
|
|
270
|
+
if (json.usage) {
|
|
271
|
+
chunk.usage = Usage.fromApi(json.usage);
|
|
272
|
+
}
|
|
273
|
+
queue.push(chunk);
|
|
274
|
+
} catch {
|
|
275
|
+
}
|
|
276
|
+
}
|
|
277
|
+
});
|
|
278
|
+
const reader = resp.body.getReader();
|
|
279
|
+
const decoder = new TextDecoder();
|
|
280
|
+
try {
|
|
281
|
+
while (true) {
|
|
282
|
+
if (queue.length > 0) {
|
|
283
|
+
yield queue.shift();
|
|
284
|
+
continue;
|
|
285
|
+
}
|
|
286
|
+
if (done) break;
|
|
287
|
+
const { value, done: streamDone } = await reader.read();
|
|
288
|
+
if (streamDone) break;
|
|
289
|
+
parser.feed(decoder.decode(value, { stream: true }));
|
|
290
|
+
}
|
|
291
|
+
while (queue.length > 0) yield queue.shift();
|
|
292
|
+
} finally {
|
|
293
|
+
clearTimeout(timer);
|
|
294
|
+
reader.releaseLock();
|
|
295
|
+
}
|
|
296
|
+
}
|
|
297
|
+
};
|
|
298
|
+
|
|
299
|
+
export {
|
|
300
|
+
Usage,
|
|
301
|
+
DeepSeekClient
|
|
302
|
+
};
|
|
303
|
+
//# sourceMappingURL=chunk-KMWKGPFZ.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../src/client.ts","../../src/retry.ts"],"sourcesContent":["import { type EventSourceMessage, createParser } from \"eventsource-parser\";\nimport { type RetryOptions, fetchWithRetry } from \"./retry.js\";\nimport type { ChatMessage, ChatRequestOptions, RawUsage, ToolCall, ToolSpec } from \"./types.js\";\n\nexport class Usage {\n constructor(\n public promptTokens = 0,\n public completionTokens = 0,\n public totalTokens = 0,\n public promptCacheHitTokens = 0,\n public promptCacheMissTokens = 0,\n ) {}\n\n get cacheHitRatio(): number {\n const denom = this.promptCacheHitTokens + this.promptCacheMissTokens;\n return denom > 0 ? this.promptCacheHitTokens / denom : 0;\n }\n\n static fromApi(raw: RawUsage | undefined | null): Usage {\n const u = raw ?? {};\n return new Usage(\n u.prompt_tokens ?? 0,\n u.completion_tokens ?? 0,\n u.total_tokens ?? 0,\n u.prompt_cache_hit_tokens ?? 0,\n u.prompt_cache_miss_tokens ?? 0,\n );\n }\n}\n\nexport interface ChatResponse {\n content: string;\n reasoningContent: string | null;\n toolCalls: ToolCall[];\n usage: Usage;\n raw: unknown;\n}\n\nexport interface StreamChunk {\n contentDelta?: string;\n reasoningDelta?: string;\n toolCallDelta?: { index: number; id?: string; name?: string; argumentsDelta?: string };\n usage?: Usage;\n finishReason?: string;\n raw: any;\n}\n\nexport interface BalanceInfo {\n currency: string;\n total_balance: string;\n granted_balance?: string;\n topped_up_balance?: string;\n}\n\nexport interface UserBalance {\n is_available: boolean;\n balance_infos: BalanceInfo[];\n}\n\nexport interface ModelInfo {\n id: string;\n object: \"model\";\n owned_by: string;\n}\n\nexport interface ModelList {\n object: \"list\";\n data: ModelInfo[];\n}\n\nexport interface DeepSeekClientOptions {\n apiKey?: string;\n baseUrl?: string;\n timeoutMs?: number;\n fetch?: typeof fetch;\n /** Retry configuration. Pass `{ maxAttempts: 1 }` to disable retries. */\n retry?: RetryOptions;\n}\n\nexport class DeepSeekClient {\n readonly apiKey: string;\n readonly baseUrl: string;\n readonly timeoutMs: number;\n readonly retry: RetryOptions;\n private readonly _fetch: typeof fetch;\n\n constructor(opts: DeepSeekClientOptions = {}) {\n const apiKey = opts.apiKey ?? process.env.DEEPSEEK_API_KEY;\n if (!apiKey) {\n throw new Error(\n \"DEEPSEEK_API_KEY is not set. Put it in .env or pass apiKey to DeepSeekClient.\",\n );\n }\n this.apiKey = apiKey;\n let url = opts.baseUrl ?? process.env.DEEPSEEK_BASE_URL ?? \"https://api.deepseek.com\";\n // Manual trim — `/\\/+$/` is O(n²) on slash-heavy non-matches per CodeQL js/polynomial-redos.\n while (url.endsWith(\"/\")) url = url.slice(0, -1);\n this.baseUrl = url;\n // 11 min. DeepSeek's load-balancer may keep a connection open for\n // up to 10 minutes while the request waits in queue (non-streaming\n // sends empty lines, streaming sends `:` SSE keep-alive comments —\n // both are invisible to our parsers, so neither surfaces until the\n // real response starts). Timing out at the legacy 2-min default\n // killed queued requests prematurely, burned the queue slot on\n // retry, and could loop through the whole queue repeatedly.\n // Setting 11 min lets the server's own 10-min cap close the\n // connection first (clean EOF → natural retry), and our timer\n // is a safety net for genuinely hung sockets.\n this.timeoutMs = opts.timeoutMs ?? 660_000;\n this._fetch = opts.fetch ?? globalThis.fetch.bind(globalThis);\n this.retry = opts.retry ?? {};\n }\n\n private buildPayload(opts: ChatRequestOptions, stream: boolean) {\n const payload: Record<string, unknown> = {\n model: opts.model,\n messages: opts.messages,\n stream,\n };\n if (opts.tools?.length) payload.tools = opts.tools;\n if (opts.temperature !== undefined) payload.temperature = opts.temperature;\n if (opts.maxTokens !== undefined) payload.max_tokens = opts.maxTokens;\n if (opts.responseFormat) payload.response_format = opts.responseFormat;\n // V4 thinking-mode toggle: lives under `extra_body.thinking.type` per\n // DeepSeek's docs. Docs also note that in thinking mode `temperature`,\n // `top_p`, `presence_penalty`, `frequency_penalty` are silently\n // ignored — we don't strip them here because the server's explicit\n // \"setting won't report an error\" contract means leaving them in is\n // safe and keeps the request payload diffable against OpenAI tooling.\n if (opts.thinking) {\n payload.extra_body = { thinking: { type: opts.thinking } };\n }\n if (opts.reasoningEffort) {\n payload.reasoning_effort = opts.reasoningEffort;\n }\n return payload;\n }\n\n /** Returns null on failure so callers can degrade — session must keep working without balance UI. */\n async getBalance(opts: { signal?: AbortSignal } = {}): Promise<UserBalance | null> {\n try {\n const resp = await this._fetch(`${this.baseUrl}/user/balance`, {\n method: \"GET\",\n headers: { Authorization: `Bearer ${this.apiKey}` },\n signal: opts.signal,\n });\n if (!resp.ok) return null;\n const data = (await resp.json()) as UserBalance;\n if (!data || !Array.isArray(data.balance_infos)) return null;\n return data;\n } catch {\n return null;\n }\n }\n\n /** Returns null on failure — callers fall back to a hardcoded model hint. */\n async listModels(opts: { signal?: AbortSignal } = {}): Promise<ModelList | null> {\n try {\n const resp = await this._fetch(`${this.baseUrl}/models`, {\n method: \"GET\",\n headers: { Authorization: `Bearer ${this.apiKey}` },\n signal: opts.signal,\n });\n if (!resp.ok) return null;\n const data = (await resp.json()) as ModelList;\n if (!data || !Array.isArray(data.data)) return null;\n return data;\n } catch {\n return null;\n }\n }\n\n async chat(opts: ChatRequestOptions): Promise<ChatResponse> {\n const ctrl = new AbortController();\n const timer = setTimeout(() => ctrl.abort(), this.timeoutMs);\n const signal = opts.signal ?? ctrl.signal;\n\n try {\n const resp = await fetchWithRetry(\n this._fetch,\n `${this.baseUrl}/chat/completions`,\n {\n method: \"POST\",\n headers: {\n Authorization: `Bearer ${this.apiKey}`,\n \"Content-Type\": \"application/json\",\n },\n body: JSON.stringify(this.buildPayload(opts, false)),\n signal,\n },\n { ...this.retry, signal },\n );\n if (!resp.ok) {\n throw new Error(`DeepSeek ${resp.status}: ${await resp.text()}`);\n }\n const data: any = await resp.json();\n const choice = data.choices?.[0]?.message ?? {};\n return {\n content: choice.content ?? \"\",\n reasoningContent: choice.reasoning_content ?? null,\n toolCalls: choice.tool_calls ?? [],\n usage: Usage.fromApi(data.usage),\n raw: data,\n };\n } finally {\n clearTimeout(timer);\n }\n }\n\n async *stream(opts: ChatRequestOptions): AsyncGenerator<StreamChunk> {\n const ctrl = new AbortController();\n const timer = setTimeout(() => ctrl.abort(), this.timeoutMs);\n const signal = opts.signal ?? ctrl.signal;\n\n let resp: Response;\n try {\n // Only the initial fetch is retried. Once the server has started sending\n // the stream body we do NOT retry — a mid-stream retry would re-bill and\n // desync the session context.\n resp = await fetchWithRetry(\n this._fetch,\n `${this.baseUrl}/chat/completions`,\n {\n method: \"POST\",\n headers: {\n Authorization: `Bearer ${this.apiKey}`,\n \"Content-Type\": \"application/json\",\n Accept: \"text/event-stream\",\n },\n body: JSON.stringify(this.buildPayload(opts, true)),\n signal,\n },\n { ...this.retry, signal },\n );\n } catch (err) {\n clearTimeout(timer);\n throw err;\n }\n if (!resp.ok || !resp.body) {\n clearTimeout(timer);\n throw new Error(`DeepSeek ${resp.status}: ${await resp.text().catch(() => \"\")}`);\n }\n\n const queue: StreamChunk[] = [];\n let done = false;\n const parser = createParser({\n onEvent: (ev: EventSourceMessage) => {\n if (!ev.data || ev.data === \"[DONE]\") {\n done = true;\n return;\n }\n try {\n const json = JSON.parse(ev.data);\n const delta = json.choices?.[0]?.delta ?? {};\n const finishReason = json.choices?.[0]?.finish_reason ?? undefined;\n const chunk: StreamChunk = { raw: json, finishReason };\n if (typeof delta.content === \"string\" && delta.content.length > 0) {\n chunk.contentDelta = delta.content;\n }\n if (typeof delta.reasoning_content === \"string\" && delta.reasoning_content.length > 0) {\n chunk.reasoningDelta = delta.reasoning_content;\n }\n if (Array.isArray(delta.tool_calls) && delta.tool_calls.length > 0) {\n const tc = delta.tool_calls[0];\n chunk.toolCallDelta = {\n index: tc.index ?? 0,\n id: tc.id,\n name: tc.function?.name,\n argumentsDelta: tc.function?.arguments,\n };\n }\n if (json.usage) {\n chunk.usage = Usage.fromApi(json.usage);\n }\n queue.push(chunk);\n } catch {\n /* skip malformed sse frame */\n }\n },\n });\n\n const reader = resp.body.getReader();\n const decoder = new TextDecoder();\n try {\n while (true) {\n if (queue.length > 0) {\n yield queue.shift()!;\n continue;\n }\n if (done) break;\n const { value, done: streamDone } = await reader.read();\n if (streamDone) break;\n parser.feed(decoder.decode(value, { stream: true }));\n }\n while (queue.length > 0) yield queue.shift()!;\n } finally {\n clearTimeout(timer);\n reader.releaseLock();\n }\n }\n}\n\nexport type { ChatMessage, ToolCall, ToolSpec };\n","/** No retry on aborts or mid-stream body errors — re-billing the user for desynced output is worse than failing. */\n\nexport interface RetryOptions {\n /** Maximum total attempts (including the first). Default 4. */\n maxAttempts?: number;\n /** Initial backoff in ms. Doubles each retry, with jitter. Default 500. */\n initialBackoffMs?: number;\n /** Upper bound on any single backoff delay. Default 10000 (10s). */\n maxBackoffMs?: number;\n /** HTTP statuses to treat as retryable. Default [408, 429, 500, 502, 503, 504]. */\n retryableStatuses?: readonly number[];\n /** Abort signal; we do NOT retry once aborted. */\n signal?: AbortSignal;\n /** Telemetry hook — called before each wait. */\n onRetry?: (info: RetryInfo) => void;\n}\n\nexport interface RetryInfo {\n attempt: number;\n reason: string;\n waitMs: number;\n}\n\nconst DEFAULT_RETRYABLE_STATUSES = [408, 429, 500, 502, 503, 504] as const;\n\nexport async function fetchWithRetry(\n fetchFn: typeof fetch,\n url: string,\n init: RequestInit,\n opts: RetryOptions = {},\n): Promise<Response> {\n const maxAttempts = opts.maxAttempts ?? 4;\n const initial = opts.initialBackoffMs ?? 500;\n const cap = opts.maxBackoffMs ?? 10_000;\n const retryable = new Set(opts.retryableStatuses ?? DEFAULT_RETRYABLE_STATUSES);\n\n let lastError: unknown;\n\n for (let attempt = 0; attempt < maxAttempts; attempt++) {\n if (opts.signal?.aborted) throw new Error(\"aborted\");\n\n try {\n const resp = await fetchFn(url, init);\n\n // Success or non-retryable failure: return as-is.\n if (resp.ok || !retryable.has(resp.status)) return resp;\n\n // Retryable but out of attempts: return the last response so the caller\n // can surface the status to the user.\n if (attempt === maxAttempts - 1) return resp;\n\n // Drain the body so the connection can be reused on the next attempt.\n await resp.text().catch(() => undefined);\n\n const waitMs = computeWait(attempt, initial, cap, resp.headers.get(\"Retry-After\"));\n opts.onRetry?.({ attempt: attempt + 1, reason: `http ${resp.status}`, waitMs });\n await sleep(waitMs, opts.signal);\n } catch (err) {\n lastError = err;\n // Respect explicit aborts — do not retry.\n if (isAbortError(err) || opts.signal?.aborted) throw err;\n if (attempt === maxAttempts - 1) throw err;\n\n const waitMs = computeWait(attempt, initial, cap, null);\n opts.onRetry?.({\n attempt: attempt + 1,\n reason: `network: ${messageOf(err)}`,\n waitMs,\n });\n await sleep(waitMs, opts.signal);\n }\n }\n\n throw lastError ?? new Error(\"fetchWithRetry: loop exited unexpectedly\");\n}\n\nfunction computeWait(\n attempt: number,\n initial: number,\n cap: number,\n retryAfter: string | null,\n): number {\n if (retryAfter) {\n const seconds = Number.parseFloat(retryAfter);\n if (Number.isFinite(seconds) && seconds > 0) {\n return Math.min(seconds * 1000, cap);\n }\n }\n const exp = initial * 2 ** attempt;\n // Jitter range [75%, 125%] to spread retries out when many clients hit 429 together.\n const jitter = exp * (0.75 + Math.random() * 0.5);\n return Math.min(Math.max(jitter, 0), cap);\n}\n\nfunction sleep(ms: number, signal?: AbortSignal): Promise<void> {\n if (ms <= 0) return Promise.resolve();\n return new Promise((resolve, reject) => {\n const timer = setTimeout(resolve, ms);\n if (signal) {\n const onAbort = () => {\n clearTimeout(timer);\n reject(new Error(\"aborted\"));\n };\n if (signal.aborted) onAbort();\n else signal.addEventListener(\"abort\", onAbort, { once: true });\n }\n });\n}\n\nfunction isAbortError(err: unknown): boolean {\n if (!err || typeof err !== \"object\") return false;\n const name = (err as { name?: unknown }).name;\n return name === \"AbortError\";\n}\n\nfunction messageOf(err: unknown): string {\n if (err instanceof Error) return err.message;\n try {\n return String(err);\n } catch {\n return \"unknown error\";\n }\n}\n"],"mappings":"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","names":[]}
|