// Package db manages the SQLite connection and schema migrations. package db import ( "database/sql" "fmt" "os" "path/filepath" _ "modernc.org/sqlite" // pure-Go SQLite driver, no CGo required ) // schema creates all tables if they do not already exist. const schema = ` CREATE TABLE IF NOT EXISTS instructions ( id TEXT PRIMARY KEY, content TEXT NOT NULL, status TEXT NOT NULL DEFAULT 'pending', created_at TEXT NOT NULL, updated_at TEXT NOT NULL, consumed_at TEXT, consumed_by_agent_id TEXT, position INTEGER NOT NULL DEFAULT 0 ); CREATE INDEX IF NOT EXISTS idx_instructions_status ON instructions(status); CREATE INDEX IF NOT EXISTS idx_instructions_position ON instructions(position); CREATE TABLE IF NOT EXISTS settings ( key TEXT PRIMARY KEY, value TEXT NOT NULL ); CREATE TABLE IF NOT EXISTS agent_activity ( agent_id TEXT PRIMARY KEY, last_seen_at TEXT NOT NULL, last_fetch_at TEXT NOT NULL, last_result_type TEXT NOT NULL ); ` // defaultSettings seeds initial values; OR IGNORE means existing rows are unchanged. const defaultSettings = "" + "INSERT OR IGNORE INTO settings (key, value) VALUES ('default_wait_seconds', '10');\n" + "DELETE FROM settings WHERE key = 'default_empty_response';\n" + "DELETE FROM settings WHERE key = 'agent_stale_after_seconds';\n" // Open opens (creating if necessary) a SQLite database at dbPath, applies the // schema, and seeds default settings. func Open(dbPath string) (*sql.DB, error) { dir := filepath.Dir(dbPath) if err := os.MkdirAll(dir, 0o755); err != nil { return nil, fmt.Errorf("create db directory: %w", err) } db, err := sql.Open("sqlite", dbPath+"?_pragma=journal_mode(WAL)&_pragma=foreign_keys(on)&_pragma=busy_timeout(5000)") if err != nil { return nil, fmt.Errorf("open database: %w", err) } // Serialise all writes through a single connection to avoid locking. db.SetMaxOpenConns(1) if _, err := db.Exec(schema); err != nil { _ = db.Close() return nil, fmt.Errorf("apply schema: %w", err) } if _, err := db.Exec(defaultSettings); err != nil { _ = db.Close() return nil, fmt.Errorf("seed settings: %w", err) } return db, nil }