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.

81 lines • 3.33 kB
/** * `secrets` namespace — project-scoped environment variables for functions. * All methods require the project's service key. */ import { LocalError } from "../errors.js"; import { requireProjectCredentials } from "../project-credentials.js"; const SECRET_KEY_RE = /^[A-Z_][A-Z0-9_]{0,127}$/; const SECRET_VALUE_LIMIT_BYTES = 4 * 1024; export class Secrets { client; constructor(client) { this.client = client; } /** Set or overwrite a project secret. Injected as `process.env.KEY` in deployed functions. */ async set(projectId, key, opts) { const value = opts.value; validateSecretKey(key, "setting secret"); validateSecretValue(value); const project = await requireProjectCredentials(this.client, projectId, "setting secret"); await this.client.request(`/projects/v1/admin/${projectId}/secrets`, { method: "POST", headers: { Authorization: `Bearer ${project.service_key}` }, body: { key, value }, context: "setting secret", }); } /** List secret keys for a project. Values and value-derived hashes are never returned. */ async list(projectId) { const project = await requireProjectCredentials(this.client, projectId, "listing secrets"); const body = await this.client.request(`/projects/v1/admin/${projectId}/secrets`, { headers: { Authorization: `Bearer ${project.service_key}` }, context: "listing secrets", }); return normalizeSecretList(body); } /** Delete a secret. */ async delete(projectId, key) { const project = await requireProjectCredentials(this.client, projectId, "deleting secret"); return this.client.request(`/projects/v1/admin/${projectId}/secrets/${encodeURIComponent(key)}`, { method: "DELETE", headers: { Authorization: `Bearer ${project.service_key}` }, context: "deleting secret", }); } } function validateSecretKey(key, context) { if (typeof key !== "string" || !SECRET_KEY_RE.test(key)) { throw new LocalError(`Secret key must match ${SECRET_KEY_RE.source}`, context); } } function validateSecretValue(value) { if (typeof value !== "string") { throw new LocalError("Secret value must be a string", "setting secret"); } const bytes = new TextEncoder().encode(value).byteLength; if (bytes > SECRET_VALUE_LIMIT_BYTES) { throw new LocalError(`Secret value is ${bytes} bytes; maximum is ${SECRET_VALUE_LIMIT_BYTES} UTF-8 bytes`, "setting secret"); } } function normalizeSecretList(body) { const raw = Array.isArray(body) ? body : body && typeof body === "object" && Array.isArray(body.secrets) ? body.secrets : []; const secrets = raw.flatMap((item) => { if (!item || typeof item !== "object") return []; const obj = item; if (typeof obj.key !== "string") return []; const out = { key: obj.key }; if (typeof obj.created_at === "string") out.created_at = obj.created_at; if (typeof obj.updated_at === "string") out.updated_at = obj.updated_at; return [out]; }); return { secrets }; } //# sourceMappingURL=secrets.js.map