Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 14 additions & 1 deletion packages/cli/src/commands/$default.ts
Original file line number Diff line number Diff line change
Expand Up @@ -107,12 +107,25 @@ export const command: CommandModule<SharedOptions, DefaultArgs> = {

if (providerSettings) {
const { keyName } = providerSettings;
const apiKey = process.env[keyName];

// First check if the API key is in the config
const configApiKey = userConfig[keyName as keyof typeof userConfig] as string;
// Then fall back to environment variable
const envApiKey = process.env[keyName];
// Use config key if available, otherwise use env key
const apiKey = configApiKey || envApiKey;

if (!apiKey) {
logger.error(getProviderApiKeyError(userModelProvider));
throw new Error(`${userModelProvider} API key not found`);
}

// If we're using a key from config, set it as an environment variable
// This ensures it's available to the provider libraries
if (configApiKey && !envApiKey) {
process.env[keyName] = configApiKey;
logger.debug(`Using ${keyName} from configuration`);
}
}
// No API key check needed for Ollama as it uses a local server

Expand Down
28 changes: 28 additions & 0 deletions packages/cli/src/commands/config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,10 @@ export const command: CommandModule<SharedOptions, ConfigOptions> = {
'$0 config clear customPrompt',
'Reset customPrompt to default value',
)
.example(
'$0 config set ANTHROPIC_API_KEY <your-key>',
'Store your Anthropic API key in configuration',
)
.example(
'$0 config clear --all',
'Clear all configuration settings',
Expand Down Expand Up @@ -151,6 +155,30 @@ export const command: CommandModule<SharedOptions, ConfigOptions> = {
return;
}

// Check if this is an API key and add a warning
if (argv.key.includes('API_KEY')) {
logger.warn(
chalk.yellow(
'Warning: Storing API keys in configuration is less secure than using environment variables.'
)
);
logger.warn(
chalk.yellow(
'Your API key will be stored in plaintext in the configuration file.'
)
);

// Ask for confirmation
const isConfirmed = await confirm(
'Do you want to continue storing your API key in the configuration?'
);

if (!isConfirmed) {
logger.info('Operation cancelled.');
return;
}
}

// Parse the value based on current type or infer boolean/number
let parsedValue: string | boolean | number = argv.value;

Expand Down
5 changes: 5 additions & 0 deletions packages/cli/src/settings/config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,11 @@ const defaultConfig = {
customPrompt: '',
profile: false,
tokenCache: true,
// API keys (empty by default)
ANTHROPIC_API_KEY: '',
OPENAI_API_KEY: '',
XAI_API_KEY: '',
MISTRAL_API_KEY: '',
};

export type Config = typeof defaultConfig;
Expand Down
12 changes: 11 additions & 1 deletion packages/cli/tests/settings/config.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,11 @@ describe('Config', () => {
profile: false,
customPrompt: '',
tokenCache: true,
// API keys
ANTHROPIC_API_KEY: '',
OPENAI_API_KEY: '',
XAI_API_KEY: '',
MISTRAL_API_KEY: '',
});
expect(fs.existsSync).toHaveBeenCalledWith(mockConfigFile);
});
Expand Down Expand Up @@ -86,6 +91,11 @@ describe('Config', () => {
profile: false,
customPrompt: '',
tokenCache: true,
// API keys
ANTHROPIC_API_KEY: '',
OPENAI_API_KEY: '',
XAI_API_KEY: '',
MISTRAL_API_KEY: '',
});
});
});
Expand Down Expand Up @@ -117,4 +127,4 @@ describe('Config', () => {
expect(result).toEqual({ githubMode: true, existingSetting: 'value' });
});
});
});
});
Loading