diff --git a/apps/server/agent/accounts/setup.ts b/apps/server/agent/accounts/setup.ts index 5bf0c6c..576e742 100644 --- a/apps/server/agent/accounts/setup.ts +++ b/apps/server/agent/accounts/setup.ts @@ -1,15 +1,15 @@ -import { mkdirSync, writeFileSync } from 'node:fs'; +import { mkdir, writeFile } from 'node:fs/promises'; import { join } from 'node:path'; -export function setupAccountConfigDir( +export async function setupAccountConfigDir( configDir: string, extracted: { configJson: object; credentials: string }, -): void { - mkdirSync(configDir, { recursive: true }); +): Promise { + await mkdir(configDir, { recursive: true }); // Write .claude.json - writeFileSync(join(configDir, '.claude.json'), JSON.stringify(extracted.configJson, null, 2)); + await writeFile(join(configDir, '.claude.json'), JSON.stringify(extracted.configJson, null, 2)); // Write .credentials.json - writeFileSync(join(configDir, '.credentials.json'), extracted.credentials); + await writeFile(join(configDir, '.credentials.json'), extracted.credentials); } diff --git a/apps/server/agent/credential-handler.ts b/apps/server/agent/credential-handler.ts index 9a80854..653d4ac 100644 --- a/apps/server/agent/credential-handler.ts +++ b/apps/server/agent/credential-handler.ts @@ -6,7 +6,8 @@ * ensuring they're fresh, and marking accounts as exhausted on failure. */ -import { readFileSync, existsSync } from 'node:fs'; +import { readFile } from 'node:fs/promises'; +import { existsSync } from 'node:fs'; import { join } from 'node:path'; import type { AccountRepository } from '../db/repositories/account-repository.js'; import type { AccountCredentialManager } from './credentials/types.js'; @@ -50,9 +51,9 @@ export class CredentialHandler { * Write account credentials from DB to the convention-based config directory. * Must be called before ensureCredentials so the files exist on disk. */ - writeCredentialsToDisk(account: Account, configDir: string): void { + async writeCredentialsToDisk(account: Account, configDir: string): Promise { if (account.configJson && account.credentials) { - setupAccountConfigDir(configDir, { + await setupAccountConfigDir(configDir, { configJson: JSON.parse(account.configJson), credentials: account.credentials, }); @@ -70,7 +71,7 @@ export class CredentialHandler { if (!this.accountRepository) return; try { const credPath = join(configDir, '.credentials.json'); - const credentials = readFileSync(credPath, 'utf-8'); + const credentials = await readFile(credPath, 'utf-8'); await this.accountRepository.updateCredentials(accountId, credentials); log.debug({ accountId }, 'persisted refreshed credentials back to DB'); } catch (err) { @@ -97,11 +98,11 @@ export class CredentialHandler { * Returns null if credentials file is missing or malformed. * Used for CLAUDE_CODE_OAUTH_TOKEN env var injection. */ - readAccessToken(configDir: string): string | null { + async readAccessToken(configDir: string): Promise { try { const credPath = join(configDir, '.credentials.json'); if (!existsSync(credPath)) return null; - const raw = readFileSync(credPath, 'utf-8'); + const raw = await readFile(credPath, 'utf-8'); const parsed = JSON.parse(raw); return parsed.claudeAiOauth?.accessToken ?? null; } catch { @@ -126,7 +127,7 @@ export class CredentialHandler { accountConfigDir = getAccountConfigDir(this.workspaceRoot, accountId); const account = await this.accountRepository.findById(accountId); if (account) { - this.writeCredentialsToDisk(account, accountConfigDir); + await this.writeCredentialsToDisk(account, accountConfigDir); } processEnv[provider.configDirEnv] = accountConfigDir; @@ -138,7 +139,7 @@ export class CredentialHandler { await this.persistRefreshedCredentials(accountId, accountConfigDir); } - const accessToken = this.readAccessToken(accountConfigDir); + const accessToken = await this.readAccessToken(accountConfigDir); if (accessToken) { processEnv['CLAUDE_CODE_OAUTH_TOKEN'] = accessToken; log.debug({ accountId }, 'CLAUDE_CODE_OAUTH_TOKEN injected'); @@ -191,7 +192,7 @@ export class CredentialHandler { // Write credentials and ensure they're fresh const nextConfigDir = getAccountConfigDir(this.workspaceRoot, nextAccount.id); - this.writeCredentialsToDisk(nextAccount, nextConfigDir); + await this.writeCredentialsToDisk(nextAccount, nextConfigDir); const { valid, refreshed } = await this.ensureCredentials(nextConfigDir, nextAccount.id); if (!valid) { log.warn({ newAccountId: nextAccount.id }, 'failed to refresh failover account credentials'); diff --git a/apps/server/agent/manager.ts b/apps/server/agent/manager.ts index 85a52fc..6ca303f 100644 --- a/apps/server/agent/manager.ts +++ b/apps/server/agent/manager.ts @@ -212,7 +212,7 @@ export class MultiProviderAgentManager implements AgentManager { accountId = accountResult.accountId; accountConfigDir = accountResult.configDir; - this.credentialHandler.writeCredentialsToDisk(accountResult.account, accountConfigDir); + await this.credentialHandler.writeCredentialsToDisk(accountResult.account, accountConfigDir); const { valid, refreshed } = await this.credentialHandler.ensureCredentials(accountConfigDir, accountId); if (!valid) { log.warn({ alias, accountId }, 'failed to refresh account credentials, proceeding anyway');