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:
committed by
GitHub
parent
5005e56e46
commit
78aceaed1e
@@ -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 };
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user