Skip to content

Commit 076b1e3

Browse files
authored
Nighttrek/bedrock credential manager (RooCodeInc#1667)
* added initial AWS bedrock support * fixed formatting * updated to fix persistence * added changeset
1 parent 99bba4a commit 076b1e3

File tree

5 files changed

+106
-37
lines changed

5 files changed

+106
-37
lines changed

.changeset/tasty-readers-move.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
"claude-dev": minor
3+
---
4+
5+
Added support for AWS provider profiles using the AWS CLI to make the profile. enabling long lived connections to AWS bedrock

src/api/providers/bedrock.ts

Lines changed: 25 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ import { Anthropic } from "@anthropic-ai/sdk"
33
import { ApiHandler } from "../"
44
import { ApiHandlerOptions, bedrockDefaultModelId, BedrockModelId, bedrockModels, ModelInfo } from "../../shared/api"
55
import { ApiStream } from "../transform/stream"
6+
import { fromIni } from "@aws-sdk/credential-providers"
67

78
// https://docs.anthropic.com/en/api/claude-on-amazon-bedrock
89
export class AwsBedrockHandler implements ApiHandler {
@@ -11,17 +12,31 @@ export class AwsBedrockHandler implements ApiHandler {
1112

1213
constructor(options: ApiHandlerOptions) {
1314
this.options = options
14-
this.client = new AnthropicBedrock({
15-
// Authenticate by either providing the keys below or use the default AWS credential providers, such as
16-
// using ~/.aws/credentials or the "AWS_SECRET_ACCESS_KEY" and "AWS_ACCESS_KEY_ID" environment variables.
17-
...(this.options.awsAccessKey ? { awsAccessKey: this.options.awsAccessKey } : {}),
18-
...(this.options.awsSecretKey ? { awsSecretKey: this.options.awsSecretKey } : {}),
19-
...(this.options.awsSessionToken ? { awsSessionToken: this.options.awsSessionToken } : {}),
2015

21-
// awsRegion changes the aws region to which the request is made. By default, we read AWS_REGION,
22-
// and if that's not present, we default to us-east-1. Note that we do not read ~/.aws/config for the region.
23-
awsRegion: this.options.awsRegion,
24-
})
16+
const clientConfig: any = {
17+
awsRegion: this.options.awsRegion || "us-east-1",
18+
}
19+
20+
if (this.options.awsUseProfile) {
21+
// Use profile-based credentials if enabled
22+
if (this.options.awsProfile) {
23+
clientConfig.credentials = fromIni({
24+
profile: this.options.awsProfile,
25+
})
26+
} else {
27+
// Use default profile if no specific profile is set
28+
clientConfig.credentials = fromIni()
29+
}
30+
} else if (this.options.awsAccessKey && this.options.awsSecretKey) {
31+
// Use direct credentials if provided
32+
clientConfig.awsAccessKey = this.options.awsAccessKey
33+
clientConfig.awsSecretKey = this.options.awsSecretKey
34+
if (this.options.awsSessionToken) {
35+
clientConfig.awsSessionToken = this.options.awsSessionToken
36+
}
37+
}
38+
39+
this.client = new AnthropicBedrock(clientConfig)
2540
}
2641

2742
async *createMessage(systemPrompt: string, messages: Anthropic.Messages.MessageParam[]): ApiStream {

src/core/webview/ClineProvider.ts

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,8 @@ type GlobalStateKey =
5555
| "apiModelId"
5656
| "awsRegion"
5757
| "awsUseCrossRegionInference"
58+
| "awsProfile"
59+
| "awsUseProfile"
5860
| "vertexProjectId"
5961
| "vertexRegion"
6062
| "lastShownAnnouncementId"
@@ -431,6 +433,8 @@ export class ClineProvider implements vscode.WebviewViewProvider {
431433
awsSessionToken,
432434
awsRegion,
433435
awsUseCrossRegionInference,
436+
awsProfile,
437+
awsUseProfile,
434438
vertexProjectId,
435439
vertexRegion,
436440
openAiBaseUrl,
@@ -465,6 +469,8 @@ export class ClineProvider implements vscode.WebviewViewProvider {
465469
await this.storeSecret("awsSessionToken", awsSessionToken)
466470
await this.updateGlobalState("awsRegion", awsRegion)
467471
await this.updateGlobalState("awsUseCrossRegionInference", awsUseCrossRegionInference)
472+
await this.updateGlobalState("awsProfile", awsProfile)
473+
await this.updateGlobalState("awsUseProfile", awsUseProfile)
468474
await this.updateGlobalState("vertexProjectId", vertexProjectId)
469475
await this.updateGlobalState("vertexRegion", vertexRegion)
470476
await this.updateGlobalState("openAiBaseUrl", openAiBaseUrl)
@@ -1364,6 +1370,8 @@ export class ClineProvider implements vscode.WebviewViewProvider {
13641370
awsSessionToken,
13651371
awsRegion,
13661372
awsUseCrossRegionInference,
1373+
awsProfile,
1374+
awsUseProfile,
13671375
vertexProjectId,
13681376
vertexRegion,
13691377
openAiBaseUrl,
@@ -1409,6 +1417,8 @@ export class ClineProvider implements vscode.WebviewViewProvider {
14091417
this.getSecret("awsSessionToken") as Promise<string | undefined>,
14101418
this.getGlobalState("awsRegion") as Promise<string | undefined>,
14111419
this.getGlobalState("awsUseCrossRegionInference") as Promise<boolean | undefined>,
1420+
this.getGlobalState("awsProfile") as Promise<string | undefined>,
1421+
this.getGlobalState("awsUseProfile") as Promise<boolean | undefined>,
14121422
this.getGlobalState("vertexProjectId") as Promise<string | undefined>,
14131423
this.getGlobalState("vertexRegion") as Promise<string | undefined>,
14141424
this.getGlobalState("openAiBaseUrl") as Promise<string | undefined>,
@@ -1471,6 +1481,8 @@ export class ClineProvider implements vscode.WebviewViewProvider {
14711481
awsSessionToken,
14721482
awsRegion,
14731483
awsUseCrossRegionInference,
1484+
awsProfile,
1485+
awsUseProfile,
14741486
vertexProjectId,
14751487
vertexRegion,
14761488
openAiBaseUrl,

src/shared/api.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,8 @@ export interface ApiHandlerOptions {
2929
awsSessionToken?: string
3030
awsRegion?: string
3131
awsUseCrossRegionInference?: boolean
32+
awsUseProfile?: boolean
33+
awsProfile?: string
3234
vertexProjectId?: string
3335
vertexRegion?: string
3436
openAiBaseUrl?: string

webview-ui/src/components/settings/ApiOptions.tsx

Lines changed: 62 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -445,30 +445,56 @@ const ApiOptions = ({ showModelOptions, apiErrorMessage, modelIdErrorMessage, is
445445
flexDirection: "column",
446446
gap: 5,
447447
}}>
448-
<VSCodeTextField
449-
value={apiConfiguration?.awsAccessKey || ""}
450-
style={{ width: "100%" }}
451-
type="password"
452-
onInput={handleInputChange("awsAccessKey")}
453-
placeholder="Enter Access Key...">
454-
<span style={{ fontWeight: 500 }}>AWS Access Key</span>
455-
</VSCodeTextField>
456-
<VSCodeTextField
457-
value={apiConfiguration?.awsSecretKey || ""}
458-
style={{ width: "100%" }}
459-
type="password"
460-
onInput={handleInputChange("awsSecretKey")}
461-
placeholder="Enter Secret Key...">
462-
<span style={{ fontWeight: 500 }}>AWS Secret Key</span>
463-
</VSCodeTextField>
464-
<VSCodeTextField
465-
value={apiConfiguration?.awsSessionToken || ""}
466-
style={{ width: "100%" }}
467-
type="password"
468-
onInput={handleInputChange("awsSessionToken")}
469-
placeholder="Enter Session Token...">
470-
<span style={{ fontWeight: 500 }}>AWS Session Token</span>
471-
</VSCodeTextField>
448+
<VSCodeRadioGroup
449+
value={apiConfiguration?.awsUseProfile ? "profile" : "credentials"}
450+
onChange={(e) => {
451+
const value = (e.target as HTMLInputElement)?.value
452+
const useProfile = value === "profile"
453+
setApiConfiguration({
454+
...apiConfiguration,
455+
awsUseProfile: useProfile,
456+
})
457+
}}>
458+
<VSCodeRadio value="credentials">AWS Credentials</VSCodeRadio>
459+
<VSCodeRadio value="profile">AWS Profile</VSCodeRadio>
460+
</VSCodeRadioGroup>
461+
462+
{apiConfiguration?.awsUseProfile ? (
463+
<VSCodeTextField
464+
value={apiConfiguration?.awsProfile || ""}
465+
style={{ width: "100%" }}
466+
onInput={handleInputChange("awsProfile")}
467+
placeholder="Enter profile name (default if empty)">
468+
<span style={{ fontWeight: 500 }}>AWS Profile Name</span>
469+
</VSCodeTextField>
470+
) : (
471+
<>
472+
<VSCodeTextField
473+
value={apiConfiguration?.awsAccessKey || ""}
474+
style={{ width: "100%" }}
475+
type="password"
476+
onInput={handleInputChange("awsAccessKey")}
477+
placeholder="Enter Access Key...">
478+
<span style={{ fontWeight: 500 }}>AWS Access Key</span>
479+
</VSCodeTextField>
480+
<VSCodeTextField
481+
value={apiConfiguration?.awsSecretKey || ""}
482+
style={{ width: "100%" }}
483+
type="password"
484+
onInput={handleInputChange("awsSecretKey")}
485+
placeholder="Enter Secret Key...">
486+
<span style={{ fontWeight: 500 }}>AWS Secret Key</span>
487+
</VSCodeTextField>
488+
<VSCodeTextField
489+
value={apiConfiguration?.awsSessionToken || ""}
490+
style={{ width: "100%" }}
491+
type="password"
492+
onInput={handleInputChange("awsSessionToken")}
493+
placeholder="Enter Session Token...">
494+
<span style={{ fontWeight: 500 }}>AWS Session Token</span>
495+
</VSCodeTextField>
496+
</>
497+
)}
472498
<DropdownContainer zIndex={DROPDOWN_Z_INDEX - 1} className="dropdown-container">
473499
<label htmlFor="aws-region-dropdown">
474500
<span style={{ fontWeight: 500 }}>AWS Region</span>
@@ -523,9 +549,18 @@ const ApiOptions = ({ showModelOptions, apiErrorMessage, modelIdErrorMessage, is
523549
marginTop: "5px",
524550
color: "var(--vscode-descriptionForeground)",
525551
}}>
526-
Authenticate by either providing the keys above or use the default AWS credential providers, i.e.
527-
~/.aws/credentials or environment variables. These credentials are only used locally to make API requests
528-
from this extension.
552+
{apiConfiguration?.awsUseProfile ? (
553+
<>
554+
Using AWS Profile credentials from ~/.aws/credentials. Leave profile name empty to use the default
555+
profile. These credentials are only used locally to make API requests from this extension.
556+
</>
557+
) : (
558+
<>
559+
Authenticate by either providing the keys above or use the default AWS credential providers, i.e.
560+
~/.aws/credentials or environment variables. These credentials are only used locally to make API
561+
requests from this extension.
562+
</>
563+
)}
529564
</p>
530565
</div>
531566
)}

0 commit comments

Comments
 (0)