summaryrefslogtreecommitdiffhomepage
path: root/packages/api/src/agent-manager.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/api/src/agent-manager.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/api/src/agent-manager.ts')
-rw-r--r--packages/api/src/agent-manager.ts29
1 files changed, 14 insertions, 15 deletions
diff --git a/packages/api/src/agent-manager.ts b/packages/api/src/agent-manager.ts
index 4f45781..92396b6 100644
--- a/packages/api/src/agent-manager.ts
+++ b/packages/api/src/agent-manager.ts
@@ -20,9 +20,10 @@ import {
TaskList,
createTaskListTool,
type ClaudeAccount,
- discoverClaudeAccounts,
+ getClaudeAccountsFromDB,
refreshAccountCredentials,
refreshAccountCredentialsAsync,
+ resolveApiKey,
} from "@dispatch/core";
import type { PermissionManager } from "./permission-manager.js";
import { setConfigGetter } from "./routes/config.js";
@@ -124,7 +125,7 @@ export class AgentManager {
private _refreshClaudeAccounts(): void {
try {
- this.claudeAccounts = discoverClaudeAccounts();
+ this.claudeAccounts = getClaudeAccountsFromDB();
if (this.claudeAccounts.length > 0) {
console.log(`dispatch: discovered ${this.claudeAccounts.length} Claude account(s)`);
}
@@ -187,8 +188,8 @@ export class AgentManager {
const ruleset = configToRuleset(this.config);
// Try to resolve model from registry, fall back to env vars
- let apiKey = process.env.OPENCODE_API_KEY ?? "";
- let model = process.env.DISPATCH_MODEL ?? "deepseek-v4-flash";
+ let apiKey = "";
+ let model = "deepseek-v4-flash";
let baseURL = "https://opencode.ai/zen/go/v1";
let provider: string | undefined;
let claudeCredentials: { accessToken: string } | undefined;
@@ -203,9 +204,8 @@ export class AgentManager {
if (key.provider === "anthropic") {
// Anthropic provider: resolve credentials from Claude accounts
const credFile = key.credentials_file;
- const account = credFile
- ? this.claudeAccounts.find((a) => a.source === credFile)
- : this.claudeAccounts[0];
+ const account = this.claudeAccounts.find((a) => a.id === effectiveKeyId)
+ ?? (credFile ? this.claudeAccounts.find((a) => a.source === credFile) : this.claudeAccounts[0]);
if (account) {
const creds = refreshAccountCredentials(account);
if (creds && creds.expiresAt > Date.now() + 60_000) {
@@ -247,7 +247,7 @@ export class AgentManager {
}
} else {
// Standard key: resolve from env var
- const envKey = key.env ? process.env[key.env] : undefined;
+ const envKey = resolveApiKey(key.id);
if (envKey) {
apiKey = envKey;
baseURL = key.base_url;
@@ -284,9 +284,8 @@ export class AgentManager {
// Check if resolved key is anthropic
if (resolved.key.provider === "anthropic") {
const credFile = resolved.key.credentials_file;
- const account = credFile
- ? this.claudeAccounts.find((a) => a.source === credFile)
- : this.claudeAccounts[0];
+ const account = this.claudeAccounts.find((a) => a.id === resolved.key.id)
+ ?? (credFile ? this.claudeAccounts.find((a) => a.source === credFile) : this.claudeAccounts[0]);
if (account) {
let creds = refreshAccountCredentials(account);
if (!creds || creds.expiresAt <= Date.now() + 60_000) {
@@ -304,14 +303,14 @@ export class AgentManager {
console.warn(`dispatch: no Claude credentials found for key "${resolved.key.id}"`);
}
} else {
- const envKey = process.env[resolved.key.env!];
+ const envKey = resolveApiKey(resolved.key.id);
if (envKey) {
apiKey = envKey;
} else {
- console.warn(`dispatch: env var "${resolved.key.env}" not set for key "${resolved.key.id}", falling back to env vars`);
- model = process.env.DISPATCH_MODEL ?? "deepseek-v4-flash";
+ console.warn(`dispatch: env var not set for key "${resolved.key.id}", falling back to defaults`);
+ model = "deepseek-v4-flash";
baseURL = "https://opencode.ai/zen/go/v1";
- apiKey = process.env.OPENCODE_API_KEY ?? "";
+ apiKey = "";
}
}
} else {