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
92 changes: 86 additions & 6 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -62,12 +62,15 @@ agentcore # interactive TUI
│ │ ├── list # list API key credential providers
│ │ ├── update # update an API key credential provider
│ │ └── delete # delete an API key credential provider
│ └── oauth2-credential-provider
│ ├── create # create an OAuth2 credential provider
│ ├── get # get an OAuth2 credential provider
│ ├── list # list OAuth2 credential providers
│ ├── update # update an OAuth2 credential provider
│ └── delete # delete an OAuth2 credential provider
│ ├── oauth2-credential-provider
│ │ ├── create # create an OAuth2 credential provider
│ │ ├── get # get an OAuth2 credential provider
│ │ ├── list # list OAuth2 credential providers
│ │ ├── update # update an OAuth2 credential provider
│ │ └── delete # delete an OAuth2 credential provider
│ └── payment-credential-provider
│ ├── get # get a payment credential provider
│ └── list # list payment credential providers
├── runtime # inspect deployed AgentCore Runtimes
│ ├── get # fetch a Runtime by id
│ ├── list # list Runtimes (server-side paginated)
Expand Down Expand Up @@ -107,6 +110,20 @@ agentcore # interactive TUI
│ │ └── list # list Rules under a Gateway
│ └── policy
│ └── generate # generate Cedar for a Gateway from a prompt (TUI when run bare)
├── payment # inspect AgentCore Payments (command line only for now)
│ ├── manager
│ │ ├── get # get a payment manager by id
│ │ └── list # list payment managers (server-side paginated)
│ ├── connector # connectors under a payment manager
│ │ ├── get # get a connector (shows the Quick Create authorization URL while pending)
│ │ └── list # list a manager's connectors
│ ├── session # budget-limited payment contexts (data plane)
│ │ ├── get
│ │ └── list
│ └── instrument # embedded crypto wallets (data plane)
│ ├── get
│ ├── list
│ └── balance # read token balance on an explicit chain (default token: USDC)
├── eval # evaluate and optimize AgentCore agents
│ └── evaluator # manage AgentCore evaluators
│ ├── llm-as-a-judge # LLM-as-a-Judge evaluators
Expand Down Expand Up @@ -221,6 +238,69 @@ When the project declares exactly one Runtime, `--name` may be omitted. Use the
imperative `agentcore runtime traces` commands when addressing a Runtime by
physical ID or working outside a project.

### Inspect AgentCore Payments

The `payment` commands call the Payments control and data planes directly, with
no project involved. This command family currently provides read-only inspection
of existing managers, connectors, sessions, instruments, and payment credential
providers. It does not create IAM roles or change provider credentials.

Choose a manager from `manager list` and use its `paymentManagerId` below:

```bash
agentcore payment manager list --json
MANAGER_ID='<paymentManagerId from manager list>'
agentcore payment manager get --id "$MANAGER_ID"
agentcore payment connector list --manager-id "$MANAGER_ID"
```

`--user-id` is the application user ID used when the session or instrument was
created, not an IAM username or AWS profile. Session and instrument reads require
it with IAM authentication; their lists return that user's resources, not every
user's resources under the manager.

```bash
USER_ID='alice' # Use the application user ID associated with the resources.
agentcore payment session list --manager-id "$MANAGER_ID" --user-id "$USER_ID"
agentcore payment instrument list --manager-id "$MANAGER_ID" --user-id "$USER_ID"

# Use paymentInstrumentId and paymentConnectorId from the same instrument list item.
INSTRUMENT_ID='<paymentInstrumentId>'
CONNECTOR_ID='<paymentConnectorId>'
agentcore payment instrument get --manager-id "$MANAGER_ID" \
--instrument-id "$INSTRUMENT_ID" --user-id "$USER_ID"
agentcore payment instrument balance --manager-id "$MANAGER_ID" \
--connector-id "$CONNECTOR_ID" --instrument-id "$INSTRUMENT_ID" \
--user-id "$USER_ID" --chain BASE_SEPOLIA
```

To inspect connector or credential provider metadata:

```bash
agentcore payment connector get --manager-id "$MANAGER_ID" --connector-id "$CONNECTOR_ID"
agentcore identity payment-credential-provider list --json
agentcore identity payment-credential-provider get --name '<provider name>'
```

The optional `--agent-name` on session and instrument reads labels the request for
observability. It does not select an AgentCore agent or filter the results.

`instrument get` returns instrument metadata without querying balances. `balance`
requires an explicit chain and defaults to `--token USDC`; wallet network families
such as ETHEREUM do not identify whether to query mainnet or a testnet. The JSON
response retains the raw atomic amount string and decimals. A service error is
reported as an error, never converted to a zero balance.

Data-plane commands work against managers that use the `AWS_IAM` authorizer.
The CLI resolves `--manager-id` through `GetPaymentManager` in the configured
region, then supplies the returned ARN to the data-plane API. Callers need
`bedrock-agentcore:GetPaymentManager` as well as the relevant data-plane action.
Region resolution follows the other imperative commands: `--region`, environment
variables, the active AWS profile, then the CLI default.
A `CUSTOM_JWT` manager accepts only bearer tokens on its data plane, which
these commands do not send yet; the CLI reports that limitation before calling
the data plane.

### Examples

```bash
Expand Down
2 changes: 1 addition & 1 deletion src/components/CliOnlyScreen.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ describe("menus list command-line-only subcommands below a divider", () => {
await waitForText(r.lastFrame, "command line only");
expect(menuEntries(r.lastFrame()!)).toEqual({
screens: ["project", "harness", "identity", "runtime", "memory", "gateway", "eval"],
cliOnly: ["feedback", "config", "update"],
cliOnly: ["payment", "feedback", "config", "update"],
});
r.unmount();
});
Expand Down
12 changes: 12 additions & 0 deletions src/core/identity.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import {
GetPaymentCredentialProviderCommand,
ListApiKeyCredentialProvidersCommand,
ListOauth2CredentialProvidersCommand,
ListPaymentCredentialProvidersCommand,
UpdateApiKeyCredentialProviderCommand,
UpdateOauth2CredentialProviderCommand,
UpdatePaymentCredentialProviderCommand,
Expand All @@ -26,6 +27,7 @@ import {
type CreatePaymentCredentialProviderResponse,
type DeletePaymentCredentialProviderResponse,
type GetPaymentCredentialProviderResponse,
type ListPaymentCredentialProvidersResponse,
type UpdatePaymentCredentialProviderResponse,
} from "@aws-sdk/client-bedrock-agentcore-control";
import type {
Expand Down Expand Up @@ -155,6 +157,16 @@ export class IdentityClient implements CoreIdentityClient {
.send(new GetPaymentCredentialProviderCommand({ name }));
}

async listPaymentCredentialProviders(
nextToken: string | undefined,
maxResults: number | undefined,
options: CoreOptions,
): Promise<ListPaymentCredentialProvidersResponse> {
return this.clients
.control(toClientConfig(options))
.send(new ListPaymentCredentialProvidersCommand({ nextToken, maxResults }));
}

async updatePaymentCredentialProvider(
input: UpdatePaymentCredentialProviderInput,
options: CoreOptions,
Expand Down
3 changes: 3 additions & 0 deletions src/core/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import { GatewayClient } from "./gateway";
import { HarnessClient } from "./harness";
import { IdentityClient } from "./identity";
import { MemoryClient } from "./memory";
import { PaymentClient } from "./payment";
import { PolicyClient } from "./policy";
import { CloudWatchClient, ObservabilityClient } from "./observability/index";
import { RuntimeClient } from "./runtime";
Expand Down Expand Up @@ -77,6 +78,7 @@ export class CoreClient implements AwsClients {
readonly eval: EvalClient;
readonly observability: ObservabilityClient;
readonly policy: PolicyClient;
readonly payment: PaymentClient;

readonly projectManager: ProjectManager;
readonly bedrockAgentImporter: CoreBedrockAgentImporter;
Expand All @@ -99,6 +101,7 @@ export class CoreClient implements AwsClients {
);
this.gateway = new GatewayClient(this, fetch, this.logger.child({ module: "gateway" }));
this.policy = new PolicyClient(this, this.logger.child({ module: "policy" }));
this.payment = new PaymentClient(this);
// EvalClient shares the injected fetch: dataset content is served from a
// presigned S3 URL, outside the SDK seam the other operations use. The logger
// is used for batch-evaluation result-log diagnostics.
Expand Down
165 changes: 165 additions & 0 deletions src/core/payment.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,165 @@
import {
GetPaymentConnectorCommand,
GetPaymentManagerCommand,
ListPaymentConnectorsCommand,
ListPaymentManagersCommand,
type GetPaymentConnectorResponse,
type GetPaymentManagerResponse,
type ListPaymentConnectorsResponse,
type ListPaymentManagersResponse,
} from "@aws-sdk/client-bedrock-agentcore-control";
import {
GetPaymentInstrumentBalanceCommand,
GetPaymentInstrumentCommand,
GetPaymentSessionCommand,
ListPaymentInstrumentsCommand,
ListPaymentSessionsCommand,
type BedrockAgentCoreClient,
type GetPaymentInstrumentResponse,
type GetPaymentInstrumentBalanceResponse,
type GetPaymentSessionResponse,
type ListPaymentInstrumentsResponse,
type ListPaymentSessionsResponse,
} from "@aws-sdk/client-bedrock-agentcore";
import { InputValidationError, MalformedServiceResponseError } from "../errors";
import type {
CorePaymentClient,
GetPaymentSessionInput,
ListPaymentSessionsInput,
GetPaymentInstrumentInput,
GetPaymentInstrumentBalanceInput,
ListPaymentInstrumentsInput,
} from "../handlers/payment/types";
import type { AwsClients, CoreOptions } from "./types";
import { toClientConfig } from "./utils";

// PaymentClient implements the payment-facing operations on top of the shared
// AWS clients provided by CoreClient. Managers and connectors live on the control
// plane; sessions and instruments on the data plane.
export class PaymentClient implements CorePaymentClient {
constructor(private readonly clients: Pick<AwsClients, "control" | "data">) {}

// ─── payment managers ───────────────────────────────────────────────────────

async getPaymentManager(id: string, options: CoreOptions): Promise<GetPaymentManagerResponse> {
return this.clients
.control(toClientConfig(options))
.send(new GetPaymentManagerCommand({ paymentManagerId: id }));
}

async listPaymentManagers(
nextToken: string | undefined,
maxResults: number | undefined,
options: CoreOptions,
): Promise<ListPaymentManagersResponse> {
return this.clients
.control(toClientConfig(options))
.send(new ListPaymentManagersCommand({ nextToken, maxResults }));
}

// ─── payment connectors ─────────────────────────────────────────────────────

async getPaymentConnector(
managerId: string,
connectorId: string,
options: CoreOptions,
): Promise<GetPaymentConnectorResponse> {
return this.clients.control(toClientConfig(options)).send(
new GetPaymentConnectorCommand({
paymentManagerId: managerId,
paymentConnectorId: connectorId,
}),
);
}

async listPaymentConnectors(
managerId: string,
nextToken: string | undefined,
maxResults: number | undefined,
options: CoreOptions,
): Promise<ListPaymentConnectorsResponse> {
return this.clients
.control(toClientConfig(options))
.send(
new ListPaymentConnectorsCommand({ paymentManagerId: managerId, nextToken, maxResults }),
);
}

// ─── payment sessions (data plane) ──────────────────────────────────────────

async getPaymentSession(
input: GetPaymentSessionInput,
options: CoreOptions,
): Promise<GetPaymentSessionResponse> {
const { managerId, ...request } = input;
return this.withPaymentManagerArn(managerId, options, (data, paymentManagerArn) =>
data.send(new GetPaymentSessionCommand({ paymentManagerArn, ...request })),
);
}

async listPaymentSessions(
input: ListPaymentSessionsInput,
options: CoreOptions,
): Promise<ListPaymentSessionsResponse> {
const { managerId, ...request } = input;
return this.withPaymentManagerArn(managerId, options, (data, paymentManagerArn) =>
data.send(new ListPaymentSessionsCommand({ paymentManagerArn, ...request })),
);
}

// ─── payment instruments (data plane) ───────────────────────────────────────

async getPaymentInstrument(
input: GetPaymentInstrumentInput,
options: CoreOptions,
): Promise<GetPaymentInstrumentResponse> {
const { managerId, ...request } = input;
return this.withPaymentManagerArn(managerId, options, (data, paymentManagerArn) =>
data.send(new GetPaymentInstrumentCommand({ paymentManagerArn, ...request })),
);
}

async getPaymentInstrumentBalance(
input: GetPaymentInstrumentBalanceInput,
options: CoreOptions,
): Promise<GetPaymentInstrumentBalanceResponse> {
const { managerId, ...request } = input;
return this.withPaymentManagerArn(managerId, options, (data, paymentManagerArn) =>
data.send(new GetPaymentInstrumentBalanceCommand({ paymentManagerArn, ...request })),
);
}

async listPaymentInstruments(
input: ListPaymentInstrumentsInput,
options: CoreOptions,
): Promise<ListPaymentInstrumentsResponse> {
const { managerId, ...request } = input;
return this.withPaymentManagerArn(managerId, options, (data, paymentManagerArn) =>
data.send(new ListPaymentInstrumentsCommand({ paymentManagerArn, ...request })),
);
}

// ─── helpers ────────────────────────────────────────────────────────────────

private async withPaymentManagerArn<T>(
managerId: string,
options: CoreOptions,
send: (data: BedrockAgentCoreClient, paymentManagerArn: string) => Promise<T>,
): Promise<T> {
if (managerId.startsWith("arn:")) {
throw new InputValidationError("use a payment manager ID, not an ARN");
}
const manager = await this.getPaymentManager(managerId, options);
if (manager.authorizerType === "CUSTOM_JWT") {
throw new InputValidationError(
`payment manager "${managerId}" uses the CUSTOM_JWT authorizer, so its data plane accepts only bearer tokens; ` +
"this CLI does not support bearer tokens for payment commands yet. " +
"Use an AWS_IAM payment manager, or call the API directly with a JWT.",
);
}
if (!manager.paymentManagerArn) {
throw new MalformedServiceResponseError(`payment manager "${managerId}" returned no ARN`);
}
return send(this.clients.data(toClientConfig(options)), manager.paymentManagerArn);
}
}
1 change: 1 addition & 0 deletions src/handlers/identity/identity.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,7 @@ describe("identity command hierarchy", () => {
expect(identity?.children().map((child) => child.name())).toEqual([
"api-key-credential-provider",
"oauth2-credential-provider",
"payment-credential-provider",
]);
expect(
identity
Expand Down
4 changes: 3 additions & 1 deletion src/handlers/identity/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,13 @@ import type { AppIO } from "../../io";
import type { Core } from "../types";
import { createApiKeyCredentialProviderHandler } from "./api-key-credential-provider";
import { createOauth2CredentialProviderHandler } from "./oauth2-credential-provider";
import { createPaymentCredentialProviderHandler } from "./payment-credential-provider";

export function createIdentityHandler(core: Core, io: AppIO): Router {
return new Router("identity", "manage AgentCore Identity resources")
.use(withTuiOnEmptyFlagsAndArgs(core, io))
.default(renderTui(core, io))
.handler(createApiKeyCredentialProviderHandler(core, io))
.handler(createOauth2CredentialProviderHandler(core, io));
.handler(createOauth2CredentialProviderHandler(core, io))
.handler(createPaymentCredentialProviderHandler(core, io));
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
{
"name": "agentcore-cli-payment-fixture",
"credentialProviderArn": "arn:aws:bedrock-agentcore:us-west-2:603141041947:token-vault/default/paymentcredentialprovider/agentcore-cli-payment-fixture",
"credentialProviderVendor": "CoinbaseCDP",
"providerConfigurationOutput": {
"coinbaseCdpConfiguration": {
"apiKeyId": "agentcore-cli-fixture-key",
"apiKeySecretArn": {
"secretArn": "arn:aws:secretsmanager:us-west-2:603141041947:secret:bedrock-agentcore-identity!default/payment/coinbasecdp/agentcore-cli-payment-fixture-541ff54c/apikey-eNjrnd"
},
"walletSecretArn": {
"secretArn": "arn:aws:secretsmanager:us-west-2:603141041947:secret:bedrock-agentcore-identity!default/payment/coinbasecdp/agentcore-cli-payment-fixture-541ff54c/wallet-DFZFS6"
},
"apiKeySecretSource": "MANAGED",
"walletSecretSource": "MANAGED"
}
},
"createdTime": {
"$date": "2026-09-08T20:16:25.544Z"
},
"lastUpdatedTime": {
"$date": "2026-09-08T20:16:25.544Z"
}
}
Loading
Loading