ai_api / src /app /(dashboard) /dashboard /tools /agent-bridge /normalizeState.ts
Yogesh
initial deploy
cd8bd0a
Raw
History Blame Contribute Delete
3.39 kB
import type {
AgentBridgePageData,
AgentBridgeServerState,
AgentStateEntry,
AgentMappingsMap,
} from "./AgentBridgePageClient";
function defaultServerState(): AgentBridgeServerState {
return {
running: false,
port: 443,
certTrusted: false,
upstreamCa: null,
lastStartedAt: null,
activeConns: 0,
interceptedCount: 0,
dnsConfigured: false,
orphanedStateDetected: false,
};
}
export const DEFAULT_AGENT_BRIDGE_STATE: AgentBridgePageData = {
serverState: defaultServerState(),
agentStates: [],
bypassPatterns: [],
mappings: {},
};
function isRecord(v: unknown): v is Record<string, unknown> {
return typeof v === "object" && v !== null && !Array.isArray(v);
}
/**
* Normalize whatever `/api/tools/agent-bridge/state` returns into the shape the
* page/components require, ALWAYS returning a well-formed object (#3318).
*
* The page previously assigned the raw response straight into `initialData`, but
* the route returns `{ server, agents }` while the UI reads
* `{ serverState, agentStates, bypassPatterns, mappings }` β€” leaving
* `serverState` undefined and crashing `serverState.running`.
*
* This maps the known server fields through (incl. the legacy `server` key β†’
* `serverState`, `server.certExists` β†’ `certTrusted`) and falls back to safe
* defaults for everything else, so the page renders instead of crashing.
* Note: the legacy `agents` payload has a different per-entry shape than
* `AgentStateEntry`, so it is intentionally NOT coerced β€” `agentStates` defaults
* to `[]` unless the response already provides the correct `agentStates` key.
*/
export function normalizeAgentBridgeState(raw: unknown): AgentBridgePageData {
if (!isRecord(raw)) return { ...DEFAULT_AGENT_BRIDGE_STATE, serverState: defaultServerState() };
const serverState = defaultServerState();
const source = isRecord(raw.serverState)
? raw.serverState
: isRecord(raw.server)
? raw.server
: undefined;
if (source) {
if (typeof source.running === "boolean") serverState.running = source.running;
if (typeof source.port === "number") serverState.port = source.port;
// accept both the canonical `certTrusted` and the legacy `certExists`
if (typeof source.certTrusted === "boolean") serverState.certTrusted = source.certTrusted;
else if (typeof source.certExists === "boolean") serverState.certTrusted = source.certExists;
if (typeof source.upstreamCa === "string") serverState.upstreamCa = source.upstreamCa;
if (typeof source.lastStartedAt === "string") serverState.lastStartedAt = source.lastStartedAt;
if (typeof source.activeConns === "number") serverState.activeConns = source.activeConns;
if (typeof source.interceptedCount === "number") {
serverState.interceptedCount = source.interceptedCount;
}
if (typeof source.dnsConfigured === "boolean") serverState.dnsConfigured = source.dnsConfigured;
if (typeof source.orphanedStateDetected === "boolean") {
serverState.orphanedStateDetected = source.orphanedStateDetected;
}
}
return {
serverState,
agentStates: Array.isArray(raw.agentStates) ? (raw.agentStates as AgentStateEntry[]) : [],
bypassPatterns: Array.isArray(raw.bypassPatterns) ? (raw.bypassPatterns as string[]) : [],
mappings: isRecord(raw.mappings) ? (raw.mappings as AgentMappingsMap) : {},
};
}