Skip to content

Commit cc48b9e

Browse files
KD-K2N2claude
andcommitted
Fix daemon startup, migrations, and project auto-creation
- Add start.ts entry point for daemon (creates DB, runs migrations, starts server) - Copy migration SQL files to dist/ during build - Install pino-pretty for structured logging - Auto-create project on first memory save (fixes FK constraint) - Fix SSE type: add sync.offline event to union - Fix quality gate: proper type casting for DB results Tested end-to-end: init → daemon start → create memory → search → doctor ✅ Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
1 parent 11215cb commit cc48b9e

6 files changed

Lines changed: 2206 additions & 14 deletions

File tree

packages/server/package.json

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,8 @@
4444
"node": ">=18.0.0"
4545
},
4646
"scripts": {
47-
"build": "tsup src/index.ts --format esm --dts --clean --external better-sqlite3",
47+
"build": "tsup src/index.ts --format esm --dts --clean --external better-sqlite3 && tsup src/start.ts --format esm --external better-sqlite3 --no-dts && mkdir -p dist/migrations && cp src/db/migrations/*.sql dist/migrations/",
48+
"start": "node dist/start.js",
4849
"dev": "tsup src/index.ts --format esm --dts --watch --external better-sqlite3",
4950
"test": "vitest run",
5051
"test:coverage": "vitest run --coverage",
@@ -62,6 +63,7 @@
6263
"better-sqlite3": "^11.0.0",
6364
"fastify": "^5.8.2",
6465
"openai": "^6.32.0",
66+
"pino-pretty": "^13.1.3",
6567
"uuid": "^10.0.0"
6668
},
6769
"devDependencies": {

packages/server/src/api/routes/memories.ts

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -95,6 +95,16 @@ export function registerMemoryRoutes(
9595
});
9696
}
9797

98+
// Auto-create project if it doesn't exist
99+
const existingProject = db.prepare('SELECT id FROM projects WHERE id = ?').get(project_id);
100+
if (!existingProject) {
101+
const { v4: projUuid } = await import('uuid');
102+
db.prepare(
103+
`INSERT OR IGNORE INTO projects (id, name, path, git_remote, tech_stack, context_budget, memory_limit, created_at, last_session_at)
104+
VALUES (?, ?, NULL, NULL, '[]', 4000, 500, strftime('%Y-%m-%dT%H:%M:%fZ', 'now'), strftime('%Y-%m-%dT%H:%M:%fZ', 'now'))`,
105+
).run(project_id, project_id);
106+
}
107+
98108
// Quality gate
99109
const gateResult = runQualityGate(parsed.data, db, project_id);
100110
if (!gateResult.passed) {

packages/server/src/quality/gate.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -100,10 +100,10 @@ export function runQualityGate(
100100
.prepare(
101101
'SELECT content FROM memories WHERE project_id = ? AND deleted_at IS NULL AND superseded_by IS NULL',
102102
)
103-
.all(projectId)
104-
.map((r: { content: string }) => r.content);
103+
.all(projectId) as { content: string }[];
104+
const existingTexts = existingContents.map((r) => r.content);
105105

106-
const duplicateResult = checkDuplicate(input.content, existingContents);
106+
const duplicateResult = checkDuplicate(input.content, existingTexts);
107107
if (!duplicateResult.passed) {
108108
return {
109109
passed: false,

packages/server/src/start.ts

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
#!/usr/bin/env node
2+
/**
3+
* Cortex daemon entry point.
4+
* Creates database, runs migrations, starts API server + MCP server.
5+
*/
6+
7+
import { createDatabase, runMigrations } from './db/index.js';
8+
import { startAPIServer } from './api/server.js';
9+
import { SSEEmitter } from './api/sse/emitter.js';
10+
import { startMCPServer } from './mcp/server.js';
11+
import os from 'node:os';
12+
import path from 'node:path';
13+
import fs from 'node:fs';
14+
15+
const PORT = parseInt(process.env.CORTEX_PORT || '7434', 10);
16+
const DB_PATH = process.env.CORTEX_DB_PATH || path.join(os.homedir(), '.cortex', 'memory.db');
17+
const STDIO_MODE = process.argv.includes('--stdio');
18+
19+
// Ensure data directory exists
20+
const dataDir = path.dirname(DB_PATH);
21+
if (!fs.existsSync(dataDir)) {
22+
fs.mkdirSync(dataDir, { recursive: true });
23+
}
24+
25+
// Create database and run migrations
26+
const db = createDatabase(DB_PATH);
27+
runMigrations(db);
28+
29+
// Create SSE emitter
30+
const sseEmitter = new SSEEmitter();
31+
32+
if (STDIO_MODE) {
33+
// MCP mode — Claude Code connects via stdio
34+
startMCPServer(db).catch((err) => {
35+
console.error('[cortex] MCP server failed:', err);
36+
process.exit(1);
37+
});
38+
} else {
39+
// Daemon mode — REST API server
40+
startAPIServer(db, sseEmitter, PORT)
41+
.then((address) => {
42+
console.log(`[cortex] Daemon running at ${address}`);
43+
})
44+
.catch((err) => {
45+
console.error('[cortex] Failed to start:', err);
46+
process.exit(1);
47+
});
48+
}

packages/shared/src/types/sse.ts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -102,6 +102,11 @@ export interface SSESyncError {
102102
data: { code: string; message: string };
103103
}
104104

105+
export interface SSESyncOffline {
106+
type: 'sync.offline';
107+
data: { offlineCount: number; latency_ms: number };
108+
}
109+
105110
export interface SSEDaemonError {
106111
type: 'daemon.error';
107112
data: { code: string; message: string };
@@ -122,6 +127,7 @@ export type SSEEvent =
122127
| SSERateLimitWarning
123128
| SSEReviewReminder
124129
| SSESyncError
130+
| SSESyncOffline
125131
| SSEDaemonError;
126132

127133
/**

0 commit comments

Comments
 (0)