rowboat/apps/cli/src/server.ts

202 lines
6 KiB
TypeScript
Raw Normal View History

2025-12-02 13:24:58 +05:30
import { Hono } from 'hono';
import { serve } from '@hono/node-server'
import { streamSSE } from 'hono/streaming'
import { describeRoute, validator, resolver, openAPIRouteHandler } from "hono-openapi"
import z from 'zod';
import container from './di/container.js';
2025-12-16 14:48:04 +05:30
import { executeTool, listServers, listTools } from "./mcp/mcp.js";
import { ListToolsResponse, McpServerDefinition, McpServerList } from "./mcp/schema.js";
2025-12-02 13:24:58 +05:30
import { IMcpConfigRepo } from './mcp/repo.js';
import { IModelConfigRepo } from './models/repo.js';
import { ModelConfig, Provider } from "./models/models.js";
import { IAgentsRepo } from "./agents/repo.js";
import { Agent } from "./agents/agents.js";
import { AskHumanResponsePayload, authorizePermission, createMessage, createRun, replyToHumanInputRequest, Run, stop, ToolPermissionAuthorizePayload } from './runs/runs.js';
2025-12-09 16:35:27 +05:30
import { IRunsRepo, CreateRunOptions, ListRunsResponse } from './runs/repo.js';
2025-12-02 13:24:58 +05:30
import { IBus } from './application/lib/bus.js';
2025-12-17 11:57:45 +05:30
import { cors } from 'hono/cors';
2025-12-02 13:24:58 +05:30
let id = 0;
const routes = new Hono()
.post(
'/runs/:runId/messages/new',
describeRoute({
summary: 'Create a new message',
description: 'Create a new message',
responses: {
200: {
description: 'Message created',
content: {
'application/json': {
schema: resolver(z.object({
messageId: z.string(),
})),
},
},
},
},
}),
validator('param', z.object({
runId: z.string(),
})),
validator('json', z.object({
message: z.string(),
})),
async (c) => {
const messageId = await createMessage(c.req.valid('param').runId, c.req.valid('json').message);
return c.json({
messageId,
});
}
)
.post(
'/runs/:runId/permissions/authorize',
describeRoute({
summary: 'Authorize permission',
description: 'Authorize a permission',
responses: {
200: {
description: 'Permission authorized',
content: {
'application/json': {
schema: resolver(z.object({
success: z.literal(true),
})),
},
}
},
},
}),
validator('param', z.object({
runId: z.string(),
})),
validator('json', ToolPermissionAuthorizePayload),
async (c) => {
const response = await authorizePermission(
c.req.valid('param').runId,
c.req.valid('json')
);
return c.json({
success: true,
});
}
)
.post(
'/runs/:runId/human-input-requests/:requestId/reply',
describeRoute({
summary: 'Reply to human input request',
description: 'Reply to a human input request',
responses: {
200: {
description: 'Human input request replied',
},
},
}),
validator('param', z.object({
runId: z.string(),
})),
validator('json', AskHumanResponsePayload),
async (c) => {
const response = await replyToHumanInputRequest(
c.req.valid('param').runId,
c.req.valid('json')
);
return c.json({
success: true,
});
}
)
.post(
'/runs/:runId/stop',
describeRoute({
summary: 'Stop run',
description: 'Stop a run',
responses: {
200: {
description: 'Run stopped',
},
},
}),
validator('param', z.object({
runId: z.string(),
})),
async (c) => {
const response = await stop(c.req.valid('param').runId);
return c.json({
success: true,
});
}
)
.get(
'/stream',
2025-12-08 12:33:49 +05:30
describeRoute({
summary: 'Subscribe to run events',
description: 'Subscribe to run events',
}),
2025-12-02 13:24:58 +05:30
async (c) => {
return streamSSE(c, async (stream) => {
const bus = container.resolve<IBus>('bus');
let id = 0;
let unsub: (() => void) | null = null;
let aborted = false;
stream.onAbort(() => {
aborted = true;
if (unsub) {
unsub();
}
});
// Subscribe to your bus
unsub = await bus.subscribe('*', async (event) => {
if (aborted) return;
await stream.writeSSE({
data: JSON.stringify(event),
event: "message",
id: String(id++),
});
});
// Keep the function alive until the client disconnects
while (!aborted) {
await stream.sleep(1000); // any interval is fine
}
});
}
)
;
const app = new Hono()
2025-12-17 11:57:45 +05:30
.use("/*", cors())
2025-12-02 13:24:58 +05:30
.route("/", routes)
.get(
"/openapi.json",
openAPIRouteHandler(routes, {
documentation: {
info: {
title: "Hono",
version: "1.0.0",
description: "RowboatX API",
},
},
}),
);
// export default app;
serve({
fetch: app.fetch,
port: Number(process.env.PORT) || 3000,
});
// GET /skills
// POST /skills/new
// GET /skills/<id>
// PUT /skills/<id>
// DELETE /skills/<id>
2025-12-16 14:48:04 +05:30
// GET /sse