fix(accounts): integrate CLIProxy OAuth accounts into API endpoint

- Add CLIProxy accounts to GET /api/accounts (was only showing legacy/unified)
- Use unique ID for key to prevent collision between accounts with same nickname
- Skip accounts with missing identifier
- Route set-default to CLIProxy account manager for provider:id format
- Route delete to CLIProxy account manager for provider:id format
This commit is contained in:
kaitranntt
2026-01-02 19:51:50 -05:00
parent c05f27fdb3
commit eebcb7b103
+78 -3
View File
@@ -8,10 +8,29 @@
import { Router, Request, Response } from 'express';
import ProfileRegistry from '../../auth/profile-registry';
import { isUnifiedMode } from '../../config/unified-config-loader';
import {
getAllAccountsSummary,
setDefaultAccount as setCliproxyDefault,
removeAccount as removeCliproxyAccount,
} from '../../cliproxy/account-manager';
import { CLIProxyProvider } from '../../cliproxy/types';
const router = Router();
const registry = new ProfileRegistry();
/** Parse CLIProxy account key format: "provider:accountId" */
function parseCliproxyKey(key: string): { provider: CLIProxyProvider; accountId: string } | null {
const providers: CLIProxyProvider[] = ['gemini', 'codex', 'agy', 'qwen', 'iflow', 'kiro', 'ghcp'];
const colonIndex = key.indexOf(':');
if (colonIndex === -1) return null;
const provider = key.slice(0, colonIndex) as CLIProxyProvider;
const accountId = key.slice(colonIndex + 1);
if (!providers.includes(provider) || !accountId) return null;
return { provider, accountId };
}
/**
* GET /api/accounts - List accounts from both profiles.json and config.yaml
*/
@@ -21,8 +40,20 @@ router.get('/', (_req: Request, res: Response): void => {
const legacyProfiles = registry.getAllProfiles();
const unifiedAccounts = registry.getAllAccountsUnified();
// Get CLIProxy OAuth accounts (gemini, codex, agy, etc.)
const cliproxyAccounts = getAllAccountsSummary();
// Merge profiles: unified config takes precedence
const merged: Record<string, { type: string; created: string; last_used: string | null }> = {};
const merged: Record<
string,
{
type: string;
created: string;
last_used: string | null;
provider?: string;
displayName?: string;
}
> = {};
// Add legacy profiles first
for (const [name, meta] of Object.entries(legacyProfiles)) {
@@ -42,6 +73,26 @@ router.get('/', (_req: Request, res: Response): void => {
};
}
// Add CLIProxy OAuth accounts
for (const [provider, accounts] of Object.entries(cliproxyAccounts)) {
for (const acct of accounts) {
// Skip accounts with no valid identifier
if (!acct.id) {
continue;
}
// Use unique ID for key to prevent collisions between accounts with same nickname/email
const displayName = acct.nickname || acct.email || acct.id;
const key = `${provider}:${acct.id}`;
merged[key] = {
type: 'cliproxy',
provider,
displayName,
created: acct.createdAt || new Date().toISOString(),
last_used: null,
};
}
}
// Convert to array format
const accounts = Object.entries(merged).map(([name, meta]) => ({
name,
@@ -69,6 +120,18 @@ router.post('/default', (req: Request, res: Response): void => {
return;
}
// Check if this is a CLIProxy account (format: "provider:accountId")
const cliproxyKey = parseCliproxyKey(name);
if (cliproxyKey) {
const success = setCliproxyDefault(cliproxyKey.provider, cliproxyKey.accountId);
if (!success) {
res.status(404).json({ error: `CLIProxy account not found: ${name}` });
return;
}
res.json({ default: name });
return;
}
// Use unified config if in unified mode, otherwise use legacy
if (isUnifiedMode()) {
registry.setDefaultUnified(name);
@@ -110,7 +173,7 @@ router.delete('/:name', (req: Request, res: Response): void => {
return;
}
// Check if trying to delete default
// Check if trying to delete default (for non-CLIProxy accounts)
const currentDefault = registry.getDefaultUnified() ?? registry.getDefaultProfile();
if (name === currentDefault) {
res
@@ -119,7 +182,19 @@ router.delete('/:name', (req: Request, res: Response): void => {
return;
}
// Delete the profile
// Check if this is a CLIProxy account (format: "provider:accountId")
const cliproxyKey = parseCliproxyKey(name);
if (cliproxyKey) {
const success = removeCliproxyAccount(cliproxyKey.provider, cliproxyKey.accountId);
if (!success) {
res.status(404).json({ error: `CLIProxy account not found: ${name}` });
return;
}
res.json({ success: true, deleted: name });
return;
}
// Delete the profile (legacy/unified)
registry.deleteProfile(name);
res.json({ success: true, deleted: name });