NODEDC_PLATFORM/infra/deploy-runner/ops-mcp-workspace-tools/overlays/ops-agents/src/app.ts

157 lines
5.0 KiB
TypeScript

import Fastify, { type FastifyInstance } from "fastify";
import { ZodError } from "zod";
import type { AppConfig } from "./config.js";
import { createPool, DatabaseNotConfiguredError } from "./db/pool.js";
import { ToolExecutionInputError } from "./mcp/tool-runtime.js";
import { AgentsRepository } from "./repositories/agents.js";
import { registerAgentRoutes } from "./routes/agents.js";
import { registerEngineGatewayRoutes } from "./routes/engine.js";
import { registerHealthRoutes } from "./routes/health.js";
import { registerInstallerRoutes } from "./routes/install.js";
import { registerMcpRoutes } from "./routes/mcp.js";
import { registerOntologyGatewayRoutes } from "./routes/ontology.js";
import { registerPublicRoutes } from "./routes/public.js";
import { registerToolRoutes } from "./routes/tools.js";
import { ForbiddenError } from "./security/authorization.js";
import { UnauthorizedError } from "./security/bearer.js";
import { InternalAuthNotConfiguredError } from "./security/internal.js";
import { TaskerAdapterError, TaskerAdapterNotConfiguredError, TaskerAdapterUnavailableError, TaskerClient } from "./tasker/client.js";
export async function buildApp(config: AppConfig): Promise<FastifyInstance> {
const pool = createPool(config);
const agentsRepository = pool ? new AgentsRepository(pool) : null;
const taskerClient = new TaskerClient({
baseUrl: config.NODEDC_TASKER_INTERNAL_URL,
internalAccessToken: config.NODEDC_INTERNAL_ACCESS_TOKEN,
});
const app = Fastify({
bodyLimit: 10 * 1024 * 1024,
logger: {
level: config.LOG_LEVEL,
},
});
app.addHook("onClose", async () => {
await pool?.end();
});
app.setErrorHandler((error, _request, reply) => {
if (error instanceof ZodError) {
void reply.status(400).send({
ok: false,
error: "validation_error",
details: error.issues,
});
return;
}
if (error instanceof DatabaseNotConfiguredError) {
void reply.status(503).send({
ok: false,
error: "database_not_configured",
message: "DATABASE_URL is required for Agent Gateway persistence endpoints.",
});
return;
}
if (error instanceof UnauthorizedError) {
void reply.status(401).send({
ok: false,
error: "unauthorized",
message: error.message,
});
return;
}
if (error instanceof InternalAuthNotConfiguredError) {
void reply.status(503).send({
ok: false,
error: "internal_auth_not_configured",
message: error.message,
});
return;
}
if (error instanceof ForbiddenError) {
void reply.status(403).send({
ok: false,
error: "forbidden",
message: error.message,
});
return;
}
if (error instanceof ToolExecutionInputError) {
void reply.status(error.httpStatus).send({
ok: false,
error: error.code,
message: error.message,
details: error.details,
});
return;
}
if (error instanceof TaskerAdapterNotConfiguredError) {
void reply.status(503).send({
ok: false,
error: "tasker_adapter_not_configured",
message: error.message,
});
return;
}
if (error instanceof TaskerAdapterError) {
void reply.status(error.statusCode).send({
ok: false,
error: "tasker_adapter_error",
message: error.message,
tasker_status: error.statusCode,
tasker_payload: error.payload,
});
return;
}
if (error instanceof TaskerAdapterUnavailableError) {
void reply.status(502).send({
ok: false,
error: "tasker_adapter_unavailable",
message: error.message,
});
return;
}
app.log.error(error);
void reply.status(500).send({
ok: false,
error: "internal_server_error",
message: "Agent Gateway request failed.",
});
});
await registerPublicRoutes(app);
await registerInstallerRoutes(app, { publicUrl: config.NODEDC_AGENT_GATEWAY_PUBLIC_URL });
await registerHealthRoutes(app, config, pool);
await registerAgentRoutes(app, {
agentsRepository,
codexInstallChannel: config.NODEDC_OPS_CODEX_INSTALL_CHANNEL,
codexNpmSpec: config.NODEDC_OPS_CODEX_NPM_SPEC,
publicUrl: config.NODEDC_AGENT_GATEWAY_PUBLIC_URL,
internalAccessToken: config.NODEDC_AGENT_GATEWAY_INTERNAL_TOKEN,
aiWorkspaceRunTokenTtlSeconds: config.NODEDC_AI_WORKSPACE_RUN_TOKEN_TTL_SECONDS,
});
await registerEngineGatewayRoutes(app, {
engineInternalUrl: config.NODEDC_ENGINE_INTERNAL_URL,
publicUrl: config.NODEDC_AGENT_GATEWAY_PUBLIC_URL,
});
await registerToolRoutes(app, { agentsRepository, taskerClient });
await registerMcpRoutes(app, { agentsRepository, taskerClient });
await registerOntologyGatewayRoutes(app, {
agentsRepository,
ontologyCoreUrl: config.NODEDC_ONTOLOGY_CORE_URL,
ontologyCoreAccessToken: config.NODEDC_ONTOLOGY_CORE_ACCESS_TOKEN ?? config.NODEDC_INTERNAL_ACCESS_TOKEN,
});
return app;
}