refactor(worker): unify provider precedence between preflight and executor

This commit is contained in:
ezl-keygraph
2026-06-15 23:06:10 +05:30
parent c2bceba95c
commit f97afb482e
2 changed files with 29 additions and 22 deletions
+2 -2
View File
@@ -37,7 +37,7 @@ const DEFAULT_MODELS: Readonly<Record<ModelTier, string>> = {
large: 'claude-opus-4-8',
};
interface EffectiveProvider {
export interface EffectiveProvider {
/** pi-ai provider id: 'anthropic' or 'amazon-bedrock'. */
providerId: string;
/** Custom-base-URL override applied to the resolved anthropic model. */
@@ -55,7 +55,7 @@ interface EffectiveProvider {
* direct Anthropic (`ANTHROPIC_API_KEY`, or `CLAUDE_CODE_OAUTH_TOKEN`). Bedrock
* authenticates from the AWS_ env vars via pi-ai, so it needs no anthropic token.
*/
function resolveEffectiveProvider(apiKey?: string, providerConfig?: ProviderConfig): EffectiveProvider {
export function resolveEffectiveProvider(apiKey?: string, providerConfig?: ProviderConfig): EffectiveProvider {
const anthropicKey = apiKey ?? providerConfig?.apiKey ?? process.env.ANTHROPIC_API_KEY;
const type = providerConfig?.providerType;
+27 -20
View File
@@ -34,7 +34,7 @@ import {
SettingsManager,
} from '@earendil-works/pi-coding-agent';
import { glob } from 'zx';
import { resolveModelId } from '../ai/models.js';
import { resolveEffectiveProvider, resolveModelId } from '../ai/models.js';
import { parseConfig } from '../config-parser.js';
import type { ActivityLogger } from '../types/activity-logger.js';
import type { Config, Rule } from '../types/config.js';
@@ -304,10 +304,12 @@ function classifyCredentialError(text: string, authType: string): Result<void, P
}
/** Minimal pi session probe to validate credentials. An optional baseUrl overrides the endpoint. */
async function probeCredentialsWithPi(authType: string, baseUrl?: string): Promise<Result<void, PentestError>> {
async function probeCredentialsWithPi(
authType: string,
token?: string,
baseUrl?: string,
): Promise<Result<void, PentestError>> {
const authStorage = AuthStorage.inMemory();
const token =
process.env.ANTHROPIC_AUTH_TOKEN ?? process.env.ANTHROPIC_API_KEY ?? process.env.CLAUDE_CODE_OAUTH_TOKEN;
if (token) authStorage.setRuntimeApiKey('anthropic', token);
const baseModel = ModelRegistry.create(authStorage).find('anthropic', resolveModelId('small'));
@@ -370,18 +372,14 @@ async function validateCredentials(
if (apiKey) {
process.env.ANTHROPIC_API_KEY = apiKey;
}
// 1. Custom base URL — validate the endpoint via a minimal pi session
if (process.env.ANTHROPIC_BASE_URL && process.env.ANTHROPIC_AUTH_TOKEN) {
const baseUrl = process.env.ANTHROPIC_BASE_URL;
logger.info('Validating custom base URL');
const probe = await probeCredentialsWithPi(`custom endpoint (${baseUrl})`, baseUrl);
if (isErr(probe)) return probe;
logger.info('Custom base URL OK');
return ok(undefined);
}
// 2. Bedrock mode — validate required AWS credentials are present
if (process.env.CLAUDE_CODE_USE_BEDROCK === '1') {
// Resolve the active provider through the same precedence the executor uses, so
// preflight validates exactly the credentials the run will use (no drift).
const eff = resolveEffectiveProvider(apiKey);
// 1. Bedrock mode — validate required AWS credentials are present (pi-ai owns the
// live AWS auth, so there is no cheap session probe here)
if (eff.providerId === 'amazon-bedrock') {
const required = [
'AWS_REGION',
'AWS_BEARER_TOKEN_BEDROCK',
@@ -405,8 +403,17 @@ async function validateCredentials(
return ok(undefined);
}
// 3. Check that at least one credential is present
if (!process.env.ANTHROPIC_API_KEY && !process.env.CLAUDE_CODE_OAUTH_TOKEN && !process.env.ANTHROPIC_AUTH_TOKEN) {
// 2. Custom base URL — validate the endpoint via a minimal pi session
if (eff.baseUrl) {
logger.info('Validating custom base URL');
const probe = await probeCredentialsWithPi(`custom endpoint (${eff.baseUrl})`, eff.anthropicToken, eff.baseUrl);
if (isErr(probe)) return probe;
logger.info('Custom base URL OK');
return ok(undefined);
}
// 3. Direct Anthropic — require a credential, then validate via a minimal pi session
if (!eff.anthropicToken) {
return err(
new PentestError(
'No API credentials found. Set ANTHROPIC_API_KEY or CLAUDE_CODE_OAUTH_TOKEN in .env (or use CLAUDE_CODE_USE_BEDROCK=1 for AWS Bedrock)',
@@ -418,10 +425,10 @@ async function validateCredentials(
);
}
// 4. Validate via a minimal pi session
const authType = process.env.CLAUDE_CODE_OAUTH_TOKEN ? 'OAuth token' : 'API key';
const usingApiKey = Boolean(apiKey ?? process.env.ANTHROPIC_API_KEY);
const authType = usingApiKey ? 'API key' : 'OAuth token';
logger.info(`Validating ${authType} via pi...`);
const probe = await probeCredentialsWithPi(authType);
const probe = await probeCredentialsWithPi(authType, eff.anthropicToken);
if (isErr(probe)) return probe;
logger.info(`${authType} OK`);
return ok(undefined);