@sellable/install
Version:
One-command installer for Sellable MCP in Claude Code, Codex, and Hermes
248 lines (244 loc) • 8.09 kB
JavaScript
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;
}
}