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 { AgentProcessManager } from './agent-process';
import { RoadmapConfig } from './types'; import { RoadmapConfig } from './types';
import type { IdeationConfig, Idea } from '../../shared/types'; import type { IdeationConfig, Idea } from '../../shared/types';
import { MODEL_ID_MAP } from '../../shared/constants';
import { detectRateLimit, createSDKRateLimitInfo, getProfileEnv } from '../rate-limit-detector'; import { detectRateLimit, createSDKRateLimitInfo, getProfileEnv } from '../rate-limit-detector';
import { getAPIProfileEnv } from '../services/profile'; import { getAPIProfileEnv } from '../services/profile';
import { getOAuthModeClearVars } from './env-utils'; import { getOAuthModeClearVars } from './env-utils';
@@ -96,9 +95,9 @@ export class AgentQueueManager {
} }
// Add model and thinking level from config // Add model and thinking level from config
// Pass shorthand (opus/sonnet/haiku) - backend resolves using API profile env vars
if (config?.model) { if (config?.model) {
const modelId = MODEL_ID_MAP[config.model] || MODEL_ID_MAP['opus']; args.push('--model', config.model);
args.push('--model', modelId);
} }
if (config?.thinkingLevel) { if (config?.thinkingLevel) {
args.push('--thinking-level', config.thinkingLevel); args.push('--thinking-level', config.thinkingLevel);
@@ -172,9 +171,9 @@ export class AgentQueueManager {
} }
// Add model and thinking level from config // Add model and thinking level from config
// Pass shorthand (opus/sonnet/haiku) - backend resolves using API profile env vars
if (config.model) { if (config.model) {
const modelId = MODEL_ID_MAP[config.model] || MODEL_ID_MAP['opus']; args.push('--model', config.model);
args.push('--model', modelId);
} }
if (config.thinkingLevel) { if (config.thinkingLevel) {
args.push('--thinking-level', config.thinkingLevel); args.push('--thinking-level', config.thinkingLevel);
@@ -1,4 +1,4 @@
import { useState, useEffect } from 'react'; import { useState, useEffect, useCallback } from 'react';
import { import {
AlertCircle, AlertCircle,
Key, Key,
@@ -13,6 +13,7 @@ import {
ChevronDown, ChevronDown,
ChevronRight ChevronRight
} from 'lucide-react'; } from 'lucide-react';
import { useSettingsStore } from '../stores/settings-store';
import { import {
Dialog, Dialog,
DialogContent, DialogContent,
@@ -592,35 +593,51 @@ export function EnvConfigModal({
/** /**
* Hook to check if the Claude token is configured * Hook to check if the Claude token is configured
* Returns { hasToken, isLoading, checkToken } * 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() { export function useClaudeTokenCheck() {
const [hasToken, setHasToken] = useState<boolean | null>(null); const [hasToken, setHasToken] = useState<boolean | null>(null);
const [isLoading, setIsLoading] = useState(true); const [isLoading, setIsLoading] = useState(true);
const [error, setError] = useState<string | null>(null); 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); setIsLoading(true);
setError(null); setError(null);
// Compute once - activeProfileId is captured from closure
const hasAPIProfile = !!activeProfileId;
try { try {
const result = await window.electronAPI.checkSourceToken(); const result = await window.electronAPI.checkSourceToken();
if (result.success && result.data) { const hasSourceOAuthToken = result.success && result.data?.hasToken;
setHasToken(result.data.hasToken);
} else { // Auth is valid if either OAuth token OR API profile exists
setHasToken(false); 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'); setError(result.error || 'Failed to check token');
} }
} catch (err) { } catch (err) {
setHasToken(false); // Even if OAuth check fails, API profile is still valid auth
setError(err instanceof Error ? err.message : 'Unknown error'); setHasToken(hasAPIProfile);
if (!hasAPIProfile) {
setError(err instanceof Error ? err.message : 'Unknown error');
}
} finally { } finally {
setIsLoading(false); setIsLoading(false);
} }
}; }, [activeProfileId]);
useEffect(() => { useEffect(() => {
checkToken(); checkToken();
}, []); }, [checkToken]); // Re-check when checkToken changes (i.e., when activeProfileId changes)
return { hasToken, isLoading, error, checkToken }; return { hasToken, isLoading, error, checkToken };
} }