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.

183 lines • 7.23 kB
import { readFileSync, writeFileSync, mkdirSync, renameSync, chmodSync, rmdirSync } from "node:fs"; import { dirname, join } from "node:path"; import { randomBytes } from "node:crypto"; import { getActiveProfile, getApiBase, getProfileStatePath } from "./config.js"; function withFileLock(path, fn, { retries = 200, delayMs = 20 } = {}) { const lockDir = path + ".lock"; mkdirSync(dirname(path), { recursive: true }); for (let i = 0; i < retries; i++) { try { mkdirSync(lockDir, { mode: 0o700 }); } catch (e) { const code = e.code; if (code !== "EEXIST") throw e; const until = Date.now() + delayMs; while (Date.now() < until) { /* spin */ } continue; } try { return fn(); } finally { try { rmdirSync(lockDir); } catch { /* best-effort */ } } } throw new Error(`Could not acquire profile-state lock after ${retries} retries: ${lockDir}`); } export function loadProfileState(path) { const p = path ?? getProfileStatePath(); try { const parsed = JSON.parse(readFileSync(p, "utf-8")); if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) return {}; return parsed; } catch { return {}; } } export function saveProfileState(state, path) { const p = path ?? getProfileStatePath(); const dir = dirname(p); mkdirSync(dir, { recursive: true }); const tmp = join(dir, `.state.${randomBytes(4).toString("hex")}.tmp`); writeFileSync(tmp, JSON.stringify({ version: 1, ...state }, null, 2), { mode: 0o600 }); renameSync(tmp, p); chmodSync(p, 0o600); } export function defaultActiveProjectScope(scope = {}) { return { api_base: scope.api_base ?? getApiBase(), profile: scope.profile ?? getActiveProfile(), principal: scope.principal ?? null, }; } export function activeProjectScopeKey(scope = {}) { const resolved = defaultActiveProjectScope(scope); return JSON.stringify({ api_base: resolved.api_base, profile: resolved.profile, principal: resolved.principal ?? "unknown", }); } export function getActiveProjectId(path, scope = {}) { const state = loadProfileState(path); const key = activeProjectScopeKey(scope); return state.active_projects?.[key]?.project_id ?? state.active_project_id; } export function setActiveProjectId(projectId, path, scope = {}) { const p = path ?? getProfileStatePath(); withFileLock(p, () => { const state = loadProfileState(p); const key = activeProjectScopeKey(scope); const resolved = defaultActiveProjectScope(scope); const current = state.active_projects?.[key]?.project_id ?? state.active_project_id; const previous = current && current !== projectId ? current : undefined; state.active_projects = state.active_projects ?? {}; state.active_projects[key] = { project_id: projectId, ...(previous ? { previous_project_id: previous } : {}), api_base: resolved.api_base, principal: resolved.principal ?? null, profile: resolved.profile, updated_at: new Date().toISOString(), }; state.active_project_id = projectId; if (previous) state.previous_active_project_id = previous; else delete state.previous_active_project_id; saveProfileState(state, p); }); } export function clearActiveProjectId(projectId, path, scope = {}) { const p = path ?? getProfileStatePath(); withFileLock(p, () => { const state = loadProfileState(p); const key = activeProjectScopeKey(scope); const scoped = state.active_projects?.[key]; if (scoped?.project_id === projectId && state.active_projects) { if (scoped.previous_project_id) { state.active_projects[key] = { ...scoped, project_id: scoped.previous_project_id, previous_project_id: undefined, updated_at: new Date().toISOString(), }; } else { delete state.active_projects[key]; } } if (state.active_project_id === projectId) { if (state.previous_active_project_id && state.previous_active_project_id !== projectId) { state.active_project_id = state.previous_active_project_id; } else { delete state.active_project_id; } delete state.previous_active_project_id; } saveProfileState(state, p); }); } // --------------------------------------------------------------------------- // Selected organization (add-cli-current-org, design D3). // // Scoped exactly like the active project id — by api_base, profile, and // principal — and stored in the SAME per-profile state.json, never in the // base-level config.json beside `active_wallet`. The chain is // wallet -> principal -> memberships, so a globally-selected org survives // `wallets use other` and then either 403s or, worse, silently resolves to a // valid-but-wrong org when both principals are members. Per-profile makes the // failure mode "this profile has no current org", which the ORG_REQUIRED // envelope already knows how to explain. // // There is deliberately no top-level `active_org_id` mirror: the project one // exists only to carry pre-scoping installs forward, and this key is new. // --------------------------------------------------------------------------- export function getActiveOrgId(path, scope = {}) { const state = loadProfileState(path); return state.active_orgs?.[activeProjectScopeKey(scope)]?.org_id; } export function setActiveOrgId(orgId, path, scope = {}) { const p = path ?? getProfileStatePath(); withFileLock(p, () => { const state = loadProfileState(p); const key = activeProjectScopeKey(scope); const resolved = defaultActiveProjectScope(scope); state.active_orgs = state.active_orgs ?? {}; state.active_orgs[key] = { org_id: orgId, api_base: resolved.api_base, principal: resolved.principal ?? null, profile: resolved.profile, updated_at: new Date().toISOString(), }; saveProfileState(state, p); }); } export function clearActiveOrgId(path, scope = {}) { const p = path ?? getProfileStatePath(); withFileLock(p, () => { const state = loadProfileState(p); const key = activeProjectScopeKey(scope); if (state.active_orgs?.[key]) delete state.active_orgs[key]; saveProfileState(state, p); }); } export function recordMigration(marker, value, path) { const p = path ?? getProfileStatePath(); withFileLock(p, () => { const state = loadProfileState(p); state.migrations = { ...(state.migrations ?? {}), [marker]: value }; saveProfileState(state, p); }); } //# sourceMappingURL=profile-state.js.map