fix(profiles): support API profiles in auth check and model resolution (#608)

* fix(profiles): support API profiles in auth check and model resolution

- useClaudeTokenCheck() now checks for active API profile in addition
  to OAuth token, preventing unnecessary OAuth prompts when using
  custom Anthropic-compatible endpoints

- agent-queue.ts now passes model shorthand (opus/sonnet/haiku) to
  backend instead of resolved full model ID, allowing backend to
  use API profile's custom model mappings via env vars

Fixes issue where Ideation/Roadmap would prompt for OAuth even when
a valid API profile was configured and active.

* Refactor token check with useCallback in EnvConfigModal

Wrapped the checkToken function in useCallback and updated useEffect dependencies to use checkToken instead of activeProfileId.

* Improve error handling in Claude token check hook

Adds logic to set an error message if the OAuth token check fails and there is no API profile fallback.

* Refactor API profile check in useClaudeTokenCheck

Simplifies the logic for determining if an API profile exists by computing hasAPIProfile once using the closure-captured activeProfileId.

---------

Co-authored-by: Andy <119136210+AndyMik90@users.noreply.github.com>
This commit is contained in:
Ginanjar Noviawan
2026-01-06 13:30:05 +07:00
committed by GitHub
parent 5005e56e46
commit 78aceaed1e
2 changed files with 31 additions and 15 deletions
+4 -5
View File
@@ -7,7 +7,6 @@ import { AgentEvents } from './agent-events';
import { AgentProcessManager } from './agent-process';
import { RoadmapConfig } from './types';
import type { IdeationConfig, Idea } from '../../shared/types';
import { MODEL_ID_MAP } from '../../shared/constants';
import { detectRateLimit, createSDKRateLimitInfo, getProfileEnv } from '../rate-limit-detector';
import { getAPIProfileEnv } from '../services/profile';
import { getOAuthModeClearVars } from './env-utils';
@@ -96,9 +95,9 @@ export class AgentQueueManager {
}
// Add model and thinking level from config
// Pass shorthand (opus/sonnet/haiku) - backend resolves using API profile env vars
if (config?.model) {
const modelId = MODEL_ID_MAP[config.model] || MODEL_ID_MAP['opus'];
args.push('--model', modelId);
args.push('--model', config.model);
}
if (config?.thinkingLevel) {
args.push('--thinking-level', config.thinkingLevel);
@@ -172,9 +171,9 @@ export class AgentQueueManager {
}
// Add model and thinking level from config
// Pass shorthand (opus/sonnet/haiku) - backend resolves using API profile env vars
if (config.model) {
const modelId = MODEL_ID_MAP[config.model] || MODEL_ID_MAP['opus'];
args.push('--model', modelId);
args.push('--model', config.model);
}
if (config.thinkingLevel) {
args.push('--thinking-level', config.thinkingLevel);
@@ -1,4 +1,4 @@
import { useState, useEffect } from 'react';
import { useState, useEffect, useCallback } from 'react';
import {
AlertCircle,
Key,
@@ -13,6 +13,7 @@ import {
ChevronDown,
ChevronRight
} from 'lucide-react';
import { useSettingsStore } from '../stores/settings-store';
import {
Dialog,
DialogContent,
@@ -592,35 +593,51 @@ export function EnvConfigModal({
/**
* Hook to check if the Claude token is configured
* Returns { hasToken, isLoading, checkToken }
*
* This combines two sources of authentication:
* 1. OAuth token from source .env (checked via checkSourceToken)
* 2. Active API profile (custom Anthropic-compatible endpoint)
*/
export function useClaudeTokenCheck() {
const [hasToken, setHasToken] = useState<boolean | null>(null);
const [isLoading, setIsLoading] = useState(true);
const [error, setError] = useState<string | null>(null);
const checkToken = async () => {
// Get active API profile from settings store
const activeProfileId = useSettingsStore((state) => state.activeProfileId);
const checkToken = useCallback(async () => {
setIsLoading(true);
setError(null);
// Compute once - activeProfileId is captured from closure
const hasAPIProfile = !!activeProfileId;
try {
const result = await window.electronAPI.checkSourceToken();
if (result.success && result.data) {
setHasToken(result.data.hasToken);
} else {
setHasToken(false);
const hasSourceOAuthToken = result.success && result.data?.hasToken;
// Auth is valid if either OAuth token OR API profile exists
setHasToken(hasSourceOAuthToken || hasAPIProfile);
// Set error if OAuth check failed and no API profile fallback
if (!result.success && !hasAPIProfile) {
setError(result.error || 'Failed to check token');
}
} catch (err) {
setHasToken(false);
setError(err instanceof Error ? err.message : 'Unknown error');
// Even if OAuth check fails, API profile is still valid auth
setHasToken(hasAPIProfile);
if (!hasAPIProfile) {
setError(err instanceof Error ? err.message : 'Unknown error');
}
} finally {
setIsLoading(false);
}
};
}, [activeProfileId]);
useEffect(() => {
checkToken();
}, []);
}, [checkToken]); // Re-check when checkToken changes (i.e., when activeProfileId changes)
return { hasToken, isLoading, error, checkToken };
}