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.
721 lines • 29.6 kB
JavaScript
/**
* Type definitions for the unified `apply` primitive (`unified-deploy`
* capability, gateway routes `POST /apply/v1/plans` + commit + operations).
*
* The user-facing inputs accept polymorphic byte sources (strings, Uint8Array,
* Blob, web streams, plus the Node-only `fileSetFromDir`). The wire shapes
* carry only `ContentRef` objects — the SDK normalizes byte sources into
* refs via the CAS content service before issuing the plan request.
*/
export const ROUTE_HTTP_METHODS = [
"GET",
"HEAD",
"POST",
"PUT",
"PATCH",
"DELETE",
"OPTIONS",
];
export const ROUTE_PRICING_NETWORKS = [
"mainnet",
"testnet",
// tenant-lnbtc-priced-routes: the x402 `exact` scheme on Bitcoin Lightning
// (`lnbtc`), settled to the organization's own Lightning receiver.
"lightning",
"lightning-testnet",
// tenant-mpp-priced-routes: an MPP Tempo charge in pathUSD on Tempo mainnet
// (chain 4217) / Moderato (42431), paid to the organization's payout address.
"tempo",
"tempo-testnet",
];
export const LIGHTNING_ROUTE_PRICING_NETWORKS = ["lightning", "lightning-testnet"];
export const EMPTY_STATIC_MANIFEST_METADATA = {
file_count: 0,
total_bytes: 0,
cache_classes: {},
cache_class_sources: {},
spa_fallback: null,
};
export function normalizeStaticManifestMetadata(metadata) {
return metadata ?? EMPTY_STATIC_MANIFEST_METADATA;
}
export function normalizeDeployResolveRequest(opts) {
if (!opts || typeof opts !== "object") {
throw new TypeError("Deploy resolve options must be an object");
}
const hasUrl = "url" in opts && opts.url !== undefined;
const hasHost = "host" in opts && opts.host !== undefined;
if (hasUrl === hasHost) {
throw new TypeError("Deploy resolve requires exactly one input form: a full absolute url, or a clean host/path pair.");
}
if (!opts.project || typeof opts.project !== "string") {
throw new TypeError("Deploy resolve requires project");
}
const method = normalizeDeployResolveMethod(opts.method);
if (hasUrl) {
const original = String(opts.url);
let parsed;
try {
parsed = opts.url instanceof URL ? opts.url : new URL(original);
}
catch {
throw new TypeError("Deploy resolve url must be an absolute HTTP(S) public URL.");
}
if (parsed.protocol !== "http:" && parsed.protocol !== "https:") {
throw new TypeError("Deploy resolve url must use http: or https:.");
}
if (parsed.username || parsed.password) {
throw new TypeError("Deploy resolve url must not include username or password credentials.");
}
const ignored = {};
if (parsed.search)
ignored.query = parsed.search;
if (parsed.hash)
ignored.fragment = parsed.hash;
return {
project: opts.project,
project_scope: "credential_lookup_only",
project_sent_to_gateway: false,
original_url: original,
host: parsed.hostname,
path: parsed.pathname || "/",
...(method ? { method } : {}),
...(Object.keys(ignored).length > 0 ? { ignored } : {}),
};
}
const host = opts.host;
if (typeof host !== "string" || host.length === 0) {
throw new TypeError("Deploy resolve host must be a non-empty hostname.");
}
if (/\s/.test(host) ||
host.includes("/") ||
host.includes("?") ||
host.includes("#") ||
/^[a-z][a-z0-9+.-]*:/i.test(host) ||
host.includes("@")) {
throw new TypeError("Deploy resolve host must be a clean hostname without scheme, credentials, path, query, or fragment.");
}
const path = opts.path;
if (path !== undefined) {
if (typeof path !== "string" || !path.startsWith("/")) {
throw new TypeError("Deploy resolve path must start with '/'.");
}
if (path.includes("?") || path.includes("#")) {
throw new TypeError("Deploy resolve host/path mode does not accept query strings or fragments in path.");
}
}
return {
project: opts.project,
project_scope: "credential_lookup_only",
project_sent_to_gateway: false,
host,
path: path ?? "/",
...(method ? { method } : {}),
};
}
export function isDeployResolveStaticHit(response) {
return (response.match === "static_exact" ||
response.match === "static_index" ||
response.match === "spa_fallback" ||
response.match === "retained_public_path");
}
export function isDeployResolveRouteHit(response) {
return !!response.route && typeof response.route === "object";
}
export function buildDeployResolveSummary(response, request) {
const warnings = deployResolveWarningsForRequest(request, response);
const next_steps = deployResolveNextSteps(response);
const would_serve = response.authorized === true &&
isDeployResolveAuthorizationPassing(response) &&
response.result >= 200 &&
response.result < 400 &&
response.match !== "host_missing" &&
response.match !== "route_method_miss" &&
!isDeployResolveCasFailure(response);
const method = request.method ?? "GET";
const url = `${method} ${request.original_url ? displayResolveUrl(request) : `https://${request.host}${request.path}`}`;
const category = deployResolveCategory(response);
const summary = withDeploymentHostNote(deployResolveSummaryText(response, url), response);
return {
would_serve,
diagnostic_status: response.result,
match: response.match,
category,
summary,
warnings,
next_steps,
};
}
export function summarizeDeployResult(result) {
const diff = result.diff ?? {};
const summary = {
schema_version: "deploy-summary.v1",
release_id: result.release_id,
operation_id: result.operation_id,
...(typeof diff.is_noop === "boolean" ? { is_noop: diff.is_noop } : {}),
...(result.static_continuity ? { static_continuity: result.static_continuity } : {}),
headline: "",
warnings: summarizeDeployWarnings(result.warnings),
};
const site = summarizeDeploySite(diff);
if (site)
summary.site = site;
if (isModernFunctionsDiff(diff.functions)) {
summary.functions = {
added: [...diff.functions.added],
removed: [...diff.functions.removed],
changed: diff.functions.changed.map((entry) => ({
name: entry.name,
fields_changed: [...entry.fields_changed],
})),
};
}
if (isModernPlanMigrationDiff(diff.migrations)) {
summary.migrations = {
new: diff.migrations.new.map((entry) => entry.id),
noop: diff.migrations.noop.map((entry) => entry.id),
};
}
if (isModernRoutesDiff(diff.routes)) {
summary.routes = summarizeResourceCounts(diff.routes);
}
if (isModernSecretsDiff(diff.secrets)) {
summary.secrets = {
added: diff.secrets.added.length,
removed: diff.secrets.removed.length,
};
}
if (isModernSubdomainsDiff(diff.subdomains)) {
summary.subdomains = {
added: diff.subdomains.added.length,
removed: diff.subdomains.removed.length,
};
}
summary.headline = buildDeploySummaryHeadline(summary);
return summary;
}
function summarizeDeployWarnings(warnings) {
const codes = Array.from(new Set(warnings.map((warning) => warning.code))).sort();
return {
count: warnings.length,
blocking: warnings.filter((warning) => warning.requires_confirmation || warning.code === "MISSING_REQUIRED_SECRET").length,
codes,
};
}
function summarizeDeploySite(diff) {
const out = {};
if (isStaticAssetsDiff(diff.static_assets)) {
out.paths = {
added: diff.static_assets.added,
changed: diff.static_assets.changed,
removed: diff.static_assets.removed,
unchanged: diff.static_assets.unchanged,
total_changed: diff.static_assets.added +
diff.static_assets.changed +
diff.static_assets.removed,
};
out.cas = {
newly_uploaded_bytes: diff.static_assets.newly_uploaded_cas_bytes,
reused_bytes: diff.static_assets.reused_cas_bytes,
deployment_copy_bytes_eliminated: diff.static_assets.deployment_copy_bytes_eliminated,
};
}
else if (isModernSiteDiff(diff.site)) {
const added = diff.site.totals?.added ?? diff.site.added.length;
const changed = diff.site.totals?.changed ?? diff.site.changed.length;
const removed = diff.site.totals?.removed ?? diff.site.removed.length;
out.paths = {
added,
changed,
removed,
total_changed: added + changed + removed,
};
}
return out.paths || out.cas ? out : undefined;
}
function summarizeResourceCounts(diff) {
const added = diff.totals?.added ?? diff.added.length;
const changed = diff.totals?.changed ?? diff.changed.length;
const removed = diff.totals?.removed ?? diff.removed.length;
return { added, changed, removed };
}
function isStaticAssetsDiff(value) {
if (!value || typeof value !== "object" || Array.isArray(value))
return false;
const obj = value;
return (typeof obj.unchanged === "number" &&
typeof obj.changed === "number" &&
typeof obj.added === "number" &&
typeof obj.removed === "number" &&
typeof obj.newly_uploaded_cas_bytes === "number" &&
typeof obj.reused_cas_bytes === "number" &&
typeof obj.deployment_copy_bytes_eliminated === "number");
}
function isModernSiteDiff(value) {
if (!value || typeof value !== "object" || Array.isArray(value))
return false;
const obj = value;
return (Array.isArray(obj.added) &&
Array.isArray(obj.changed) &&
Array.isArray(obj.removed));
}
function isModernFunctionsDiff(value) {
if (!value || typeof value !== "object" || Array.isArray(value))
return false;
const obj = value;
return (Array.isArray(obj.added) &&
Array.isArray(obj.removed) &&
Array.isArray(obj.changed));
}
export function isModernPlanMigrationDiff(value) {
if (!value || typeof value !== "object" || Array.isArray(value))
return false;
const obj = value;
return Array.isArray(obj.new) && Array.isArray(obj.noop);
}
function isModernRoutesDiff(value) {
if (!value || typeof value !== "object" || Array.isArray(value))
return false;
const obj = value;
return Array.isArray(obj.added) && Array.isArray(obj.changed) && Array.isArray(obj.removed);
}
function isModernSecretsDiff(value) {
if (!value || typeof value !== "object" || Array.isArray(value))
return false;
const obj = value;
return Array.isArray(obj.added) && Array.isArray(obj.removed);
}
function isModernSubdomainsDiff(value) {
if (!value || typeof value !== "object" || Array.isArray(value))
return false;
const obj = value;
return Array.isArray(obj.added) && Array.isArray(obj.removed);
}
function buildDeploySummaryHeadline(summary) {
const parts = [];
const paths = summary.site?.paths;
if (paths) {
parts.push(paths.total_changed === 0
? "no static path changes"
: `${formatCount(paths.total_changed, "static path")} changed`);
}
const cas = summary.site?.cas;
if (cas) {
parts.push(`${formatBytes(cas.newly_uploaded_bytes)} uploaded, ${formatBytes(cas.reused_bytes)} reused`);
}
const functions = summary.functions;
if (functions) {
const count = functions.added.length + functions.removed.length + functions.changed.length;
parts.push(count === 0
? "no functions changed"
: `${formatCount(count, "function")} changed`);
}
const migrations = summary.migrations;
if (migrations && migrations.new.length > 0) {
parts.push(`${formatCount(migrations.new.length, "migration")} new`);
}
const routes = summary.routes;
if (routes) {
const count = routes.added + routes.changed + routes.removed;
if (count > 0)
parts.push(`${formatCount(count, "route")} changed`);
}
if (parts.length > 0)
return parts.join("; ");
if (summary.is_noop === true)
return "no deploy changes reported";
return "deploy summary unavailable";
}
function formatCount(count, singular) {
return `${count} ${singular}${count === 1 ? "" : "s"}`;
}
function formatBytes(bytes) {
if (!Number.isFinite(bytes))
return `${bytes} B`;
const units = ["B", "KB", "MB", "GB", "TB"];
let value = bytes;
let unitIndex = 0;
while (Math.abs(value) >= 1000 && unitIndex < units.length - 1) {
value /= 1000;
unitIndex += 1;
}
if (unitIndex === 0)
return `${bytes} B`;
return `${value.toFixed(1)} ${units[unitIndex]}`;
}
function normalizeDeployResolveMethod(method) {
if (method === undefined)
return undefined;
if (typeof method !== "string") {
throw new TypeError("Deploy resolve method must be a string.");
}
const normalized = method.trim().toUpperCase();
if (!/^[A-Z][A-Z0-9!#$%&'*+.^_`|~-]*$/.test(normalized)) {
throw new TypeError("Deploy resolve method must be a valid HTTP token such as GET, HEAD, POST, or OPTIONS.");
}
return normalized;
}
function displayResolveUrl(request) {
if (!request.original_url)
return `https://${request.host}${request.path}`;
try {
const url = new URL(request.original_url);
return `${url.protocol}//${url.host}${url.pathname || "/"}`;
}
catch {
return `${request.host}${request.path}`;
}
}
function deployResolveWarningsForRequest(request, response) {
const warnings = [];
if (request.ignored?.query) {
warnings.push({
code: "query_ignored",
message: "Query strings do not affect Run402 route resolution.",
});
}
if (request.ignored?.fragment) {
warnings.push({
code: "fragment_ignored",
message: "URL fragments are never sent to the server and do not affect resolution.",
});
}
const edge = response?.edge_propagation;
if (edge && edge.status !== "settled") {
warnings.push({
code: `edge_${edge.status}`,
message: edge.hint,
});
}
return warnings;
}
function deployResolveCategory(response) {
if (isDeployResolveCasFailure(response))
return "cas";
if (response.match === "route_method_miss")
return "route_method";
if (response.match === "active_release_missing")
return "release";
if (response.match === "unsupported_manifest_version")
return "manifest";
if (isDeployResolveRouteHit(response))
return "route";
if (response.match === "route_function" || response.match === "route_static_alias") {
return "route";
}
if (isDeployResolveStaticHit(response))
return "static";
if (response.match === "host_missing")
return "host";
if (response.match === "deployment_host" || response.match === "deployment_retired")
return "deployment";
if (response.match === "manifest_missing")
return "manifest";
if (response.match === "path_error")
return "path";
if (response.match === "spa_fallback_missing")
return "fallback";
if (response.match === "none")
return "miss";
return "unknown";
}
function withDeploymentHostNote(summary, response) {
if (response.host_kind !== "deployment" ||
response.path_evaluated !== true ||
response.deployment_status !== "retained" ||
!response.origin_available_until) {
return summary;
}
const routes = response.routes_active === false ? " Its routes no longer run." : "";
return `${summary} This deployment host was replaced and serves until ${response.origin_available_until}, then stops.${routes}`;
}
function deployResolveSummaryText(response, url) {
if (isDeployResolveCasFailure(response)) {
const sha = response.cas_object?.sha256;
const suffix = sha ? ` for CAS object ${sha}` : "";
switch (response.authorization_result) {
case "missing_cas_object":
return `${url} matched static content, but the backing CAS object is missing${suffix}.`;
case "unfinalized_or_deleting_cas_object":
return `${url} matched static content, but the backing CAS object is not finalized or is being deleted${suffix}.`;
case "size_mismatch":
return `${url} matched static content, but the backing CAS object size does not match the release manifest${suffix}.`;
case "unauthorized_cas_object":
return `${url} matched static content, but the selected credentials are not authorized to inspect the backing CAS object${suffix}.`;
default:
return `${url} matched static content, but CAS object health prevented a servable diagnostic${suffix}.`;
}
}
if (response.match === "host_missing") {
return `${url} did not resolve because the host is not bound to this account/project context.`;
}
if (response.match === "manifest_missing") {
return `${url} reached the host, but no active static manifest is available for diagnostics.`;
}
if (response.match === "deployment_host") {
const deployment = response.deployment_id ?? "its deployment";
return response.deployment_status === "retained" && response.origin_available_until
? `${url} is a deployment host for ${deployment}, which was replaced; it serves the whole deployment until ${response.origin_available_until}, then stops. The path itself is not evaluated.`
: `${url} is a deployment host serving ${deployment}. The path itself is not evaluated.`;
}
if (response.match === "deployment_retired") {
return `${url} is a deployment host for ${response.deployment_id ?? "a deployment"} that was replaced and is no longer served.`;
}
if (response.match === "active_release_missing") {
return `${url} reached the host, but no active release is available for diagnostics.`;
}
if (response.match === "unsupported_manifest_version" ||
response.authorization_result === "unsupported_manifest_version") {
return `${url} reached the host, but the active static manifest version is not supported by the gateway.`;
}
if (response.match === "path_error") {
return `${url} could not be evaluated because the path is not a valid Run402 public path.`;
}
if (response.match === "route_method_miss") {
const allowed = formatAllowedMethods(response.allow);
return allowed
? `${url} matched a deploy route pattern, but the method is not allowed. Allowed methods: ${allowed}.`
: `${url} matched a deploy route pattern, but the method is not allowed.`;
}
if (response.match === "none") {
return `${url} did not match a materialized static file or SPA fallback.`;
}
if (response.match === "spa_fallback_missing") {
return `${url} was eligible for SPA fallback, but the configured fallback file is missing.`;
}
if (response.match === "spa_fallback") {
return `${url} would serve the configured SPA fallback.`;
}
if (response.match === "static_index") {
return `${url} would serve a static index file.`;
}
if (response.match === "static_exact") {
return `${url} would serve an exact static file.`;
}
if (response.match === "route_function") {
return `${url} matched a deploy function route.`;
}
if (response.match === "route_static_alias") {
return `${url} matched a deploy static route alias.`;
}
if (isDeployResolveRouteHit(response)) {
return `${url} matched a deploy route.`;
}
if (response.result >= 200 && response.result < 400) {
return `${url} resolved with gateway match ${String(response.match)}.`;
}
return `${url} did not resolve to a servable public response; gateway match was ${String(response.match)}.`;
}
function deployResolveNextSteps(response) {
const edge = response.edge_propagation;
if (edge && edge.status !== "settled") {
return [
{
code: edge.status === "sync_pending" ? "retry_after_edge_sync" : "retry_after_edge_propagation",
message: edge.status === "sync_pending"
? "Retry after the edge-store source write is confirmed; the Run402 reconciler will rewrite the binding."
: "Retry after edge propagation settles, or run `run402 up verify` for the app workspace.",
},
{
code: "inspect_edge_propagation",
message: "Inspect resolution.edge_propagation for claimed_at, kvs_synced_at, expected_visible_by, and hint.",
},
];
}
if (isDeployResolveCasFailure(response)) {
switch (response.authorization_result) {
case "unauthorized_cas_object":
return [
{
code: "check_credentials",
message: "Check that the selected project credentials can inspect the backing static asset CAS object.",
},
{
code: "inspect_release_asset",
message: "Inspect the active release asset path and redeploy the affected static asset if the CAS authorization is unexpected.",
},
];
case "missing_cas_object":
return [
{
code: "redeploy_static_asset",
message: "Redeploy the affected static asset so the backing CAS object is uploaded and finalized.",
},
{
code: "inspect_release_asset",
message: "Inspect the active release asset path and CAS SHA in the full resolution payload.",
},
];
case "unfinalized_or_deleting_cas_object":
return [
{
code: "retry_after_cas_finalization",
message: "Retry diagnostics after the backing CAS object has finalized, or redeploy the affected static asset.",
},
{
code: "inspect_release_asset",
message: "Inspect the active release asset path and CAS SHA in the full resolution payload.",
},
];
case "size_mismatch":
return [
{
code: "redeploy_static_asset",
message: "Redeploy the affected static asset so release metadata and CAS object size agree.",
},
{
code: "inspect_cas_object",
message: "Compare cas_object.expected_size and cas_object.actual_size in the full resolution payload.",
},
];
default:
return [
{
code: "inspect_cas_object",
message: "Inspect authorization_result and cas_object in the full resolution payload.",
},
{
code: "redeploy_static_asset",
message: "Redeploy the affected static asset if the CAS health failure persists.",
},
];
}
}
switch (response.match) {
case "host_missing":
return [
{
code: "check_domain_binding",
message: "Check that the host is configured as a Run402 custom domain or subdomain.",
},
{
code: "check_dns",
message: "Check DNS and domain binding status.",
},
{
code: "check_credentials",
message: "Check that the selected local project credentials can inspect this host.",
},
];
case "deployment_retired":
return [
{
code: "use_project_host",
message: "Use the project's subdomain or custom domain, which serves the live release.",
},
{
code: "promote_release",
message: "Promote this deployment's release (`run402 deploy promote <release_id>`) only if it should be live again.",
},
];
case "manifest_missing":
return [
{
code: "check_active_release",
message: "Check that the project has an active apply-v1 release.",
},
{
code: "redeploy_static_site",
message: "Deploy static site content again if the active release predates static manifest metadata.",
},
];
case "active_release_missing":
return [
{
code: "check_active_release",
message: "Check that the project has an active apply-v1 release.",
},
{
code: "deploy_release",
message: "Deploy site content or routes before diagnosing this public URL.",
},
];
case "unsupported_manifest_version":
return [
{
code: "redeploy_static_site",
message: "Redeploy static site content so the active release has a current static manifest.",
},
{
code: "inspect_release",
message: "Inspect the active release static_manifest_sha256 and retry diagnostics after redeploy.",
},
];
case "path_error":
return [
{
code: "check_public_path",
message: "Use an absolute URL path that starts with '/' and does not contain invalid encoded segments.",
},
];
case "none":
return [
{
code: "check_static_path",
message: "Check active release static_public_paths for browser reachability and site.paths for backing release assets.",
},
{
code: "check_spa_fallback",
message: "Check whether the release has a configured SPA fallback.",
},
];
case "spa_fallback_missing":
return [
{
code: "restore_spa_fallback",
message: "Deploy the configured SPA fallback file or remove the fallback declaration.",
},
];
case "route_method_miss": {
const allowed = formatAllowedMethods(response.allow);
return [
{
code: "check_route_methods",
message: allowed
? `Retry with one of the allowed methods (${allowed}) or update the route method list.`
: "Inspect the route method list and retry with an allowed method or update the route.",
},
{
code: "inspect_route",
message: "Inspect route_pattern, target_type, target_name, and target_file in the full resolution payload.",
},
];
}
default:
return response.result >= 200 && response.result < 400
? []
: [
{
code: "inspect_resolution",
message: "Inspect the full resolution payload for gateway-specific fields.",
},
];
}
}
function isDeployResolveAuthorizationPassing(response) {
const result = response.authorization_result;
return result === undefined || result === null || result === "authorized" || result === "not_applicable";
}
function isDeployResolveCasFailure(response) {
switch (response.authorization_result) {
case "missing_cas_object":
case "unfinalized_or_deleting_cas_object":
case "size_mismatch":
case "unauthorized_cas_object":
return true;
default:
break;
}
const cas = response.cas_object;
if (!cas)
return false;
if (cas.exists === false)
return true;
return typeof cas.actual_size === "number" && cas.actual_size !== cas.expected_size;
}
function formatAllowedMethods(methods) {
if (!methods || methods.length === 0)
return "";
return methods.join(", ");
}
//# sourceMappingURL=deploy.types.js.map