import Fastify, { type FastifyInstance, type FastifyServerOptions } from "fastify"; import websocket from "@fastify/websocket"; import { resolveAccount, LOCAL_ACCOUNT_ID } from "./auth.js"; import { createOpencodeProbe } from "./opencode.js"; import gatewayModule from "./modules/trapos-cloud-gateway/index.js"; import { GatewayRegistry } from "./modules/trapos-cloud-gateway/registry.js"; import mcpModule from "./modules/mcp/index.js"; export type Config = { host: string; port: number; serverPassword?: string; requestTimeoutMs: number; opencodeUrl?: string; opencodeUsername: string; opencodeServerPassword?: string; // MCP HTTP endpoint (exec-lua / write-file / probe-computers). Bound to mcpHost, // which defaults to 127.0.0.1 so the powerful tools stay local-only. mcpEnabled: boolean; mcpHost: string; mcpPort: number; mcpDefaultTimeoutMs: number; mcpMaxTimeoutMs: number; mcpProbeTimeoutMs: number; logger: FastifyServerOptions["logger"]; }; const MAX_PAYLOAD_BYTES = 1024 * 1024; // ~1MB // Composes the service: registry + opencode probe + auth seam, wired into the // trapos-cloud-gateway module, plus an optional MCP app sharing the same registry. // Never reads process.env (that is index.ts's job), so it is fully constructable // from tests. The MCP app is a separate Fastify instance (its own host/port) so it // can bind to localhost independently of the public gateway listener. export async function createApp( config: Config, ): Promise<{ app: FastifyInstance; mcpApp: FastifyInstance | null; registry: GatewayRegistry }> { const app = Fastify({ logger: config.logger, disableRequestLogging: true }); const registry = new GatewayRegistry(app.log, config.requestTimeoutMs); const probe = createOpencodeProbe({ opencodeUrl: config.opencodeUrl, opencodeUsername: config.opencodeUsername, opencodeServerPassword: config.opencodeServerPassword, }); await app.register(websocket, { options: { maxPayload: MAX_PAYLOAD_BYTES } }); await app.register(gatewayModule, { registry, probe, resolveAccount: (secret) => resolveAccount(secret, config.serverPassword), }); const mcpApp = config.mcpEnabled ? await createMcpApp(config, registry) : null; return { app, mcpApp, registry }; } async function createMcpApp(config: Config, registry: GatewayRegistry): Promise { const mcpApp = Fastify({ logger: config.logger, disableRequestLogging: true }); await mcpApp.register(mcpModule, { registry, accountId: LOCAL_ACCOUNT_ID, defaultTimeoutMs: config.mcpDefaultTimeoutMs, maxTimeoutMs: config.mcpMaxTimeoutMs, probeTimeoutMs: config.mcpProbeTimeoutMs, }); return mcpApp; }