UNPKG

@sellable/install

Version:

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

123 lines (119 loc) • 4.87 kB
import { createHash } from "node:crypto"; import { compileEndpointModelSelection } from "./endpoint-model-selection.mjs"; import { createHermesRunsClient } from "./hermes-runs-client.mjs"; import { createPinnedFetch } from "./public-endpoint-guard.mjs"; import { createWorkspaceConnectionRunsClient } from "./workspace-connection-client.mjs"; const record = (value) => value && typeof value === "object" && !Array.isArray(value); const stable = (value) => Array.isArray(value) ? `[${value.map(stable).join(",")}]` : record(value) ? `{${Object.keys(value) .sort() .map((key) => `${JSON.stringify(key)}:${stable(value[key])}`) .join(",")}}` : JSON.stringify(value); // Exactly the members the endpoint-connection-key verb returns. The key follows // the saved key's rule (16-1024 characters), never the key-file rule; the // remote tool's values are checked where the create body is built. const connectionRelease = (value) => record(value) && Object.keys(value).sort().join(",") === "apiKey,keyGeneration,protocol,structuredOutputs,tools" && typeof value.apiKey === "string" && value.apiKey.length >= 16 && value.apiKey.length <= 1024 && !/[\s\x00-\x1f\x7f]/.test(value.apiKey) && Number.isSafeInteger(value.keyGeneration) && ["runs-api", "openai-chat", "openai-responses"].includes(value.protocol) && typeof value.structuredOutputs === "boolean" && (value.tools === null || (value.protocol === "runs-api" && record(value.tools) && Object.keys(value.tools).sort().join(",") === "allowedTools,authorization,expiresAt,serverUrl" && typeof value.tools.serverUrl === "string" && typeof value.tools.authorization === "string" && Array.isArray(value.tools.allowedTools) && typeof value.tools.expiresAt === "string")); // Adds the opener output contract (and a granted remote tool) to every submit. function customRunsClient(client, claim, remoteTools) { return Object.freeze({ ...client, submit(compiled, options) { const request = remoteTools ? { ...compiled, remoteTools } : compiled; // Public semantic endpoints have a published schema but no product tuple. // Preserve that existing contract when calling a custom Runs service. const schema = claim.revision?.outputSchema; if (compiled.outputContract || !schema?.properties?.proposal) return client.submit(request, options); return client.submit( { ...request, outputContract: { schemaVersion: "sellable-opener-output-contract/v1", schema, nativeProposalSchemaDigest: createHash("sha256") .update(stable(schema.properties.proposal)) .digest("hex"), }, }, options ); }, }); } /** @param {any} options */ export async function createConfiguredRunsClient({ apiKeyFile, claim, clientFactory = createHermesRunsClient, fetchConnectionKey, chainStep = 0, createFetch = createPinnedFetch, }) { const selection = claim?.run?.modelSelection; // A workspace connection's key is fetched for this attempt and lives only in // these closures, and every request goes through the address guard. URL and // model come from the signed claim, never from the key response. if (selection?.modelConnectionId) { const step = compileEndpointModelSelection(selection); if (!step || step === "invalid") throw new Error("runs_api_model_rejected"); let release = null; try { release = await fetchConnectionKey(step.modelConnectionId, chainStep); } catch { // Every refusal is one fixed code: no server reason, no response field. } if (!connectionRelease(release)) throw new Error("runs_api_connection_unavailable"); const { apiKey, protocol, structuredOutputs, tools } = release; const client = createWorkspaceConnectionRunsClient({ connection: { baseUrl: step.runsApiBaseUrl, protocol, structuredOutputs }, apiKey, // An OpenAI-protocol provider sends its headers only when it is done, so // the adapter's 750 s run signal bounds that call, not the guard. fetchImpl: protocol === "runs-api" ? createFetch() : createFetch({ deadlineMs: 760_000 }), modelSelection: step, responseSchema: claim.revision?.outputSchema, }); return customRunsClient( client, claim, tools && { serverUrl: tools.serverUrl, authorization: tools.authorization, allowedTools: tools.allowedTools, } ); } // A custom URL is served only through a workspace connection: no other // client, credential or socket is opened for it. if (selection?.runsApiBaseUrl) throw new Error("runs_api_connection_unavailable"); return clientFactory({ apiKeyFile }); }