daemon: enforce auth_mode for OpenAI and Anthropic

This commit is contained in:
William Valentin
2026-02-15 10:39:53 -08:00
parent fac740f362
commit 7627e6e630
2 changed files with 274 additions and 61 deletions
+119 -14
View File
@@ -3,7 +3,20 @@ import { AnthropicClient, OpenAIClient, OllamaClient, LlamaCppClient, GeminiClie
import type { ModelClient, RetryConfig, ModelTier } from '../models/index.js';
import { logger } from '../logger.js';
import { getZaiApiKey } from '../auth/zai.js';
import { getAnthropicApiKey } from '../auth/anthropic.js';
import { getAnthropicApiKey, getAnthropicAuthToken } from '../auth/anthropic.js';
import { getOpenAIApiKey, loadStoredOpenAIAuth } from '../auth/openai.js';
type AuthMode = 'auto' | 'api_key' | 'oauth';
function getEffectiveAuthMode(cfg: ModelConfig): AuthMode {
if (cfg.auth_mode) {
return cfg.auth_mode;
}
if (cfg.use_oauth) {
return 'oauth';
}
return 'auto';
}
/**
* Resolve an API key from config or environment variable.
@@ -45,23 +58,115 @@ function resolveAuthCredential(cfg: ModelConfig, apiKeyEnvVar: string, authToken
export function createClientFromConfig(cfg: ModelConfig): ModelClient {
switch (cfg.provider) {
case 'anthropic':
if (!cfg.api_key && !getAnthropicApiKey()) {
{
const authMode = getEffectiveAuthMode(cfg);
if (authMode === 'oauth') {
const token = cfg.auth_token ?? getAnthropicAuthToken();
if (!token) {
throw new Error(
'Anthropic auth token not configured (auth_mode: oauth). ' +
'Set ANTHROPIC_AUTH_TOKEN, run `flynn anthropic-auth --token`, or provide auth_token in config.',
);
}
return new AnthropicClient({
model: cfg.model,
authToken: token,
});
}
if (authMode === 'api_key') {
const apiKey = cfg.api_key ?? getAnthropicApiKey();
if (!apiKey) {
throw new Error(
'Anthropic API key not configured (auth_mode: api_key). ' +
'Set ANTHROPIC_API_KEY, run `flynn anthropic-auth`, or provide api_key in config.',
);
}
return new AnthropicClient({
model: cfg.model,
apiKey,
});
}
// auto: prefer API key, then token
const apiKey = cfg.api_key ?? getAnthropicApiKey();
if (apiKey) {
return new AnthropicClient({
model: cfg.model,
apiKey,
});
}
const token = cfg.auth_token ?? getAnthropicAuthToken();
if (token) {
return new AnthropicClient({
model: cfg.model,
authToken: token,
});
}
throw new Error(
'Anthropic API key not configured. ' +
'Set ANTHROPIC_API_KEY, run `flynn anthropic-auth`, or provide api_key in config.',
'Anthropic credentials not configured (auth_mode: auto). ' +
'Set ANTHROPIC_API_KEY (or run `flynn anthropic-auth`), ' +
'or set ANTHROPIC_AUTH_TOKEN (or run `flynn anthropic-auth --token`).',
);
}
return new AnthropicClient({
model: cfg.model,
apiKey: cfg.api_key ?? getAnthropicApiKey() ?? undefined,
authToken: cfg.auth_token,
});
case 'openai':
return new OpenAIClient({
model: cfg.model,
apiKey: cfg.api_key,
useOAuth: Boolean(cfg.use_oauth),
});
{
const authMode = getEffectiveAuthMode(cfg);
if (authMode === 'oauth') {
const existing = loadStoredOpenAIAuth();
if (!existing) {
throw new Error(
'OpenAI OAuth is not configured (auth_mode: oauth). ' +
'Run `flynn openai-auth` to authenticate.',
);
}
return new OpenAIClient({
model: cfg.model,
useOAuth: true,
});
}
if (authMode === 'api_key') {
const apiKey = cfg.api_key ?? getOpenAIApiKey();
if (!apiKey) {
throw new Error(
'OpenAI API key not configured (auth_mode: api_key). ' +
'Set OPENAI_API_KEY, run `flynn openai-key`, or provide api_key in config.',
);
}
return new OpenAIClient({
model: cfg.model,
apiKey,
});
}
// auto: prefer API key, then OAuth
const apiKey = cfg.api_key ?? getOpenAIApiKey();
if (apiKey) {
return new OpenAIClient({
model: cfg.model,
apiKey,
});
}
const existing = loadStoredOpenAIAuth();
if (existing) {
return new OpenAIClient({
model: cfg.model,
useOAuth: true,
});
}
throw new Error(
'OpenAI credentials not configured (auth_mode: auto). ' +
'Set OPENAI_API_KEY (or run `flynn openai-key`), ' +
'or run `flynn openai-auth` for OAuth.',
);
}
case 'ollama':
return new OllamaClient({
model: cfg.model,