summaryrefslogtreecommitdiffhomepage
path: root/packages/core/src/credentials/api-keys.ts
diff options
context:
space:
mode:
authorAdam Malczewski <[email protected]>2026-05-21 17:30:08 +0900
committerAdam Malczewski <[email protected]>2026-05-21 17:30:08 +0900
commitd6b208342edf97bafa5b1dcc986b782f9879d141 (patch)
treec6d8f9bffa86f78e3b1369b811bef9642df788d0 /packages/core/src/credentials/api-keys.ts
parent1f309ccca20aabbd0ee3fb8fbb3c8192124edd95 (diff)
downloaddispatch-d6b208342edf97bafa5b1dcc986b782f9879d141.tar.gz
dispatch-d6b208342edf97bafa5b1dcc986b782f9879d141.zip
feat: SQLite database for all credentials, keys, wake schedule, and usage cache
- Add SQLite database at ~/.local/share/dispatch/dispatch.db with tables: credentials, api_keys, wake_schedule, usage_cache - Store Claude OAuth credentials in DB with import button in Model Status UI - Store OpenCode/Copilot API keys in DB with paste-to-import modal - Store OpenCode cookie and workspace IDs in DB - Migrate wake schedule from .wake-schedule.json to DB - Migrate usage cache from in-memory Map + localStorage to DB - Remove all env var and file fallbacks — DB is the single source of truth - Add seed scripts: bin/import-credentials.ts, bin/seed-opencode-keys.ts - Docker: container runs as host UID/GID with matching home directory - Clean up dispatch.toml: remove env fields, update comments - Progress bar time markers for usage cycle tracking
Diffstat (limited to 'packages/core/src/credentials/api-keys.ts')
-rw-r--r--packages/core/src/credentials/api-keys.ts71
1 files changed, 71 insertions, 0 deletions
diff --git a/packages/core/src/credentials/api-keys.ts b/packages/core/src/credentials/api-keys.ts
new file mode 100644
index 0000000..5f92ffa
--- /dev/null
+++ b/packages/core/src/credentials/api-keys.ts
@@ -0,0 +1,71 @@
+import { getDatabase } from "../db/index.js";
+
+export interface StoredApiKey {
+ keyId: string;
+ provider: string;
+ apiKey: string;
+ importedAt: number;
+ updatedAt: number;
+}
+
+/**
+ * Store or update an API key in the database.
+ */
+export function setApiKey(keyId: string, provider: string, apiKey: string): void {
+ const db = getDatabase();
+ const now = Date.now();
+ db.query(
+ `INSERT INTO api_keys (key_id, provider, api_key, imported_at, updated_at)
+ VALUES ($keyId, $provider, $apiKey, $now, $now)
+ ON CONFLICT(key_id) DO UPDATE SET
+ api_key = $apiKey,
+ updated_at = $now`,
+ ).run({
+ $keyId: keyId,
+ $provider: provider,
+ $apiKey: apiKey,
+ $now: now,
+ });
+}
+
+/**
+ * Get a stored API key by key ID. Returns the key string or null.
+ */
+export function getApiKey(keyId: string): string | null {
+ const db = getDatabase();
+ const row = db.query(
+ "SELECT api_key FROM api_keys WHERE key_id = $keyId",
+ ).get({ $keyId: keyId }) as { api_key: string } | null;
+ return row?.api_key ?? null;
+}
+
+/**
+ * Resolve an API key from the database. Returns null if not found.
+ */
+export function resolveApiKey(keyId: string): string | null {
+ return getApiKey(keyId);
+}
+
+/**
+ * Delete a stored API key.
+ */
+export function deleteApiKey(keyId: string): void {
+ const db = getDatabase();
+ db.query("DELETE FROM api_keys WHERE key_id = $keyId").run({ $keyId: keyId });
+}
+
+/**
+ * List all stored API keys with metadata (key value excluded for security).
+ */
+export function listApiKeys(): Array<{ keyId: string; provider: string; importedAt: number; updatedAt: number }> {
+ const db = getDatabase();
+ const rows = db.query(
+ "SELECT key_id, provider, imported_at, updated_at FROM api_keys ORDER BY key_id",
+ ).all() as Array<Record<string, unknown>>;
+ return rows.map((row) => ({
+ keyId: row.key_id as string,
+ provider: row.provider as string,
+ importedAt: row.imported_at as number,
+ updatedAt: row.updated_at as number,
+ }));
+}