src/web/server.js
Rama main · réplica de lectura del repositorio original.
import {
createHash,
randomBytes as createRandomBytes,
randomUUID,
timingSafeEqual,
} from "node:crypto";
import { readFile } from "node:fs/promises";
import { createServer } from "node:http";
import { isAbsolute } from "node:path";
import packageManifest from "../../package.json" with { type: "json" };
import { isOpenAiAuthorizationUrl } from "../browser.js";
import { listSiwcModels } from "../gateway/openai-oauth-sidecar.mjs";
import { discoverN8n, discoverNetworks } from "../services/discovery.js";
import {
changeVpsCodexImages, changeVpsModelChecks, getVpsCodexImagesStatus, getVpsModelDiscovery, getVpsSiwcInstallationStatus,
inspectStoppedVpsSiwcInstallation, getVpsUsageStatus,
installSidecar, manageVpsSiwcInstallation, reviewVpsLegacyMigration, reviewVpsSiwcReplacement, reviewVpsSiwcTarget,
reviewVpsSiwcResume, reconcileVpsSiwcHandoff, reviewVpsSiwcRuntimeUpdate, updateVpsSiwcRuntime,
} from "../services/installer.js";
import { usageView } from "../services/model-discovery.mjs";
import { inspectVpsSuperGrok, reviewVpsSuperGrok, installVpsSuperGrok, changeVpsSuperGrok, getVpsGrokLoginStatus, discoverVpsGrokModels } from "../services/vps-supergrok.js";
import { getVpsClaudeStatus, reviewVpsClaude, applyVpsClaude, getVpsClaudeAuthStatus, discoverVpsClaudeModels, vpsClaudeLoginCommand } from "../services/vps-claude.js";
import { CLAUDE_COMPANION } from "../claude/companion.js";
import { validModels } from "../claude/launcher.js";
import { inspectVpsLocalModel, reviewVpsLocalModel, installVpsLocalModel, changeVpsLocalModel, getVpsLocalModelOperationStatus } from "../services/vps-local-model.js";
import { installAssistant } from "../services/assistant-installer.js";
import { getAuthStatus, listAuthRegistrations, startOAuthLogin } from "../services/oauth.js";
import {
acknowledgePlanUse, getSelectedRegistration, readRegistration, resolveSiwcStorageRoot,
selectRegistration, setPlanEnabled, signOut,
} from "../services/siwc-session.mjs";
import { readUiPreferences, writeUiPreferences } from "../services/ui-preferences.js";
import { createProjectMetaReader } from "../services/project-meta.js";
import {
connectVerified,
scanHostFingerprint,
getSshCapabilities,
} from "../infrastructure/ssh.js";
import { buildVpsHostReport } from "../domain/vps-host-report.js";
import {
validateHostname,
validatePort,
validateUsername,
} from "../domain/validation.js";
import { SIDECAR_HOSTNAME } from "../domain/templates.js";
import { VPS_NETWORK_UNSUPPORTED } from "../domain/vps-network.js";
import {
ASSISTANT_ROOT,
validateAssistantSearxngSelection,
} from "../domain/assistant.js";
import { ASSISTANT_COMPANION_IMAGES } from "../domain/assistant-templates.js";
import {
createLocalDeploymentPlan,
validateLocalTarget,
} from "../domain/local-endpoints.js";
import {
LOCAL_N8N_SIDECAR_ENDPOINT,
LOCAL_N8N_SIDECAR_TARGET,
createLocalN8nSidecarPlan,
} from "../domain/local-n8n-sidecar.js";
import {
LOCAL_N8N_ASSISTANT_TARGET,
createLocalN8nAssistantPlan,
} from "../domain/local-n8n-assistant.js";
import {
LOCAL_N8N_SUPERGROK_ENDPOINT,
LOCAL_N8N_SUPERGROK_TARGET,
createLocalN8nSuperGrokPlan,
} from "../domain/local-n8n-supergrok.js";
import {
LOCAL_N8N_CLAUDE_TARGET, createLocalN8nClaudePlan, validateClaudeReview,
createSafeLocalN8nClaudePlan, createSafeLocalN8nClaudeInstallResult,
createSafeLocalN8nClaudeSnapshot, createSafeLocalN8nClaudeStatus, createSafeLocalN8nClaudeChangeResult,
} from "../domain/local-n8n-claude.js";
import { LOCAL_N8N_MODEL_TARGET, LOCAL_N8N_MODEL_ENDPOINT, LOCAL_MODEL_CATALOG, LOCAL_MODEL_RUNTIME_IMAGE, getLocalModelDefinition, createLocalN8nModelPlan } from "../domain/local-n8n-model.js";
import { LOCAL_MODEL_CATALOG_REVISION } from "../local-model/catalog.mjs";
import { HOSTING_PROVIDERS } from "../domain/hosting-providers.js";
import { createHostingDeploymentPlan, getHostingDeploymentProfiles } from "../domain/hosting-deployment.js";
import {
LOCAL_N8N_STACK_PUBLIC_CONFIRMATION,
LOCAL_N8N_STACK_REMOVE_CONFIRMATION,
LOCAL_N8N_STACK_TARGET,
createLocalN8nStackPlan,
} from "../domain/local-n8n-stack.js";
import {
acquireLocalEndpointChangeLock,
activateLocalClientCredentialRotation,
getManagedLocalEndpointStatus,
getLocalDockerStatus,
installLocalEndpoint,
reviewLocalCodexLegacyMigration,
reviewLocalCodexSiwcReplacement,
inspectStoppedLocalSiwcInstallation,
manageLocalSiwcInstallation,
reviewLocalSiwcResume,
reconcileLocalSiwcHandoff,
prepareLocalClientCredentialRotation,
} from "../services/local-installer.js";
import { copySiwcAccountView, copySiwcStaging, getLocalDashboardStatus } from "../services/local-dashboard.js";
import {
discoverLocalN8nSidecarTargets,
getLocalN8nSidecarStatus,
getLocalN8nSidecarUsage,
reviewLocalN8nLegacyMigration,
reviewLocalN8nSiwcReplacement,
inspectStoppedLocalN8nSiwcInstallation,
installLocalN8nSidecar,
manageLocalN8nSiwcInstallation,
getLocalN8nCodexImagesStatus,
changeLocalN8nCodexImages,
reviewLocalN8nSiwcResume,
reconcileLocalN8nSiwcHandoff,
removeLocalN8nSidecar,
} from "../services/local-n8n-sidecar-installer.js";
import {
editLocalN8nAssistantSearxng,
installLocalN8nAssistant,
prepareLocalN8nAssistantSearxngUpdate,
removeLocalN8nAssistant,
} from "../services/local-n8n-assistant-installer.js";
import {
getLocalN8nSuperGrokStatus,
installLocalN8nSuperGrok,
removeLocalN8nSuperGrok,
} from "../services/local-n8n-supergrok-installer.js";
import {
getLocalN8nClaudeStatus, installLocalN8nClaude, changeLocalN8nClaude,
getLocalN8nClaudeAuthStatus, inspectLocalN8nClaudeHost,
discoverLocalN8nClaudeModels,
} from "../services/local-n8n-claude-installer.js";
import { getLocalN8nModelStatus, installLocalN8nModel, reviewLocalN8nModelAction, applyLocalN8nModelAction, inspectLocalN8nModelResources } from "../services/local-n8n-model-installer.js";
import {
LOCAL_N8N_MANAGED_PARTIAL_STACK_ERROR_CODE,
LOCAL_N8N_STACK_NGROK_SETUP_REJECTED_FAILURE_KIND,
LOCAL_N8N_STACK_RETRYABLE_STARTUP_ERROR_CODE,
getLocalN8nStackStatus,
getManagedLocalN8nStackTarget,
installLocalN8nStack,
removeLocalN8nStack,
resumeLocalN8nStack,
} from "../services/local-n8n-stack-installer.js";
import { validateLocalN8nStackSecrets } from "../templates/local-n8n-stack/index.js";
import { importLocalStackN8nCredential } from "../services/local-n8n-stack-credentials.js";
import { createLocalChatTestService } from "../services/local-chat-test.js";
import { createPrivateBrowserHandoff } from "../services/browser-handoff.js";
import { isPrivateBrowserLaunchUrl } from "../browser.js";
const MAX_BODY_BYTES = 32 * 1024;
const RATE_LIMIT_WINDOW_MS = 15 * 60 * 1000;
const RATE_LIMIT_MAX = 10;
const OAUTH_SHUTDOWN_WAIT_MS = 2_000;
const VPS_CONNECTION_IDLE_MS = 15 * 60 * 1000;
const LOCAL_ROTATION_STAGE_TTL_MS = 2 * 60 * 1000;
const BROWSER_BOOTSTRAP_TTL_MS = 30 * 1000;
const BROWSER_TRANSFER_TTL_MS = 10 * 1000;
const MAX_PENDING_BROWSER_BOOTSTRAPS = 8;
const MAX_PENDING_BROWSER_TRANSFERS = 8;
const BROWSER_PREPARE_PATH = "/__relmio/browser/prepare";
const BROWSER_BOOTSTRAP_PATH = "/__relmio/browser/bootstrap";
const BROWSER_TRANSFER_PATH = "/__relmio/browser/transfer";
const BROWSER_BOOTSTRAP_ROUTES = new Set(["/", "/assistant", "/local", "/supergrok-vps", "/claude-vps", "/local-model-vps", "/hosting"]);
const BROWSER_BOOTSTRAP_TOKEN_PATTERN = /^[A-Za-z0-9_-]{43}$/u;
const PACKAGE_VERSION = packageManifest.version;
const OAUTH_VPS_CONFLICT_MESSAGE =
"ChatGPT sign-in is in progress. Wait for it to finish or cancel it before changing the VPS.";
const VPS_OAUTH_CONFLICT_MESSAGE =
"A VPS change is already in progress. Wait for it to finish before starting ChatGPT sign-in.";
const getProjectMeta = createProjectMetaReader({ version: PACKAGE_VERSION });
const defaultServices = {
getAuthStatus,
listAuthRegistrations,
getSelectedRegistration,
readRegistration,
selectRegistration,
acknowledgePlanUse,
setPlanEnabled,
signOut,
startOAuthLogin,
listSiwcModels,
scanHostFingerprint,
connectVerified,
getSshCapabilities,
discoverN8n,
discoverNetworks,
installSidecar,
getVpsSiwcInstallationStatus,
inspectStoppedVpsSiwcInstallation,
manageVpsSiwcInstallation,
reviewVpsLegacyMigration,
reviewVpsSiwcReplacement,
reviewVpsSiwcTarget,
reviewVpsSiwcResume,
reconcileVpsSiwcHandoff,
reviewVpsSiwcRuntimeUpdate,
updateVpsSiwcRuntime,
getVpsCodexImagesStatus,
changeVpsCodexImages,
getVpsModelDiscovery,
changeVpsModelChecks,
getVpsUsageStatus,
inspectVpsSuperGrok, reviewVpsSuperGrok, installVpsSuperGrok, changeVpsSuperGrok, getVpsGrokLoginStatus, discoverVpsGrokModels,
getVpsClaudeStatus, reviewVpsClaude, applyVpsClaude, getVpsClaudeAuthStatus, discoverVpsClaudeModels,
inspectVpsLocalModel, reviewVpsLocalModel, installVpsLocalModel, changeVpsLocalModel, getVpsLocalModelOperationStatus,
installAssistant,
getManagedLocalEndpointStatus,
getLocalDockerStatus,
getLocalDashboardStatus,
getLocalN8nStackStatus,
getManagedLocalN8nStackTarget,
importLocalStackN8nCredential,
getLocalN8nSuperGrokStatus,
getLocalN8nClaudeStatus, getLocalN8nClaudeAuthStatus, inspectLocalN8nClaudeHost, changeLocalN8nClaude,
discoverLocalN8nClaudeModels,
discoverLocalN8nSidecarTargets,
getLocalN8nSidecarStatus,
getLocalN8nSidecarUsage,
inspectStoppedLocalN8nSiwcInstallation,
inspectStoppedLocalSiwcInstallation,
manageLocalN8nSiwcInstallation,
getLocalN8nCodexImagesStatus,
changeLocalN8nCodexImages,
manageLocalSiwcInstallation,
getLocalN8nModelStatus,
inspectLocalN8nModelResources,
getProjectMeta,
readUiPreferences,
writeUiPreferences,
reviewLocalCodexLegacyMigration,
reviewLocalN8nLegacyMigration,
reviewLocalCodexSiwcReplacement,
reviewLocalN8nSiwcReplacement,
reviewLocalSiwcResume,
reviewLocalN8nSiwcResume,
reconcileLocalSiwcHandoff,
reconcileLocalN8nSiwcHandoff,
installLocalEndpoint,
installLocalN8nSidecar,
installLocalN8nAssistant,
installLocalN8nSuperGrok,
installLocalN8nClaude,
editLocalN8nAssistantSearxng,
installLocalN8nModel,
installLocalN8nStack,
prepareLocalN8nAssistantSearxngUpdate,
prepareLocalN8nAssistantPlan: createLocalN8nAssistantPlan,
prepareLocalN8nSidecarPlan: createLocalN8nSidecarPlan,
prepareLocalN8nSuperGrokPlan: createLocalN8nSuperGrokPlan,
prepareLocalN8nClaudePlan: createLocalN8nClaudePlan,
prepareLocalN8nModelPlan: createLocalN8nModelPlan,
prepareLocalN8nStackPlan: createLocalN8nStackPlan,
prepareHostingDeploymentPlan: createHostingDeploymentPlan,
removeLocalN8nAssistant,
removeLocalN8nSidecar,
removeLocalN8nSuperGrok,
reviewLocalN8nModelAction,
applyLocalN8nModelAction,
removeLocalN8nStack,
resumeLocalN8nStack,
acquireLocalEndpointChangeLock,
activateLocalClientCredentialRotation,
prepareLocalClientCredentialRotation,
createLocalChatTestService,
};
function setSecurityHeaders(response) {
response.setHeader(
"Content-Security-Policy",
"default-src 'self'; script-src 'self'; style-src 'self'; connect-src 'self'; img-src 'self' data:; base-uri 'none'; frame-ancestors 'none'; form-action 'self'",
);
response.setHeader("X-Content-Type-Options", "nosniff");
response.setHeader("X-Frame-Options", "DENY");
response.setHeader("Referrer-Policy", "no-referrer");
response.setHeader(
"Permissions-Policy",
"camera=(), microphone=(), geolocation=()",
);
response.setHeader("Cache-Control", "no-store");
}
function sendJson(response, statusCode, body) {
const contents = JSON.stringify(body);
response.writeHead(statusCode, {
"Content-Type": "application/json; charset=utf-8",
"Content-Length": Buffer.byteLength(contents),
});
response.end(contents);
}
function requestAcceptsEventStream(request) {
const value = request.headers.accept;
return (
typeof value === "string" &&
value
.split(",")
.some((entry) => entry.trim().split(";", 1)[0] === "text/event-stream")
);
}
function startLocalChatTestStream(response) {
let ended = false;
response.writeHead(200, {
"Content-Encoding": "none",
"Content-Type": "text/event-stream; charset=utf-8",
"X-Accel-Buffering": "no",
"X-Relmio-Stream": "v1",
});
const send = (event, data) => {
if (ended || response.writableEnded || response.destroyed) return;
response.write(`event: ${event}\ndata: ${JSON.stringify(data)}\n\n`);
};
const keepalive = setInterval(() => {
if (!ended && !response.writableEnded && !response.destroyed) {
response.write(": keepalive\n\n");
}
}, 15_000);
keepalive.unref?.();
send("start", { requestId: randomUUID() });
return {
send(event, data) {
if (event === "progress" || event === "delta") send(event, data);
},
complete(result) {
if (ended) return;
send("terminal", {
outcome: "completed",
conversationId: result.conversationId,
});
ended = true;
clearInterval(keepalive);
response.end();
},
fail(error) {
if (ended) return;
const failure = safeFailure(error);
const outcome = error?.outcome === "interrupted" ? "interrupted"
: error?.outcome === "incomplete" ? "incomplete" : "failed";
send("error", { ...failure, outcome });
send("terminal", { outcome });
ended = true;
clearInterval(keepalive);
response.end();
},
dispose() {
ended = true;
clearInterval(keepalive);
},
};
}
function tokenMatches(actual, expected) {
if (typeof actual !== "string") {
return false;
}
const left = Buffer.from(actual);
const right = Buffer.from(expected);
return left.length === right.length && timingSafeEqual(left, right);
}
function exactObjectKeys(value, expected) {
return value && typeof value === "object" && !Array.isArray(value) &&
Object.keys(value).sort().join("\0") === [...expected].sort().join("\0");
}
function invalidSiwcRequest() {
return Object.assign(new Error("Select a valid ChatGPT account and try again."), { statusCode: 400 });
}
function requireSiwcId(value) {
if (typeof value !== "string" || !/^[a-zA-Z0-9_-]{8,128}$/u.test(value)) throw invalidSiwcRequest();
return value;
}
async function selectedSiwcAccount(state, { requirePlan = false } = {}) {
const registrationId = await state.services.getSelectedRegistration({ storageRoot: state.storageRoot });
const accounts = await state.services.listAuthRegistrations({ storageRoot: state.storageRoot });
const account = accounts.find((item) => item.registrationId === registrationId);
if (!account || account.identity !== "verified" || account.session !== "connected" ||
account.ownership !== "owned" ||
(requirePlan && (account.planEnabled !== true || account.planPermission !== "granted" ||
account.needsPlanWelcome === true))) {
throw Object.assign(new Error(requirePlan
? "Select a connected account with ChatGPT plan use enabled before reviewing this plan."
: "Select a connected ChatGPT account before continuing."), { statusCode: 409 });
}
const status = await state.services.getAuthStatus({
storageRoot: state.storageRoot, registrationId, runtimeId: state.runtimeId,
});
if (status?.exists !== true || status.generation !== account.generation ||
status.ownerHostId !== account.ownerHostId || status.ownerRuntimeId !== account.ownerRuntimeId) {
throw Object.assign(new Error("This ChatGPT account is not owned by this computer. Sign in with a fresh registration."),
{ statusCode: 409, recovery: "review-again" });
}
return account;
}
async function siwcBinding(state, account) {
const record = await state.services.readRegistration({
storageRoot: state.storageRoot, registrationId: account.registrationId,
});
if (!record || record.generation !== account.generation || typeof record.clientId !== "string") {
throw Object.assign(new Error("The ChatGPT account changed. Review again."), { statusCode: 409 });
}
return {
registrationId: account.registrationId,
clientId: record.clientId,
generation: account.generation,
ownerHostId: account.ownerHostId,
ownerRuntimeId: account.ownerRuntimeId,
};
}
async function requireMatchingSiwcBinding(state, binding) {
const account = await selectedSiwcAccount(state, { requirePlan: true });
const current = await siwcBinding(state, account);
if (Object.keys(current).some((key) => current[key] !== binding[key])) {
throw Object.assign(new Error("The ChatGPT account changed. Review again."), { statusCode: 409 });
}
return account;
}
async function recoveryAccount(state, registrationId, { pending = false } = {}) {
const accounts = await state.services.listAuthRegistrations({ storageRoot: state.storageRoot });
const account = accounts.find((item) => item.registrationId === requireSiwcId(registrationId));
if (!account || account.identity !== "verified" ||
(pending ? account.ownership !== "handoff-pending" : account.ownership === "handoff-pending")) {
throw Object.assign(new Error(pending
? "Choose the account whose transfer is pending."
: "Reconcile a pending transfer before reviewing its installation resume."),
{ statusCode: 409, recovery: "resolve-handoff" });
}
return copySiwcAccountView(account);
}
async function requireRecoveryBinding(state, binding, options) {
const account = await recoveryAccount(state, binding.registrationId, options);
const current = await siwcBinding(state, account);
if (Object.keys(current).some((key) => current[key] !== binding[key])) {
throw Object.assign(new Error("The recovery account changed. Review again."),
{ statusCode: 409, recovery: "review-again" });
}
return account;
}
function invalidateSiwcWork(state) {
state.sidecarPlan = null;
state.localPlan = null;
state.localStoppedOwnerReview = null;
state.vpsOwnerTargetReview = null;
state.vpsStoppedOwnerReview = null;
state.localDashboardGeneration += 1;
state.siwcRecoveryReview = null;
state.vpsRuntimeUpdateReview = null;
state.vpsImagesTarget = null;
state.localImagesTarget = null;
state.vpsModelsTarget = null;
state.localChatTest.resetAll?.();
}
function browserBootstrapToken(randomBytes) {
let bytes;
try { bytes = Buffer.from(randomBytes(32)); } catch {
throw new Error("Browser launch could not be prepared.");
}
if (bytes.length !== 32) throw new Error("Browser launch could not be prepared.");
return bytes.toString("base64url");
}
function digestBrowserBootstrapSecret(secret) {
return createHash("sha256").update(secret, "utf8").digest();
}
function disposeBrowserBootstrap(state, record) {
if (!record) return Promise.resolve(false);
if (record.disposed) return record.disposal ?? Promise.resolve(false);
record.disposed = true;
state.clearBrowserBootstrapTimer(record.timer);
try {
record.disposal = Promise.resolve(record.handoff.dispose()).catch(() => false);
} catch {
record.disposal = Promise.resolve(false);
}
return record.disposal;
}
function retireBrowserBootstrap(state, ticketId, record) {
if (state.browserBootstraps.get(ticketId) !== record) return false;
state.browserBootstraps.delete(ticketId);
state.browserBootstrapIds.delete(ticketId);
disposeBrowserBootstrap(state, record);
return true;
}
function purgeExpiredBrowserBootstraps(state) {
const currentTime = state.browserBootstrapNow();
for (const [ticketId, record] of state.browserBootstraps) {
if (record.expiresAtMs <= currentTime) retireBrowserBootstrap(state, ticketId, record);
}
}
function disposeBrowserTransfer(state, record) {
if (!record || record.disposed) return;
record.disposed = true;
state.clearBrowserBootstrapTimer(record.timer);
}
function retireBrowserTransfer(state, transferId, record) {
if (state.browserTransfers.get(transferId) !== record) return false;
state.browserTransfers.delete(transferId);
state.browserTransferIds.delete(transferId);
disposeBrowserTransfer(state, record);
return true;
}
function purgeExpiredBrowserTransfers(state) {
const currentTime = state.browserBootstrapNow();
for (const [transferId, record] of state.browserTransfers) {
if (record.expiresAtMs <= currentTime) {
retireBrowserTransfer(state, transferId, record);
}
}
}
function prepareBrowserTransfer(state, route) {
purgeExpiredBrowserTransfers(state);
if (state.browserTransfers.size >= state.maxPendingBrowserTransfers) {
throw Object.assign(new Error("Too many pending browser transfers."), {
statusCode: 429,
});
}
let transferId;
for (let attempt = 0; attempt < 4; attempt += 1) {
const candidate = browserBootstrapToken(state.browserBootstrapRandomBytes);
if (!state.browserTransferIds.has(candidate)) {
transferId = candidate;
break;
}
}
if (!transferId) throw new Error("Browser launch could not be completed.");
const secret = browserBootstrapToken(state.browserBootstrapRandomBytes);
const expiresAtMs = state.browserBootstrapNow() + state.browserTransferTtlMs;
if (!Number.isSafeInteger(expiresAtMs)) {
throw new Error("Browser launch could not be completed.");
}
state.browserTransferIds.add(transferId);
const record = {
route,
digest: digestBrowserBootstrapSecret(secret),
expiresAtMs,
disposed: false,
timer: null,
};
try {
record.timer = state.setBrowserBootstrapTimer(() => {
retireBrowserTransfer(state, transferId, record);
}, Math.max(1, expiresAtMs - state.browserBootstrapNow()));
record.timer?.unref?.();
state.browserTransfers.set(transferId, record);
return { transferId, secret };
} catch (error) {
state.browserTransferIds.delete(transferId);
disposeBrowserTransfer(state, record);
throw error;
}
}
async function prepareBrowserBootstrap(state, route) {
if (!BROWSER_BOOTSTRAP_ROUTES.has(route)) {
throw Object.assign(new Error("Browser launch route is invalid."), { statusCode: 400 });
}
purgeExpiredBrowserBootstraps(state);
if (
state.browserBootstraps.size + state.browserBootstrapReservations >=
state.maxPendingBrowserBootstraps
) {
throw Object.assign(new Error("Too many pending browser launches."), { statusCode: 429 });
}
let ticketId;
for (let attempt = 0; attempt < 4; attempt += 1) {
const candidate = browserBootstrapToken(state.browserBootstrapRandomBytes);
if (!state.browserBootstrapIds.has(candidate)) {
ticketId = candidate;
break;
}
}
if (!ticketId) throw new Error("Browser launch could not be prepared.");
const secret = browserBootstrapToken(state.browserBootstrapRandomBytes);
const expiresAtMs = state.browserBootstrapNow() + state.browserBootstrapTtlMs;
if (!Number.isSafeInteger(expiresAtMs)) throw new Error("Browser launch could not be prepared.");
state.browserBootstrapIds.add(ticketId);
state.browserBootstrapReservations += 1;
let handoff;
try {
handoff = await state.createBrowserHandoff({
origin: state.origin,
route,
ticketId,
secret,
privateRoot: state.browserHandoffRoot,
});
if (
state.closing || !handoff || !isPrivateBrowserLaunchUrl(handoff.launchUrl) ||
typeof handoff.dispose !== "function"
) throw new Error("Browser launch could not be prepared.");
const record = {
route,
digest: digestBrowserBootstrapSecret(secret),
createdAtMs: state.browserBootstrapNow(),
expiresAtMs,
handoff,
disposed: false,
timer: null,
};
record.timer = state.setBrowserBootstrapTimer(() => {
retireBrowserBootstrap(state, ticketId, record);
}, Math.max(1, expiresAtMs - state.browserBootstrapNow()));
record.timer?.unref?.();
state.browserBootstraps.set(ticketId, record);
return handoff.launchUrl;
} catch (error) {
state.browserBootstrapIds.delete(ticketId);
if (handoff?.dispose) {
try { await handoff.dispose(); } catch { /* Preserve the prepare failure. */ }
}
throw error;
} finally {
state.browserBootstrapReservations -= 1;
}
}
function genericBrowserBootstrapFailure(state) {
try { enforceRateLimit(state, "browser-bootstrap"); } catch (error) { return error; }
return Object.assign(new Error("Browser launch could not be verified."), { statusCode: 401 });
}
function genericBrowserTransferFailure(state) {
try { enforceRateLimit(state, "browser-transfer"); } catch (error) { return error; }
return Object.assign(new Error("Browser transfer could not be verified."), {
statusCode: 401,
});
}
function consumeBrowserBootstrap(state, body) {
if (
!exactObjectKeys(body, ["route", "secret", "ticketId"]) ||
!BROWSER_BOOTSTRAP_ROUTES.has(body.route) ||
!BROWSER_BOOTSTRAP_TOKEN_PATTERN.test(body.ticketId) ||
!BROWSER_BOOTSTRAP_TOKEN_PATTERN.test(body.secret)
) throw genericBrowserBootstrapFailure(state);
const record = state.browserBootstraps.get(body.ticketId);
if (!record) throw genericBrowserBootstrapFailure(state);
if (record.expiresAtMs <= state.browserBootstrapNow()) {
retireBrowserBootstrap(state, body.ticketId, record);
throw genericBrowserBootstrapFailure(state);
}
const actualDigest = digestBrowserBootstrapSecret(body.secret);
if (
record.route !== body.route || actualDigest.length !== record.digest.length ||
!timingSafeEqual(actualDigest, record.digest)
) throw genericBrowserBootstrapFailure(state);
if (!retireBrowserBootstrap(state, body.ticketId, record)) {
throw genericBrowserBootstrapFailure(state);
}
return record.route;
}
function consumeBrowserTransfer(state, body) {
if (
!exactObjectKeys(body, ["route", "secret", "transferId"]) ||
!BROWSER_BOOTSTRAP_ROUTES.has(body.route) ||
!BROWSER_BOOTSTRAP_TOKEN_PATTERN.test(body.transferId) ||
!BROWSER_BOOTSTRAP_TOKEN_PATTERN.test(body.secret)
) throw genericBrowserTransferFailure(state);
const record = state.browserTransfers.get(body.transferId);
if (!record) throw genericBrowserTransferFailure(state);
if (record.expiresAtMs <= state.browserBootstrapNow()) {
retireBrowserTransfer(state, body.transferId, record);
throw genericBrowserTransferFailure(state);
}
const actualDigest = digestBrowserBootstrapSecret(body.secret);
if (
record.route !== body.route || actualDigest.length !== record.digest.length ||
!timingSafeEqual(actualDigest, record.digest)
) throw genericBrowserTransferFailure(state);
if (!retireBrowserTransfer(state, body.transferId, record)) {
throw genericBrowserTransferFailure(state);
}
return state.sessionToken;
}
function readFormBody(request) {
const contentType = request.headers["content-type"];
if (
typeof contentType !== "string" ||
contentType.split(";", 1)[0].trim().toLowerCase() !==
"application/x-www-form-urlencoded"
) return Promise.reject(new Error("Browser launch could not be verified."));
return new Promise((resolveBody, rejectBody) => {
const chunks = [];
let bytes = 0;
request.on("data", (chunk) => {
bytes += chunk.length;
if (bytes <= 2_048) chunks.push(Buffer.from(chunk));
});
request.once("error", () => rejectBody(new Error("Browser launch could not be verified.")));
request.once("end", () => {
if (bytes > 2_048) {
rejectBody(new Error("Browser launch could not be verified."));
return;
}
try {
const params = new URLSearchParams(Buffer.concat(chunks).toString("utf8"));
const keys = [...params.keys()].sort();
if (keys.join("\0") !== ["route", "secret", "ticketId"].sort().join("\0")) {
throw new TypeError();
}
resolveBody({
route: params.get("route"),
secret: params.get("secret"),
ticketId: params.get("ticketId"),
});
} catch {
rejectBody(new Error("Browser launch could not be verified."));
}
});
});
}
function sendBrowserBootstrapHtml(response, state, route) {
const transfer = prepareBrowserTransfer(state, route);
const nonce = browserBootstrapToken(state.browserBootstrapRandomBytes);
const envelope = `relmio-v1.${transfer.transferId}.${transfer.secret}`;
const script = `window.name = ${JSON.stringify(envelope)}; window.location.replace(${JSON.stringify(route)});`;
const contents = `<!doctype html><html><head><meta charset="utf-8"><title>Opening Relmio</title></head><body><script nonce="${nonce}">${script}</script></body></html>`;
response.setHeader(
"Content-Security-Policy",
`default-src 'none'; script-src 'nonce-${nonce}'; base-uri 'none'; frame-ancestors 'none'; form-action 'none'`,
);
response.writeHead(200, {
"Content-Type": "text/html; charset=utf-8",
"Content-Length": Buffer.byteLength(contents),
"Cache-Control": "no-store",
});
response.end(contents);
}
function requireApiToken(request, state) {
if (!tokenMatches(request.headers["x-setup-token"], state.sessionToken)) {
throw Object.assign(new Error("Unauthorized."), { statusCode: 401 });
}
}
function requireSameOrigin(request, state) {
if (request.method === "POST" && request.headers.origin !== state.origin) {
throw Object.assign(new Error("Cross-origin request rejected."), {
statusCode: 403,
});
}
}
function enforceRateLimit(state, key) {
const now = Date.now();
const previous = state.rateLimits.get(key) ?? [];
const recent = previous.filter((time) => now - time < RATE_LIMIT_WINDOW_MS);
if (recent.length >= RATE_LIMIT_MAX) {
throw Object.assign(
new Error("Too many attempts. Wait a few minutes and try again."),
{ statusCode: 429 },
);
}
recent.push(now);
state.rateLimits.set(key, recent);
}
function waitForBoundedResult(promise, milliseconds) {
if (!promise) {
return Promise.resolve(true);
}
return new Promise((resolvePromise) => {
let settled = false;
const timer = setTimeout(() => {
if (!settled) {
settled = true;
resolvePromise(false);
}
}, milliseconds);
Promise.resolve(promise).then(
() => {
if (!settled) {
settled = true;
clearTimeout(timer);
resolvePromise(true);
}
},
() => {
if (!settled) {
settled = true;
clearTimeout(timer);
resolvePromise(true);
}
},
);
});
}
function readJsonBody(request) {
return new Promise((resolve, reject) => {
const chunks = [];
let bytes = 0;
let tooLarge = false;
request.on("data", (chunk) => {
bytes += chunk.length;
if (bytes > MAX_BODY_BYTES) {
tooLarge = true;
return;
}
chunks.push(Buffer.from(chunk));
});
request.once("error", () => {
reject(new Error("The request body could not be read."));
});
request.once("end", () => {
if (tooLarge) {
reject(
Object.assign(new Error("The request body is too large."), {
statusCode: 413,
}),
);
return;
}
try {
const text = Buffer.concat(chunks).toString("utf8");
resolve(text === "" ? {} : JSON.parse(text));
} catch {
reject(
Object.assign(new Error("The request body must be valid JSON."), {
statusCode: 400,
}),
);
}
});
});
}
function requireConnection(state) {
if (!state.connection) {
throw new Error("Connect to the VPS first.");
}
return state.connection;
}
function requireFullVpsScope(connection) {
if (connection?.scope !== "vps" || connection.identity?.privilege !== "root" ||
connection.identity?.loginUid !== 0 || connection.identity?.effectiveUid !== 0) {
throw Object.assign(new Error(
"This SSH session is local-model-only. OAuth bridge, Assistant, SuperGrok and Claude VPS operations require a verified root session. Disconnect and reconnect using a UID 0 account; local ChatGPT sign-in remains available.",
), { statusCode: 403 });
}
}
const VPS_IDENTITY_FIELDS = Object.freeze(["host", "port", "fingerprint", "username", "authentication",
"privilege", "loginUid", "effectiveUid"]);
const VPS_OWNER_TARGET_FIELDS = Object.freeze([...VPS_IDENTITY_FIELDS, "containerName", "networkName"]);
function requireReviewedVpsOwnerTarget(state, containerName, networkName) {
const prior = state.vpsOwnerTargetReview;
const current = { ...state.connectionIdentity, containerName, networkName };
if (!prior || prior.expiresAt <= Date.now() ||
!VPS_OWNER_TARGET_FIELDS.every((key) => prior.reviewedTarget[key] === current[key])) {
throw Object.assign(new Error("Review this VPS owner and destination again."),
{ statusCode: 409, recovery: "review-again" });
}
return prior.reviewedTarget;
}
const CODEX_IMAGES_VERIFICATION_URL = "https://auth.openai.com/codex/device";
const CODEX_IMAGES_STATES = new Set(["off", "pending", "signed-in", "reauthorize", "unavailable"]);
// The image sign-out that local SIWC sign-out and removal run first; "unknown" means it could not run.
const IMAGES_REVOCATIONS = new Set(["confirmed", "unconfirmed", "not-applicable", "unknown"]);
const VPS_MODEL_STATES = new Set(["verified", "failed", "unchecked"]);
const VPS_MODEL_CATALOG_ERRORS = new Set(["catalog_unavailable", "registration_unavailable"]);
const VPS_MODEL_STOP_REASONS = new Set(["usage_limit", "reauthorize", "probe_rejected", "checks_off", "time_limit",
"lease_unavailable"]);
const VPS_ADDON_TARGET_MS = 20 * 60_000;
// An add-on target stored by a status check serves only this connection until it expires.
function requireVpsAddonTarget(state, key, message, { pending = false } = {}) {
const target = state[key];
if (!target || target.expiresAt <= Date.now() || (pending && !target.pending) ||
!VPS_IDENTITY_FIELDS.every((field) => target.reviewedTarget[field] === state.connectionIdentity?.[field])) {
throw Object.assign(new Error(message), { statusCode: 409, recovery: "review-again" });
}
return target;
}
function boundedText(value, max) {
return typeof value === "string" && value.length > 0 && value.length <= max &&
!/[\u0000-\u001f\u007f]/u.test(value);
}
function isoTime(value) {
return typeof value === "string" && /^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}(?:\.\d{3})?Z$/u.test(value) &&
Number.isFinite(Date.parse(value));
}
// Only known add-on fields reach the browser; tokens, device IDs and sidecar output never do.
function copyCodexImagesStatus(result) {
const { state, account, pending } = result ?? {};
if (!CODEX_IMAGES_STATES.has(state) || !/^[a-f0-9]{64}$/u.test(result.containerId ?? "") ||
(account !== undefined && (!/^[A-Za-z0-9_-]{6}$/u.test(account?.accountIdSuffix ?? "") ||
(account.email !== undefined && !boundedText(account.email, 254)) ||
(account.planType !== undefined && !boundedText(account.planType, 32)))) ||
(state === "pending" && (!/^[A-Z0-9]{2,16}(?:-[A-Z0-9]{2,16}){0,3}$/u.test(pending?.userCode ?? "") ||
pending.verificationUrl !== CODEX_IMAGES_VERIFICATION_URL ||
!isoTime(pending.expiresAt)))) {
throw Object.assign(new Error("The sidecar returned an invalid image generation status."), { statusCode: 502 });
}
return {
state,
...(account === undefined ? {} : { account: {
...(account.email === undefined ? {} : { email: account.email }),
...(account.planType === undefined ? {} : { planType: account.planType }),
accountIdSuffix: account.accountIdSuffix,
} }),
...(state === "pending" ? { pending: { userCode: pending.userCode,
verificationUrl: CODEX_IMAGES_VERIFICATION_URL, expiresAt: pending.expiresAt } } : {}),
...(["declined", "expired"].includes(result.outcome) ? { outcome: result.outcome } : {}),
...(["confirmed", "unconfirmed", "not-applicable"].includes(result.revocation)
? { revocation: result.revocation } : {}),
};
}
function validVpsModelRow(model) {
return typeof model?.id === "string" && /^[A-Za-z0-9][A-Za-z0-9_.:-]{0,127}$/u.test(model.id) &&
boundedText(model.display_name, 256) && VPS_MODEL_STATES.has(model.state) && typeof model.listed === "boolean" &&
(model.checkedAt === undefined || isoTime(model.checkedAt));
}
function validVpsModelRun(run) {
const count = (value) => Number.isInteger(value) && value >= 0 && value <= 64;
return run === undefined || (count(run?.checked) && count(run.verified) && count(run.failed) &&
(run.stoppedReason === undefined || VPS_MODEL_STOP_REASONS.has(run.stoppedReason)));
}
// Only the validated catalog view reaches the browser; tokens, upstream text and remote output never do.
function copyVpsModelsStatus(result) {
const { state, catalogError, lastRun, models } = result ?? {};
if (!["available", "unavailable"].includes(state) || !/^[a-f0-9]{64}$/u.test(result.containerId ?? "") ||
(state === "available" && (typeof result.checksEnabled !== "boolean" ||
typeof result.clientVersion !== "string" || !/^\d{1,4}\.\d{1,4}\.\d{1,4}$/u.test(result.clientVersion) ||
(result.catalogCheckedAt !== null && !isoTime(result.catalogCheckedAt)) ||
(catalogError !== undefined && !VPS_MODEL_CATALOG_ERRORS.has(catalogError)) || !validVpsModelRun(lastRun) ||
!Array.isArray(models) || models.length > 64 || !models.every(validVpsModelRow)))) {
throw Object.assign(new Error("The VPS returned an invalid model status."), { statusCode: 502 });
}
if (state === "unavailable") return { state };
return {
state, checksEnabled: result.checksEnabled, clientVersion: result.clientVersion,
catalogCheckedAt: result.catalogCheckedAt,
...(catalogError === undefined ? {} : { catalogError }),
...(lastRun === undefined ? {} : { lastRun: { checked: lastRun.checked, verified: lastRun.verified,
failed: lastRun.failed, ...(lastRun.stoppedReason === undefined ? {} : { stoppedReason: lastRun.stoppedReason }) } }),
models: models.map((model) => ({ id: model.id, display_name: model.display_name, state: model.state,
listed: model.listed, ...(model.checkedAt === undefined ? {} : { checkedAt: model.checkedAt }) })),
};
}
function credentialBearingVpsRoute(path) {
return path === "/api/plan" || path === "/api/install" ||
path === "/api/siwc/vps/status" || path === "/api/siwc/vps/manage" ||
path === "/api/siwc/vps/inspect-stopped" || path.startsWith("/api/siwc/vps/recovery/") ||
path === "/api/siwc/vps/runtime-update/review" || path === "/api/siwc/vps/runtime-update/apply" ||
path.startsWith("/api/siwc/vps/images/") || path.startsWith("/api/siwc/vps/models/") || path === "/api/siwc/vps/usage/status" ||
path === "/api/assistant/plan" || path === "/api/assistant/install" ||
path.startsWith("/api/vps/supergrok/") || path.startsWith("/api/vps/claude/");
}
function validatedSshRequest(body) {
requireExactRequestBody(body, [
"host", "port", "username", "expectedFingerprint", "useAgent", "privilege",
...(body?.useAgent === false ? ["password"] : []),
], "The SSH connection request must contain only the selected authentication fields.");
if (typeof body.useAgent !== "boolean" || !["root", "sudo-n"].includes(body.privilege)) {
throw new Error("Choose SSH password or local agent authentication and an explicit administrative privilege.");
}
if (!body.useAgent && (typeof body.password !== "string" || !body.password || body.password.length > 16384)) {
throw new Error("Enter the SSH password for the selected account.");
}
if (typeof body.expectedFingerprint !== "string" || !/^SHA256:[A-Za-z0-9+/]{43}$/u.test(body.expectedFingerprint)) {
throw new Error("Confirm a valid SSH host fingerprint first.");
}
return {
host: validateHostname(body.host), port: validatePort(body.port),
username: validateUsername(body.username), expectedFingerprint: body.expectedFingerprint,
useAgent: body.useAgent, privilege: body.privilege,
...(body.useAgent ? {} : { password: body.password }),
};
}
function verifiedConnectionIdentity(connection, request, generation) {
const identity = connection?.identity;
const scope = request.privilege === "root" ? "vps" : "local-model-only";
if (!identity || connection.scope !== scope ||
identity.host !== request.host || identity.port !== request.port ||
identity.fingerprint !== request.expectedFingerprint || identity.username !== request.username ||
identity.authentication !== (request.useAgent ? "agent" : "password") ||
identity.privilege !== request.privilege || identity.effectiveUid !== 0 ||
!Number.isSafeInteger(identity.loginUid) || identity.loginUid < 0 ||
(request.privilege === "root" && identity.loginUid !== 0)) {
throw new Error("The authenticated SSH identity does not match the requested administrative session. Reconnect and verify it again.");
}
return Object.freeze({
host: identity.host, port: identity.port, fingerprint: identity.fingerprint,
username: identity.username, authentication: identity.authentication,
privilege: identity.privilege, loginUid: identity.loginUid, effectiveUid: 0, scope, generation,
});
}
function oauthCredentialChangeInFlight(state) {
return (
state.oauthLoginStartInFlight ||
state.oauthCredentialOperation !== null ||
state.oauthLogin?.status === "pending" ||
state.oauthRetryBlocked ||
state.oauthLogin?.retryBlocked === true
);
}
function oauthCredentialOperationInFlight(state) {
return Boolean(
state.oauthLoginStartInFlight ||
state.oauthCredentialOperation !== null ||
state.oauthLogin?.status === "pending"
);
}
function rejectActiveOAuthCredentialChange(state) {
if (state.oauthRetryBlocked || state.oauthLogin?.retryBlocked === true) {
throw Object.assign(
new Error(
"ChatGPT sign-in could not be confirmed safely. Restart Relmio before changing the VPS.",
),
{ retryBlocked: true, statusCode: 409 },
);
}
if (oauthCredentialChangeInFlight(state)) {
throw Object.assign(new Error(OAUTH_VPS_CONFLICT_MESSAGE), {
statusCode: 409,
});
}
}
function rejectActiveVpsOwner(state) {
if (state.vpsConnectionOperation !== null) {
throw Object.assign(
new Error("A VPS connection change is already in progress. Wait for it to finish before trying another VPS action."),
{ statusCode: 409 },
);
}
if (state.vpsMutationInFlight || state.vpsCredentialOperation !== null) {
throw Object.assign(
new Error(
"A VPS installation is already in progress. Wait for it to finish before trying another installation.",
),
{ statusCode: 409 },
);
}
}
function rejectActiveVpsMutation(state) {
if (state.closing) {
throw Object.assign(new Error("The local wizard is closing."), {
statusCode: 409,
});
}
rejectActiveVpsOwner(state);
rejectActiveOAuthCredentialChange(state);
}
function advanceVpsLifecycleGeneration(state) {
state.vpsLifecycleGeneration += 1;
return state.vpsLifecycleGeneration;
}
function invalidateVpsPlans(state) {
state.vpsOwnerTargetReview = null;
state.siwcRecoveryReview = null;
state.vpsRuntimeUpdateReview = null;
state.vpsImagesTarget = null;
state.vpsModelsTarget = null;
state.sidecarPlan = null;
state.assistantPlan = null;
invalidateSuperGrokPlan(state);
invalidateClaudePlan(state);
invalidateLocalModelVpsPlan(state);
}
function invalidateSuperGrokPlan(state) {
state.supergrokPlan = null;
state.supergrokPlanGeneration += 1;
}
function invalidateClaudePlan(state) {
state.claudePlan = null;
state.claudePlanGeneration += 1;
}
function invalidateLocalModelVpsPlan(state) {
state.localModelVpsPlan = null;
state.localModelVpsPlanGeneration += 1;
}
function beginVpsDiscoveryRefresh(state) {
invalidateVpsPlans(state);
state.vpsDiscoveryGeneration += 1;
return state.vpsDiscoveryGeneration;
}
function beginSuperGrokPlanReview(state) {
invalidateVpsPlans(state);
return state.supergrokPlanGeneration;
}
function beginClaudePlanReview(state) {
invalidateVpsPlans(state);
return state.claudePlanGeneration;
}
function beginLocalModelVpsReview(state) {
invalidateVpsPlans(state);
return state.localModelVpsPlanGeneration;
}
function closeVpsConnectionBestEffort(connection) {
try {
connection?.close?.();
} catch {
// Shared state is detached first, so a local close failure cannot make the
// stale connection or its reviewed plans reusable.
}
}
function clearVpsConnectionIdleExpiry(state, connection = null) {
const expiry = state.vpsConnectionIdleExpiry;
if (!expiry || (connection !== null && expiry.connection !== connection)) {
return;
}
clearTimeout(expiry.timer);
state.vpsConnectionIdleExpiry = null;
}
function vpsConnectionHasActiveUse(state, connection) {
for (const use of state.vpsConnectionUses) {
if (use.connection === connection) return true;
}
return false;
}
function retireVpsConnectionUses(state, connection) {
for (const use of state.vpsConnectionUses) {
if (use.connection === connection) {
state.vpsConnectionUses.delete(use);
}
}
}
function armVpsConnectionIdleExpiry(state, connection = state.connection) {
if (
state.closing ||
!connection ||
state.connection !== connection ||
vpsConnectionHasActiveUse(state, connection)
) {
return;
}
clearVpsConnectionIdleExpiry(state);
const expiry = { connection, timer: null };
expiry.timer = setTimeout(() => {
if (state.vpsConnectionIdleExpiry !== expiry) return;
state.vpsConnectionIdleExpiry = null;
if (state.closing || state.connection !== connection) return;
if (
vpsConnectionHasActiveUse(state, connection) ||
state.vpsFingerprintOperation !== null ||
state.vpsConnectionOperation !== null ||
state.vpsMutationInFlight ||
state.vpsCredentialOperation !== null
) {
armVpsConnectionIdleExpiry(state, connection);
return;
}
detachVpsConnection(state, connection, { clearScannedHost: true });
}, state.vpsConnectionIdleMs);
expiry.timer.unref?.();
state.vpsConnectionIdleExpiry = expiry;
}
function acquireVpsConnectionUse(state) {
const connection = requireConnection(state);
const use = { connection, token: Symbol("vps-connection-use") };
clearVpsConnectionIdleExpiry(state, connection);
state.vpsConnectionUses.add(use);
let released = false;
return {
connection,
release() {
if (released) return;
released = true;
state.vpsConnectionUses.delete(use);
if (state.connection === connection) {
armVpsConnectionIdleExpiry(state, connection);
}
},
};
}
function detachVpsConnection(
state,
connection = state.connection,
{ clearScannedHost = false } = {},
) {
if (state.connection === connection) {
clearVpsConnectionIdleExpiry(state, connection);
retireVpsConnectionUses(state, connection);
state.connection = null;
state.connectionIdentity = null;
advanceVpsLifecycleGeneration(state);
state.discovery = null;
state.networksByContainer.clear();
invalidateVpsPlans(state);
if (clearScannedHost) {
state.scannedHost = null;
}
}
closeVpsConnectionBestEffort(connection);
}
function rejectUnsafeVpsDisconnect(state) {
if (state.closing) {
throw Object.assign(new Error("The local wizard is closing."), {
statusCode: 409,
});
}
rejectActiveVpsOwner(state);
if (state.vpsFingerprintOperation !== null) {
throw Object.assign(
new Error("A VPS identity scan is already in progress. Wait for it to finish before disconnecting."),
{ statusCode: 409 },
);
}
if (oauthCredentialOperationInFlight(state)) {
throw Object.assign(new Error(OAUTH_VPS_CONFLICT_MESSAGE), {
statusCode: 409,
});
}
}
function requireUnchangedVpsSession(
state,
snapshot,
{ allowCredentialSnapshot = false } = {},
) {
if (allowCredentialSnapshot) {
if (state.closing) {
throw Object.assign(new Error("The local wizard is closing."), {
statusCode: 409,
});
}
} else {
rejectActiveVpsMutation(state);
}
if (
state.connection !== snapshot.connection ||
state.vpsLifecycleGeneration !== snapshot.lifecycleGeneration ||
(
snapshot.discoveryGeneration !== undefined &&
state.vpsDiscoveryGeneration !== snapshot.discoveryGeneration
)
) {
throw Object.assign(
new Error("The VPS session changed while this request was running. Try the VPS action again."),
{ statusCode: 409 },
);
}
}
function beginVpsFingerprintScan(state) {
rejectActiveVpsMutation(state);
const operation = {
lifecycleGeneration: state.vpsLifecycleGeneration,
scanGeneration: state.vpsFingerprintGeneration + 1,
token: Symbol("vps-fingerprint-scan"),
};
state.vpsFingerprintGeneration = operation.scanGeneration;
state.vpsFingerprintOperation = operation;
state.scannedHost = null;
return operation;
}
function requireCurrentVpsFingerprintScan(state, operation) {
if (
state.closing ||
state.vpsFingerprintOperation?.token !== operation.token ||
state.vpsFingerprintGeneration !== operation.scanGeneration ||
state.vpsLifecycleGeneration !== operation.lifecycleGeneration
) {
throw Object.assign(
new Error("The VPS identity scan changed while this request was running. Check the latest identity result."),
{ statusCode: 409 },
);
}
rejectActiveVpsMutation(state);
}
function releaseVpsFingerprintScan(state, operation) {
if (state.vpsFingerprintOperation?.token === operation.token) {
state.vpsFingerprintOperation = null;
}
}
function acquireVpsConnectionOperation(state) {
rejectActiveVpsMutation(state);
if (state.vpsFingerprintOperation !== null) {
throw Object.assign(
new Error("A VPS identity scan is already in progress. Wait for it to finish before connecting."),
{ statusCode: 409 },
);
}
const operation = {
lifecycleGeneration: state.vpsLifecycleGeneration,
token: Symbol("vps-connection-operation"),
};
state.vpsConnectionOperation = operation;
return {
operation,
release() {
if (state.vpsConnectionOperation?.token === operation.token) {
state.vpsConnectionOperation = null;
}
},
};
}
function requireCurrentVpsConnectionOperation(state, operation) {
if (state.closing) {
throw Object.assign(new Error("The local wizard is closing."), {
statusCode: 409,
});
}
if (
state.vpsConnectionOperation?.token !== operation.token ||
state.vpsLifecycleGeneration !== operation.lifecycleGeneration
) {
throw Object.assign(
new Error("The VPS connection changed while this request was running. Try connecting again."),
{ statusCode: 409 },
);
}
}
function beginOAuthCredentialOperation(state) {
if (state.oauthCredentialOperation !== null) {
throw Object.assign(
new Error("A ChatGPT sign-in start is already in progress."),
{ statusCode: 409 },
);
}
const operation = {
phase: "starting",
token: Symbol("oauth-credential-operation"),
};
state.oauthCredentialOperation = operation;
return operation;
}
function setOAuthCredentialOperationPhase(state, operation, phase) {
if (state.oauthCredentialOperation?.token === operation?.token) {
operation.phase = phase;
}
}
function releaseOAuthCredentialOperation(state, operation) {
if (state.oauthCredentialOperation?.token === operation?.token) {
state.oauthCredentialOperation = null;
}
}
function acquireVpsCredentialOperation(state, phase) {
rejectActiveVpsMutation(state);
const operation = {
phase,
token: Symbol("vps-credential-operation"),
};
state.vpsCredentialOperation = operation;
return {
operation,
release() {
if (state.vpsCredentialOperation?.token === operation.token) {
state.vpsCredentialOperation = null;
}
},
};
}
function requireCurrentVpsCredentialOperation(state, operation) {
if (
state.closing ||
state.vpsCredentialOperation?.token !== operation.token
) {
throw Object.assign(new Error("The local wizard is closing."), {
statusCode: 409,
});
}
}
function acquireVpsMutationLock(state) {
const credentialOperation = acquireVpsCredentialOperation(
state,
"remote-mutation",
);
const lock = Symbol("vps-mutation");
let resolveCompletion;
state.vpsMutationInFlight = true;
state.vpsMutationLock = lock;
advanceVpsLifecycleGeneration(state);
invalidateVpsPlans(state);
state.vpsMutationCompletion = new Promise((resolve) => {
resolveCompletion = resolve;
});
return () => {
if (state.vpsMutationLock === lock) {
state.vpsMutationInFlight = false;
state.vpsMutationLock = null;
state.vpsMutationCompletion = null;
resolveCompletion();
}
credentialOperation.release();
};
}
function requireVpsPlanAuthStatus(account, message) {
if (!account || account.identity !== "verified" || account.session !== "connected" ||
account.planPermission !== "granted" || account.planEnabled !== true ||
account.ownership !== "owned") {
throw Object.assign(new Error(message), { statusCode: 409 });
}
return account;
}
function requireReviewedVpsPlan(plan, body, label) {
if (
!plan ||
typeof plan.planId !== "string" ||
!tokenMatches(body.planId, plan.planId) ||
plan.containerName !== body.containerName ||
plan.networkName !== body.networkName
) {
throw new Error(`Review a fresh ${label} plan before installing.`);
}
}
function requireAssistantSearxngSelection(value) {
if (typeof value !== "boolean") {
throw new Error("Choose whether to include optional SearXNG web search.");
}
return value;
}
function requireEnabledInstanceAi(state, containerName) {
const instanceAi = state.networksByContainer.get(containerName)?.instanceAi;
if (instanceAi?.status === "enabled") return instanceAi;
if (instanceAi?.status === "missing" || instanceAi?.status === "configured") {
throw new Error(
"The selected n8n container needs N8N_ENABLED_MODULES to include instance-ai. Update its existing deployment separately; restart n8n outside this wizard only if you later authorize that action.",
);
}
throw new Error("The selected n8n container's AI Assistant prerequisite could not be verified.");
}
function requireReviewedAssistantPlan(state, plan, body) {
requireReviewedVpsPlan(plan, body, "AI Assistant");
if (plan.includeSearxng !== body.includeSearxng) {
throw new Error("Review a fresh AI Assistant plan for the selected web-search option.");
}
const instanceAi = requireEnabledInstanceAi(state, body.containerName);
if (plan.instanceAi?.status !== instanceAi.status) {
throw new Error("Review a fresh AI Assistant plan after prerequisite discovery.");
}
}
function requireFreshAssistantInstallState(plan, networks, body) {
if (!Array.isArray(networks?.networks) || !networks.networks.includes(body.networkName)) {
throw new Error(
"The selected Docker network changed after plan review. Review a fresh AI Assistant plan before installing.",
);
}
const instanceAi = networks.instanceAi;
if (instanceAi?.status !== "enabled") {
throw new Error(
"The selected n8n container needs N8N_ENABLED_MODULES to include instance-ai. Update its existing deployment separately; restart n8n outside this wizard only if you later authorize that action.",
);
}
if (plan.instanceAi?.status !== instanceAi.status) {
throw new Error("Review a fresh AI Assistant plan after prerequisite discovery.");
}
}
// Read from discovery's marking of the selected network, so the plan makes no remote read. The
// sandbox reaches the internet through its own network; only SearXNG needs the n8n network for that.
function requireAssistantNetworkSupport(networks, networkName, includeSearxng) {
const support = networks?.support?.[networkName];
if (support?.supported !== false || (support.reason === "internal" && !includeSearxng)) return;
throw Object.assign(new Error(typeof support.message === "string" ? support.message
: "The selected Docker network cannot carry the AI Assistant companion. Choose another network, then check again."),
{ code: VPS_NETWORK_UNSUPPORTED });
}
// Plain review warnings. They inform the owner and never refuse the plan.
function assistantPlanWarnings(container) {
return container?.n8nSandboxPresent === true
? ["This n8n already runs its own sandbox. Installing the Assistant companion adds a second privileged runner on this server."]
: [];
}
function requireDiscoveredContainer(state, containerName) {
const container = state.discovery?.containers.find(
(candidate) => candidate.name === containerName,
);
if (!container) {
throw new Error("Select an n8n container found by this wizard.");
}
return container;
}
function requireDiscoveredNetwork(state, containerName, networkName) {
requireDiscoveredContainer(state, containerName);
const networks = state.networksByContainer.get(containerName)?.networks ?? [];
if (!networks.includes(networkName)) {
throw new Error("Select a Docker network found by this wizard.");
}
return networkName;
}
function safeErrorMessage(error) {
const message =
typeof error?.safeMessage === "string"
? error.safeMessage
: typeof error?.message === "string"
? error.message
: "Request failed.";
if (
message.length > 240 ||
/[\r\n]/u.test(message) ||
/(?:access|refresh)[_-]?token|private[_-]?key|\bsk-[A-Za-z0-9_-]{8,}|\bBearer\s+\S+|\/(?:Users|home|private|tmp|var|opt|docker)\/|[A-Za-z]:\\/iu.test(
message,
)
) {
return "The request could not be completed safely.";
}
return message;
}
const SIWC_RECOVERY = new Set([
"none", "retry-later", "reauthorize", "enable-plan", "manage-usage",
"fix-request", "fix-configuration", "review-again", "resolve-handoff",
]);
function safeFailure(error) {
const status = [error?.statusCode, error?.status, error?.upstream?.status]
.find((value) => Number.isInteger(value) && value >= 400 && value <= 599) ?? 400;
const phase = ["authorize", "refresh", "request", "handoff", "revoke"]
.includes(error?.phase) ? error.phase : "request";
const source = error?.upstream?.body?.error ?? error?.error;
const identifier = (value, pattern = /^[A-Za-z0-9_.:\[\]-]{1,128}$/u) =>
typeof value === "string" && pattern.test(value) ? value : undefined;
const code = identifier(error?.code) ?? identifier(source?.code);
const param = identifier(error?.param) ?? identifier(source?.param);
const requestId = identifier(error?.requestId ?? error?.upstream?.requestId);
const recovery = SIWC_RECOVERY.has(error?.recovery) ? error.recovery
: code === "subscription_sharing_usage_limit_exceeded" ? "manage-usage"
: status === 503 ? "retry-later"
: status === 400 ? "fix-request"
: status === 401 ? "reauthorize" : "none";
const body = phase === "request" && error?.upstream?.body?.detail !== undefined
? { detail: safeErrorMessage({ safeMessage: error.upstream.body.detail }) }
: phase === "request" && source && typeof source === "object"
? { error: {
message: safeErrorMessage({ safeMessage: source.message }),
...(code ? { code } : {}),
...(param ? { param } : {}),
...(identifier(source.type) ? { type: source.type } : {}),
} }
: undefined;
return {
error: safeErrorMessage(error), status, phase, recovery,
...(code ? { code } : {}), ...(param ? { param } : {}),
...(requestId ? { requestId } : {}),
...(body ? { upstream: { status, body, ...(requestId ? { requestId } : {}) } } : {}),
...(error?.retryBlocked === true ? { retryBlocked: true } : {}),
...(error?.remoteOutcomeUnknown === true ? { remoteOutcomeUnknown: true } : {}),
};
}
function safeInstallFailure(value, phase) {
if (!value || typeof value !== "object" || Array.isArray(value) ||
!Number.isInteger(value.status) || value.status < 400 || value.status > 599 ||
typeof value.error !== "string" || value.error.length === 0) {
throw Object.assign(new Error("The installed runtime failure report is invalid."), { statusCode: 502 });
}
return safeFailure({
message: value.error, status: value.status, phase,
code: value.code, param: value.param, requestId: value.requestId,
recovery: value.recovery, upstream: value.upstream,
});
}
function copySiwcReadiness(result) {
const runtimeState = result?.runtimeState ?? "running";
const finalization = result?.finalizationFailure;
if (!["verified", "unverified"].includes(result?.readiness) ||
!["running", "unknown", "stopped"].includes(runtimeState) ||
(finalization && (result.deploymentMode !== "partial" ||
typeof finalization.error !== "string" || !finalization.error ||
!SIWC_RECOVERY.has(finalization.recovery))) ||
(result.readiness === "verified" &&
(result.catalogFailure || result.runtimeFailure || finalization || runtimeState !== "running")) ||
(result.readiness === "unverified" && !finalization &&
(Boolean(result.catalogFailure) === Boolean(result.runtimeFailure) ||
(runtimeState !== "running") !== Boolean(result.runtimeFailure) ||
result.models?.length !== 0))) {
throw Object.assign(new Error("The installed account readiness is invalid."), { statusCode: 502 });
}
return {
readiness: result.readiness, runtimeState,
...(finalization ? { finalizationFailure: {
error: safeErrorMessage({ safeMessage: finalization.error }), recovery: finalization.recovery,
} } : {}),
...(result.catalogFailure ? { catalogFailure: safeInstallFailure(result.catalogFailure, "request") } : {}),
...(result.runtimeFailure ? { runtimeFailure: safeInstallFailure(result.runtimeFailure, "handoff") } : {}),
};
}
async function cancelOAuthLogin(state, login) {
try {
await login.attempt.cancel();
} catch {
if (state.oauthLogin === login) {
login.status = "error";
login.retryBlocked = true;
login.error =
"ChatGPT sign-in could not be stopped safely. Close the sign-in helper, then restart Relmio.";
}
state.oauthRetryBlocked = true;
state.oauthStartupError =
"ChatGPT sign-in could not be stopped safely. Close the sign-in helper, then restart Relmio.";
throw Object.assign(
new Error(
"ChatGPT sign-in could not be stopped safely. Close the sign-in helper, then restart Relmio.",
),
{ retryBlocked: true, statusCode: 409 },
);
}
if (state.oauthLogin === login && login.status === "pending") {
login.status = "cancelled";
}
releaseOAuthCredentialOperation(state, login.credentialOperation);
}
// Wizard pages and assets served from src/ui, keyed by request path. Entries
// without an encoding are binary and stay Buffers. Pages get the package
// version substituted for __RELMIO_PACKAGE_VERSION__.
const UI_FILE_SOURCES = Object.freeze({
"/": ["../ui/index.html", "utf8"],
"/local": ["../ui/local.html", "utf8"],
"/assistant": ["../ui/assistant.html", "utf8"],
"/supergrok-vps": ["../ui/supergrok-vps.html", "utf8"],
"/claude-vps": ["../ui/claude-vps.html", "utf8"],
"/local-model-vps": ["../ui/local-model-vps.html", "utf8"],
"/hosting": ["../ui/hosting.html", "utf8"],
"/app.js": ["../ui/app.js", "utf8"],
"/local.js": ["../ui/local.js", "utf8"],
"/device-code.js": ["../ui/device-code.js", "utf8"],
"/siwc-controls.js": ["../ui/siwc-controls.js", "utf8"],
"/assistant.js": ["../ui/assistant.js", "utf8"],
"/supergrok-vps.js": ["../ui/supergrok-vps.js", "utf8"],
"/claude-vps.js": ["../ui/claude-vps.js", "utf8"],
"/local-model-vps.js": ["../ui/local-model-vps.js", "utf8"],
"/hosting.js": ["../ui/hosting.js", "utf8"],
"/chat-tester-feedback.js": ["../ui/chat-tester-feedback.js", "utf8"],
"/session.js": ["../ui/session.js", "utf8"],
"/session-bootstrap.js": ["../ui/session-bootstrap.js", "utf8"],
"/oauth-popup.js": ["../ui/oauth-popup.js", "utf8"],
"/theme.js": ["../ui/theme.js", "utf8"],
"/time.js": ["../ui/time.js", "utf8"],
"/topbar.js": ["../ui/topbar.js", "utf8"],
"/ssh-form.js": ["../ui/ssh-form.js", "utf8"],
"/hosting-archive.js": ["../ui/hosting-archive.js", "utf8"],
"/domain/hosting-providers.js": ["../domain/hosting-providers.js", "utf8"],
"/guide.js": ["../ui/guide.js", "utf8"],
"/guide-dock.js": ["../ui/guide-dock.js", "utf8"],
"/mascot.js": ["../ui/mascot.js", "utf8"],
"/guide/content-vps.js": ["../ui/guide/content-vps.js", "utf8"],
"/guide/content-local.js": ["../ui/guide/content-local.js", "utf8"],
"/guide/content-assistant.js": ["../ui/guide/content-assistant.js", "utf8"],
"/guide/content-supergrok-vps.js": ["../ui/guide/content-supergrok-vps.js", "utf8"],
"/guide/content-claude-vps.js": ["../ui/guide/content-claude-vps.js", "utf8"],
"/guide/content-local-model-vps.js": ["../ui/guide/content-local-model-vps.js", "utf8"],
"/guide/content-hosting.js": ["../ui/guide/content-hosting.js", "utf8"],
"/guide/errors.js": ["../ui/guide/errors.js", "utf8"],
"/guide/context.js": ["../ui/guide/context.js", "utf8"],
"/usage-panel.js": ["../ui/usage-panel.js", "utf8"],
"/relmio-ui.css": ["../ui/relmio-ui.css", "utf8"],
"/styles.css": ["../ui/styles.css", "utf8"],
"/local.css": ["../ui/local.css", "utf8"],
"/siwc.css": ["../ui/siwc.css", "utf8"],
"/assistant.css": ["../ui/assistant.css", "utf8"],
"/supergrok-vps.css": ["../ui/supergrok-vps.css", "utf8"],
"/claude-vps.css": ["../ui/claude-vps.css", "utf8"],
"/local-model-vps.css": ["../ui/local-model-vps.css", "utf8"],
"/hosting.css": ["../ui/hosting.css", "utf8"],
"/guide.css": ["../ui/guide.css", "utf8"],
"/guide-dock.css": ["../ui/guide-dock.css", "utf8"],
"/usage-panel.css": ["../ui/usage-panel.css", "utf8"],
"/relmio-icon-96.png": ["../ui/relmio-icon-96.png"],
"/relmio-icon-rounded.svg": ["../ui/relmio-icon-rounded.svg", "utf8"],
"/fonts/geist-latin.woff2": ["../ui/fonts/geist-latin.woff2"],
"/fonts/bricolage-grotesque-latin.woff2": ["../ui/fonts/bricolage-grotesque-latin.woff2"],
});
const UI_PAGES = new Set(["/", "/local", "/assistant", "/supergrok-vps", "/claude-vps", "/local-model-vps", "/hosting"]);
async function loadDefaultUiFiles() {
const entries = await Promise.all(
Object.entries(UI_FILE_SOURCES).map(async ([route, [path, encoding]]) => {
const contents = await readFile(new URL(path, import.meta.url), encoding);
return [
route,
UI_PAGES.has(route)
? contents.replaceAll("__RELMIO_PACKAGE_VERSION__", PACKAGE_VERSION)
: contents,
];
}),
);
return Object.fromEntries(entries);
}
function createSafeLocalPlan(plan) {
return {
target: plan.target,
label: plan.label,
bindHost: plan.bindHost,
port: plan.port,
endpoint: plan.endpoint,
protocol: plan.protocol,
upstreamAuth: plan.upstreamAuth,
browserClients: plan.browserClients,
experimental: plan.experimental,
managedPath: plan.managedPath,
...(plan.account ? { account: copySiwcAccountView(plan.account) } : {}),
...(plan.resumeRequired ? { resumeRequired: true, staging: copySiwcStaging(plan.staging) } : {}),
...(plan.migrationRequired ? { migrationRequired: true, legacyResourcesPreserved: true,
requiresMigrationConsent: true } : {}),
...(plan.replacementRequired ? { replacementRequired: true, oldSessionSignedOut: true,
oldHistoryRetained: true, requiresReplacementConsent: true } : {}),
};
}
function createSafeLocalInstallResult(result, plan) {
const chatgpt = ["codex-chatgpt", "codex-chat"].includes(result?.target);
const staged = result?.deploymentMode === "staged";
if (chatgpt && (typeof result.clientCredential !== "string" ||
!/^[A-Za-z0-9_-]{32,256}$/u.test(result.clientCredential) ||
result.credentialShownOnce !== true ||
!["staged", "installed", "migrated", "replaced", "partial"].includes(result.deploymentMode) ||
(result.deploymentMode === "migrated" && (result.migratedLegacy !== true || result.legacyRetained !== true)) ||
(result.deploymentMode === "replaced" && (result.replacedAccount !== true || result.oldHistoryRetained !== true)) ||
(!staged && (!result.account?.registrationId ||
(plan?.authBinding && result.account.registrationId !== plan.authBinding.registrationId))) ||
!Array.isArray(result.models) ||
result.models.some((model) => typeof model !== "string" ||
!/^[A-Za-z0-9_.:-]{1,128}$/u.test(model)) ||
(staged && result.models.length !== 0) ||
(result.deploymentMode === "partial" && !result.runtimeFailure && !result.finalizationFailure))) {
throw Object.assign(new Error("The local Codex installation result is invalid."), { statusCode: 502 });
}
const readiness = chatgpt && !staged ? copySiwcReadiness(result) : {};
return {
target: result.target, endpoint: result.endpoint, protocol: result.protocol,
clientCredential: result.clientCredential,
credentialShownOnce: result.credentialShownOnce === true,
models: Array.isArray(result.models) ? [...result.models] : [],
deploymentMode: result.deploymentMode,
experimental: result.experimental === true,
browserClients: result.browserClients === true,
...(result.account ? { account: copySiwcAccountView(result.account) } : {}),
...readiness,
...(result.migratedLegacy === true ? { migratedLegacy: true, legacyRetained: result.legacyRetained === true } : {}),
...(result.replacedAccount === true ? { replacedAccount: true, oldHistoryRetained: result.oldHistoryRetained === true } : {}),
};
}
function createSafeLocalActivationResult(result) {
return {
target: result.target,
endpoint: result.endpoint,
protocol: result.protocol,
models: Array.isArray(result.models) ? [...result.models] : [],
deploymentMode: result.deploymentMode,
experimental: result.experimental === true,
browserClients: result.browserClients === true,
};
}
function createSafeProjectMeta(result) {
return {
stars:
Number.isSafeInteger(result?.stars) && result.stars >= 0
? result.stars
: null,
version: PACKAGE_VERSION,
};
}
function createSafeLocalChatTestKey(result) {
if (
!result ||
typeof result !== "object" ||
Array.isArray(result) ||
typeof result.keyId !== "string" ||
result.keyId.length === 0 ||
result.keyId.length > 128 ||
!result.publicKeyJwk ||
typeof result.publicKeyJwk !== "object" ||
Array.isArray(result.publicKeyJwk) ||
result.publicKeyJwk.kty !== "RSA" ||
typeof result.publicKeyJwk.n !== "string" ||
typeof result.publicKeyJwk.e !== "string" ||
result.publicKeyJwk.n.length === 0 ||
result.publicKeyJwk.n.length > 1_024 ||
result.publicKeyJwk.e.length === 0 ||
result.publicKeyJwk.e.length > 32 ||
result.algorithm !== "RSA-OAEP-256" ||
typeof result.expiresAt !== "string" ||
Number.isNaN(Date.parse(result.expiresAt))
) {
throw Object.assign(new Error("The local tester could not start safely."), {
statusCode: 502,
});
}
return {
keyId: result.keyId,
publicKeyJwk: {
kty: "RSA",
n: result.publicKeyJwk.n,
e: result.publicKeyJwk.e,
},
algorithm: result.algorithm,
expiresAt: result.expiresAt,
};
}
function createSafeLocalChatTestResponse(result) {
if (
!result ||
typeof result !== "object" ||
Array.isArray(result) ||
typeof result.conversationId !== "string" ||
result.conversationId.length === 0 ||
result.conversationId.length > 160 ||
typeof result.output !== "string" ||
result.output.length === 0 ||
result.output.length > 12 * 1_024
) {
throw Object.assign(
new Error("The local adapter returned an unexpected response."),
{ statusCode: 502 },
);
}
return {
conversationId: result.conversationId,
output: result.output,
};
}
function getPendingLocalCredentialRotation(state) {
if (
state.localCredentialRotationPending &&
state.localCredentialRotationPending.expiresAt <= Date.now()
) {
state.localCredentialRotationPending = null;
}
return state.localCredentialRotationPending;
}
const SAFE_LOCAL_N8N_STACK_STATES = new Set([
"healthy",
"stopped",
"partial",
"unavailable",
]);
const LOCAL_DASHBOARD_SERVICE_DEFINITIONS = Object.freeze({
"codex-chatgpt": Object.freeze({
label: "Codex (ChatGPT plan)",
kind: "endpoint",
actions: new Set(["setup", "sign-out-chatgpt", "disable-chatgpt-plan", "inspect-stopped-chatgpt", "rotate-local-capability"]),
}),
"codex-chat": Object.freeze({
label: "Codex Chat adapter",
kind: "endpoint",
actions: new Set(["setup", "sign-out-chatgpt", "disable-chatgpt-plan", "inspect-stopped-chatgpt", "rotate-local-capability"]),
}),
"xai-grok-build": Object.freeze({
label: "SuperGrok",
kind: "endpoint",
actions: new Set(["setup", "sign-in-grok-build", "sign-out-grok-build", "rotate-local-capability"]),
}),
"local-n8n-stack": Object.freeze({
label: "Local n8n stack",
kind: "n8n-stack",
actions: new Set(["setup", "resume", "remove"]),
}),
"n8n-openai-oauth": Object.freeze({
label: "ChatGPT plan sidecar",
kind: "n8n-oauth-bridge",
actions: new Set(["setup", "sign-out-chatgpt", "disable-chatgpt-plan", "inspect-stopped-chatgpt", "remove"]),
}),
"local-n8n-assistant": Object.freeze({
label: "AI Assistant tools",
kind: "n8n-assistant",
actions: new Set(["setup", "remove"]),
}),
"n8n-supergrok-oauth": Object.freeze({
label: "SuperGrok for n8n",
kind: "n8n-supergrok",
actions: new Set([
"setup",
"sign-in-grok-build",
"sign-out-grok-build",
"remove-owned-supergrok",
]),
}),
"n8n-claude-oauth": Object.freeze({
label: "Claude for n8n", kind: "n8n-claude",
actions: new Set(["setup", "manage-claude", "remove"]),
}),
"n8n-local-model": Object.freeze({
label: "Local model for n8n",
kind: "n8n-local-model",
actions: new Set(["setup", "retry-model", "remove"]),
}),
});
const LOCAL_DASHBOARD_PROVIDER_DEFINITIONS = Object.freeze({
"codex-chatgpt": Object.freeze({
label: "ChatGPT",
authentication: "provider-oauth",
}),
"codex-chat": Object.freeze({
label: "ChatGPT",
authentication: "provider-oauth",
}),
"xai-grok-build": Object.freeze({
label: "SuperGrok",
authentication: "provider-oauth",
}),
"n8n-supergrok-oauth": Object.freeze({
label: "SuperGrok (n8n)",
authentication: "provider-oauth",
}),
"n8n-claude-oauth": Object.freeze({ label: "Claude (n8n)", authentication: "provider-oauth" }),
});
const LOCAL_DASHBOARD_STATES = new Set([
"absent",
"healthy",
"stopped",
"staged",
"partial",
"legacy",
"unavailable",
]);
function hasSafeExplicitLoopbackPort(value) {
const match = /^(?:http|ws):\/\/127\.0\.0\.1:(\d{1,5})(?:\/|$)/u.exec(value);
const port = Number(match?.[1]);
return Number.isInteger(port) && port >= 1 && port <= 65_535;
}
function requireSafeDashboardUrl(value, kind) {
if (typeof value !== "string" || value.length > 512) {
throw new TypeError("The local dashboard endpoint is invalid.");
}
let url;
try {
url = new URL(value);
} catch {
throw new TypeError("The local dashboard endpoint is invalid.");
}
if (
url.username !== "" ||
url.password !== "" ||
url.search !== "" ||
url.hash !== ""
) {
throw new TypeError("The local dashboard endpoint is invalid.");
}
const loopback = url.hostname === "127.0.0.1";
const valid =
(kind === "codex-chatgpt" && loopback && url.protocol === "ws:" && url.pathname === "/") ||
(["codex-chat", "xai-grok-build"].includes(kind) && loopback && url.protocol === "http:" && url.pathname === "/") ||
(kind === "n8n-local" && loopback && url.protocol === "http:" && url.pathname === "/") ||
(kind === "ngrok-public" && url.protocol === "https:" && url.pathname === "/");
const hasExpectedPort = kind === "ngrok-public"
? url.port === "" && value === url.origin
: hasSafeExplicitLoopbackPort(value);
if (!valid || !hasExpectedPort) {
throw new TypeError("The local dashboard endpoint is invalid.");
}
return value;
}
function createSafeDashboardSnapshot(target, snapshot) {
if (!snapshot || typeof snapshot !== "object" || Array.isArray(snapshot)) {
throw new TypeError("The local dashboard snapshot is invalid.");
}
if (["codex-chatgpt", "codex-chat", "xai-grok-build"].includes(target)) {
const chatgpt = target !== "xai-grok-build";
if (snapshot.target !== target || snapshot.auth?.disclosure !== "rotate-only" ||
typeof snapshot.auth.configured !== "boolean" ||
snapshot.canRotateCredential !== (chatgpt && snapshot.migrationRequired ? false : true) ||
(!chatgpt && snapshot.auth.configured !== true)) {
throw new TypeError("The local dashboard endpoint snapshot is invalid.");
}
const account = snapshot.auth.account ? copySiwcAccountView(snapshot.auth.account) : null;
if (chatgpt && (typeof snapshot.migrationRequired !== "boolean" ||
(snapshot.migrationRequired && (account || snapshot.registrationId !== undefined)) ||
(!snapshot.migrationRequired && (!snapshot.registrationId ||
(account && account.registrationId !== snapshot.registrationId))) ||
snapshot.auth.configured !== (account?.ownership === "owned" &&
account?.session === "connected" && account?.planEnabled === true &&
account?.planPermission === "granted"))) throw new TypeError("The SIWC endpoint ownership is invalid.");
if (snapshot.migrationState !== undefined &&
(!chatgpt || snapshot.migrationRequired !== true ||
!["incomplete", "prepared", "stopped"].includes(snapshot.migrationState) ||
snapshot.legacyResourcesPreserved !== true)) throw new TypeError("The SIWC migration status is invalid.");
return {
target,
endpoint: requireSafeDashboardUrl(snapshot.endpoint, target),
auth: { configured: snapshot.auth.configured, disclosure: "rotate-only",
...(account ? { account } : {}) },
...(chatgpt ? { migrationRequired: snapshot.migrationRequired,
...(snapshot.registrationId ? { registrationId: requireSiwcId(snapshot.registrationId) } : {}),
...(snapshot.migrationState ? { migrationState: snapshot.migrationState,
legacyResourcesPreserved: true } : {}) } : {}),
canRotateCredential: snapshot.canRotateCredential,
};
}
if (target === "n8n-openai-oauth") {
if (snapshot.target !== target ||
snapshot.endpoint !== "http://n8n-openai-oauth:10531/v1" ||
typeof snapshot.auth?.configured !== "boolean" ||
snapshot.auth.disclosure !== "server-managed" ||
snapshot.canRefreshCredential !== false || snapshot.canRemove !== true ||
typeof snapshot.migrationRequired !== "boolean") {
throw new TypeError("The local dashboard SIWC sidecar snapshot is invalid.");
}
const account = snapshot.auth.account ? copySiwcAccountView(snapshot.auth.account) : null;
if ((snapshot.migrationRequired && (account || snapshot.registrationId !== undefined)) ||
(!snapshot.migrationRequired && (!snapshot.registrationId ||
(account && account.registrationId !== snapshot.registrationId))) ||
snapshot.auth.configured !== (account?.ownership === "owned" &&
account?.session === "connected" && account?.planEnabled === true &&
account?.planPermission === "granted")) throw new TypeError("The sidecar owner state is invalid.");
if (snapshot.migrationState !== undefined &&
(snapshot.migrationRequired !== true ||
!["incomplete", "prepared", "stopped"].includes(snapshot.migrationState) ||
snapshot.legacyResourcesPreserved !== true)) throw new TypeError("The SIWC migration status is invalid.");
return {
target, endpoint: snapshot.endpoint,
auth: { configured: snapshot.auth.configured, disclosure: "server-managed",
...(account ? { account } : {}) },
...(snapshot.registrationId ? { registrationId: requireSiwcId(snapshot.registrationId) } : {}),
migrationRequired: snapshot.migrationRequired,
...(snapshot.migrationState ? { migrationState: snapshot.migrationState,
legacyResourcesPreserved: true } : {}),
canRefreshCredential: false, canRemove: true,
};
}
if (target === "local-n8n-assistant") {
if (
snapshot.target !== target ||
snapshot.components?.codeSandbox !== true ||
typeof snapshot.components?.searxng !== "boolean" ||
snapshot.auth?.sandboxConfigured !== true ||
snapshot.auth?.disclosure !== "one-time" ||
snapshot.canRemove !== true
) {
throw new TypeError("The local dashboard Assistant snapshot is invalid.");
}
return {
target,
components: {
codeSandbox: true,
searxng: snapshot.components.searxng,
},
auth: { sandboxConfigured: true, disclosure: "one-time" },
canRemove: true,
};
}
if (target === LOCAL_N8N_CLAUDE_TARGET) return createSafeLocalN8nClaudeSnapshot(snapshot);
if (target === LOCAL_N8N_SUPERGROK_TARGET) {
if (
snapshot.target !== LOCAL_N8N_SUPERGROK_TARGET ||
snapshot.endpoint !== LOCAL_N8N_SUPERGROK_ENDPOINT ||
snapshot.auth?.configured !== true ||
snapshot.auth?.disclosure !== "one-time" ||
snapshot.canRemove !== true
) {
throw new TypeError("The local dashboard SuperGrok snapshot is invalid.");
}
return {
target: LOCAL_N8N_SUPERGROK_TARGET,
endpoint: LOCAL_N8N_SUPERGROK_ENDPOINT,
auth: { configured: true, disclosure: "one-time" },
canRemove: true,
};
}
if (target === "n8n-local-model") {
if (
snapshot.target !== target ||
snapshot.endpoint !== "http://n8n-local-model:11434/v1" ||
!["missing", "downloading", "ready", "failed", "partial"].includes(snapshot.model?.state) ||
!["qwen3:0.6b", "qwen3:1.7b", "qwen3.5:2b", "qwen3.5:4b", "qwen3.5:9b"].includes(snapshot.model?.id) ||
(snapshot.model.digest !== null && !/^[a-f0-9]{64}$/u.test(snapshot.model.digest)) ||
snapshot.canRetry !== ["missing", "failed"].includes(snapshot.model.state) ||
snapshot.canRemove !== (snapshot.model.state !== "downloading") ||
(snapshot.model.state === "ready" && snapshot.model.digest === null)
) throw new TypeError("The local dashboard model snapshot is invalid.");
return {
target,
endpoint: snapshot.endpoint,
model: { state: snapshot.model.state, id: snapshot.model.id, digest: snapshot.model.digest },
canRetry: snapshot.canRetry,
canRemove: snapshot.canRemove,
};
}
if (target === "local-n8n-stack") {
const assistantModes = new Set(["disabled", "sandbox", "sandbox-with-searxng"]);
if (
snapshot.target !== target ||
!["none", "ngrok"].includes(snapshot.publicAccess) ||
!assistantModes.has(snapshot.assistantMode) ||
snapshot.components?.ngrok !== (snapshot.publicAccess === "ngrok") ||
typeof snapshot.components?.n8n !== "boolean" ||
typeof snapshot.components?.codeSandbox !== "boolean" ||
typeof snapshot.components?.searxng !== "boolean" ||
!/^relmio-local-n8n-[a-f0-9]{32}-n8n-1$/u.test(snapshot.n8nContainerName) ||
snapshot.networkName !== `${snapshot.n8nContainerName.slice(0, -6)}_edge` ||
!/^http:\/\/localhost:[1-9][0-9]{0,4}$/u.test(snapshot.localUrl) ||
snapshot.endpoints?.n8nLocal !== snapshot.localUrl ||
snapshot.endpoints?.ngrokPublic !== snapshot.ngrokPublicUrl ||
(snapshot.publicAccess === "none" && snapshot.ngrokPublicUrl !== null) ||
typeof snapshot.canResume !== "boolean" ||
snapshot.canRemove !== true
) throw new TypeError("The local dashboard n8n snapshot is invalid.");
return {
target,
publicAccess: snapshot.publicAccess,
localUrl: snapshot.localUrl,
ngrokPublicUrl: snapshot.publicAccess === "ngrok"
? requireSafeDashboardUrl(snapshot.ngrokPublicUrl, "ngrok-public") : null,
n8nContainerName: snapshot.n8nContainerName,
networkName: snapshot.networkName,
assistantMode: snapshot.assistantMode,
endpoints: {
n8nLocal: snapshot.localUrl,
ngrokPublic: snapshot.ngrokPublicUrl,
},
components: {
n8n: snapshot.components.n8n,
ngrok: snapshot.components.ngrok,
codeSandbox: snapshot.components.codeSandbox,
searxng: snapshot.components.searxng,
},
canResume: snapshot.canResume,
canRemove: true,
};
}
throw new TypeError("The local dashboard snapshot target is invalid.");
}
function createSafeDashboardProvider(target, provider) {
const definition = LOCAL_DASHBOARD_PROVIDER_DEFINITIONS[target];
if (
!definition ||
!provider ||
typeof provider !== "object" ||
Array.isArray(provider) ||
Object.keys(provider).length !== 4 ||
provider.target !== target ||
provider.label !== definition.label ||
provider.authentication !== "provider-oauth" ||
provider.readiness !== "runtime-owned"
) {
throw new TypeError("The local dashboard provider status is invalid.");
}
return { target, label: definition.label, authentication: "provider-oauth", readiness: "runtime-owned" };
}
function expectedDashboardActions({ definition, state, snapshot }) {
if (state === "absent" || state === "staged") return ["setup"];
if (state === "unavailable" || snapshot === null) return [];
const actions = [];
if (state === "legacy") return snapshot.migrationRequired === true ? ["setup"] : [];
if (state === "partial" && snapshot.migrationState) return [];
if (state === "stopped" && snapshot.registrationId &&
(["codex-chatgpt", "codex-chat"].includes(snapshot.target) ||
definition.kind === "n8n-oauth-bridge")) actions.push("inspect-stopped-chatgpt");
if (
definition.kind === "n8n-stack" &&
state === "stopped" &&
snapshot.canResume === true
) {
actions.push("resume");
}
if (
definition.kind === "endpoint" &&
state === "healthy" &&
snapshot.canRotateCredential === true
) {
if (["codex-chatgpt", "codex-chat"].includes(snapshot.target)) {
actions.push("setup");
if (snapshot.auth.account?.ownership === "owned") {
actions.push("sign-out-chatgpt");
if (snapshot.auth.account.planEnabled) actions.push("disable-chatgpt-plan");
}
} else if (snapshot.target === "xai-grok-build") {
actions.push("sign-in-grok-build", "sign-out-grok-build");
}
actions.push("rotate-local-capability");
}
if (definition.kind === "n8n-oauth-bridge" && state === "healthy") {
actions.push("setup");
if (snapshot.auth.account?.ownership === "owned") {
actions.push("sign-out-chatgpt");
if (snapshot.auth.account.planEnabled) actions.push("disable-chatgpt-plan");
}
}
if (definition.kind === "n8n-local-model" && snapshot.canRetry === true) {
actions.push("retry-model");
}
if (definition.kind === "n8n-claude") actions.push("manage-claude");
if (definition.kind === "n8n-supergrok") {
if (state === "healthy") {
actions.push("sign-in-grok-build", "sign-out-grok-build");
}
if (snapshot.canRemove === true) actions.push("remove-owned-supergrok");
} else if (snapshot.canRemove === true) actions.push("remove");
return actions;
}
function requireSafeDashboardVersion(value, label) {
if (typeof value !== "string" || !/^[A-Za-z0-9.+-]{1,64}$/u.test(value)) {
throw new TypeError(`The local dashboard ${label} is invalid.`);
}
return value;
}
function createSafeLocalDashboardStatus(status, previewMode, previewFixture) {
if (previewMode) {
return {
schemaVersion: 1,
generatedAt: new Date().toISOString(),
docker: { available: false, version: null, composeVersion: null },
auth: { secretsRevealable: false },
services: Object.entries(LOCAL_DASHBOARD_SERVICE_DEFINITIONS).map(
([target, definition]) => ({
target,
label: definition.label,
kind: definition.kind,
managed: previewFixture === "staged" && target === "codex-chat",
state: previewFixture === "staged" && target === "codex-chat" ? "staged" : "absent",
...(previewFixture === "staged" && target === "codex-chat" ? { staging: {
installId: "preview_installation_1", registrationId: "preview_registration_1", stage: "prepared",
} } : {}),
snapshot: null,
actions: ["setup"],
}),
),
providers: Object.entries(LOCAL_DASHBOARD_PROVIDER_DEFINITIONS).map(
([target, definition]) => ({
target,
label: definition.label,
authentication: "provider-oauth",
readiness: "runtime-owned",
}),
),
previewMode: true,
};
}
if (
status?.schemaVersion !== 1 ||
typeof status.generatedAt !== "string" ||
new Date(status.generatedAt).toISOString() !== status.generatedAt ||
typeof status.docker?.available !== "boolean" ||
status.auth?.secretsRevealable !== false ||
!Array.isArray(status.services) ||
!Array.isArray(status.providers)
) {
throw new TypeError("The local dashboard status is invalid.");
}
const expectedProviders = Object.keys(LOCAL_DASHBOARD_PROVIDER_DEFINITIONS);
if (status.providers.length !== expectedProviders.length) {
throw new TypeError("The local dashboard provider set is invalid.");
}
const providers = status.providers.map((provider, index) =>
createSafeDashboardProvider(expectedProviders[index], provider));
const providersByTarget = new Map(providers.map((provider) => [provider.target, provider]));
const expectedServices = Object.entries(LOCAL_DASHBOARD_SERVICE_DEFINITIONS);
if (status.services.length !== expectedServices.length) {
throw new TypeError("The local dashboard service set is invalid.");
}
const services = [];
for (const [index, [target, definition]] of expectedServices.entries()) {
const service = status.services[index];
if (
service?.target !== target ||
service.kind !== definition.kind ||
typeof service.managed !== "boolean" ||
!LOCAL_DASHBOARD_STATES.has(service.state) ||
!Array.isArray(service.actions) ||
(["absent", "unavailable"].includes(service.state)
? service.managed !== false
: service.managed !== true)
) {
throw new TypeError("The local dashboard service status is invalid.");
}
const requiresNullSnapshot = ["absent", "unavailable", "staged"].includes(service.state);
if (service.state === "staged" &&
!["codex-chatgpt", "codex-chat", "n8n-openai-oauth"].includes(target)) throw new TypeError();
const allowsUnattestedPartial =
service.state === "partial" && service.snapshot === null;
if (requiresNullSnapshot && service.snapshot !== null) {
throw new TypeError("The local dashboard service snapshot is invalid.");
}
const snapshot = requiresNullSnapshot || allowsUnattestedPartial
? null
: createSafeDashboardSnapshot(service.target, service.snapshot);
const actions = expectedDashboardActions({
definition,
state: service.state,
snapshot,
provider: providersByTarget.get(target),
});
if (
actions.some((action) => !definition.actions.has(action)) ||
service.actions.length !== actions.length ||
service.actions.some((action, actionIndex) => action !== actions[actionIndex])
) {
throw new TypeError("The local dashboard service actions are invalid.");
}
services.push({
target: service.target,
label: definition.label,
kind: definition.kind,
managed: service.managed,
state: service.state,
...(service.state === "staged" ? { staging: copySiwcStaging(service.staging) } : {}),
snapshot,
actions,
});
}
if (
status.docker.available
? status.docker.version === null || status.docker.composeVersion === null
: status.docker.version !== null || status.docker.composeVersion !== null
) {
throw new TypeError("The local dashboard Docker status is invalid.");
}
const version = status.docker.available
? requireSafeDashboardVersion(status.docker.version, "Docker version")
: null;
const composeVersion = status.docker.available
? requireSafeDashboardVersion(status.docker.composeVersion, "Compose version")
: null;
return {
schemaVersion: 1,
generatedAt: status.generatedAt,
docker: {
available: status.docker.available,
version,
composeVersion,
},
auth: { secretsRevealable: false },
services,
providers,
};
}
function createSafeDockerStatus(
status,
previewMode,
localN8nStackState = null,
) {
if (previewMode || status?.dockerAvailable !== true) {
return {
dockerAvailable: false,
...(previewMode ? { previewMode: true } : {}),
...(!previewMode && status?.unsupportedPlatform === true
? { unsupportedPlatform: true }
: {}),
...(!previewMode && status?.claudeUnsupportedPlatform === true ? { claudeUnsupportedPlatform: true } : {}),
};
}
return {
dockerAvailable: true,
dockerVersion: status.dockerVersion,
composeVersion: status.composeVersion,
...(SAFE_LOCAL_N8N_STACK_STATES.has(localN8nStackState)
? { localN8nStackState }
: {}),
...(status.claudeUnsupportedPlatform === true ? { claudeUnsupportedPlatform: true } : {}),
};
}
function requireSafeDockerIdentifier(value, label) {
if (typeof value !== "string" || !/^[a-f0-9]{12,64}$/u.test(value)) {
throw Object.assign(new Error(`The discovered ${label} is invalid.`), {
statusCode: 502,
});
}
return value;
}
function requireSafeDockerName(value, label) {
if (
typeof value !== "string" ||
value.length === 0 ||
value.length > 128 ||
!/^[A-Za-z0-9][A-Za-z0-9_.-]*$/u.test(value)
) {
throw Object.assign(new Error(`The discovered ${label} is invalid.`), {
statusCode: 502,
});
}
return value;
}
function requireSafeDisplayValue(value, label, maximumLength = 512) {
if (
typeof value !== "string" ||
value.length === 0 ||
value.length > maximumLength ||
/[\u0000-\u001f\u007f]/u.test(value)
) {
throw Object.assign(new Error(`The discovered ${label} is invalid.`), {
statusCode: 502,
});
}
return value;
}
function createSafeLocalN8nDiscovery(discovery, previewMode, managedTarget = null) {
if (previewMode) {
return {
dockerAvailable: false,
previewMode: true,
containers: [],
};
}
if (discovery?.dockerAvailable !== true) {
return { dockerAvailable: false, containers: [] };
}
if (!Array.isArray(discovery.containers)) {
throw Object.assign(
new Error("The local n8n discovery response is invalid."),
{ statusCode: 502 },
);
}
return {
dockerAvailable: true,
...(typeof discovery.dockerVersion === "string"
? {
dockerVersion: requireSafeDisplayValue(
discovery.dockerVersion,
"Docker version",
80,
),
}
: {}),
...(typeof discovery.composeVersion === "string"
? {
composeVersion: requireSafeDisplayValue(
discovery.composeVersion,
"Compose version",
80,
),
}
: {}),
containers: discovery.containers.map((container) => {
if (!Array.isArray(container?.networks)) {
throw Object.assign(
new Error("The discovered n8n network list is invalid."),
{ statusCode: 502 },
);
}
const marker = managedTarget?.marker;
const recommendedNetwork = marker && `${marker.projectName}_edge`;
const managedStack = marker?.dockerHost === discovery.dockerHost &&
managedTarget.containerId === container.containerId &&
container.containerName === `${marker.projectName}-n8n-1` &&
container.networks.some(network => network.networkName === recommendedNetwork);
return {
containerId: requireSafeDockerIdentifier(
container.containerId,
"n8n container ID",
),
containerName: requireSafeDockerName(
container.containerName,
"n8n container name",
),
image: requireSafeDisplayValue(container.image, "n8n image"),
managedStack: managedStack === true,
...(managedStack ? { recommendedNetwork } : {}),
networks: container.networks.map((network) => ({
dockerNetworkId: requireSafeDockerIdentifier(
network?.dockerNetworkId,
"Docker network ID",
),
networkName: requireSafeDockerName(
network?.networkName,
"Docker network name",
),
disposable: network?.disposable === true,
})),
};
}),
};
}
function createSafeLocalN8nPlan(plan) {
return {
kind: plan.kind,
target: plan.target,
label: plan.label,
n8nContainerId: plan.n8nContainerId,
n8nContainerName: plan.n8nContainerName,
dockerNetworkId: plan.dockerNetworkId,
networkName: plan.networkName,
endpoint: plan.endpoint,
upstreamAuth: plan.upstreamAuth,
hostPublication: plan.hostPublication,
managedPath: plan.managedPath,
disposableHarnessWarning: plan.disposableHarnessWarning === true,
...(plan.account ? { account: copySiwcAccountView(plan.account) } : {}),
...(plan.resumeRequired ? { resumeRequired: true, staging: copySiwcStaging(plan.staging) } : {}),
...(plan.migrationRequired ? { migrationRequired: true, legacyResourcesPreserved: true,
requiresMigrationConsent: true } : {}),
...(plan.replacementRequired ? { replacementRequired: true, oldSessionSignedOut: true,
oldHistoryRetained: true, requiresReplacementConsent: true } : {}),
};
}
function createSafeLocalN8nSuperGrokPlan(plan) {
if (
plan?.kind !== "n8n-supergrok" ||
plan.target !== LOCAL_N8N_SUPERGROK_TARGET ||
plan.label !== "SuperGrok for n8n" ||
plan.endpoint !== LOCAL_N8N_SUPERGROK_ENDPOINT ||
plan.baseUrl !== LOCAL_N8N_SUPERGROK_ENDPOINT ||
plan.protocol !== "openai-chat-completions" ||
plan.upstreamAuth !== "provider-owned-oauth" ||
plan.managedPath !== "~/.relmio/local/n8n-supergrok-oauth" ||
plan.hostPublication !== "none" ||
plan.experimental !== true ||
typeof plan.disposableHarnessWarning !== "boolean"
) {
throw Object.assign(
new Error("The local n8n SuperGrok plan is invalid."),
{ statusCode: 502 },
);
}
return {
kind: "n8n-supergrok",
target: LOCAL_N8N_SUPERGROK_TARGET,
label: "SuperGrok for n8n",
endpoint: LOCAL_N8N_SUPERGROK_ENDPOINT,
baseUrl: LOCAL_N8N_SUPERGROK_ENDPOINT,
protocol: "openai-chat-completions",
upstreamAuth: "provider-owned-oauth",
n8nContainerId: requireSafeDockerIdentifier(
plan.n8nContainerId,
"n8n container ID",
),
n8nContainerName: requireSafeDockerName(
plan.n8nContainerName,
"n8n container name",
),
dockerNetworkId: requireSafeDockerIdentifier(
plan.dockerNetworkId,
"Docker network ID",
),
networkName: requireSafeDockerName(plan.networkName, "Docker network name"),
managedPath: "~/.relmio/local/n8n-supergrok-oauth",
hostPublication: "none",
experimental: true,
disposableHarnessWarning: plan.disposableHarnessWarning,
};
}
function requireCatalogModel(value) {
if (!LOCAL_MODEL_CATALOG.some((model) => model.id === value)) throw new TypeError("Select an allowlisted local model.");
return value;
}
function safeModelBytes(value) {
if (!Number.isSafeInteger(value) || value < 0) throw new TypeError("The model resource measurement is invalid.");
return value;
}
function safeModelBudget(value) {
if (!value || typeof value !== "object" ||
!Number.isSafeInteger(value.memoryBytes) || value.memoryBytes <= 0 ||
typeof value.cpus !== "number" || !Number.isFinite(value.cpus) || value.cpus <= 0 ||
!Number.isSafeInteger(value.contextTokens) || value.contextTokens <= 0) {
throw new TypeError("The reviewed model budget is invalid.");
}
return { memoryBytes: value.memoryBytes, cpus: value.cpus, contextTokens: value.contextTokens };
}
function createSafeLocalN8nModelPlan(plan) {
if (plan?.kind !== LOCAL_N8N_MODEL_TARGET || plan.target !== LOCAL_N8N_MODEL_TARGET ||
plan.endpoint !== LOCAL_N8N_MODEL_ENDPOINT || plan.baseUrl !== LOCAL_N8N_MODEL_ENDPOINT ||
plan.hostPublication !== "none" || plan.authentication !== "none" ||
plan.upstreamAuth !== "none" || plan.protocol !== "openai-chat-completions" ||
plan.apiKeyPlaceholder !== "local-only" || plan.responsesApi !== false ||
plan.cloudEnabled !== false || plan.experimental !== true ||
plan.managedPath !== "~/.relmio/local/n8n-local-model" ||
plan.catalogRevision !== LOCAL_MODEL_CATALOG_REVISION ||
plan.runtimeImage !== LOCAL_MODEL_RUNTIME_IMAGE ||
plan.approvedModelDigest !== getLocalModelDefinition(requireCatalogModel(plan.modelId)).manifestDigest ||
!/^sha256:[a-f0-9]{64}$/u.test(plan.approvedModelDigest) ||
!Number.isFinite(plan.hostResources?.cpus) || plan.hostResources.cpus <= 0) {
throw Object.assign(new TypeError("The local model plan is invalid."), { statusCode: 502 });
}
return {
kind: LOCAL_N8N_MODEL_TARGET, target: LOCAL_N8N_MODEL_TARGET,
label: "Local model for n8n", endpoint: LOCAL_N8N_MODEL_ENDPOINT,
n8nContainerId: requireSafeDockerIdentifier(plan.n8nContainerId, "n8n container ID"),
n8nContainerName: requireSafeDockerName(plan.n8nContainerName, "n8n container name"),
dockerNetworkId: requireSafeDockerIdentifier(plan.dockerNetworkId, "Docker network ID"),
networkName: requireSafeDockerName(plan.networkName, "Docker network name"),
modelId: requireCatalogModel(plan.modelId), catalogRevision: LOCAL_MODEL_CATALOG_REVISION,
runtimeImage: LOCAL_MODEL_RUNTIME_IMAGE, approvedModelDigest: plan.approvedModelDigest,
contextTokens: safeModelBudget(plan).contextTokens, memoryBytes: plan.memoryBytes,
cpus: plan.cpus, expectedDownloadBytes: safeModelBytes(plan.expectedDownloadBytes),
requiredDiskBytes: safeModelBytes(plan.requiredDiskBytes),
reservedMemoryBytes: safeModelBytes(plan.reservedMemoryBytes),
hostResources: {
memoryBytes: safeModelBytes(plan.hostResources?.memoryBytes),
cpus: plan.hostResources.cpus,
diskAvailableBytes: safeModelBytes(plan.hostResources.diskAvailableBytes),
},
managedPath: plan.managedPath, hostPublication: "none",
authentication: "none", apiKeyPlaceholder: "local-only",
responsesApi: false, cloudEnabled: false, experimental: true,
disposableHarnessWarning: plan.disposableHarnessWarning === true,
};
}
function createSafeVpsLocalModelPlan(plan) {
if (!["install", "retry", "remove"].includes(plan?.action) ||
plan.installDirectory !== "/docker/n8n-openai-oauth/local-model" ||
plan.operationLockPath !== "/docker/n8n-openai-oauth/.local-model-operation.lock" ||
plan.temporaryBuildStatePath !== (plan.action === "remove" ? null : "/docker/n8n-openai-oauth/.local-model-operation.lock/buildx") ||
(plan.action === "install"
? !exactObjectKeys(plan.sharedRootBootstrap, [
"directory", "markerPath", "mayCreateDirectory", "mayCreateMarker", "directoryMode",
]) ||
plan.sharedRootBootstrap.directory !== "/docker/n8n-openai-oauth" ||
plan.sharedRootBootstrap.markerPath !== "/docker/n8n-openai-oauth/.managed-by-relmio-root" ||
plan.sharedRootBootstrap.mayCreateDirectory !== true ||
plan.sharedRootBootstrap.mayCreateMarker !== true ||
plan.sharedRootBootstrap.directoryMode !== "0700"
: plan.sharedRootBootstrap !== null) ||
plan.endpoint !== LOCAL_N8N_MODEL_ENDPOINT ||
plan.catalogRevision !== LOCAL_MODEL_CATALOG_REVISION ||
plan.runtimeImage !== LOCAL_MODEL_RUNTIME_IMAGE ||
plan.approvedModelDigest !== getLocalModelDefinition(requireCatalogModel(plan.modelId)).manifestDigest ||
!/^sha256:[a-f0-9]{64}$/u.test(plan.approvedModelDigest) ||
plan.clearModelCache !== (plan.action === "remove") ||
!Array.isArray(plan.publishedPorts) || plan.publishedPorts.length !== 0 ||
!Array.isArray(plan.existingN8nChanges) || plan.existingN8nChanges.length !== 0 ||
plan.existingN8nRestarts !== 0 ||
!Number.isSafeInteger(plan.hostResources?.memoryBytes) ||
!Number.isSafeInteger(plan.hostResources?.diskAvailableBytes) ||
typeof plan.hostResources?.cpus !== "number" || !Number.isFinite(plan.hostResources.cpus) ||
plan.hostResources.cpus <= 0) {
throw Object.assign(new TypeError("The reviewed VPS model plan is invalid."), { statusCode: 502 });
}
const budget = safeModelBudget(plan);
return {
action: plan.action,
containerName: requireSafeDockerName(plan.containerName, "n8n container name"),
networkName: requireSafeDockerName(plan.networkName, "Docker network name"),
containerId: requireSafeDockerIdentifier(plan.containerId, "n8n container ID"),
networkId: requireSafeDockerIdentifier(plan.networkId, "Docker network ID"),
installId: plan.installId ?? null,
installDirectory: plan.installDirectory,
operationLockPath: "/docker/n8n-openai-oauth/.local-model-operation.lock",
temporaryBuildStatePath: plan.temporaryBuildStatePath,
sharedRootBootstrap: plan.action === "install"
? {
directory: "/docker/n8n-openai-oauth",
markerPath: "/docker/n8n-openai-oauth/.managed-by-relmio-root",
mayCreateDirectory: true,
mayCreateMarker: true,
directoryMode: "0700",
}
: null,
endpoint: LOCAL_N8N_MODEL_ENDPOINT,
modelId: requireCatalogModel(plan.modelId),
catalogRevision: LOCAL_MODEL_CATALOG_REVISION,
runtimeImage: LOCAL_MODEL_RUNTIME_IMAGE, approvedModelDigest: plan.approvedModelDigest,
contextTokens: budget.contextTokens, memoryBytes: budget.memoryBytes, cpus: budget.cpus,
expectedDownloadBytes: safeModelBytes(plan.expectedDownloadBytes),
requiredDiskBytes: safeModelBytes(plan.requiredDiskBytes),
reservedMemoryBytes: safeModelBytes(plan.reservedMemoryBytes),
hostResources: {
memoryBytes: safeModelBytes(plan.hostResources.memoryBytes),
cpus: plan.hostResources.cpus,
diskAvailableBytes: safeModelBytes(plan.hostResources.diskAvailableBytes),
},
clearModelCache: plan.clearModelCache,
publishedPorts: [], existingN8nChanges: [], existingN8nRestarts: 0,
};
}
function requireVpsSuperGrokBuildBoundary(plan) {
const mayBuild = ["install", "sign-in", "sign-out"].includes(plan?.action);
if (plan?.operationLockPath !== "/docker/n8n-openai-oauth/.supergrok-operation.lock" ||
plan.temporaryBuildStatePath !== (mayBuild ? "/docker/n8n-openai-oauth/.supergrok-operation.lock/buildx" : null)) {
throw Object.assign(new TypeError("The reviewed SuperGrok build boundary is invalid."), { statusCode: 502 });
}
}
const VPS_CLAUDE_ACTIONS = Object.freeze(["install", "replace", "renew", "sign-out", "remove"]);
const VPS_CLAUDE_LOCK = "/docker/n8n-openai-oauth/.claude-operation.lock";
function vpsClaudeInvalid(subject) {
return Object.assign(new TypeError(`The VPS Claude ${subject} is invalid.`), { statusCode: 502 });
}
function validVpsClaudePin(pin) {
return ["amd64", "arm64"].includes(pin?.arch) && typeof pin.version === "string" && /^\d{1,4}\.\d{1,4}\.\d{1,4}$/u.test(pin.version) &&
typeof pin.executableSha256 === "string" && /^[a-f0-9]{64}$/u.test(pin.executableSha256);
}
function createSafeVpsClaudeStatus(status) {
const { state, installation, candidate } = status ?? {};
if (!["absent", "manual", "installed", "invalid"].includes(state) || (state === "installed") !== (installation !== null) ||
(candidate !== null && (!validVpsClaudePin(candidate) || !["linux-x64", "linux-arm64"].includes(candidate.platform))) ||
(status.existingLogin !== undefined && status.existingLogin !== true) ||
(status.existingLogin === true && state !== "absent")) {
throw vpsClaudeInvalid("status");
}
if (state === "installed" && (!["amd64", "arm64"].includes(installation?.arch) || !validModels(installation.models) ||
(installation.expiresAt !== null && !isoTime(installation.expiresAt)) || typeof installation.approvalExpired !== "boolean" ||
!["running", "stopped", "missing", "restarting", "unhealthy"].includes(installation.container) || installation.endpoint !== CLAUDE_COMPANION.endpoint)) {
throw vpsClaudeInvalid("status");
}
return {
state,
...(status.existingLogin === true ? { existingLogin: true } : {}),
installation: state === "installed" ? {
network: requireSafeDockerName(installation.network, "Claude network name"), arch: installation.arch,
models: [...installation.models], expiresAt: installation.expiresAt, approvalExpired: installation.approvalExpired,
container: installation.container, endpoint: CLAUDE_COMPANION.endpoint,
} : null,
candidate: candidate === null ? null : {
version: candidate.version, arch: candidate.arch, platform: candidate.platform, executableSha256: candidate.executableSha256,
},
};
}
// The browser sees only the contract fields; selection ids and review answers stay in the server-side plan.
function createSafeVpsClaudePlan(plan) {
const approves = ["install", "replace", "renew"].includes(plan?.action);
if (!VPS_CLAUDE_ACTIONS.includes(plan?.action) || plan.installDirectory !== "/docker/n8n-openai-oauth/claude" ||
plan.endpoint !== CLAUDE_COMPANION.endpoint || plan.operationLockPath !== VPS_CLAUDE_LOCK || !validVpsClaudePin(plan) ||
!Array.isArray(plan.existingN8nChanges) || plan.existingN8nChanges.length !== 0 || plan.restarts !== 0 ||
!Array.isArray(plan.publishedPorts) || plan.publishedPorts.length !== 0 || !validModels(plan.models) ||
(approves ? !isoTime(plan.expiresAt) : plan.expiresAt !== null) ||
(plan.action === "install" ? typeof plan.keepsClaudeLogin !== "boolean"
: plan.keepsClaudeLogin !== ["replace", "renew"].includes(plan.action))) {
throw vpsClaudeInvalid("plan");
}
return {
action: plan.action, installDirectory: plan.installDirectory, endpoint: plan.endpoint,
network: requireSafeDockerName(plan.network, "Claude network name"), arch: plan.arch, models: [...plan.models],
version: plan.version, executableSha256: plan.executableSha256, expiresAt: plan.expiresAt,
keepsClaudeLogin: plan.keepsClaudeLogin, existingN8nChanges: [], restarts: 0, publishedPorts: [], operationLockPath: VPS_CLAUDE_LOCK,
};
}
// The one-time key leaves only for install and replace; nothing else from the VPS passes through.
function createSafeVpsClaudeApplyResult(result, action) {
const deploys = action === "install" || action === "replace";
if (result?.state !== (action === "remove" ? "absent" : "installed") ||
(deploys ? typeof result.clientKey !== "string" || !/^[A-Za-z0-9_-]{43}$/u.test(result.clientKey) : result.clientKey !== undefined) ||
(deploys || action === "renew" ? !isoTime(result.expiresAt) : result.expiresAt !== undefined) ||
(action === "sign-out" ? result.loggedIn !== false : result.loggedIn !== undefined)) {
throw vpsClaudeInvalid("result");
}
return {
state: result.state,
...(deploys ? { clientKey: result.clientKey } : {}),
...(result.expiresAt === undefined ? {} : { expiresAt: result.expiresAt }),
...(action === "sign-out" ? { loggedIn: false } : {}),
};
}
// Never email or organization: only the three fields that decide readiness.
function createSafeVpsClaudeAuthStatus(status) {
const field = (value) => value === null || (typeof value === "string" && /^[A-Za-z0-9_.:-]{1,64}$/u.test(value));
if (typeof status?.loggedIn !== "boolean" || !field(status.authMethod) || !field(status.subscriptionType)) {
throw vpsClaudeInvalid("sign-in status");
}
return { loggedIn: status.loggedIn, authMethod: status.authMethod, subscriptionType: status.subscriptionType };
}
function createSafeLocalN8nAssistantPlan(plan) {
return {
kind: plan.kind,
target: plan.target,
label: plan.label,
protocol: plan.protocol,
n8nContainerId: plan.n8nContainerId,
n8nContainerName: plan.n8nContainerName,
dockerNetworkId: plan.dockerNetworkId,
networkName: plan.networkName,
codeSandbox: plan.codeSandbox === true,
includeSearxng: plan.includeSearxng === true,
privilegedRunner: plan.privilegedRunner === true,
hostPublication: plan.hostPublication,
managedPath: plan.managedPath,
n8nConfigurationRequired: plan.n8nConfigurationRequired === true,
disposableHarnessWarning: plan.disposableHarnessWarning === true,
};
}
function createSafeLocalN8nStackPlan(plan) {
if (
plan?.target !== LOCAL_N8N_STACK_TARGET ||
plan.kind !== "local-n8n-stack" ||
!["none", "ngrok"].includes(plan.publicAccess) ||
plan.localUrl !== `http://localhost:${plan.n8nPort}` ||
plan.ngrokPublicUrl !== (plan.publicAccess === "ngrok" ? `https://${plan.ngrokHostname}` : null) ||
plan.n8nContainerName !== null || plan.networkName !== null ||
plan.hostPublication !== "loopback-only" ||
plan.deploymentMode !== "new-disposable-stack" ||
plan.managedPath !== "~/.relmio/local/n8n-stack" ||
!Number.isInteger(plan.n8nPort) ||
(plan.publicAccess === "ngrok"
? !/^[a-z0-9](?:[a-z0-9.-]*[a-z0-9])?\.[a-z0-9.-]+$/u.test(plan.ngrokHostname ?? "")
: plan.ngrokHostname !== null) ||
!["disabled", "sandbox", "sandbox-with-searxng"].includes(plan.assistantMode) ||
typeof plan.timezone !== "string"
) {
throw Object.assign(new Error("The local n8n stack plan is invalid."), { statusCode: 502 });
}
return {
kind: plan.kind, target: plan.target, label: plan.label,
publicAccess: plan.publicAccess,
ngrokHostname: plan.ngrokHostname, n8nPort: plan.n8nPort,
timezone: plan.timezone,
assistantMode: plan.assistantMode, localUrl: plan.localUrl,
ngrokPublicUrl: plan.ngrokPublicUrl,
n8nContainerName: null, networkName: null,
hostPublication: plan.hostPublication, deploymentMode: plan.deploymentMode,
managedPath: plan.managedPath,
};
}
function createSafeLocalN8nStackInstallResult(result, plan) {
if (
result?.target !== LOCAL_N8N_STACK_TARGET ||
result.publicAccess !== plan.publicAccess ||
result.localUrl !== plan.localUrl ||
result.ngrokPublicUrl !== plan.ngrokPublicUrl ||
result.assistantMode !== plan.assistantMode ||
result.hostPublication !== `n8n http://127.0.0.1:${plan.n8nPort}` ||
result.deploymentMode !== "new-disposable-stack" ||
!/^relmio-local-n8n-[a-f0-9]{32}$/u.test(result.projectName ?? "") ||
result.n8nContainerName !== `${result.projectName}-n8n-1` ||
result.networkName !== `${result.projectName}_edge` ||
!Array.isArray(result.containerServices) ||
result.containerServices.some((name) => requireSafeDockerName(name, "n8n service name") !== name) ||
!Array.isArray(result.networks) ||
result.networks.some((name) => !["edge", "assistant-shared", "assistant-internal"].includes(name))
) {
throw Object.assign(new Error("The local n8n stack returned an invalid result."), {
statusCode: 502,
});
}
const assistantSettings = result.assistantSettings;
const expectedAssistantSettings =
result.assistantMode === "disabled"
? null
: {
sandboxUrl: "http://relmio-sandbox-api:8080",
...(result.assistantMode === "sandbox-with-searxng"
? { searxngUrl: "http://relmio-searxng:8080" }
: {}),
};
if (
JSON.stringify(assistantSettings) !== JSON.stringify(expectedAssistantSettings)
) {
throw Object.assign(new Error("The local n8n stack returned invalid Assistant settings."), {
statusCode: 502,
});
}
return {
target: result.target,
publicAccess: result.publicAccess,
localUrl: result.localUrl,
ngrokPublicUrl: result.ngrokPublicUrl,
n8nContainerName: requireSafeDockerName(result.n8nContainerName, "n8n container name"),
networkName: requireSafeDockerName(result.networkName, "n8n network name"),
projectName: result.projectName,
containerServices: [...result.containerServices],
networks: [...result.networks],
assistantMode: result.assistantMode,
assistantSettings: assistantSettings === null ? null : { ...assistantSettings },
hostPublication: result.hostPublication,
deploymentMode: result.deploymentMode,
};
}
function createSafeLocalN8nStackRemovalResult(result) {
if (
result?.target !== LOCAL_N8N_STACK_TARGET ||
result.removed !== true ||
result.deploymentMode !== "removed-owned-disposable-stack"
) {
throw Object.assign(new Error("The local n8n stack removal result is invalid."), {
statusCode: 502,
});
}
return {
target: LOCAL_N8N_STACK_TARGET,
removed: true,
deploymentMode: result.deploymentMode,
};
}
function invalidAssistantInstallResult(label) {
return Object.assign(
new Error(`The ${label} returned an invalid result.`),
{ statusCode: 502 },
);
}
function requireSafeAssistantUrl(value, prefix, label) {
if (
typeof value !== "string" ||
!new RegExp(
`^http://${prefix}-[a-f0-9]{32}:8080$`,
"u",
).test(value)
) {
throw invalidAssistantInstallResult(label);
}
return value;
}
function requireExactAssistantSettings({
settings,
sandboxUrl,
sandboxApiKey,
includeSearxng,
searxngUrl,
label,
}) {
const expectedSettings = {
N8N_INSTANCE_AI_SANDBOX_ENABLED: "true",
N8N_INSTANCE_AI_SANDBOX_PROVIDER: "n8n-sandbox",
N8N_INSTANCE_AI_SANDBOX_IMAGE: ASSISTANT_COMPANION_IMAGES.sandbox,
N8N_SANDBOX_SERVICE_URL: sandboxUrl,
...(sandboxApiKey === null
? {}
: { N8N_SANDBOX_SERVICE_API_KEY: sandboxApiKey }),
...(includeSearxng
? { N8N_INSTANCE_AI_SEARXNG_URL: searxngUrl }
: {}),
};
const expectedNames = Object.keys(expectedSettings);
if (
!settings ||
typeof settings !== "object" ||
Array.isArray(settings) ||
Object.keys(settings).length !== expectedNames.length ||
expectedNames.some(
(name) =>
!Object.hasOwn(settings, name) ||
settings[name] !== expectedSettings[name],
)
) {
throw invalidAssistantInstallResult(label);
}
return expectedSettings;
}
function createSafeAssistantInstallResult(result, reviewedPlan) {
const label = "VPS AI Assistant";
const sandboxUrl = requireSafeAssistantUrl(
result?.sandboxUrl,
"relmio-ai-sandbox",
label,
);
const includeSearxng = result?.includeSearxng;
const deploymentMode = result?.deploymentMode;
const sandboxApiKey = result?.sandboxApiKey;
if (
typeof includeSearxng !== "boolean" ||
includeSearxng !== reviewedPlan?.includeSearxng ||
!["installed", "updated"].includes(deploymentMode) ||
(sandboxApiKey !== null &&
(typeof sandboxApiKey !== "string" ||
!/^[A-Za-z0-9_-]{43}$/u.test(sandboxApiKey))) ||
(deploymentMode === "installed" && sandboxApiKey === null)
) {
throw invalidAssistantInstallResult(label);
}
const searxngUrl = includeSearxng
? requireSafeAssistantUrl(
result?.searxngUrl,
"relmio-ai-searxng",
label,
)
: undefined;
if (!includeSearxng && Object.hasOwn(result, "searxngUrl")) {
throw invalidAssistantInstallResult(label);
}
const n8nSettings = requireExactAssistantSettings({
settings: result?.n8nSettings,
sandboxUrl,
sandboxApiKey,
includeSearxng,
searxngUrl,
label,
});
return {
sandboxUrl,
sandboxApiKey,
includeSearxng,
...(includeSearxng ? { searxngUrl } : {}),
n8nSettings,
deploymentMode,
};
}
function createSafeLocalN8nStackResumeResult(result) {
if (
result?.target !== LOCAL_N8N_STACK_TARGET ||
result.resumed !== true ||
result.deploymentMode !== "resumed-owned-disposable-stack"
) {
throw Object.assign(new Error("The local n8n stack resume result is invalid."), {
statusCode: 502,
});
}
return {
...createSafeLocalN8nStackInstallResult(
{ ...result, deploymentMode: "new-disposable-stack" },
{ publicAccess: result.publicAccess, localUrl: result.localUrl,
ngrokPublicUrl: result.ngrokPublicUrl, assistantMode: result.assistantMode,
n8nPort: Number(new URL(result.localUrl).port) },
),
resumed: true,
deploymentMode: result.deploymentMode,
};
}
function createSafeLocalN8nAssistantInstallResult(result, reviewedPlan) {
const label = "local n8n Assistant";
const sandboxUrl = requireSafeAssistantUrl(
result?.sandboxUrl,
"relmio-ai-sandbox",
label,
);
const includeSearxng = result?.includeSearxng;
const searxngUrl = includeSearxng
? requireSafeAssistantUrl(
result?.searxngUrl,
"relmio-ai-searxng",
label,
)
: undefined;
const sandboxApiKey = result?.sandboxApiKey;
if (
result?.target !== LOCAL_N8N_ASSISTANT_TARGET ||
result.protocol !== "n8n-instance-ai-companion" ||
result.endpoint !== sandboxUrl ||
typeof includeSearxng !== "boolean" ||
includeSearxng !== reviewedPlan?.includeSearxng ||
(!includeSearxng && Object.hasOwn(result, "searxngUrl")) ||
typeof sandboxApiKey !== "string" ||
!/^[A-Za-z0-9_-]{43}$/u.test(sandboxApiKey) ||
result.deploymentMode !== "installed" ||
result.hostPublication !== "none" ||
result.privilegedRunner !== true ||
result.n8nConfigurationRequired !== true ||
result.credentialShownOnce !== true
) {
throw invalidAssistantInstallResult(label);
}
const networkName = requireSafeDockerName(
result.networkName,
"Assistant network name",
);
const n8nContainerName = requireSafeDockerName(
result.n8nContainerName,
"n8n container name",
);
if (
networkName !== reviewedPlan?.networkName ||
n8nContainerName !== reviewedPlan?.n8nContainerName
) {
throw invalidAssistantInstallResult(label);
}
const n8nSettings = requireExactAssistantSettings({
settings: result.n8nSettings,
sandboxUrl,
sandboxApiKey,
includeSearxng,
searxngUrl,
label,
});
return {
target: result.target,
endpoint: sandboxUrl,
sandboxUrl,
sandboxApiKey,
...(includeSearxng ? { searxngUrl } : {}),
protocol: result.protocol,
includeSearxng,
networkName,
n8nContainerName,
hostPublication: "none",
privilegedRunner: true,
n8nConfigurationRequired: true,
n8nSettings,
deploymentMode: "installed",
credentialShownOnce: true,
};
}
function createSafeLocalN8nAssistantRemovalResult(result) {
if (
result?.target !== LOCAL_N8N_ASSISTANT_TARGET ||
result.removed !== true
) {
throw Object.assign(
new Error("The local n8n Assistant removal result is invalid."),
{ statusCode: 502 },
);
}
return { target: LOCAL_N8N_ASSISTANT_TARGET, removed: true };
}
function createSafeLocalN8nAssistantSearxngReview(review, reviewId) {
const plan = review?.plan;
const installation = review?.installation;
const sandboxUrl = `http://${installation?.sandboxAlias}:8080`;
const searxngUrl = `http://${installation?.searxngAlias}:8080`;
if (
!review ||
typeof review !== "object" ||
Array.isArray(review) ||
review.schemaVersion !== 1 ||
review.kind !== "relmio-local-n8n-assistant-searxng-update" ||
review.target !== LOCAL_N8N_ASSISTANT_TARGET ||
review.includeSearxng !== true ||
review.sandboxApiKeyRotated !== false ||
!plan ||
typeof plan !== "object" ||
Array.isArray(plan) ||
plan.target !== LOCAL_N8N_ASSISTANT_TARGET ||
plan.includeSearxng !== false ||
plan.hostPublication !== "none" ||
plan.n8nConfigurationRequired !== true ||
!installation ||
typeof installation !== "object" ||
Array.isArray(installation) ||
installation.includeSearxng !== false
) {
throw Object.assign(
new Error("The local n8n Assistant edit review is invalid."),
{ statusCode: 502 },
);
}
return {
reviewId: requireSafeDisplayValue(reviewId, "Assistant edit review ID", 128),
target: LOCAL_N8N_ASSISTANT_TARGET,
includeSearxng: true,
sandboxApiKeyRotated: false,
sandboxUrl: requireSafeAssistantUrl(
sandboxUrl,
"relmio-ai-sandbox",
"local n8n Assistant edit",
),
searxngUrl: requireSafeAssistantUrl(
searxngUrl,
"relmio-ai-searxng",
"local n8n Assistant edit",
),
n8nContainerName: requireSafeDockerName(
plan.n8nContainerName,
"n8n container name",
),
networkName: requireSafeDockerName(plan.networkName, "Docker network name"),
hostPublication: "none",
n8nConfigurationRequired: true,
};
}
function createSafeLocalN8nAssistantSearxngEditResult(result) {
const label = "local n8n Assistant SearXNG edit";
const sandboxUrl = requireSafeAssistantUrl(
result?.sandboxUrl,
"relmio-ai-sandbox",
label,
);
const searxngUrl = requireSafeAssistantUrl(
result?.searxngUrl,
"relmio-ai-searxng",
label,
);
const expectedSettings = {
N8N_INSTANCE_AI_SEARXNG_URL: searxngUrl,
};
if (
!result ||
typeof result !== "object" ||
Array.isArray(result) ||
result.target !== LOCAL_N8N_ASSISTANT_TARGET ||
result.endpoint !== sandboxUrl ||
result.protocol !== "n8n-instance-ai-companion" ||
result.includeSearxng !== true ||
result.hostPublication !== "none" ||
result.privilegedRunner !== true ||
result.n8nConfigurationRequired !== true ||
result.deploymentMode !== "searxng-enabled" ||
result.sandboxApiKeyRotated !== false ||
Object.hasOwn(result, "sandboxApiKey") ||
!result.n8nSettings ||
typeof result.n8nSettings !== "object" ||
Array.isArray(result.n8nSettings) ||
Object.keys(result.n8nSettings).length !== 1 ||
result.n8nSettings.N8N_INSTANCE_AI_SEARXNG_URL !== searxngUrl
) {
throw Object.assign(
new Error("The local n8n Assistant SearXNG edit returned an invalid result."),
{ statusCode: 502 },
);
}
return {
target: LOCAL_N8N_ASSISTANT_TARGET,
endpoint: sandboxUrl,
sandboxUrl,
searxngUrl,
protocol: "n8n-instance-ai-companion",
includeSearxng: true,
networkName: requireSafeDockerName(result.networkName, "Assistant network name"),
n8nContainerName: requireSafeDockerName(
result.n8nContainerName,
"n8n container name",
),
hostPublication: "none",
privilegedRunner: true,
n8nConfigurationRequired: true,
n8nSettings: expectedSettings,
deploymentMode: "searxng-enabled",
sandboxApiKeyRotated: false,
};
}
function createSafeLocalN8nInstallResult(result, plan) {
const endpoint = result?.endpoint ?? result?.baseUrl;
const models = result?.models;
if (
result?.target !== LOCAL_N8N_SIDECAR_TARGET ||
endpoint !== LOCAL_N8N_SIDECAR_ENDPOINT ||
result.protocol !== "openai-v1" ||
typeof result.clientCredential !== "string" ||
!/^[A-Za-z0-9_-]{32,256}$/u.test(result.clientCredential) ||
result.credentialShownOnce !== true ||
!Array.isArray(models) ||
models.some((model) => typeof model !== "string" || model.length === 0 ||
model.length > 128 || !/^[A-Za-z0-9_.:-]+$/u.test(model)) ||
!["installed", "migrated", "replaced", "partial"].includes(result.deploymentMode) ||
(result.deploymentMode === "migrated" && (result.migratedLegacy !== true || result.legacyRetained !== true)) ||
(result.deploymentMode === "replaced" && (result.replacedAccount !== true || result.oldHistoryRetained !== true)) ||
(result.deploymentMode === "partial" && !result.runtimeFailure && !result.finalizationFailure) ||
typeof result.networkName !== "string" ||
(result.hostPublication !== "none" &&
!(result.deploymentMode === "partial" && result.runtimeState === "unknown" &&
result.hostPublication === "unknown")) ||
result.account?.registrationId !== plan.authBinding.registrationId
) {
throw Object.assign(new Error("The local n8n sidecar returned an invalid result."), { statusCode: 502 });
}
return {
target: result.target,
endpoint,
clientCredential: result.clientCredential,
credentialShownOnce: true,
protocol: result.protocol,
models: [...models],
deploymentMode: result.deploymentMode,
networkName: requireSafeDockerName(result.networkName, "sidecar network name"),
hostPublication: result.hostPublication,
account: copySiwcAccountView(result.account),
...copySiwcReadiness(result),
...(result.migratedLegacy === true ? { migratedLegacy: true, legacyRetained: result.legacyRetained === true } : {}),
...(result.replacedAccount === true ? { replacedAccount: true, oldHistoryRetained: result.oldHistoryRetained === true } : {}),
};
}
function createSafeLocalN8nSuperGrokInstallResult(result, reviewedPlan) {
if (
result?.target !== LOCAL_N8N_SUPERGROK_TARGET ||
result.endpoint !== LOCAL_N8N_SUPERGROK_ENDPOINT ||
result.baseUrl !== LOCAL_N8N_SUPERGROK_ENDPOINT ||
result.protocol !== "openai-chat-completions" ||
result.networkName !== reviewedPlan?.networkName ||
result.n8nContainerName !== reviewedPlan?.n8nContainerName ||
result.hostPublication !== "none" ||
typeof result.clientKey !== "string" ||
!/^[A-Za-z0-9_-]{32,256}$/u.test(result.clientKey) ||
result.credentialShownOnce !== true ||
result.deploymentMode !== "installed"
) {
throw Object.assign(
new Error("The local n8n SuperGrok installer returned an invalid result."),
{ statusCode: 502 },
);
}
return {
target: LOCAL_N8N_SUPERGROK_TARGET,
endpoint: LOCAL_N8N_SUPERGROK_ENDPOINT,
baseUrl: LOCAL_N8N_SUPERGROK_ENDPOINT,
protocol: "openai-chat-completions",
networkName: requireSafeDockerName(
result.networkName,
"SuperGrok network name",
),
n8nContainerName: requireSafeDockerName(
result.n8nContainerName,
"n8n container name",
),
hostPublication: "none",
clientCredential: result.clientKey,
credentialShownOnce: true,
deploymentMode: "installed",
models: ["grok-build"],
};
}
function createSafeLocalN8nSuperGrokStatus(result) {
if (
result?.target !== LOCAL_N8N_SUPERGROK_TARGET ||
typeof result.managed !== "boolean" ||
!["absent", "healthy", "unavailable"].includes(result.state) ||
(result.state === "healthy" ? result.managed !== true : result.managed !== false)
) {
throw Object.assign(
new Error("The local n8n SuperGrok status is invalid."),
{ statusCode: 502 },
);
}
if (result.state !== "healthy") {
return {
target: LOCAL_N8N_SUPERGROK_TARGET,
managed: false,
state: result.state,
snapshot: null,
};
}
let snapshot;
try {
snapshot = createSafeDashboardSnapshot(
LOCAL_N8N_SUPERGROK_TARGET,
result.snapshot,
);
} catch {
throw Object.assign(
new Error("The local n8n SuperGrok status is invalid."),
{ statusCode: 502 },
);
}
return {
target: LOCAL_N8N_SUPERGROK_TARGET,
managed: true,
state: "healthy",
snapshot,
};
}
function createSafeLocalN8nSuperGrokRemovalResult(result) {
if (
result?.target !== LOCAL_N8N_SUPERGROK_TARGET ||
result.removed !== true
) {
throw Object.assign(
new Error("The local n8n SuperGrok removal result is invalid."),
{ statusCode: 502 },
);
}
return { target: LOCAL_N8N_SUPERGROK_TARGET, removed: true };
}
function createSafeLocalN8nRemovalResult(result) {
if (
result?.target !== LOCAL_N8N_SIDECAR_TARGET ||
result.removed !== true
) {
throw Object.assign(
new Error("The local n8n sidecar removal result is invalid."),
{ statusCode: 502 },
);
}
return {
target: result.target,
removed: true,
...(IMAGES_REVOCATIONS.has(result.imagesRevocation) ? { imagesRevocation: result.imagesRevocation } : {}),
};
}
const MODEL_INSTALL_STATES = new Set(["absent", "unavailable", "partial", "runtime-ready", "downloading", "model-ready", "model-error"]);
const MODEL_PROGRESS_STATES = new Set(["downloading", "verifying", "model-ready", "model-error"]);
function createSafeLocalModelStatus(result) {
if (result?.target !== LOCAL_N8N_MODEL_TARGET || !MODEL_INSTALL_STATES.has(result.status) ||
typeof result.managed !== "boolean" ||
(["absent", "unavailable"].includes(result.status) ? result.managed !== false : result.managed !== true)) {
throw Object.assign(new TypeError("The local model status is invalid."), { statusCode: 502 });
}
const base = {
target: LOCAL_N8N_MODEL_TARGET, managed: result.managed, status: result.status,
installId: null, modelId: null, modelDigest: null, endpoint: null,
networkName: null, containerName: null, resourceBudget: null,
operationId: null, progress: null, reason: null,
};
if (result.status === "absent" || result.status === "unavailable") return base;
if (result.status === "partial") return { ...base, managed: true };
if (result.endpoint !== LOCAL_N8N_MODEL_ENDPOINT ||
!/^[a-f0-9-]{16,64}$/iu.test(result.installId) ||
(result.modelDigest !== null && !/^[a-f0-9]{64}$/u.test(result.modelDigest)) ||
(result.status === "model-ready" && !result.modelDigest)) {
throw Object.assign(new TypeError("The installed model identity is invalid."), { statusCode: 502 });
}
const budget = safeModelBudget(result.resourceBudget);
let progress = null;
if (result.progress !== null) {
if (!MODEL_PROGRESS_STATES.has(result.progress?.status) ||
(result.progress.completed !== null && (!Number.isSafeInteger(result.progress.completed) || result.progress.completed < 0)) ||
(result.progress.total !== null && (!Number.isSafeInteger(result.progress.total) || result.progress.total < 0)) ||
(result.progress.total !== null && result.progress.completed !== null && result.progress.completed > result.progress.total)) {
throw Object.assign(new TypeError("The model transfer status is invalid."), { statusCode: 502 });
}
progress = { status: result.progress.status, completed: result.progress.completed, total: result.progress.total };
}
if (result.operationId !== null && !/^[a-f0-9]{32}$/u.test(result.operationId)) {
throw Object.assign(new TypeError("The model operation identity is invalid."), { statusCode: 502 });
}
return {
...base, installId: result.installId, modelId: requireCatalogModel(result.modelId),
modelDigest: result.modelDigest, endpoint: LOCAL_N8N_MODEL_ENDPOINT,
networkName: requireSafeDockerName(result.networkName, "model network name"),
containerName: requireSafeDockerName(result.containerName, "n8n container name"),
resourceBudget: budget, operationId: result.operationId, progress,
};
}
function createSafeVpsLocalModelStatus(result) {
if (!MODEL_INSTALL_STATES.has(result?.state)) {
throw Object.assign(new TypeError("The VPS model status is invalid."), { statusCode: 502 });
}
if (result.state === "absent" || result.state === "unavailable") {
return { state: result.state, installId: null, modelId: null, endpoint: null, operation: null,
...(result.removed === true && result.modelCacheDeleted === true ? { removed: true, modelCacheDeleted: true } : {}) };
}
if (!/^[a-f0-9-]{16,64}$/iu.test(result.installId) ||
result.endpoint !== LOCAL_N8N_MODEL_ENDPOINT ||
!["string", "undefined"].includes(typeof result.containerId) ||
!["string", "undefined"].includes(typeof result.networkId)) {
throw Object.assign(new TypeError("The VPS model identity is invalid."), { statusCode: 502 });
}
const operation = result.operation;
let safeOperation = null;
if (operation !== null && operation !== undefined) {
if (!["downloading", "verifying", "model-ready", "model-error", "partial"].includes(operation.state) ||
!["download", "inference", "complete", "error", "unknown"].includes(operation.phase) ||
typeof operation.writerMayBeActive !== "boolean" ||
(operation.completedBytes !== null && (!Number.isSafeInteger(operation.completedBytes) || operation.completedBytes < 0)) ||
(operation.totalBytes !== null && (!Number.isSafeInteger(operation.totalBytes) || operation.totalBytes < 0)) ||
(operation.totalBytes !== null && operation.completedBytes !== null && operation.completedBytes > operation.totalBytes) ||
(operation.modelDigest !== null && !/^sha256:[a-f0-9]{64}$/u.test(operation.modelDigest)) ||
(operation.errorCode !== null && !/^[a-z][a-z0-9_-]{0,63}$/u.test(operation.errorCode))) {
throw Object.assign(new TypeError("The VPS model progress is invalid."), { statusCode: 502 });
}
safeOperation = {
state: operation.state, phase: operation.phase, completedBytes: operation.completedBytes,
totalBytes: operation.totalBytes, modelDigest: operation.modelDigest,
errorCode: operation.errorCode, writerMayBeActive: operation.writerMayBeActive,
};
}
if (result.state === "model-ready" && safeOperation?.state !== "model-ready") {
throw Object.assign(new TypeError("The VPS model inference is not verified."), { statusCode: 502 });
}
return {
state: result.state, installId: result.installId, modelId: requireCatalogModel(result.modelId),
endpoint: LOCAL_N8N_MODEL_ENDPOINT, operation: safeOperation,
};
}
function createSafeLocalModelActionReview(review, reviewId) {
if (!["retry", "remove"].includes(review?.action) ||
!/^[a-f0-9-]{16,64}$/iu.test(review.installId) ||
review.endpoint !== LOCAL_N8N_MODEL_ENDPOINT ||
review.removeModelData !== (review.action === "remove") ||
(review.modelDigest !== null && !/^[a-f0-9]{64}$/u.test(review.modelDigest)) ||
(review.operationId !== null && !/^[a-f0-9]{32}$/u.test(review.operationId))) {
throw Object.assign(new TypeError("The reviewed model action is invalid."), { statusCode: 502 });
}
return {
reviewId, action: review.action, installId: review.installId,
modelId: requireCatalogModel(review.modelId), endpoint: LOCAL_N8N_MODEL_ENDPOINT,
modelDigest: review.modelDigest,
networkName: requireSafeDockerName(review.networkName, "model network name"),
containerName: requireSafeDockerName(review.containerName, "n8n container name"),
resourceBudget: safeModelBudget(review.resourceBudget),
removeModelData: review.removeModelData,
};
}
function requireLiveLocalAction(state, action) {
if (state.previewMode) {
throw Object.assign(
new Error(`${action} is disabled in sanitized preview mode.`),
{ statusCode: 403 },
);
}
}
function localOAuthChangeInFlight(state) {
return oauthCredentialChangeInFlight(state);
}
function requireCurrentLocalDashboardGeneration(state, generation) {
if (state.localDashboardGeneration !== generation) {
throw Object.assign(
new Error(
"The local dashboard changed while this request was in progress. Try again.",
),
{ statusCode: 409 },
);
}
}
function requireExactRequestBody(body, names, message) {
if (
!body ||
typeof body !== "object" ||
Array.isArray(body) ||
Object.keys(body).length !== names.length ||
names.some((name) => !Object.hasOwn(body, name))
) {
throw new Error(message);
}
}
function requireOAuthLocalEndpointPlanBody(body) {
if (
!body ||
typeof body !== "object" ||
Array.isArray(body) ||
Object.keys(body).some((name) => !["target", "port"].includes(name))
) {
throw new Error("The OAuth local endpoint plan request is invalid.");
}
}
function requireExactLocalPlanBody(body, names, message) {
requireExactRequestBody(body, names, message);
}
function requireLocalInstallBody(plan, body) {
if (plan?.kind === "local-n8n-stack") {
const secretFields = ["ngrokAuthtoken", "basicAuthUsername", "basicAuthPassword"];
requireExactRequestBody(
body,
["planId", "confirmed", ...(plan.publicAccess === "ngrok"
? secretFields : secretFields.filter(key => Object.hasOwn(body, key) && body[key] === null))],
"The local n8n stack install request is invalid.",
);
return;
}
if (plan?.kind === "n8n-claude") {
requireExactRequestBody(body, ["planId", "confirmed", "review"], "The local Claude install request is invalid.");
validateClaudeReview(body.review);
if (body.confirmed !== true) throw new Error("Confirm the reviewed local Claude installation.");
return;
}
requireExactRequestBody(
body,
plan?.kind === "n8n-sidecar"
? ["planId", "confirmed", "backgroundConsent",
...(plan.migrationRequired ? ["migrationConsent"] : []),
...(plan.replacementRequired ? ["replacementConsent"] : [])]
: ["planId", "confirmed",
...(plan?.migrationRequired ? ["migrationConsent"] : []),
...(plan?.replacementRequired ? ["replacementConsent"] : [])],
plan?.kind === "n8n-sidecar"
? "The local n8n SIWC install request is invalid."
: plan?.kind === "n8n-assistant"
? "The local n8n Assistant install request is invalid."
: plan?.kind === "n8n-supergrok"
? "The local n8n SuperGrok install request is invalid."
: plan?.kind === LOCAL_N8N_MODEL_TARGET
? "The local model install request is invalid."
: "The OAuth local endpoint install request is invalid.",
);
}
async function requireReadyLocalChatTester(state) {
let status;
try {
status = await state.services.getManagedLocalEndpointStatus({ target: "codex-chat" });
} catch {
// An ambiguous installation cannot own a browser test session.
}
const account = status?.snapshot?.auth?.account;
if (status?.managed === true && status.state === "healthy" &&
status.snapshot?.target === "codex-chat" &&
status.snapshot?.migrationRequired === false &&
status.snapshot.auth.configured === true &&
account?.registrationId === status.snapshot.registrationId &&
account.ownership === "owned" && account.session === "connected" &&
account.planPermission === "granted" && account.planEnabled === true) {
return copySiwcAccountView(account);
}
throw Object.assign(new Error("Connect an owned ChatGPT plan to the Codex Chat Adapter before testing."),
{ statusCode: 409, recovery: "review-again" });
}
async function handleApi(request, response, path, state) {
requireApiToken(request, state);
requireSameOrigin(request, state);
if (path === "/api/ui/preferences" && request.method === "GET") {
sendJson(response, 200, await state.services.readUiPreferences({ storageRoot: state.storageRoot }));
return;
}
if (path === "/api/ui/preferences" && request.method === "POST") {
const body = await readJsonBody(request);
if (!exactObjectKeys(body, ["guide"]) || !["on", "off"].includes(body.guide)) {
throw Object.assign(new Error("Guide preference is invalid."), { statusCode: 400 });
}
await state.services.writeUiPreferences({ storageRoot: state.storageRoot, preferences: body });
sendJson(response, 200, { guide: body.guide });
return;
}
if (request.method === "GET" && path === "/api/hosting/providers") {
sendJson(response, 200, {
providers: HOSTING_PROVIDERS,
profiles: getHostingDeploymentProfiles(),
models: LOCAL_MODEL_CATALOG,
});
return;
}
if (request.method === "GET" && path === "/api/ssh/capabilities") {
const capability = await state.services.getSshCapabilities();
const agent = capability?.agent;
sendJson(response, 200, { agent: {
status: agent?.status === "configured" ? "configured" : "unavailable",
transport: ["unix-socket", "windows-openssh-pipe"].includes(agent?.transport) ? agent.transport : null,
verification: "untested",
} });
return;
}
if (request.method === "GET" && path === "/api/ssh/connection") {
// No session is an expected state on every route load, not a failure.
if (!state.connection) {
sendJson(response, 200, { connected: false });
return;
}
if (!state.connectionIdentity) throw new Error("Reconnect to verify the VPS identity.");
sendJson(response, 200, state.connectionIdentity);
return;
}
if (request.method === "GET" && path === "/api/ssh/host-report") {
// Built from the connection probe and the cached discovery; no remote call.
const connection = requireConnection(state);
if (!state.connectionIdentity) throw new Error("Reconnect to verify the VPS identity.");
const query = new URL(request.url, state.origin).searchParams;
const selection = ["container", "network"].map((key) => query.getAll(key));
if ([...query.keys()].some((key) => key !== "container" && key !== "network") ||
selection.some((values) => values.length > 1 ||
values.some((value) => value.length > 128 || !/^[A-Za-z0-9][A-Za-z0-9_.-]*$/u.test(value)))) {
throw new Error("The host report selection is invalid.");
}
const [containerName = null, networkName = null] = selection.map((values) => values[0]);
sendJson(response, 200, buildVpsHostReport({
identity: state.connectionIdentity, host: connection.host ?? null, discovery: state.discovery,
networks: containerName === null ? undefined : state.networksByContainer.get(containerName),
containerName, networkName,
}));
return;
}
if (credentialBearingVpsRoute(path)) {
rejectActiveVpsMutation(state);
requireFullVpsScope(requireConnection(state));
}
if (request.method === "GET" && path === "/api/siwc/accounts") {
const registrations = await state.services.listAuthRegistrations({ storageRoot: state.storageRoot });
const accounts = await Promise.all(registrations.filter((item) => item.identity === "verified")
.map(async (item) => {
const account = copySiwcAccountView(item);
if (account.ownership !== "owned" || account.session !== "connected") return account;
let hostReady = false;
try {
const status = await state.services.getAuthStatus({
storageRoot: state.storageRoot, registrationId: account.registrationId, runtimeId: state.runtimeId,
});
hostReady = status?.exists === true && status.generation === account.generation &&
status.ownerHostId === account.ownerHostId && status.ownerRuntimeId === account.ownerRuntimeId;
} catch {
// Invalid or foreign protected records remain visible, never usable.
}
return { ...account, hostReady };
}));
const pendingRegistrations = registrations.filter((item) => item.identity === "unverified")
.map((item) => ({ registrationId: requireSiwcId(item.registrationId),
label: typeof item.label === "string" && item.label.length <= 160 ? item.label : "ChatGPT sign-in pending" }));
const selected = await state.services.getSelectedRegistration({ storageRoot: state.storageRoot });
sendJson(response, 200, {
accounts, pendingRegistrations,
selectedRegistrationId: accounts.some((account) => account.registrationId === selected) ? selected : null,
...(state.previewMode ? { previewMode: true } : {}),
...(state.previewFixture ? { previewFixture: state.previewFixture } : {}),
});
return;
}
if (request.method === "GET" && path === "/api/oauth/status") {
const login = state.oauthLogin;
const { status: failureStatus, ...failure } = login?.failure ?? {};
sendJson(response, 200, {
status: login?.status ?? (state.oauthStartupError ? "error" : "idle"),
...(login ? { attemptId: login.attemptId } : {}),
...(login?.status === "error"
? { ...failure, ...(failureStatus ? { upstreamStatus: failureStatus } : {}) }
: state.oauthStartupError
? { error: state.oauthStartupError }
: {}),
...(login?.status === "success" ? { account: copySiwcAccountView(login.account) } : {}),
...(state.oauthRetryBlocked || login?.retryBlocked === true
? { retryBlocked: true }
: {}),
});
return;
}
if (request.method === "GET" && path === "/api/local/dashboard") {
const localDashboardGeneration = state.localDashboardGeneration;
const status = state.previewMode
? null
: await state.services.getLocalDashboardStatus({
inspectLocalN8nStack: state.services.getLocalN8nStackStatus,
inspectLocalN8nSuperGrok: state.services.getLocalN8nSuperGrokStatus,
inspectLocalN8nClaude: state.services.getLocalN8nClaudeStatus,
inspectLocalN8nModel: state.services.getLocalN8nModelStatus,
});
requireCurrentLocalDashboardGeneration(state, localDashboardGeneration);
sendJson(
response,
200,
createSafeLocalDashboardStatus(status, state.previewMode, state.previewFixture),
);
return;
}
if (request.method === "GET" && path === "/api/local/supergrok/status") {
const localDashboardGeneration = state.localDashboardGeneration;
const status = state.previewMode
? {
target: LOCAL_N8N_SUPERGROK_TARGET,
managed: false,
state: "absent",
}
: await state.services.getLocalN8nSuperGrokStatus();
requireCurrentLocalDashboardGeneration(state, localDashboardGeneration);
sendJson(response, 200, createSafeLocalN8nSuperGrokStatus(status));
return;
}
if (request.method === "GET" && path === "/api/local/claude/status") {
const generation = state.localDashboardGeneration;
const result = state.previewMode
? { target: LOCAL_N8N_CLAUDE_TARGET, managed: false, state: "absent" }
: await state.services.getLocalN8nClaudeStatus();
requireCurrentLocalDashboardGeneration(state, generation);
sendJson(response, 200, createSafeLocalN8nClaudeStatus(result));
return;
}
if (request.method === "GET" && (path === "/api/local/model/status" || path === "/api/local/model/operation")) {
const generation = state.localDashboardGeneration;
const result = state.previewMode
? { target: LOCAL_N8N_MODEL_TARGET, managed: false, status: "absent" }
: await state.services.getLocalN8nModelStatus();
requireCurrentLocalDashboardGeneration(state, generation);
sendJson(response, 200, createSafeLocalModelStatus(result));
return;
}
if (request.method === "GET" && path === "/api/local/usage/status") {
const generation = state.localDashboardGeneration;
// Read-only. Sanitized preview shows no counts; the sidecar's record becomes the bounded view here.
const record = state.previewMode ? null : await state.services.getLocalN8nSidecarUsage();
requireCurrentLocalDashboardGeneration(state, generation);
sendJson(response, 200, usageView(record));
return;
}
if (request.method === "GET" && path === "/api/siwc/models") {
requireLiveLocalAction(state, "ChatGPT model discovery");
if (oauthCredentialChangeInFlight(state)) {
throw Object.assign(new Error("Wait for ChatGPT sign-in to finish."), { statusCode: 409 });
}
const account = await selectedSiwcAccount(state, { requirePlan: true });
const models = await state.services.listSiwcModels({
storageRoot: state.storageRoot, registrationId: account.registrationId, runtimeId: state.runtimeId,
});
// Token refresh inside listSiwcModels rotates `generation`; the re-read below
// still re-validates ownership, connection and plan use for this registration.
const latest = await selectedSiwcAccount(state, { requirePlan: true });
if (latest.registrationId !== account.registrationId) {
throw Object.assign(new Error("The selected ChatGPT account changed. Refresh it."), { statusCode: 409 });
}
if (!Array.isArray(models) ||
models.some((model) => typeof model?.slug !== "string" ||
!/^[A-Za-z0-9][A-Za-z0-9_.:-]{0,127}$/u.test(model.slug) ||
typeof model.display_name !== "string" || model.display_name.length > 256)) {
throw Object.assign(new Error("The account model catalog could not be verified."), { statusCode: 409 });
}
sendJson(response, 200, { models, account: latest });
return;
}
if (request.method === "GET" && path === "/api/local/docker/status") {
const status = state.previewMode
? null
: await state.services.getLocalDockerStatus();
let localN8nStackState = null;
if (!state.previewMode && status?.dockerAvailable === true) {
try {
const managedStatus = await state.services.getLocalN8nStackStatus();
if (
managedStatus?.managed === true &&
["healthy", "stopped", "partial"].includes(managedStatus.state)
) {
localN8nStackState = managedStatus.state;
} else if (
managedStatus?.managed === false &&
managedStatus.state === "unavailable"
) {
localN8nStackState = "unavailable";
}
} catch {
// Docker readiness remains useful when managed-stack detection fails closed.
}
}
sendJson(
response,
200,
createSafeDockerStatus(
status,
state.previewMode,
localN8nStackState,
),
);
return;
}
if (request.method === "GET" && path === "/api/local/n8n/discover") {
const localDashboardGeneration = state.localDashboardGeneration;
const discovery = state.previewMode
? null
: await state.services.discoverLocalN8nSidecarTargets();
requireCurrentLocalDashboardGeneration(state, localDashboardGeneration);
let managedTarget = null;
if (discovery?.dockerAvailable === true) {
try { managedTarget = await state.services.getManagedLocalN8nStackTarget(); }
catch { /* Keep unrelated n8n discovery available. */ }
}
state.localPlan = null;
sendJson(
response,
200,
createSafeLocalN8nDiscovery(discovery, state.previewMode, managedTarget),
);
return;
}
if (request.method === "GET" && path === "/api/local/project-meta") {
sendJson(
response,
200,
createSafeProjectMeta(await state.services.getProjectMeta()),
);
return;
}
// Read-only: no selection is needed because one Claude companion serves the whole Docker host.
if (request.method === "GET" && path === "/api/vps/claude/status") {
const connectionUse = acquireVpsConnectionUse(state);
try {
const { connection } = connectionUse;
requireFullVpsScope(connection);
const snapshot = { connection, lifecycleGeneration: state.vpsLifecycleGeneration };
const status = createSafeVpsClaudeStatus(await state.services.getVpsClaudeStatus({ remote: connection }));
requireUnchangedVpsSession(state, snapshot);
sendJson(response, 200, { ...status, loginCommand: vpsClaudeLoginCommand(state.connectionIdentity) });
} finally {
connectionUse.release();
}
return;
}
if (request.method !== "POST") {
sendJson(response, 405, { error: "Method not allowed." });
return;
}
if (path === "/api/oauth/login") {
const loginBody = await readJsonBody(request);
if (!loginBody || typeof loginBody !== "object" || Array.isArray(loginBody) ||
Object.keys(loginBody).some((key) => !["purpose", "registrationId", "launchMode"].includes(key)) ||
!["sign-in", "enable-plan"].includes(loginBody.purpose) ||
(loginBody.launchMode !== undefined && !["system-browser", "same-browser"].includes(loginBody.launchMode)) ||
(loginBody.registrationId !== undefined && typeof loginBody.registrationId !== "string")) {
throw invalidSiwcRequest();
}
const registrationId = loginBody.registrationId === undefined
? undefined : requireSiwcId(loginBody.registrationId);
if (loginBody.purpose === "enable-plan") {
const selected = await selectedSiwcAccount(state);
if (registrationId !== selected.registrationId) throw invalidSiwcRequest();
} else if (registrationId !== undefined) {
const accounts = await state.services.listAuthRegistrations({ storageRoot: state.storageRoot });
if (!accounts.some((account) => account.registrationId === registrationId &&
account.ownership === "owned")) throw invalidSiwcRequest();
}
if (state.previewMode) {
throw Object.assign(
new Error(
"Live ChatGPT sign-in is disabled in sanitized preview mode.",
),
{ statusCode: 403 },
);
}
if (state.closing) {
throw Object.assign(new Error("The local wizard is closing."), {
statusCode: 409,
});
}
if (
state.localInstallInFlight ||
state.localCredentialRotationInFlight ||
getPendingLocalCredentialRotation(state)
) {
throw Object.assign(
new Error("A local endpoint change is already in progress."),
{ statusCode: 409 },
);
}
if (
state.vpsMutationInFlight ||
state.vpsCredentialOperation !== null ||
state.vpsConnectionOperation !== null
) {
throw Object.assign(new Error(VPS_OAUTH_CONFLICT_MESSAGE), {
statusCode: 409,
});
}
if (state.oauthRetryBlocked || state.oauthLogin?.retryBlocked === true) {
throw Object.assign(
new Error(
"ChatGPT sign-in could not be stopped safely. Close the sign-in helper, then restart Relmio.",
),
{ retryBlocked: true, statusCode: 409 },
);
}
if (state.oauthLoginStartInFlight) {
throw Object.assign(
new Error("A ChatGPT sign-in start is already in progress."),
{ statusCode: 409 },
);
}
enforceRateLimit(state, path);
state.oauthLoginStartInFlight = true;
state.oauthStartupError = null;
let startAttempted = false;
let startPromise;
let credentialOperation;
let operationTransferred = false;
try {
const previousLogin = state.oauthLogin;
if (previousLogin?.status === "pending") {
setOAuthCredentialOperationPhase(
state,
previousLogin.credentialOperation,
"cancelling-for-replacement",
);
await cancelOAuthLogin(state, previousLogin);
}
if (state.closing) {
throw Object.assign(new Error("The local wizard is closing."), {
statusCode: 409,
});
}
if (
state.oauthLogin === previousLogin &&
previousLogin?.status !== "pending" &&
previousLogin?.retryBlocked !== true
) {
state.oauthLogin = null;
}
credentialOperation = beginOAuthCredentialOperation(state);
startAttempted = true;
startPromise = Promise.resolve(state.services.startOAuthLogin({
storageRoot: state.storageRoot, registrationId, purpose: loginBody.purpose,
runtimeId: state.runtimeId,
...(loginBody.launchMode === undefined ? {} : { launchMode: loginBody.launchMode }),
}));
state.oauthLoginStartPromise = startPromise;
const attempt = await startPromise;
if (
(attempt?.launchMode !== "system-browser" &&
!(loginBody.launchMode === "same-browser" && attempt?.launchMode === "same-browser" &&
typeof attempt?.consumeBrowserAuthorization === "function")) ||
typeof attempt?.completion?.then !== "function" ||
typeof attempt?.cancel !== "function"
) {
try {
await attempt?.cancel?.();
} catch {
// The invalid helper result is already unusable; keep its details private.
}
throw Object.assign(
new Error("The ChatGPT sign-in helper returned an invalid result."),
{ statusCode: 500 },
);
}
const login = {
attempt,
attemptId: randomUUID(),
error: null,
retryBlocked: false,
status: "pending",
credentialOperation,
};
state.oauthLogin = login;
operationTransferred = true;
setOAuthCredentialOperationPhase(
state,
credentialOperation,
"pending-or-committing",
);
void Promise.resolve(attempt.completion)
.then(
async (account) => {
if (state.oauthLogin !== login || login.status !== "pending" || state.closing) return;
if (account?.identity !== "verified" || typeof account.registrationId !== "string" ||
(registrationId !== undefined && account.registrationId !== registrationId)) {
throw new Error("ChatGPT identity could not be verified.");
}
await state.services.selectRegistration({
storageRoot: state.storageRoot, registrationId: account.registrationId,
});
if (state.oauthLogin === login && login.status === "pending" && !state.closing) {
invalidateSiwcWork(state);
login.account = account;
login.status = "success";
}
},
)
.catch((error) => {
if (state.oauthLogin === login && login.status === "pending" && !state.closing) {
login.status = "error";
login.failure = { ...safeFailure(error), phase: "authorize",
...(typeof error?.registrationId === "string" &&
/^[A-Za-z0-9_-]{8,128}$/u.test(error.registrationId)
? { registrationId: error.registrationId } : {}) };
login.error = login.failure.error;
if (error?.retryBlocked === true) {
login.retryBlocked = true;
state.oauthRetryBlocked = true;
state.oauthStartupError = login.error;
}
}
})
.finally(() => {
releaseOAuthCredentialOperation(state, credentialOperation);
});
if (state.closing) {
try {
await cancelOAuthLogin(state, login);
} catch {
// The server is already closing and must not restart this helper.
}
throw Object.assign(new Error("The local wizard is closing."), {
statusCode: 409,
});
}
sendJson(response, 200, {
launchMode: attempt.launchMode,
attemptId: login.attemptId,
});
return;
} catch (error) {
const message = safeErrorMessage(error);
if (startAttempted) {
state.oauthStartupError = message;
}
if (error?.retryBlocked === true) {
state.oauthRetryBlocked = true;
state.oauthStartupError = message;
}
if (!operationTransferred) {
releaseOAuthCredentialOperation(state, credentialOperation);
}
throw error;
} finally {
if (state.oauthLoginStartPromise === startPromise) {
state.oauthLoginStartPromise = null;
}
state.oauthLoginStartInFlight = false;
}
}
const body = await readJsonBody(request);
// Body parsing yields: a different authenticated connection may now own the session.
if (credentialBearingVpsRoute(path)) {
rejectActiveVpsMutation(state);
requireFullVpsScope(requireConnection(state));
}
if (["/api/siwc/select", "/api/siwc/plan", "/api/siwc/ack", "/api/siwc/logout"].includes(path)) {
requireLiveLocalAction(state, "ChatGPT account changes");
enforceRateLimit(state, path);
if (oauthCredentialChangeInFlight(state) || state.localInstallInFlight ||
state.localCredentialRotationInFlight || state.vpsMutationInFlight ||
state.vpsCredentialOperation !== null || state.vpsConnectionOperation !== null) {
throw Object.assign(new Error("Wait for the current ChatGPT or installation action to finish."), { statusCode: 409 });
}
const registrationId = requireSiwcId(body?.registrationId);
const registration = { storageRoot: state.storageRoot, registrationId };
const accounts = await state.services.listAuthRegistrations({ storageRoot: state.storageRoot });
const account = accounts.find((item) => item.registrationId === registrationId);
if (!account || account.identity !== "verified" || account.ownership !== "owned") throw invalidSiwcRequest();
if (path === "/api/siwc/select") {
requireExactRequestBody(body, ["registrationId"], "Select one ChatGPT account.");
if (account.session === "connected" && account.ownership === "owned") {
const owned = await state.services.getAuthStatus({
storageRoot: state.storageRoot, registrationId, runtimeId: state.runtimeId,
});
if (owned?.exists !== true || owned.generation !== account.generation) {
throw Object.assign(new Error("This account is not owned by this computer."), { statusCode: 409 });
}
}
await state.services.selectRegistration(registration);
invalidateSiwcWork(state);
sendJson(response, 200, { account: copySiwcAccountView(account), selectedRegistrationId: registrationId });
return;
}
if (path === "/api/siwc/plan") {
requireExactRequestBody(body, ["registrationId", "enabled", "expectedGeneration"], "The plan preference request is invalid.");
if (typeof body.enabled !== "boolean" || body.expectedGeneration !== account.generation ||
account.ownership !== "owned" || account.session !== "connected" ||
await state.services.getSelectedRegistration({ storageRoot: state.storageRoot }) !== registrationId) {
throw Object.assign(new Error("The selected account changed. Choose it again."), { statusCode: 409 });
}
const updated = await state.services.setPlanEnabled(registration, {
enabled: body.enabled, expectedGeneration: body.expectedGeneration,
});
invalidateSiwcWork(state);
sendJson(response, 200, { account: copySiwcAccountView(updated) });
return;
}
if (path === "/api/siwc/ack") {
requireExactRequestBody(body, ["registrationId", "expectedGeneration"], "The plan confirmation request is invalid.");
if (account.generation !== body.expectedGeneration || account.planEnabled !== true ||
account.planPermission !== "granted" || account.session !== "connected" ||
account.ownership !== "owned" ||
await state.services.getSelectedRegistration({ storageRoot: state.storageRoot }) !== registrationId) {
throw Object.assign(new Error("The selected plan changed. Check it again."), { statusCode: 409 });
}
const updated = await state.services.acknowledgePlanUse(registration, {
expectedGeneration: body.expectedGeneration,
});
invalidateSiwcWork(state);
sendJson(response, 200, { account: copySiwcAccountView(updated) });
return;
}
if (path === "/api/siwc/logout") {
requireExactRequestBody(body, ["registrationId", "expectedGeneration"], "The sign-out request is invalid.");
if (account.generation !== body.expectedGeneration ||
await state.services.getSelectedRegistration({ storageRoot: state.storageRoot }) !== registrationId) {
throw Object.assign(new Error("The selected account changed. Choose it again."), { statusCode: 409 });
}
const result = await state.services.signOut(registration, { runtimeId: state.runtimeId });
invalidateSiwcWork(state);
if (!["confirmed", "unconfirmed", "not-applicable"].includes(result?.revocation)) {
throw Object.assign(new Error("Provider revocation could not be verified."), { statusCode: 502 });
}
sendJson(response, 200, {
account: copySiwcAccountView(result.account), revocation: result.revocation,
...(result.revocation === "unconfirmed"
? { cleanup: "Local credentials cleared. Revocation could not be confirmed. Disconnect Relmio in ChatGPT settings." }
: {}),
});
return;
}
}
if (path === "/api/hosting/plan") {
try {
sendJson(response, 200, state.services.prepareHostingDeploymentPlan(body));
} catch {
throw Object.assign(new Error("The hosting plan request is invalid."), { statusCode: 400 });
}
return;
}
if (["/api/local/siwc/recovery/review", "/api/local/siwc/recovery/reconcile",
"/api/siwc/vps/recovery/review", "/api/siwc/vps/recovery/reconcile"].includes(path)) {
requireLiveLocalAction(state, "ChatGPT installation recovery");
enforceRateLimit(state, path);
const vps = path.startsWith("/api/siwc/vps/");
const reviewing = path.endsWith("/review");
const freshN8nTarget = reviewing && !vps && body?.target === LOCAL_N8N_SIDECAR_TARGET &&
body.action === "resume" && (body.n8nContainerId !== undefined || body.dockerNetworkId !== undefined);
requireExactRequestBody(body, reviewing
? vps ? ["containerName", "networkName", "registrationId", "action"]
: ["target", "registrationId", "action", ...(freshN8nTarget ? ["n8nContainerId", "dockerNetworkId"] : [])]
: ["reviewId", "confirmed"], "Review this exact installation before confirming recovery.");
if (freshN8nTarget && (!/^[a-f0-9]{64}$/u.test(body.n8nContainerId ?? "") ||
!/^[a-f0-9]{64}$/u.test(body.dockerNetworkId ?? ""))) throw invalidSiwcRequest();
if (state.localInstallInFlight || state.localCredentialRotationInFlight ||
getPendingLocalCredentialRotation(state) || oauthCredentialChangeInFlight(state) ||
state.vpsMutationInFlight || state.vpsCredentialOperation || state.vpsConnectionOperation) {
throw Object.assign(new Error("An account or installation change is in progress."), { statusCode: 409 });
}
const pending = state.siwcRecoveryReview;
if (!reviewing && (body.confirmed !== true || !pending || pending.vps !== vps ||
pending.expiresAt <= Date.now() || !tokenMatches(body.reviewId, pending.reviewId))) {
throw Object.assign(new Error("Review and confirm this transfer recovery again."), { statusCode: 409 });
}
const target = vps ? LOCAL_N8N_SIDECAR_TARGET : reviewing ? body.target : pending.target;
if (!["codex-chatgpt", "codex-chat", LOCAL_N8N_SIDECAR_TARGET].includes(target) ||
(reviewing && !["resume", "reconcile"].includes(body.action))) throw invalidSiwcRequest();
const connectionUse = vps ? acquireVpsConnectionUse(state) : null;
let operation;
let releaseMutation;
if (!vps) state.localInstallInFlight = true;
try {
let reviewedTarget;
if (vps) {
requireFullVpsScope(connectionUse.connection);
if (reviewing) {
requireDiscoveredNetwork(state, body.containerName, body.networkName);
operation = acquireVpsCredentialOperation(state, "recovery-review");
reviewedTarget = Object.freeze({ ...state.connectionIdentity,
containerName: body.containerName, networkName: body.networkName,
...await state.services.reviewVpsSiwcTarget({
remote: connectionUse.connection, containerName: body.containerName, networkName: body.networkName,
}) });
} else {
reviewedTarget = pending.reviewedTarget;
if (!["host", "port", "fingerprint", "username", "authentication", "privilege",
"loginUid", "effectiveUid"].every((key) => reviewedTarget[key] === state.connectionIdentity[key])) {
throw Object.assign(new Error("Reconnect to the reviewed VPS identity."), { statusCode: 409 });
}
releaseMutation = acquireVpsMutationLock(state);
}
}
if (reviewing) {
state.siwcRecoveryReview = null;
const account = await recoveryAccount(state, body.registrationId, { pending: body.action === "reconcile" });
const sourceBinding = await siwcBinding(state, account);
if (body.action === "reconcile") {
const reviewId = randomUUID();
state.siwcRecoveryReview = { reviewId, vps, target, sourceBinding, reviewedTarget,
expiresAt: Date.now() + 5 * 60_000 };
sendJson(response, 200, { reviewId, action: "reconcile", target, account,
...(vps ? { destination: { n8nContainerId: reviewedTarget.n8nContainerId,
networkId: reviewedTarget.networkId } } : {}) });
} else {
let freshPlan;
if (freshN8nTarget) {
const discovery = await state.services.discoverLocalN8nSidecarTargets();
const container = discovery?.dockerAvailable === true && discovery.containers?.find(
(item) => item.containerId === body.n8nContainerId);
const network = container && container.networks?.find(
(item) => item.dockerNetworkId === body.dockerNetworkId);
if (!container || !network) {
throw Object.assign(new Error("Refresh n8n discovery and select its attached network before reviewing resume."),
{ statusCode: 409, recovery: "review-again" });
}
freshPlan = state.services.prepareLocalN8nSidecarPlan({
dockerHost: discovery.dockerHost, n8nContainerId: container.containerId,
n8nContainerName: container.containerName, dockerNetworkId: network.dockerNetworkId,
networkName: network.networkName, authBinding: sourceBinding,
});
}
const resume = vps
? await state.services.reviewVpsSiwcResume({ remote: connectionUse.connection,
networkName: reviewedTarget.networkName, reviewedTarget, registrationId: account.registrationId })
: target === LOCAL_N8N_SIDECAR_TARGET
? await state.services.reviewLocalN8nSiwcResume({ registration: {
storageRoot: state.storageRoot, registrationId: account.registrationId },
...(freshPlan ? { plan: freshPlan } : {}) })
: await state.services.reviewLocalSiwcResume({ target, registration: {
storageRoot: state.storageRoot, registrationId: account.registrationId } });
const staging = copySiwcStaging(resume);
if (staging.registrationId !== account.registrationId) throw invalidSiwcRequest();
const planId = randomUUID();
if (vps) {
if (!["installed", "migrated", "replaced"].includes(resume.deploymentMode)) {
throw Object.assign(new Error("The reviewed resume deployment mode is invalid."), { statusCode: 502 });
}
state.sidecarPlan = { planId, containerName: reviewedTarget.containerName,
networkName: reviewedTarget.networkName, reviewedTarget, authBinding: sourceBinding,
account, resume, sourceBinding };
sendJson(response, 200, { planId, installDirectory: "/docker/n8n-openai-oauth",
operationLockPath: "/docker/n8n-openai-oauth/.openai-oauth-operation.lock",
temporaryBuildStatePath: "/docker/n8n-openai-oauth/.openai-oauth-operation.lock/buildx",
endpointHostname: SIDECAR_HOSTNAME, containerName: reviewedTarget.containerName,
networkName: reviewedTarget.networkName, n8nContainerId: reviewedTarget.n8nContainerId,
networkId: reviewedTarget.networkId, account, resumeRequired: true, staging });
} else {
if (resume.plan?.target !== target ||
resume.plan.authBinding?.registrationId !== account.registrationId) throw invalidSiwcRequest();
const plan = { ...resume.plan, account, resumeRequired: true, staging };
state.localPlan = { planId, plan, resume, sourceBinding };
sendJson(response, 200, { planId, plan: target === LOCAL_N8N_SIDECAR_TARGET
? createSafeLocalN8nPlan(plan) : createSafeLocalPlan(plan) });
}
}
} else {
state.siwcRecoveryReview = null;
await requireRecoveryBinding(state, pending.sourceBinding, { pending: true });
const registration = { storageRoot: state.storageRoot, registrationId: pending.sourceBinding.registrationId };
const result = vps
? await state.services.reconcileVpsSiwcHandoff({ remote: connectionUse.connection,
networkName: reviewedTarget.networkName, reviewedTarget, registration, confirmed: true })
: target === LOCAL_N8N_SIDECAR_TARGET
? await state.services.reconcileLocalN8nSiwcHandoff({ registration, confirmed: true })
: await state.services.reconcileLocalSiwcHandoff({ target, registration, confirmed: true });
const account = copySiwcAccountView(result?.account);
if (!["finished", "not-accepted"].includes(result?.outcome) ||
account.registrationId !== registration.registrationId ||
account.ownership !== (result.outcome === "finished" ? "transferred" : "handoff-pending")) {
throw Object.assign(new Error("Transfer recovery was not confirmed."), { statusCode: 502 });
}
invalidateSiwcWork(state);
sendJson(response, 200, { outcome: result.outcome, account });
}
} finally {
operation?.release();
if (releaseMutation) {
detachVpsConnection(state, connectionUse.connection);
releaseMutation();
}
connectionUse?.release();
if (!vps) state.localInstallInFlight = false;
}
return;
}
if (path === "/api/local/discard") {
if (
state.localInstallInFlight ||
state.localCredentialRotationInFlight
) {
throw Object.assign(
new Error("A local endpoint change is already in progress."),
{ statusCode: 409 },
);
}
state.localDashboardGeneration += 1;
state.localPlan = null;
state.localAssistantSearxngReview = null;
state.localModelActionReview = null;
state.localCredentialRotationPending = null;
state.localInstalledTarget = null;
state.localChatTest.resetAll?.();
sendJson(response, 200, { discarded: true });
return;
}
if (["/api/local/siwc/manage", "/api/local/n8n/siwc/manage",
"/api/local/siwc/inspect-stopped", "/api/local/n8n/siwc/inspect-stopped"].includes(path)) {
requireLiveLocalAction(state, "Installed ChatGPT account management");
enforceRateLimit(state, path);
const sidecar = path.startsWith("/api/local/n8n/");
const inspecting = path.endsWith("/inspect-stopped");
requireExactRequestBody(body, inspecting
? sidecar ? ["registrationId", "confirmed"] : ["target", "registrationId", "confirmed"]
: sidecar
? ["registrationId", "expectedGeneration", "action", "confirmed",
...(body?.action === "enable-plan" ? ["backgroundConsent"] : [])]
: ["target", "registrationId", "expectedGeneration", "action", "confirmed"],
"Choose the installed account and confirm this owner action.");
const registrationId = requireSiwcId(body.registrationId);
const target = sidecar ? "n8n-openai-oauth" : body.target;
if (body.confirmed !== true ||
(!sidecar && !["codex-chatgpt", "codex-chat"].includes(target)) ||
(!inspecting && !["sign-out", "disable-plan", "enable-plan"].includes(body.action)) ||
(sidecar && body.action === "enable-plan" && body.backgroundConsent !== true)) throw invalidSiwcRequest();
if (state.localInstallInFlight || state.localCredentialRotationInFlight ||
oauthCredentialChangeInFlight(state) || getPendingLocalCredentialRotation(state)) {
throw Object.assign(new Error("An installation or account change is in progress."), { statusCode: 409 });
}
state.localInstallInFlight = true;
try {
const status = sidecar
? await state.services.getLocalN8nSidecarStatus()
: await state.services.getManagedLocalEndpointStatus({ target });
if (status?.managed !== true || status.snapshot?.migrationRequired !== false ||
status.snapshot.registrationId !== registrationId) {
throw Object.assign(new Error("The installed account changed. Refresh its status."), { statusCode: 409 });
}
if (inspecting) {
if (status.state !== "stopped" || status.snapshot.auth?.account) {
throw Object.assign(new Error("This owner is not stopped. Refresh its status."), { statusCode: 409 });
}
const result = sidecar
? await state.services.inspectStoppedLocalN8nSiwcInstallation({ registrationId, confirmed: true })
: await state.services.inspectStoppedLocalSiwcInstallation({ target, registrationId, confirmed: true });
const account = copySiwcAccountView(result.account);
state.localStoppedOwnerReview = { target, account, expiresAt: Date.now() + 5 * 60_000 };
state.localPlan = null;
state.localDashboardGeneration += 1;
state.localChatTest.resetAll?.();
sendJson(response, 200, { account });
return;
}
const account = status.snapshot.auth?.account;
const live = status.state === "healthy" && account?.registrationId === registrationId &&
account.generation === body.expectedGeneration && account.ownership === "owned";
const review = state.localStoppedOwnerReview;
const stopped = status.state === "stopped" && !account && review?.target === target &&
review.account.registrationId === registrationId &&
review.account.generation === body.expectedGeneration && review.expiresAt > Date.now();
if (!live && !stopped) {
throw Object.assign(new Error("The installed session changed. Inspect its owner again."),
{ statusCode: 409, recovery: "review-again" });
}
state.localStoppedOwnerReview = null;
const result = sidecar
? await state.services.manageLocalN8nSiwcInstallation({
registrationId, expectedGeneration: body.expectedGeneration, action: body.action, confirmed: true,
...(body.action === "enable-plan" ? { backgroundConsent: true } : {}),
})
: await state.services.manageLocalSiwcInstallation({
target, registrationId, expectedGeneration: body.expectedGeneration,
action: body.action, confirmed: true,
});
invalidateSiwcWork(state);
if (result.runtimeStopped !== (body.action !== "enable-plan") ||
!["confirmed", "unconfirmed", "not-applicable"].includes(result.revocation)) {
throw Object.assign(new Error("The installed owner result was not confirmed."), { statusCode: 502 });
}
const updated = copySiwcAccountView(result.account);
if (body.action === "sign-out" && updated.session === "signed-out" &&
updated.planEnabled === false) {
state.localStoppedOwnerReview = { target, account: updated, expiresAt: Date.now() + 5 * 60_000 };
}
sendJson(response, 200, {
account: updated, revocation: result.revocation, runtimeStopped: result.runtimeStopped,
...(sidecar && body.action === "sign-out" && IMAGES_REVOCATIONS.has(result.imagesRevocation)
? { imagesRevocation: result.imagesRevocation } : {}),
});
} finally {
state.localInstallInFlight = false;
}
return;
}
if (["/api/local/n8n/siwc/images/status", "/api/local/n8n/siwc/images/action",
"/api/local/n8n/siwc/images/login-status"].includes(path)) {
requireLiveLocalAction(state, "Local image generation");
const checking = path.endsWith("/images/status");
const polling = path.endsWith("/login-status");
const action = checking ? null : polling ? "login-poll" : body?.action;
// As on a VPS, polls skip the shared rate limit; a stored pending sign-in, the single-flight
// flag and the code expiry bound them instead.
if (!polling) enforceRateLimit(state, path);
requireExactRequestBody(body, checking ? ["registrationId"] : polling ? [] : ["action", "confirmed"],
"Check this installed sidecar's image generation again.");
if (!checking && !polling && (!["login-start", "login-cancel", "sign-out"].includes(action) ||
typeof body.confirmed !== "boolean" || (action !== "login-cancel" && body.confirmed !== true))) {
throw Object.assign(new Error("Confirm this image sign-in change first."), { statusCode: 400 });
}
const stored = checking ? null : state.localImagesTarget;
if (!checking && (!stored || stored.expiresAt <= Date.now() || (polling && !stored.pending))) {
throw Object.assign(new Error("Check the installed sidecar's image generation again."),
{ statusCode: 409, recovery: "review-again" });
}
const registrationId = checking ? requireSiwcId(body.registrationId) : stored.registrationId;
if (state.localInstallInFlight || state.localCredentialRotationInFlight) {
throw Object.assign(new Error("An installation or account change is in progress."), { statusCode: 409 });
}
state.localInstallInFlight = true;
try {
if (checking) state.localImagesTarget = null;
const result = checking
? await state.services.getLocalN8nCodexImagesStatus({ registrationId })
: await state.services.changeLocalN8nCodexImages({ registrationId, action,
expectedContainerId: stored.containerId, confirmed: polling ? false : body.confirmed });
const view = copyCodexImagesStatus(result);
state.localImagesTarget = { registrationId, containerId: result.containerId,
pending: view.state === "pending", expiresAt: Date.now() + VPS_ADDON_TARGET_MS };
sendJson(response, 200, view);
} finally {
state.localInstallInFlight = false;
}
return;
}
if (path === "/api/local/chat-test/key") {
const localDashboardGeneration = state.localDashboardGeneration;
requireLiveLocalAction(state, "Local chat testing");
const account = await requireReadyLocalChatTester(state);
requireCurrentLocalDashboardGeneration(
state,
localDashboardGeneration,
);
enforceRateLimit(state, path);
const issued = await state.localChatTest.issueKey({ accountBinding: account });
try {
requireCurrentLocalDashboardGeneration(
state,
localDashboardGeneration,
);
} catch (error) {
try {
await state.localChatTest.reset({ keyId: issued?.keyId });
} catch {
// Revoke only this stale issuance; never clear newer tester sessions.
}
throw error;
}
sendJson(response, 200, createSafeLocalChatTestKey(issued));
return;
}
if (path === "/api/local/chat-test/models") {
const localDashboardGeneration = state.localDashboardGeneration;
requireLiveLocalAction(state, "Local ChatGPT model discovery");
const account = await requireReadyLocalChatTester(state);
requireCurrentLocalDashboardGeneration(state, localDashboardGeneration);
enforceRateLimit(state, path);
try {
const models = await state.localChatTest.models(body, { accountBinding: account });
const current = await requireReadyLocalChatTester(state);
requireCurrentLocalDashboardGeneration(state, localDashboardGeneration);
if (current.registrationId !== account.registrationId ||
current.generation !== account.generation) {
throw Object.assign(new Error("The installed ChatGPT account changed. Secure the tester again."),
{ statusCode: 409, recovery: "review-again" });
}
sendJson(response, 200, { models, account: current });
} finally {
if (body && typeof body === "object") body.encryptedCredential = undefined;
}
return;
}
if (path === "/api/local/chat-test/message") {
const localDashboardGeneration = state.localDashboardGeneration;
requireLiveLocalAction(state, "Local chat testing");
const account = await requireReadyLocalChatTester(state);
requireCurrentLocalDashboardGeneration(
state,
localDashboardGeneration,
);
enforceRateLimit(state, path);
const wantsStream = requestAcceptsEventStream(request);
const stream = wantsStream ? startLocalChatTestStream(response) : null;
const requestController = wantsStream ? new AbortController() : null;
const abortOnClose = () => {
if (!response.writableEnded) requestController?.abort();
};
response.once("close", abortOnClose);
try {
const rawResult = await state.localChatTest.message(body, {
accountBinding: account,
...(stream
? {
onEvent: (event, data) => {
requireCurrentLocalDashboardGeneration(
state,
localDashboardGeneration,
);
stream.send(event, data);
},
signal: requestController.signal,
}
: {}),
});
requireCurrentLocalDashboardGeneration(
state,
localDashboardGeneration,
);
const result = createSafeLocalChatTestResponse(rawResult);
if (stream) stream.complete(result);
else sendJson(response, 200, result);
} catch (error) {
if (stream) {
stream.fail(error);
} else {
throw error;
}
} finally {
response.off("close", abortOnClose);
stream?.dispose();
if (body && typeof body === "object") {
body.encryptedCredential = undefined;
body.input = undefined;
}
}
return;
}
if (path === "/api/local/chat-test/reset") {
const localDashboardGeneration = state.localDashboardGeneration;
requireLiveLocalAction(state, "Local chat testing");
requireCurrentLocalDashboardGeneration(
state,
localDashboardGeneration,
);
enforceRateLimit(state, path);
await state.localChatTest.reset(body);
requireCurrentLocalDashboardGeneration(
state,
localDashboardGeneration,
);
sendJson(response, 200, { forgotten: true });
return;
}
if (path === "/api/oauth/cancel") {
requireLiveLocalAction(state, "Live ChatGPT sign-in");
const login = state.oauthLogin;
if (
!login ||
login.status !== "pending" ||
!body ||
typeof body !== "object" ||
Array.isArray(body) ||
typeof body.attemptId !== "string" ||
body.attemptId !== login.attemptId
) {
throw Object.assign(
new Error("The ChatGPT sign-in attempt has already changed. Start again."),
{ statusCode: 409 },
);
}
await cancelOAuthLogin(state, login);
sendJson(response, 200, {
status: login.status,
attemptId: login.attemptId,
});
return;
}
if (path === "/api/local/plan") {
const localDashboardGeneration = state.localDashboardGeneration;
let plan;
let legacyBinding = null;
let existingBinding = null;
let disposableHarnessWarning = false;
state.localModelActionReview = null;
if (body?.target === LOCAL_N8N_STACK_TARGET) {
requireExactLocalPlanBody(
body,
[
"target", "publicAccess", "n8nPort", "timezone", "assistantMode",
...(body.publicAccess === "ngrok"
? ["ngrokHostname"]
: ["ngrokHostname"].filter(key => Object.hasOwn(body, key) && body[key] === null)),
],
"The local n8n stack plan request is invalid.",
);
requireLiveLocalAction(state, "Local n8n + ngrok planning");
if (localOAuthChangeInFlight(state)) {
throw Object.assign(new Error("A local endpoint change is already in progress."), {
statusCode: 409,
});
}
const docker = await state.services.getLocalDockerStatus();
if (docker?.dockerAvailable !== true || typeof docker.dockerHost !== "string") {
throw new Error("Docker is unavailable for local n8n + ngrok planning.");
}
plan = state.services.prepareLocalN8nStackPlan({
dockerHost: docker.dockerHost,
publicAccess: body.publicAccess,
ngrokHostname: body.ngrokHostname,
n8nPort: body.n8nPort,
timezone: body.timezone,
assistantMode: body.assistantMode,
});
} else if (
body?.target === LOCAL_N8N_SIDECAR_TARGET ||
body?.target === LOCAL_N8N_ASSISTANT_TARGET ||
body?.target === LOCAL_N8N_SUPERGROK_TARGET ||
body?.target === LOCAL_N8N_CLAUDE_TARGET ||
body?.target === LOCAL_N8N_MODEL_TARGET
) {
const assistantTarget = body.target === LOCAL_N8N_ASSISTANT_TARGET;
const superGrokTarget = body.target === LOCAL_N8N_SUPERGROK_TARGET;
const claudeTarget = body.target === LOCAL_N8N_CLAUDE_TARGET;
const modelTarget = body.target === LOCAL_N8N_MODEL_TARGET;
requireExactLocalPlanBody(
body,
assistantTarget
? ["target", "n8nContainerId", "dockerNetworkId", "includeSearxng"]
: modelTarget
? ["target", "n8nContainerId", "dockerNetworkId", "modelId"]
: claudeTarget ? ["target", "n8nContainerId", "dockerNetworkId", "models"]
: ["target", "n8nContainerId", "dockerNetworkId"],
assistantTarget
? "The local n8n Assistant plan request is invalid."
: superGrokTarget
? "The local n8n SuperGrok plan request is invalid."
: modelTarget
? "The local model plan request is invalid."
: "The local n8n OAuth bridge plan request is invalid.",
);
requireLiveLocalAction(state, "Local n8n sidecar planning");
if (!assistantTarget && !superGrokTarget && !claudeTarget && !modelTarget && localOAuthChangeInFlight(state)) {
throw Object.assign(
new Error("ChatGPT sign-in is already in progress."),
{ statusCode: 409 },
);
}
const discovery = await state.services.discoverLocalN8nSidecarTargets();
if (discovery?.dockerAvailable !== true) {
throw new Error("Docker is unavailable for local n8n discovery.");
}
const container = discovery.containers?.find(
(candidate) => candidate?.containerId === body.n8nContainerId,
);
if (!container) {
throw new Error("Select a running n8n container found by this wizard.");
}
const network = container.networks?.find(
(candidate) => candidate?.dockerNetworkId === body.dockerNetworkId,
);
if (!network) {
throw new Error("Select a Docker network attached to that n8n container.");
}
if (assistantTarget) {
const managed = await state.services.getManagedLocalN8nStackTarget();
if (managed?.containerId === container.containerId && managed.marker?.assistantMode !== "disabled") {
throw Object.assign(new Error("This n8n already has Assistant tools from its setup."), { statusCode: 409 });
}
}
if (assistantTarget) {
const includeSearxng = validateAssistantSearxngSelection(
body.includeSearxng,
);
plan = {
...state.services.prepareLocalN8nAssistantPlan({
dockerHost: discovery.dockerHost,
n8nContainerId: container.containerId,
n8nContainerName: container.containerName,
dockerNetworkId: network.dockerNetworkId,
networkName: network.networkName,
includeSearxng,
}),
disposableHarnessWarning: network.disposable === true,
};
} else if (modelTarget) {
const modelId = requireCatalogModel(body.modelId);
const selected = {
dockerHost: discovery.dockerHost,
n8nContainerId: container.containerId,
n8nContainerName: container.containerName,
dockerNetworkId: network.dockerNetworkId,
networkName: network.networkName,
};
const hostResources = await state.services.inspectLocalN8nModelResources(selected);
plan = {
...state.services.prepareLocalN8nModelPlan({ ...selected, modelId, hostResources }),
disposableHarnessWarning: network.disposable === true,
};
} else if (claudeTarget) {
const selected = {
dockerHost: discovery.dockerHost, n8nContainerId: container.containerId,
n8nContainerName: container.containerName, dockerNetworkId: network.dockerNetworkId,
networkName: network.networkName,
};
const { arch } = await state.services.inspectLocalN8nClaudeHost(selected);
plan = { ...state.services.prepareLocalN8nClaudePlan({ ...selected, arch, models: body.models }),
disposableHarnessWarning: network.disposable === true };
} else if (superGrokTarget) {
plan = {
...state.services.prepareLocalN8nSuperGrokPlan({
dockerHost: discovery.dockerHost,
n8nContainerId: container.containerId,
n8nContainerName: container.containerName,
dockerNetworkId: network.dockerNetworkId,
networkName: network.networkName,
}),
disposableHarnessWarning: network.disposable === true,
};
} else {
const account = await selectedSiwcAccount(state, { requirePlan: true });
const authBinding = await siwcBinding(state, account);
plan = {
...state.services.prepareLocalN8nSidecarPlan({
dockerHost: discovery.dockerHost,
n8nContainerId: container.containerId,
n8nContainerName: container.containerName,
dockerNetworkId: network.dockerNetworkId,
networkName: network.networkName,
authBinding,
}),
account,
disposableHarnessWarning: network.disposable === true,
};
const installed = await state.services.getLocalN8nSidecarStatus();
if (installed?.state === "legacy" && installed.managed === true) {
legacyBinding = await state.services.reviewLocalN8nLegacyMigration({ plan });
plan = { ...plan, migrationRequired: true,
legacyResourcesPreserved: true, requiresMigrationConsent: true };
} else if (installed?.state === "stopped" && installed.managed === true) {
const prior = state.localStoppedOwnerReview;
if (prior?.target !== LOCAL_N8N_SIDECAR_TARGET ||
installed.snapshot?.registrationId !== prior.account?.registrationId ||
prior.account.session !== "signed-out" || prior.account.planEnabled !== false ||
prior.account.registrationId === account.registrationId || prior.expiresAt <= Date.now()) {
throw Object.assign(new Error("Inspect the signed-out old sidecar owner before reviewing replacement."),
{ statusCode: 409, recovery: "review-again" });
}
const bound = await state.services.reviewLocalN8nSiwcReplacement({ plan });
if (bound.registrationId !== prior.account.registrationId ||
bound.ownerHostId !== prior.account.ownerHostId) {
throw Object.assign(new Error("The old n8n sidecar owner changed."), { statusCode: 409 });
}
existingBinding = Object.freeze({ ...bound, expectedGeneration: prior.account.generation });
plan = { ...plan, replacementRequired: true,
oldSessionSignedOut: true, oldHistoryRetained: true, requiresReplacementConsent: true };
} else if (installed?.state !== "absent" || installed.managed !== false) {
throw Object.assign(new Error("This n8n sidecar is already owned or needs manual migration recovery."),
{ statusCode: 409, recovery: "review-again" });
}
}
} else {
requireOAuthLocalEndpointPlanBody(body);
const target = body.target ?? "xai-grok-build";
const isChatGpt = target === "codex-chatgpt" || target === "codex-chat";
if (isChatGpt) requireLiveLocalAction(state, "Local ChatGPT endpoint planning");
const account = isChatGpt ? await selectedSiwcAccount(state, { requirePlan: true }) : null;
plan = createLocalDeploymentPlan({
target,
port: body.port,
...(account ? { authBinding: await siwcBinding(state, account) } : {}),
});
if (account) plan = { ...plan, account };
if (isChatGpt) {
const installed = await state.services.getManagedLocalEndpointStatus({ target });
if (installed?.state === "legacy" && installed.managed === true) {
legacyBinding = await state.services.reviewLocalCodexLegacyMigration({ target });
plan = { ...plan, migrationRequired: true,
legacyResourcesPreserved: true, requiresMigrationConsent: true };
} else if (installed?.state === "stopped" && installed.managed === true) {
const prior = state.localStoppedOwnerReview;
if (prior?.target !== target ||
installed.snapshot?.registrationId !== prior.account?.registrationId ||
prior.account.session !== "signed-out" || prior.account.planEnabled !== false ||
prior.account.registrationId === account.registrationId || prior.expiresAt <= Date.now()) {
throw Object.assign(new Error("Inspect the signed-out Codex owner before reviewing replacement."),
{ statusCode: 409, recovery: "review-again" });
}
const bound = await state.services.reviewLocalCodexSiwcReplacement({ target });
if (bound.registrationId !== prior.account.registrationId ||
bound.ownerHostId !== prior.account.ownerHostId) {
throw Object.assign(new Error("The old Codex owner changed."), { statusCode: 409 });
}
existingBinding = Object.freeze({ ...bound, expectedGeneration: prior.account.generation });
plan = { ...plan, replacementRequired: true,
oldSessionSignedOut: true, oldHistoryRetained: true, requiresReplacementConsent: true };
} else if (installed?.state !== "absent" || installed.managed !== false) {
throw Object.assign(new Error("This Codex endpoint is already owned or needs manual migration recovery."),
{ statusCode: 409, recovery: "review-again" });
}
}
}
requireCurrentLocalDashboardGeneration(state, localDashboardGeneration);
const planId = randomUUID();
state.localPlan = { planId, plan, legacyBinding, existingBinding };
sendJson(response, 200, {
planId,
plan:
plan.kind === "local-n8n-stack"
? createSafeLocalN8nStackPlan(plan)
: plan.kind === "n8n-sidecar"
? createSafeLocalN8nPlan(plan)
: plan.kind === "n8n-assistant"
? createSafeLocalN8nAssistantPlan(plan)
: plan.kind === "n8n-supergrok"
? createSafeLocalN8nSuperGrokPlan(plan)
: plan.kind === "n8n-claude"
? createSafeLocalN8nClaudePlan(plan)
: plan.kind === LOCAL_N8N_MODEL_TARGET
? createSafeLocalN8nModelPlan(plan)
: createSafeLocalPlan(plan),
});
return;
}
if (path === "/api/local/install") {
const localDashboardGeneration = state.localDashboardGeneration;
let acquiredInstallLock = false;
try {
requireLiveLocalAction(state, "Local endpoint installation");
enforceRateLimit(state, path);
if (
state.localInstallInFlight ||
state.localCredentialRotationInFlight ||
getPendingLocalCredentialRotation(state) ||
localOAuthChangeInFlight(state)
) {
throw Object.assign(
new Error("A local endpoint change is already in progress."),
{ statusCode: 409 },
);
}
const pending = state.localPlan;
if (!pending || !tokenMatches(body.planId, pending.planId)) {
throw new Error(
"Review a fresh local endpoint plan before installing.",
);
}
requireLocalInstallBody(pending.plan, body);
if (pending.plan.migrationRequired &&
(body.migrationConsent !== true || !pending.legacyBinding)) {
throw Object.assign(new Error("Confirm the separate legacy-service migration before installation."),
{ statusCode: 400 });
}
if (pending.plan.replacementRequired &&
(body.replacementConsent !== true || !pending.existingBinding)) {
throw Object.assign(new Error("Confirm replacement of the signed-out installed account."),
{ statusCode: 400 });
}
if (pending.plan.authBinding && body.confirmed !== true) {
throw Object.assign(new Error("Confirm the reviewed ChatGPT installation."), { statusCode: 400 });
}
if (pending.plan.kind === "n8n-supergrok" && body.confirmed !== true) {
throw new Error(
"Confirm the reviewed local n8n SuperGrok plan before installing.",
);
}
if (pending.plan.kind === LOCAL_N8N_MODEL_TARGET && body.confirmed !== true) {
throw new Error("Confirm the reviewed local model and download before installing.");
}
let localN8nStackSecrets;
if (pending.plan.kind === "local-n8n-stack") {
if (body.confirmed !== true) {
throw Object.assign(
new Error("Confirm the reviewed local n8n stack plan before installing."),
{ retryablePlan: true },
);
}
try {
localN8nStackSecrets = validateLocalN8nStackSecrets({
ngrokAuthtoken: body.ngrokAuthtoken,
basicAuthUsername: body.basicAuthUsername,
basicAuthPassword: body.basicAuthPassword,
}, pending.plan.publicAccess);
} catch (error) {
throw Object.assign(error, { retryablePlan: true });
}
}
state.localInstallInFlight = true;
acquiredInstallLock = true;
state.localPlan = null;
state.localAssistantSearxngReview = null;
state.localInstalledTarget = null;
let result;
if (pending.plan.kind === "n8n-sidecar") {
if (pending.resume) await requireRecoveryBinding(state, pending.sourceBinding);
else await requireMatchingSiwcBinding(state, pending.plan.authBinding);
if (body.backgroundConsent !== true || body.confirmed !== true) {
throw new Error("Approve background n8n use for the selected account and confirm the reviewed installation.");
}
result = await state.services.installLocalN8nSidecar({
plan: pending.plan,
registration: { storageRoot: state.storageRoot, registrationId: pending.plan.authBinding.registrationId },
confirmed: true,
...(pending.resume ? { resume: pending.resume, plan: pending.resume.plan } : {}),
backgroundConsent: { acceptedAt: new Date().toISOString(), noticeVersion: "siwc-local-2026-10-04" },
...(pending.legacyBinding ? { migrationConsent: true,
legacyBinding: pending.legacyBinding } : {}),
...(pending.existingBinding ? { replacementConsent: true,
existingBinding: pending.existingBinding } : {}),
});
} else if (pending.plan.kind === "n8n-assistant") {
const managed = await state.services.getManagedLocalN8nStackTarget();
if (managed?.containerId === pending.plan.n8nContainerId && managed.marker?.assistantMode !== "disabled") {
throw Object.assign(new Error("This n8n already has Assistant tools from its setup."), { statusCode: 409 });
}
result = await state.services.installLocalN8nAssistant({
plan: pending.plan,
confirmed: body.confirmed,
});
} else if (pending.plan.kind === "n8n-supergrok") {
result = await state.services.installLocalN8nSuperGrok({
plan: pending.plan,
confirmed: body.confirmed,
});
} else if (pending.plan.kind === "n8n-claude") {
result = await state.services.installLocalN8nClaude({
plan: pending.plan, confirmed: body.confirmed, review: body.review,
});
} else if (pending.plan.kind === LOCAL_N8N_MODEL_TARGET) {
result = await state.services.installLocalN8nModel({ plan: pending.plan, confirmed: true });
} else if (pending.plan.kind === "local-n8n-stack") {
try {
result = await state.services.installLocalN8nStack({
plan: pending.plan,
secrets: localN8nStackSecrets,
...(pending.plan.publicAccess === "ngrok"
? { publicExposureConfirmation: LOCAL_N8N_STACK_PUBLIC_CONFIRMATION } : {}),
});
} catch (error) {
if (error?.code === LOCAL_N8N_STACK_RETRYABLE_STARTUP_ERROR_CODE) {
// The service only emits this code after ownership-attested
// cleanup proves that no owned Docker resources remain. Restore
// the server-side, non-secret reviewed plan so the user can
// retry the classified startup failure without redoing the form.
requireCurrentLocalDashboardGeneration(
state,
localDashboardGeneration,
);
state.localPlan = pending;
throw Object.assign(error, { retryablePlan: true });
}
throw error;
}
} else {
if (pending.resume) await requireRecoveryBinding(state, pending.sourceBinding);
else if (pending.plan.authBinding) await requireMatchingSiwcBinding(state, pending.plan.authBinding);
result = await state.services.installLocalEndpoint({
plan: pending.plan,
...(pending.plan.authBinding
? { registration: { storageRoot: state.storageRoot, registrationId: pending.plan.authBinding.registrationId } }
: {}),
confirmed: body.confirmed,
...(pending.resume ? { resume: pending.resume, plan: pending.resume.plan } : {}),
...(pending.legacyBinding ? { migrationConsent: true,
legacyBinding: pending.legacyBinding } : {}),
...(pending.existingBinding ? { replacementConsent: true,
existingBinding: pending.existingBinding } : {}),
});
}
requireCurrentLocalDashboardGeneration(
state,
localDashboardGeneration,
);
state.localInstalledTarget = pending.plan.target;
let safeResult = pending.plan.kind === "local-n8n-stack"
? createSafeLocalN8nStackInstallResult(result, pending.plan)
: pending.plan.kind === "n8n-sidecar"
? createSafeLocalN8nInstallResult(result, pending.plan)
: pending.plan.kind === "n8n-assistant"
? createSafeLocalN8nAssistantInstallResult(result, pending.plan)
: pending.plan.kind === "n8n-supergrok"
? createSafeLocalN8nSuperGrokInstallResult(result, pending.plan)
: pending.plan.kind === "n8n-claude"
? createSafeLocalN8nClaudeInstallResult(result, pending.plan)
: pending.plan.kind === LOCAL_N8N_MODEL_TARGET
? createSafeLocalModelStatus(result)
: createSafeLocalInstallResult(result, pending.plan);
const feature = pending.plan.kind === "n8n-sidecar" ? "chatgpt"
: pending.plan.kind === LOCAL_N8N_MODEL_TARGET ? "local-model"
: pending.plan.kind === "n8n-supergrok" ? "supergrok"
: pending.plan.kind === "n8n-claude" ? "claude" : null;
if (feature && result.deploymentMode !== "partial") {
let managed = null;
try { managed = await state.services.getManagedLocalN8nStackTarget(); }
catch { /* An unavailable stack must not fail the add-on install. */ }
if (managed?.containerId === pending.plan.n8nContainerId &&
managed.marker.dockerHost === pending.plan.dockerHost) {
const name = { chatgpt: "Relmio ChatGPT plan", "local-model": "Relmio local model",
supergrok: "Relmio SuperGrok", claude: "Relmio Claude" }[feature];
let n8nCredential = { state: "failed", name };
try {
n8nCredential = await state.services.importLocalStackN8nCredential({
marker: managed.marker, dockerHost: managed.marker.dockerHost,
credential: { feature, apiKey: feature === "chatgpt" ? result.clientCredential
: ["supergrok", "claude"].includes(feature) ? result.clientKey : "local-only" },
});
} catch { /* The one-time key remains available for manual entry. */ }
if (!["created", "updated", "failed"].includes(n8nCredential?.state) ||
n8nCredential.name !== name) n8nCredential = { state: "failed", name };
safeResult = { ...safeResult, n8nCredential };
}
}
sendJson(response, 200, safeResult);
} finally {
if (acquiredInstallLock) {
state.localInstallInFlight = false;
}
body.ngrokAuthtoken = undefined;
body.basicAuthUsername = undefined;
body.basicAuthPassword = undefined;
}
return;
}
if (path === "/api/local/model/plan") {
requireLiveLocalAction(state, "Local model action review");
if (body?.action === "remove") {
requireExactRequestBody(body, ["action", "removeModelData"], "The model removal review is invalid.");
if (body.removeModelData !== true) throw new Error("Review deletion of the owned model cache.");
} else {
requireExactRequestBody(body, ["action"], "The model retry review is invalid.");
if (body.action !== "retry") throw new Error("The model action is invalid.");
}
if (state.localInstallInFlight || state.localCredentialRotationInFlight || localOAuthChangeInFlight(state)) {
throw Object.assign(new Error("A local change is in progress."), { statusCode: 409 });
}
const generation = state.localDashboardGeneration;
state.localPlan = null;
state.localModelActionReview = null;
const review = await state.services.reviewLocalN8nModelAction({
action: body.action, ...(body.action === "remove" ? { removeModelData: true } : {}),
});
requireCurrentLocalDashboardGeneration(state, generation);
const reviewId = randomUUID();
const safe = createSafeLocalModelActionReview(review, reviewId);
state.localModelActionReview = { reviewId, review, generation };
sendJson(response, 200, safe);
return;
}
if (path === "/api/local/model/apply") {
requireLiveLocalAction(state, "Local model action");
enforceRateLimit(state, path);
const pending = state.localModelActionReview;
requireExactRequestBody(
body,
pending?.review?.action === "remove"
? ["reviewId", "confirmed", "removeModelData"]
: ["reviewId", "confirmed"],
"The model action confirmation is invalid.",
);
if (!pending || !tokenMatches(body.reviewId, pending.reviewId) ||
pending.generation !== state.localDashboardGeneration || body.confirmed !== true ||
(pending.review.action === "remove" && body.removeModelData !== true)) {
throw new Error("Review and explicitly confirm this model action again.");
}
if (state.localInstallInFlight || state.localCredentialRotationInFlight || localOAuthChangeInFlight(state)) {
throw Object.assign(new Error("A local change is in progress."), { statusCode: 409 });
}
state.localInstallInFlight = true;
state.localModelActionReview = null;
state.localPlan = null;
try {
const fresh = await state.services.reviewLocalN8nModelAction({
action: pending.review.action,
...(pending.review.action === "remove" ? { removeModelData: true } : {}),
});
requireCurrentLocalDashboardGeneration(state, pending.generation);
for (const key of ["action", "installId", "modelId", "modelDigest", "endpoint", "removeModelData", "networkName", "containerName", "operationId"]) {
if (fresh[key] !== pending.review[key]) throw new Error("The model state changed after review. Review again.");
}
const result = await state.services.applyLocalN8nModelAction({ review: pending.review, confirmed: true });
requireCurrentLocalDashboardGeneration(state, pending.generation);
if (pending.review.action === "remove") {
if (result?.target !== LOCAL_N8N_MODEL_TARGET || result.removed !== true || result.cacheRetained !== false) {
throw Object.assign(new TypeError("The model removal result is invalid."), { statusCode: 502 });
}
sendJson(response, 200, { target: LOCAL_N8N_MODEL_TARGET, removed: true, cacheRetained: false });
} else sendJson(response, 200, createSafeLocalModelStatus(result));
} finally {
state.localInstallInFlight = false;
}
return;
}
if (path === "/api/local/n8n/assistant/searxng/review") {
const localDashboardGeneration = state.localDashboardGeneration;
requireLiveLocalAction(state, "Local n8n Assistant SearXNG review");
enforceRateLimit(state, path);
if (body?.includeSearxng !== true) {
throw new Error("This edit can only enable optional SearXNG web search.");
}
if (
state.localInstallInFlight ||
state.localCredentialRotationInFlight ||
getPendingLocalCredentialRotation(state) ||
localOAuthChangeInFlight(state)
) {
throw Object.assign(
new Error("A local endpoint change is already in progress."),
{ statusCode: 409 },
);
}
// Preparing the review performs asynchronous Docker/file attestation. Hold the
// shared slot for that whole snapshot so another local change cannot make the
// review stale while it is being assembled.
state.localInstallInFlight = true;
state.localAssistantSearxngReview = null;
try {
const review = await state.services.prepareLocalN8nAssistantSearxngUpdate({
includeSearxng: true,
});
const reviewId = randomUUID();
const safeReview = createSafeLocalN8nAssistantSearxngReview(review, reviewId);
requireCurrentLocalDashboardGeneration(
state,
localDashboardGeneration,
);
state.localAssistantSearxngReview = { reviewId, review };
sendJson(response, 200, safeReview);
} finally {
state.localInstallInFlight = false;
}
return;
}
if (path === "/api/local/n8n/assistant/searxng/enable") {
requireLiveLocalAction(state, "Local n8n Assistant SearXNG enablement");
enforceRateLimit(state, path);
if (body?.confirmed !== true) {
throw new Error(
"Confirm enabling SearXNG web search for the reviewed managed local n8n Assistant tools.",
);
}
const pending = state.localAssistantSearxngReview;
if (!pending || !tokenMatches(body.reviewId, pending.reviewId)) {
throw new Error(
"Review the managed local n8n Assistant SearXNG change before enabling it.",
);
}
if (
state.localInstallInFlight ||
state.localCredentialRotationInFlight ||
getPendingLocalCredentialRotation(state) ||
localOAuthChangeInFlight(state)
) {
throw Object.assign(
new Error("A local endpoint change is already in progress."),
{ statusCode: 409 },
);
}
// Claim the same mutation slot used by local install/removal/resume before
// the first awaited service call. The review is consumed on any attempt.
state.localInstallInFlight = true;
state.localPlan = null;
state.localAssistantSearxngReview = null;
try {
const result = await state.services.editLocalN8nAssistantSearxng({
review: pending.review,
confirmed: true,
});
sendJson(
response,
200,
createSafeLocalN8nAssistantSearxngEditResult(result),
);
} finally {
state.localInstallInFlight = false;
}
return;
}
if (path === "/api/local/n8n/remove") {
requireLiveLocalAction(state, "Local n8n sidecar removal");
enforceRateLimit(state, path);
if (body?.confirmed !== true) {
throw new Error("Confirm removal of the managed local n8n sidecar.");
}
if (
state.localInstallInFlight ||
state.localCredentialRotationInFlight ||
getPendingLocalCredentialRotation(state) ||
localOAuthChangeInFlight(state)
) {
throw Object.assign(
new Error("A local endpoint change is already in progress."),
{ statusCode: 409 },
);
}
state.localInstallInFlight = true;
state.localPlan = null;
state.localAssistantSearxngReview = null;
state.localInstalledTarget = null;
try {
const result = await state.services.removeLocalN8nSidecar({
confirmed: true,
});
sendJson(response, 200, createSafeLocalN8nRemovalResult(result));
} finally {
state.localInstallInFlight = false;
}
return;
}
if (path === "/api/local/claude/models") {
requireLiveLocalAction(state, "Claude local model check");
requireExactRequestBody(body, ["clientKey"], "The Claude model check request is invalid.");
const result = await state.services.discoverLocalN8nClaudeModels({ clientKey: body.clientKey });
if (!Array.isArray(result?.models) || !result.models.length || result.models.length > 8 ||
new Set(result.models).size !== result.models.length || result.models.some(model =>
typeof model !== "string" || model.length > 128 || !/^claude-[a-z0-9]+(?:-[a-z0-9]+)*$/u.test(model) || model.includes("fable"))) {
throw new Error("The Claude model list is invalid.");
}
sendJson(response, 200, { models: [...result.models] });
return;
}
if (path === "/api/local/claude/auth-status") {
requireLiveLocalAction(state, "Claude sign-in check");
requireExactRequestBody(body, [], "The Claude sign-in check request is invalid.");
const result = await state.services.getLocalN8nClaudeAuthStatus();
if (typeof result?.loggedIn !== "boolean" ||
![result.authMethod, result.subscriptionType].every(value => value === null ||
(typeof value === "string" && /^[A-Za-z0-9_.-]{1,64}$/u.test(value)))) {
throw new Error("The Claude sign-in status is invalid.");
}
sendJson(response, 200, { loggedIn: result.loggedIn, authMethod: result.authMethod, subscriptionType: result.subscriptionType });
return;
}
if (["/api/local/claude/renew", "/api/local/claude/sign-out", "/api/local/claude/remove"].includes(path)) {
const generation = state.localDashboardGeneration;
const action = path.split("/").at(-1);
requireLiveLocalAction(state, "Local Claude change");
enforceRateLimit(state, path);
// Removal may carry keepLogin: true to leave the Claude sign-in volume for a later install.
const keepLoginSent = action === "remove" && Boolean(body) && typeof body === "object" && Object.hasOwn(body, "keepLogin");
requireExactRequestBody(body, action === "renew" ? ["confirmed", "review"] : keepLoginSent ? ["confirmed", "keepLogin"] : ["confirmed"],
"The local Claude action request is invalid.");
if (keepLoginSent && typeof body.keepLogin !== "boolean") throw new Error("The local Claude action request is invalid.");
if (body.confirmed !== true) throw new Error("Confirm this local Claude action.");
if (action === "renew") validateClaudeReview(body.review);
if (state.localInstallInFlight || state.localCredentialRotationInFlight ||
getPendingLocalCredentialRotation(state) || localOAuthChangeInFlight(state)) {
throw Object.assign(new Error("A local endpoint change is already in progress."), { statusCode: 409 });
}
state.localInstallInFlight = true;
state.localPlan = null;
state.localInstalledTarget = null;
try {
const result = await state.services.changeLocalN8nClaude({ action, confirmed: true, ...(action === "renew" ? { review: body.review } : {}),
...(body.keepLogin === true ? { keepLogin: true } : {}) });
requireCurrentLocalDashboardGeneration(state, generation);
sendJson(response, 200, createSafeLocalN8nClaudeChangeResult(result, action));
} finally { state.localInstallInFlight = false; }
return;
}
if (path === "/api/local/supergrok/remove") {
const localDashboardGeneration = state.localDashboardGeneration;
requireLiveLocalAction(state, "Local n8n SuperGrok removal");
enforceRateLimit(state, path);
requireExactRequestBody(
body,
["confirmed"],
"The local n8n SuperGrok removal request is invalid.",
);
if (body.confirmed !== true) {
throw new Error("Confirm removal of the managed local n8n SuperGrok sidecar.");
}
if (
state.localInstallInFlight ||
state.localCredentialRotationInFlight ||
getPendingLocalCredentialRotation(state) ||
localOAuthChangeInFlight(state)
) {
throw Object.assign(
new Error("A local endpoint change is already in progress."),
{ statusCode: 409 },
);
}
state.localInstallInFlight = true;
state.localPlan = null;
state.localAssistantSearxngReview = null;
state.localInstalledTarget = null;
try {
const result = await state.services.removeLocalN8nSuperGrok({
confirmed: true,
});
requireCurrentLocalDashboardGeneration(state, localDashboardGeneration);
sendJson(
response,
200,
createSafeLocalN8nSuperGrokRemovalResult(result),
);
} finally {
state.localInstallInFlight = false;
}
return;
}
if (path === "/api/local/n8n/stack/resume") {
requireLiveLocalAction(state, "Local n8n + ngrok resume");
enforceRateLimit(state, path);
if (body?.confirmed !== true) {
throw new Error("Confirm resuming the managed local n8n + ngrok stack.");
}
if (
state.localInstallInFlight ||
state.localCredentialRotationInFlight ||
getPendingLocalCredentialRotation(state) ||
localOAuthChangeInFlight(state)
) {
throw Object.assign(new Error("A local endpoint change is already in progress."), {
statusCode: 409,
});
}
state.localInstallInFlight = true;
state.localPlan = null;
state.localAssistantSearxngReview = null;
state.localInstalledTarget = null;
try {
const result = await state.services.resumeLocalN8nStack({
confirmed: true,
});
sendJson(response, 200, createSafeLocalN8nStackResumeResult(result));
} finally {
state.localInstallInFlight = false;
}
return;
}
if (path === "/api/local/n8n/stack/remove") {
requireLiveLocalAction(state, "Local n8n + ngrok removal");
enforceRateLimit(state, path);
if (body?.confirmed !== true) {
throw new Error("Confirm removal of the managed local n8n + ngrok stack.");
}
if (
state.localInstallInFlight ||
state.localCredentialRotationInFlight ||
getPendingLocalCredentialRotation(state) ||
localOAuthChangeInFlight(state)
) {
throw Object.assign(new Error("A local endpoint change is already in progress."), {
statusCode: 409,
});
}
state.localInstallInFlight = true;
state.localPlan = null;
state.localAssistantSearxngReview = null;
state.localInstalledTarget = null;
try {
const result = await state.services.removeLocalN8nStack({
confirmation: LOCAL_N8N_STACK_REMOVE_CONFIRMATION,
});
sendJson(response, 200, createSafeLocalN8nStackRemovalResult(result));
} finally {
state.localInstallInFlight = false;
}
return;
}
if (path === "/api/local/n8n/assistant/remove") {
requireLiveLocalAction(state, "Local n8n Assistant companion removal");
enforceRateLimit(state, path);
if (body?.confirmed !== true) {
throw new Error("Confirm removal of the managed local n8n Assistant tools.");
}
if (
state.localInstallInFlight ||
state.localCredentialRotationInFlight ||
getPendingLocalCredentialRotation(state) ||
localOAuthChangeInFlight(state)
) {
throw Object.assign(
new Error("A local endpoint change is already in progress."),
{ statusCode: 409 },
);
}
state.localInstallInFlight = true;
state.localPlan = null;
state.localAssistantSearxngReview = null;
state.localInstalledTarget = null;
try {
const result = await state.services.removeLocalN8nAssistant({
confirmed: true,
});
sendJson(
response,
200,
createSafeLocalN8nAssistantRemovalResult(result),
);
} finally {
state.localInstallInFlight = false;
}
return;
}
if (path === "/api/local/client-credential/rotate") {
const localDashboardGeneration = state.localDashboardGeneration;
requireLiveLocalAction(state, "Local client credential rotation");
enforceRateLimit(state, path);
if (
state.localCredentialRotationInFlight ||
state.localInstallInFlight ||
getPendingLocalCredentialRotation(state) ||
localOAuthChangeInFlight(state)
) {
throw Object.assign(
new Error("A local endpoint change is already in progress."),
{ statusCode: 409 },
);
}
if (!body || typeof body !== "object" || Array.isArray(body)) {
throw new Error("Choose the installed local endpoint before rotating its credential.");
}
state.localCredentialRotationInFlight = true;
try {
state.localChatTest.resetAll?.();
const result = await state.services.prepareLocalClientCredentialRotation({
target: validateLocalTarget(body.target),
});
const rotationId = randomUUID();
requireCurrentLocalDashboardGeneration(
state,
localDashboardGeneration,
);
state.localCredentialRotationPending = {
rotationId,
target: result.target,
tokenSha256: result.tokenSha256,
...(["codex-chatgpt", "codex-chat"].includes(result.target)
? {
registrationId: requireSiwcId(result.registrationId),
expectedGeneration: result.expectedGeneration,
} : {}),
expiresAt: Date.now() + LOCAL_ROTATION_STAGE_TTL_MS,
};
sendJson(response, 200, {
...createSafeLocalInstallResult(result),
rotationId,
});
} finally {
state.localCredentialRotationInFlight = false;
}
return;
}
if (path === "/api/local/client-credential/activate") {
requireLiveLocalAction(state, "Local client credential activation");
enforceRateLimit(state, path);
if (
state.localCredentialRotationInFlight ||
state.localInstallInFlight ||
localOAuthChangeInFlight(state)
) {
throw Object.assign(
new Error("A local endpoint change is already in progress."),
{ statusCode: 409 },
);
}
const pending = getPendingLocalCredentialRotation(state);
if (!pending || !tokenMatches(body?.rotationId, pending.rotationId)) {
throw new Error("Stage a fresh local client credential before activating it.");
}
state.localCredentialRotationPending = null;
state.localCredentialRotationInFlight = true;
try {
state.localChatTest.resetAll?.();
const result = await state.services.activateLocalClientCredentialRotation({
target: pending.target,
clientCredential: body.clientCredential,
tokenSha256: pending.tokenSha256,
...(pending.registrationId
? { registrationId: pending.registrationId, expectedGeneration: pending.expectedGeneration }
: {}),
});
sendJson(response, 200, createSafeLocalActivationResult(result));
} finally {
state.localCredentialRotationInFlight = false;
}
return;
}
if (path === "/api/ssh/fingerprint") {
enforceRateLimit(state, path);
const host = validateHostname(body.host);
const port = validatePort(body.port);
const scanOperation = beginVpsFingerprintScan(state);
try {
const fingerprint = await state.services.scanHostFingerprint({
host,
port,
});
requireCurrentVpsFingerprintScan(state, scanOperation);
state.scannedHost = {
host,
port,
fingerprint,
scanGeneration: scanOperation.scanGeneration,
};
sendJson(response, 200, { fingerprint });
} finally {
releaseVpsFingerprintScan(state, scanOperation);
}
return;
}
if (path === "/api/ssh/connect") {
let connectionOperation;
let candidateConnection = null;
try {
rejectActiveVpsMutation(state);
enforceRateLimit(state, path);
const connectionRequest = validatedSshRequest(body);
const { host, port } = connectionRequest;
const scannedHost = state.scannedHost;
if (
!scannedHost ||
scannedHost.host !== host ||
scannedHost.port !== port ||
!tokenMatches(body.expectedFingerprint, scannedHost.fingerprint)
) {
throw Object.assign(new Error(
"This server identity check was already used or no longer matches. Check and confirm the server identity again before connecting."),
{ code: "ssh_identity_review_required", recovery: "review-again" });
}
connectionOperation = acquireVpsConnectionOperation(state);
const previousConnection = state.connection;
clearVpsConnectionIdleExpiry(state, previousConnection);
retireVpsConnectionUses(state, previousConnection);
state.connection = null;
state.connectionIdentity = null;
connectionOperation.operation.lifecycleGeneration =
advanceVpsLifecycleGeneration(state);
state.discovery = null;
state.networksByContainer.clear();
invalidateVpsPlans(state);
closeVpsConnectionBestEffort(previousConnection);
candidateConnection = await state.services.connectVerified(connectionRequest);
requireCurrentVpsConnectionOperation(
state,
connectionOperation.operation,
);
if (state.scannedHost !== scannedHost) {
throw Object.assign(
new Error("The VPS identity scan changed before the connection was ready. Check it again."),
{ statusCode: 409 },
);
}
// Administrative identity belongs to this connection, not to later model mutations.
const identity = verifiedConnectionIdentity(candidateConnection, connectionRequest, state.vpsLifecycleGeneration);
state.connection = candidateConnection;
state.connectionIdentity = identity;
armVpsConnectionIdleExpiry(state, candidateConnection);
candidateConnection = null;
state.scannedHost = null;
sendJson(response, 200, { connected: true, identity });
} finally {
if (body && typeof body === "object") body.password = undefined;
closeVpsConnectionBestEffort(candidateConnection);
connectionOperation?.release();
}
return;
}
if (path === "/api/discover") {
rejectActiveVpsMutation(state);
const connectionUse = acquireVpsConnectionUse(state);
const { connection } = connectionUse;
const sessionSnapshot = {
connection,
lifecycleGeneration: state.vpsLifecycleGeneration,
discoveryGeneration: beginVpsDiscoveryRefresh(state),
};
try {
const discovery = await state.services.discoverN8n(connection);
requireUnchangedVpsSession(state, sessionSnapshot);
state.discovery = discovery;
state.networksByContainer.clear();
invalidateVpsPlans(state);
sendJson(response, 200, state.discovery);
} finally {
connectionUse.release();
}
return;
}
if (path === "/api/networks") {
rejectActiveVpsMutation(state);
const connectionUse = acquireVpsConnectionUse(state);
const { connection } = connectionUse;
const sessionSnapshot = {
connection,
lifecycleGeneration: state.vpsLifecycleGeneration,
discoveryGeneration: beginVpsDiscoveryRefresh(state),
};
try {
requireDiscoveredContainer(state, body.containerName);
const networks = await state.services.discoverNetworks(
connection,
body.containerName,
);
requireUnchangedVpsSession(state, sessionSnapshot);
state.networksByContainer.set(body.containerName, networks);
invalidateVpsPlans(state);
sendJson(response, 200, networks);
} finally {
connectionUse.release();
}
return;
}
if (path.startsWith("/api/vps/local-model/")) {
rejectActiveVpsMutation(state);
const connectionUse = acquireVpsConnectionUse(state);
const { connection } = connectionUse;
const snapshot = {
connection,
lifecycleGeneration: state.vpsLifecycleGeneration,
discoveryGeneration: state.vpsDiscoveryGeneration,
};
let releaseMutation;
try {
if (path === "/api/vps/local-model/status") {
requireExactRequestBody(body, ["containerName", "networkName"], "The model status selection is invalid.");
requireSafeDockerName(body.containerName, "n8n container name");
requireSafeDockerName(body.networkName, "Docker network name");
const result = await state.services.inspectVpsLocalModel({ remote: connection, containerName: body.containerName, networkName: body.networkName });
requireUnchangedVpsSession(state, snapshot);
sendJson(response, 200, createSafeVpsLocalModelStatus(result));
} else if (path === "/api/vps/local-model/plan") {
if (body?.action === "install") {
requireExactRequestBody(body, ["containerName", "networkName", "action", "modelId"], "The model install selection is invalid.");
requireCatalogModel(body.modelId);
} else if (body?.action === "remove") {
requireExactRequestBody(body, ["containerName", "networkName", "action", "clearModelCache"], "The model removal selection is invalid.");
if (body.clearModelCache !== true) throw new Error("Confirm that removal deletes the owned model cache.");
} else if (body?.action === "retry") {
requireExactRequestBody(body, ["containerName", "networkName", "action"], "The model retry selection is invalid.");
} else throw new Error("The model action is invalid.");
if (body.action !== "remove") requireDiscoveredNetwork(state, body.containerName, body.networkName);
else {
requireSafeDockerName(body.containerName, "n8n container name");
requireSafeDockerName(body.networkName, "Docker network name");
}
const generation = beginLocalModelVpsReview(state);
const reviewed = await state.services.reviewVpsLocalModel({
remote: connection, containerName: body.containerName, networkName: body.networkName,
action: body.action, ...(body.action === "install" ? { modelId: body.modelId } : {}),
...(body.action === "remove" ? { clearModelCache: true } : {}),
});
requireUnchangedVpsSession(state, snapshot);
if (generation !== state.localModelVpsPlanGeneration) throw Object.assign(new Error("A newer model review replaced this plan."), { statusCode: 409 });
const plan = createSafeVpsLocalModelPlan(reviewed);
state.localModelVpsPlan = { ...reviewed, planId: randomUUID() };
sendJson(response, 200, { ...plan, planId: state.localModelVpsPlan.planId });
} else if (path === "/api/vps/local-model/apply") {
enforceRateLimit(state, path);
const plan = state.localModelVpsPlan;
requireExactRequestBody(
body,
plan?.action === "remove"
? ["containerName", "networkName", "planId", "action", "confirmed", "clearModelCache"]
: ["containerName", "networkName", "planId", "action", "confirmed"],
"The model apply request is invalid.",
);
requireReviewedVpsPlan(plan, body, "local model");
if (!["install", "retry", "remove"].includes(body.action) || plan.action !== body.action || body.confirmed !== true ||
(plan.action === "remove" && (plan.clearModelCache !== true || body.clearModelCache !== true))) {
throw new Error("Confirm the exact reviewed model action and cache deletion.");
}
if (plan.action !== "remove") requireDiscoveredNetwork(state, body.containerName, body.networkName);
state.localModelVpsPlan = null;
releaseMutation = acquireVpsMutationLock(state);
snapshot.lifecycleGeneration = state.vpsLifecycleGeneration;
const fresh = await state.services.reviewVpsLocalModel({
remote: connection, containerName: plan.containerName, networkName: plan.networkName,
action: plan.action, ...(plan.action === "install" ? { modelId: plan.modelId } : {}),
...(plan.action === "remove" ? { clearModelCache: true } : {}),
});
if (state.connection !== connection || state.vpsLifecycleGeneration !== snapshot.lifecycleGeneration ||
state.vpsDiscoveryGeneration !== snapshot.discoveryGeneration) {
throw new Error("The VPS session changed after model review. Reconnect and inspect again.");
}
createSafeVpsLocalModelPlan(fresh);
for (const key of ["action", "containerId", "networkId", "installId", "modelId", "approvedModelDigest", "catalogRevision", "runtimeImage", "contextTokens", "memoryBytes", "cpus", "expectedDownloadBytes", "requiredDiskBytes", "reservedMemoryBytes", "clearModelCache"]) {
if (fresh[key] !== plan[key]) throw new Error("The selected model, resources or Docker boundary changed. Review again.");
}
const servicePlan = { ...plan };
delete servicePlan.planId;
const result = await (plan.action === "install" ? state.services.installVpsLocalModel : state.services.changeVpsLocalModel)({ remote: connection, plan: servicePlan, confirmed: true });
if (state.connection !== connection || state.vpsLifecycleGeneration !== snapshot.lifecycleGeneration) throw new Error("VPS connection changed. Reconnect and inspect the owned model status.");
sendJson(response, 200, createSafeVpsLocalModelStatus(result));
} else if (path === "/api/vps/local-model/operation-status") {
requireExactRequestBody(body, ["containerName", "networkName", "installId"], "The model operation selection is invalid.");
requireSafeDockerName(body.containerName, "n8n container name");
requireSafeDockerName(body.networkName, "Docker network name");
// One inspection pass per download poll: the service checks the selection and the installation.
const result = await state.services.getVpsLocalModelOperationStatus({
remote: connection, installId: body.installId, containerName: body.containerName, networkName: body.networkName,
});
requireUnchangedVpsSession(state, snapshot);
if (result?.installId !== body.installId) throw new Error("The model operation identity changed. Inspect again.");
sendJson(response, 200, createSafeVpsLocalModelStatus(result));
} else sendJson(response, 404, { error: "Not found." });
} finally {
releaseMutation?.();
connectionUse.release();
}
return;
}
if (path.startsWith("/api/vps/supergrok/")) {
rejectActiveVpsMutation(state);
const connectionUse = acquireVpsConnectionUse(state);
const { connection } = connectionUse;
requireFullVpsScope(connection);
const snapshot = {
connection,
lifecycleGeneration: state.vpsLifecycleGeneration,
discoveryGeneration: state.vpsDiscoveryGeneration,
};
let releaseMutation;
try {
if (path === "/api/vps/supergrok/status" || path === "/api/vps/supergrok/plan") {
requireDiscoveredNetwork(state, body.containerName, body.networkName);
const args = { remote: connection, containerName: body.containerName, networkName: body.networkName, action: body.action };
const planGeneration = path.endsWith("/plan")
? beginSuperGrokPlanReview(state)
: null;
const result = path.endsWith("/plan")
? await state.services.reviewVpsSuperGrok(args)
: await state.services.inspectVpsSuperGrok(args);
requireUnchangedVpsSession(state, snapshot);
if (path.endsWith("/plan")) {
if (state.supergrokPlanGeneration !== planGeneration) {
throw Object.assign(
new Error("A newer SuperGrok review replaced this plan. Review the current plan before installing."),
{ statusCode: 409 },
);
}
requireVpsSuperGrokBuildBoundary(result);
state.supergrokPlan = { ...result, planId: randomUUID() };
sendJson(response, 200, state.supergrokPlan);
} else sendJson(response, 200, result);
} else if (path === "/api/vps/supergrok/apply") {
enforceRateLimit(state, path);
const plan = state.supergrokPlan;
requireReviewedVpsPlan(plan, body, "SuperGrok");
if (body.confirmed !== true || plan.action !== body.action) throw new Error("Confirm the exact reviewed SuperGrok action.");
requireDiscoveredNetwork(state, body.containerName, body.networkName);
state.supergrokPlan = null;
releaseMutation = acquireVpsMutationLock(state);
snapshot.lifecycleGeneration = state.vpsLifecycleGeneration;
const fresh = await state.services.reviewVpsSuperGrok({ remote: connection, ...plan });
requireVpsSuperGrokBuildBoundary(fresh);
if (["containerId", "networkId", "installId", "action", "operationLockPath", "temporaryBuildStatePath"].some(key => fresh[key] !== plan[key])) throw new Error("The SuperGrok selection changed. Review a fresh plan.");
const result = await (plan.action === "install" ? state.services.installVpsSuperGrok : state.services.changeVpsSuperGrok)({ remote: connection, plan, confirmed: true });
if (state.connection !== connection || state.vpsLifecycleGeneration !== snapshot.lifecycleGeneration) {
throw new Error("The VPS session changed during the companion action. Reconnect and inspect its status.");
}
sendJson(response, 200, result);
} else if (path === "/api/vps/supergrok/login-status") {
const result = await state.services.getVpsGrokLoginStatus({ remote: connection, installId: body.installId });
requireUnchangedVpsSession(state, snapshot);
sendJson(response, 200, result);
} else if (path === "/api/vps/supergrok/models") {
try {
const result = await state.services.discoverVpsGrokModels({ remote: connection, installId: body.installId, clientKey: body.clientKey });
requireUnchangedVpsSession(state, snapshot);
sendJson(response, 200, result);
} finally { body.clientKey = undefined; }
} else sendJson(response, 404, { error: "Not found." });
} finally {
releaseMutation?.();
connectionUse.release();
}
return;
}
if (path.startsWith("/api/vps/claude/")) {
rejectActiveVpsMutation(state);
const connectionUse = acquireVpsConnectionUse(state);
const { connection } = connectionUse;
let releaseMutation;
try {
requireFullVpsScope(connection);
const snapshot = {
connection,
lifecycleGeneration: state.vpsLifecycleGeneration,
discoveryGeneration: state.vpsDiscoveryGeneration,
};
if (path === "/api/vps/claude/plan") {
const action = body?.action;
const selects = action === "install" || action === "replace";
const approves = selects || action === "renew";
if (!VPS_CLAUDE_ACTIONS.includes(action)) throw new Error("Choose a supported Claude action.");
if (approves && !Object.hasOwn(body, "review")) {
throw new Error("Answer yes to all four Claude review questions first. Nothing was changed.");
}
requireExactRequestBody(body, selects ? ["action", "containerName", "networkName", "models", "review"]
: approves ? ["action", "review"] : ["action"], "The Claude plan request is invalid.");
if (selects) requireDiscoveredNetwork(state, body.containerName, body.networkName);
const generation = beginClaudePlanReview(state);
const reviewed = await state.services.reviewVpsClaude({
remote: connection, action,
...(selects ? { containerName: body.containerName, networkName: body.networkName, models: body.models } : {}),
...(approves ? { review: body.review } : {}),
});
requireUnchangedVpsSession(state, snapshot);
if (generation !== state.claudePlanGeneration) {
throw Object.assign(new Error("A newer Claude review replaced this plan. Review the current plan before applying it."), { statusCode: 409 });
}
const plan = createSafeVpsClaudePlan(reviewed);
state.claudePlan = { ...reviewed, planId: randomUUID() };
sendJson(response, 200, { planId: state.claudePlan.planId, plan });
} else if (path === "/api/vps/claude/apply") {
enforceRateLimit(state, path);
requireExactRequestBody(body, ["planId", "confirmed"], "The Claude apply request is invalid.");
const plan = state.claudePlan;
if (!plan || !tokenMatches(body.planId, plan.planId)) throw new Error("Review a fresh Claude plan before applying it.");
if (body.confirmed !== true) throw new Error("Confirm the exact reviewed Claude action.");
const selects = plan.action === "install" || plan.action === "replace";
if (selects) requireDiscoveredNetwork(state, plan.containerName, plan.networkName);
state.claudePlan = null;
releaseMutation = acquireVpsMutationLock(state);
snapshot.lifecycleGeneration = state.vpsLifecycleGeneration;
const servicePlan = { ...plan };
delete servicePlan.planId;
const fresh = await state.services.reviewVpsClaude({
remote: connection, action: plan.action,
...(selects ? { containerName: plan.containerName, networkName: plan.networkName, models: plan.models } : {}),
...(plan.review ? { review: plan.review } : {}),
});
createSafeVpsClaudePlan(fresh);
if (["action", "installId", "containerId", "networkId", "network", "arch", "executableSha256", "operationLockPath", "keepsClaudeLogin"]
.some((key) => fresh[key] !== plan[key]) || JSON.stringify(fresh.models) !== JSON.stringify(plan.models)) {
throw new Error("The Claude companion or its n8n selection changed. Review a fresh plan.");
}
const result = await state.services.applyVpsClaude({ remote: connection, plan: servicePlan, confirmed: true });
if (state.connection !== connection || state.vpsLifecycleGeneration !== snapshot.lifecycleGeneration) {
throw new Error("The VPS session changed during the Claude action. Reconnect and check its status.");
}
sendJson(response, 200, createSafeVpsClaudeApplyResult(result, plan.action));
} else if (path === "/api/vps/claude/auth-status") {
requireExactRequestBody(body, [], "The Claude sign-in check request is invalid.");
const result = createSafeVpsClaudeAuthStatus(await state.services.getVpsClaudeAuthStatus({ remote: connection }));
requireUnchangedVpsSession(state, snapshot);
sendJson(response, 200, result);
} else if (path === "/api/vps/claude/models") {
try {
requireExactRequestBody(body, ["clientKey"], "The Claude model check request is invalid.");
const result = await state.services.discoverVpsClaudeModels({ remote: connection, clientKey: body.clientKey });
requireUnchangedVpsSession(state, snapshot);
if (!validModels(result?.models)) throw vpsClaudeInvalid("model list");
sendJson(response, 200, { models: [...result.models] });
} finally { body.clientKey = undefined; }
} else sendJson(response, 404, { error: "Not found." });
} finally {
releaseMutation?.();
connectionUse.release();
}
return;
}
if (["/api/siwc/vps/status", "/api/siwc/vps/manage", "/api/siwc/vps/inspect-stopped"].includes(path)) {
requireLiveLocalAction(state, "VPS ChatGPT account management");
enforceRateLimit(state, path);
const managing = path.endsWith("/manage");
const inspecting = path.endsWith("/inspect-stopped");
requireExactRequestBody(body, managing
? ["containerName", "networkName", "registrationId", "expectedGeneration", "action", "confirmed",
...(body?.action === "enable-plan" ? ["backgroundConsent"] : [])]
: inspecting
? ["containerName", "networkName", "registrationId", "confirmed"]
: ["containerName", "networkName"],
"Choose the verified VPS installation and its exact account.");
requireDiscoveredNetwork(state, body.containerName, body.networkName);
if ((managing || inspecting) && (body.confirmed !== true ||
!requireSiwcId(body.registrationId) ||
(managing && !["sign-out", "disable-plan", "enable-plan"].includes(body.action)) ||
(managing && body.action === "enable-plan" && body.backgroundConsent !== true))) throw invalidSiwcRequest();
rejectActiveVpsMutation(state);
if (oauthCredentialChangeInFlight(state)) {
throw Object.assign(new Error("Finish ChatGPT sign-in before managing the VPS."), { statusCode: 409 });
}
const connectionUse = acquireVpsConnectionUse(state);
const { connection } = connectionUse;
let releaseMutation;
let credentialOperation;
try {
requireFullVpsScope(connection);
const reviewedTarget = managing || inspecting
? requireReviewedVpsOwnerTarget(state, body.containerName, body.networkName)
: Object.freeze({ ...state.connectionIdentity,
containerName: body.containerName, networkName: body.networkName,
...await state.services.reviewVpsSiwcTarget({
remote: connection, networkName: body.networkName, containerName: body.containerName,
}) });
releaseMutation = managing || inspecting ? acquireVpsMutationLock(state) : null;
credentialOperation = managing || inspecting ? null : acquireVpsCredentialOperation(state, "owner-status");
const status = await state.services.getVpsSiwcInstallationStatus({
remote: connection, networkName: body.networkName, reviewedTarget,
// A plain status check attested this n8n target a moment ago, in this request.
targetAttested: !managing && !inspecting,
});
if (!managing && !inspecting) {
state.vpsOwnerTargetReview = { reviewedTarget, expiresAt: Date.now() + 5 * 60_000 };
sendJson(response, 200, {
state: status.state,
destination: { n8nContainerId: reviewedTarget.n8nContainerId,
networkId: reviewedTarget.networkId },
...(status.account ? { account: copySiwcAccountView(status.account) } : {}),
...(status.registrationId ? { registrationId: requireSiwcId(status.registrationId) } : {}),
...(["staged", "updating"].includes(status.state) ? { staging: copySiwcStaging(status.staging) } : {}),
...(status.state === "owned" && typeof status.runtimeUpdateAvailable === "boolean"
? { runtimeUpdateAvailable: status.runtimeUpdateAvailable } : {}),
});
return;
}
if (inspecting) {
if (status.state !== "stopped" || status.registrationId !== body.registrationId) {
throw Object.assign(new Error("This installed account is not stopped. Check its status again."), { statusCode: 409 });
}
const result = await state.services.inspectStoppedVpsSiwcInstallation({
remote: connection, networkName: body.networkName, registrationId: body.registrationId,
reviewedTarget, confirmed: true,
});
const account = copySiwcAccountView(result.account);
state.vpsStoppedOwnerReview = { account, reviewedTarget, expiresAt: Date.now() + 5 * 60_000 };
sendJson(response, 200, { account });
return;
}
const stoppedReview = state.vpsStoppedOwnerReview;
const stopped = status.state === "stopped" && status.registrationId === body.registrationId &&
stoppedReview?.expiresAt > Date.now() &&
stoppedReview.account.registrationId === body.registrationId &&
stoppedReview.account.generation === body.expectedGeneration &&
["host", "port", "fingerprint", "username", "authentication", "privilege",
"loginUid", "effectiveUid", "containerName", "networkName", "n8nContainerId", "networkId"].every(
(key) => stoppedReview.reviewedTarget[key] === reviewedTarget[key]);
const live = status.state === "owned" && status.registrationId === body.registrationId &&
status.account?.registrationId === body.registrationId &&
status.account.generation === body.expectedGeneration &&
status.account.ownership === "owned";
if (!live && !stopped) {
throw Object.assign(new Error("The VPS owner session changed. Check its status again."), { statusCode: 409 });
}
state.vpsStoppedOwnerReview = null;
const result = await state.services.manageVpsSiwcInstallation({
remote: connection, networkName: body.networkName,
registrationId: body.registrationId, expectedGeneration: body.expectedGeneration,
action: body.action, reviewedTarget, confirmed: true,
...(body.action === "enable-plan" ? { backgroundConsent: true } : {}),
});
invalidateSiwcWork(state);
if (result.runtimeStopped !== (body.action !== "enable-plan") ||
!["confirmed", "unconfirmed", "not-applicable"].includes(result.revocation)) {
throw Object.assign(new Error("The VPS owner result was not confirmed."), { statusCode: 502 });
}
const updated = copySiwcAccountView(result.account);
if (body.action === "sign-out" && updated.session === "signed-out" && updated.planEnabled === false) {
state.vpsStoppedOwnerReview = { account: updated, reviewedTarget,
expiresAt: Date.now() + 5 * 60_000 };
}
sendJson(response, 200, {
account: updated, revocation: result.revocation, runtimeStopped: result.runtimeStopped,
});
} finally {
credentialOperation?.release();
if (releaseMutation) {
detachVpsConnection(state, connection);
releaseMutation();
}
connectionUse.release();
}
return;
}
if (path === "/api/siwc/vps/runtime-update/review" || path === "/api/siwc/vps/runtime-update/apply") {
requireLiveLocalAction(state, "VPS ChatGPT sidecar update");
enforceRateLimit(state, path);
const applying = path.endsWith("/apply");
requireExactRequestBody(body, applying ? ["reviewId", "confirmed"] : ["containerName", "networkName", "registrationId"],
"Review this exact installed sidecar before updating it.");
// Also refuses while a ChatGPT sign-in or credential change is in flight.
rejectActiveVpsMutation(state);
const pending = applying ? state.vpsRuntimeUpdateReview : null;
if (applying) {
state.vpsRuntimeUpdateReview = null;
if (body.confirmed !== true || !pending || pending.expiresAt <= Date.now() ||
!tokenMatches(body.reviewId, pending.reviewId)) {
throw Object.assign(new Error("Review and confirm this sidecar update again."),
{ statusCode: 409, recovery: "review-again" });
}
}
const containerName = applying ? pending.reviewedTarget.containerName : body.containerName;
const networkName = applying ? pending.reviewedTarget.networkName : body.networkName;
requireDiscoveredNetwork(state, containerName, networkName);
const registrationId = applying ? pending.registrationId : requireSiwcId(body.registrationId);
const ownerTarget = requireReviewedVpsOwnerTarget(state, containerName, networkName);
if (applying && ![...VPS_OWNER_TARGET_FIELDS, "n8nContainerId", "networkId"].every(
(key) => ownerTarget[key] === pending.reviewedTarget[key])) {
throw Object.assign(new Error("Review this VPS owner and destination again."),
{ statusCode: 409, recovery: "review-again" });
}
const reviewedTarget = applying ? pending.reviewedTarget : ownerTarget;
const connectionUse = acquireVpsConnectionUse(state);
const { connection } = connectionUse;
let operation;
let releaseMutation;
try {
requireFullVpsScope(connection);
if (!applying) {
operation = acquireVpsCredentialOperation(state, "runtime-update-review");
state.vpsRuntimeUpdateReview = null;
const review = await state.services.reviewVpsSiwcRuntimeUpdate({
remote: connection, networkName, reviewedTarget, registrationId,
});
if (review?.rebuildRequired === false) {
sendJson(response, 200, { rebuildRequired: false });
return;
}
if (review?.rebuildRequired !== true || review.registrationId !== registrationId ||
typeof review.continuing !== "boolean" || !/^[a-z][a-z0-9-]{0,63}$/u.test(review.stage ?? "") ||
!/^[a-f0-9]{64}$/u.test(review.containerId ?? "") ||
!/^sha256:[a-f0-9]{64}$/u.test(review.imageId ?? "") ||
!Array.isArray(review.changedFiles) || review.changedFiles.some((file) => typeof file !== "string" ||
!/^[A-Za-z0-9._-]{1,128}(?:\/[A-Za-z0-9._-]{1,128}){0,15}$/u.test(file) ||
file.split("/").some((part) => part === "." || part === ".."))) {
throw Object.assign(new Error("The sidecar update review is invalid."), { statusCode: 502 });
}
const reviewId = randomUUID();
state.vpsRuntimeUpdateReview = { reviewId, review, reviewedTarget, registrationId,
expiresAt: Date.now() + 5 * 60_000 };
sendJson(response, 200, { reviewId, stage: review.stage, continuing: review.continuing,
changedFiles: [...review.changedFiles], rebuildRequired: true,
imageId: review.imageId.slice(0, 19), containerId: review.containerId.slice(0, 12) });
return;
}
releaseMutation = acquireVpsMutationLock(state);
const result = await state.services.updateVpsSiwcRuntime({
remote: connection, networkName, reviewedTarget, registrationId, review: pending.review, confirmed: true,
});
invalidateSiwcWork(state);
const account = copySiwcAccountView(result?.account);
if (result.registrationId !== registrationId || account.registrationId !== registrationId ||
account.ownership !== "owned" || result.runtimeState !== "running" || result.keyChanged !== false) {
throw Object.assign(new Error("The sidecar update result was not confirmed."), { statusCode: 502 });
}
sendJson(response, 200, { account, runtimeState: "running", keyChanged: false });
} finally {
operation?.release();
if (releaseMutation) {
detachVpsConnection(state, connection);
releaseMutation();
}
connectionUse.release();
}
return;
}
if (["/api/siwc/vps/images/status", "/api/siwc/vps/images/action", "/api/siwc/vps/images/login-status"].includes(path)) {
requireLiveLocalAction(state, "VPS image generation");
const checking = path.endsWith("/images/status");
const polling = path.endsWith("/login-status");
const action = checking ? null : polling ? "login-poll" : body?.action;
// ponytail: 5 s polls over a 15-minute code would exhaust the shared 10-per-15-minute limit.
// Polls are bounded instead by a stored pending sign-in, single-flight use and the code expiry.
if (!polling) enforceRateLimit(state, path);
requireExactRequestBody(body, checking ? ["containerName", "networkName", "registrationId"]
: polling ? [] : ["action", "confirmed"], "Check this installed account's image generation again.");
if (!checking && !polling && (!["login-start", "login-cancel", "sign-out"].includes(action) ||
typeof body.confirmed !== "boolean" || (action !== "login-cancel" && body.confirmed !== true))) {
throw Object.assign(new Error("Confirm this image sign-in change first."), { statusCode: 400 });
}
rejectActiveVpsMutation(state);
const stored = checking ? null : requireVpsAddonTarget(state, "vpsImagesTarget",
"Check the installed account's image generation again.", { pending: polling });
const containerName = stored ? stored.reviewedTarget.containerName : body.containerName;
const networkName = stored ? stored.reviewedTarget.networkName : body.networkName;
requireDiscoveredNetwork(state, containerName, networkName);
const registrationId = stored ? stored.registrationId : requireSiwcId(body.registrationId);
const reviewedTarget = stored ? stored.reviewedTarget
: requireReviewedVpsOwnerTarget(state, containerName, networkName);
const connectionUse = acquireVpsConnectionUse(state);
const { connection } = connectionUse;
let operation;
let releaseMutation;
let detach = action === "sign-out" || action === "login-cancel" || polling;
try {
requireFullVpsScope(connection);
if (checking || polling) {
operation = acquireVpsCredentialOperation(state, polling ? "images-login-poll" : "images-status");
} else releaseMutation = acquireVpsMutationLock(state);
if (checking) state.vpsImagesTarget = null;
const result = checking
? await state.services.getVpsCodexImagesStatus({ remote: connection, networkName, reviewedTarget, registrationId })
: await state.services.changeVpsCodexImages({ remote: connection, networkName, reviewedTarget, registrationId,
action, expectedContainerId: stored.containerId, confirmed: polling ? false : body.confirmed });
const view = copyCodexImagesStatus(result);
if (polling && view.state === "pending") detach = false;
state.vpsImagesTarget = detach ? null : { reviewedTarget, registrationId, containerId: result.containerId,
pending: view.state === "pending", expiresAt: Date.now() + VPS_ADDON_TARGET_MS };
sendJson(response, 200, view);
} finally {
operation?.release();
if (detach) detachVpsConnection(state, connection);
releaseMutation?.();
connectionUse.release();
}
return;
}
if (path === "/api/siwc/vps/models/status" || path === "/api/siwc/vps/models/checks") {
requireLiveLocalAction(state, "VPS model discovery");
enforceRateLimit(state, path);
const checking = path.endsWith("/status");
requireExactRequestBody(body, checking ? ["containerName", "networkName", "registrationId"] : ["enabled", "confirmed"],
"Check this installed account's models again.");
if (!checking && (typeof body.enabled !== "boolean" || body.confirmed !== true)) {
throw Object.assign(new Error("Confirm this model check change first."), { statusCode: 400 });
}
rejectActiveVpsMutation(state);
const stored = checking ? null
: requireVpsAddonTarget(state, "vpsModelsTarget", "Check the installed account's models again.");
const containerName = stored ? stored.reviewedTarget.containerName : body.containerName;
const networkName = stored ? stored.reviewedTarget.networkName : body.networkName;
requireDiscoveredNetwork(state, containerName, networkName);
const registrationId = stored ? stored.registrationId : requireSiwcId(body.registrationId);
const reviewedTarget = stored ? stored.reviewedTarget
: requireReviewedVpsOwnerTarget(state, containerName, networkName);
const connectionUse = acquireVpsConnectionUse(state);
const { connection } = connectionUse;
let operation;
let releaseMutation;
try {
requireFullVpsScope(connection);
if (checking) {
operation = acquireVpsCredentialOperation(state, "models-status");
state.vpsModelsTarget = null;
} else releaseMutation = acquireVpsMutationLock(state);
const result = checking
? await state.services.getVpsModelDiscovery({ remote: connection, networkName, reviewedTarget, registrationId })
: await state.services.changeVpsModelChecks({ remote: connection, networkName, reviewedTarget, registrationId,
enabled: body.enabled, expectedContainerId: stored.containerId, confirmed: true });
const view = copyVpsModelsStatus(result);
if (!checking && view.checksEnabled !== body.enabled) {
throw Object.assign(new Error("The model check change was not confirmed."), { statusCode: 502 });
}
// An older sidecar cannot change model checks, so its status leaves no target.
state.vpsModelsTarget = view.state === "available" ? { reviewedTarget, registrationId,
containerId: result.containerId, expiresAt: Date.now() + VPS_ADDON_TARGET_MS } : null;
sendJson(response, 200, view);
} finally {
operation?.release();
releaseMutation?.();
connectionUse.release();
}
return;
}
if (path === "/api/siwc/vps/usage/status") {
requireLiveLocalAction(state, "VPS usage status");
enforceRateLimit(state, path);
requireExactRequestBody(body, ["containerName", "networkName", "registrationId"],
"Check this installed account's usage again.");
rejectActiveVpsMutation(state);
requireDiscoveredNetwork(state, body.containerName, body.networkName);
const registrationId = requireSiwcId(body.registrationId);
const reviewedTarget = requireReviewedVpsOwnerTarget(state, body.containerName, body.networkName);
const connectionUse = acquireVpsConnectionUse(state);
let operation;
try {
requireFullVpsScope(connectionUse.connection);
operation = acquireVpsCredentialOperation(state, "usage-status");
// Read-only: the sidecar's stored record becomes the bounded view here; nothing on the VPS changes.
const record = await state.services.getVpsUsageStatus({ remote: connectionUse.connection,
networkName: body.networkName, reviewedTarget, registrationId });
sendJson(response, 200, usageView(record, { registrationId }));
} finally {
operation?.release();
connectionUse.release();
}
return;
}
if (path === "/api/plan") {
rejectActiveVpsMutation(state);
const connectionUse = acquireVpsConnectionUse(state);
let credentialOperation;
try {
requireFullVpsScope(connectionUse.connection);
requireDiscoveredNetwork(
state,
body.containerName,
body.networkName,
);
credentialOperation = acquireVpsCredentialOperation(
state,
"plan-snapshot",
);
const sessionSnapshot = {
connection: connectionUse.connection,
lifecycleGeneration: state.vpsLifecycleGeneration,
discoveryGeneration: state.vpsDiscoveryGeneration,
};
state.sidecarPlan = null;
invalidateSuperGrokPlan(state);
invalidateClaudePlan(state);
invalidateLocalModelVpsPlan(state);
const account = requireVpsPlanAuthStatus(
await selectedSiwcAccount(state, { requirePlan: true }),
"Enable ChatGPT plan use for the selected account before reviewing this sidecar plan.",
);
const authBinding = await siwcBinding(state, account);
const reviewedTarget = Object.freeze({ ...state.connectionIdentity,
containerName: body.containerName, networkName: body.networkName,
...await state.services.reviewVpsSiwcTarget({
remote: connectionUse.connection, containerName: body.containerName, networkName: body.networkName,
requireSupportedNetwork: true,
}) });
const installed = await state.services.getVpsSiwcInstallationStatus({
remote: connectionUse.connection, networkName: body.networkName, reviewedTarget, targetAttested: true,
});
let legacyBinding = null;
let existingBinding = null;
if (installed?.state === "legacy") {
legacyBinding = await state.services.reviewVpsLegacyMigration({
remote: connectionUse.connection, networkName: body.networkName, reviewedTarget,
});
} else if (installed?.state === "stopped") {
const previous = state.vpsStoppedOwnerReview;
if (installed.registrationId !== previous?.account?.registrationId ||
previous.account.session !== "signed-out" || previous.account.planEnabled !== false ||
previous.expiresAt <= Date.now() ||
!["host", "port", "fingerprint", "username", "authentication", "privilege",
"loginUid", "effectiveUid", "containerName", "networkName", "n8nContainerId", "networkId"].every(
(key) => previous.reviewedTarget[key] === reviewedTarget[key])) {
throw Object.assign(new Error("Inspect the stopped, signed-out owner before reviewing replacement."),
{ statusCode: 409, recovery: "review-again" });
}
const bound = await state.services.reviewVpsSiwcReplacement({
remote: connectionUse.connection, networkName: body.networkName, reviewedTarget,
});
if (bound.registrationId !== previous.account.registrationId ||
bound.registrationId === account.registrationId) {
throw Object.assign(new Error("The installed owner or selected new account changed."), { statusCode: 409 });
}
existingBinding = Object.freeze({ ...bound, ownerHostId: previous.account.ownerHostId,
expectedGeneration: previous.account.generation });
} else if (installed?.state !== "absent") {
const detail = {
staged: "An earlier installation on this server was interrupted. Open Recover a transfer or staged installation to resume it.",
partial: "An earlier migration on this server started but did not finish. Open Recover a transfer or staged installation.",
owned: "A ChatGPT plan sidecar is already installed and running here. To update it, open Manage the installed ChatGPT session and review the sidecar update.",
updating: "A sidecar update on this server was interrupted. Open Manage the installed ChatGPT session to finish it.",
}[installed?.state] ?? "The installed sidecar must be signed out or manually recovered before another write.";
throw Object.assign(new Error(detail),
{ statusCode: 409, recovery: "review-again", code: `vps_sidecar_${installed?.state ?? "unknown"}` });
}
requireCurrentVpsCredentialOperation(
state,
credentialOperation.operation,
);
requireUnchangedVpsSession(state, sessionSnapshot, {
allowCredentialSnapshot: true,
});
state.sidecarPlan = {
planId: randomUUID(),
containerName: body.containerName,
networkName: body.networkName,
authBinding,
account,
reviewedTarget,
legacyBinding,
existingBinding,
};
sendJson(response, 200, {
planId: state.sidecarPlan.planId,
installDirectory: "/docker/n8n-openai-oauth",
operationLockPath: "/docker/n8n-openai-oauth/.openai-oauth-operation.lock",
temporaryBuildStatePath: "/docker/n8n-openai-oauth/.openai-oauth-operation.lock/buildx",
sidecarProject: "n8n-openai-oauth",
endpointHostname: SIDECAR_HOSTNAME,
networkName: body.networkName,
containerName: body.containerName,
n8nContainerId: reviewedTarget.n8nContainerId,
networkId: reviewedTarget.networkId,
account: copySiwcAccountView(account),
...(legacyBinding ? { migrationRequired: true, legacyResourcesPreserved: true,
requiresMigrationConsent: true } : {}),
...(existingBinding ? { replacementRequired: true, oldSessionSignedOut: true,
oldHistoryRetained: true, requiresReplacementConsent: true } : {}),
existingN8nChanges: [],
existingN8nRestarts: 0,
publishedPorts: [],
});
} finally {
credentialOperation?.release();
connectionUse.release();
}
return;
}
if (path === "/api/assistant/plan") {
rejectActiveVpsMutation(state);
const connectionUse = acquireVpsConnectionUse(state);
try {
requireFullVpsScope(connectionUse.connection);
const includeSearxng = requireAssistantSearxngSelection(body.includeSearxng);
requireDiscoveredNetwork(
state,
body.containerName,
body.networkName,
);
const instanceAi = requireEnabledInstanceAi(state, body.containerName);
requireAssistantNetworkSupport(state.networksByContainer.get(body.containerName), body.networkName, includeSearxng);
const warnings = assistantPlanWarnings(requireDiscoveredContainer(state, body.containerName));
state.assistantPlan = null;
invalidateSuperGrokPlan(state);
invalidateClaudePlan(state);
invalidateLocalModelVpsPlan(state);
state.assistantPlan = {
planId: randomUUID(),
containerName: body.containerName,
networkName: body.networkName,
includeSearxng,
instanceAi,
};
sendJson(response, 200, {
planId: state.assistantPlan.planId,
installDirectory: ASSISTANT_ROOT,
companionProject: "generated ownership-bound project after confirmation",
sandboxUrl: "generated after verified installation",
includeSearxng,
webSearch: includeSearxng ? "enabled" : "disabled",
...(includeSearxng ? { searxngUrl: "generated after verified installation" } : {}),
networkName: body.networkName,
instanceAi,
existingN8nChanges: [],
existingN8nRestarts: 0,
publishedPorts: [],
privilegedRunner: true,
warnings,
preview: true,
});
} finally {
connectionUse.release();
}
return;
}
if (path === "/api/assistant/install") {
rejectActiveVpsMutation(state);
enforceRateLimit(state, path);
const includeSearxng = requireAssistantSearxngSelection(body.includeSearxng);
const reviewedPlan = state.assistantPlan;
requireReviewedVpsPlan(reviewedPlan, body, "AI Assistant");
requireDiscoveredNetwork(
state,
body.containerName,
body.networkName,
);
requireReviewedAssistantPlan(state, reviewedPlan, body);
const connectionUse = acquireVpsConnectionUse(state);
const { connection } = connectionUse;
requireFullVpsScope(connection);
state.assistantPlan = null;
const releaseVpsMutationLock = acquireVpsMutationLock(state);
try {
const freshNetworks = await state.services.discoverNetworks(
connection,
body.containerName,
);
requireFreshAssistantInstallState(reviewedPlan, freshNetworks, body);
requireAssistantNetworkSupport(freshNetworks, body.networkName, includeSearxng);
const result = await state.services.installAssistant({
remote: connection,
networkName: body.networkName,
confirmed: body.confirmed,
includeSearxng,
});
sendJson(
response,
200,
createSafeAssistantInstallResult(result, reviewedPlan),
);
} finally {
detachVpsConnection(state, connection);
releaseVpsMutationLock();
connectionUse.release();
}
return;
}
if (path === "/api/install") {
rejectActiveVpsMutation(state);
enforceRateLimit(state, path);
const reviewedPlan = state.sidecarPlan;
requireExactRequestBody(body,
["planId", "containerName", "networkName", "confirmed", "backgroundConsent", "imagesConsent",
...(reviewedPlan?.legacyBinding ? ["migrationConsent"] : []),
...(reviewedPlan?.existingBinding ? ["replacementConsent"] : [])],
"Confirm the reviewed ChatGPT account and background n8n use.");
if (body.confirmed !== true || body.backgroundConsent !== true || typeof body.imagesConsent !== "boolean" ||
(reviewedPlan?.legacyBinding && body.migrationConsent !== true) ||
(reviewedPlan?.existingBinding && body.replacementConsent !== true)) {
throw Object.assign(new Error("Approve the reviewed destination, background use, and any separate migration or replacement."), { statusCode: 400 });
}
requireReviewedVpsPlan(reviewedPlan, body, "sidecar");
requireDiscoveredNetwork(
state,
body.containerName,
body.networkName,
);
const connectionUse = acquireVpsConnectionUse(state);
const { connection } = connectionUse;
requireFullVpsScope(connection);
state.sidecarPlan = null;
const releaseVpsMutationLock = acquireVpsMutationLock(state);
let keepConnectionForImages = false;
try {
if (reviewedPlan.resume) await requireRecoveryBinding(state, reviewedPlan.sourceBinding);
else await requireMatchingSiwcBinding(state, reviewedPlan.authBinding);
const result = await state.services.installSidecar({
remote: connection,
networkName: body.networkName,
registration: { storageRoot: state.storageRoot, registrationId: reviewedPlan.authBinding.registrationId },
authBinding: reviewedPlan.authBinding,
reviewedTarget: reviewedPlan.reviewedTarget,
confirmed: true,
...(reviewedPlan.resume ? { resume: reviewedPlan.resume } : {}),
backgroundConsent: { acceptedAt: new Date().toISOString(), noticeVersion: "siwc-vps-2026-10-04" },
...(reviewedPlan.legacyBinding ? { migrationConsent: true,
legacyBinding: reviewedPlan.legacyBinding } : {}),
...(reviewedPlan.existingBinding ? { replacementConsent: true,
existingBinding: reviewedPlan.existingBinding } : {}),
});
if (!result || result.baseUrl !== "http://n8n-openai-oauth:10531/v1" ||
typeof result.clientCredential !== "string" ||
!/^[A-Za-z0-9_-]{32,256}$/u.test(result.clientCredential) ||
result.credentialShownOnce !== true ||
!["installed", "migrated", "replaced", "partial"].includes(result.deploymentMode) ||
(result.deploymentMode === "migrated" && (result.migratedLegacy !== true || result.legacyRetained !== true ||
!(reviewedPlan.resume
? reviewedPlan.resume.deploymentMode === result.deploymentMode
: reviewedPlan.legacyBinding))) ||
(result.deploymentMode === "replaced" && (result.replacedAccount !== true ||
!(reviewedPlan.resume
? reviewedPlan.resume.deploymentMode === result.deploymentMode
: reviewedPlan.existingBinding))) ||
(result.deploymentMode === "partial" && !result.runtimeFailure && !result.finalizationFailure) ||
(result.hostPublication !== "none" &&
!(result.deploymentMode === "partial" && result.runtimeState === "unknown" &&
result.hostPublication === "unknown")) ||
result.account?.registrationId !== reviewedPlan.authBinding.registrationId ||
!Array.isArray(result.models) || result.models.some((model) =>
typeof model !== "string" || !/^[A-Za-z0-9_.:-]{1,128}$/u.test(model))) {
throw Object.assign(new Error("The sidecar returned an invalid installation result."), { statusCode: 502 });
}
const installView = {
baseUrl: result.baseUrl, clientCredential: result.clientCredential,
credentialShownOnce: true, models: result.models, deploymentMode: result.deploymentMode,
hostPublication: result.hostPublication,
account: copySiwcAccountView(result.account),
...copySiwcReadiness(result),
...(result.migratedLegacy === true ? { migratedLegacy: true, legacyRetained: result.legacyRetained === true } : {}),
...(result.replacedAccount === true ? { replacedAccount: true } : {}),
};
if (body.imagesConsent) {
if (result.readiness === "verified" && result.runtimeState === "running" &&
!result.finalizationFailure && result.deploymentMode !== "partial") {
try {
const reviewedTarget = reviewedPlan.reviewedTarget;
const registrationId = reviewedPlan.authBinding.registrationId;
const options = { remote: connection, networkName: body.networkName, reviewedTarget, registrationId };
const current = await state.services.getVpsCodexImagesStatus(options);
const status = copyCodexImagesStatus(current);
const images = status.state === "off"
? await state.services.changeVpsCodexImages({ ...options, action: "login-start",
expectedContainerId: current.containerId, confirmed: true })
: current;
installView.images = copyCodexImagesStatus(images);
if (installView.images.state === "pending") {
state.vpsImagesTarget = { reviewedTarget, registrationId, containerId: images.containerId,
pending: true, expiresAt: Date.now() + VPS_ADDON_TARGET_MS };
keepConnectionForImages = true;
}
} catch {
installView.imagesFailure = { message: "Sidecar installed. Image sign-in did not start. You can try again under Manage the installed ChatGPT session." };
}
} else {
installView.imagesFailure = { message: "Sidecar installed. Image sign-in did not start. You can try again under Manage the installed ChatGPT session." };
}
}
sendJson(response, 200, installView);
} finally {
if (!keepConnectionForImages) detachVpsConnection(state, connection);
releaseVpsMutationLock();
connectionUse.release();
}
return;
}
if (path === "/api/disconnect") {
rejectUnsafeVpsDisconnect(state);
detachVpsConnection(state, state.connection, { clearScannedHost: true });
sendJson(response, 200, { disconnected: true });
return;
}
sendJson(response, 404, { error: "Not found." });
}
function persistentControlBusy(state) {
return Boolean(
state.localInstallInFlight ||
state.localCredentialRotationInFlight ||
getPendingLocalCredentialRotation(state) ||
state.vpsFingerprintOperation ||
state.vpsConnectionOperation ||
state.vpsConnectionUses.size > 0 ||
state.vpsMutationInFlight ||
state.vpsCredentialOperation ||
oauthCredentialOperationInFlight(state)
);
}
function requireControlToken(request, state) {
if (!tokenMatches(request.headers["x-relmio-control"], state.controlToken)) {
throw Object.assign(new Error("Unauthorized."), { statusCode: 401 });
}
}
async function handleBrowserBootstrap(request, response, path, state) {
if (path === BROWSER_PREPARE_PATH) {
if (request.method !== "POST") {
sendJson(response, 405, { error: "Method not allowed." });
return;
}
requireApiToken(request, state);
requireSameOrigin(request, state);
const body = await readJsonBody(request);
if (!exactObjectKeys(body, ["route"]) || !BROWSER_BOOTSTRAP_ROUTES.has(body.route)) {
throw Object.assign(new Error("Browser launch route is invalid."), { statusCode: 400 });
}
const launchUrl = await prepareBrowserBootstrap(state, body.route);
sendJson(response, 201, { launchUrl });
return;
}
if (path === BROWSER_BOOTSTRAP_PATH) {
if (request.method !== "POST") {
sendJson(response, 405, { error: "Method not allowed." });
return;
}
if (request.headers.origin !== "null") {
throw Object.assign(new Error("Cross-origin browser launch rejected."), { statusCode: 403 });
}
let body;
try { body = await readFormBody(request); } catch {
throw genericBrowserBootstrapFailure(state);
}
const route = consumeBrowserBootstrap(state, body);
sendBrowserBootstrapHtml(response, state, route);
return;
}
if (path === BROWSER_TRANSFER_PATH) {
if (request.method !== "POST") {
sendJson(response, 405, { error: "Method not allowed." });
return;
}
if (request.headers.origin !== state.origin) {
throw Object.assign(new Error("Cross-origin browser transfer rejected."), {
statusCode: 403,
});
}
let body;
try { body = await readJsonBody(request); } catch {
throw genericBrowserTransferFailure(state);
}
const sessionToken = consumeBrowserTransfer(state, body);
sendJson(response, 200, { sessionToken });
return;
}
sendJson(response, 404, { error: "Not found." });
}
async function handlePersistentControl(request, response, path, state) {
if (!state.controlToken) {
sendJson(response, 404, { error: "Not found." });
return;
}
requireControlToken(request, state);
if (request.method === "GET" && path === "/__relmio/control/status") {
sendJson(response, 200, {
kind: "relmio-dashboard-control",
protocolVersion: 1,
packageVersion: PACKAGE_VERSION,
instanceId: state.controlInstanceId,
pid: process.pid,
origin: state.origin,
});
return;
}
if (request.method === "POST" && path === "/__relmio/control/stop") {
if (persistentControlBusy(state)) {
throw Object.assign(
new Error(
"Relmio is completing another operation. Wait for it to finish before stopping the dashboard.",
),
{ statusCode: 409 },
);
}
sendJson(response, 202, {
stopping: true,
instanceId: state.controlInstanceId,
});
if (!state.controlStopRequested) {
state.controlStopRequested = true;
setImmediate(() => {
try {
state.onControlStop();
} catch {
// The daemon owns shutdown reporting after this response is sent.
}
});
}
return;
}
if (!["GET", "POST"].includes(request.method)) {
sendJson(response, 405, { error: "Method not allowed." });
return;
}
sendJson(response, 404, { error: "Not found." });
}
function createRequestHandler(state) {
return async (request, response) => {
setSecurityHeaders(response);
let path;
try {
if (state.closing) {
sendJson(response, 503, { error: "The local wizard is closing." });
return;
}
const url = new URL(request.url, state.origin);
path = url.pathname;
if (path.startsWith("/__relmio/oauth/launch/")) {
const login = state.oauthLogin;
if (request.method !== "GET" || url.search || url.hash ||
request.headers.host !== new URL(state.origin).host ||
(request.headers.origin && request.headers.origin !== state.origin) ||
(request.headers["sec-fetch-site"] && request.headers["sec-fetch-site"] !== "same-origin") ||
!login || login.status !== "pending" || login.attempt.launchMode !== "same-browser" ||
path !== `/__relmio/oauth/launch/${login.attemptId}`) {
sendJson(response, 404, { error: "Sign-in launch is no longer available. Return to Relmio." });
return;
}
// The authenticated POST creates this one-use launch capability. JSON contains the attempt ID,
// not the authorization URL. The redirect sends the authorization URL to the browser; browser
// history is outside this guarantee.
const authorization = login.attempt.consumeBrowserAuthorization();
if (!isOpenAiAuthorizationUrl(authorization)) {
sendJson(response, 404, { error: "Sign-in launch is no longer available. Return to Relmio." });
return;
}
response.writeHead(302, { Location: authorization, "Cache-Control": "no-store",
"Referrer-Policy": "no-referrer" });
response.end();
return;
}
if (path.startsWith("/__relmio/browser/")) {
await handleBrowserBootstrap(request, response, path, state);
return;
}
if (path.startsWith("/__relmio/control/")) {
await handlePersistentControl(request, response, path, state);
return;
}
if (path.startsWith("/api/")) {
await handleApi(request, response, path, state);
return;
}
if (request.method !== "GET" || !(path in state.uiFiles)) {
if (
request.method === "GET" &&
!path.startsWith("/api/") &&
request.headers.accept?.split(",").some((entry) => entry.trim().split(";", 1)[0] === "text/html")
) {
const html = `<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Page not found | Relmio</title>
<link rel="stylesheet" href="/relmio-ui.css">
</head>
<body class="rm-app rm-app--fit">
<main id="main-content" class="rm-app__main">
<div class="rm-container rm-split">
<section class="rm-panel">
<div class="rm-panel__header">
<div class="rm-panel__heading">
<h1 class="rm-h1">Page not found</h1>
<p class="rm-muted">This address is not part of the Relmio wizard.</p>
</div>
</div>
<div class="rm-panel__body"><p>Wizard pages open only through the private link Relmio gives your browser. Go back to your Relmio tab, or run <code>relmio open</code> in a terminal to start a new session.</p></div>
</section>
</div>
</main>
</body>
</html>`;
response.writeHead(404, {
"Content-Type": "text/html; charset=utf-8",
"Content-Length": Buffer.byteLength(html),
});
response.end(html);
} else {
sendJson(response, 404, { error: "Not found." });
}
return;
}
const contentType =
path.endsWith(".js")
? "text/javascript; charset=utf-8"
: path.endsWith(".svg")
? "image/svg+xml; charset=utf-8"
: path.endsWith(".png")
? "image/png"
: path.endsWith(".css")
? "text/css; charset=utf-8"
: path.endsWith(".woff2")
? "font/woff2"
: "text/html; charset=utf-8";
const contents = state.uiFiles[path];
response.writeHead(200, {
"Content-Type": contentType,
"Content-Length": Buffer.byteLength(contents),
});
response.end(contents);
} catch (error) {
if (!response.headersSent) {
const managedPartialStack =
error?.code === LOCAL_N8N_MANAGED_PARTIAL_STACK_ERROR_CODE;
const retryableStackStartup =
error?.code === LOCAL_N8N_STACK_RETRYABLE_STARTUP_ERROR_CODE;
const retryableNgrokSetup =
retryableStackStartup &&
error?.failureKind ===
LOCAL_N8N_STACK_NGROK_SETUP_REJECTED_FAILURE_KIND;
const failure = safeFailure(error);
sendJson(response, failure.status, {
...failure,
error: managedPartialStack
? "Relmio confirmed that its owned partial local n8n + ngrok stack remains. Use the explicit removal control to retry cleanup safely."
: failure.error,
...(error.retryablePlan === true ? { retryablePlan: true } : {}),
...(retryableNgrokSetup ? { retryableNgrokSetup: true } : {}),
...(managedPartialStack ? { managedPartialStack: true } : {}),
});
} else {
response.end();
}
}
};
}
export async function startWizardServer({
sessionToken,
storageRoot = resolveSiwcStorageRoot(),
runtimeId = "local",
services = defaultServices,
uiFiles,
port = 0,
previewMode = false,
previewFixture = null,
oauthShutdownWaitMs = OAUTH_SHUTDOWN_WAIT_MS,
vpsConnectionIdleMs = VPS_CONNECTION_IDLE_MS,
controlToken = null,
controlInstanceId = null,
onControlStop = null,
browserHandoffRoot = null,
browserBootstrapTtlMs = BROWSER_BOOTSTRAP_TTL_MS,
maxPendingBrowserBootstraps = MAX_PENDING_BROWSER_BOOTSTRAPS,
browserTransferTtlMs = BROWSER_TRANSFER_TTL_MS,
maxPendingBrowserTransfers = MAX_PENDING_BROWSER_TRANSFERS,
createBrowserHandoff = createPrivateBrowserHandoff,
randomBytes = createRandomBytes,
now = Date.now,
setTimer = setTimeout,
clearTimer = clearTimeout,
} = {}) {
if (typeof sessionToken !== "string" || sessionToken.length < 32) {
throw new TypeError("A strong wizard session token is required.");
}
if (typeof storageRoot !== "string" || !isAbsolute(storageRoot) ||
typeof runtimeId !== "string" || !/^[A-Za-z0-9_-]{1,64}$/u.test(runtimeId)) {
throw new TypeError("The SIWC host storage and runtime must be trusted.");
}
if (previewFixture !== null && (!previewMode ||
!["signed-out", "connected", "reauthorize", "plan-not-granted", "usage-limit", "handoff-pending", "staged"].includes(previewFixture))) {
throw new TypeError("Select a valid sanitized preview fixture.");
}
if (!Number.isSafeInteger(vpsConnectionIdleMs) || vpsConnectionIdleMs < 1) {
throw new TypeError("The VPS connection idle timeout must be a positive integer.");
}
if (
!Number.isSafeInteger(browserBootstrapTtlMs) || browserBootstrapTtlMs < 1 ||
browserBootstrapTtlMs > BROWSER_BOOTSTRAP_TTL_MS ||
!Number.isSafeInteger(maxPendingBrowserBootstraps) ||
maxPendingBrowserBootstraps < 1 || maxPendingBrowserBootstraps > 32 ||
!Number.isSafeInteger(browserTransferTtlMs) || browserTransferTtlMs < 1 ||
browserTransferTtlMs > BROWSER_TRANSFER_TTL_MS ||
!Number.isSafeInteger(maxPendingBrowserTransfers) ||
maxPendingBrowserTransfers < 1 || maxPendingBrowserTransfers > 32 ||
typeof createBrowserHandoff !== "function" || typeof randomBytes !== "function" ||
typeof now !== "function" || typeof setTimer !== "function" ||
typeof clearTimer !== "function" ||
(browserHandoffRoot !== null && typeof browserHandoffRoot !== "string")
) {
throw new TypeError("The private browser bootstrap adapter is invalid.");
}
const controlConfigured =
controlToken !== null ||
controlInstanceId !== null ||
onControlStop !== null;
if (
controlConfigured &&
(
typeof controlToken !== "string" ||
!/^[A-Za-z0-9_-]{43}$/u.test(controlToken) ||
typeof controlInstanceId !== "string" ||
!/^[a-f0-9]{8}-[a-f0-9]{4}-[1-5][a-f0-9]{3}-[89ab][a-f0-9]{3}-[a-f0-9]{12}$/iu.test(controlInstanceId) ||
typeof onControlStop !== "function"
)
) {
throw new TypeError("The persistent dashboard stop handler is invalid.");
}
const resolvedServices = { ...defaultServices, ...services };
const state = {
sessionToken,
storageRoot,
runtimeId,
services: resolvedServices,
localChatTest:
resolvedServices.localChatTest ??
resolvedServices.createLocalChatTestService(),
uiFiles: uiFiles ?? (await loadDefaultUiFiles()),
origin: "http://127.0.0.1",
connection: null,
connectionIdentity: null,
vpsConnectionIdleMs,
vpsConnectionIdleExpiry: null,
vpsConnectionUses: new Set(),
scannedHost: null,
vpsFingerprintGeneration: 0,
vpsFingerprintOperation: null,
vpsConnectionOperation: null,
vpsLifecycleGeneration: 0,
vpsDiscoveryGeneration: 0,
discovery: null,
networksByContainer: new Map(),
sidecarPlan: null,
vpsStoppedOwnerReview: null,
vpsOwnerTargetReview: null,
vpsRuntimeUpdateReview: null,
vpsImagesTarget: null,
localImagesTarget: null,
vpsModelsTarget: null,
assistantPlan: null,
supergrokPlan: null,
supergrokPlanGeneration: 0,
claudePlan: null,
claudePlanGeneration: 0,
localModelVpsPlan: null,
l