v2.0.0-alpha: BooCoder foundation — container, schema, DB rename
Phase 1 of v2.0. BooCoder is live at port 9502 with a health endpoint. - Database renamed: ALTER DATABASE boocode RENAME TO boochat (one-time). All services updated to connect to /boochat. Docker service name stays boocode_db (rename is internal to Postgres, not Docker). - New apps/coder/ app skeleton: Fastify server with health endpoint, postgres connection, schema apply on boot. Mirrors apps/server pattern but minimal (no inference loop yet — Phase 2). - Schema: pending_changes (operation queue before /apply), tasks (dispatch DAG with state machine), available_agents (startup-probed agent registry), human_inbox view (tasks WHERE state IN blocked/failed). All IF NOT EXISTS, idempotent on re-run. Same boochat database, different tables. - Dockerfile: Node 20 bookworm-slim (glibc for future node-pty in Phase 5). Multi-stage build matching the existing boocode image pattern. - docker-compose.yml: boocoder service on 100.114.205.53:9502, /opt:/opt:rw mount (write-capable, policy-gated at tool layer), depends on boocode_db. - BOOCODER.md: container guidance declaring write-tool capability + pending-changes discipline. All 4 services boot and pass health checks. 9 tables in the shared DB. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
55
apps/coder/src/index.ts
Normal file
55
apps/coder/src/index.ts
Normal file
@@ -0,0 +1,55 @@
|
||||
import Fastify from 'fastify';
|
||||
import { loadConfig } from './config.js';
|
||||
import { getSql, applySchema, pingDb, closeDb } from './db.js';
|
||||
|
||||
async function main() {
|
||||
const config = loadConfig();
|
||||
|
||||
const app = Fastify({
|
||||
logger: { level: config.LOG_LEVEL },
|
||||
});
|
||||
|
||||
// Allow empty JSON bodies (same pattern as apps/server).
|
||||
app.removeContentTypeParser(['application/json']);
|
||||
app.addContentTypeParser('application/json', { parseAs: 'string' }, (_req, body, done) => {
|
||||
const str = (body as string) ?? '';
|
||||
if (str.trim().length === 0) {
|
||||
done(null, {});
|
||||
return;
|
||||
}
|
||||
try {
|
||||
done(null, JSON.parse(str));
|
||||
} catch (err) {
|
||||
done(err as Error, undefined);
|
||||
}
|
||||
});
|
||||
|
||||
const sql = getSql(config);
|
||||
await applySchema(sql);
|
||||
app.log.info('database schema applied');
|
||||
|
||||
// Health endpoint
|
||||
app.get('/api/health', async (_req, reply) => {
|
||||
const dbOk = await pingDb(sql);
|
||||
const status = dbOk ? 200 : 503;
|
||||
return reply.status(status).send({ ok: dbOk, db: dbOk });
|
||||
});
|
||||
|
||||
// Graceful shutdown
|
||||
const shutdown = async () => {
|
||||
app.log.info('shutting down');
|
||||
await app.close();
|
||||
await closeDb();
|
||||
process.exit(0);
|
||||
};
|
||||
process.on('SIGTERM', shutdown);
|
||||
process.on('SIGINT', shutdown);
|
||||
|
||||
await app.listen({ port: config.PORT, host: config.HOST });
|
||||
app.log.info(`BooCoder listening on ${config.HOST}:${config.PORT}`);
|
||||
}
|
||||
|
||||
main().catch((err) => {
|
||||
console.error('fatal:', err);
|
||||
process.exit(1);
|
||||
});
|
||||
Reference in New Issue
Block a user