UNPKG

@sellable/install

Version:

One-command installer for Sellable MCP in Claude Code, Codex, and Hermes

99 lines (96 loc) • 3.74 kB
// Builds the Runs client for a workspace's own model connection. The Next // server and the agent runtime both import only this module for workspace // connections, so it has no top-level code beyond imports and declarations. import { randomUUID } from "node:crypto"; import { createHermesRunsClient } from "./hermes-runs-client.mjs"; import { createOpenAiRunsClient } from "./openai-runs-client.mjs"; /** @param {any} options */ export function createWorkspaceConnectionRunsClient({ connection, apiKey, fetchImpl, modelSelection, responseSchema, }) { // The caller's fetchImpl is the address guard (createPinnedFetch). Without it // both clients would fall back to the global fetch and dial unguarded. if (typeof fetchImpl !== "function") throw new Error("workspace_connection_fetch_required"); const { baseUrl, protocol, structuredOutputs } = connection; // No registry entry and no scope headers: the key belongs to the workspace's // connection, and every request goes through the caller's fetchImpl. if (protocol === "runs-api") return createHermesRunsClient({ baseUrl, apiKey, fetchImpl, scopeHeaders: {}, }); if (protocol !== "openai-chat" && protocol !== "openai-responses") throw new Error("workspace_connection_protocol_rejected"); const { provider, model, reasoningEffort } = modelSelection; return createOpenAiRunsClient({ connection: { baseUrl, protocol, structuredOutputs, models: [{ provider, model, reasoningEfforts: [reasoningEffort] }], }, apiKey, fetchImpl, modelSelection, responseSchema, }); } // One ready-token probe for the machine verify and the server's connection // check. It never reads a key: the caller hands it a built client. /** @param {any} client @param {any} selection @param {any} [options] */ export async function probeRunsClient( client, selection, { signal, submitVerification = false } = {} ) { let run; try { // A fresh token per check. Claude Opus 5.5 answers one fixed string // ("SELLABLE_CONNECTION_OK") with nothing, so Claude Code fell back to // another model and every Claude Code verification failed. const token = `ready-${randomUUID().replaceAll("-", "").slice(0, 8)}`; const compiled = { input: `Connection verification. Reply with ${token}.`, instructions: `This is a connection check. Do not use tools or take actions. Return exactly ${token} and nothing else.`, conversationHistory: [], correlation: `connection-check-${randomUUID()}`, ...(selection ? { modelSelection: selection } : {}), }; // Default Hermes gets the tool-free verification prefix. A Runs service // owns its own tool policy; the Claude and OpenAI adapters are tool-free. run = await ( submitVerification ? client.submitVerification : client.submit )(compiled, { signal }); await client.streamEvents(run.runId, { signal, onEvent: async () => {} }); const status = await client.status(run.runId, { signal }); const served = status.servedRuntime; if ( status.status !== "completed" || typeof status.output !== "string" || status.output.trim() !== token || !served ) return { state: "UNAVAILABLE" }; if ( selection && (served.provider !== selection.provider || (served.model !== selection.model && !served.model.startsWith(`${selection.model}-`))) ) return { state: "UNAVAILABLE" }; return { state: "READY", servedRuntime: served }; } catch { if (run) await client .stop(run.runId, { signal: AbortSignal.timeout(3_000) }) .catch(() => {}); return { state: "UNAVAILABLE" }; } }