Merge pull request #339 from ramarivera/feat/custom-anthropic-endpoint

feat: support ANTHROPIC_BASE_URL and ANTHROPIC_AUTH_TOKEN for custom endpoints
This commit is contained in:
Web Dev Cody
2026-01-14 10:09:56 -05:00
committed by GitHub
2 changed files with 91 additions and 0 deletions

View File

@@ -22,6 +22,8 @@ import type {
// Only these vars are passed - nothing else from process.env leaks through.
const ALLOWED_ENV_VARS = [
'ANTHROPIC_API_KEY',
'ANTHROPIC_BASE_URL',
'ANTHROPIC_AUTH_TOKEN',
'PATH',
'HOME',
'SHELL',

View File

@@ -12,6 +12,8 @@ describe('claude-provider.ts', () => {
vi.clearAllMocks();
provider = new ClaudeProvider();
delete process.env.ANTHROPIC_API_KEY;
delete process.env.ANTHROPIC_BASE_URL;
delete process.env.ANTHROPIC_AUTH_TOKEN;
});
describe('getName', () => {
@@ -267,6 +269,93 @@ describe('claude-provider.ts', () => {
});
});
describe('environment variable passthrough', () => {
afterEach(() => {
delete process.env.ANTHROPIC_BASE_URL;
delete process.env.ANTHROPIC_AUTH_TOKEN;
});
it('should pass ANTHROPIC_BASE_URL to SDK env', async () => {
process.env.ANTHROPIC_BASE_URL = 'https://custom.example.com/v1';
vi.mocked(sdk.query).mockReturnValue(
(async function* () {
yield { type: 'text', text: 'test' };
})()
);
const generator = provider.executeQuery({
prompt: 'Test',
cwd: '/test',
});
await collectAsyncGenerator(generator);
expect(sdk.query).toHaveBeenCalledWith({
prompt: 'Test',
options: expect.objectContaining({
env: expect.objectContaining({
ANTHROPIC_BASE_URL: 'https://custom.example.com/v1',
}),
}),
});
});
it('should pass ANTHROPIC_AUTH_TOKEN to SDK env', async () => {
process.env.ANTHROPIC_AUTH_TOKEN = 'custom-auth-token';
vi.mocked(sdk.query).mockReturnValue(
(async function* () {
yield { type: 'text', text: 'test' };
})()
);
const generator = provider.executeQuery({
prompt: 'Test',
cwd: '/test',
});
await collectAsyncGenerator(generator);
expect(sdk.query).toHaveBeenCalledWith({
prompt: 'Test',
options: expect.objectContaining({
env: expect.objectContaining({
ANTHROPIC_AUTH_TOKEN: 'custom-auth-token',
}),
}),
});
});
it('should pass both custom endpoint vars together', async () => {
process.env.ANTHROPIC_BASE_URL = 'https://gateway.example.com';
process.env.ANTHROPIC_AUTH_TOKEN = 'gateway-token';
vi.mocked(sdk.query).mockReturnValue(
(async function* () {
yield { type: 'text', text: 'test' };
})()
);
const generator = provider.executeQuery({
prompt: 'Test',
cwd: '/test',
});
await collectAsyncGenerator(generator);
expect(sdk.query).toHaveBeenCalledWith({
prompt: 'Test',
options: expect.objectContaining({
env: expect.objectContaining({
ANTHROPIC_BASE_URL: 'https://gateway.example.com',
ANTHROPIC_AUTH_TOKEN: 'gateway-token',
}),
}),
});
});
});
describe('getAvailableModels', () => {
it('should return 4 Claude models', () => {
const models = provider.getAvailableModels();