UNPKG

run402-mcp

Version:

MCP server for Run402 — full-stack backend infrastructure for AI agents: Postgres, auth, storage, serverless functions and atomic deploys. Paid with x402/MPP. Includes $0.03 image generation.

103 lines • 4.33 kB
/** CI-session credential helpers for OIDC-backed deploy flows. */ import { LocalError } from "./errors.js"; import { buildClient } from "./kernel.js"; import { Ci } from "./namespaces/ci.js"; import { CI_AUDIENCE, } from "./namespaces/ci.types.js"; export const CI_SESSION_CREDENTIALS = Symbol.for("@run402/sdk/ci-session-credentials"); export function isCiSessionCredentials(credentials) { return Boolean(credentials[CI_SESSION_CREDENTIALS]); } export function createCiSessionCredentials(opts) { if (!opts?.projectId) { throw new LocalError("createCiSessionCredentials requires projectId", "creating CI session credentials"); } if (!opts.accessToken && !opts.getAccessToken) { throw new LocalError("createCiSessionCredentials requires accessToken or getAccessToken", "creating CI session credentials"); } const provider = { async getAuth() { const token = opts.getAccessToken ? await opts.getAccessToken() : opts.accessToken; if (!token) { throw new LocalError("CI session credentials did not return an access token", "authenticating with CI session"); } return { Authorization: `Bearer ${token}` }; }, async getProject(id) { if (id !== opts.projectId) return null; return { anon_key: "", service_key: "" }; }, async getActiveProject() { return opts.projectId; }, }; Object.defineProperty(provider, CI_SESSION_CREDENTIALS, { value: true, enumerable: false, }); return provider; } export function githubActionsCredentials(opts) { if (!opts?.projectId) { throw new LocalError("githubActionsCredentials requires projectId", "creating GitHub Actions CI credentials"); } const apiBase = opts.apiBase ?? CI_AUDIENCE; const audience = opts.audience ?? CI_AUDIENCE; const fetchImpl = opts.fetch ?? globalThis.fetch.bind(globalThis); const refreshBeforeMs = Math.max(0, opts.refreshBeforeSeconds ?? 60) * 1000; let cached = null; return createCiSessionCredentials({ projectId: opts.projectId, getAccessToken: async () => { const now = Date.now(); if (cached && now < cached.refreshAtMs) return cached.token; const subjectToken = await requestGithubOidcToken(fetchImpl, audience); const exchanged = await exchangeWithRun402Ci(fetchImpl, apiBase, opts.projectId, subjectToken); cached = { token: exchanged.access_token, refreshAtMs: now + Math.max(0, exchanged.expires_in * 1000 - refreshBeforeMs), }; return cached.token; }, }); } async function requestGithubOidcToken(fetchImpl, audience) { const env = getProcessEnv(); const requestUrl = env.ACTIONS_ID_TOKEN_REQUEST_URL; const requestToken = env.ACTIONS_ID_TOKEN_REQUEST_TOKEN; if (!requestUrl || !requestToken) { throw new LocalError("GitHub Actions OIDC environment is missing ACTIONS_ID_TOKEN_REQUEST_URL or ACTIONS_ID_TOKEN_REQUEST_TOKEN. Ensure the workflow has permissions: id-token: write.", "requesting GitHub Actions OIDC token"); } const url = new URL(requestUrl); url.searchParams.set("audience", audience); const res = await fetchImpl(url.toString(), { headers: { Authorization: `Bearer ${requestToken}` }, }); const body = await res.json().catch(() => null); if (!res.ok || typeof body?.value !== "string" || body.value.length === 0) { throw new LocalError(`GitHub Actions OIDC token request failed (HTTP ${res.status})`, "requesting GitHub Actions OIDC token"); } return body.value; } async function exchangeWithRun402Ci(fetchImpl, apiBase, projectId, subjectToken) { const noAuth = { async getAuth() { return null; }, async getProject() { return null; }, }; const ci = new Ci(buildClient({ apiBase, fetch: fetchImpl, credentials: noAuth, })); return ci.exchangeToken({ project_id: projectId, subject_token: subjectToken }); } function getProcessEnv() { const proc = globalThis; return proc.process?.env ?? {}; } //# sourceMappingURL=ci-credentials.js.map