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
25 changes: 17 additions & 8 deletions src/cli/cloudformation/__tests__/outputs.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -112,9 +112,13 @@ describe('buildDeployedState', () => {
});

describe('parseGatewayOutputs', () => {
it('extracts gateway URL from outputs matching pattern', () => {
it('extracts gateway outputs matching pattern', () => {
const outputs = {
GatewayMyGatewayIdOutput3E11FAB4: 'gw-123',
GatewayMyGatewayArnOutput3E11FAB4: 'arn:aws:bedrock:us-east-1:123:gateway/gw-123',
GatewayMyGatewayUrlOutput3E11FAB4: 'https://api.gateway.url',
GatewayAnotherGatewayIdOutputABC123: 'gw-456',
GatewayAnotherGatewayArnOutputABC123: 'arn:aws:bedrock:us-east-1:123:gateway/gw-456',
GatewayAnotherGatewayUrlOutputABC123: 'https://another.gateway.url',
UnrelatedOutput: 'some-value',
};
Expand All @@ -128,12 +132,14 @@ describe('parseGatewayOutputs', () => {

expect(result).toEqual({
'my-gateway': {
gatewayId: 'my-gateway',
gatewayArn: 'https://api.gateway.url',
gatewayId: 'gw-123',
gatewayArn: 'arn:aws:bedrock:us-east-1:123:gateway/gw-123',
gatewayUrl: 'https://api.gateway.url',
},
'another-gateway': {
gatewayId: 'another-gateway',
gatewayArn: 'https://another.gateway.url',
gatewayId: 'gw-456',
gatewayArn: 'arn:aws:bedrock:us-east-1:123:gateway/gw-456',
gatewayUrl: 'https://another.gateway.url',
},
});
});
Expand All @@ -155,8 +161,11 @@ describe('parseGatewayOutputs', () => {

it('maps multiple gateways correctly', () => {
const outputs = {
GatewayFirstGatewayArnOutput123: 'arn:first',
GatewayFirstGatewayUrlOutput123: 'https://first.url',
GatewaySecondGatewayArnOutput456: 'arn:second',
GatewaySecondGatewayUrlOutput456: 'https://second.url',
GatewayThirdGatewayArnOutput789: 'arn:third',
GatewayThirdGatewayUrlOutput789: 'https://third.url',
};

Expand All @@ -169,8 +178,8 @@ describe('parseGatewayOutputs', () => {
const result = parseGatewayOutputs(outputs, gatewaySpecs);

expect(Object.keys(result)).toHaveLength(3);
expect(result['first-gateway']?.gatewayArn).toBe('https://first.url');
expect(result['second-gateway']?.gatewayArn).toBe('https://second.url');
expect(result['third-gateway']?.gatewayArn).toBe('https://third.url');
expect(result['first-gateway']?.gatewayUrl).toBe('https://first.url');
expect(result['second-gateway']?.gatewayUrl).toBe('https://second.url');
expect(result['third-gateway']?.gatewayUrl).toBe('https://third.url');
});
});
22 changes: 14 additions & 8 deletions src/cli/cloudformation/outputs.ts
Original file line number Diff line number Diff line change
Expand Up @@ -38,8 +38,8 @@ export async function getStackOutputs(region: string, stackName: string): Promis
export function parseGatewayOutputs(
outputs: StackOutputs,
gatewaySpecs: Record<string, unknown>
): Record<string, { gatewayId: string; gatewayArn: string }> {
const gateways: Record<string, { gatewayId: string; gatewayArn: string }> = {};
): Record<string, { gatewayId: string; gatewayArn: string; gatewayUrl?: string }> {
const gateways: Record<string, { gatewayId: string; gatewayArn: string; gatewayUrl?: string }> = {};

// Map PascalCase gateway names to original names for lookup
const gatewayNames = Object.keys(gatewaySpecs);
Expand All @@ -53,15 +53,21 @@ export function parseGatewayOutputs(
if (!match) continue;

const logicalGateway = match[1];
if (!logicalGateway) continue;
const outputType = match[2];
if (!logicalGateway || !outputType) continue;

// Look up original gateway name from PascalCase version
const gatewayName = gatewayIdMap.get(logicalGateway) ?? logicalGateway;

gateways[gatewayName] = {
gatewayId: gatewayName,
gatewayArn: value,
};
gateways[gatewayName] ??= { gatewayId: gatewayName, gatewayArn: '' };

if (outputType === 'Id') {
gateways[gatewayName].gatewayId = value;
} else if (outputType === 'Arn') {
gateways[gatewayName].gatewayArn = value;
} else if (outputType === 'Url') {
gateways[gatewayName].gatewayUrl = value;
}
}

return gateways;
Expand Down Expand Up @@ -173,7 +179,7 @@ export function buildDeployedState(
targetName: string,
stackName: string,
agents: Record<string, AgentCoreDeployedState>,
gateways: Record<string, { gatewayId: string; gatewayArn: string }>,
gateways: Record<string, { gatewayId: string; gatewayArn: string; gatewayUrl?: string }>,
existingState?: DeployedState,
identityKmsKeyArn?: string,
credentials?: Record<string, { credentialProviderArn: string; clientSecretArn?: string; callbackUrl?: string }>
Expand Down
6 changes: 5 additions & 1 deletion src/cli/commands/dev/command.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import {
invokeAgentStreaming,
loadProjectConfig,
} from '../../operations/dev';
import { getGatewayEnvVars } from '../../operations/dev/gateway-env.js';
import { FatalError } from '../../tui/components';
import { LayoutProvider } from '../../tui/context';
import { COMMAND_DESCRIPTIONS } from '../../tui/copy';
Expand Down Expand Up @@ -123,6 +124,9 @@ export const registerDev = (program: Command) => {
const agentName = opts.agent ?? project.agents[0]?.name;
const configRoot = findConfigRoot(workingDir);
const envVars = configRoot ? await readEnvFile(configRoot) : {};
const gatewayEnvVars = await getGatewayEnvVars();
// Gateway env vars go first, .env.local overrides take precedence
const mergedEnvVars = { ...gatewayEnvVars, ...envVars };
const config = getDevConfig(workingDir, project, configRoot ?? undefined, agentName);

if (!config) {
Expand Down Expand Up @@ -164,7 +168,7 @@ export const registerDev = (program: Command) => {
},
};

const server = createDevServer(config, { port: actualPort, envVars, callbacks: devCallbacks });
const server = createDevServer(config, { port: actualPort, envVars: mergedEnvVars, callbacks: devCallbacks });
await server.start();

// Handle Ctrl+C — use server.kill() for proper container cleanup
Expand Down
30 changes: 30 additions & 0 deletions src/cli/operations/dev/gateway-env.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
import { ConfigIO } from '../../../lib/index.js';

export async function getGatewayEnvVars(): Promise<Record<string, string>> {
const configIO = new ConfigIO();
const envVars: Record<string, string> = {};

try {
const deployedState = await configIO.readDeployedState();
const mcpSpec = configIO.configExists('mcp') ? await configIO.readMcpSpec() : undefined;

// Iterate all targets (not just 'default')
for (const target of Object.values(deployedState?.targets ?? {})) {
const gateways = target?.resources?.mcp?.gateways ?? {};

for (const [name, gateway] of Object.entries(gateways)) {
if (!gateway.gatewayUrl) continue;
const sanitized = name.toUpperCase().replace(/-/g, '_');
envVars[`AGENTCORE_GATEWAY_${sanitized}_URL`] = gateway.gatewayUrl;

const gatewaySpec = mcpSpec?.agentCoreGateways?.find(g => g.name === name);
const authType = gatewaySpec?.authorizerType ?? 'NONE';
envVars[`AGENTCORE_GATEWAY_${sanitized}_AUTH_TYPE`] = authType;
}
}
} catch {
// No deployed state or mcp.json — skip gateway env vars
}

return envVars;
}
6 changes: 5 additions & 1 deletion src/cli/tui/hooks/useDevServer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import {
loadProjectConfig,
waitForPort,
} from '../../operations/dev';
import { getGatewayEnvVars } from '../../operations/dev/gateway-env.js';
import { useEffect, useMemo, useRef, useState } from 'react';

type ServerStatus = 'starting' | 'running' | 'error' | 'stopped';
Expand Down Expand Up @@ -69,7 +70,10 @@ export function useDevServer(options: { workingDir: string; port: number; agentN
// Load env vars from agentcore/.env
if (root) {
const vars = await readEnvFile(root);
setEnvVars(vars);
const gatewayEnvVars = await getGatewayEnvVars();
// Gateway env vars go first, .env.local overrides take precedence
const mergedEnvVars = { ...gatewayEnvVars, ...vars };
setEnvVars(mergedEnvVars);
}

setConfigLoaded(true);
Expand Down
1 change: 1 addition & 0 deletions src/schema/schemas/deployed-state.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ export type AgentCoreDeployedState = z.infer<typeof AgentCoreDeployedStateSchema
export const GatewayDeployedStateSchema = z.object({
gatewayId: z.string().min(1),
gatewayArn: z.string().min(1),
gatewayUrl: z.string().optional(),
});

export type GatewayDeployedState = z.infer<typeof GatewayDeployedStateSchema>;
Expand Down
Loading