From 8d7845d67fb156671713888726d631415d0f4f9c Mon Sep 17 00:00:00 2001 From: kaitranntt Date: Thu, 25 Dec 2025 14:25:40 -0500 Subject: [PATCH] fix(dashboard): read accounts from unified config Dashboard accounts page was only reading from profiles.json, ignoring accounts stored in config.yaml when in unified mode. This caused accounts created via CLI to be invisible in the dashboard. - Use ProfileRegistry instead of direct file access - Merge legacy and unified accounts (unified takes precedence) - Set default via unified config when in unified mode Fixes #203 --- src/web-server/routes/account-routes.ts | 69 +++++++++++++++---------- 1 file changed, 43 insertions(+), 26 deletions(-) diff --git a/src/web-server/routes/account-routes.ts b/src/web-server/routes/account-routes.ts index 848383f5..6b512b3f 100644 --- a/src/web-server/routes/account-routes.ts +++ b/src/web-server/routes/account-routes.ts @@ -1,38 +1,57 @@ /** - * Account Routes - CRUD operations for Claude accounts (profiles.json) + * Account Routes - CRUD operations for Claude accounts * - * Separated from profile-routes.ts to avoid dual-mounting conflicts. + * Uses ProfileRegistry to read from both legacy (profiles.json) + * and unified config (config.yaml) for consistent data with CLI. */ import { Router, Request, Response } from 'express'; -import * as fs from 'fs'; -import * as path from 'path'; -import { getCcsDir } from '../../utils/config-manager'; +import ProfileRegistry from '../../auth/profile-registry'; +import { isUnifiedMode } from '../../config/unified-config-loader'; const router = Router(); +const registry = new ProfileRegistry(); /** - * GET /api/accounts - List accounts from profiles.json + * GET /api/accounts - List accounts from both profiles.json and config.yaml */ router.get('/', (_req: Request, res: Response): void => { try { - const profilesPath = path.join(getCcsDir(), 'profiles.json'); + // Get profiles from both legacy and unified config (same logic as CLI) + const legacyProfiles = registry.getAllProfiles(); + const unifiedAccounts = registry.getAllAccountsUnified(); - if (!fs.existsSync(profilesPath)) { - res.json({ accounts: [], default: null }); - return; + // Merge profiles: unified config takes precedence + const merged: Record = {}; + + // Add legacy profiles first + for (const [name, meta] of Object.entries(legacyProfiles)) { + merged[name] = { + type: meta.type || 'account', + created: meta.created, + last_used: meta.last_used || null, + }; } - const data = JSON.parse(fs.readFileSync(profilesPath, 'utf8')); - const accounts = Object.entries(data.profiles || {}).map(([name, meta]) => { - const metadata = meta as Record; - return { - name, - ...metadata, + // Override with unified config accounts (takes precedence) + for (const [name, account] of Object.entries(unifiedAccounts)) { + merged[name] = { + type: 'account', + created: account.created, + last_used: account.last_used, }; - }); + } - res.json({ accounts, default: data.default || null }); + // Convert to array format + const accounts = Object.entries(merged).map(([name, meta]) => ({ + name, + ...meta, + })); + + // Get default from unified config first, fallback to legacy + const defaultProfile = registry.getDefaultUnified() ?? registry.getDefaultProfile() ?? null; + + res.json({ accounts, default: defaultProfile }); } catch (error) { res.status(500).json({ error: (error as Error).message }); } @@ -50,14 +69,12 @@ router.post('/default', (req: Request, res: Response): void => { return; } - const profilesPath = path.join(getCcsDir(), 'profiles.json'); - - const data = fs.existsSync(profilesPath) - ? JSON.parse(fs.readFileSync(profilesPath, 'utf8')) - : { profiles: {} }; - - data.default = name; - fs.writeFileSync(profilesPath, JSON.stringify(data, null, 2) + '\n'); + // Use unified config if in unified mode, otherwise use legacy + if (isUnifiedMode()) { + registry.setDefaultUnified(name); + } else { + registry.setDefaultProfile(name); + } res.json({ default: name }); } catch (error) {