Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
19 commits
Select commit Hold shift + click to select a range
cb839bf
feat: add runtime endpoint support to AgentCore CLI
jariy17 Apr 25, 2026
3b29773
fix: correct output key prefix for runtime endpoint parsing
jariy17 Apr 25, 2026
33b81da
fix: remove .omc state files and unused useCallback import
jariy17 Apr 27, 2026
6b4f8f3
fix: shorten runtime endpoint description to prevent TUI overflow
jariy17 Apr 27, 2026
c1ad7ea
fix: validate runtime endpoint version is a positive integer
jariy17 Apr 27, 2026
f3face8
fix: use agent/endpoint composite key to prevent React key collision
jariy17 Apr 27, 2026
21147b1
fix: render runtime endpoints in status --type runtime-endpoint
jariy17 Apr 27, 2026
132f969
fix: add runtime-endpoint to status --help --type documentation
jariy17 Apr 27, 2026
3aba3e9
fix: return richer JSON response from add runtime-endpoint
jariy17 Apr 27, 2026
2bb5a86
fix: validate endpoint version against deployed runtime version
jariy17 Apr 27, 2026
844689a
chore: remove planning and bug bash docs from PR
jariy17 Apr 27, 2026
11a7a86
fix: use composite key and parentName for endpoint identification
jariy17 Apr 27, 2026
d6389c8
test: add comprehensive unit tests for RuntimeEndpointPrimitive
jariy17 Apr 27, 2026
b9f158f
fix: remove dead findGatewayTargetReferences stub
jariy17 Apr 27, 2026
0ff1db8
fix: use BasePrimitive configIO instead of ad-hoc ConfigIO in add()
jariy17 Apr 27, 2026
be47f35
fix: use Number() instead of parseInt in TUI version validation
jariy17 Apr 27, 2026
fd8e15c
Merge branch 'main' into feat/endpoint_based_abs
jariy17 Apr 27, 2026
ddbeff2
chore: fix prettier formatting
jariy17 Apr 27, 2026
117a07e
fix: use T[] instead of Array<T> to satisfy eslint array-type rule
jariy17 Apr 27, 2026
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
1 change: 1 addition & 0 deletions .gitignore
Original file line numberDiff line numberDiff line change
Expand Up@@ -67,6 +67,7 @@ ProtocolTesting/

# Auto-cloned CDK constructs (from scripts/bundle.mjs)
.cdk-constructs-clone/
.omc/

# Browser tests
browser-tests/.browser-test-env
Expand Down
42 changes: 42 additions & 0 deletions src/cli/cloudformation/outputs.ts
Original file line numberDiff line numberDiff line change
Expand Up@@ -6,6 +6,7 @@ import type {
OnlineEvalDeployedState,
PolicyDeployedState,
PolicyEngineDeployedState,
RuntimeEndpointDeployedState,
TargetDeployedState,
} from '../../schema';
import { getCredentialProvider } from '../aws';
Expand DownExpand Up@@ -338,6 +339,40 @@ export function parsePolicyOutputs(
return policies;
}

/**
* Parse stack outputs into deployed state for runtime endpoints.
*
* Output key pattern: ApplicationAgent{AgentPascal}Endpoint{AgentPascal}{EndpointPascal}(Id|Arn)Output{Hash}
* The Agent{PascalName} prefix comes from the AgentEnvironment construct in the CDK tree.
*/
export function parseRuntimeEndpointOutputs(
outputs: StackOutputs,
endpointSpecs: { agentName: string; endpointName: string }[]
): Record<string, RuntimeEndpointDeployedState> {
const endpoints: Record<string, RuntimeEndpointDeployedState> = {};
const outputKeys = Object.keys(outputs);

for (const { agentName, endpointName } of endpointSpecs) {
const agentPascal = toPascalId(agentName);
const endpointPascal = toPascalId('Endpoint', agentName, endpointName);
const idPrefix = `ApplicationAgent${agentPascal}${endpointPascal}IdOutput`;
const arnPrefix = `ApplicationAgent${agentPascal}${endpointPascal}ArnOutput`;

const idKey = outputKeys.find(k => k.startsWith(idPrefix));
const arnKey = outputKeys.find(k => k.startsWith(arnPrefix));

if (idKey && arnKey) {
const key = `${agentName}/${endpointName}`;
endpoints[key] = {
endpointId: outputs[idKey]!,
endpointArn: outputs[arnKey]!,
};
}
}

return endpoints;
}

export interface BuildDeployedStateOptions {
targetName: string;
stackName: string;
Expand All@@ -351,6 +386,7 @@ export interface BuildDeployedStateOptions {
onlineEvalConfigs?: Record<string, OnlineEvalDeployedState>;
policyEngines?: Record<string, PolicyEngineDeployedState>;
policies?: Record<string, PolicyDeployedState>;
runtimeEndpoints?: Record<string, RuntimeEndpointDeployedState>;
}

/**
Expand All@@ -370,6 +406,7 @@ export function buildDeployedState(opts: BuildDeployedStateOptions): DeployedSta
onlineEvalConfigs,
policyEngines,
policies,
runtimeEndpoints,
} = opts;
const targetState: TargetDeployedState = {
resources: {
Expand DownExpand Up@@ -404,6 +441,11 @@ export function buildDeployedState(opts: BuildDeployedStateOptions): DeployedSta
targetState.resources!.onlineEvalConfigs = onlineEvalConfigs;
}

// Add runtime endpoint state if endpoints exist
if (runtimeEndpoints && Object.keys(runtimeEndpoints).length > 0) {
targetState.resources!.runtimeEndpoints = runtimeEndpoints;
}

return {
targets: {
...existingState?.targets,
Expand Down
13 changes: 13 additions & 0 deletions src/cli/commands/deploy/actions.ts
Original file line numberDiff line numberDiff line change
Expand Up@@ -13,6 +13,7 @@ import {
parseOnlineEvalOutputs,
parsePolicyEngineOutputs,
parsePolicyOutputs,
parseRuntimeEndpointOutputs,
} from '../../cloudformation';
import { getErrorMessage } from '../../errors';
import { ExecLogger } from '../../logging';
Expand DownExpand Up@@ -403,6 +404,17 @@ export async function handleDeploy(options: ValidatedDeployOptions): Promise<Dep
);
const policies = parsePolicyOutputs(outputs, policySpecs);

// Parse runtime endpoint outputs
const endpointSpecs: { agentName: string; endpointName: string }[] = [];
for (const runtime of context.projectSpec.runtimes) {
if (runtime.endpoints) {
for (const endpointName of Object.keys(runtime.endpoints)) {
endpointSpecs.push({ agentName: runtime.name, endpointName });
}
}
}
const runtimeEndpoints = parseRuntimeEndpointOutputs(outputs, endpointSpecs);

// Parse gateway outputs
const gatewaySpecs =
mcpSpec?.agentCoreGateways?.reduce(
Expand All@@ -428,6 +440,7 @@ export async function handleDeploy(options: ValidatedDeployOptions): Promise<Dep
onlineEvalConfigs,
policyEngines,
policies,
runtimeEndpoints,
});
await configIO.writeDeployedState(deployedState);

Expand Down
1 change: 1 addition & 0 deletions src/cli/commands/remove/types.ts
Original file line numberDiff line numberDiff line change
Expand Up@@ -2,6 +2,7 @@ export type ResourceType =
| 'agent'
| 'gateway'
| 'gateway-target'
| 'runtime-endpoint'
| 'memory'
| 'credential'
| 'evaluator'
Expand Down
36 changes: 35 additions & 1 deletion src/cli/commands/status/action.ts
Original file line numberDiff line numberDiff line change
Expand Up@@ -18,11 +18,13 @@ export interface ResourceStatusEntry {
| 'evaluator'
| 'online-eval'
| 'policy-engine'
| 'policy';
| 'policy'
| 'runtime-endpoint';
name: string;
deploymentState: ResourceDeploymentState;
identifier?: string;
detail?: string;
parentName?: string;
error?: string;
invocationUrl?: string;
}
Expand DownExpand Up@@ -79,13 +81,15 @@ function diffResourceSet<TLocal extends { name: string }, TDeployed>({
getIdentifier,
getLocalDetail,
getDeployedKey,
getParentName,
}: {
resourceType: ResourceStatusEntry['resourceType'];
localItems: TLocal[];
deployedRecord: Record<string, TDeployed>;
getIdentifier: (deployed: TDeployed) => string | undefined;
getLocalDetail?: (item: TLocal) => string | undefined;
getDeployedKey?: (item: TLocal) => string;
getParentName?: (item: TLocal) => string | undefined;
}): ResourceStatusEntry[] {
const entries: ResourceStatusEntry[] = [];
const localKeys = new Set(localItems.map(item => (getDeployedKey ? getDeployedKey(item) : item.name)));
Expand All@@ -99,16 +103,20 @@ function diffResourceSet<TLocal extends { name: string }, TDeployed>({
deploymentState: deployed ? 'deployed' : 'local-only',
identifier: deployed ? getIdentifier(deployed) : undefined,
detail: getLocalDetail?.(item),
parentName: getParentName?.(item),
});
}

for (const [name, deployed] of Object.entries(deployedRecord)) {
if (!localKeys.has(name)) {
// For pending-removal entries, try to extract parentName from composite key
const slashIdx = name.indexOf('/');
entries.push({
resourceType,
name,
deploymentState: 'pending-removal',
identifier: getIdentifier(deployed),
parentName: getParentName && slashIdx > 0 ? name.substring(0, slashIdx) : undefined,
});
}
}
Expand DownExpand Up@@ -202,8 +210,34 @@ export function computeResourceStatuses(
getDeployedKey: item => `${item.engineName}/${item.name}`,
});

// Flatten runtime endpoints for diffing against deployed state
const localEndpoints: { name: string; agentName: string; version: number; description?: string }[] = [];
for (const runtime of project.runtimes) {
if (runtime.endpoints) {
for (const [epName, ep] of Object.entries(runtime.endpoints)) {
localEndpoints.push({
name: epName,
agentName: runtime.name,
version: ep.version,
description: ep.description,
});
}
}
}

const runtimeEndpoints = diffResourceSet({
resourceType: 'runtime-endpoint',
localItems: localEndpoints,
deployedRecord: resources?.runtimeEndpoints ?? {},
getIdentifier: deployed => deployed.endpointArn,
getLocalDetail: item => `v${item.version}${item.description ? ` — ${item.description}` : ''}`,
getDeployedKey: item => `${item.agentName}/${item.name}`,
getParentName: item => item.agentName,
});

return [
...agents,
...runtimeEndpoints,
...credentials,
...memories,
...gateways,
Expand Down
48 changes: 38 additions & 10 deletions src/cli/commands/status/command.tsx
Original file line numberDiff line numberDiff line change
Expand Up@@ -9,6 +9,7 @@

const VALID_RESOURCE_TYPES = [
'agent',
'runtime-endpoint',
'memory',
'credential',
'gateway',
Expand DownExpand Up@@ -58,7 +59,7 @@
.option('--target <name>', 'Select deployment target')
.option(
'--type <type>',
'Filter by resource type (agent, memory, credential, gateway, evaluator, online-eval, policy-engine, policy)'
'Filter by resource type (agent, runtime-endpoint, memory, credential, gateway, evaluator, online-eval, policy-engine, policy)'
)
.option('--state <state>', 'Filter by deployment state (deployed, local-only, pending-removal)')
.option('--runtime <name>', 'Filter to a specific runtime')
Expand DownExpand Up@@ -135,6 +136,7 @@

const filtered = filterResources(result.resources, cliOptions);
const agents = filtered.filter(r => r.resourceType === 'agent');
const runtimeEndpoints = filtered.filter(r => r.resourceType === 'runtime-endpoint');
const credentials = filtered.filter(r => r.resourceType === 'credential');
const memories = filtered.filter(r => r.resourceType === 'memory');
const gateways = filtered.filter(r => r.resourceType === 'gateway');
Expand All@@ -153,15 +155,41 @@
{agents.length > 0 && (
<Box flexDirection="column" marginTop={1}>
<Text bold>Agents</Text>
{agents.map(entry => (
<Box key={`${entry.resourceType}-${entry.name}`} flexDirection="column">
<ResourceEntry entry={entry} showRuntime />
{entry.invocationUrl && (
<Text dimColor>
{' '}URL: {entry.invocationUrl}
</Text>
)}
</Box>
{agents.map(entry => {
// Find endpoints belonging to this agent
const agentEndpoints = runtimeEndpoints.filter(ep => ep.parentName === entry.name);
return (
<Box key={`${entry.resourceType}-${entry.name}`} flexDirection="column">
<ResourceEntry entry={entry} showRuntime />
{entry.invocationUrl && (
<Text dimColor>
{' '}URL: {entry.invocationUrl}
</Text>
)}
{agentEndpoints.map(ep => (
Comment thread
notgitika marked this conversation as resolved.
Comment thread
notgitika marked this conversation as resolved.
<Text key={`${ep.parentName}/${ep.name}`}>
{' '}◉ {ep.name} <Text dimColor>{ep.detail}</Text>{' '}
<Text color={DEPLOYMENT_STATE_COLORS[ep.deploymentState] ?? 'gray'}>
[{DEPLOYMENT_STATE_LABELS[ep.deploymentState] ?? ep.deploymentState}]
</Text>
</Text>
))}
</Box>
);
})}
</Box>
)}

{agents.length === 0 && runtimeEndpoints.length > 0 && (
<Box flexDirection="column" marginTop={1}>
<Text bold>Runtime Endpoints</Text>
{runtimeEndpoints.map(ep => (
<Text key={`${ep.parentName}/${ep.name}`}>
{' '}◉ {ep.parentName}/{ep.name} <Text dimColor>{ep.detail}</Text>{' '}
<Text color={DEPLOYMENT_STATE_COLORS[ep.deploymentState] ?? 'gray'}>
Comment thread
notgitika marked this conversation as resolved.
Comment thread
notgitika marked this conversation as resolved.
[{DEPLOYMENT_STATE_LABELS[ep.deploymentState] ?? ep.deploymentState}]
</Text>
</Text>
))}
</Box>
)}
Expand DownExpand Up@@ -239,7 +267,7 @@
});
};

function ResourceEntry({ entry, showRuntime }: { entry: ResourceStatusEntry; showRuntime?: boolean }) {

Check warning on line 270 in src/cli/commands/status/command.tsx

View workflow job for this annotation

GitHub Actions/ lint

Fast refresh only works when a file only exports components. Move your component(s) to a separate file. If all exports are HOCs, add them to the `extraHOCs` option
return (
<Text>
{' '}
Expand Down
1 change: 1 addition & 0 deletions src/cli/logging/remove-logger.ts
Original file line numberDiff line numberDiff line change
Expand Up@@ -13,6 +13,7 @@ export interface RemoveLoggerOptions {
| 'credential'
| 'gateway'
| 'gateway-target'
| 'runtime-endpoint'
| 'evaluator'
| 'online-eval'
| 'policy-engine'
Expand Down
Loading
Loading
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Add copy buttons to all
 blocks\n(function() {\n function addCopyButtons() {\n document.querySelectorAll('pre code').forEach(function(codeBlock) {\n if (codeBlock.parentElement.hasAttribute('data-copy-added')) return;\n codeBlock.parentElement.setAttribute('data-copy-added', 'true');\n \n var btn = document.createElement('button');\n btn.textContent = 'Copy';\n btn.style.cssText = 'position:absolute;top:4px;right:4px;padding:2px 8px;font-size:11px;background:#4ecdc4;border:none;border-radius:4px;color:#1a1a2e;cursor:pointer;opacity:0.7;transition:opacity 0.2s;';\n btn.onmouseover = function() { this.style.opacity = '1'; };\n btn.onmouseout = function() { this.style.opacity = '0.7'; };\n btn.onclick = function() {\n navigator.clipboard.writeText(codeBlock.textContent).then(function() {\n btn.textContent = 'Copied!';\n setTimeout(function() { btn.textContent = 'Copy'; }, 1500);\n });\n };\n codeBlock.parentElement.style.position = 'relative';\n codeBlock.parentElement.appendChild(btn);\n });\n }\n \n addCopyButtons();\n \n // Re-run on dynamic content\n var observer = new MutationObserver(addCopyButtons);\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Add Copy Buttons to Code Blocks");
}
} catch(__e) { console.warn('[Userscript:Add Copy Buttons to Code Blocks]', __e); }
})();
(function(){
try {
var __m = "github.com";
var __re = new RegExp('^' + "github\\.com" + '
Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
19 commits
Select commit Hold shift + click to select a range
cb839bf
feat: add runtime endpoint support to AgentCore CLI
jariy17 Apr 25, 2026
3b29773
fix: correct output key prefix for runtime endpoint parsing
jariy17 Apr 25, 2026
33b81da
fix: remove .omc state files and unused useCallback import
jariy17 Apr 27, 2026
6b4f8f3
fix: shorten runtime endpoint description to prevent TUI overflow
jariy17 Apr 27, 2026
c1ad7ea
fix: validate runtime endpoint version is a positive integer
jariy17 Apr 27, 2026
f3face8
fix: use agent/endpoint composite key to prevent React key collision
jariy17 Apr 27, 2026
21147b1
fix: render runtime endpoints in status --type runtime-endpoint
jariy17 Apr 27, 2026
132f969
fix: add runtime-endpoint to status --help --type documentation
jariy17 Apr 27, 2026
3aba3e9
fix: return richer JSON response from add runtime-endpoint
jariy17 Apr 27, 2026
2bb5a86
fix: validate endpoint version against deployed runtime version
jariy17 Apr 27, 2026
844689a
chore: remove planning and bug bash docs from PR
jariy17 Apr 27, 2026
11a7a86
fix: use composite key and parentName for endpoint identification
jariy17 Apr 27, 2026
d6389c8
test: add comprehensive unit tests for RuntimeEndpointPrimitive
jariy17 Apr 27, 2026
b9f158f
fix: remove dead findGatewayTargetReferences stub
jariy17 Apr 27, 2026
0ff1db8
fix: use BasePrimitive configIO instead of ad-hoc ConfigIO in add()
jariy17 Apr 27, 2026
be47f35
fix: use Number() instead of parseInt in TUI version validation
jariy17 Apr 27, 2026
fd8e15c
Merge branch 'main' into feat/endpoint_based_abs
jariy17 Apr 27, 2026
ddbeff2
chore: fix prettier formatting
jariy17 Apr 27, 2026
117a07e
fix: use T[] instead of Array<T> to satisfy eslint array-type rule
jariy17 Apr 27, 2026
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
1 change: 1 addition & 0 deletions .gitignore
Original file line numberDiff line numberDiff line change
Expand Up@@ -67,6 +67,7 @@ ProtocolTesting/

# Auto-cloned CDK constructs (from scripts/bundle.mjs)
.cdk-constructs-clone/
.omc/

# Browser tests
browser-tests/.browser-test-env
Expand Down
42 changes: 42 additions & 0 deletions src/cli/cloudformation/outputs.ts
Original file line numberDiff line numberDiff line change
Expand Up@@ -6,6 +6,7 @@ import type {
OnlineEvalDeployedState,
PolicyDeployedState,
PolicyEngineDeployedState,
RuntimeEndpointDeployedState,
TargetDeployedState,
} from '../../schema';
import { getCredentialProvider } from '../aws';
Expand DownExpand Up@@ -338,6 +339,40 @@ export function parsePolicyOutputs(
return policies;
}

/**
* Parse stack outputs into deployed state for runtime endpoints.
*
* Output key pattern: ApplicationAgent{AgentPascal}Endpoint{AgentPascal}{EndpointPascal}(Id|Arn)Output{Hash}
* The Agent{PascalName} prefix comes from the AgentEnvironment construct in the CDK tree.
*/
export function parseRuntimeEndpointOutputs(
outputs: StackOutputs,
endpointSpecs: { agentName: string; endpointName: string }[]
): Record<string, RuntimeEndpointDeployedState> {
const endpoints: Record<string, RuntimeEndpointDeployedState> = {};
const outputKeys = Object.keys(outputs);

for (const { agentName, endpointName } of endpointSpecs) {
const agentPascal = toPascalId(agentName);
const endpointPascal = toPascalId('Endpoint', agentName, endpointName);
const idPrefix = `ApplicationAgent${agentPascal}${endpointPascal}IdOutput`;
const arnPrefix = `ApplicationAgent${agentPascal}${endpointPascal}ArnOutput`;

const idKey = outputKeys.find(k => k.startsWith(idPrefix));
const arnKey = outputKeys.find(k => k.startsWith(arnPrefix));

if (idKey && arnKey) {
const key = `${agentName}/${endpointName}`;
endpoints[key] = {
endpointId: outputs[idKey]!,
endpointArn: outputs[arnKey]!,
};
}
}

return endpoints;
}

export interface BuildDeployedStateOptions {
targetName: string;
stackName: string;
Expand All@@ -351,6 +386,7 @@ export interface BuildDeployedStateOptions {
onlineEvalConfigs?: Record<string, OnlineEvalDeployedState>;
policyEngines?: Record<string, PolicyEngineDeployedState>;
policies?: Record<string, PolicyDeployedState>;
runtimeEndpoints?: Record<string, RuntimeEndpointDeployedState>;
}

/**
Expand All@@ -370,6 +406,7 @@ export function buildDeployedState(opts: BuildDeployedStateOptions): DeployedSta
onlineEvalConfigs,
policyEngines,
policies,
runtimeEndpoints,
} = opts;
const targetState: TargetDeployedState = {
resources: {
Expand DownExpand Up@@ -404,6 +441,11 @@ export function buildDeployedState(opts: BuildDeployedStateOptions): DeployedSta
targetState.resources!.onlineEvalConfigs = onlineEvalConfigs;
}

// Add runtime endpoint state if endpoints exist
if (runtimeEndpoints && Object.keys(runtimeEndpoints).length > 0) {
targetState.resources!.runtimeEndpoints = runtimeEndpoints;
}

return {
targets: {
...existingState?.targets,
Expand Down
13 changes: 13 additions & 0 deletions src/cli/commands/deploy/actions.ts
Original file line numberDiff line numberDiff line change
Expand Up@@ -13,6 +13,7 @@ import {
parseOnlineEvalOutputs,
parsePolicyEngineOutputs,
parsePolicyOutputs,
parseRuntimeEndpointOutputs,
} from '../../cloudformation';
import { getErrorMessage } from '../../errors';
import { ExecLogger } from '../../logging';
Expand DownExpand Up@@ -403,6 +404,17 @@ export async function handleDeploy(options: ValidatedDeployOptions): Promise<Dep
);
const policies = parsePolicyOutputs(outputs, policySpecs);

// Parse runtime endpoint outputs
const endpointSpecs: { agentName: string; endpointName: string }[] = [];
for (const runtime of context.projectSpec.runtimes) {
if (runtime.endpoints) {
for (const endpointName of Object.keys(runtime.endpoints)) {
endpointSpecs.push({ agentName: runtime.name, endpointName });
}
}
}
const runtimeEndpoints = parseRuntimeEndpointOutputs(outputs, endpointSpecs);

// Parse gateway outputs
const gatewaySpecs =
mcpSpec?.agentCoreGateways?.reduce(
Expand All@@ -428,6 +440,7 @@ export async function handleDeploy(options: ValidatedDeployOptions): Promise<Dep
onlineEvalConfigs,
policyEngines,
policies,
runtimeEndpoints,
});
await configIO.writeDeployedState(deployedState);

Expand Down
1 change: 1 addition & 0 deletions src/cli/commands/remove/types.ts
Original file line numberDiff line numberDiff line change
Expand Up@@ -2,6 +2,7 @@ export type ResourceType =
| 'agent'
| 'gateway'
| 'gateway-target'
| 'runtime-endpoint'
| 'memory'
| 'credential'
| 'evaluator'
Expand Down
36 changes: 35 additions & 1 deletion src/cli/commands/status/action.ts
Original file line numberDiff line numberDiff line change
Expand Up@@ -18,11 +18,13 @@ export interface ResourceStatusEntry {
| 'evaluator'
| 'online-eval'
| 'policy-engine'
| 'policy';
| 'policy'
| 'runtime-endpoint';
name: string;
deploymentState: ResourceDeploymentState;
identifier?: string;
detail?: string;
parentName?: string;
error?: string;
invocationUrl?: string;
}
Expand DownExpand Up@@ -79,13 +81,15 @@ function diffResourceSet<TLocal extends { name: string }, TDeployed>({
getIdentifier,
getLocalDetail,
getDeployedKey,
getParentName,
}: {
resourceType: ResourceStatusEntry['resourceType'];
localItems: TLocal[];
deployedRecord: Record<string, TDeployed>;
getIdentifier: (deployed: TDeployed) => string | undefined;
getLocalDetail?: (item: TLocal) => string | undefined;
getDeployedKey?: (item: TLocal) => string;
getParentName?: (item: TLocal) => string | undefined;
}): ResourceStatusEntry[] {
const entries: ResourceStatusEntry[] = [];
const localKeys = new Set(localItems.map(item => (getDeployedKey ? getDeployedKey(item) : item.name)));
Expand All@@ -99,16 +103,20 @@ function diffResourceSet<TLocal extends { name: string }, TDeployed>({
deploymentState: deployed ? 'deployed' : 'local-only',
identifier: deployed ? getIdentifier(deployed) : undefined,
detail: getLocalDetail?.(item),
parentName: getParentName?.(item),
});
}

for (const [name, deployed] of Object.entries(deployedRecord)) {
if (!localKeys.has(name)) {
// For pending-removal entries, try to extract parentName from composite key
const slashIdx = name.indexOf('/');
entries.push({
resourceType,
name,
deploymentState: 'pending-removal',
identifier: getIdentifier(deployed),
parentName: getParentName && slashIdx > 0 ? name.substring(0, slashIdx) : undefined,
});
}
}
Expand DownExpand Up@@ -202,8 +210,34 @@ export function computeResourceStatuses(
getDeployedKey: item => `${item.engineName}/${item.name}`,
});

// Flatten runtime endpoints for diffing against deployed state
const localEndpoints: { name: string; agentName: string; version: number; description?: string }[] = [];
for (const runtime of project.runtimes) {
if (runtime.endpoints) {
for (const [epName, ep] of Object.entries(runtime.endpoints)) {
localEndpoints.push({
name: epName,
agentName: runtime.name,
version: ep.version,
description: ep.description,
});
}
}
}

const runtimeEndpoints = diffResourceSet({
resourceType: 'runtime-endpoint',
localItems: localEndpoints,
deployedRecord: resources?.runtimeEndpoints ?? {},
getIdentifier: deployed => deployed.endpointArn,
getLocalDetail: item => `v${item.version}${item.description ? ` — ${item.description}` : ''}`,
getDeployedKey: item => `${item.agentName}/${item.name}`,
getParentName: item => item.agentName,
});

return [
...agents,
...runtimeEndpoints,
...credentials,
...memories,
...gateways,
Expand Down
48 changes: 38 additions & 10 deletions src/cli/commands/status/command.tsx
Original file line numberDiff line numberDiff line change
Expand Up@@ -9,6 +9,7 @@

const VALID_RESOURCE_TYPES = [
'agent',
'runtime-endpoint',
'memory',
'credential',
'gateway',
Expand DownExpand Up@@ -58,7 +59,7 @@
.option('--target <name>', 'Select deployment target')
.option(
'--type <type>',
'Filter by resource type (agent, memory, credential, gateway, evaluator, online-eval, policy-engine, policy)'
'Filter by resource type (agent, runtime-endpoint, memory, credential, gateway, evaluator, online-eval, policy-engine, policy)'
)
.option('--state <state>', 'Filter by deployment state (deployed, local-only, pending-removal)')
.option('--runtime <name>', 'Filter to a specific runtime')
Expand DownExpand Up@@ -135,6 +136,7 @@

const filtered = filterResources(result.resources, cliOptions);
const agents = filtered.filter(r => r.resourceType === 'agent');
const runtimeEndpoints = filtered.filter(r => r.resourceType === 'runtime-endpoint');
const credentials = filtered.filter(r => r.resourceType === 'credential');
const memories = filtered.filter(r => r.resourceType === 'memory');
const gateways = filtered.filter(r => r.resourceType === 'gateway');
Expand All@@ -153,15 +155,41 @@
{agents.length > 0 && (
<Box flexDirection="column" marginTop={1}>
<Text bold>Agents</Text>
{agents.map(entry => (
<Box key={`${entry.resourceType}-${entry.name}`} flexDirection="column">
<ResourceEntry entry={entry} showRuntime />
{entry.invocationUrl && (
<Text dimColor>
{' '}URL: {entry.invocationUrl}
</Text>
)}
</Box>
{agents.map(entry => {
// Find endpoints belonging to this agent
const agentEndpoints = runtimeEndpoints.filter(ep => ep.parentName === entry.name);
return (
<Box key={`${entry.resourceType}-${entry.name}`} flexDirection="column">
<ResourceEntry entry={entry} showRuntime />
{entry.invocationUrl && (
<Text dimColor>
{' '}URL: {entry.invocationUrl}
</Text>
)}
{agentEndpoints.map(ep => (
Comment thread
notgitika marked this conversation as resolved.
Comment thread
notgitika marked this conversation as resolved.
<Text key={`${ep.parentName}/${ep.name}`}>
{' '}◉ {ep.name} <Text dimColor>{ep.detail}</Text>{' '}
<Text color={DEPLOYMENT_STATE_COLORS[ep.deploymentState] ?? 'gray'}>
[{DEPLOYMENT_STATE_LABELS[ep.deploymentState] ?? ep.deploymentState}]
</Text>
</Text>
))}
</Box>
);
})}
</Box>
)}

{agents.length === 0 && runtimeEndpoints.length > 0 && (
<Box flexDirection="column" marginTop={1}>
<Text bold>Runtime Endpoints</Text>
{runtimeEndpoints.map(ep => (
<Text key={`${ep.parentName}/${ep.name}`}>
{' '}◉ {ep.parentName}/{ep.name} <Text dimColor>{ep.detail}</Text>{' '}
<Text color={DEPLOYMENT_STATE_COLORS[ep.deploymentState] ?? 'gray'}>
Comment thread
notgitika marked this conversation as resolved.
Comment thread
notgitika marked this conversation as resolved.
[{DEPLOYMENT_STATE_LABELS[ep.deploymentState] ?? ep.deploymentState}]
</Text>
</Text>
))}
</Box>
)}
Expand DownExpand Up@@ -239,7 +267,7 @@
});
};

function ResourceEntry({ entry, showRuntime }: { entry: ResourceStatusEntry; showRuntime?: boolean }) {

Check warning on line 270 in src/cli/commands/status/command.tsx

View workflow job for this annotation

GitHub Actions/ lint

Fast refresh only works when a file only exports components. Move your component(s) to a separate file. If all exports are HOCs, add them to the `extraHOCs` option
return (
<Text>
{' '}
Expand Down
1 change: 1 addition & 0 deletions src/cli/logging/remove-logger.ts
Original file line numberDiff line numberDiff line change
Expand Up@@ -13,6 +13,7 @@ export interface RemoveLoggerOptions {
| 'credential'
| 'gateway'
| 'gateway-target'
| 'runtime-endpoint'
| 'evaluator'
| 'online-eval'
| 'policy-engine'
Expand Down
Loading
Loading
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Force GitHub README to respect dark mode\n(function() {\n var style = document.createElement('style');\n style.textContent = '\n .markdown-body {\n color-scheme: dark light;\n }\n .markdown-body pre { background: #161b22 !important; }\n .markdown-body code { background: rgba(110, 118, 129, 0.4) !important; }\n .markdown-body table th, .markdown-body table td { border-color: #30363d !important; }\n .markdown-body img { background: #0d1117; }\n .markdown-body blockquote { border-left-color: #8b949e; }\n .markdown-body hr { border-color: #30363d; }\n ';\n document.head.appendChild(style);\n})();", "GitHub Dark Mode README Fix"); } } catch(__e) { console.warn('[Userscript:GitHub Dark Mode README Fix]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
19 commits
Select commit Hold shift + click to select a range
cb839bf
feat: add runtime endpoint support to AgentCore CLI
jariy17 Apr 25, 2026
3b29773
fix: correct output key prefix for runtime endpoint parsing
jariy17 Apr 25, 2026
33b81da
fix: remove .omc state files and unused useCallback import
jariy17 Apr 27, 2026
6b4f8f3
fix: shorten runtime endpoint description to prevent TUI overflow
jariy17 Apr 27, 2026
c1ad7ea
fix: validate runtime endpoint version is a positive integer
jariy17 Apr 27, 2026
f3face8
fix: use agent/endpoint composite key to prevent React key collision
jariy17 Apr 27, 2026
21147b1
fix: render runtime endpoints in status --type runtime-endpoint
jariy17 Apr 27, 2026
132f969
fix: add runtime-endpoint to status --help --type documentation
jariy17 Apr 27, 2026
3aba3e9
fix: return richer JSON response from add runtime-endpoint
jariy17 Apr 27, 2026
2bb5a86
fix: validate endpoint version against deployed runtime version
jariy17 Apr 27, 2026
844689a
chore: remove planning and bug bash docs from PR
jariy17 Apr 27, 2026
11a7a86
fix: use composite key and parentName for endpoint identification
jariy17 Apr 27, 2026
d6389c8
test: add comprehensive unit tests for RuntimeEndpointPrimitive
jariy17 Apr 27, 2026
b9f158f
fix: remove dead findGatewayTargetReferences stub
jariy17 Apr 27, 2026
0ff1db8
fix: use BasePrimitive configIO instead of ad-hoc ConfigIO in add()
jariy17 Apr 27, 2026
be47f35
fix: use Number() instead of parseInt in TUI version validation
jariy17 Apr 27, 2026
fd8e15c
Merge branch 'main' into feat/endpoint_based_abs
jariy17 Apr 27, 2026
ddbeff2
chore: fix prettier formatting
jariy17 Apr 27, 2026
117a07e
fix: use T[] instead of Array<T> to satisfy eslint array-type rule
jariy17 Apr 27, 2026
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
1 change: 1 addition & 0 deletions .gitignore
Original file line numberDiff line numberDiff line change
Expand Up@@ -67,6 +67,7 @@ ProtocolTesting/

# Auto-cloned CDK constructs (from scripts/bundle.mjs)
.cdk-constructs-clone/
.omc/

# Browser tests
browser-tests/.browser-test-env
Expand Down
42 changes: 42 additions & 0 deletions src/cli/cloudformation/outputs.ts
Original file line numberDiff line numberDiff line change
Expand Up@@ -6,6 +6,7 @@ import type {
OnlineEvalDeployedState,
PolicyDeployedState,
PolicyEngineDeployedState,
RuntimeEndpointDeployedState,
TargetDeployedState,
} from '../../schema';
import { getCredentialProvider } from '../aws';
Expand DownExpand Up@@ -338,6 +339,40 @@ export function parsePolicyOutputs(
return policies;
}

/**
* Parse stack outputs into deployed state for runtime endpoints.
*
* Output key pattern: ApplicationAgent{AgentPascal}Endpoint{AgentPascal}{EndpointPascal}(Id|Arn)Output{Hash}
* The Agent{PascalName} prefix comes from the AgentEnvironment construct in the CDK tree.
*/
export function parseRuntimeEndpointOutputs(
outputs: StackOutputs,
endpointSpecs: { agentName: string; endpointName: string }[]
): Record<string, RuntimeEndpointDeployedState> {
const endpoints: Record<string, RuntimeEndpointDeployedState> = {};
const outputKeys = Object.keys(outputs);

for (const { agentName, endpointName } of endpointSpecs) {
const agentPascal = toPascalId(agentName);
const endpointPascal = toPascalId('Endpoint', agentName, endpointName);
const idPrefix = `ApplicationAgent${agentPascal}${endpointPascal}IdOutput`;
const arnPrefix = `ApplicationAgent${agentPascal}${endpointPascal}ArnOutput`;

const idKey = outputKeys.find(k => k.startsWith(idPrefix));
const arnKey = outputKeys.find(k => k.startsWith(arnPrefix));

if (idKey && arnKey) {
const key = `${agentName}/${endpointName}`;
endpoints[key] = {
endpointId: outputs[idKey]!,
endpointArn: outputs[arnKey]!,
};
}
}

return endpoints;
}

export interface BuildDeployedStateOptions {
targetName: string;
stackName: string;
Expand All@@ -351,6 +386,7 @@ export interface BuildDeployedStateOptions {
onlineEvalConfigs?: Record<string, OnlineEvalDeployedState>;
policyEngines?: Record<string, PolicyEngineDeployedState>;
policies?: Record<string, PolicyDeployedState>;
runtimeEndpoints?: Record<string, RuntimeEndpointDeployedState>;
}

/**
Expand All@@ -370,6 +406,7 @@ export function buildDeployedState(opts: BuildDeployedStateOptions): DeployedSta
onlineEvalConfigs,
policyEngines,
policies,
runtimeEndpoints,
} = opts;
const targetState: TargetDeployedState = {
resources: {
Expand DownExpand Up@@ -404,6 +441,11 @@ export function buildDeployedState(opts: BuildDeployedStateOptions): DeployedSta
targetState.resources!.onlineEvalConfigs = onlineEvalConfigs;
}

// Add runtime endpoint state if endpoints exist
if (runtimeEndpoints && Object.keys(runtimeEndpoints).length > 0) {
targetState.resources!.runtimeEndpoints = runtimeEndpoints;
}

return {
targets: {
...existingState?.targets,
Expand Down
13 changes: 13 additions & 0 deletions src/cli/commands/deploy/actions.ts
Original file line numberDiff line numberDiff line change
Expand Up@@ -13,6 +13,7 @@ import {
parseOnlineEvalOutputs,
parsePolicyEngineOutputs,
parsePolicyOutputs,
parseRuntimeEndpointOutputs,
} from '../../cloudformation';
import { getErrorMessage } from '../../errors';
import { ExecLogger } from '../../logging';
Expand DownExpand Up@@ -403,6 +404,17 @@ export async function handleDeploy(options: ValidatedDeployOptions): Promise<Dep
);
const policies = parsePolicyOutputs(outputs, policySpecs);

// Parse runtime endpoint outputs
const endpointSpecs: { agentName: string; endpointName: string }[] = [];
for (const runtime of context.projectSpec.runtimes) {
if (runtime.endpoints) {
for (const endpointName of Object.keys(runtime.endpoints)) {
endpointSpecs.push({ agentName: runtime.name, endpointName });
}
}
}
const runtimeEndpoints = parseRuntimeEndpointOutputs(outputs, endpointSpecs);

// Parse gateway outputs
const gatewaySpecs =
mcpSpec?.agentCoreGateways?.reduce(
Expand All@@ -428,6 +440,7 @@ export async function handleDeploy(options: ValidatedDeployOptions): Promise<Dep
onlineEvalConfigs,
policyEngines,
policies,
runtimeEndpoints,
});
await configIO.writeDeployedState(deployedState);

Expand Down
1 change: 1 addition & 0 deletions src/cli/commands/remove/types.ts
Original file line numberDiff line numberDiff line change
Expand Up@@ -2,6 +2,7 @@ export type ResourceType =
| 'agent'
| 'gateway'
| 'gateway-target'
| 'runtime-endpoint'
| 'memory'
| 'credential'
| 'evaluator'
Expand Down
36 changes: 35 additions & 1 deletion src/cli/commands/status/action.ts
Original file line numberDiff line numberDiff line change
Expand Up@@ -18,11 +18,13 @@ export interface ResourceStatusEntry {
| 'evaluator'
| 'online-eval'
| 'policy-engine'
| 'policy';
| 'policy'
| 'runtime-endpoint';
name: string;
deploymentState: ResourceDeploymentState;
identifier?: string;
detail?: string;
parentName?: string;
error?: string;
invocationUrl?: string;
}
Expand DownExpand Up@@ -79,13 +81,15 @@ function diffResourceSet<TLocal extends { name: string }, TDeployed>({
getIdentifier,
getLocalDetail,
getDeployedKey,
getParentName,
}: {
resourceType: ResourceStatusEntry['resourceType'];
localItems: TLocal[];
deployedRecord: Record<string, TDeployed>;
getIdentifier: (deployed: TDeployed) => string | undefined;
getLocalDetail?: (item: TLocal) => string | undefined;
getDeployedKey?: (item: TLocal) => string;
getParentName?: (item: TLocal) => string | undefined;
}): ResourceStatusEntry[] {
const entries: ResourceStatusEntry[] = [];
const localKeys = new Set(localItems.map(item => (getDeployedKey ? getDeployedKey(item) : item.name)));
Expand All@@ -99,16 +103,20 @@ function diffResourceSet<TLocal extends { name: string }, TDeployed>({
deploymentState: deployed ? 'deployed' : 'local-only',
identifier: deployed ? getIdentifier(deployed) : undefined,
detail: getLocalDetail?.(item),
parentName: getParentName?.(item),
});
}

for (const [name, deployed] of Object.entries(deployedRecord)) {
if (!localKeys.has(name)) {
// For pending-removal entries, try to extract parentName from composite key
const slashIdx = name.indexOf('/');
entries.push({
resourceType,
name,
deploymentState: 'pending-removal',
identifier: getIdentifier(deployed),
parentName: getParentName && slashIdx > 0 ? name.substring(0, slashIdx) : undefined,
});
}
}
Expand DownExpand Up@@ -202,8 +210,34 @@ export function computeResourceStatuses(
getDeployedKey: item => `${item.engineName}/${item.name}`,
});

// Flatten runtime endpoints for diffing against deployed state
const localEndpoints: { name: string; agentName: string; version: number; description?: string }[] = [];
for (const runtime of project.runtimes) {
if (runtime.endpoints) {
for (const [epName, ep] of Object.entries(runtime.endpoints)) {
localEndpoints.push({
name: epName,
agentName: runtime.name,
version: ep.version,
description: ep.description,
});
}
}
}

const runtimeEndpoints = diffResourceSet({
resourceType: 'runtime-endpoint',
localItems: localEndpoints,
deployedRecord: resources?.runtimeEndpoints ?? {},
getIdentifier: deployed => deployed.endpointArn,
getLocalDetail: item => `v${item.version}${item.description ? ` — ${item.description}` : ''}`,
getDeployedKey: item => `${item.agentName}/${item.name}`,
getParentName: item => item.agentName,
});

return [
...agents,
...runtimeEndpoints,
...credentials,
...memories,
...gateways,
Expand Down
48 changes: 38 additions & 10 deletions src/cli/commands/status/command.tsx
Original file line numberDiff line numberDiff line change
Expand Up@@ -9,6 +9,7 @@

const VALID_RESOURCE_TYPES = [
'agent',
'runtime-endpoint',
'memory',
'credential',
'gateway',
Expand DownExpand Up@@ -58,7 +59,7 @@
.option('--target <name>', 'Select deployment target')
.option(
'--type <type>',
'Filter by resource type (agent, memory, credential, gateway, evaluator, online-eval, policy-engine, policy)'
'Filter by resource type (agent, runtime-endpoint, memory, credential, gateway, evaluator, online-eval, policy-engine, policy)'
)
.option('--state <state>', 'Filter by deployment state (deployed, local-only, pending-removal)')
.option('--runtime <name>', 'Filter to a specific runtime')
Expand DownExpand Up@@ -135,6 +136,7 @@

const filtered = filterResources(result.resources, cliOptions);
const agents = filtered.filter(r => r.resourceType === 'agent');
const runtimeEndpoints = filtered.filter(r => r.resourceType === 'runtime-endpoint');
const credentials = filtered.filter(r => r.resourceType === 'credential');
const memories = filtered.filter(r => r.resourceType === 'memory');
const gateways = filtered.filter(r => r.resourceType === 'gateway');
Expand All@@ -153,15 +155,41 @@
{agents.length > 0 && (
<Box flexDirection="column" marginTop={1}>
<Text bold>Agents</Text>
{agents.map(entry => (
<Box key={`${entry.resourceType}-${entry.name}`} flexDirection="column">
<ResourceEntry entry={entry} showRuntime />
{entry.invocationUrl && (
<Text dimColor>
{' '}URL: {entry.invocationUrl}
</Text>
)}
</Box>
{agents.map(entry => {
// Find endpoints belonging to this agent
const agentEndpoints = runtimeEndpoints.filter(ep => ep.parentName === entry.name);
return (
<Box key={`${entry.resourceType}-${entry.name}`} flexDirection="column">
<ResourceEntry entry={entry} showRuntime />
{entry.invocationUrl && (
<Text dimColor>
{' '}URL: {entry.invocationUrl}
</Text>
)}
{agentEndpoints.map(ep => (
Comment thread
notgitika marked this conversation as resolved.
Comment thread
notgitika marked this conversation as resolved.
<Text key={`${ep.parentName}/${ep.name}`}>
{' '}◉ {ep.name} <Text dimColor>{ep.detail}</Text>{' '}
<Text color={DEPLOYMENT_STATE_COLORS[ep.deploymentState] ?? 'gray'}>
[{DEPLOYMENT_STATE_LABELS[ep.deploymentState] ?? ep.deploymentState}]
</Text>
</Text>
))}
</Box>
);
})}
</Box>
)}

{agents.length === 0 && runtimeEndpoints.length > 0 && (
<Box flexDirection="column" marginTop={1}>
<Text bold>Runtime Endpoints</Text>
{runtimeEndpoints.map(ep => (
<Text key={`${ep.parentName}/${ep.name}`}>
{' '}◉ {ep.parentName}/{ep.name} <Text dimColor>{ep.detail}</Text>{' '}
<Text color={DEPLOYMENT_STATE_COLORS[ep.deploymentState] ?? 'gray'}>
Comment thread
notgitika marked this conversation as resolved.
Comment thread
notgitika marked this conversation as resolved.
[{DEPLOYMENT_STATE_LABELS[ep.deploymentState] ?? ep.deploymentState}]
</Text>
</Text>
))}
</Box>
)}
Expand DownExpand Up@@ -239,7 +267,7 @@
});
};

function ResourceEntry({ entry, showRuntime }: { entry: ResourceStatusEntry; showRuntime?: boolean }) {

Check warning on line 270 in src/cli/commands/status/command.tsx

View workflow job for this annotation

GitHub Actions/ lint

Fast refresh only works when a file only exports components. Move your component(s) to a separate file. If all exports are HOCs, add them to the `extraHOCs` option
return (
<Text>
{' '}
Expand Down
1 change: 1 addition & 0 deletions src/cli/logging/remove-logger.ts
Original file line numberDiff line numberDiff line change
Expand Up@@ -13,6 +13,7 @@ export interface RemoveLoggerOptions {
| 'credential'
| 'gateway'
| 'gateway-target'
| 'runtime-endpoint'
| 'evaluator'
| 'online-eval'
| 'policy-engine'
Expand Down
Loading
Loading
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Highlight search terms from Google/DuckDuckGo/Bing referrer\n(function() {\n var ref = document.referrer;\n var terms = [];\n \n if (ref.includes('google.com') || ref.includes('duckduckgo.com') || ref.includes('bing.com')) {\n var url = new URL(ref);\n var q = url.searchParams.get('q') || url.searchParams.get('p');\n if (q) {\n terms = q.split(/\\s+/).filter(function(t) { return t.length > 2; });\n }\n }\n \n if (terms.length === 0) return;\n \n var style = document.createElement('style');\n style.textContent = '.userscript-highlight { background: #fbbf24; color: #1a1a2e; padding: 1px 3px; border-radius: 2px; }';\n document.head.appendChild(style);\n \n function highlight(node) {\n if (node.nodeType === 3) { // text node\n var text = node.textContent;\n var found = false;\n terms.forEach(function(term) {\n var regex = new RegExp('(' + term.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\') + ')', 'gi');\n if (regex.test(text)) {\n found = true;\n var frag = document.createDocumentFragment();\n var parts = text.split(regex);\n parts.forEach(function(part, i) {\n if (i % 2 === 0) {\n frag.appendChild(document.createTextNode(part));\n } else {\n var span = document.createElement('span');\n span.className = 'userscript-highlight';\n span.textContent = part;\n frag.appendChild(span);\n }\n });\n node.parentNode.replaceChild(frag, node);\n }\n });\n } else if (node.nodeType === 1 && node.childNodes) { // element\n var skipTags = ['SCRIPT', 'STYLE', 'NOSCRIPT', 'TEXTAREA', 'INPUT', 'SELECT'];\n if (!skipTags.includes(node.tagName)) {\n Array.from(node.childNodes).forEach(highlight);\n }\n }\n }\n \n highlight(document.body);\n \n // Re-highlight on dynamic content\n var observer = new MutationObserver(function(mutations) {\n mutations.forEach(function(m) {\n m.addedNodes.forEach(function(node) {\n if (node.nodeType === 1 || node.nodeType === 3) highlight(node);\n });\n });\n });\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Highlight Search Terms"); } } catch(__e) { console.warn('[Userscript:Highlight Search Terms]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
19 commits
Select commit Hold shift + click to select a range
cb839bf
feat: add runtime endpoint support to AgentCore CLI
jariy17 Apr 25, 2026
3b29773
fix: correct output key prefix for runtime endpoint parsing
jariy17 Apr 25, 2026
33b81da
fix: remove .omc state files and unused useCallback import
jariy17 Apr 27, 2026
6b4f8f3
fix: shorten runtime endpoint description to prevent TUI overflow
jariy17 Apr 27, 2026
c1ad7ea
fix: validate runtime endpoint version is a positive integer
jariy17 Apr 27, 2026
f3face8
fix: use agent/endpoint composite key to prevent React key collision
jariy17 Apr 27, 2026
21147b1
fix: render runtime endpoints in status --type runtime-endpoint
jariy17 Apr 27, 2026
132f969
fix: add runtime-endpoint to status --help --type documentation
jariy17 Apr 27, 2026
3aba3e9
fix: return richer JSON response from add runtime-endpoint
jariy17 Apr 27, 2026
2bb5a86
fix: validate endpoint version against deployed runtime version
jariy17 Apr 27, 2026
844689a
chore: remove planning and bug bash docs from PR
jariy17 Apr 27, 2026
11a7a86
fix: use composite key and parentName for endpoint identification
jariy17 Apr 27, 2026
d6389c8
test: add comprehensive unit tests for RuntimeEndpointPrimitive
jariy17 Apr 27, 2026
b9f158f
fix: remove dead findGatewayTargetReferences stub
jariy17 Apr 27, 2026
0ff1db8
fix: use BasePrimitive configIO instead of ad-hoc ConfigIO in add()
jariy17 Apr 27, 2026
be47f35
fix: use Number() instead of parseInt in TUI version validation
jariy17 Apr 27, 2026
fd8e15c
Merge branch 'main' into feat/endpoint_based_abs
jariy17 Apr 27, 2026
ddbeff2
chore: fix prettier formatting
jariy17 Apr 27, 2026
117a07e
fix: use T[] instead of Array<T> to satisfy eslint array-type rule
jariy17 Apr 27, 2026
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
1 change: 1 addition & 0 deletions .gitignore
Original file line numberDiff line numberDiff line change
Expand Up@@ -67,6 +67,7 @@ ProtocolTesting/

# Auto-cloned CDK constructs (from scripts/bundle.mjs)
.cdk-constructs-clone/
.omc/

# Browser tests
browser-tests/.browser-test-env
Expand Down
42 changes: 42 additions & 0 deletions src/cli/cloudformation/outputs.ts
Original file line numberDiff line numberDiff line change
Expand Up@@ -6,6 +6,7 @@ import type {
OnlineEvalDeployedState,
PolicyDeployedState,
PolicyEngineDeployedState,
RuntimeEndpointDeployedState,
TargetDeployedState,
} from '../../schema';
import { getCredentialProvider } from '../aws';
Expand DownExpand Up@@ -338,6 +339,40 @@ export function parsePolicyOutputs(
return policies;
}

/**
* Parse stack outputs into deployed state for runtime endpoints.
*
* Output key pattern: ApplicationAgent{AgentPascal}Endpoint{AgentPascal}{EndpointPascal}(Id|Arn)Output{Hash}
* The Agent{PascalName} prefix comes from the AgentEnvironment construct in the CDK tree.
*/
export function parseRuntimeEndpointOutputs(
outputs: StackOutputs,
endpointSpecs: { agentName: string; endpointName: string }[]
): Record<string, RuntimeEndpointDeployedState> {
const endpoints: Record<string, RuntimeEndpointDeployedState> = {};
const outputKeys = Object.keys(outputs);

for (const { agentName, endpointName } of endpointSpecs) {
const agentPascal = toPascalId(agentName);
const endpointPascal = toPascalId('Endpoint', agentName, endpointName);
const idPrefix = `ApplicationAgent${agentPascal}${endpointPascal}IdOutput`;
const arnPrefix = `ApplicationAgent${agentPascal}${endpointPascal}ArnOutput`;

const idKey = outputKeys.find(k => k.startsWith(idPrefix));
const arnKey = outputKeys.find(k => k.startsWith(arnPrefix));

if (idKey && arnKey) {
const key = `${agentName}/${endpointName}`;
endpoints[key] = {
endpointId: outputs[idKey]!,
endpointArn: outputs[arnKey]!,
};
}
}

return endpoints;
}

export interface BuildDeployedStateOptions {
targetName: string;
stackName: string;
Expand All@@ -351,6 +386,7 @@ export interface BuildDeployedStateOptions {
onlineEvalConfigs?: Record<string, OnlineEvalDeployedState>;
policyEngines?: Record<string, PolicyEngineDeployedState>;
policies?: Record<string, PolicyDeployedState>;
runtimeEndpoints?: Record<string, RuntimeEndpointDeployedState>;
}

/**
Expand All@@ -370,6 +406,7 @@ export function buildDeployedState(opts: BuildDeployedStateOptions): DeployedSta
onlineEvalConfigs,
policyEngines,
policies,
runtimeEndpoints,
} = opts;
const targetState: TargetDeployedState = {
resources: {
Expand DownExpand Up@@ -404,6 +441,11 @@ export function buildDeployedState(opts: BuildDeployedStateOptions): DeployedSta
targetState.resources!.onlineEvalConfigs = onlineEvalConfigs;
}

// Add runtime endpoint state if endpoints exist
if (runtimeEndpoints && Object.keys(runtimeEndpoints).length > 0) {
targetState.resources!.runtimeEndpoints = runtimeEndpoints;
}

return {
targets: {
...existingState?.targets,
Expand Down
13 changes: 13 additions & 0 deletions src/cli/commands/deploy/actions.ts
Original file line numberDiff line numberDiff line change
Expand Up@@ -13,6 +13,7 @@ import {
parseOnlineEvalOutputs,
parsePolicyEngineOutputs,
parsePolicyOutputs,
parseRuntimeEndpointOutputs,
} from '../../cloudformation';
import { getErrorMessage } from '../../errors';
import { ExecLogger } from '../../logging';
Expand DownExpand Up@@ -403,6 +404,17 @@ export async function handleDeploy(options: ValidatedDeployOptions): Promise<Dep
);
const policies = parsePolicyOutputs(outputs, policySpecs);

// Parse runtime endpoint outputs
const endpointSpecs: { agentName: string; endpointName: string }[] = [];
for (const runtime of context.projectSpec.runtimes) {
if (runtime.endpoints) {
for (const endpointName of Object.keys(runtime.endpoints)) {
endpointSpecs.push({ agentName: runtime.name, endpointName });
}
}
}
const runtimeEndpoints = parseRuntimeEndpointOutputs(outputs, endpointSpecs);

// Parse gateway outputs
const gatewaySpecs =
mcpSpec?.agentCoreGateways?.reduce(
Expand All@@ -428,6 +440,7 @@ export async function handleDeploy(options: ValidatedDeployOptions): Promise<Dep
onlineEvalConfigs,
policyEngines,
policies,
runtimeEndpoints,
});
await configIO.writeDeployedState(deployedState);

Expand Down
1 change: 1 addition & 0 deletions src/cli/commands/remove/types.ts
Original file line numberDiff line numberDiff line change
Expand Up@@ -2,6 +2,7 @@ export type ResourceType =
| 'agent'
| 'gateway'
| 'gateway-target'
| 'runtime-endpoint'
| 'memory'
| 'credential'
| 'evaluator'
Expand Down
36 changes: 35 additions & 1 deletion src/cli/commands/status/action.ts
Original file line numberDiff line numberDiff line change
Expand Up@@ -18,11 +18,13 @@ export interface ResourceStatusEntry {
| 'evaluator'
| 'online-eval'
| 'policy-engine'
| 'policy';
| 'policy'
| 'runtime-endpoint';
name: string;
deploymentState: ResourceDeploymentState;
identifier?: string;
detail?: string;
parentName?: string;
error?: string;
invocationUrl?: string;
}
Expand DownExpand Up@@ -79,13 +81,15 @@ function diffResourceSet<TLocal extends { name: string }, TDeployed>({
getIdentifier,
getLocalDetail,
getDeployedKey,
getParentName,
}: {
resourceType: ResourceStatusEntry['resourceType'];
localItems: TLocal[];
deployedRecord: Record<string, TDeployed>;
getIdentifier: (deployed: TDeployed) => string | undefined;
getLocalDetail?: (item: TLocal) => string | undefined;
getDeployedKey?: (item: TLocal) => string;
getParentName?: (item: TLocal) => string | undefined;
}): ResourceStatusEntry[] {
const entries: ResourceStatusEntry[] = [];
const localKeys = new Set(localItems.map(item => (getDeployedKey ? getDeployedKey(item) : item.name)));
Expand All@@ -99,16 +103,20 @@ function diffResourceSet<TLocal extends { name: string }, TDeployed>({
deploymentState: deployed ? 'deployed' : 'local-only',
identifier: deployed ? getIdentifier(deployed) : undefined,
detail: getLocalDetail?.(item),
parentName: getParentName?.(item),
});
}

for (const [name, deployed] of Object.entries(deployedRecord)) {
if (!localKeys.has(name)) {
// For pending-removal entries, try to extract parentName from composite key
const slashIdx = name.indexOf('/');
entries.push({
resourceType,
name,
deploymentState: 'pending-removal',
identifier: getIdentifier(deployed),
parentName: getParentName && slashIdx > 0 ? name.substring(0, slashIdx) : undefined,
});
}
}
Expand DownExpand Up@@ -202,8 +210,34 @@ export function computeResourceStatuses(
getDeployedKey: item => `${item.engineName}/${item.name}`,
});

// Flatten runtime endpoints for diffing against deployed state
const localEndpoints: { name: string; agentName: string; version: number; description?: string }[] = [];
for (const runtime of project.runtimes) {
if (runtime.endpoints) {
for (const [epName, ep] of Object.entries(runtime.endpoints)) {
localEndpoints.push({
name: epName,
agentName: runtime.name,
version: ep.version,
description: ep.description,
});
}
}
}

const runtimeEndpoints = diffResourceSet({
resourceType: 'runtime-endpoint',
localItems: localEndpoints,
deployedRecord: resources?.runtimeEndpoints ?? {},
getIdentifier: deployed => deployed.endpointArn,
getLocalDetail: item => `v${item.version}${item.description ? ` — ${item.description}` : ''}`,
getDeployedKey: item => `${item.agentName}/${item.name}`,
getParentName: item => item.agentName,
});

return [
...agents,
...runtimeEndpoints,
...credentials,
...memories,
...gateways,
Expand Down
48 changes: 38 additions & 10 deletions src/cli/commands/status/command.tsx
Original file line numberDiff line numberDiff line change
Expand Up@@ -9,6 +9,7 @@

const VALID_RESOURCE_TYPES = [
'agent',
'runtime-endpoint',
'memory',
'credential',
'gateway',
Expand DownExpand Up@@ -58,7 +59,7 @@
.option('--target <name>', 'Select deployment target')
.option(
'--type <type>',
'Filter by resource type (agent, memory, credential, gateway, evaluator, online-eval, policy-engine, policy)'
'Filter by resource type (agent, runtime-endpoint, memory, credential, gateway, evaluator, online-eval, policy-engine, policy)'
)
.option('--state <state>', 'Filter by deployment state (deployed, local-only, pending-removal)')
.option('--runtime <name>', 'Filter to a specific runtime')
Expand DownExpand Up@@ -135,6 +136,7 @@

const filtered = filterResources(result.resources, cliOptions);
const agents = filtered.filter(r => r.resourceType === 'agent');
const runtimeEndpoints = filtered.filter(r => r.resourceType === 'runtime-endpoint');
const credentials = filtered.filter(r => r.resourceType === 'credential');
const memories = filtered.filter(r => r.resourceType === 'memory');
const gateways = filtered.filter(r => r.resourceType === 'gateway');
Expand All@@ -153,15 +155,41 @@
{agents.length > 0 && (
<Box flexDirection="column" marginTop={1}>
<Text bold>Agents</Text>
{agents.map(entry => (
<Box key={`${entry.resourceType}-${entry.name}`} flexDirection="column">
<ResourceEntry entry={entry} showRuntime />
{entry.invocationUrl && (
<Text dimColor>
{' '}URL: {entry.invocationUrl}
</Text>
)}
</Box>
{agents.map(entry => {
// Find endpoints belonging to this agent
const agentEndpoints = runtimeEndpoints.filter(ep => ep.parentName === entry.name);
return (
<Box key={`${entry.resourceType}-${entry.name}`} flexDirection="column">
<ResourceEntry entry={entry} showRuntime />
{entry.invocationUrl && (
<Text dimColor>
{' '}URL: {entry.invocationUrl}
</Text>
)}
{agentEndpoints.map(ep => (
Comment thread
notgitika marked this conversation as resolved.
Comment thread
notgitika marked this conversation as resolved.
<Text key={`${ep.parentName}/${ep.name}`}>
{' '}◉ {ep.name} <Text dimColor>{ep.detail}</Text>{' '}
<Text color={DEPLOYMENT_STATE_COLORS[ep.deploymentState] ?? 'gray'}>
[{DEPLOYMENT_STATE_LABELS[ep.deploymentState] ?? ep.deploymentState}]
</Text>
</Text>
))}
</Box>
);
})}
</Box>
)}

{agents.length === 0 && runtimeEndpoints.length > 0 && (
<Box flexDirection="column" marginTop={1}>
<Text bold>Runtime Endpoints</Text>
{runtimeEndpoints.map(ep => (
<Text key={`${ep.parentName}/${ep.name}`}>
{' '}◉ {ep.parentName}/{ep.name} <Text dimColor>{ep.detail}</Text>{' '}
<Text color={DEPLOYMENT_STATE_COLORS[ep.deploymentState] ?? 'gray'}>
Comment thread
notgitika marked this conversation as resolved.
Comment thread
notgitika marked this conversation as resolved.
[{DEPLOYMENT_STATE_LABELS[ep.deploymentState] ?? ep.deploymentState}]
</Text>
</Text>
))}
</Box>
)}
Expand DownExpand Up@@ -239,7 +267,7 @@
});
};

function ResourceEntry({ entry, showRuntime }: { entry: ResourceStatusEntry; showRuntime?: boolean }) {

Check warning on line 270 in src/cli/commands/status/command.tsx

View workflow job for this annotation

GitHub Actions/ lint

Fast refresh only works when a file only exports components. Move your component(s) to a separate file. If all exports are HOCs, add them to the `extraHOCs` option
return (
<Text>
{' '}
Expand Down
1 change: 1 addition & 0 deletions src/cli/logging/remove-logger.ts
Original file line numberDiff line numberDiff line change
Expand Up@@ -13,6 +13,7 @@ export interface RemoveLoggerOptions {
| 'credential'
| 'gateway'
| 'gateway-target'
| 'runtime-endpoint'
| 'evaluator'
| 'online-eval'
| 'policy-engine'
Expand Down
Loading
Loading
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Strip utm_, fbclid, gclid, etc. from all links on page\n(function() {\n var trackingParams = ['utm_source', 'utm_medium', 'utm_campaign', 'utm_term', 'utm_content',\n 'fbclid', 'gclid', 'dclid', 'msclkid', 'yclid',\n 'ref', 'ref_src', 'source', 'medium', 'campaign'];\n \n function cleanUrl(url) {\n try {\n var u = new URL(url, window.location.origin);\n var changed = false;\n trackingParams.forEach(function(p) {\n if (u.searchParams.has(p)) {\n u.searchParams.delete(p);\n changed = true;\n }\n });\n return changed ? u.toString() : url;\n } catch (e) {\n return url;\n }\n }\n \n function cleanLinks() {\n document.querySelectorAll('a[href]').forEach(function(a) {\n var clean = cleanUrl(a.href);\n if (clean !== a.href) a.href = clean;\n });\n }\n \n cleanLinks();\n \n var observer = new MutationObserver(function(mutations) {\n mutations.forEach(function(m) {\n m.addedNodes.forEach(function(node) {\n if (node.nodeType === 1) {\n if (node.tagName === 'A') cleanLinks();\n node.querySelectorAll('a[href]').forEach(function(a) {\n var clean = cleanUrl(a.href);\n if (clean !== a.href) a.href = clean;\n });\n }\n });\n });\n });\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Remove Tracking Parameters from Links"); } } catch(__e) { console.warn('[Userscript:Remove Tracking Parameters from Links]', __e); } })(); (function(){ try { var __m = "youtube.com"; var __re = new RegExp('^' + "youtube\\.com" + '
Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
19 commits
Select commit Hold shift + click to select a range
cb839bf
feat: add runtime endpoint support to AgentCore CLI
jariy17 Apr 25, 2026
3b29773
fix: correct output key prefix for runtime endpoint parsing
jariy17 Apr 25, 2026
33b81da
fix: remove .omc state files and unused useCallback import
jariy17 Apr 27, 2026
6b4f8f3
fix: shorten runtime endpoint description to prevent TUI overflow
jariy17 Apr 27, 2026
c1ad7ea
fix: validate runtime endpoint version is a positive integer
jariy17 Apr 27, 2026
f3face8
fix: use agent/endpoint composite key to prevent React key collision
jariy17 Apr 27, 2026
21147b1
fix: render runtime endpoints in status --type runtime-endpoint
jariy17 Apr 27, 2026
132f969
fix: add runtime-endpoint to status --help --type documentation
jariy17 Apr 27, 2026
3aba3e9
fix: return richer JSON response from add runtime-endpoint
jariy17 Apr 27, 2026
2bb5a86
fix: validate endpoint version against deployed runtime version
jariy17 Apr 27, 2026
844689a
chore: remove planning and bug bash docs from PR
jariy17 Apr 27, 2026
11a7a86
fix: use composite key and parentName for endpoint identification
jariy17 Apr 27, 2026
d6389c8
test: add comprehensive unit tests for RuntimeEndpointPrimitive
jariy17 Apr 27, 2026
b9f158f
fix: remove dead findGatewayTargetReferences stub
jariy17 Apr 27, 2026
0ff1db8
fix: use BasePrimitive configIO instead of ad-hoc ConfigIO in add()
jariy17 Apr 27, 2026
be47f35
fix: use Number() instead of parseInt in TUI version validation
jariy17 Apr 27, 2026
fd8e15c
Merge branch 'main' into feat/endpoint_based_abs
jariy17 Apr 27, 2026
ddbeff2
chore: fix prettier formatting
jariy17 Apr 27, 2026
117a07e
fix: use T[] instead of Array<T> to satisfy eslint array-type rule
jariy17 Apr 27, 2026
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
1 change: 1 addition & 0 deletions .gitignore
Original file line numberDiff line numberDiff line change
Expand Up@@ -67,6 +67,7 @@ ProtocolTesting/

# Auto-cloned CDK constructs (from scripts/bundle.mjs)
.cdk-constructs-clone/
.omc/

# Browser tests
browser-tests/.browser-test-env
Expand Down
42 changes: 42 additions & 0 deletions src/cli/cloudformation/outputs.ts
Original file line numberDiff line numberDiff line change
Expand Up@@ -6,6 +6,7 @@ import type {
OnlineEvalDeployedState,
PolicyDeployedState,
PolicyEngineDeployedState,
RuntimeEndpointDeployedState,
TargetDeployedState,
} from '../../schema';
import { getCredentialProvider } from '../aws';
Expand DownExpand Up@@ -338,6 +339,40 @@ export function parsePolicyOutputs(
return policies;
}

/**
* Parse stack outputs into deployed state for runtime endpoints.
*
* Output key pattern: ApplicationAgent{AgentPascal}Endpoint{AgentPascal}{EndpointPascal}(Id|Arn)Output{Hash}
* The Agent{PascalName} prefix comes from the AgentEnvironment construct in the CDK tree.
*/
export function parseRuntimeEndpointOutputs(
outputs: StackOutputs,
endpointSpecs: { agentName: string; endpointName: string }[]
): Record<string, RuntimeEndpointDeployedState> {
const endpoints: Record<string, RuntimeEndpointDeployedState> = {};
const outputKeys = Object.keys(outputs);

for (const { agentName, endpointName } of endpointSpecs) {
const agentPascal = toPascalId(agentName);
const endpointPascal = toPascalId('Endpoint', agentName, endpointName);
const idPrefix = `ApplicationAgent${agentPascal}${endpointPascal}IdOutput`;
const arnPrefix = `ApplicationAgent${agentPascal}${endpointPascal}ArnOutput`;

const idKey = outputKeys.find(k => k.startsWith(idPrefix));
const arnKey = outputKeys.find(k => k.startsWith(arnPrefix));

if (idKey && arnKey) {
const key = `${agentName}/${endpointName}`;
endpoints[key] = {
endpointId: outputs[idKey]!,
endpointArn: outputs[arnKey]!,
};
}
}

return endpoints;
}

export interface BuildDeployedStateOptions {
targetName: string;
stackName: string;
Expand All@@ -351,6 +386,7 @@ export interface BuildDeployedStateOptions {
onlineEvalConfigs?: Record<string, OnlineEvalDeployedState>;
policyEngines?: Record<string, PolicyEngineDeployedState>;
policies?: Record<string, PolicyDeployedState>;
runtimeEndpoints?: Record<string, RuntimeEndpointDeployedState>;
}

/**
Expand All@@ -370,6 +406,7 @@ export function buildDeployedState(opts: BuildDeployedStateOptions): DeployedSta
onlineEvalConfigs,
policyEngines,
policies,
runtimeEndpoints,
} = opts;
const targetState: TargetDeployedState = {
resources: {
Expand DownExpand Up@@ -404,6 +441,11 @@ export function buildDeployedState(opts: BuildDeployedStateOptions): DeployedSta
targetState.resources!.onlineEvalConfigs = onlineEvalConfigs;
}

// Add runtime endpoint state if endpoints exist
if (runtimeEndpoints && Object.keys(runtimeEndpoints).length > 0) {
targetState.resources!.runtimeEndpoints = runtimeEndpoints;
}

return {
targets: {
...existingState?.targets,
Expand Down
13 changes: 13 additions & 0 deletions src/cli/commands/deploy/actions.ts
Original file line numberDiff line numberDiff line change
Expand Up@@ -13,6 +13,7 @@ import {
parseOnlineEvalOutputs,
parsePolicyEngineOutputs,
parsePolicyOutputs,
parseRuntimeEndpointOutputs,
} from '../../cloudformation';
import { getErrorMessage } from '../../errors';
import { ExecLogger } from '../../logging';
Expand DownExpand Up@@ -403,6 +404,17 @@ export async function handleDeploy(options: ValidatedDeployOptions): Promise<Dep
);
const policies = parsePolicyOutputs(outputs, policySpecs);

// Parse runtime endpoint outputs
const endpointSpecs: { agentName: string; endpointName: string }[] = [];
for (const runtime of context.projectSpec.runtimes) {
if (runtime.endpoints) {
for (const endpointName of Object.keys(runtime.endpoints)) {
endpointSpecs.push({ agentName: runtime.name, endpointName });
}
}
}
const runtimeEndpoints = parseRuntimeEndpointOutputs(outputs, endpointSpecs);

// Parse gateway outputs
const gatewaySpecs =
mcpSpec?.agentCoreGateways?.reduce(
Expand All@@ -428,6 +440,7 @@ export async function handleDeploy(options: ValidatedDeployOptions): Promise<Dep
onlineEvalConfigs,
policyEngines,
policies,
runtimeEndpoints,
});
await configIO.writeDeployedState(deployedState);

Expand Down
1 change: 1 addition & 0 deletions src/cli/commands/remove/types.ts
Original file line numberDiff line numberDiff line change
Expand Up@@ -2,6 +2,7 @@ export type ResourceType =
| 'agent'
| 'gateway'
| 'gateway-target'
| 'runtime-endpoint'
| 'memory'
| 'credential'
| 'evaluator'
Expand Down
36 changes: 35 additions & 1 deletion src/cli/commands/status/action.ts
Original file line numberDiff line numberDiff line change
Expand Up@@ -18,11 +18,13 @@ export interface ResourceStatusEntry {
| 'evaluator'
| 'online-eval'
| 'policy-engine'
| 'policy';
| 'policy'
| 'runtime-endpoint';
name: string;
deploymentState: ResourceDeploymentState;
identifier?: string;
detail?: string;
parentName?: string;
error?: string;
invocationUrl?: string;
}
Expand DownExpand Up@@ -79,13 +81,15 @@ function diffResourceSet<TLocal extends { name: string }, TDeployed>({
getIdentifier,
getLocalDetail,
getDeployedKey,
getParentName,
}: {
resourceType: ResourceStatusEntry['resourceType'];
localItems: TLocal[];
deployedRecord: Record<string, TDeployed>;
getIdentifier: (deployed: TDeployed) => string | undefined;
getLocalDetail?: (item: TLocal) => string | undefined;
getDeployedKey?: (item: TLocal) => string;
getParentName?: (item: TLocal) => string | undefined;
}): ResourceStatusEntry[] {
const entries: ResourceStatusEntry[] = [];
const localKeys = new Set(localItems.map(item => (getDeployedKey ? getDeployedKey(item) : item.name)));
Expand All@@ -99,16 +103,20 @@ function diffResourceSet<TLocal extends { name: string }, TDeployed>({
deploymentState: deployed ? 'deployed' : 'local-only',
identifier: deployed ? getIdentifier(deployed) : undefined,
detail: getLocalDetail?.(item),
parentName: getParentName?.(item),
});
}

for (const [name, deployed] of Object.entries(deployedRecord)) {
if (!localKeys.has(name)) {
// For pending-removal entries, try to extract parentName from composite key
const slashIdx = name.indexOf('/');
entries.push({
resourceType,
name,
deploymentState: 'pending-removal',
identifier: getIdentifier(deployed),
parentName: getParentName && slashIdx > 0 ? name.substring(0, slashIdx) : undefined,
});
}
}
Expand DownExpand Up@@ -202,8 +210,34 @@ export function computeResourceStatuses(
getDeployedKey: item => `${item.engineName}/${item.name}`,
});

// Flatten runtime endpoints for diffing against deployed state
const localEndpoints: { name: string; agentName: string; version: number; description?: string }[] = [];
for (const runtime of project.runtimes) {
if (runtime.endpoints) {
for (const [epName, ep] of Object.entries(runtime.endpoints)) {
localEndpoints.push({
name: epName,
agentName: runtime.name,
version: ep.version,
description: ep.description,
});
}
}
}

const runtimeEndpoints = diffResourceSet({
resourceType: 'runtime-endpoint',
localItems: localEndpoints,
deployedRecord: resources?.runtimeEndpoints ?? {},
getIdentifier: deployed => deployed.endpointArn,
getLocalDetail: item => `v${item.version}${item.description ? ` — ${item.description}` : ''}`,
getDeployedKey: item => `${item.agentName}/${item.name}`,
getParentName: item => item.agentName,
});

return [
...agents,
...runtimeEndpoints,
...credentials,
...memories,
...gateways,
Expand Down
48 changes: 38 additions & 10 deletions src/cli/commands/status/command.tsx
Original file line numberDiff line numberDiff line change
Expand Up@@ -9,6 +9,7 @@

const VALID_RESOURCE_TYPES = [
'agent',
'runtime-endpoint',
'memory',
'credential',
'gateway',
Expand DownExpand Up@@ -58,7 +59,7 @@
.option('--target <name>', 'Select deployment target')
.option(
'--type <type>',
'Filter by resource type (agent, memory, credential, gateway, evaluator, online-eval, policy-engine, policy)'
'Filter by resource type (agent, runtime-endpoint, memory, credential, gateway, evaluator, online-eval, policy-engine, policy)'
)
.option('--state <state>', 'Filter by deployment state (deployed, local-only, pending-removal)')
.option('--runtime <name>', 'Filter to a specific runtime')
Expand DownExpand Up@@ -135,6 +136,7 @@

const filtered = filterResources(result.resources, cliOptions);
const agents = filtered.filter(r => r.resourceType === 'agent');
const runtimeEndpoints = filtered.filter(r => r.resourceType === 'runtime-endpoint');
const credentials = filtered.filter(r => r.resourceType === 'credential');
const memories = filtered.filter(r => r.resourceType === 'memory');
const gateways = filtered.filter(r => r.resourceType === 'gateway');
Expand All@@ -153,15 +155,41 @@
{agents.length > 0 && (
<Box flexDirection="column" marginTop={1}>
<Text bold>Agents</Text>
{agents.map(entry => (
<Box key={`${entry.resourceType}-${entry.name}`} flexDirection="column">
<ResourceEntry entry={entry} showRuntime />
{entry.invocationUrl && (
<Text dimColor>
{' '}URL: {entry.invocationUrl}
</Text>
)}
</Box>
{agents.map(entry => {
// Find endpoints belonging to this agent
const agentEndpoints = runtimeEndpoints.filter(ep => ep.parentName === entry.name);
return (
<Box key={`${entry.resourceType}-${entry.name}`} flexDirection="column">
<ResourceEntry entry={entry} showRuntime />
{entry.invocationUrl && (
<Text dimColor>
{' '}URL: {entry.invocationUrl}
</Text>
)}
{agentEndpoints.map(ep => (
Comment thread
notgitika marked this conversation as resolved.
Comment thread
notgitika marked this conversation as resolved.
<Text key={`${ep.parentName}/${ep.name}`}>
{' '}◉ {ep.name} <Text dimColor>{ep.detail}</Text>{' '}
<Text color={DEPLOYMENT_STATE_COLORS[ep.deploymentState] ?? 'gray'}>
[{DEPLOYMENT_STATE_LABELS[ep.deploymentState] ?? ep.deploymentState}]
</Text>
</Text>
))}
</Box>
);
})}
</Box>
)}

{agents.length === 0 && runtimeEndpoints.length > 0 && (
<Box flexDirection="column" marginTop={1}>
<Text bold>Runtime Endpoints</Text>
{runtimeEndpoints.map(ep => (
<Text key={`${ep.parentName}/${ep.name}`}>
{' '}◉ {ep.parentName}/{ep.name} <Text dimColor>{ep.detail}</Text>{' '}
<Text color={DEPLOYMENT_STATE_COLORS[ep.deploymentState] ?? 'gray'}>
Comment thread
notgitika marked this conversation as resolved.
Comment thread
notgitika marked this conversation as resolved.
[{DEPLOYMENT_STATE_LABELS[ep.deploymentState] ?? ep.deploymentState}]
</Text>
</Text>
))}
</Box>
)}
Expand DownExpand Up@@ -239,7 +267,7 @@
});
};

function ResourceEntry({ entry, showRuntime }: { entry: ResourceStatusEntry; showRuntime?: boolean }) {

Check warning on line 270 in src/cli/commands/status/command.tsx

View workflow job for this annotation

GitHub Actions/ lint

Fast refresh only works when a file only exports components. Move your component(s) to a separate file. If all exports are HOCs, add them to the `extraHOCs` option
return (
<Text>
{' '}
Expand Down
1 change: 1 addition & 0 deletions src/cli/logging/remove-logger.ts
Original file line numberDiff line numberDiff line change
Expand Up@@ -13,6 +13,7 @@ export interface RemoveLoggerOptions {
| 'credential'
| 'gateway'
| 'gateway-target'
| 'runtime-endpoint'
| 'evaluator'
| 'online-eval'
| 'policy-engine'
Expand Down
Loading
Loading
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Auto-enable theater mode on YouTube\n(function() {\n function tryTheater() {\n var btn = document.querySelector('button[aria-label=\"Theater mode\"], ytd-player #player button[title=\"Theater mode\"]');\n if (btn && !btn.classList.contains('activated')) {\n btn.click();\n }\n }\n \n // Try immediately\n tryTheater();\n \n // Try after navigation (SPA)\n var lastUrl = location.href;\n setInterval(function() {\n if (location.href !== lastUrl) {\n lastUrl = location.href;\n setTimeout(tryTheater, 500);\n }\n }, 1000);\n \n // Also try on player load\n var observer = new MutationObserver(tryTheater);\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "YouTube Theater Mode Default"); } } catch(__e) { console.warn('[Userscript:YouTube Theater Mode Default]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
19 commits
Select commit Hold shift + click to select a range
cb839bf
feat: add runtime endpoint support to AgentCore CLI
jariy17 Apr 25, 2026
3b29773
fix: correct output key prefix for runtime endpoint parsing
jariy17 Apr 25, 2026
33b81da
fix: remove .omc state files and unused useCallback import
jariy17 Apr 27, 2026
6b4f8f3
fix: shorten runtime endpoint description to prevent TUI overflow
jariy17 Apr 27, 2026
c1ad7ea
fix: validate runtime endpoint version is a positive integer
jariy17 Apr 27, 2026
f3face8
fix: use agent/endpoint composite key to prevent React key collision
jariy17 Apr 27, 2026
21147b1
fix: render runtime endpoints in status --type runtime-endpoint
jariy17 Apr 27, 2026
132f969
fix: add runtime-endpoint to status --help --type documentation
jariy17 Apr 27, 2026
3aba3e9
fix: return richer JSON response from add runtime-endpoint
jariy17 Apr 27, 2026
2bb5a86
fix: validate endpoint version against deployed runtime version
jariy17 Apr 27, 2026
844689a
chore: remove planning and bug bash docs from PR
jariy17 Apr 27, 2026
11a7a86
fix: use composite key and parentName for endpoint identification
jariy17 Apr 27, 2026
d6389c8
test: add comprehensive unit tests for RuntimeEndpointPrimitive
jariy17 Apr 27, 2026
b9f158f
fix: remove dead findGatewayTargetReferences stub
jariy17 Apr 27, 2026
0ff1db8
fix: use BasePrimitive configIO instead of ad-hoc ConfigIO in add()
jariy17 Apr 27, 2026
be47f35
fix: use Number() instead of parseInt in TUI version validation
jariy17 Apr 27, 2026
fd8e15c
Merge branch 'main' into feat/endpoint_based_abs
jariy17 Apr 27, 2026
ddbeff2
chore: fix prettier formatting
jariy17 Apr 27, 2026
117a07e
fix: use T[] instead of Array<T> to satisfy eslint array-type rule
jariy17 Apr 27, 2026
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
1 change: 1 addition & 0 deletions .gitignore
Original file line numberDiff line numberDiff line change
Expand Up@@ -67,6 +67,7 @@ ProtocolTesting/

# Auto-cloned CDK constructs (from scripts/bundle.mjs)
.cdk-constructs-clone/
.omc/

# Browser tests
browser-tests/.browser-test-env
Expand Down
42 changes: 42 additions & 0 deletions src/cli/cloudformation/outputs.ts
Original file line numberDiff line numberDiff line change
Expand Up@@ -6,6 +6,7 @@ import type {
OnlineEvalDeployedState,
PolicyDeployedState,
PolicyEngineDeployedState,
RuntimeEndpointDeployedState,
TargetDeployedState,
} from '../../schema';
import { getCredentialProvider } from '../aws';
Expand DownExpand Up@@ -338,6 +339,40 @@ export function parsePolicyOutputs(
return policies;
}

/**
* Parse stack outputs into deployed state for runtime endpoints.
*
* Output key pattern: ApplicationAgent{AgentPascal}Endpoint{AgentPascal}{EndpointPascal}(Id|Arn)Output{Hash}
* The Agent{PascalName} prefix comes from the AgentEnvironment construct in the CDK tree.
*/
export function parseRuntimeEndpointOutputs(
outputs: StackOutputs,
endpointSpecs: { agentName: string; endpointName: string }[]
): Record<string, RuntimeEndpointDeployedState> {
const endpoints: Record<string, RuntimeEndpointDeployedState> = {};
const outputKeys = Object.keys(outputs);

for (const { agentName, endpointName } of endpointSpecs) {
const agentPascal = toPascalId(agentName);
const endpointPascal = toPascalId('Endpoint', agentName, endpointName);
const idPrefix = `ApplicationAgent${agentPascal}${endpointPascal}IdOutput`;
const arnPrefix = `ApplicationAgent${agentPascal}${endpointPascal}ArnOutput`;

const idKey = outputKeys.find(k => k.startsWith(idPrefix));
const arnKey = outputKeys.find(k => k.startsWith(arnPrefix));

if (idKey && arnKey) {
const key = `${agentName}/${endpointName}`;
endpoints[key] = {
endpointId: outputs[idKey]!,
endpointArn: outputs[arnKey]!,
};
}
}

return endpoints;
}

export interface BuildDeployedStateOptions {
targetName: string;
stackName: string;
Expand All@@ -351,6 +386,7 @@ export interface BuildDeployedStateOptions {
onlineEvalConfigs?: Record<string, OnlineEvalDeployedState>;
policyEngines?: Record<string, PolicyEngineDeployedState>;
policies?: Record<string, PolicyDeployedState>;
runtimeEndpoints?: Record<string, RuntimeEndpointDeployedState>;
}

/**
Expand All@@ -370,6 +406,7 @@ export function buildDeployedState(opts: BuildDeployedStateOptions): DeployedSta
onlineEvalConfigs,
policyEngines,
policies,
runtimeEndpoints,
} = opts;
const targetState: TargetDeployedState = {
resources: {
Expand DownExpand Up@@ -404,6 +441,11 @@ export function buildDeployedState(opts: BuildDeployedStateOptions): DeployedSta
targetState.resources!.onlineEvalConfigs = onlineEvalConfigs;
}

// Add runtime endpoint state if endpoints exist
if (runtimeEndpoints && Object.keys(runtimeEndpoints).length > 0) {
targetState.resources!.runtimeEndpoints = runtimeEndpoints;
}

return {
targets: {
...existingState?.targets,
Expand Down
13 changes: 13 additions & 0 deletions src/cli/commands/deploy/actions.ts
Original file line numberDiff line numberDiff line change
Expand Up@@ -13,6 +13,7 @@ import {
parseOnlineEvalOutputs,
parsePolicyEngineOutputs,
parsePolicyOutputs,
parseRuntimeEndpointOutputs,
} from '../../cloudformation';
import { getErrorMessage } from '../../errors';
import { ExecLogger } from '../../logging';
Expand DownExpand Up@@ -403,6 +404,17 @@ export async function handleDeploy(options: ValidatedDeployOptions): Promise<Dep
);
const policies = parsePolicyOutputs(outputs, policySpecs);

// Parse runtime endpoint outputs
const endpointSpecs: { agentName: string; endpointName: string }[] = [];
for (const runtime of context.projectSpec.runtimes) {
if (runtime.endpoints) {
for (const endpointName of Object.keys(runtime.endpoints)) {
endpointSpecs.push({ agentName: runtime.name, endpointName });
}
}
}
const runtimeEndpoints = parseRuntimeEndpointOutputs(outputs, endpointSpecs);

// Parse gateway outputs
const gatewaySpecs =
mcpSpec?.agentCoreGateways?.reduce(
Expand All@@ -428,6 +440,7 @@ export async function handleDeploy(options: ValidatedDeployOptions): Promise<Dep
onlineEvalConfigs,
policyEngines,
policies,
runtimeEndpoints,
});
await configIO.writeDeployedState(deployedState);

Expand Down
1 change: 1 addition & 0 deletions src/cli/commands/remove/types.ts
Original file line numberDiff line numberDiff line change
Expand Up@@ -2,6 +2,7 @@ export type ResourceType =
| 'agent'
| 'gateway'
| 'gateway-target'
| 'runtime-endpoint'
| 'memory'
| 'credential'
| 'evaluator'
Expand Down
36 changes: 35 additions & 1 deletion src/cli/commands/status/action.ts
Original file line numberDiff line numberDiff line change
Expand Up@@ -18,11 +18,13 @@ export interface ResourceStatusEntry {
| 'evaluator'
| 'online-eval'
| 'policy-engine'
| 'policy';
| 'policy'
| 'runtime-endpoint';
name: string;
deploymentState: ResourceDeploymentState;
identifier?: string;
detail?: string;
parentName?: string;
error?: string;
invocationUrl?: string;
}
Expand DownExpand Up@@ -79,13 +81,15 @@ function diffResourceSet<TLocal extends { name: string }, TDeployed>({
getIdentifier,
getLocalDetail,
getDeployedKey,
getParentName,
}: {
resourceType: ResourceStatusEntry['resourceType'];
localItems: TLocal[];
deployedRecord: Record<string, TDeployed>;
getIdentifier: (deployed: TDeployed) => string | undefined;
getLocalDetail?: (item: TLocal) => string | undefined;
getDeployedKey?: (item: TLocal) => string;
getParentName?: (item: TLocal) => string | undefined;
}): ResourceStatusEntry[] {
const entries: ResourceStatusEntry[] = [];
const localKeys = new Set(localItems.map(item => (getDeployedKey ? getDeployedKey(item) : item.name)));
Expand All@@ -99,16 +103,20 @@ function diffResourceSet<TLocal extends { name: string }, TDeployed>({
deploymentState: deployed ? 'deployed' : 'local-only',
identifier: deployed ? getIdentifier(deployed) : undefined,
detail: getLocalDetail?.(item),
parentName: getParentName?.(item),
});
}

for (const [name, deployed] of Object.entries(deployedRecord)) {
if (!localKeys.has(name)) {
// For pending-removal entries, try to extract parentName from composite key
const slashIdx = name.indexOf('/');
entries.push({
resourceType,
name,
deploymentState: 'pending-removal',
identifier: getIdentifier(deployed),
parentName: getParentName && slashIdx > 0 ? name.substring(0, slashIdx) : undefined,
});
}
}
Expand DownExpand Up@@ -202,8 +210,34 @@ export function computeResourceStatuses(
getDeployedKey: item => `${item.engineName}/${item.name}`,
});

// Flatten runtime endpoints for diffing against deployed state
const localEndpoints: { name: string; agentName: string; version: number; description?: string }[] = [];
for (const runtime of project.runtimes) {
if (runtime.endpoints) {
for (const [epName, ep] of Object.entries(runtime.endpoints)) {
localEndpoints.push({
name: epName,
agentName: runtime.name,
version: ep.version,
description: ep.description,
});
}
}
}

const runtimeEndpoints = diffResourceSet({
resourceType: 'runtime-endpoint',
localItems: localEndpoints,
deployedRecord: resources?.runtimeEndpoints ?? {},
getIdentifier: deployed => deployed.endpointArn,
getLocalDetail: item => `v${item.version}${item.description ? ` — ${item.description}` : ''}`,
getDeployedKey: item => `${item.agentName}/${item.name}`,
getParentName: item => item.agentName,
});

return [
...agents,
...runtimeEndpoints,
...credentials,
...memories,
...gateways,
Expand Down
48 changes: 38 additions & 10 deletions src/cli/commands/status/command.tsx
Original file line numberDiff line numberDiff line change
Expand Up@@ -9,6 +9,7 @@

const VALID_RESOURCE_TYPES = [
'agent',
'runtime-endpoint',
'memory',
'credential',
'gateway',
Expand DownExpand Up@@ -58,7 +59,7 @@
.option('--target <name>', 'Select deployment target')
.option(
'--type <type>',
'Filter by resource type (agent, memory, credential, gateway, evaluator, online-eval, policy-engine, policy)'
'Filter by resource type (agent, runtime-endpoint, memory, credential, gateway, evaluator, online-eval, policy-engine, policy)'
)
.option('--state <state>', 'Filter by deployment state (deployed, local-only, pending-removal)')
.option('--runtime <name>', 'Filter to a specific runtime')
Expand DownExpand Up@@ -135,6 +136,7 @@

const filtered = filterResources(result.resources, cliOptions);
const agents = filtered.filter(r => r.resourceType === 'agent');
const runtimeEndpoints = filtered.filter(r => r.resourceType === 'runtime-endpoint');
const credentials = filtered.filter(r => r.resourceType === 'credential');
const memories = filtered.filter(r => r.resourceType === 'memory');
const gateways = filtered.filter(r => r.resourceType === 'gateway');
Expand All@@ -153,15 +155,41 @@
{agents.length > 0 && (
<Box flexDirection="column" marginTop={1}>
<Text bold>Agents</Text>
{agents.map(entry => (
<Box key={`${entry.resourceType}-${entry.name}`} flexDirection="column">
<ResourceEntry entry={entry} showRuntime />
{entry.invocationUrl && (
<Text dimColor>
{' '}URL: {entry.invocationUrl}
</Text>
)}
</Box>
{agents.map(entry => {
// Find endpoints belonging to this agent
const agentEndpoints = runtimeEndpoints.filter(ep => ep.parentName === entry.name);
return (
<Box key={`${entry.resourceType}-${entry.name}`} flexDirection="column">
<ResourceEntry entry={entry} showRuntime />
{entry.invocationUrl && (
<Text dimColor>
{' '}URL: {entry.invocationUrl}
</Text>
)}
{agentEndpoints.map(ep => (
Comment thread
notgitika marked this conversation as resolved.
Comment thread
notgitika marked this conversation as resolved.
<Text key={`${ep.parentName}/${ep.name}`}>
{' '}◉ {ep.name} <Text dimColor>{ep.detail}</Text>{' '}
<Text color={DEPLOYMENT_STATE_COLORS[ep.deploymentState] ?? 'gray'}>
[{DEPLOYMENT_STATE_LABELS[ep.deploymentState] ?? ep.deploymentState}]
</Text>
</Text>
))}
</Box>
);
})}
</Box>
)}

{agents.length === 0 && runtimeEndpoints.length > 0 && (
<Box flexDirection="column" marginTop={1}>
<Text bold>Runtime Endpoints</Text>
{runtimeEndpoints.map(ep => (
<Text key={`${ep.parentName}/${ep.name}`}>
{' '}◉ {ep.parentName}/{ep.name} <Text dimColor>{ep.detail}</Text>{' '}
<Text color={DEPLOYMENT_STATE_COLORS[ep.deploymentState] ?? 'gray'}>
Comment thread
notgitika marked this conversation as resolved.
Comment thread
notgitika marked this conversation as resolved.
[{DEPLOYMENT_STATE_LABELS[ep.deploymentState] ?? ep.deploymentState}]
</Text>
</Text>
))}
</Box>
)}
Expand DownExpand Up@@ -239,7 +267,7 @@
});
};

function ResourceEntry({ entry, showRuntime }: { entry: ResourceStatusEntry; showRuntime?: boolean }) {

Check warning on line 270 in src/cli/commands/status/command.tsx

View workflow job for this annotation

GitHub Actions/ lint

Fast refresh only works when a file only exports components. Move your component(s) to a separate file. If all exports are HOCs, add them to the `extraHOCs` option
return (
<Text>
{' '}
Expand Down
1 change: 1 addition & 0 deletions src/cli/logging/remove-logger.ts
Original file line numberDiff line numberDiff line change
Expand Up@@ -13,6 +13,7 @@ export interface RemoveLoggerOptions {
| 'credential'
| 'gateway'
| 'gateway-target'
| 'runtime-endpoint'
| 'evaluator'
| 'online-eval'
| 'policy-engine'
Expand Down
Loading
Loading
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Remove or un-stick sticky/fixed headers that block content\n(function() {\n function unstick() {\n document.querySelectorAll('header, nav, [role=\"banner\"], .header, .navbar, .sticky, .fixed-top, [style*=\"position: fixed\"], [style*=\"position:sticky\"]').forEach(function(el) {\n if (el.style.position === 'fixed' || el.style.position === 'sticky' || \n getComputedStyle(el).position === 'fixed' || getComputedStyle(el).position === 'sticky') {\n el.style.position = 'static';\n el.style.top = 'auto';\n el.style.zIndex = 'auto';\n }\n });\n }\n \n unstick();\n \n var observer = new MutationObserver(unstick);\n observer.observe(document.body, { childList: true, subtree: true, attributes: true, attributeFilter: ['style', 'class'] });\n})();", "Kill Sticky Headers"); } } catch(__e) { console.warn('[Userscript:Kill Sticky Headers]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
19 commits
Select commit Hold shift + click to select a range
cb839bf
feat: add runtime endpoint support to AgentCore CLI
jariy17 Apr 25, 2026
3b29773
fix: correct output key prefix for runtime endpoint parsing
jariy17 Apr 25, 2026
33b81da
fix: remove .omc state files and unused useCallback import
jariy17 Apr 27, 2026
6b4f8f3
fix: shorten runtime endpoint description to prevent TUI overflow
jariy17 Apr 27, 2026
c1ad7ea
fix: validate runtime endpoint version is a positive integer
jariy17 Apr 27, 2026
f3face8
fix: use agent/endpoint composite key to prevent React key collision
jariy17 Apr 27, 2026
21147b1
fix: render runtime endpoints in status --type runtime-endpoint
jariy17 Apr 27, 2026
132f969
fix: add runtime-endpoint to status --help --type documentation
jariy17 Apr 27, 2026
3aba3e9
fix: return richer JSON response from add runtime-endpoint
jariy17 Apr 27, 2026
2bb5a86
fix: validate endpoint version against deployed runtime version
jariy17 Apr 27, 2026
844689a
chore: remove planning and bug bash docs from PR
jariy17 Apr 27, 2026
11a7a86
fix: use composite key and parentName for endpoint identification
jariy17 Apr 27, 2026
d6389c8
test: add comprehensive unit tests for RuntimeEndpointPrimitive
jariy17 Apr 27, 2026
b9f158f
fix: remove dead findGatewayTargetReferences stub
jariy17 Apr 27, 2026
0ff1db8
fix: use BasePrimitive configIO instead of ad-hoc ConfigIO in add()
jariy17 Apr 27, 2026
be47f35
fix: use Number() instead of parseInt in TUI version validation
jariy17 Apr 27, 2026
fd8e15c
Merge branch 'main' into feat/endpoint_based_abs
jariy17 Apr 27, 2026
ddbeff2
chore: fix prettier formatting
jariy17 Apr 27, 2026
117a07e
fix: use T[] instead of Array<T> to satisfy eslint array-type rule
jariy17 Apr 27, 2026
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
1 change: 1 addition & 0 deletions .gitignore
Original file line numberDiff line numberDiff line change
Expand Up@@ -67,6 +67,7 @@ ProtocolTesting/

# Auto-cloned CDK constructs (from scripts/bundle.mjs)
.cdk-constructs-clone/
.omc/

# Browser tests
browser-tests/.browser-test-env
Expand Down
42 changes: 42 additions & 0 deletions src/cli/cloudformation/outputs.ts
Original file line numberDiff line numberDiff line change
Expand Up@@ -6,6 +6,7 @@ import type {
OnlineEvalDeployedState,
PolicyDeployedState,
PolicyEngineDeployedState,
RuntimeEndpointDeployedState,
TargetDeployedState,
} from '../../schema';
import { getCredentialProvider } from '../aws';
Expand DownExpand Up@@ -338,6 +339,40 @@ export function parsePolicyOutputs(
return policies;
}

/**
* Parse stack outputs into deployed state for runtime endpoints.
*
* Output key pattern: ApplicationAgent{AgentPascal}Endpoint{AgentPascal}{EndpointPascal}(Id|Arn)Output{Hash}
* The Agent{PascalName} prefix comes from the AgentEnvironment construct in the CDK tree.
*/
export function parseRuntimeEndpointOutputs(
outputs: StackOutputs,
endpointSpecs: { agentName: string; endpointName: string }[]
): Record<string, RuntimeEndpointDeployedState> {
const endpoints: Record<string, RuntimeEndpointDeployedState> = {};
const outputKeys = Object.keys(outputs);

for (const { agentName, endpointName } of endpointSpecs) {
const agentPascal = toPascalId(agentName);
const endpointPascal = toPascalId('Endpoint', agentName, endpointName);
const idPrefix = `ApplicationAgent${agentPascal}${endpointPascal}IdOutput`;
const arnPrefix = `ApplicationAgent${agentPascal}${endpointPascal}ArnOutput`;

const idKey = outputKeys.find(k => k.startsWith(idPrefix));
const arnKey = outputKeys.find(k => k.startsWith(arnPrefix));

if (idKey && arnKey) {
const key = `${agentName}/${endpointName}`;
endpoints[key] = {
endpointId: outputs[idKey]!,
endpointArn: outputs[arnKey]!,
};
}
}

return endpoints;
}

export interface BuildDeployedStateOptions {
targetName: string;
stackName: string;
Expand All@@ -351,6 +386,7 @@ export interface BuildDeployedStateOptions {
onlineEvalConfigs?: Record<string, OnlineEvalDeployedState>;
policyEngines?: Record<string, PolicyEngineDeployedState>;
policies?: Record<string, PolicyDeployedState>;
runtimeEndpoints?: Record<string, RuntimeEndpointDeployedState>;
}

/**
Expand All@@ -370,6 +406,7 @@ export function buildDeployedState(opts: BuildDeployedStateOptions): DeployedSta
onlineEvalConfigs,
policyEngines,
policies,
runtimeEndpoints,
} = opts;
const targetState: TargetDeployedState = {
resources: {
Expand DownExpand Up@@ -404,6 +441,11 @@ export function buildDeployedState(opts: BuildDeployedStateOptions): DeployedSta
targetState.resources!.onlineEvalConfigs = onlineEvalConfigs;
}

// Add runtime endpoint state if endpoints exist
if (runtimeEndpoints && Object.keys(runtimeEndpoints).length > 0) {
targetState.resources!.runtimeEndpoints = runtimeEndpoints;
}

return {
targets: {
...existingState?.targets,
Expand Down
13 changes: 13 additions & 0 deletions src/cli/commands/deploy/actions.ts
Original file line numberDiff line numberDiff line change
Expand Up@@ -13,6 +13,7 @@ import {
parseOnlineEvalOutputs,
parsePolicyEngineOutputs,
parsePolicyOutputs,
parseRuntimeEndpointOutputs,
} from '../../cloudformation';
import { getErrorMessage } from '../../errors';
import { ExecLogger } from '../../logging';
Expand DownExpand Up@@ -403,6 +404,17 @@ export async function handleDeploy(options: ValidatedDeployOptions): Promise<Dep
);
const policies = parsePolicyOutputs(outputs, policySpecs);

// Parse runtime endpoint outputs
const endpointSpecs: { agentName: string; endpointName: string }[] = [];
for (const runtime of context.projectSpec.runtimes) {
if (runtime.endpoints) {
for (const endpointName of Object.keys(runtime.endpoints)) {
endpointSpecs.push({ agentName: runtime.name, endpointName });
}
}
}
const runtimeEndpoints = parseRuntimeEndpointOutputs(outputs, endpointSpecs);

// Parse gateway outputs
const gatewaySpecs =
mcpSpec?.agentCoreGateways?.reduce(
Expand All@@ -428,6 +440,7 @@ export async function handleDeploy(options: ValidatedDeployOptions): Promise<Dep
onlineEvalConfigs,
policyEngines,
policies,
runtimeEndpoints,
});
await configIO.writeDeployedState(deployedState);

Expand Down
1 change: 1 addition & 0 deletions src/cli/commands/remove/types.ts
Original file line numberDiff line numberDiff line change
Expand Up@@ -2,6 +2,7 @@ export type ResourceType =
| 'agent'
| 'gateway'
| 'gateway-target'
| 'runtime-endpoint'
| 'memory'
| 'credential'
| 'evaluator'
Expand Down
36 changes: 35 additions & 1 deletion src/cli/commands/status/action.ts
Original file line numberDiff line numberDiff line change
Expand Up@@ -18,11 +18,13 @@ export interface ResourceStatusEntry {
| 'evaluator'
| 'online-eval'
| 'policy-engine'
| 'policy';
| 'policy'
| 'runtime-endpoint';
name: string;
deploymentState: ResourceDeploymentState;
identifier?: string;
detail?: string;
parentName?: string;
error?: string;
invocationUrl?: string;
}
Expand DownExpand Up@@ -79,13 +81,15 @@ function diffResourceSet<TLocal extends { name: string }, TDeployed>({
getIdentifier,
getLocalDetail,
getDeployedKey,
getParentName,
}: {
resourceType: ResourceStatusEntry['resourceType'];
localItems: TLocal[];
deployedRecord: Record<string, TDeployed>;
getIdentifier: (deployed: TDeployed) => string | undefined;
getLocalDetail?: (item: TLocal) => string | undefined;
getDeployedKey?: (item: TLocal) => string;
getParentName?: (item: TLocal) => string | undefined;
}): ResourceStatusEntry[] {
const entries: ResourceStatusEntry[] = [];
const localKeys = new Set(localItems.map(item => (getDeployedKey ? getDeployedKey(item) : item.name)));
Expand All@@ -99,16 +103,20 @@ function diffResourceSet<TLocal extends { name: string }, TDeployed>({
deploymentState: deployed ? 'deployed' : 'local-only',
identifier: deployed ? getIdentifier(deployed) : undefined,
detail: getLocalDetail?.(item),
parentName: getParentName?.(item),
});
}

for (const [name, deployed] of Object.entries(deployedRecord)) {
if (!localKeys.has(name)) {
// For pending-removal entries, try to extract parentName from composite key
const slashIdx = name.indexOf('/');
entries.push({
resourceType,
name,
deploymentState: 'pending-removal',
identifier: getIdentifier(deployed),
parentName: getParentName && slashIdx > 0 ? name.substring(0, slashIdx) : undefined,
});
}
}
Expand DownExpand Up@@ -202,8 +210,34 @@ export function computeResourceStatuses(
getDeployedKey: item => `${item.engineName}/${item.name}`,
});

// Flatten runtime endpoints for diffing against deployed state
const localEndpoints: { name: string; agentName: string; version: number; description?: string }[] = [];
for (const runtime of project.runtimes) {
if (runtime.endpoints) {
for (const [epName, ep] of Object.entries(runtime.endpoints)) {
localEndpoints.push({
name: epName,
agentName: runtime.name,
version: ep.version,
description: ep.description,
});
}
}
}

const runtimeEndpoints = diffResourceSet({
resourceType: 'runtime-endpoint',
localItems: localEndpoints,
deployedRecord: resources?.runtimeEndpoints ?? {},
getIdentifier: deployed => deployed.endpointArn,
getLocalDetail: item => `v${item.version}${item.description ? ` — ${item.description}` : ''}`,
getDeployedKey: item => `${item.agentName}/${item.name}`,
getParentName: item => item.agentName,
});

return [
...agents,
...runtimeEndpoints,
...credentials,
...memories,
...gateways,
Expand Down
48 changes: 38 additions & 10 deletions src/cli/commands/status/command.tsx
Original file line numberDiff line numberDiff line change
Expand Up@@ -9,6 +9,7 @@

const VALID_RESOURCE_TYPES = [
'agent',
'runtime-endpoint',
'memory',
'credential',
'gateway',
Expand DownExpand Up@@ -58,7 +59,7 @@
.option('--target <name>', 'Select deployment target')
.option(
'--type <type>',
'Filter by resource type (agent, memory, credential, gateway, evaluator, online-eval, policy-engine, policy)'
'Filter by resource type (agent, runtime-endpoint, memory, credential, gateway, evaluator, online-eval, policy-engine, policy)'
)
.option('--state <state>', 'Filter by deployment state (deployed, local-only, pending-removal)')
.option('--runtime <name>', 'Filter to a specific runtime')
Expand DownExpand Up@@ -135,6 +136,7 @@

const filtered = filterResources(result.resources, cliOptions);
const agents = filtered.filter(r => r.resourceType === 'agent');
const runtimeEndpoints = filtered.filter(r => r.resourceType === 'runtime-endpoint');
const credentials = filtered.filter(r => r.resourceType === 'credential');
const memories = filtered.filter(r => r.resourceType === 'memory');
const gateways = filtered.filter(r => r.resourceType === 'gateway');
Expand All@@ -153,15 +155,41 @@
{agents.length > 0 && (
<Box flexDirection="column" marginTop={1}>
<Text bold>Agents</Text>
{agents.map(entry => (
<Box key={`${entry.resourceType}-${entry.name}`} flexDirection="column">
<ResourceEntry entry={entry} showRuntime />
{entry.invocationUrl && (
<Text dimColor>
{' '}URL: {entry.invocationUrl}
</Text>
)}
</Box>
{agents.map(entry => {
// Find endpoints belonging to this agent
const agentEndpoints = runtimeEndpoints.filter(ep => ep.parentName === entry.name);
return (
<Box key={`${entry.resourceType}-${entry.name}`} flexDirection="column">
<ResourceEntry entry={entry} showRuntime />
{entry.invocationUrl && (
<Text dimColor>
{' '}URL: {entry.invocationUrl}
</Text>
)}
{agentEndpoints.map(ep => (
Comment thread
notgitika marked this conversation as resolved.
Comment thread
notgitika marked this conversation as resolved.
<Text key={`${ep.parentName}/${ep.name}`}>
{' '}◉ {ep.name} <Text dimColor>{ep.detail}</Text>{' '}
<Text color={DEPLOYMENT_STATE_COLORS[ep.deploymentState] ?? 'gray'}>
[{DEPLOYMENT_STATE_LABELS[ep.deploymentState] ?? ep.deploymentState}]
</Text>
</Text>
))}
</Box>
);
})}
</Box>
)}

{agents.length === 0 && runtimeEndpoints.length > 0 && (
<Box flexDirection="column" marginTop={1}>
<Text bold>Runtime Endpoints</Text>
{runtimeEndpoints.map(ep => (
<Text key={`${ep.parentName}/${ep.name}`}>
{' '}◉ {ep.parentName}/{ep.name} <Text dimColor>{ep.detail}</Text>{' '}
<Text color={DEPLOYMENT_STATE_COLORS[ep.deploymentState] ?? 'gray'}>
Comment thread
notgitika marked this conversation as resolved.
Comment thread
notgitika marked this conversation as resolved.
[{DEPLOYMENT_STATE_LABELS[ep.deploymentState] ?? ep.deploymentState}]
</Text>
</Text>
))}
</Box>
)}
Expand DownExpand Up@@ -239,7 +267,7 @@
});
};

function ResourceEntry({ entry, showRuntime }: { entry: ResourceStatusEntry; showRuntime?: boolean }) {

Check warning on line 270 in src/cli/commands/status/command.tsx

View workflow job for this annotation

GitHub Actions/ lint

Fast refresh only works when a file only exports components. Move your component(s) to a separate file. If all exports are HOCs, add them to the `extraHOCs` option
return (
<Text>
{' '}
Expand Down
1 change: 1 addition & 0 deletions src/cli/logging/remove-logger.ts
Original file line numberDiff line numberDiff line change
Expand Up@@ -13,6 +13,7 @@ export interface RemoveLoggerOptions {
| 'credential'
| 'gateway'
| 'gateway-target'
| 'runtime-endpoint'
| 'evaluator'
| 'online-eval'
| 'policy-engine'
Expand Down
Loading
Loading
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Universal Dark Mode - works on any site\n(function() {\n var enabled = true;\n \n function applyDarkMode() {\n if (!enabled) return;\n \n // Create style element if it doesn't exist\n var style = document.getElementById('universal-dark-mode-style');\n if (!style) {\n style = document.createElement('style');\n style.id = 'universal-dark-mode-style';\n document.head.appendChild(style);\n }\n \n // Dark mode CSS - inverts colors but preserves images/video\n style.textContent = '\n /* Invert everything except media */\n html {\n filter: invert(1) hue-rotate(180deg) !important;\n background: #1a1a2e !important;\n }\n \n /* Restore images, videos, iframes, canvas */\n img, video, iframe, canvas, svg, picture, [style*=\"background-image\"] {\n filter: invert(1) hue-rotate(180deg) !important;\n }\n \n /* Preserve specific elements that should not be inverted */\n .no-dark-mode, .no-dark-mode *,\n [data-theme=\"light\"], [data-theme=\"light\"],\n .ace_editor, .ace_editor *,\n .CodeMirror, .CodeMirror *,\n .monaco-editor, .monaco-editor *,\n .markdown-body pre, .markdown-body pre *,\n .highlight, .highlight *,\n pre code, pre code * {\n filter: none !important;\n }\n \n /* Fix common UI elements */\n .modal, .popup, .dropdown-menu, .tooltip, .popover {\n filter: invert(1) hue-rotate(180deg) !important;\n background: #2d2d44 !important;\n border-color: #444 !important;\n }\n \n /* Scrollbars */\n ::-webkit-scrollbar { background: #1a1a2e !important; }\n ::-webkit-scrollbar-thumb { background: #444 !important; }\n ::-webkit-scrollbar-thumb:hover { background: #555 !important; }\n \n /* Selection */\n ::selection { background: #4ecdc4 !important; color: #1a1a2e !important; }\n ::-moz-selection { background: #4ecdc4 !important; color: #1a1a2e !important; }\n ';\n }\n \n function removeDarkMode() {\n var style = document.getElementById('universal-dark-mode-style');\n if (style) style.remove();\n }\n \n // Toggle with Alt+Shift+D\n document.addEventListener('keydown', function(e) {\n if (e.altKey && e.shiftKey && e.key === 'D') {\n e.preventDefault();\n enabled = !enabled;\n if (enabled) {\n applyDarkMode();\n console.log('[Universal Dark Mode] Enabled');\n } else {\n removeDarkMode();\n console.log('[Universal Dark Mode] Disabled');\n }\n }\n });\n \n // Apply on load\n applyDarkMode();\n \n // Re-apply on dynamic content\n var observer = new MutationObserver(function(mutations) {\n if (enabled && !document.getElementById('universal-dark-mode-style')) {\n applyDarkMode();\n }\n });\n observer.observe(document.head, { childList: true });\n \n console.log('[Universal Dark Mode] Loaded - Press Alt+Shift+D to toggle');\n})();", "Universal Dark Mode"); } } catch(__e) { console.warn('[Userscript:Universal Dark Mode]', __e); } })(); })();
Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
19 commits
Select commit Hold shift + click to select a range
cb839bf
feat: add runtime endpoint support to AgentCore CLI
jariy17 Apr 25, 2026
3b29773
fix: correct output key prefix for runtime endpoint parsing
jariy17 Apr 25, 2026
33b81da
fix: remove .omc state files and unused useCallback import
jariy17 Apr 27, 2026
6b4f8f3
fix: shorten runtime endpoint description to prevent TUI overflow
jariy17 Apr 27, 2026
c1ad7ea
fix: validate runtime endpoint version is a positive integer
jariy17 Apr 27, 2026
f3face8
fix: use agent/endpoint composite key to prevent React key collision
jariy17 Apr 27, 2026
21147b1
fix: render runtime endpoints in status --type runtime-endpoint
jariy17 Apr 27, 2026
132f969
fix: add runtime-endpoint to status --help --type documentation
jariy17 Apr 27, 2026
3aba3e9
fix: return richer JSON response from add runtime-endpoint
jariy17 Apr 27, 2026
2bb5a86
fix: validate endpoint version against deployed runtime version
jariy17 Apr 27, 2026
844689a
chore: remove planning and bug bash docs from PR
jariy17 Apr 27, 2026
11a7a86
fix: use composite key and parentName for endpoint identification
jariy17 Apr 27, 2026
d6389c8
test: add comprehensive unit tests for RuntimeEndpointPrimitive
jariy17 Apr 27, 2026
b9f158f
fix: remove dead findGatewayTargetReferences stub
jariy17 Apr 27, 2026
0ff1db8
fix: use BasePrimitive configIO instead of ad-hoc ConfigIO in add()
jariy17 Apr 27, 2026
be47f35
fix: use Number() instead of parseInt in TUI version validation
jariy17 Apr 27, 2026
fd8e15c
Merge branch 'main' into feat/endpoint_based_abs
jariy17 Apr 27, 2026
ddbeff2
chore: fix prettier formatting
jariy17 Apr 27, 2026
117a07e
fix: use T[] instead of Array<T> to satisfy eslint array-type rule
jariy17 Apr 27, 2026
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
1 change: 1 addition & 0 deletions .gitignore
Original file line numberDiff line numberDiff line change
Expand Up@@ -67,6 +67,7 @@ ProtocolTesting/

# Auto-cloned CDK constructs (from scripts/bundle.mjs)
.cdk-constructs-clone/
.omc/

# Browser tests
browser-tests/.browser-test-env
Expand Down
42 changes: 42 additions & 0 deletions src/cli/cloudformation/outputs.ts
Original file line numberDiff line numberDiff line change
Expand Up@@ -6,6 +6,7 @@ import type {
OnlineEvalDeployedState,
PolicyDeployedState,
PolicyEngineDeployedState,
RuntimeEndpointDeployedState,
TargetDeployedState,
} from '../../schema';
import { getCredentialProvider } from '../aws';
Expand DownExpand Up@@ -338,6 +339,40 @@ export function parsePolicyOutputs(
return policies;
}

/**
* Parse stack outputs into deployed state for runtime endpoints.
*
* Output key pattern: ApplicationAgent{AgentPascal}Endpoint{AgentPascal}{EndpointPascal}(Id|Arn)Output{Hash}
* The Agent{PascalName} prefix comes from the AgentEnvironment construct in the CDK tree.
*/
export function parseRuntimeEndpointOutputs(
outputs: StackOutputs,
endpointSpecs: { agentName: string; endpointName: string }[]
): Record<string, RuntimeEndpointDeployedState> {
const endpoints: Record<string, RuntimeEndpointDeployedState> = {};
const outputKeys = Object.keys(outputs);

for (const { agentName, endpointName } of endpointSpecs) {
const agentPascal = toPascalId(agentName);
const endpointPascal = toPascalId('Endpoint', agentName, endpointName);
const idPrefix = `ApplicationAgent${agentPascal}${endpointPascal}IdOutput`;
const arnPrefix = `ApplicationAgent${agentPascal}${endpointPascal}ArnOutput`;

const idKey = outputKeys.find(k => k.startsWith(idPrefix));
const arnKey = outputKeys.find(k => k.startsWith(arnPrefix));

if (idKey && arnKey) {
const key = `${agentName}/${endpointName}`;
endpoints[key] = {
endpointId: outputs[idKey]!,
endpointArn: outputs[arnKey]!,
};
}
}

return endpoints;
}

export interface BuildDeployedStateOptions {
targetName: string;
stackName: string;
Expand All@@ -351,6 +386,7 @@ export interface BuildDeployedStateOptions {
onlineEvalConfigs?: Record<string, OnlineEvalDeployedState>;
policyEngines?: Record<string, PolicyEngineDeployedState>;
policies?: Record<string, PolicyDeployedState>;
runtimeEndpoints?: Record<string, RuntimeEndpointDeployedState>;
}

/**
Expand All@@ -370,6 +406,7 @@ export function buildDeployedState(opts: BuildDeployedStateOptions): DeployedSta
onlineEvalConfigs,
policyEngines,
policies,
runtimeEndpoints,
} = opts;
const targetState: TargetDeployedState = {
resources: {
Expand DownExpand Up@@ -404,6 +441,11 @@ export function buildDeployedState(opts: BuildDeployedStateOptions): DeployedSta
targetState.resources!.onlineEvalConfigs = onlineEvalConfigs;
}

// Add runtime endpoint state if endpoints exist
if (runtimeEndpoints && Object.keys(runtimeEndpoints).length > 0) {
targetState.resources!.runtimeEndpoints = runtimeEndpoints;
}

return {
targets: {
...existingState?.targets,
Expand Down
13 changes: 13 additions & 0 deletions src/cli/commands/deploy/actions.ts
Original file line numberDiff line numberDiff line change
Expand Up@@ -13,6 +13,7 @@ import {
parseOnlineEvalOutputs,
parsePolicyEngineOutputs,
parsePolicyOutputs,
parseRuntimeEndpointOutputs,
} from '../../cloudformation';
import { getErrorMessage } from '../../errors';
import { ExecLogger } from '../../logging';
Expand DownExpand Up@@ -403,6 +404,17 @@ export async function handleDeploy(options: ValidatedDeployOptions): Promise<Dep
);
const policies = parsePolicyOutputs(outputs, policySpecs);

// Parse runtime endpoint outputs
const endpointSpecs: { agentName: string; endpointName: string }[] = [];
for (const runtime of context.projectSpec.runtimes) {
if (runtime.endpoints) {
for (const endpointName of Object.keys(runtime.endpoints)) {
endpointSpecs.push({ agentName: runtime.name, endpointName });
}
}
}
const runtimeEndpoints = parseRuntimeEndpointOutputs(outputs, endpointSpecs);

// Parse gateway outputs
const gatewaySpecs =
mcpSpec?.agentCoreGateways?.reduce(
Expand All@@ -428,6 +440,7 @@ export async function handleDeploy(options: ValidatedDeployOptions): Promise<Dep
onlineEvalConfigs,
policyEngines,
policies,
runtimeEndpoints,
});
await configIO.writeDeployedState(deployedState);

Expand Down
1 change: 1 addition & 0 deletions src/cli/commands/remove/types.ts
Original file line numberDiff line numberDiff line change
Expand Up@@ -2,6 +2,7 @@ export type ResourceType =
| 'agent'
| 'gateway'
| 'gateway-target'
| 'runtime-endpoint'
| 'memory'
| 'credential'
| 'evaluator'
Expand Down
36 changes: 35 additions & 1 deletion src/cli/commands/status/action.ts
Original file line numberDiff line numberDiff line change
Expand Up@@ -18,11 +18,13 @@ export interface ResourceStatusEntry {
| 'evaluator'
| 'online-eval'
| 'policy-engine'
| 'policy';
| 'policy'
| 'runtime-endpoint';
name: string;
deploymentState: ResourceDeploymentState;
identifier?: string;
detail?: string;
parentName?: string;
error?: string;
invocationUrl?: string;
}
Expand DownExpand Up@@ -79,13 +81,15 @@ function diffResourceSet<TLocal extends { name: string }, TDeployed>({
getIdentifier,
getLocalDetail,
getDeployedKey,
getParentName,
}: {
resourceType: ResourceStatusEntry['resourceType'];
localItems: TLocal[];
deployedRecord: Record<string, TDeployed>;
getIdentifier: (deployed: TDeployed) => string | undefined;
getLocalDetail?: (item: TLocal) => string | undefined;
getDeployedKey?: (item: TLocal) => string;
getParentName?: (item: TLocal) => string | undefined;
}): ResourceStatusEntry[] {
const entries: ResourceStatusEntry[] = [];
const localKeys = new Set(localItems.map(item => (getDeployedKey ? getDeployedKey(item) : item.name)));
Expand All@@ -99,16 +103,20 @@ function diffResourceSet<TLocal extends { name: string }, TDeployed>({
deploymentState: deployed ? 'deployed' : 'local-only',
identifier: deployed ? getIdentifier(deployed) : undefined,
detail: getLocalDetail?.(item),
parentName: getParentName?.(item),
});
}

for (const [name, deployed] of Object.entries(deployedRecord)) {
if (!localKeys.has(name)) {
// For pending-removal entries, try to extract parentName from composite key
const slashIdx = name.indexOf('/');
entries.push({
resourceType,
name,
deploymentState: 'pending-removal',
identifier: getIdentifier(deployed),
parentName: getParentName && slashIdx > 0 ? name.substring(0, slashIdx) : undefined,
});
}
}
Expand DownExpand Up@@ -202,8 +210,34 @@ export function computeResourceStatuses(
getDeployedKey: item => `${item.engineName}/${item.name}`,
});

// Flatten runtime endpoints for diffing against deployed state
const localEndpoints: { name: string; agentName: string; version: number; description?: string }[] = [];
for (const runtime of project.runtimes) {
if (runtime.endpoints) {
for (const [epName, ep] of Object.entries(runtime.endpoints)) {
localEndpoints.push({
name: epName,
agentName: runtime.name,
version: ep.version,
description: ep.description,
});
}
}
}

const runtimeEndpoints = diffResourceSet({
resourceType: 'runtime-endpoint',
localItems: localEndpoints,
deployedRecord: resources?.runtimeEndpoints ?? {},
getIdentifier: deployed => deployed.endpointArn,
getLocalDetail: item => `v${item.version}${item.description ? ` — ${item.description}` : ''}`,
getDeployedKey: item => `${item.agentName}/${item.name}`,
getParentName: item => item.agentName,
});

return [
...agents,
...runtimeEndpoints,
...credentials,
...memories,
...gateways,
Expand Down
48 changes: 38 additions & 10 deletions src/cli/commands/status/command.tsx
Original file line numberDiff line numberDiff line change
Expand Up@@ -9,6 +9,7 @@

const VALID_RESOURCE_TYPES = [
'agent',
'runtime-endpoint',
'memory',
'credential',
'gateway',
Expand DownExpand Up@@ -58,7 +59,7 @@
.option('--target <name>', 'Select deployment target')
.option(
'--type <type>',
'Filter by resource type (agent, memory, credential, gateway, evaluator, online-eval, policy-engine, policy)'
'Filter by resource type (agent, runtime-endpoint, memory, credential, gateway, evaluator, online-eval, policy-engine, policy)'
)
.option('--state <state>', 'Filter by deployment state (deployed, local-only, pending-removal)')
.option('--runtime <name>', 'Filter to a specific runtime')
Expand DownExpand Up@@ -135,6 +136,7 @@

const filtered = filterResources(result.resources, cliOptions);
const agents = filtered.filter(r => r.resourceType === 'agent');
const runtimeEndpoints = filtered.filter(r => r.resourceType === 'runtime-endpoint');
const credentials = filtered.filter(r => r.resourceType === 'credential');
const memories = filtered.filter(r => r.resourceType === 'memory');
const gateways = filtered.filter(r => r.resourceType === 'gateway');
Expand All@@ -153,15 +155,41 @@
{agents.length > 0 && (
<Box flexDirection="column" marginTop={1}>
<Text bold>Agents</Text>
{agents.map(entry => (
<Box key={`${entry.resourceType}-${entry.name}`} flexDirection="column">
<ResourceEntry entry={entry} showRuntime />
{entry.invocationUrl && (
<Text dimColor>
{' '}URL: {entry.invocationUrl}
</Text>
)}
</Box>
{agents.map(entry => {
// Find endpoints belonging to this agent
const agentEndpoints = runtimeEndpoints.filter(ep => ep.parentName === entry.name);
return (
<Box key={`${entry.resourceType}-${entry.name}`} flexDirection="column">
<ResourceEntry entry={entry} showRuntime />
{entry.invocationUrl && (
<Text dimColor>
{' '}URL: {entry.invocationUrl}
</Text>
)}
{agentEndpoints.map(ep => (
Comment thread
notgitika marked this conversation as resolved.
Comment thread
notgitika marked this conversation as resolved.
<Text key={`${ep.parentName}/${ep.name}`}>
{' '}◉ {ep.name} <Text dimColor>{ep.detail}</Text>{' '}
<Text color={DEPLOYMENT_STATE_COLORS[ep.deploymentState] ?? 'gray'}>
[{DEPLOYMENT_STATE_LABELS[ep.deploymentState] ?? ep.deploymentState}]
</Text>
</Text>
))}
</Box>
);
})}
</Box>
)}

{agents.length === 0 && runtimeEndpoints.length > 0 && (
<Box flexDirection="column" marginTop={1}>
<Text bold>Runtime Endpoints</Text>
{runtimeEndpoints.map(ep => (
<Text key={`${ep.parentName}/${ep.name}`}>
{' '}◉ {ep.parentName}/{ep.name} <Text dimColor>{ep.detail}</Text>{' '}
<Text color={DEPLOYMENT_STATE_COLORS[ep.deploymentState] ?? 'gray'}>
Comment thread
notgitika marked this conversation as resolved.
Comment thread
notgitika marked this conversation as resolved.
[{DEPLOYMENT_STATE_LABELS[ep.deploymentState] ?? ep.deploymentState}]
</Text>
</Text>
))}
</Box>
)}
Expand DownExpand Up@@ -239,7 +267,7 @@
});
};

function ResourceEntry({ entry, showRuntime }: { entry: ResourceStatusEntry; showRuntime?: boolean }) {

Check warning on line 270 in src/cli/commands/status/command.tsx

View workflow job for this annotation

GitHub Actions/ lint

Fast refresh only works when a file only exports components. Move your component(s) to a separate file. If all exports are HOCs, add them to the `extraHOCs` option
return (
<Text>
{' '}
Expand Down
1 change: 1 addition & 0 deletions src/cli/logging/remove-logger.ts
Original file line numberDiff line numberDiff line change
Expand Up@@ -13,6 +13,7 @@ export interface RemoveLoggerOptions {
| 'credential'
| 'gateway'
| 'gateway-target'
| 'runtime-endpoint'
| 'evaluator'
| 'online-eval'
| 'policy-engine'
Expand Down
Loading
Loading