|
| 1 | +import {Database} from "@db/sqlite" |
| 2 | +import {join} from "jsr:@std/path" |
| 3 | +import {ensureDir} from "jsr:@std/fs" |
| 4 | +import {homedir} from "node:os" |
| 5 | +import {format} from "jsr:@std/datetime" |
| 6 | +import {log} from "../../config/logging.ts" |
| 7 | + |
| 8 | +export type PromptEntry = { |
| 9 | + id: number |
| 10 | + timestamp: string |
| 11 | + mode: string |
| 12 | + prompt: string |
| 13 | + result: string |
| 14 | + tokens_used: number |
| 15 | + cost: number |
| 16 | +} |
| 17 | + |
| 18 | +export class PromptDatabase { |
| 19 | + private db: Database |
| 20 | + |
| 21 | + constructor() { |
| 22 | + try { |
| 23 | + const dbPath = join(homedir(), ".ComputerUseAgent", "data") |
| 24 | + // Ensure directory exists with recursive creation |
| 25 | + ensureDir(dbPath) |
| 26 | + |
| 27 | + const dbFile = join(dbPath, "history.db") |
| 28 | + this.db = new Database(dbFile) |
| 29 | + this.initialize() |
| 30 | + } catch (error) { |
| 31 | + log.error(`Failed to initialize database: ${error}`) |
| 32 | + throw new Error(`Database initialization failed: ${error}`) |
| 33 | + } |
| 34 | + } |
| 35 | + |
| 36 | + private initialize() { |
| 37 | + this.db.exec(` |
| 38 | + CREATE TABLE IF NOT EXISTS prompts ( |
| 39 | + id INTEGER PRIMARY KEY AUTOINCREMENT, |
| 40 | + timestamp TEXT NOT NULL, |
| 41 | + mode TEXT NOT NULL, |
| 42 | + prompt TEXT NOT NULL, |
| 43 | + result TEXT, |
| 44 | + tokens_used INTEGER, |
| 45 | + cost REAL |
| 46 | + ) |
| 47 | + `) |
| 48 | + } |
| 49 | + |
| 50 | + async savePrompt(data: { |
| 51 | + mode: string |
| 52 | + prompt: string |
| 53 | + result: string |
| 54 | + tokens_used: number |
| 55 | + cost: number |
| 56 | + }) { |
| 57 | + |
| 58 | + const timestamp = format(new Date(), "yyyy-MM-dd HH:mm:ss") |
| 59 | + const number = await this.db.exec( |
| 60 | + `INSERT INTO prompts (timestamp, mode, prompt, result, tokens_used, cost) |
| 61 | + VALUES (:timestamp, :mode, :prompt, :result, :tokens, :cost)`, |
| 62 | + { |
| 63 | + timestamp, |
| 64 | + mode: data.mode, |
| 65 | + prompt: data.prompt, |
| 66 | + result: data.result, |
| 67 | + tokens: data.tokens_used, |
| 68 | + cost: data.cost |
| 69 | + } |
| 70 | + ) |
| 71 | + log.debug({number}) |
| 72 | + return number |
| 73 | + } |
| 74 | + |
| 75 | + listPrompts(limit = 10) { |
| 76 | + const stmt = this.db.prepare( |
| 77 | + `SELECT * FROM prompts ORDER BY timestamp DESC LIMIT ?` |
| 78 | + ) |
| 79 | + const results = stmt.all(limit) |
| 80 | + return results.map((row: any) => ({ |
| 81 | + id: row.id, |
| 82 | + timestamp: row.timestamp, |
| 83 | + mode: row.mode, |
| 84 | + prompt: row.prompt, |
| 85 | + result: row.result, |
| 86 | + tokens_used: row.tokens_used, |
| 87 | + cost: row.cost |
| 88 | + })) |
| 89 | + } |
| 90 | + |
| 91 | + getPromptById(id: number): PromptEntry | null { |
| 92 | + const stmt = this.db.prepare( |
| 93 | + `SELECT * FROM prompts WHERE id = ?` |
| 94 | + ) |
| 95 | + const results = stmt.all(id) |
| 96 | + return results.length === 0 ? null : { |
| 97 | + id: results[0].id, |
| 98 | + timestamp: results[0].timestamp, |
| 99 | + mode: results[0].mode, |
| 100 | + prompt: results[0].prompt, |
| 101 | + result: results[0].result, |
| 102 | + tokens_used: results[0].tokens_used, |
| 103 | + cost: results[0].cost |
| 104 | + } |
| 105 | + } |
| 106 | +} |
0 commit comments