diff options
| author | Adam Malczewski <[email protected]> | 2026-05-21 17:30:08 +0900 |
|---|---|---|
| committer | Adam Malczewski <[email protected]> | 2026-05-21 17:30:08 +0900 |
| commit | d6b208342edf97bafa5b1dcc986b782f9879d141 (patch) | |
| tree | c6d8f9bffa86f78e3b1369b811bef9642df788d0 /packages/core/src/credentials/api-keys.ts | |
| parent | 1f309ccca20aabbd0ee3fb8fbb3c8192124edd95 (diff) | |
| download | dispatch-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.ts | 71 |
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, + })); +} |
