Skip to content
Open
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
35 changes: 24 additions & 11 deletions src/core/identity.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,21 +10,30 @@ import {
type ListApiKeyCredentialProvidersResponse,
type UpdateApiKeyCredentialProviderResponse,
} from "@aws-sdk/client-bedrock-agentcore-control";
import type { CoreIdentityClient } from "../handlers/identity/types";
import type {
CoreIdentityClient,
CreateApiKeyCredentialProviderInput,
UpdateApiKeyCredentialProviderInput,
} from "../handlers/identity/types";
import type { AwsClients, CoreOptions } from "./types";
import { toClientConfig } from "./utils";

export class IdentityClient implements CoreIdentityClient {
constructor(private readonly clients: AwsClients) {}

async createApiKeyCredentialProvider(
name: string,
apiKey: string,
input: CreateApiKeyCredentialProviderInput,
options: CoreOptions,
): Promise<CreateApiKeyCredentialProviderResponse> {
return this.clients
.control(toClientConfig(options))
.send(new CreateApiKeyCredentialProviderCommand({ name, apiKey }));
return this.clients.control(toClientConfig(options)).send(
new CreateApiKeyCredentialProviderCommand({
name: input.name,
apiKey: input.apiKey,
apiKeySecretConfig: input.apiKeySecretConfig,
apiKeySecretSource: input.apiKeySecretSource,
tags: input.tags,
}),
);
}

async getApiKeyCredentialProvider(
Expand All @@ -47,13 +56,17 @@ export class IdentityClient implements CoreIdentityClient {
}

async updateApiKeyCredentialProvider(
name: string,
apiKey: string,
input: UpdateApiKeyCredentialProviderInput,
options: CoreOptions,
): Promise<UpdateApiKeyCredentialProviderResponse> {
return this.clients
.control(toClientConfig(options))
.send(new UpdateApiKeyCredentialProviderCommand({ name, apiKey }));
return this.clients.control(toClientConfig(options)).send(
new UpdateApiKeyCredentialProviderCommand({
name: input.name,
apiKey: input.apiKey,
apiKeySecretConfig: input.apiKeySecretConfig,
apiKeySecretSource: input.apiKeySecretSource,
}),
);
}

async deleteApiKeyCredentialProvider(
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
{
"apiKeySecretArn": {
"secretArn": "arn:aws:secretsmanager:us-west-2:685197708687:secret:bedrock-agentcore-identity!default/apikey/agentcore-cli-identity-fixture-ceac1e1d-2BRhkE"
},
"name": "agentcore-cli-identity-fixture",
"credentialProviderArn": "arn:aws:bedrock-agentcore:us-west-2:685197708687:token-vault/default/apikeycredentialprovider/agentcore-cli-identity-fixture",
"apiKeySecretSource": "MANAGED"
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
{
"apiKeySecretArn": {
"secretArn": "arn:aws:secretsmanager:us-west-2:685197708687:secret:bedrock-agentcore-identity!default/apikey/agentcore-cli-identity-fixture-2-56db9332-Jv9u5N"
},
"name": "agentcore-cli-identity-fixture-2",
"credentialProviderArn": "arn:aws:bedrock-agentcore:us-west-2:685197708687:token-vault/default/apikeycredentialprovider/agentcore-cli-identity-fixture-2",
"apiKeySecretSource": "MANAGED"
}
Original file line number Diff line number Diff line change
@@ -1,14 +1,14 @@
{
"apiKeySecretArn": {
"secretArn": "arn:aws:secretsmanager:us-west-2:685197708687:secret:bedrock-agentcore-identity!default/apikey/agentcore-cli-identity-fixture-34b3f8a9-cHh41n"
"secretArn": "arn:aws:secretsmanager:us-west-2:685197708687:secret:bedrock-agentcore-identity!default/apikey/agentcore-cli-identity-fixture-ceac1e1d-2BRhkE"
},
"name": "agentcore-cli-identity-fixture",
"credentialProviderArn": "arn:aws:bedrock-agentcore:us-west-2:685197708687:token-vault/default/apikeycredentialprovider/agentcore-cli-identity-fixture",
"createdTime": {
"$date": "2026-07-23T16:31:54.009Z"
"$date": "2026-07-23T19:31:48.713Z"
},
"lastUpdatedTime": {
"$date": "2026-07-23T16:31:54.009Z"
"$date": "2026-07-23T19:31:48.713Z"
},
"apiKeySecretSource": "MANAGED"
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,20 +4,20 @@
"name": "agentcore-cli-identity-fixture",
"credentialProviderArn": "arn:aws:bedrock-agentcore:us-west-2:685197708687:token-vault/default/apikeycredentialprovider/agentcore-cli-identity-fixture",
"createdTime": {
"$date": "2026-07-23T16:31:54.009Z"
"$date": "2026-07-23T19:31:48.713Z"
},
"lastUpdatedTime": {
"$date": "2026-07-23T16:31:54.009Z"
"$date": "2026-07-23T19:31:48.713Z"
}
},
{
"name": "agentcore-cli-identity-fixture-2",
"credentialProviderArn": "arn:aws:bedrock-agentcore:us-west-2:685197708687:token-vault/default/apikeycredentialprovider/agentcore-cli-identity-fixture-2",
"createdTime": {
"$date": "2026-07-23T16:31:54.309Z"
"$date": "2026-07-23T19:31:49.014Z"
},
"lastUpdatedTime": {
"$date": "2026-07-23T16:31:54.309Z"
"$date": "2026-07-23T19:31:49.014Z"
}
}
]
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,10 @@
"name": "agentcore-cli-identity-fixture-2",
"credentialProviderArn": "arn:aws:bedrock-agentcore:us-west-2:685197708687:token-vault/default/apikeycredentialprovider/agentcore-cli-identity-fixture-2",
"createdTime": {
"$date": "2026-07-23T16:31:54.309Z"
"$date": "2026-07-23T19:31:49.014Z"
},
"lastUpdatedTime": {
"$date": "2026-07-23T16:31:54.309Z"
"$date": "2026-07-23T19:31:49.014Z"
}
}
]
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,10 @@
"name": "agentcore-cli-identity-fixture",
"credentialProviderArn": "arn:aws:bedrock-agentcore:us-west-2:685197708687:token-vault/default/apikeycredentialprovider/agentcore-cli-identity-fixture",
"createdTime": {
"$date": "2026-07-23T16:31:54.009Z"
"$date": "2026-07-23T19:31:48.713Z"
},
"lastUpdatedTime": {
"$date": "2026-07-23T16:31:54.009Z"
"$date": "2026-07-23T19:31:48.713Z"
}
}
],
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
{
"apiKeySecretArn": {
"secretArn": "arn:aws:secretsmanager:us-west-2:685197708687:secret:bedrock-agentcore-identity!default/apikey/agentcore-cli-identity-fixture-ceac1e1d-2BRhkE"
},
"name": "agentcore-cli-identity-fixture",
"credentialProviderArn": "arn:aws:bedrock-agentcore:us-west-2:685197708687:token-vault/default/apikeycredentialprovider/agentcore-cli-identity-fixture",
"createdTime": {
"$date": "2026-07-23T19:31:48.713Z"
},
"lastUpdatedTime": {
"$date": "2026-07-23T19:31:48.713Z"
},
"apiKeySecretSource": "MANAGED"
}
2 changes: 1 addition & 1 deletion src/handlers/identity/__fixtures__/create-2.golden.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"apiKeySecretArn": {
"secretArn": "arn:aws:secretsmanager:us-west-2:685197708687:secret:bedrock-agentcore-identity!default/apikey/agentcore-cli-identity-fixture-2-29a3e5b6-qiRJxb"
"secretArn": "arn:aws:secretsmanager:us-west-2:685197708687:secret:bedrock-agentcore-identity!default/apikey/agentcore-cli-identity-fixture-2-56db9332-Jv9u5N"
},
"name": "agentcore-cli-identity-fixture-2",
"credentialProviderArn": "arn:aws:bedrock-agentcore:us-west-2:685197708687:token-vault/default/apikeycredentialprovider/agentcore-cli-identity-fixture-2",
Expand Down
2 changes: 1 addition & 1 deletion src/handlers/identity/__fixtures__/create.golden.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"apiKeySecretArn": {
"secretArn": "arn:aws:secretsmanager:us-west-2:685197708687:secret:bedrock-agentcore-identity!default/apikey/agentcore-cli-identity-fixture-34b3f8a9-cHh41n"
"secretArn": "arn:aws:secretsmanager:us-west-2:685197708687:secret:bedrock-agentcore-identity!default/apikey/agentcore-cli-identity-fixture-ceac1e1d-2BRhkE"
},
"name": "agentcore-cli-identity-fixture",
"credentialProviderArn": "arn:aws:bedrock-agentcore:us-west-2:685197708687:token-vault/default/apikeycredentialprovider/agentcore-cli-identity-fixture",
Expand Down
6 changes: 3 additions & 3 deletions src/handlers/identity/__fixtures__/get.golden.json
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
{
"apiKeySecretArn": {
"secretArn": "arn:aws:secretsmanager:us-west-2:685197708687:secret:bedrock-agentcore-identity!default/apikey/agentcore-cli-identity-fixture-34b3f8a9-cHh41n"
"secretArn": "arn:aws:secretsmanager:us-west-2:685197708687:secret:bedrock-agentcore-identity!default/apikey/agentcore-cli-identity-fixture-ceac1e1d-2BRhkE"
},
"name": "agentcore-cli-identity-fixture",
"credentialProviderArn": "arn:aws:bedrock-agentcore:us-west-2:685197708687:token-vault/default/apikeycredentialprovider/agentcore-cli-identity-fixture",
"createdTime": "2026-07-23T16:31:54.009Z",
"lastUpdatedTime": "2026-07-23T16:31:54.009Z",
"createdTime": "2026-07-23T19:31:48.713Z",
"lastUpdatedTime": "2026-07-23T19:31:48.713Z",
"apiKeySecretSource": "MANAGED"
}
4 changes: 2 additions & 2 deletions src/handlers/identity/__fixtures__/list-page-1.golden.json
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,8 @@
{
"name": "agentcore-cli-identity-fixture",
"credentialProviderArn": "arn:aws:bedrock-agentcore:us-west-2:685197708687:token-vault/default/apikeycredentialprovider/agentcore-cli-identity-fixture",
"createdTime": "2026-07-23T16:31:54.009Z",
"lastUpdatedTime": "2026-07-23T16:31:54.009Z"
"createdTime": "2026-07-23T19:31:48.713Z",
"lastUpdatedTime": "2026-07-23T19:31:48.713Z"
}
],
"nextToken": "eyJBY2NvdW50QW5kVHlwZUdzaVBrIjp7InZhbHVlU3RyIjoiNjg1MTk3NzA4Njg3I0FQSV9LRVkiLCJhdHRyaWJ1dGVUeXBlIjoiUyJ9LCJTSyI6eyJ2YWx1ZVN0ciI6ImFnZW50Y29yZS1jbGktaWRlbnRpdHktZml4dHVyZSIsImF0dHJpYnV0ZVR5cGUiOiJTIn0sIlBLIjp7InZhbHVlU3RyIjoiQVBJX0tFWV9DUkVEX1BST1ZJREVSIzY4NTE5NzcwODY4NyNkZWZhdWx0I2FnZW50Y29yZS1jbGktaWRlbnRpdHktZml4dHVyZSIsImF0dHJpYnV0ZVR5cGUiOiJTIn19"
Expand Down
4 changes: 2 additions & 2 deletions src/handlers/identity/__fixtures__/list-page-2.golden.json
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,8 @@
{
"name": "agentcore-cli-identity-fixture-2",
"credentialProviderArn": "arn:aws:bedrock-agentcore:us-west-2:685197708687:token-vault/default/apikeycredentialprovider/agentcore-cli-identity-fixture-2",
"createdTime": "2026-07-23T16:31:54.309Z",
"lastUpdatedTime": "2026-07-23T16:31:54.309Z"
"createdTime": "2026-07-23T19:31:49.014Z",
"lastUpdatedTime": "2026-07-23T19:31:49.014Z"
}
]
}
8 changes: 4 additions & 4 deletions src/handlers/identity/__fixtures__/list.golden.json
Original file line number Diff line number Diff line change
Expand Up @@ -3,14 +3,14 @@
{
"name": "agentcore-cli-identity-fixture",
"credentialProviderArn": "arn:aws:bedrock-agentcore:us-west-2:685197708687:token-vault/default/apikeycredentialprovider/agentcore-cli-identity-fixture",
"createdTime": "2026-07-23T16:31:54.009Z",
"lastUpdatedTime": "2026-07-23T16:31:54.009Z"
"createdTime": "2026-07-23T19:31:48.713Z",
"lastUpdatedTime": "2026-07-23T19:31:48.713Z"
},
{
"name": "agentcore-cli-identity-fixture-2",
"credentialProviderArn": "arn:aws:bedrock-agentcore:us-west-2:685197708687:token-vault/default/apikeycredentialprovider/agentcore-cli-identity-fixture-2",
"createdTime": "2026-07-23T16:31:54.309Z",
"lastUpdatedTime": "2026-07-23T16:31:54.309Z"
"createdTime": "2026-07-23T19:31:49.014Z",
"lastUpdatedTime": "2026-07-23T19:31:49.014Z"
}
]
}
6 changes: 3 additions & 3 deletions src/handlers/identity/__fixtures__/update.golden.json
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
{
"apiKeySecretArn": {
"secretArn": "arn:aws:secretsmanager:us-west-2:685197708687:secret:bedrock-agentcore-identity!default/apikey/agentcore-cli-identity-fixture-34b3f8a9-cHh41n"
"secretArn": "arn:aws:secretsmanager:us-west-2:685197708687:secret:bedrock-agentcore-identity!default/apikey/agentcore-cli-identity-fixture-ceac1e1d-2BRhkE"
},
"name": "agentcore-cli-identity-fixture",
"credentialProviderArn": "arn:aws:bedrock-agentcore:us-west-2:685197708687:token-vault/default/apikeycredentialprovider/agentcore-cli-identity-fixture",
"createdTime": "2026-07-23T16:31:54.009Z",
"lastUpdatedTime": "2026-07-23T16:31:54.009Z",
"createdTime": "2026-07-23T19:31:48.713Z",
"lastUpdatedTime": "2026-07-23T19:31:48.713Z",
"apiKeySecretSource": "MANAGED"
}
69 changes: 55 additions & 14 deletions src/handlers/identity/api-key-credential-provider/create/index.tsx
Original file line number Diff line number Diff line change
@@ -1,33 +1,74 @@
import z from "zod";
import { createHandler, flag } from "../../../../router";
import { JsonRendererKey } from "../../../../tui";
import type { Core } from "../../../types";
import type { AppIO, Core } from "../../../types";
import { coreOptsFromCtx } from "../../../utils";
import { readSourceText } from "../../../source";
import { parseJsonFlag } from "../../../utils";

export const createCreateApiKeyCredentialProviderHandler = (core: Core) =>
export const createCreateApiKeyCredentialProviderHandler = (core: Core, io: AppIO) =>
createHandler({
name: "create",
description: "create an API key credential provider",
flags: [
flag("name", "the name of the API key credential provider", z.string().optional()),
flag("api-key", "the API key value", z.string().optional()),
flag("api-key", "the API key value (inline, file://path, or -)", z.string().optional(), {
sensitive: true,
}),
flag("api-key-secret-arn", "existing Secrets Manager secret ARN", z.string().optional()),

@aidandaly24 aidandaly24 Jul 23, 2026

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In the doc discussion, the preferred external-secret representation was one structured-reference flag rather than separate ARN and JSON-key flags. Could we use something like --api-key-secret-reference '{"secretId":"...","jsonKey":"..."}' for create/update, mutually exclusive with --api-key? We should also have a successful external-reference request-mapping test, I think the current tests only cover invalid combinations.

Update should first retrieve the provider and preserve its existing apiKeySecretSource. A MANAGED provider should accept only --api-key, while an EXTERNAL provider should accept only the structured secret reference. I think we should reject mismatched input locally and test both directions instead of deriving the source mode from the supplied flags.

flag(
"api-key-secret-json-key",
"JSON key containing the API key in the secret",
z.string().optional(),
),
flag("tags", "tags as key=value (repeatable) or JSON object", z.string().optional()),

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This advertises repeatable key=value tags or a JSON object, but z.string() plus parseJsonFlag only accepts one JSON value. For example, --tags env=prod currently fails as invalid JSON. Could we implement and test both documented forms, including validating that the JSON form is a string map?

],
handle: async (ctx, flags) => {
if (!flags.name) {
throw new TypeError("required option '--name <name>' not specified");
}
if (!flags["api-key"]) {
throw new TypeError("required option '--api-key <api-key>' not specified");
}

ctx
.require(JsonRendererKey)
.renderJson(
await core.identity.createApiKeyCredentialProvider(
flags.name,
flags["api-key"],
coreOptsFromCtx(ctx),
),
const hasApiKey = flags["api-key"] !== undefined;
const hasSecretArn = flags["api-key-secret-arn"] !== undefined;
const hasSecretJsonKey = flags["api-key-secret-json-key"] !== undefined;

if (hasApiKey && (hasSecretArn || hasSecretJsonKey)) {
throw new TypeError(
"--api-key and --api-key-secret-arn/--api-key-secret-json-key are mutually exclusive",
);
}
if (!hasApiKey && !hasSecretArn) {
throw new TypeError(
"either --api-key or --api-key-secret-arn and --api-key-secret-json-key are required",
);
}
if (hasSecretArn !== hasSecretJsonKey) {
throw new TypeError(
"--api-key-secret-arn and --api-key-secret-json-key must be specified together",
);
}

const apiKey = hasApiKey ? await readSourceText(flags["api-key"]!, io.stdin) : undefined;

const apiKeySecretConfig = hasSecretArn
? { secretId: flags["api-key-secret-arn"]!, jsonKey: flags["api-key-secret-json-key"]! }
: undefined;

const apiKeySecretSource = hasApiKey ? "MANAGED" : "EXTERNAL";

const tags = parseJsonFlag<Record<string, string>>("tags", flags.tags);

ctx.require(JsonRendererKey).renderJson(
await core.identity.createApiKeyCredentialProvider(
{
name: flags.name,
apiKey,
apiKeySecretConfig,
apiKeySecretSource: apiKeySecretSource as any,
tags,
},
coreOptsFromCtx(ctx),
),
);
},
});
4 changes: 2 additions & 2 deletions src/handlers/identity/api-key-credential-provider/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,9 @@ import { createUpdateApiKeyCredentialProviderHandler } from "./update";
export function createApiKeyCredentialProviderHandler(core: Core, io: AppIO): Router {
return new Router("api-key-credential-provider", "manage API key credential providers")
.default(createHelpDefault(io))
.handler(createCreateApiKeyCredentialProviderHandler(core))
.handler(createCreateApiKeyCredentialProviderHandler(core, io))
.handler(createGetApiKeyCredentialProviderHandler(core))
.handler(createListApiKeyCredentialProvidersHandler(core))
.handler(createUpdateApiKeyCredentialProviderHandler(core))
.handler(createUpdateApiKeyCredentialProviderHandler(core, io))
.handler(createDeleteApiKeyCredentialProviderHandler(core));
}
Loading
Loading