cc-libs/tools/trapos-server/src/app.ts

70 lines
2.6 KiB
TypeScript

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<FastifyInstance> {
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;
}