UNPKG

@sellable/install

Version:

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

738 lines (711 loc) • 24.8 kB
import { createHash, randomUUID } from "node:crypto"; import { chmodSync, existsSync, lstatSync, mkdirSync, readFileSync, renameSync, writeFileSync, } from "node:fs"; import { dirname } from "node:path"; export const MODEL_AUTH_RUNTIME_SCHEMA = "sellable-agent-model-auth-runtime/v3"; export const MODEL_AUTH_LEGACY_RUNTIME_SCHEMA = "sellable-agent-model-auth-runtime/v2"; export const MODEL_AUTH_READBACK_SCHEMA = "sellable-agent-model-auth-readback/v3"; export const MODEL_AUTH_LEGACY_READBACK_SCHEMA = "sellable-agent-model-auth-readback/v2"; export const MODEL_AUTH_MINIMUM_VALIDITY_MS = 20 * 60 * 1000; export const MODEL_AUTH_PROVIDER = "openai-codex"; export const MODEL_AUTH_DEFAULT_MODEL = "gpt-6.1-sol"; export const MODEL_AUTH_REASONING_EFFORT = "high"; const LEGACY_MARKER_VERSION = 2; const MARKER_VERSION = 3; const SHA256 = /^[a-f0-9]{64}$/; const FINGERPRINT = /^[a-f0-9]{16,64}$/; const SAFE_ID = /^[A-Za-z0-9_.:-]{1,256}$/; const MODEL_ID = /^~?[A-Za-z0-9][A-Za-z0-9._-]*\/[A-Za-z0-9][A-Za-z0-9._:-]*$/; const FORBIDDEN_REFRESH_KEY = /^(?:refresh[_-]?token|id[_-]?token)$/i; const LEGACY_SLOT_IDENTITIES = Object.freeze([ Object.freeze({ slotId: "dittto-primary", priority: 1 }), Object.freeze({ slotId: "kaigo-secondary", priority: 2 }), ]); const CURRENT_SLOT_IDENTITIES = Object.freeze([ Object.freeze({ slotId: "sellable-primary", priority: 1 }), Object.freeze({ slotId: "dittto-secondary", priority: 2 }), Object.freeze({ slotId: "kaigo-tertiary", priority: 3 }), ]); const CURRENT_OPENROUTER_MODELS = Object.freeze([ Object.freeze({ id: "openai/gpt-5.6-luna", reasoningEffort: "max" }), Object.freeze({ id: "deepseek/deepseek-v4-flash-0731" }), Object.freeze({ id: "z-ai/glm-5.2" }), Object.freeze({ id: "moonshotai/kimi-k3" }), ]); const RUNTIME_KEYS = Object.freeze([ "schemaVersion", "activationId", "bundleGeneration", "policyHash", "credentialPoolStrategy", "runtime", "codex", "openrouter", ]); const RUNTIME_BEHAVIOR_KEYS = Object.freeze([ "apiMaxRetries", "requestTimeoutSeconds", ]); const SLOT_KEYS = Object.freeze([ "slotId", "priority", "accessToken", "accountId", "expiresAt", "grantFingerprint", ]); const OPENROUTER_KEYS = Object.freeze(["apiKey", "keyFingerprint", "models"]); const MODEL_KEYS = Object.freeze(["id"]); const MODEL_REASONING_KEYS = Object.freeze(["id", "reasoningEffort"]); const MARKER_KEYS = Object.freeze([ "version", "activationId", "bundleGeneration", "policyHash", "sourceFingerprint", "slots", "openrouterKeyFingerprint", "models", ]); export class ModelAuthRuntimeError extends Error { constructor(code) { super(code); this.name = "ModelAuthRuntimeError"; this.code = code; } } function fail(code) { throw new ModelAuthRuntimeError(code); } function isRecord(value) { return Boolean(value) && typeof value === "object" && !Array.isArray(value); } function exactKeys(value, keys) { return ( isRecord(value) && Object.keys(value).sort().join("\0") === [...keys].sort().join("\0") ); } function bounded(value, minimum, maximum) { return ( typeof value === "string" && value.length >= minimum && value.length <= maximum && !value.includes("\0") ); } function stableJson(value) { if (Array.isArray(value)) return `[${value.map(stableJson).join(",")}]`; if (isRecord(value)) { return `{${Object.keys(value) .sort() .map((key) => `${JSON.stringify(key)}:${stableJson(value[key])}`) .join(",")}}`; } return JSON.stringify(value); } const sha256 = (value) => createHash("sha256").update(value).digest("hex"); function containsRuntimeRefreshOwnership(value) { if (Array.isArray(value)) return value.some(containsRuntimeRefreshOwnership); if (!isRecord(value)) return false; return Object.entries(value).some( ([key, entry]) => FORBIDDEN_REFRESH_KEY.test(key) || containsRuntimeRefreshOwnership(entry) ); } function jwtExpiry(accessToken) { try { const parts = accessToken.split("."); if (parts.length !== 3) return null; const payload = JSON.parse(Buffer.from(parts[1], "base64url").toString()); return Number.isFinite(payload?.exp) ? Number(payload.exp) * 1000 : null; } catch { return null; } } function assertRegularFileOrMissing(path) { if (!existsSync(path)) return; const entry = lstatSync(path); if (entry.isSymbolicLink() || !entry.isFile()) fail("model_auth_store_rejected"); } function atomicPrivateFile(path, bytes) { mkdirSync(dirname(path), { recursive: true, mode: 0o700 }); const temporary = `${path}.${process.pid}.${randomUUID()}.tmp`; writeFileSync(temporary, bytes, { flag: "wx", mode: 0o600 }); chmodSync(temporary, 0o600); renameSync(temporary, path); chmodSync(path, 0o600); } function projectionIdentity(value) { return { activationId: value.activationId, bundleGeneration: value.bundleGeneration, policyHash: value.policyHash, slots: value.codex.map((slot) => ({ slotId: slot.slotId, priority: slot.priority, grantFingerprint: slot.grantFingerprint, expiresAt: slot.expiresAt, })), openrouterKeyFingerprint: value.openrouter.keyFingerprint, models: value.openrouter.models.map((model) => typeof model === "string" ? model : { ...model } ), }; } function markerFor(credential) { return { version: credential.value.schemaVersion === MODEL_AUTH_RUNTIME_SCHEMA ? MARKER_VERSION : LEGACY_MARKER_VERSION, ...credential.identity, sourceFingerprint: credential.sourceFingerprint, }; } function parseMarker(path) { assertRegularFileOrMissing(path); if (!existsSync(path)) return null; try { const marker = JSON.parse(readFileSync(path, "utf8")); if ( !exactKeys(marker, MARKER_KEYS) || ![LEGACY_MARKER_VERSION, MARKER_VERSION].includes(marker.version) || !SAFE_ID.test(marker.activationId ?? "") || !Number.isSafeInteger(marker.bundleGeneration) || marker.bundleGeneration < 1 || !SHA256.test(marker.policyHash ?? "") || !FINGERPRINT.test(marker.sourceFingerprint ?? "") || !Array.isArray(marker.slots) || marker.slots.length !== (marker.version === MARKER_VERSION ? CURRENT_SLOT_IDENTITIES.length : 2) || !FINGERPRINT.test(marker.openrouterKeyFingerprint ?? "") || !Array.isArray(marker.models) || marker.models.length < 1 || (marker.version === MARKER_VERSION && (marker.models.length !== CURRENT_OPENROUTER_MODELS.length || marker.models.some((model, index) => { const expected = CURRENT_OPENROUTER_MODELS[index]; // Preserve the activated effort during a rolling max -> high update. return index === 0 ? !exactKeys(model, MODEL_REASONING_KEYS) || (model.id !== expected.id && !(index === 0 && model.id === "openai/gpt-6-luna")) || !["max", "high"].includes(model.reasoningEffort) : stableJson(model) !== stableJson(expected); }))) ) { return null; } return marker; } catch { return null; } } function accessOnlyStoreFor(value) { return { version: 1, credential_pool: { "openai-codex": value.codex.map((slot) => ({ id: slot.slotId, label: slot.slotId, auth_type: "oauth", priority: slot.priority, source: "manual:device_code", access_token: slot.accessToken, account_id: slot.accountId, expires_at: slot.expiresAt, request_count: 0, secret_fingerprint: slot.grantFingerprint, })), openrouter: [ { id: "sellable-openrouter", label: "sellable-openrouter", auth_type: "api_key", priority: 1, source: "manual", access_token: value.openrouter.apiKey, base_url: "https://openrouter.ai/api/v1", request_count: 0, secret_fingerprint: value.openrouter.keyFingerprint, }, ], }, active_provider: MODEL_AUTH_PROVIDER, }; } function essentialStoreMatches(path, credential) { if (!existsSync(path)) return false; try { const stored = JSON.parse(readFileSync(path, "utf8")); if (containsRuntimeRefreshOwnership(stored)) return false; const codex = stored?.credential_pool?.[MODEL_AUTH_PROVIDER]; const openrouter = stored?.credential_pool?.openrouter; if ( stored?.version !== 1 || stored?.active_provider !== MODEL_AUTH_PROVIDER || !Array.isArray(codex) || codex.length !== credential.value.codex.length || !Array.isArray(openrouter) || openrouter.length !== 1 ) { return false; } return ( codex.every((entry, index) => { const expected = credential.value.codex[index]; return ( entry?.id === expected.slotId && entry?.priority === expected.priority && entry?.source === "manual:device_code" && entry?.auth_type === "oauth" && entry?.access_token === expected.accessToken && entry?.account_id === expected.accountId && entry?.expires_at === expected.expiresAt && entry?.secret_fingerprint === expected.grantFingerprint ); }) && openrouter[0]?.id === "sellable-openrouter" && openrouter[0]?.auth_type === "api_key" && openrouter[0]?.access_token === credential.value.openrouter.apiKey && openrouter[0]?.secret_fingerprint === credential.value.openrouter.keyFingerprint ); } catch { return false; } } export function parseModelAuthRuntimeCredential(secret, options = {}) { if ( typeof secret !== "string" || !secret || Buffer.byteLength(secret, "utf8") > 64 * 1024 ) { fail("model_auth_projection_incomplete"); } let value; try { value = JSON.parse(secret); } catch { fail("model_auth_projection_incomplete"); } if (containsRuntimeRefreshOwnership(value)) { fail("model_auth_runtime_refresh_ownership_rejected"); } const isV2 = value?.schemaVersion === MODEL_AUTH_LEGACY_RUNTIME_SCHEMA; const isV3 = value?.schemaVersion === MODEL_AUTH_RUNTIME_SCHEMA; const expectedSlots = isV3 ? CURRENT_SLOT_IDENTITIES : LEGACY_SLOT_IDENTITIES; if ( !exactKeys(value, RUNTIME_KEYS) || (!isV2 && !isV3) || value.credentialPoolStrategy !== "fill_first" || !SAFE_ID.test(value.activationId ?? "") || !Number.isSafeInteger(value.bundleGeneration) || value.bundleGeneration < 1 || !SHA256.test(value.policyHash ?? "") || !Array.isArray(value.codex) || value.codex.length !== expectedSlots.length || !exactKeys(value.runtime, RUNTIME_BEHAVIOR_KEYS) || !exactKeys(value.openrouter, OPENROUTER_KEYS) ) { fail("model_auth_projection_incomplete"); } if ( !Number.isInteger(value.runtime.apiMaxRetries) || value.runtime.apiMaxRetries < 0 || value.runtime.apiMaxRetries > 2 || !Number.isInteger(value.runtime.requestTimeoutSeconds) || value.runtime.requestTimeoutSeconds < 1 || value.runtime.requestTimeoutSeconds > 60 ) { fail("model_auth_projection_incomplete"); } const nowValue = options.now ?? new Date(); const nowMs = nowValue instanceof Date ? nowValue.getTime() : Number(nowValue); if (!Number.isFinite(nowMs)) fail("model_auth_projection_incomplete"); for (const [index, slot] of value.codex.entries()) { const expected = expectedSlots[index]; if ( !exactKeys(slot, SLOT_KEYS) || slot.slotId !== expected.slotId || slot.priority !== expected.priority || !bounded(slot.accessToken, 32, 32 * 1024) || !bounded(slot.accountId, 1, 512) || !bounded(slot.expiresAt, 10, 128) || !FINGERPRINT.test(slot.grantFingerprint ?? "") ) { fail("model_auth_projection_incomplete"); } const declaredExpiry = Date.parse(slot.expiresAt); const embeddedExpiry = jwtExpiry(slot.accessToken); if ( !Number.isFinite(declaredExpiry) || embeddedExpiry === null || // A provider expires_in estimate taken before its response can expire // earlier than the JWT. Preserve that conservative lifetime; reject only // estimates that overstate the token's validity beyond rounding tolerance. declaredExpiry - embeddedExpiry > 1_000 || Math.min(declaredExpiry, embeddedExpiry) - nowMs < MODEL_AUTH_MINIMUM_VALIDITY_MS ) { fail("model_auth_projection_validity_insufficient"); } } if ( new Set(value.codex.map((slot) => slot.accountId)).size !== expectedSlots.length || new Set(value.codex.map((slot) => slot.accessToken)).size !== expectedSlots.length || new Set(value.codex.map((slot) => slot.grantFingerprint)).size !== expectedSlots.length ) { fail("model_auth_projection_incomplete"); } if ( !bounded(value.openrouter.apiKey, 20, 4096) || !FINGERPRINT.test(value.openrouter.keyFingerprint ?? "") || !Array.isArray(value.openrouter.models) || value.openrouter.models.length < 1 || value.openrouter.models.length > 8 || (isV2 ? value.openrouter.models.some( (model) => typeof model !== "string" || !MODEL_ID.test(model) ) || new Set(value.openrouter.models).size !== value.openrouter.models.length : value.openrouter.models.length !== CURRENT_OPENROUTER_MODELS.length) ) { fail("model_auth_projection_incomplete"); } if (isV3) { for (const [index, model] of value.openrouter.models.entries()) { const expected = CURRENT_OPENROUTER_MODELS[index]; const withReasoning = expected.reasoningEffort !== undefined; if ( !exactKeys(model, withReasoning ? MODEL_REASONING_KEYS : MODEL_KEYS) || (model.id !== expected.id && !(index === 0 && model.id === "openai/gpt-6-luna")) || (withReasoning && !["max", "high"].includes(model.reasoningEffort)) ) { fail("model_auth_projection_incomplete"); } } } const identity = projectionIdentity(value); const store = accessOnlyStoreFor(value); const sourceFingerprint = sha256(stableJson(identity)).slice(0, 16); return Object.freeze({ provider: MODEL_AUTH_PROVIDER, model: MODEL_AUTH_DEFAULT_MODEL, reasoningEffort: MODEL_AUTH_REASONING_EFFORT, activationId: value.activationId, bundleGeneration: value.bundleGeneration, policyHash: value.policyHash, sourceFingerprint, identity: Object.freeze(identity), value: Object.freeze(value), authJson: JSON.stringify(value), hermesRootAuthJson: `${JSON.stringify(store, null, 2)}\n`, testAccessToken: value.codex[0].accessToken, }); } export function modelAuthConfigOverlay(credential) { if (!credential?.value || !SHA256.test(credential.policyHash ?? "")) { fail("model_auth_projection_incomplete"); } return Object.freeze({ // Phase 169 live Slack UAT showed that Hermes's default three inference // attempts can keep a slow-but-open provider leg alive for five minutes. // The ordered provider/model chain is the retry strategy for these // runtimes, so bound each leg once and advance instead of retrying it. agent: Object.freeze({ reasoning_effort: MODEL_AUTH_REASONING_EFFORT, api_max_retries: credential.value.runtime.apiMaxRetries, ...(credential.value.schemaVersion === MODEL_AUTH_RUNTIME_SCHEMA ? { reasoning_overrides: Object.freeze({ [credential.value.openrouter.models[0].id]: credential.value.openrouter.models[0].reasoningEffort, }), } : {}), }), providers: Object.freeze({ "openai-codex": Object.freeze({ request_timeout_seconds: credential.value.runtime.requestTimeoutSeconds, }), openrouter: Object.freeze({ request_timeout_seconds: credential.value.runtime.requestTimeoutSeconds, }), }), model: Object.freeze({ default: MODEL_AUTH_DEFAULT_MODEL, provider: MODEL_AUTH_PROVIDER, reasoning_effort: MODEL_AUTH_REASONING_EFFORT, bundle_generation: credential.bundleGeneration, policy_hash: credential.policyHash, }), credential_pool_strategies: Object.freeze({ "openai-codex": "fill_first", openrouter: "fill_first", }), fallback_providers: Object.freeze( credential.value.openrouter.models.map((entry) => Object.freeze({ provider: "openrouter", model: typeof entry === "string" ? entry : entry.id, }) ) ), }); } export function reconcileHermesModelAuth({ authStorePath, markerPath, credential, lifecycle, }) { if ( typeof authStorePath !== "string" || !authStorePath || typeof markerPath !== "string" || !markerPath || !credential?.value || !["ON", "OFF"].includes(lifecycle) ) { fail("model_auth_store_rejected"); } if (lifecycle === "OFF") { return Object.freeze({ status: "desired_converged_materialization_pending_start", materialized: false, activationId: credential.activationId, bundleGeneration: credential.bundleGeneration, policyHash: credential.policyHash, sourceFingerprint: credential.sourceFingerprint, reseeded: false, }); } assertRegularFileOrMissing(authStorePath); assertRegularFileOrMissing(markerPath); const expectedMarker = markerFor(credential); const priorMarker = parseMarker(markerPath); const markerMatches = stableJson(priorMarker) === stableJson(expectedMarker); const storeMatches = essentialStoreMatches(authStorePath, credential); const reseeded = !markerMatches || !storeMatches; if (reseeded) { atomicPrivateFile(authStorePath, credential.hermesRootAuthJson); atomicPrivateFile( markerPath, `${JSON.stringify(expectedMarker, null, 2)}\n` ); } else { chmodSync(authStorePath, 0o600); chmodSync(markerPath, 0o600); } return Object.freeze({ status: "materialized_current", materialized: true, activationId: credential.activationId, bundleGeneration: credential.bundleGeneration, policyHash: credential.policyHash, sourceFingerprint: credential.sourceFingerprint, reseeded, }); } function safeRuntimeReason(value) { return typeof value === "string" && /^[a-z0-9_.:-]{1,128}$/.test(value) ? value : null; } function resetAt(value) { if (typeof value === "string" && Number.isFinite(Date.parse(value))) { return new Date(value).toISOString(); } if (typeof value === "number" && Number.isFinite(value) && value > 0) { const milliseconds = value > 1_000_000_000_000 ? value : value * 1000; return new Date(milliseconds).toISOString(); } return null; } export function readModelAuthMaterialization({ authStorePath, markerPath }) { assertRegularFileOrMissing(authStorePath); const marker = parseMarker(markerPath); const readbackSchema = marker?.version === MARKER_VERSION ? MODEL_AUTH_READBACK_SCHEMA : MODEL_AUTH_LEGACY_READBACK_SCHEMA; if (!marker || !existsSync(authStorePath)) { return Object.freeze({ schemaVersion: readbackSchema, activationId: marker?.activationId ?? null, bundleGeneration: marker?.bundleGeneration ?? null, policyHash: marker?.policyHash ?? null, provider: MODEL_AUTH_PROVIDER, model: MODEL_AUTH_DEFAULT_MODEL, fallbackDepth: marker?.models?.length ?? 0, desiredStatus: marker ? "converged" : "unknown", materializedStatus: "pending_start", slots: Object.freeze([]), openrouter: null, activeProvider: null, activeFallbackModelId: null, }); } let auth; try { auth = JSON.parse(readFileSync(authStorePath, "utf8")); } catch { fail("model_auth_store_rejected"); } if (containsRuntimeRefreshOwnership(auth)) { fail("model_auth_runtime_refresh_ownership_rejected"); } const codex = auth?.credential_pool?.[MODEL_AUTH_PROVIDER]; const openrouter = auth?.credential_pool?.openrouter; if ( !Array.isArray(codex) || ![LEGACY_SLOT_IDENTITIES.length, CURRENT_SLOT_IDENTITIES.length].includes( codex.length ) || codex.length !== marker.slots.length || !Array.isArray(openrouter) ) { fail("model_auth_store_rejected"); } const slots = codex.map((entry, index) => { const expected = marker.version === MARKER_VERSION ? CURRENT_SLOT_IDENTITIES[index] : LEGACY_SLOT_IDENTITIES[index]; if ( entry?.id !== expected.slotId || entry?.priority !== expected.priority || !FINGERPRINT.test(entry?.secret_fingerprint ?? "") || !Number.isFinite(Date.parse(entry?.expires_at ?? "")) ) { fail("model_auth_store_rejected"); } const state = ["ok", "exhausted", "dead"].includes(entry.last_status) ? entry.last_status : "ok"; return Object.freeze({ slotId: entry.id, ...(marker.version === MARKER_VERSION ? { priority: entry.priority } : {}), grantFingerprint: entry.secret_fingerprint, expiresAt: new Date(entry.expires_at).toISOString(), state, lastErrorReason: safeRuntimeReason(entry.last_error_reason), lastErrorResetAt: resetAt(entry.last_error_reset_at), }); }); return Object.freeze({ schemaVersion: readbackSchema, activationId: marker.activationId, bundleGeneration: marker.bundleGeneration, policyHash: marker.policyHash, provider: MODEL_AUTH_PROVIDER, model: MODEL_AUTH_DEFAULT_MODEL, fallbackDepth: marker.models.length, desiredStatus: "converged", materializedStatus: "current", slots: Object.freeze(slots), openrouter: Object.freeze({ keyFingerprint: marker.openrouterKeyFingerprint, state: ["ok", "exhausted", "dead"].includes(openrouter[0]?.last_status) ? openrouter[0].last_status : "ok", }), activeProvider: auth.active_provider === "openrouter" ? "openrouter" : MODEL_AUTH_PROVIDER, activeFallbackModelId: auth.active_provider === "openrouter" && typeof auth.active_model === "string" && marker.models.some((model) => (typeof model === "string" ? model : model.id) === auth.active_model ) ? auth.active_model : null, ...(marker.version === MARKER_VERSION ? { models: Object.freeze(marker.models.map((model) => Object.freeze({ ...model }))), reasoningOverrides: Object.freeze({ [marker.models[0].id]: marker.models[0].reasoningEffort, }), } : {}), }); } /** * @param {unknown} readback * @param {{activationId: string, bundleGeneration: number, policyHash: string} | null} [expected] */ export function evaluateModelAuthReadback(readback, expected = null) { const reject = (rejection) => ({ accepted: false, rejection }); if (!isRecord(readback)) return reject("readback_unparsable"); if ( ![MODEL_AUTH_LEGACY_READBACK_SCHEMA, MODEL_AUTH_READBACK_SCHEMA].includes( readback.schemaVersion ) ) { return reject("readback_schema_mismatch"); } if (readback.provider !== MODEL_AUTH_PROVIDER) return reject("provider_mismatch"); if (readback.materializedStatus !== "current") { return reject("materialization_pending_start"); } if (expected && readback.activationId !== expected.activationId) { return reject("activation_id_mismatch"); } if (expected && readback.bundleGeneration !== expected.bundleGeneration) { return reject("bundle_generation_mismatch"); } if (expected && readback.policyHash !== expected.policyHash) { return reject("policy_hash_mismatch"); } const expectedSlotCount = readback.schemaVersion === MODEL_AUTH_READBACK_SCHEMA ? 3 : 2; if ( !Array.isArray(readback.slots) || readback.slots.length !== expectedSlotCount ) { return reject("slot_pool_incomplete"); } return { accepted: true, rejection: null }; } /** Secret-free exact-image observation used by the bounded canary. */ export function modelAuthSelectionObservation(readback) { const verdict = evaluateModelAuthReadback(readback); if (!verdict.accepted) fail("model_auth_selection_observation_rejected"); const provider = readback.activeProvider === "openrouter" ? "openrouter" : MODEL_AUTH_PROVIDER; const modelId = provider === "openrouter" ? readback.activeFallbackModelId : MODEL_AUTH_DEFAULT_MODEL; if (typeof modelId !== "string" || !modelId) { fail("model_auth_selection_observation_rejected"); } return Object.freeze({ provider, modelId, reasoningEffort: readback.reasoningOverrides?.[modelId] ?? MODEL_AUTH_REASONING_EFFORT, }); }