UNPKG

comfyui-mcp

Version:

Local-first, agent-native control plane for ComfyUI — MCP server + autonomous sidebar agent that drives your live graph in natural language on ANY LLM: Claude/ChatGPT/Gemini on your subscription (no API key), free local models via Ollama (fully offline),

79 lines • 3.67 kB
import { logger } from "../utils/logger.js"; import { KIMI_CAPABILITIES } from "./agent-backend.js"; import { OllamaBackend } from "./ollama-backend.js"; import { KIMI_CODE_DEFAULT_BASE, resolveKimiCodeOAuth, } from "../services/code-provider-auth.js"; export const KIMI_DEFAULT_MODEL = process.env.COMFYUI_MCP_KIMI_MODEL?.trim() || "kimi-for-coding"; /** Kimi Code subscription OAuth (or KIMI_API_KEY) — OpenAI-compatible coding API. */ export class KimiBackend extends OllamaBackend { capabilities = KIMI_CAPABILITIES; constructor(deps = {}) { const apiKey = process.env.KIMI_API_KEY?.trim() || "pending-oauth"; super({ ...deps, backendId: "kimi", api: "openai", host: process.env.COMFYUI_MCP_KIMI_BASE_URL?.trim().replace(/\/$/, "") || KIMI_CODE_DEFAULT_BASE, apiKey, model: deps.model ?? KIMI_DEFAULT_MODEL, }); } /** * K3 refuses the inherited `temperature: 0` outright — * `400 invalid temperature: only 1 is allowed for this model` — which made * this backend unusable on every turn (#2535). * * Sends NO temperature rather than pinning 1: the field is only refused by * some models on this host (`kimi-for-coding` accepts a value), so letting the * endpoint apply its own per-model default is the one choice that is correct * for all of them. An explicit COMFYUI_MCP_OLLAMA_TEMPERATURE still overrides. */ defaultTemperature() { return undefined; } /** * Re-resolve the OAuth token, at session start AND before every turn (#2546). * * Kimi Code access tokens carry `expires_in: 900` — fifteen minutes. Resolving * once in prepare() meant any session that idled longer than that 401'd on * every subsequent turn until the user hit Disconnect → Connect, while the * CLI's credential file on disk held a perfectly good token. * * Cheap to call per turn: resolveKimiCodeOAuth re-READS the credential file * each time and only reaches the network inside the refresh skew. Re-reading * is also what makes this correct rather than merely fresh — the Kimi Code CLI * rotates that same file on its own schedule, so any in-memory cache here * would go stale behind our back. Mirrors CopilotBackend's * ensureFreshCopilotToken, which exists for the same short-bearer reason. */ async ensureFreshKimiAuth() { const creds = await resolveKimiCodeOAuth(); this.setOpenAiAuth(creds.baseUrl, creds.accessToken); } async prepare() { await this.ensureFreshKimiAuth(); return super.prepare(); } /** * Best-effort, exactly as CopilotBackend's wrapper is: a refresh failure is * logged and the turn proceeds on the existing token. If that token really is * stale the call 401s and surfaces through OllamaBackend's normal turn-error * path — a `result:false` the user sees — rather than an unhandled rejection * that could park the panel's turn gate. */ async *wrapChannel(channel) { for await (const turn of channel) { try { await this.ensureFreshKimiAuth(); } catch (err) { logger.warn(`[kimi-backend] token refresh before turn failed (${err instanceof Error ? err.message : String(err)}) — attempting the turn with the existing token.`); } yield turn; } } async *run(opts) { yield* super.run({ ...opts, channel: this.wrapChannel(opts.channel) }); } } //# sourceMappingURL=kimi-backend.js.map