/** * PersistentMcpBridge — thin wrapper * * The implementation lives in qiming-mcp-stdio-proxy, installed to ~/.qimingclaw/node_modules. * This module creates a singleton with electron-log injected as the logger. * Uses dynamic require from ~/.qimingclaw so the app does not bundle the package. */ import * as path from "path"; import log from "electron-log"; import { getAppPaths, getBundledMcpProxyDir } from "./packageLocator"; const PKG_NAME = "qiming-mcp-stdio-proxy"; /** * Wrap electron-log so high-frequency messages (e.g. "New HTTP session") * are downgraded from info → debug to reduce log noise. */ const NOISY_PATTERNS = [/New HTTP session/]; function createQuietLogger(): typeof log { const quiet = Object.create(log) as typeof log; const originalInfo = log.info.bind(log); quiet.info = (...args: Parameters) => { const first = args[0]; if ( typeof first === "string" && NOISY_PATTERNS.some((p) => p.test(first)) ) { log.debug(...args); return; } originalInfo(...args); }; return quiet; } /** Lazy-loaded singleton instance (from qiming-mcp-stdio-proxy) */ let instance: { start: (args: unknown) => Promise; stop: () => Promise; isRunning: () => boolean; getBridgeUrl: (name: string) => string | null; } | null = null; function getInstance(): NonNullable { if (instance) return instance; // 1. 应用内集成版本(bundled resources) const bundledDir = getBundledMcpProxyDir(); if (bundledDir) { try { // eslint-disable-next-line @typescript-eslint/no-require-imports const pkg = require(bundledDir); if (pkg.PersistentMcpBridge) { log.info( `[PersistentMcpBridge] Using bundled integration: ${bundledDir}`, ); instance = new pkg.PersistentMcpBridge( createQuietLogger(), ) as NonNullable; return instance; } } catch (err) { log.warn( `[PersistentMcpBridge] Bundled integration load failed, falling back to node_modules:`, err instanceof Error ? err.message : String(err), ); } } // 2. 回退兼容: ~/.qimingbot/node_modules const nodeModules = getAppPaths().nodeModules; const pkgPath = path.join(nodeModules, PKG_NAME); try { // eslint-disable-next-line @typescript-eslint/no-require-imports const pkg = require(pkgPath); if (!pkg.PersistentMcpBridge) { throw new Error(`${PKG_NAME}: PersistentMcpBridge export not found`); } log.info( `[PersistentMcpBridge] Using ~/.qimingbot path (legacy fallback): ${pkgPath}`, ); instance = new pkg.PersistentMcpBridge(createQuietLogger()) as NonNullable< typeof instance >; return instance; } catch (err) { const msg = err instanceof Error ? err.message : String(err); log.error(`[PersistentMcpBridge] Failed to load ${PKG_NAME}:`, msg); throw new Error( `${PKG_NAME} not installed or failed to load.${msg ? ` (${msg})` : ""}`, ); } } export const persistentMcpBridge = { async start( args: Parameters["start"]>[0], ): Promise { return getInstance().start(args); }, async stop(): Promise { return getInstance().stop(); }, isRunning(): boolean { try { return getInstance().isRunning(); } catch { return false; } }, getBridgeUrl(name: string): string | null { try { return getInstance().getBridgeUrl(name); } catch { return null; } }, };