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
JavaScript
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