UNPKG

@sellable/install

Version:

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

248 lines (244 loc) • 8.09 kB
import { readFile } from "node:fs/promises"; import { pathToFileURL } from "node:url"; import { compileEndpointModelSelection, endpointModelSelectionChain, endpointModelSelectionSource, } from "./endpoint-model-selection.mjs"; import { createHermesRunsClient } from "./hermes-runs-client.mjs"; import { createConfiguredRunsClient, RUNS_API_CONNECTIONS_ENV, runsApiConnectionId, runsApiConnectionServesEndpoint, runsApiConnectionsForRuntime, } from "./runs-api-connections.mjs"; import { probeRunsClient } from "./workspace-connection-client.mjs"; // Fixed read-only machine command. Callers supply identities, never a URL, // credential path, shell command, or prompt. The installed registry authorizes // the destination again on the machine before a credential is read. export async function checkRunsApiConnection( request, { env = process.env, fetchImpl = fetch, readToken = (path) => readFile(path, "utf8"), } = {} ) { const checkedAt = new Date().toISOString(); const result = (state) => ({ connectionId: request.connectionId, state, checkedAt, }); if (request.runtimeId !== env.SELLABLE_RUNTIME_ID) return result("NOT_CONFIGURED"); const connection = runsApiConnectionsForRuntime( env[RUNS_API_CONNECTIONS_ENV], request ).find( (candidate) => runsApiConnectionId(candidate) === request.connectionId && runsApiConnectionServesEndpoint(candidate, request.endpointId) ); if (!connection) return result("NOT_CONFIGURED"); try { const token = (await readToken(connection.apiKeyFile)).trim(); if (!token || token.length > 8192 || /[\r\n]/.test(token)) return result("UNAVAILABLE"); const response = await fetchImpl( `${connection.baseUrl}/${connection.protocol?.startsWith("openai-") ? "models" : "health"}`, { headers: { authorization: `Bearer ${token}`, ...(connection.protocol?.startsWith("openai-") ? {} : { "x-sellable-workspace-id": connection.workspaceId, "x-sellable-agent-id": connection.agentId, }), }, redirect: "error", signal: AbortSignal.timeout(12_000), } ); if (response.status === 401 || response.status === 403) return result("ACCESS_DENIED"); if (!response.ok) return result("UNAVAILABLE"); const reader = response.body?.getReader(); if (!reader) return result("UNAVAILABLE"); let text = ""; let size = 0; try { const decoder = new TextDecoder(); while (true) { const chunk = await reader.read(); if (chunk.done) break; size += chunk.value.byteLength; if (size > 16_384) { await reader.cancel(); return result("UNAVAILABLE"); } text += decoder.decode(chunk.value, { stream: true }); } text += decoder.decode(); } finally { reader.releaseLock(); } const health = JSON.parse(text); if (connection.localRoot) { if ( health.provider !== "claude-code" || health.scope?.workspaceId !== connection.workspaceId || health.scope?.agentId !== connection.agentId ) return result("ACCESS_DENIED"); if (health.authentication === "SIGN_IN_REQUIRED") return result("SIGN_IN_REQUIRED"); return result( health.authentication === "READY" && health.ok === true ? "READY" : "UNAVAILABLE" ); } return result( ( connection.protocol?.startsWith("openai-") ? Array.isArray(health.data) : health.ok === true ) ? "REACHABLE" : "UNAVAILABLE" ); } catch { return result("UNAVAILABLE"); } } export async function verifyRunsApiSelection( request, { env = process.env, makeDefaultClient = createHermesRunsClient, makeConfiguredClient = createConfiguredRunsClient, health = checkRunsApiConnection, } = {} ) { const result = (state, servedRuntime) => ({ connectionId: request.connectionId, state, checkedAt: new Date().toISOString(), ...(servedRuntime ? { servedRuntime } : {}), }); if ( request.runtimeId !== env.SELLABLE_RUNTIME_ID || !/^[A-Za-z0-9_-]{1,128}$/.test(request.profileId ?? "") ) return result("NOT_CONFIGURED"); const compiled = compileEndpointModelSelection(request.modelSelection); if (compiled === "invalid") return result("NOT_CONFIGURED"); // A fallback chain is verified one step per call. The control plane sends // the primary step's check the whole chain, so a machine whose compiler // cannot run a chain refuses it here and no chain is saved for it. const selection = endpointModelSelectionChain(compiled)[0] ?? null; // A workspace connection's key is fetched per attempt by the run, so the // machine holds none to probe with. This is a shape check only; the server // performs the connectivity probe (228-05). if (selection?.modelConnectionId) return result("REACHABLE"); let verificationWorkKind = "STANDARD"; if (selection?.runsApiBaseUrl) { const connection = runsApiConnectionsForRuntime( env[RUNS_API_CONNECTIONS_ENV], request ).find( (item) => runsApiConnectionId(item) === request.connectionId && item.baseUrl === selection.runsApiBaseUrl ); if ( !connection || !runsApiConnectionServesEndpoint(connection, request.endpointId) ) return result("NOT_CONFIGURED"); verificationWorkKind = connection.workKinds.includes("STANDARD") ? "STANDARD" : connection.workKinds[0]; if (connection.localRoot) { const readiness = await health(request, { env }); if (readiness.state !== "READY") return readiness; } } else if (request.connectionId !== endpointModelSelectionSource(selection)) return result("NOT_CONFIGURED"); const apiKeyFile = env.SELLABLE_AGENT_SERVER_AUTHENTICATED_KIND === "ADMIN" ? "/run/sellable-agent/admin-runtime/api-server-key" : `/run/sellable-agent/${request.profileId}/api-server-key`; let client; try { client = selection?.runsApiBaseUrl ? await makeConfiguredClient({ apiKeyFile, env, claim: { runtime: { runtimeId: request.runtimeId }, attempt: { workspaceId: request.workspaceId, agentId: request.agentId, endpointId: request.endpointId, }, run: { modelSelection: selection, productExecution: { workKind: verificationWorkKind }, }, }, }) : makeDefaultClient({ apiKeyFile }); } catch { return result("UNAVAILABLE"); } const probe = await probeRunsClient(client, selection, { signal: AbortSignal.timeout(70_000), submitVerification: !selection?.runsApiBaseUrl, }); return result(probe.state, probe.servedRuntime); } if ( process.argv[1] && import.meta.url === pathToFileURL(process.argv[1]).href ) { try { const raw = process.argv[2]; if (!raw || raw.length > 4096) throw new Error(); const request = JSON.parse(Buffer.from(raw, "base64url").toString("utf8")); const verify = request.action === "VERIFY"; if ( Object.keys(request) .filter( (key) => !["action", "profileId", "modelSelection"].includes(key) ) .sort() .join(",") !== "agentId,connectionId,endpointId,runtimeId,workspaceId" || [ request.agentId, request.connectionId, request.endpointId, request.runtimeId, request.workspaceId, ].some( (value) => typeof value !== "string" || !/^[A-Za-z0-9_.:-]{1,128}$/.test(value) ) || (request.action !== undefined && !verify) ) throw new Error(); process.stdout.write( JSON.stringify( await (verify ? verifyRunsApiSelection(request) : checkRunsApiConnection(request)) ) ); } catch { process.exitCode = 1; } }