From eef56cc1663c51e2ca5eaf181ff407ee44e7105c Mon Sep 17 00:00:00 2001 From: Nicolas Borges Date: Tue, 15 Sep 2026 10:42:32 -0400 Subject: [PATCH 1/3] fix: use project target credentials for invoke --- src/core/project/backends/cdk.test.ts | 3 ++ src/core/project/backends/cdk.ts | 2 +- src/core/project/manager.tsx | 5 +- src/handlers/keys.tsx | 6 ++- src/handlers/project/invoke/harness.tsx | 6 ++- src/handlers/project/invoke/index.test.tsx | 20 ++++++-- .../project/invoke/invoke.screen.test.tsx | 49 +++++++++++++++++-- src/handlers/project/invoke/runtime.tsx | 6 ++- src/handlers/project/invoke/screen.tsx | 6 ++- src/handlers/project/types.ts | 5 +- src/handlers/utils.tsx | 9 ++-- 11 files changed, 95 insertions(+), 22 deletions(-) diff --git a/src/core/project/backends/cdk.test.ts b/src/core/project/backends/cdk.test.ts index cb48c8f0c..937ed5799 100644 --- a/src/core/project/backends/cdk.test.ts +++ b/src/core/project/backends/cdk.test.ts @@ -861,12 +861,14 @@ describe("CdkBackend.resolveDeployedResources", () => { name: "checkout_agent", id: "checkout_agent-AbCdEf1234", target: TARGET, + credentials: subject.credentials, }, { resourceType: "harness", name: "support_agent", id: "support_agent-AbCdEf1234", target: TARGET, + credentials: subject.credentials, }, ]); expect(subject.stackReads).toHaveLength(1); @@ -903,6 +905,7 @@ describe("CdkBackend.resolveDeployedResources", () => { name: "support", id: "support-AbCdEf1234", target: TARGET, + credentials: subject.credentials, }, ]); expect(subject.stackReads[0]?.stackName).toBe("AgentCore-example-default"); diff --git a/src/core/project/backends/cdk.ts b/src/core/project/backends/cdk.ts index d7bb9bda0..ddeb9617d 100644 --- a/src/core/project/backends/cdk.ts +++ b/src/core/project/backends/cdk.ts @@ -431,7 +431,7 @@ export class CdkBackend implements ProjectBackend { ]; return resources.flatMap((resource) => { const id = findDeployedResourceId(stack, resource); - return id ? [{ ...resource, id, target }] : []; + return id ? [{ ...resource, id, target, credentials }] : []; }); } diff --git a/src/core/project/manager.tsx b/src/core/project/manager.tsx index bda377b96..2abf12233 100644 --- a/src/core/project/manager.tsx +++ b/src/core/project/manager.tsx @@ -974,8 +974,9 @@ export class FsProjectManager implements ProjectManager { ({ resourceType, name }) => resourceType === input.resourceType && name === input.name, ); // The declared target wins over the copy on the item: the manager resolved it - // from aws-targets.json, and both invoke handlers pin the AWS region off this - // value, so trusting a backend's echo would let it redirect the call. + // from aws-targets.json, and both invoke handlers pin the AWS region from this + // value while reusing the backend's verified credentials. Trusting a backend's + // target echo would let it redirect the call. if (resource) return { ...resource, target: resolved.target }; const label = input.resourceType === "runtime" ? "Runtime" : "Harness"; diff --git a/src/handlers/keys.tsx b/src/handlers/keys.tsx index 14fc24a12..91a288254 100644 --- a/src/handlers/keys.tsx +++ b/src/handlers/keys.tsx @@ -1,5 +1,6 @@ import z from "zod"; -import { globalFlag } from "../router"; +import type { AwsCredentials } from "../core/types"; +import { contextKey, globalFlag } from "../router"; // These keys are group-level flags declared on the root router. Because a // GlobalFlag is also a typed ContextKey, handlers read its validated value back @@ -18,3 +19,6 @@ export const EndpointKey = globalFlag( "endpoint URL override", z.string().optional(), ); + +/** Explicit credentials pinned by project target resolution. */ +export const AwsCredentialsKey = contextKey("aws.credentials"); diff --git a/src/handlers/project/invoke/harness.tsx b/src/handlers/project/invoke/harness.tsx index c3d8a8b30..28bf608bf 100644 --- a/src/handlers/project/invoke/harness.tsx +++ b/src/handlers/project/invoke/harness.tsx @@ -3,7 +3,7 @@ import { InputValidationError } from "../../../errors"; import type { AppIO } from "../../../io"; import { createHandler, flag, ProjectKey } from "../../../router"; import { JsonRendererKey, renderTuiAt } from "../../../tui"; -import { JsonKey, RegionKey } from "../../keys"; +import { AwsCredentialsKey, JsonKey, RegionKey } from "../../keys"; import { invokeHarnessTurn } from "../../harness/invoke/operation"; import type { Core } from "../../types"; import { coreOptsFromCtx } from "../../utils"; @@ -40,7 +40,9 @@ export const createProjectInvokeHarnessHandler = ( resourceType: "harness", name, }); - const invokeCtx = ctx.withValue(RegionKey, deployed.target.region); + const invokeCtx = ctx + .withValue(RegionKey, deployed.target.region) + .withValue(AwsCredentialsKey, deployed.credentials); if (!flags.prompt) { if (invokeCtx.require(JsonKey)) { diff --git a/src/handlers/project/invoke/index.test.tsx b/src/handlers/project/invoke/index.test.tsx index 4c60b9971..759997401 100644 --- a/src/handlers/project/invoke/index.test.tsx +++ b/src/handlers/project/invoke/index.test.tsx @@ -19,7 +19,7 @@ import { inTempDirectory, } from "../../../testing"; import { createRootHandler } from "../../index"; -import { JsonKey, RegionKey } from "../../keys"; +import { AwsCredentialsKey, JsonKey, RegionKey } from "../../keys"; import { RuntimeInvokeLaunchContextKey } from "../../runtime/invoke/launchContext"; import type { RuntimeInvokeRequest } from "../../runtime/types"; import type { Project } from "../types"; @@ -35,6 +35,10 @@ const TARGET = { account: "111122223333", region: "eu-west-1", } as const; +const TARGET_CREDENTIALS = async () => ({ + accessKeyId: "target-access-key", + secretAccessKey: "target-secret-key", +}); const RUNTIME_ID = "checkout-AbCdEf1234"; const RUNTIME_ARN = `arn:aws:bedrock-agentcore:${TARGET.region}:${TARGET.account}:runtime/${RUNTIME_ID}`; const HARNESS_ID = "support-AbCdEf1234"; @@ -96,12 +100,14 @@ function backend() { name, id: RUNTIME_ID, target: input.target, + credentials: TARGET_CREDENTIALS, })), ...project.spec.harnesses.map(({ name }) => ({ resourceType: "harness" as const, name, id: HARNESS_ID, target: input.target, + credentials: TARGET_CREDENTIALS, })), ]; }, @@ -382,7 +388,10 @@ describe("project invoke", () => { expect(new TextDecoder().decode(request.payload)).toBe(payload); expect(request.contentType).toBe("application/custom+json"); expect(request.runtimeUserId).toBe("default"); - expect(core.runtime.calls.at(-1)!.args[1]).toEqual({ region: TARGET.region }); + expect(core.runtime.calls.at(-1)!.args[1]).toEqual({ + region: TARGET.region, + credentials: TARGET_CREDENTIALS, + }); expect(io.stdout()).toBe("runtime response"); expect(resolved.calls).toEqual([{ target: TARGET }]); }); @@ -399,7 +408,10 @@ describe("project invoke", () => { qualifier: "DEFAULT", messages: [{ role: "user", content: [{ text: "hello" }] }], }); - expect(core.harness.calls.at(-1)!.args[1]).toEqual({ region: TARGET.region }); + expect(core.harness.calls.at(-1)!.args[1]).toEqual({ + region: TARGET.region, + credentials: TARGET_CREDENTIALS, + }); expect(JSON.parse(io.stdout()).transcript).toContainEqual({ kind: "text", text: "harness response", @@ -453,6 +465,7 @@ describe("project invoke", () => { expect(launches[0]!.path).toBe(`/agentcore/runtime/invoke/${RUNTIME_ID}`); expect(launches[0]!.context.require(RegionKey)).toBe(TARGET.region); + expect(launches[0]!.context.require(AwsCredentialsKey)).toBe(TARGET_CREDENTIALS); expect(launches[0]!.context.require(RuntimeInvokeLaunchContextKey)).toMatchObject({ runtimeId: RUNTIME_ID, }); @@ -498,6 +511,7 @@ describe("project invoke", () => { expect(launches[0]!.path).toBe(`/agentcore/harness/invoke/${HARNESS_ID}?qualifier=prod`); expect(launches[0]!.context.require(RegionKey)).toBe(TARGET.region); + expect(launches[0]!.context.require(AwsCredentialsKey)).toBe(TARGET_CREDENTIALS); }); test("bare project invoke opens the project resource picker", async () => { diff --git a/src/handlers/project/invoke/invoke.screen.test.tsx b/src/handlers/project/invoke/invoke.screen.test.tsx index 983b56b49..a1a98716e 100644 --- a/src/handlers/project/invoke/invoke.screen.test.tsx +++ b/src/handlers/project/invoke/invoke.screen.test.tsx @@ -55,10 +55,26 @@ function endpoint(name: string): AgentRuntimeEndpoint { } const TARGET = { name: "default", account: "111122223333", region: "eu-west-1" } as const; +const TARGET_CREDENTIALS = async () => ({ + accessKeyId: "target-access-key", + secretAccessKey: "target-secret-key", +}); const DEPLOYED_RESOURCES: ResolvedDeployedResource[] = [ - { resourceType: "runtime", name: "checkout", id: "runtime-123", target: TARGET }, - { resourceType: "harness", name: "support", id: "harness-123", target: TARGET }, + { + resourceType: "runtime", + name: "checkout", + id: "runtime-123", + target: TARGET, + credentials: TARGET_CREDENTIALS, + }, + { + resourceType: "harness", + name: "support", + id: "harness-123", + target: TARGET, + credentials: TARGET_CREDENTIALS, + }, ]; function core(resources: ResolvedDeployedResource[] = DEPLOYED_RESOURCES): TestCoreClient { @@ -68,6 +84,7 @@ function core(resources: ResolvedDeployedResource[] = DEPLOYED_RESOURCES): TestC name: input.name, id: input.resourceType === "runtime" ? "runtime-123" : "harness-123", target: TARGET, + credentials: TARGET_CREDENTIALS, }); value.projectManager.resolveDeployedResources = async () => ({ resources, target: TARGET }); value.runtime @@ -88,7 +105,15 @@ function core(resources: ResolvedDeployedResource[] = DEPLOYED_RESOURCES): TestC describe("project invoke picker", () => { test("lists only resources present in the deployed target", async () => { const screen = renderScreen("/agentcore/project/invoke", { - core: core([{ resourceType: "harness", name: "support", id: "harness-123", target: TARGET }]), + core: core([ + { + resourceType: "harness", + name: "support", + id: "harness-123", + target: TARGET, + credentials: TARGET_CREDENTIALS, + }, + ]), withContext: (ctx) => ctx.withValue(ProjectKey, project), }); @@ -164,8 +189,9 @@ describe("project invoke picker", () => { }); test("opens the selected Harness chat in the same TUI", async () => { + const value = core(); const screen = renderScreen("/agentcore/project/invoke", { - core: core(), + core: value, withContext: (ctx) => ctx.withValue(ProjectKey, project), }); @@ -174,11 +200,17 @@ describe("project invoke picker", () => { await screen.press("return"); await waitForText(screen.lastFrame, "send a message…"); expect(screen.lastFrame()).toContain("harness-123"); + expect(value.harness.calls.find(({ method }) => method === "getHarness")?.args[1]).toEqual({ + region: TARGET.region, + endpointUrl: undefined, + credentials: TARGET_CREDENTIALS, + }); }); test("uses the existing Runtime endpoint picker before its JSON console", async () => { + const value = core(); const screen = renderScreen("/agentcore/project/invoke", { - core: core(), + core: value, withContext: (ctx) => ctx.withValue(ProjectKey, project), }); @@ -188,5 +220,12 @@ describe("project invoke picker", () => { await screen.press("return"); await waitForText(screen.lastFrame, "Enter JSON payload"); expect(screen.lastFrame()).not.toContain("Enter prompt"); + expect( + value.runtime.calls.find(({ method }) => method === "listRuntimeEndpoints")?.args[3], + ).toEqual({ + region: TARGET.region, + endpointUrl: undefined, + credentials: TARGET_CREDENTIALS, + }); }); }); diff --git a/src/handlers/project/invoke/runtime.tsx b/src/handlers/project/invoke/runtime.tsx index f266a1a6b..852aa0904 100644 --- a/src/handlers/project/invoke/runtime.tsx +++ b/src/handlers/project/invoke/runtime.tsx @@ -6,7 +6,7 @@ import type { AppIO } from "../../../io"; import { ExitCode, withUserCancellation } from "../../../runnable"; import { createHandler, flag, ProjectKey } from "../../../router"; import { renderTuiAt } from "../../../tui"; -import { JsonKey, RegionKey } from "../../keys"; +import { AwsCredentialsKey, JsonKey, RegionKey } from "../../keys"; import { RuntimeInvokeLaunchContextKey } from "../../runtime/invoke/launchContext"; import { invokeRuntimeTarget } from "../../runtime/invoke/operation"; import { @@ -136,7 +136,9 @@ export const createProjectInvokeRuntimeHandler = ( resourceType: "runtime", name, }); - const invokeCtx = ctx.withValue(RegionKey, deployed.target.region); + const invokeCtx = ctx + .withValue(RegionKey, deployed.target.region) + .withValue(AwsCredentialsKey, deployed.credentials); if (flags.payload === undefined) { const hasHeadlessOnlyFlag = Object.entries(flags).some( diff --git a/src/handlers/project/invoke/screen.tsx b/src/handlers/project/invoke/screen.tsx index 35f142c9d..c0e2b5c5c 100644 --- a/src/handlers/project/invoke/screen.tsx +++ b/src/handlers/project/invoke/screen.tsx @@ -8,7 +8,7 @@ import { Spinner } from "../../../components/ui/spinner"; import { glyphs } from "../../../components/ui/_core.js"; import { ProjectKey, type Context } from "../../../router"; import { HarnessChat } from "../../harness/invoke/screen"; -import { RegionKey } from "../../keys"; +import { AwsCredentialsKey, RegionKey } from "../../keys"; import { RuntimeInvokeConsole } from "../../runtime/invoke/screen"; import type { ScreenProps } from "../../types"; import type { Project, ResolvedDeployedResources } from "../types"; @@ -110,7 +110,9 @@ function ProjectInvokePicker({ setDestination({ resourceType: row.resourceType, id: row.id, - ctx: ctx.withValue(RegionKey, deployed.target.region), + ctx: ctx + .withValue(RegionKey, deployed.target.region) + .withValue(AwsCredentialsKey, row.credentials), }); }; diff --git a/src/handlers/project/types.ts b/src/handlers/project/types.ts index eb08483b9..18f8f6407 100644 --- a/src/handlers/project/types.ts +++ b/src/handlers/project/types.ts @@ -15,6 +15,7 @@ import type { AgentCoreGateway, AgentCoreGatewayTarget } from "../../projectSche import type { PolicyEngineSchema, PolicySchema } from "../../projectSchemas/policy"; import type { AwsDeploymentTarget } from "../../projectSchemas/aws-targets"; import type { ProgressEvent } from "../../tui/progress"; +import type { AwsCredentials } from "../../core/types"; type CreateProjectInputBase = { /** The name of the project; also the directory it is scaffolded into. */ @@ -213,6 +214,8 @@ export type ResolvedDeployedResource = { name: string; id: string; target: AwsDeploymentTarget; + /** Credential provider used to resolve and access this target. */ + credentials: AwsCredentials; }; export type ResolvedDeployedResources = { @@ -459,7 +462,7 @@ export interface ProjectManager { /** Locate an existing AgentCore project. Returns undefined if no project can be found. */ resolve(input: ResolveProjectInput): Promise; - /** Resolve a logical project resource to its deployed physical ID and target. */ + /** Resolve a logical project resource to its deployed physical ID, target, and credentials. */ resolveDeployedResource( project: Project, input: ResolveDeployedResourceInput, diff --git a/src/handlers/utils.tsx b/src/handlers/utils.tsx index befca47b6..7941c4e19 100644 --- a/src/handlers/utils.tsx +++ b/src/handlers/utils.tsx @@ -5,17 +5,20 @@ import type { CoreOptions } from "../core/types"; import type { AppIO } from "../io"; import { AgentCoreCLIError, InputValidationError, SilentCLIError } from "../errors"; import { formatZodError } from "../router/schema"; -import { EndpointKey, JsonKey, RegionKey } from "./keys"; +import { AwsCredentialsKey, EndpointKey, JsonKey, RegionKey } from "./keys"; import { JsonRendererKey } from "../tui"; // coreOptsFromCtx builds the standard CoreOptions handed to Core operations from // the values pinned on the context: the resolved region (always present, see the -// withRegion middleware) and the optional --endpoint-url override. Shared by every -// handler that calls into a Core sub-client. +// withRegion middleware), the optional --endpoint-url override, and any explicit +// credentials selected for a project target. Shared by every handler that calls +// into a Core sub-client. export function coreOptsFromCtx(ctx: Context): CoreOptions { + const credentials = ctx.value(AwsCredentialsKey); return { region: ctx.require(RegionKey), endpointUrl: ctx.value(EndpointKey), + ...(credentials ? { credentials } : {}), }; } From a7d1b971793ade1368a82d01a24ada576e3a4a7b Mon Sep 17 00:00:00 2001 From: Nicolas Borges Date: Tue, 15 Sep 2026 10:45:47 -0400 Subject: [PATCH 2/3] fix: use project target credentials in runtime log --- src/handlers/project/log/runtime.test.tsx | 17 +++++++++++++++-- src/handlers/project/log/runtime.tsx | 1 + 2 files changed, 16 insertions(+), 2 deletions(-) diff --git a/src/handlers/project/log/runtime.test.tsx b/src/handlers/project/log/runtime.test.tsx index e68704f75..4aef5e6da 100644 --- a/src/handlers/project/log/runtime.test.tsx +++ b/src/handlers/project/log/runtime.test.tsx @@ -24,6 +24,10 @@ const PRODUCTION_TARGET = { account: "111122223333", region: "ap-southeast-2", } as const; +const TARGET_CREDENTIALS = async () => ({ + accessKeyId: "target-access-key", + secretAccessKey: "target-secret-key", +}); const RUNTIMES = [ { name: "checkout", @@ -78,6 +82,7 @@ function backend(options: { deployed?: boolean } = {}) { name, id: `${name}-AbCdEf1234`, target: input.target, + credentials: TARGET_CREDENTIALS, })); }, async resolveProjectResources() { @@ -131,7 +136,11 @@ describe("project log runtime", () => { logGroupName: "/aws/bedrock-agentcore/runtimes/checkout-AbCdEf1234-DEFAULT", }); expect(call.args[1]).toEqual({ filterPattern: undefined }); - expect(call.args[2]).toEqual({ region: DEFAULT_TARGET.region, endpointUrl: undefined }); + expect(call.args[2]).toEqual({ + region: DEFAULT_TARGET.region, + endpointUrl: undefined, + credentials: TARGET_CREDENTIALS, + }); expect(subject.io.stderr()).toContain( "Streaming logs for Runtime 'checkout' on target 'default'... (Ctrl+C to stop)", ); @@ -163,7 +172,11 @@ describe("project log runtime", () => { logGroupName: "/aws/bedrock-agentcore/runtimes/inventory-AbCdEf1234-BLUE", }); expect(call.args[1]).toMatchObject({ limit: 25 }); - expect(call.args[2]).toEqual({ region: PRODUCTION_TARGET.region, endpointUrl: undefined }); + expect(call.args[2]).toEqual({ + region: PRODUCTION_TARGET.region, + endpointUrl: undefined, + credentials: TARGET_CREDENTIALS, + }); }); test("requires --name when the project declares several Runtimes", async () => { diff --git a/src/handlers/project/log/runtime.tsx b/src/handlers/project/log/runtime.tsx index fa5a2c86b..17f55766a 100644 --- a/src/handlers/project/log/runtime.tsx +++ b/src/handlers/project/log/runtime.tsx @@ -30,6 +30,7 @@ export const createProjectRuntimeLogHandler = (core: Core, io: AppIO) => const options = { ...coreOptsFromCtx(ctx), region: deployed.target.region, + credentials: deployed.credentials, }; const source = { logGroupName: runtimeLogGroup(deployed.id, flags.qualifier ?? DEFAULT_ENDPOINT_QUALIFIER), From 9138c674fb4f73e02fc3fcb4826a95d7bc1e8be0 Mon Sep 17 00:00:00 2001 From: Nicolas Borges Date: Tue, 15 Sep 2026 10:50:07 -0400 Subject: [PATCH 3/3] fix: require credential provider for resolved targets --- src/core/project/backends/cdk.test.ts | 6 +++--- src/core/project/backends/cdk.ts | 2 +- src/core/project/manager.tsx | 4 ++-- src/core/types.tsx | 1 + src/handlers/keys.tsx | 6 +++--- src/handlers/project/invoke/harness.tsx | 4 ++-- src/handlers/project/invoke/index.test.tsx | 10 +++++----- src/handlers/project/invoke/invoke.screen.test.tsx | 8 ++++---- src/handlers/project/invoke/runtime.tsx | 4 ++-- src/handlers/project/invoke/screen.tsx | 4 ++-- src/handlers/project/log/runtime.test.tsx | 2 +- src/handlers/project/log/runtime.tsx | 2 +- src/handlers/project/types.ts | 6 +++--- src/handlers/utils.tsx | 10 +++++----- 14 files changed, 35 insertions(+), 34 deletions(-) diff --git a/src/core/project/backends/cdk.test.ts b/src/core/project/backends/cdk.test.ts index 937ed5799..c6d0cd476 100644 --- a/src/core/project/backends/cdk.test.ts +++ b/src/core/project/backends/cdk.test.ts @@ -861,14 +861,14 @@ describe("CdkBackend.resolveDeployedResources", () => { name: "checkout_agent", id: "checkout_agent-AbCdEf1234", target: TARGET, - credentials: subject.credentials, + credentialProvider: subject.credentials, }, { resourceType: "harness", name: "support_agent", id: "support_agent-AbCdEf1234", target: TARGET, - credentials: subject.credentials, + credentialProvider: subject.credentials, }, ]); expect(subject.stackReads).toHaveLength(1); @@ -905,7 +905,7 @@ describe("CdkBackend.resolveDeployedResources", () => { name: "support", id: "support-AbCdEf1234", target: TARGET, - credentials: subject.credentials, + credentialProvider: subject.credentials, }, ]); expect(subject.stackReads[0]?.stackName).toBe("AgentCore-example-default"); diff --git a/src/core/project/backends/cdk.ts b/src/core/project/backends/cdk.ts index ddeb9617d..1428c16f1 100644 --- a/src/core/project/backends/cdk.ts +++ b/src/core/project/backends/cdk.ts @@ -431,7 +431,7 @@ export class CdkBackend implements ProjectBackend { ]; return resources.flatMap((resource) => { const id = findDeployedResourceId(stack, resource); - return id ? [{ ...resource, id, target, credentials }] : []; + return id ? [{ ...resource, id, target, credentialProvider: credentials }] : []; }); } diff --git a/src/core/project/manager.tsx b/src/core/project/manager.tsx index 2abf12233..7524e07c2 100644 --- a/src/core/project/manager.tsx +++ b/src/core/project/manager.tsx @@ -975,8 +975,8 @@ export class FsProjectManager implements ProjectManager { ); // The declared target wins over the copy on the item: the manager resolved it // from aws-targets.json, and both invoke handlers pin the AWS region from this - // value while reusing the backend's verified credentials. Trusting a backend's - // target echo would let it redirect the call. + // value while reusing the backend's verified credential provider. Trusting a + // backend's target echo would let it redirect the call. if (resource) return { ...resource, target: resolved.target }; const label = input.resourceType === "runtime" ? "Runtime" : "Harness"; diff --git a/src/core/types.tsx b/src/core/types.tsx index bc76110a2..5da4486c6 100644 --- a/src/core/types.tsx +++ b/src/core/types.tsx @@ -10,6 +10,7 @@ import type { AwsCredentialIdentity, AwsCredentialIdentityProvider } from "@smit // default credential chain leave it unset. Every v3 client accepts this same shape, // so it comes from the shared Smithy types rather than any one client's config. export type AwsCredentials = AwsCredentialIdentity | AwsCredentialIdentityProvider; +export type AwsCredentialProvider = AwsCredentialIdentityProvider; // CoreOptions is the standard trailing argument for Core operations. It carries // the per-call settings a handler resolves from context (the AWS region and an diff --git a/src/handlers/keys.tsx b/src/handlers/keys.tsx index 91a288254..974bc7221 100644 --- a/src/handlers/keys.tsx +++ b/src/handlers/keys.tsx @@ -1,5 +1,5 @@ import z from "zod"; -import type { AwsCredentials } from "../core/types"; +import type { AwsCredentialProvider } from "../core/types"; import { contextKey, globalFlag } from "../router"; // These keys are group-level flags declared on the root router. Because a @@ -20,5 +20,5 @@ export const EndpointKey = globalFlag( z.string().optional(), ); -/** Explicit credentials pinned by project target resolution. */ -export const AwsCredentialsKey = contextKey("aws.credentials"); +/** Explicit credential provider pinned by project target resolution. */ +export const AwsCredentialProviderKey = contextKey("aws.credentialProvider"); diff --git a/src/handlers/project/invoke/harness.tsx b/src/handlers/project/invoke/harness.tsx index 28bf608bf..18c2b4b7f 100644 --- a/src/handlers/project/invoke/harness.tsx +++ b/src/handlers/project/invoke/harness.tsx @@ -3,7 +3,7 @@ import { InputValidationError } from "../../../errors"; import type { AppIO } from "../../../io"; import { createHandler, flag, ProjectKey } from "../../../router"; import { JsonRendererKey, renderTuiAt } from "../../../tui"; -import { AwsCredentialsKey, JsonKey, RegionKey } from "../../keys"; +import { AwsCredentialProviderKey, JsonKey, RegionKey } from "../../keys"; import { invokeHarnessTurn } from "../../harness/invoke/operation"; import type { Core } from "../../types"; import { coreOptsFromCtx } from "../../utils"; @@ -42,7 +42,7 @@ export const createProjectInvokeHarnessHandler = ( }); const invokeCtx = ctx .withValue(RegionKey, deployed.target.region) - .withValue(AwsCredentialsKey, deployed.credentials); + .withValue(AwsCredentialProviderKey, deployed.credentialProvider); if (!flags.prompt) { if (invokeCtx.require(JsonKey)) { diff --git a/src/handlers/project/invoke/index.test.tsx b/src/handlers/project/invoke/index.test.tsx index 759997401..0d9992054 100644 --- a/src/handlers/project/invoke/index.test.tsx +++ b/src/handlers/project/invoke/index.test.tsx @@ -19,7 +19,7 @@ import { inTempDirectory, } from "../../../testing"; import { createRootHandler } from "../../index"; -import { AwsCredentialsKey, JsonKey, RegionKey } from "../../keys"; +import { AwsCredentialProviderKey, JsonKey, RegionKey } from "../../keys"; import { RuntimeInvokeLaunchContextKey } from "../../runtime/invoke/launchContext"; import type { RuntimeInvokeRequest } from "../../runtime/types"; import type { Project } from "../types"; @@ -100,14 +100,14 @@ function backend() { name, id: RUNTIME_ID, target: input.target, - credentials: TARGET_CREDENTIALS, + credentialProvider: TARGET_CREDENTIALS, })), ...project.spec.harnesses.map(({ name }) => ({ resourceType: "harness" as const, name, id: HARNESS_ID, target: input.target, - credentials: TARGET_CREDENTIALS, + credentialProvider: TARGET_CREDENTIALS, })), ]; }, @@ -465,7 +465,7 @@ describe("project invoke", () => { expect(launches[0]!.path).toBe(`/agentcore/runtime/invoke/${RUNTIME_ID}`); expect(launches[0]!.context.require(RegionKey)).toBe(TARGET.region); - expect(launches[0]!.context.require(AwsCredentialsKey)).toBe(TARGET_CREDENTIALS); + expect(launches[0]!.context.require(AwsCredentialProviderKey)).toBe(TARGET_CREDENTIALS); expect(launches[0]!.context.require(RuntimeInvokeLaunchContextKey)).toMatchObject({ runtimeId: RUNTIME_ID, }); @@ -511,7 +511,7 @@ describe("project invoke", () => { expect(launches[0]!.path).toBe(`/agentcore/harness/invoke/${HARNESS_ID}?qualifier=prod`); expect(launches[0]!.context.require(RegionKey)).toBe(TARGET.region); - expect(launches[0]!.context.require(AwsCredentialsKey)).toBe(TARGET_CREDENTIALS); + expect(launches[0]!.context.require(AwsCredentialProviderKey)).toBe(TARGET_CREDENTIALS); }); test("bare project invoke opens the project resource picker", async () => { diff --git a/src/handlers/project/invoke/invoke.screen.test.tsx b/src/handlers/project/invoke/invoke.screen.test.tsx index a1a98716e..3cc84623f 100644 --- a/src/handlers/project/invoke/invoke.screen.test.tsx +++ b/src/handlers/project/invoke/invoke.screen.test.tsx @@ -66,14 +66,14 @@ const DEPLOYED_RESOURCES: ResolvedDeployedResource[] = [ name: "checkout", id: "runtime-123", target: TARGET, - credentials: TARGET_CREDENTIALS, + credentialProvider: TARGET_CREDENTIALS, }, { resourceType: "harness", name: "support", id: "harness-123", target: TARGET, - credentials: TARGET_CREDENTIALS, + credentialProvider: TARGET_CREDENTIALS, }, ]; @@ -84,7 +84,7 @@ function core(resources: ResolvedDeployedResource[] = DEPLOYED_RESOURCES): TestC name: input.name, id: input.resourceType === "runtime" ? "runtime-123" : "harness-123", target: TARGET, - credentials: TARGET_CREDENTIALS, + credentialProvider: TARGET_CREDENTIALS, }); value.projectManager.resolveDeployedResources = async () => ({ resources, target: TARGET }); value.runtime @@ -111,7 +111,7 @@ describe("project invoke picker", () => { name: "support", id: "harness-123", target: TARGET, - credentials: TARGET_CREDENTIALS, + credentialProvider: TARGET_CREDENTIALS, }, ]), withContext: (ctx) => ctx.withValue(ProjectKey, project), diff --git a/src/handlers/project/invoke/runtime.tsx b/src/handlers/project/invoke/runtime.tsx index 852aa0904..95a9f482d 100644 --- a/src/handlers/project/invoke/runtime.tsx +++ b/src/handlers/project/invoke/runtime.tsx @@ -6,7 +6,7 @@ import type { AppIO } from "../../../io"; import { ExitCode, withUserCancellation } from "../../../runnable"; import { createHandler, flag, ProjectKey } from "../../../router"; import { renderTuiAt } from "../../../tui"; -import { AwsCredentialsKey, JsonKey, RegionKey } from "../../keys"; +import { AwsCredentialProviderKey, JsonKey, RegionKey } from "../../keys"; import { RuntimeInvokeLaunchContextKey } from "../../runtime/invoke/launchContext"; import { invokeRuntimeTarget } from "../../runtime/invoke/operation"; import { @@ -138,7 +138,7 @@ export const createProjectInvokeRuntimeHandler = ( }); const invokeCtx = ctx .withValue(RegionKey, deployed.target.region) - .withValue(AwsCredentialsKey, deployed.credentials); + .withValue(AwsCredentialProviderKey, deployed.credentialProvider); if (flags.payload === undefined) { const hasHeadlessOnlyFlag = Object.entries(flags).some( diff --git a/src/handlers/project/invoke/screen.tsx b/src/handlers/project/invoke/screen.tsx index c0e2b5c5c..9c761acb4 100644 --- a/src/handlers/project/invoke/screen.tsx +++ b/src/handlers/project/invoke/screen.tsx @@ -8,7 +8,7 @@ import { Spinner } from "../../../components/ui/spinner"; import { glyphs } from "../../../components/ui/_core.js"; import { ProjectKey, type Context } from "../../../router"; import { HarnessChat } from "../../harness/invoke/screen"; -import { AwsCredentialsKey, RegionKey } from "../../keys"; +import { AwsCredentialProviderKey, RegionKey } from "../../keys"; import { RuntimeInvokeConsole } from "../../runtime/invoke/screen"; import type { ScreenProps } from "../../types"; import type { Project, ResolvedDeployedResources } from "../types"; @@ -112,7 +112,7 @@ function ProjectInvokePicker({ id: row.id, ctx: ctx .withValue(RegionKey, deployed.target.region) - .withValue(AwsCredentialsKey, row.credentials), + .withValue(AwsCredentialProviderKey, row.credentialProvider), }); }; diff --git a/src/handlers/project/log/runtime.test.tsx b/src/handlers/project/log/runtime.test.tsx index 4aef5e6da..4e7af44c1 100644 --- a/src/handlers/project/log/runtime.test.tsx +++ b/src/handlers/project/log/runtime.test.tsx @@ -82,7 +82,7 @@ function backend(options: { deployed?: boolean } = {}) { name, id: `${name}-AbCdEf1234`, target: input.target, - credentials: TARGET_CREDENTIALS, + credentialProvider: TARGET_CREDENTIALS, })); }, async resolveProjectResources() { diff --git a/src/handlers/project/log/runtime.tsx b/src/handlers/project/log/runtime.tsx index 17f55766a..9b826692e 100644 --- a/src/handlers/project/log/runtime.tsx +++ b/src/handlers/project/log/runtime.tsx @@ -30,7 +30,7 @@ export const createProjectRuntimeLogHandler = (core: Core, io: AppIO) => const options = { ...coreOptsFromCtx(ctx), region: deployed.target.region, - credentials: deployed.credentials, + credentials: deployed.credentialProvider, }; const source = { logGroupName: runtimeLogGroup(deployed.id, flags.qualifier ?? DEFAULT_ENDPOINT_QUALIFIER), diff --git a/src/handlers/project/types.ts b/src/handlers/project/types.ts index 18f8f6407..7a43c57a5 100644 --- a/src/handlers/project/types.ts +++ b/src/handlers/project/types.ts @@ -15,7 +15,7 @@ import type { AgentCoreGateway, AgentCoreGatewayTarget } from "../../projectSche import type { PolicyEngineSchema, PolicySchema } from "../../projectSchemas/policy"; import type { AwsDeploymentTarget } from "../../projectSchemas/aws-targets"; import type { ProgressEvent } from "../../tui/progress"; -import type { AwsCredentials } from "../../core/types"; +import type { AwsCredentialProvider } from "../../core/types"; type CreateProjectInputBase = { /** The name of the project; also the directory it is scaffolded into. */ @@ -215,7 +215,7 @@ export type ResolvedDeployedResource = { id: string; target: AwsDeploymentTarget; /** Credential provider used to resolve and access this target. */ - credentials: AwsCredentials; + credentialProvider: AwsCredentialProvider; }; export type ResolvedDeployedResources = { @@ -462,7 +462,7 @@ export interface ProjectManager { /** Locate an existing AgentCore project. Returns undefined if no project can be found. */ resolve(input: ResolveProjectInput): Promise; - /** Resolve a logical project resource to its deployed physical ID, target, and credentials. */ + /** Resolve a logical project resource to its deployed physical ID, target, and credential provider. */ resolveDeployedResource( project: Project, input: ResolveDeployedResourceInput, diff --git a/src/handlers/utils.tsx b/src/handlers/utils.tsx index 7941c4e19..d0f20781b 100644 --- a/src/handlers/utils.tsx +++ b/src/handlers/utils.tsx @@ -5,20 +5,20 @@ import type { CoreOptions } from "../core/types"; import type { AppIO } from "../io"; import { AgentCoreCLIError, InputValidationError, SilentCLIError } from "../errors"; import { formatZodError } from "../router/schema"; -import { AwsCredentialsKey, EndpointKey, JsonKey, RegionKey } from "./keys"; +import { AwsCredentialProviderKey, EndpointKey, JsonKey, RegionKey } from "./keys"; import { JsonRendererKey } from "../tui"; // coreOptsFromCtx builds the standard CoreOptions handed to Core operations from // the values pinned on the context: the resolved region (always present, see the // withRegion middleware), the optional --endpoint-url override, and any explicit -// credentials selected for a project target. Shared by every handler that calls -// into a Core sub-client. +// credential provider selected for a project target. Shared by every handler +// that calls into a Core sub-client. export function coreOptsFromCtx(ctx: Context): CoreOptions { - const credentials = ctx.value(AwsCredentialsKey); + const credentialProvider = ctx.value(AwsCredentialProviderKey); return { region: ctx.require(RegionKey), endpointUrl: ctx.value(EndpointKey), - ...(credentials ? { credentials } : {}), + ...(credentialProvider ? { credentials: credentialProvider } : {}), }; }