fix: support Bedrock API key login

This commit is contained in:
Armin Ronacher
2026-07-10 19:34:11 +02:00
parent 91585d9a38
commit 3ea064ea2a
10 changed files with 72 additions and 36 deletions
+4
View File
@@ -2,6 +2,10 @@
## [Unreleased]
### Fixed
- Fixed `/login amazon-bedrock` to prompt for and save a Bedrock API key instead of only displaying ambient AWS credential setup instructions.
## [0.80.6] - 2026-07-09
### New Features
+3
View File
@@ -55,6 +55,7 @@ pi
| DeepSeek | `DEEPSEEK_API_KEY` | `deepseek` |
| NVIDIA NIM | `NVIDIA_API_KEY` | `nvidia` |
| Google Gemini | `GEMINI_API_KEY` | `google` |
| Amazon Bedrock | `AWS_BEARER_TOKEN_BEDROCK` | `amazon-bedrock` |
| Mistral | `MISTRAL_API_KEY` | `mistral` |
| Groq | `GROQ_API_KEY` | `groq` |
| Cerebras | `CEREBRAS_API_KEY` | `cerebras` |
@@ -170,6 +171,8 @@ export AZURE_OPENAI_DEPLOYMENT_NAME_MAP=gpt-4=my-gpt4,gpt-4o=my-gpt4o
### Amazon Bedrock
Use `/login amazon-bedrock` to store a Bedrock API key, or configure one of the ambient AWS credential sources below:
```bash
# Option 1: AWS Profile
export AWS_PROFILE=your-profile
@@ -177,14 +177,22 @@ export class LoginDialogComponent extends Container implements Focusable {
}
/**
* Show informational text without prompting for input.
* Show informational text before another login step.
*/
showInfo(lines: string[]): void {
showDetails(lines: string[]): void {
this.contentContainer.clear();
this.contentContainer.addChild(new Spacer(1));
for (const line of lines) {
this.contentContainer.addChild(new Text(line, 1, 0));
}
this.tui.requestRender();
}
/**
* Show informational text without prompting for input.
*/
showInfo(lines: string[]): void {
this.showDetails(lines);
this.contentContainer.addChild(new Spacer(1));
this.contentContainer.addChild(new Text(`(${keyHint("tui.select.cancel", "to close")})`, 1, 0));
this.tui.requestRender();
@@ -248,8 +248,6 @@ function hasDefaultModelProvider(providerId: string): providerId is keyof typeof
return providerId in defaultModelPerProvider;
}
const BEDROCK_PROVIDER_ID = "amazon-bedrock";
const BUILT_IN_MODEL_PROVIDERS = new Set<string>(getProviders());
export function isApiKeyLoginProvider(
@@ -4873,8 +4871,6 @@ export class InteractiveMode {
private async startProviderLogin(providerOption: AuthSelectorProvider): Promise<void> {
if (providerOption.authType === "oauth") {
await this.showLoginDialog(providerOption.id, providerOption.name);
} else if (providerOption.id === BEDROCK_PROVIDER_ID) {
this.showBedrockSetupDialog(providerOption.id, providerOption.name);
} else {
await this.showApiKeyLoginDialog(providerOption.id, providerOption.name);
}
@@ -5082,34 +5078,6 @@ export class InteractiveMode {
}
}
private showBedrockSetupDialog(providerId: string, providerName: string): void {
const restoreEditor = () => {
this.editorContainer.clear();
this.editorContainer.addChild(this.editor);
this.ui.setFocus(this.editor);
this.ui.requestRender();
};
const dialog = new LoginDialogComponent(
this.ui,
providerId,
() => restoreEditor(),
providerName,
"Amazon Bedrock setup",
);
dialog.showInfo([
theme.fg("text", "Amazon Bedrock uses AWS credentials instead of a single API key."),
theme.fg("text", "Configure an AWS profile, IAM keys, bearer token, or role-based credentials."),
theme.fg("muted", "See:"),
theme.fg("accent", ` ${path.join(getDocsPath(), "providers.md")}`),
]);
this.editorContainer.clear();
this.editorContainer.addChild(dialog);
this.ui.setFocus(dialog);
this.ui.requestRender();
}
private async showApiKeyLoginDialog(providerId: string, providerName: string): Promise<void> {
const previousModel = this.session.model;
@@ -5122,6 +5090,14 @@ export class InteractiveMode {
providerName,
);
if (providerId === "amazon-bedrock") {
dialog.showDetails([
theme.fg("text", "You can also use an AWS profile, IAM keys, or role-based credentials."),
theme.fg("muted", "See:"),
theme.fg("accent", ` ${path.join(getDocsPath(), "providers.md")}`),
]);
}
this.editorContainer.clear();
this.editorContainer.addChild(dialog);
this.ui.setFocus(dialog);
@@ -70,6 +70,18 @@ describe("LoginDialogComponent OAuth prompts", () => {
expect(output).toContain("First prompt:");
});
test("preserves setup details when showing a prompt", () => {
const dialog = createDialog();
dialog.showDetails(["AWS credential setup:", "providers.md"]);
dialog.showPrompt("Enter API key:");
const output = renderDialog(dialog).join("\n");
expect(output).toContain("AWS credential setup:");
expect(output).toContain("providers.md");
expect(output).toContain("Enter API key:");
});
test("keeps previous manual input stable when a later prompt is active", async () => {
const dialog = createDialog();