fix: integrate Claude CLI provider state
This commit is contained in:
@@ -52,7 +52,7 @@ export const DefaultsSettings: React.FC = () => {
|
||||
const [defaultAgent, setDefaultAgent] = React.useState<string | undefined>();
|
||||
const [smallModelUseDefault, setSmallModelUseDefault] = React.useState(true);
|
||||
const [smallModelOverride, setSmallModelOverride] = React.useState<string | undefined>();
|
||||
const [smallModelProviders, setSmallModelProviders] = React.useState<string[] | undefined>();
|
||||
const [smallModelProviders, setSmallModelProviders] = React.useState<string[]>([]);
|
||||
const [walkthroughModelOverride, setWalkthroughModelOverride] = React.useState<string | undefined>();
|
||||
const [isLoading, setIsLoading] = React.useState(true);
|
||||
|
||||
@@ -274,13 +274,9 @@ export const DefaultsSettings: React.FC = () => {
|
||||
() => getDisplayModel(walkthroughModelOverride),
|
||||
[walkthroughModelOverride]
|
||||
);
|
||||
|
||||
React.useEffect(() => {
|
||||
// Both pickers filter by the same authenticated-provider list, so either
|
||||
// one being open is reason enough to fetch it.
|
||||
// Both pickers filter by the same authenticated-provider list, and the
|
||||
// walkthrough picker is always visible, so this is always worth fetching.
|
||||
if (smallModelProviders !== undefined) return;
|
||||
let cancelled = false;
|
||||
(async () => {
|
||||
try {
|
||||
@@ -291,13 +287,13 @@ export const DefaultsSettings: React.FC = () => {
|
||||
setSmallModelProviders(payload.authenticatedProviders.filter((id): id is string => typeof id === 'string'));
|
||||
}
|
||||
} catch {
|
||||
// leave undefined — picker falls back to showing all providers
|
||||
// Fail closed: never offer providers whose credentials were not verified.
|
||||
}
|
||||
})();
|
||||
return () => {
|
||||
cancelled = true;
|
||||
};
|
||||
}, [smallModelProviders]);
|
||||
}, []);
|
||||
|
||||
const availableVariants = React.useMemo(() => {
|
||||
if (!parsedModel.providerId || !parsedModel.modelId) return [];
|
||||
|
||||
@@ -20,6 +20,7 @@ import {
|
||||
firstUnansweredPrompt,
|
||||
parseAuthPrompts,
|
||||
parseAuthorization,
|
||||
shouldOpenAuthorizationUrl,
|
||||
visiblePrompts,
|
||||
type AuthPrompt,
|
||||
type OAuthAuthorization,
|
||||
@@ -173,7 +174,10 @@ export const ProviderOAuthMethods: React.FC<ProviderOAuthMethodsProps> = ({
|
||||
return;
|
||||
}
|
||||
|
||||
if (authorization.url) {
|
||||
// Claude Code CLI owns its OAuth flow and opens the browser itself. Its
|
||||
// plugin URL is informational only; opening it creates a misleading docs
|
||||
// tab alongside the real sign-in page.
|
||||
if (authorization.url && shouldOpenAuthorizationUrl(providerId, authorization.url)) {
|
||||
void openExternalUrl(authorization.url);
|
||||
}
|
||||
|
||||
|
||||
@@ -4,6 +4,7 @@ import {
|
||||
getOAuthAuthMethods,
|
||||
normalizeAuthType,
|
||||
parseAuthPayload,
|
||||
requiresOpenCodeRestartAfterOAuth,
|
||||
shouldShowApiKeyAuth,
|
||||
} from './providerAuth';
|
||||
|
||||
@@ -57,4 +58,9 @@ describe('provider auth method helpers', () => {
|
||||
{ method: { type: 'oauth', label: 'Cursor' }, methodIndex: 0 },
|
||||
]);
|
||||
});
|
||||
|
||||
test('Claude CLI OAuth does not require an OpenCode restart', () => {
|
||||
expect(requiresOpenCodeRestartAfterOAuth('claude-code')).toBe(false);
|
||||
expect(requiresOpenCodeRestartAfterOAuth('github-copilot')).toBe(true);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -27,6 +27,7 @@ import { shouldLoadAvailableProviders } from './providerAvailability';
|
||||
import {
|
||||
getOAuthAuthMethods,
|
||||
parseAuthPayload,
|
||||
requiresOpenCodeRestartAfterOAuth,
|
||||
shouldShowApiKeyAuth,
|
||||
type AuthMethod,
|
||||
type OAuthAuthMethodEntry,
|
||||
@@ -471,7 +472,9 @@ export const ProvidersPage: React.FC = () => {
|
||||
|
||||
const handleOAuthConnected = (providerId: string) => {
|
||||
setShowAuthPanel(false);
|
||||
recordDeferredOpenCodeRestart('providers', { id: providerId });
|
||||
if (requiresOpenCodeRestartAfterOAuth(providerId)) {
|
||||
recordDeferredOpenCodeRestart('providers', { id: providerId });
|
||||
}
|
||||
setSelectedProvider(providerId);
|
||||
};
|
||||
|
||||
|
||||
@@ -7,11 +7,19 @@ import {
|
||||
isPromptVisible,
|
||||
parseAuthPrompts,
|
||||
parseAuthorization,
|
||||
shouldOpenAuthorizationUrl,
|
||||
visiblePrompts,
|
||||
type AuthPrompt,
|
||||
type ProviderOAuthTranslator,
|
||||
} from './provider-oauth';
|
||||
|
||||
describe('shouldOpenAuthorizationUrl', () => {
|
||||
test('lets Claude Code CLI own browser launch', () => {
|
||||
expect(shouldOpenAuthorizationUrl('claude-code', 'https://docs.example')).toBe(false);
|
||||
expect(shouldOpenAuthorizationUrl('github-copilot', 'https://github.com/login')).toBe(true);
|
||||
});
|
||||
});
|
||||
|
||||
/** Mirrors the github-copilot auth method shipped by OpenCode. */
|
||||
const copilotPrompts = [
|
||||
{
|
||||
|
||||
@@ -29,6 +29,9 @@ export interface OAuthAuthorization {
|
||||
userCode?: string;
|
||||
}
|
||||
|
||||
export const shouldOpenAuthorizationUrl = (providerId: string, url?: string): boolean =>
|
||||
Boolean(url) && providerId !== 'claude-code';
|
||||
|
||||
export interface AuthPromptOption {
|
||||
label: string;
|
||||
value: string;
|
||||
|
||||
@@ -57,3 +57,6 @@ export const getOAuthAuthMethods = (methods: AuthMethod[]): OAuthAuthMethodEntry
|
||||
methods
|
||||
.map((method, methodIndex) => ({ method, methodIndex }))
|
||||
.filter(({ method }) => normalizeAuthType(method) === 'oauth');
|
||||
|
||||
export const requiresOpenCodeRestartAfterOAuth = (providerId: string): boolean =>
|
||||
providerId !== 'claude-code';
|
||||
|
||||
Reference in New Issue
Block a user