From 19d67cfda92589ebfcdc83784c2d8d66cb13ad8d Mon Sep 17 00:00:00 2001 From: AskaEth Date: Wed, 12 Aug 2026 20:45:25 +0800 Subject: [PATCH] feat: add daemon server skeleton with health check endpoint --- packages/daemon/src/__tests__/health.test.ts | 23 ++++++++++++++++++ packages/daemon/src/routes/health.ts | 11 +++++++++ packages/daemon/src/server.ts | 25 ++++++++++++++++++++ 3 files changed, 59 insertions(+) create mode 100644 packages/daemon/src/__tests__/health.test.ts create mode 100644 packages/daemon/src/routes/health.ts create mode 100644 packages/daemon/src/server.ts diff --git a/packages/daemon/src/__tests__/health.test.ts b/packages/daemon/src/__tests__/health.test.ts new file mode 100644 index 0000000..cb36f87 --- /dev/null +++ b/packages/daemon/src/__tests__/health.test.ts @@ -0,0 +1,23 @@ +import { describe, it, expect, beforeAll, afterAll } from 'vitest'; +import http from 'node:http'; +import { startServer } from '../server.js'; + +describe('daemon health endpoint', () => { + let server: http.Server; + const port = 19528; + + beforeAll(async () => { + server = await startServer(port); + }); + + afterAll(async () => { + await new Promise((resolve) => server.close(() => resolve())); + }); + + it('GET /health returns 200 with status ok', async () => { + const response = await fetch(`http://127.0.0.1:${port}/health`); + expect(response.status).toBe(200); + const json = await response.json(); + expect(json).toEqual({ status: 'ok' }); + }); +}); diff --git a/packages/daemon/src/routes/health.ts b/packages/daemon/src/routes/health.ts new file mode 100644 index 0000000..f8a14cd --- /dev/null +++ b/packages/daemon/src/routes/health.ts @@ -0,0 +1,11 @@ +import type { IncomingMessage, ServerResponse } from 'node:http'; + +export function healthRoute(req: IncomingMessage, res: ServerResponse): boolean { + const url = new URL(req.url || '/', 'http://localhost'); + if (req.method === 'GET' && url.pathname === '/health') { + res.writeHead(200, { 'Content-Type': 'application/json' }); + res.end(JSON.stringify({ status: 'ok' })); + return true; + } + return false; +} diff --git a/packages/daemon/src/server.ts b/packages/daemon/src/server.ts new file mode 100644 index 0000000..05cb832 --- /dev/null +++ b/packages/daemon/src/server.ts @@ -0,0 +1,25 @@ +import http from 'node:http'; +import { healthRoute } from './routes/health.js'; + +const routes = [healthRoute]; + +export function startServer(port: number): Promise { + return new Promise((resolve) => { + const server = http.createServer((req, res) => { + for (const route of routes) { + if (route(req, res)) return; + } + res.writeHead(404, { 'Content-Type': 'application/json' }); + res.end(JSON.stringify({ ok: false, error: { code: 'INTERNAL', message: 'not found' } })); + }); + + server.listen(port, '127.0.0.1', () => { + console.log(`[daemon] VisionL daemon started on http://127.0.0.1:${port}`); + resolve(server); + }); + }); +} + +// Direct start when running as script +const port = parseInt(process.env.VISIONL_PORT || '9527', 10); +startServer(port);