Skip to content

Commit 96e6691

Browse files
authored
feat: revamp agentcore status command to show all resources status (#504)
* feat: revamp agentcore status command to show all resources status * feat: add logging and CLI filter options to status command * fix: remove duplicate MemoryDeployedStateSchema after rebase
1 parent baae06b commit 96e6691

File tree

11 files changed

+1067
-406
lines changed

11 files changed

+1067
-406
lines changed
Lines changed: 303 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,303 @@
1+
import type { AgentCoreMcpSpec, AgentCoreProjectSpec, DeployedResourceState } from '../../../../schema/index.js';
2+
import { computeResourceStatuses } from '../action.js';
3+
import { describe, expect, it } from 'vitest';
4+
5+
const baseProject: AgentCoreProjectSpec = {
6+
name: 'test-project',
7+
version: 1,
8+
agents: [],
9+
memories: [],
10+
credentials: [],
11+
} as unknown as AgentCoreProjectSpec;
12+
13+
describe('computeResourceStatuses', () => {
14+
it('returns empty array for empty project with no deployed state', () => {
15+
const result = computeResourceStatuses(baseProject, undefined);
16+
expect(result).toEqual([]);
17+
});
18+
19+
it('marks agent as deployed when in both local and deployed state', () => {
20+
const project = {
21+
...baseProject,
22+
agents: [{ name: 'my-agent' }],
23+
} as unknown as AgentCoreProjectSpec;
24+
25+
const resources: DeployedResourceState = {
26+
agents: {
27+
'my-agent': {
28+
runtimeId: 'rt-123',
29+
runtimeArn: 'arn:aws:bedrock:us-east-1:123456789:agent-runtime/rt-123',
30+
roleArn: 'arn:aws:iam::123456789:role/test',
31+
},
32+
},
33+
};
34+
35+
const result = computeResourceStatuses(project, resources);
36+
const agentEntry = result.find(r => r.resourceType === 'agent' && r.name === 'my-agent');
37+
38+
expect(agentEntry).toBeDefined();
39+
expect(agentEntry!.deploymentState).toBe('deployed');
40+
expect(agentEntry!.identifier).toBe('arn:aws:bedrock:us-east-1:123456789:agent-runtime/rt-123');
41+
});
42+
43+
it('marks agent as local-only when not in deployed state', () => {
44+
const project = {
45+
...baseProject,
46+
agents: [{ name: 'my-agent' }],
47+
} as unknown as AgentCoreProjectSpec;
48+
49+
const result = computeResourceStatuses(project, undefined);
50+
const agentEntry = result.find(r => r.resourceType === 'agent' && r.name === 'my-agent');
51+
52+
expect(agentEntry).toBeDefined();
53+
expect(agentEntry!.deploymentState).toBe('local-only');
54+
expect(agentEntry!.identifier).toBeUndefined();
55+
});
56+
57+
it('marks agent as pending-removal when in deployed state but not in local schema', () => {
58+
const resources: DeployedResourceState = {
59+
agents: {
60+
'removed-agent': {
61+
runtimeId: 'rt-456',
62+
runtimeArn: 'arn:aws:bedrock:us-east-1:123456789:agent-runtime/rt-456',
63+
roleArn: 'arn:aws:iam::123456789:role/test',
64+
},
65+
},
66+
};
67+
68+
const result = computeResourceStatuses(baseProject, resources);
69+
const agentEntry = result.find(r => r.resourceType === 'agent' && r.name === 'removed-agent');
70+
71+
expect(agentEntry).toBeDefined();
72+
expect(agentEntry!.deploymentState).toBe('pending-removal');
73+
expect(agentEntry!.identifier).toBe('arn:aws:bedrock:us-east-1:123456789:agent-runtime/rt-456');
74+
});
75+
76+
it('marks credential as deployed when in both local and deployed state', () => {
77+
const project = {
78+
...baseProject,
79+
credentials: [{ name: 'my-cred', type: 'OAuthCredentialProvider' }],
80+
} as unknown as AgentCoreProjectSpec;
81+
82+
const resources: DeployedResourceState = {
83+
credentials: {
84+
'my-cred': {
85+
credentialProviderArn: 'arn:aws:bedrock:us-east-1:123456789:credential-provider/my-cred',
86+
},
87+
},
88+
};
89+
90+
const result = computeResourceStatuses(project, resources);
91+
const credEntry = result.find(r => r.resourceType === 'credential' && r.name === 'my-cred');
92+
93+
expect(credEntry).toBeDefined();
94+
expect(credEntry!.deploymentState).toBe('deployed');
95+
expect(credEntry!.identifier).toBe('arn:aws:bedrock:us-east-1:123456789:credential-provider/my-cred');
96+
expect(credEntry!.detail).toBe('OAuth');
97+
});
98+
99+
it('marks credential as local-only when not in deployed state', () => {
100+
const project = {
101+
...baseProject,
102+
credentials: [{ name: 'my-cred', type: 'ApiKeyCredentialProvider' }],
103+
} as unknown as AgentCoreProjectSpec;
104+
105+
const result = computeResourceStatuses(project, undefined);
106+
const credEntry = result.find(r => r.resourceType === 'credential' && r.name === 'my-cred');
107+
108+
expect(credEntry).toBeDefined();
109+
expect(credEntry!.deploymentState).toBe('local-only');
110+
expect(credEntry!.detail).toBe('ApiKey');
111+
});
112+
113+
it('marks credential as pending-removal when in deployed state but not in local schema', () => {
114+
const resources: DeployedResourceState = {
115+
credentials: {
116+
'removed-cred': {
117+
credentialProviderArn: 'arn:aws:bedrock:us-east-1:123456789:credential-provider/removed-cred',
118+
},
119+
},
120+
};
121+
122+
const result = computeResourceStatuses(baseProject, resources);
123+
const credEntry = result.find(r => r.resourceType === 'credential' && r.name === 'removed-cred');
124+
125+
expect(credEntry).toBeDefined();
126+
expect(credEntry!.deploymentState).toBe('pending-removal');
127+
expect(credEntry!.identifier).toBe('arn:aws:bedrock:us-east-1:123456789:credential-provider/removed-cred');
128+
});
129+
130+
it('marks memory as deployed when in both local and deployed state', () => {
131+
const project = {
132+
...baseProject,
133+
memories: [{ name: 'my-memory', strategies: [{ type: 'SEMANTIC' }] }],
134+
} as unknown as AgentCoreProjectSpec;
135+
136+
const resources: DeployedResourceState = {
137+
memories: {
138+
'my-memory': {
139+
memoryId: 'mem-123',
140+
memoryArn: 'arn:aws:bedrock:us-east-1:123456789:memory/mem-123',
141+
},
142+
},
143+
};
144+
145+
const result = computeResourceStatuses(project, resources);
146+
const memEntry = result.find(r => r.resourceType === 'memory' && r.name === 'my-memory');
147+
148+
expect(memEntry).toBeDefined();
149+
expect(memEntry!.deploymentState).toBe('deployed');
150+
expect(memEntry!.identifier).toBe('arn:aws:bedrock:us-east-1:123456789:memory/mem-123');
151+
expect(memEntry!.detail).toBe('SEMANTIC');
152+
});
153+
154+
it('marks memory as local-only when not in deployed state', () => {
155+
const project = {
156+
...baseProject,
157+
memories: [{ name: 'my-memory', strategies: [{ type: 'SUMMARIZATION' }] }],
158+
} as unknown as AgentCoreProjectSpec;
159+
160+
const result = computeResourceStatuses(project, undefined);
161+
const memEntry = result.find(r => r.resourceType === 'memory' && r.name === 'my-memory');
162+
163+
expect(memEntry).toBeDefined();
164+
expect(memEntry!.deploymentState).toBe('local-only');
165+
expect(memEntry!.detail).toBe('SUMMARIZATION');
166+
});
167+
168+
it('marks memory as pending-removal when in deployed state but not in local schema', () => {
169+
const resources: DeployedResourceState = {
170+
memories: {
171+
'removed-memory': {
172+
memoryId: 'mem-456',
173+
memoryArn: 'arn:aws:bedrock:us-east-1:123456789:memory/mem-456',
174+
},
175+
},
176+
};
177+
178+
const result = computeResourceStatuses(baseProject, resources);
179+
const pendingMemEntry = result.find(r => r.resourceType === 'memory' && r.deploymentState === 'pending-removal');
180+
181+
expect(pendingMemEntry).toBeDefined();
182+
expect(pendingMemEntry!.name).toBe('removed-memory');
183+
expect(pendingMemEntry!.identifier).toBe('arn:aws:bedrock:us-east-1:123456789:memory/mem-456');
184+
});
185+
186+
it('marks all resources as local-only when never deployed', () => {
187+
const project = {
188+
...baseProject,
189+
agents: [{ name: 'agent-a' }],
190+
memories: [{ name: 'mem-a', strategies: [] }],
191+
credentials: [{ name: 'cred-a', type: 'ApiKeyCredentialProvider' }],
192+
} as unknown as AgentCoreProjectSpec;
193+
194+
const result = computeResourceStatuses(project, undefined);
195+
196+
expect(result).toHaveLength(3);
197+
expect(result.every(r => r.deploymentState === 'local-only')).toBe(true);
198+
});
199+
200+
it('marks gateway as deployed when in both local mcp spec and deployed state', () => {
201+
const mcpSpec = {
202+
agentCoreGateways: [{ name: 'my-gateway', targets: [{ name: 't1' }, { name: 't2' }] }],
203+
} as unknown as AgentCoreMcpSpec;
204+
205+
const resources: DeployedResourceState = {
206+
mcp: {
207+
gateways: {
208+
'my-gateway': {
209+
gatewayId: 'gw-123',
210+
gatewayArn: 'arn:aws:bedrock:us-east-1:123456789:gateway/gw-123',
211+
},
212+
},
213+
},
214+
};
215+
216+
const result = computeResourceStatuses(baseProject, resources, mcpSpec);
217+
const gwEntry = result.find(r => r.resourceType === 'gateway' && r.name === 'my-gateway');
218+
219+
expect(gwEntry).toBeDefined();
220+
expect(gwEntry!.deploymentState).toBe('deployed');
221+
expect(gwEntry!.identifier).toBe('gw-123');
222+
expect(gwEntry!.detail).toBe('2 targets');
223+
});
224+
225+
it('marks gateway as local-only when not in deployed state', () => {
226+
const mcpSpec = {
227+
agentCoreGateways: [{ name: 'my-gateway', targets: [{ name: 't1' }] }],
228+
} as unknown as AgentCoreMcpSpec;
229+
230+
const result = computeResourceStatuses(baseProject, undefined, mcpSpec);
231+
const gwEntry = result.find(r => r.resourceType === 'gateway' && r.name === 'my-gateway');
232+
233+
expect(gwEntry).toBeDefined();
234+
expect(gwEntry!.deploymentState).toBe('local-only');
235+
expect(gwEntry!.detail).toBe('1 target');
236+
});
237+
238+
it('marks gateway as pending-removal when in deployed state but not in local mcp spec', () => {
239+
const mcpSpec = {
240+
agentCoreGateways: [],
241+
} as unknown as AgentCoreMcpSpec;
242+
243+
const resources: DeployedResourceState = {
244+
mcp: {
245+
gateways: {
246+
'removed-gateway': {
247+
gatewayId: 'gw-456',
248+
gatewayArn: 'arn:aws:bedrock:us-east-1:123456789:gateway/gw-456',
249+
},
250+
},
251+
},
252+
};
253+
254+
const result = computeResourceStatuses(baseProject, resources, mcpSpec);
255+
const gwEntry = result.find(r => r.resourceType === 'gateway' && r.name === 'removed-gateway');
256+
257+
expect(gwEntry).toBeDefined();
258+
expect(gwEntry!.deploymentState).toBe('pending-removal');
259+
expect(gwEntry!.identifier).toBe('gw-456');
260+
});
261+
262+
it('handles mixed deployed and local-only resources', () => {
263+
const project = {
264+
...baseProject,
265+
agents: [{ name: 'deployed-agent' }, { name: 'new-agent' }],
266+
credentials: [{ name: 'deployed-cred', type: 'OAuthCredentialProvider' }],
267+
} as unknown as AgentCoreProjectSpec;
268+
269+
const resources: DeployedResourceState = {
270+
agents: {
271+
'deployed-agent': {
272+
runtimeId: 'rt-123',
273+
runtimeArn: 'arn:aws:bedrock:us-east-1:123456789:agent-runtime/rt-123',
274+
roleArn: 'arn:aws:iam::123456789:role/test',
275+
},
276+
'old-agent': {
277+
runtimeId: 'rt-old',
278+
runtimeArn: 'arn:aws:bedrock:us-east-1:123456789:agent-runtime/rt-old',
279+
roleArn: 'arn:aws:iam::123456789:role/test',
280+
},
281+
},
282+
credentials: {
283+
'deployed-cred': {
284+
credentialProviderArn: 'arn:aws:bedrock:us-east-1:123456789:credential-provider/deployed-cred',
285+
},
286+
},
287+
};
288+
289+
const result = computeResourceStatuses(project, resources);
290+
291+
const deployedAgent = result.find(r => r.name === 'deployed-agent');
292+
expect(deployedAgent!.deploymentState).toBe('deployed');
293+
294+
const newAgent = result.find(r => r.name === 'new-agent');
295+
expect(newAgent!.deploymentState).toBe('local-only');
296+
297+
const oldAgent = result.find(r => r.name === 'old-agent');
298+
expect(oldAgent!.deploymentState).toBe('pending-removal');
299+
300+
const deployedCred = result.find(r => r.name === 'deployed-cred');
301+
expect(deployedCred!.deploymentState).toBe('deployed');
302+
});
303+
});

0 commit comments

Comments
 (0)