Skip to content

Commit ae5dfd8

Browse files
authored
feat: Implement TUI for identity (#1886)
* feat: implement TUI for identity * chore: use ResourceDetail screen and display failure reason in details
1 parent 259e314 commit ae5dfd8

20 files changed

Lines changed: 1080 additions & 37 deletions

File tree

README.md

Lines changed: 39 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -9,8 +9,8 @@ It gives you two ways to work, from the same binary:
99
- **A scriptable CLI** — every operation is a flag-driven subcommand that emits
1010
JSON (`--json`), so it can be used by codeing agents and can drop cleanly into
1111
scripts, CI, and automation.
12-
- **An interactive TUI** — bare Harness, Runtime, and Memory branches and leaves open
13-
their corresponding menus and selection flows.
12+
- **An interactive TUI** — bare Harness, Runtime, Memory, and Identity branches
13+
and leaves open their corresponding menus and selection flows.
1414

1515
```bash
1616
agentcore # launch the interactive TUI
@@ -26,8 +26,8 @@ responses. `agentcore` wraps all of that behind one ergonomic tool.
2626

2727
## Command surface
2828

29-
Commands with operation flags run headlessly. Bare Harness, Runtime, and Memory branches
30-
and leaves open their interactive flows.
29+
Commands with operation flags run headlessly. Bare Harness, Runtime, Memory, and
30+
Identity branches and leaves open their interactive flows.
3131

3232
```
3333
agentcore # interactive TUI
@@ -49,12 +49,18 @@ agentcore # interactive TUI
4949
│ ├── update
5050
│ └── delete
5151
├── identity # manage AgentCore Identity resources
52-
│ └── api-key-credential-provider
53-
│ ├── create # create an API key credential provider
54-
│ ├── get # get an API key credential provider
55-
│ ├── list # list API key credential providers
56-
│ ├── update # update an API key credential provider
57-
│ └── delete # delete an API key credential provider
52+
│ ├── api-key-credential-provider
53+
│ │ ├── create # create an API key credential provider
54+
│ │ ├── get # get an API key credential provider
55+
│ │ ├── list # list API key credential providers
56+
│ │ ├── update # update an API key credential provider
57+
│ │ └── delete # delete an API key credential provider
58+
│ └── oauth2-credential-provider
59+
│ ├── create # create an OAuth2 credential provider
60+
│ ├── get # get an OAuth2 credential provider
61+
│ ├── list # list OAuth2 credential providers
62+
│ ├── update # update an OAuth2 credential provider
63+
│ └── delete # delete an OAuth2 credential provider
5864
├── runtime # inspect deployed AgentCore Runtimes
5965
│ ├── get # fetch a Runtime by id
6066
│ ├── list # list Runtimes (server-side paginated)
@@ -162,6 +168,17 @@ agentcore identity api-key-credential-provider list --max-results 10
162168
agentcore identity api-key-credential-provider update --name my-provider --api-key <new-key>
163169
agentcore identity api-key-credential-provider delete --name my-provider
164170

171+
# Manage OAuth2 credential providers (guided Custom OAuth2, or --provider-configuration for other vendors)
172+
agentcore identity oauth2-credential-provider create \
173+
--name my-oauth-provider \
174+
--vendor CustomOauth2 \
175+
--client-id <client-id> \
176+
--discovery-url https://issuer.example.com/.well-known/openid-configuration \
177+
--client-secret -
178+
agentcore identity oauth2-credential-provider get --name my-oauth-provider
179+
agentcore identity oauth2-credential-provider list --max-results 10
180+
agentcore identity oauth2-credential-provider delete --name my-oauth-provider
181+
165182
# Manage evaluators
166183
# Create an LLM-as-a-Judge evaluator with a rating-scale preset.
167184
agentcore eval evaluator llm-as-a-judge create \
@@ -311,6 +328,18 @@ agentcore memory event
311328
agentcore memory record
312329
```
313330

331+
The Identity TUI is read-only: bare `identity` branches and the `get`/`list`
332+
leaves open interactive menus and detail views. Mutations (`create`, `update`,
333+
`delete`) remain available through the CLI and are omitted from the TUI menus.
334+
335+
```bash
336+
agentcore identity
337+
agentcore identity api-key-credential-provider list
338+
agentcore identity api-key-credential-provider get
339+
agentcore identity oauth2-credential-provider list
340+
agentcore identity oauth2-credential-provider get
341+
```
342+
314343
---
315344

316345
# Architecture & patterns

src/components/PaginatedTablePicker.tsx

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ export interface PaginatedTablePickerProps<TItem, TRow extends Record<string, un
2626
errorMessage: (error: Error) => string;
2727
emptyMessage: string;
2828
emptyPageMessage: string;
29+
maxPageSize?: number;
2930
}
3031

3132
export function PaginatedTablePicker<TItem, TRow extends Record<string, unknown>>({
@@ -43,8 +44,9 @@ export function PaginatedTablePicker<TItem, TRow extends Record<string, unknown>
4344
errorMessage,
4445
emptyMessage,
4546
emptyPageMessage,
47+
maxPageSize,
4648
}: PaginatedTablePickerProps<TItem, TRow>) {
47-
const paging = usePagedList();
49+
const paging = usePagedList(maxPageSize);
4850
const list = useQuery({
4951
queryKey: [...queryKey, paging.pageSize, paging.token],
5052
queryFn: () => loadPage(paging.token, paging.pageSize),

src/components/Root.tsx

Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,19 @@ import {
4848
import { MemoryEventScreen } from "../handlers/memory/event/screen.tsx";
4949
import { MemoryEventGetScreen } from "../handlers/memory/event/get/screen.tsx";
5050
import { MemoryEventListScreen } from "../handlers/memory/event/list/screen.tsx";
51+
import { IdentityScreen } from "../handlers/identity/screen.tsx";
52+
import { ApiKeyCredentialProviderScreen } from "../handlers/identity/api-key-credential-provider/screen.tsx";
53+
import { ApiKeyCredentialProviderListScreen } from "../handlers/identity/api-key-credential-provider/list/screen.tsx";
54+
import {
55+
ApiKeyCredentialProviderGetScreen,
56+
ApiKeyCredentialProviderGetJsonScreen,
57+
} from "../handlers/identity/api-key-credential-provider/get/screen.tsx";
58+
import { Oauth2CredentialProviderScreen } from "../handlers/identity/oauth2-credential-provider/screen.tsx";
59+
import { Oauth2CredentialProviderListScreen } from "../handlers/identity/oauth2-credential-provider/list/screen.tsx";
60+
import {
61+
Oauth2CredentialProviderGetScreen,
62+
Oauth2CredentialProviderGetJsonScreen,
63+
} from "../handlers/identity/oauth2-credential-provider/get/screen.tsx";
5164
import { RootScreen, HelpScreen } from "../handlers/screen.tsx";
5265
import type { Context } from "../router";
5366

@@ -374,6 +387,48 @@ export function Root({ path, ctx, core, queryClient }: RootProps) {
374387
path="agentcore/memory/event/list/:memoryId/:actorId/:sessionId"
375388
element={<MemoryEventListScreen ctx={ctx} core={core} />}
376389
/>
390+
<Route path="agentcore/identity" element={<IdentityScreen ctx={ctx} core={core} />} />
391+
<Route
392+
path="agentcore/identity/api-key-credential-provider"
393+
element={<ApiKeyCredentialProviderScreen ctx={ctx} core={core} />}
394+
/>
395+
{/* Bare `get` (no name) has nothing to show — send the user to the list. */}
396+
<Route
397+
path="agentcore/identity/api-key-credential-provider/get"
398+
element={<Navigate to="/agentcore/identity/api-key-credential-provider/list" replace />}
399+
/>
400+
<Route
401+
path="agentcore/identity/api-key-credential-provider/list"
402+
element={<ApiKeyCredentialProviderListScreen ctx={ctx} core={core} />}
403+
/>
404+
<Route
405+
path="agentcore/identity/api-key-credential-provider/get/:name"
406+
element={<ApiKeyCredentialProviderGetScreen ctx={ctx} core={core} />}
407+
/>
408+
<Route
409+
path="agentcore/identity/api-key-credential-provider/get/:name/json"
410+
element={<ApiKeyCredentialProviderGetJsonScreen ctx={ctx} core={core} />}
411+
/>
412+
<Route
413+
path="agentcore/identity/oauth2-credential-provider"
414+
element={<Oauth2CredentialProviderScreen ctx={ctx} core={core} />}
415+
/>
416+
<Route
417+
path="agentcore/identity/oauth2-credential-provider/get"
418+
element={<Navigate to="/agentcore/identity/oauth2-credential-provider/list" replace />}
419+
/>
420+
<Route
421+
path="agentcore/identity/oauth2-credential-provider/list"
422+
element={<Oauth2CredentialProviderListScreen ctx={ctx} core={core} />}
423+
/>
424+
<Route
425+
path="agentcore/identity/oauth2-credential-provider/get/:name"
426+
element={<Oauth2CredentialProviderGetScreen ctx={ctx} core={core} />}
427+
/>
428+
<Route
429+
path="agentcore/identity/oauth2-credential-provider/get/:name/json"
430+
element={<Oauth2CredentialProviderGetJsonScreen ctx={ctx} core={core} />}
431+
/>
377432
<Route path="*" element={<HelpScreen ctx={ctx} core={core} />} />
378433
</Routes>
379434
</MemoryRouter>

src/components/usePagedList.tsx

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -37,9 +37,13 @@ function initialPagination(pageSize: number): PaginationState {
3737
// usePagedList holds the server-side pagination state shared by the picker
3838
// tables: a terminal-height-derived page size and the trail of nextTokens
3939
// leading to the current page, so ←/h can walk back through cached pages.
40-
export function usePagedList(): PagedList {
40+
//
41+
// maxPageSize caps the terminal-derived page size for APIs that constrain
42+
// maxResults (e.g. identity list operation capped at 20)
43+
export function usePagedList(maxPageSize?: number): PagedList {
4144
const { rows } = useWindowSize();
42-
const pageSize = Math.max(3, rows - CHROME_ROWS);
45+
const fitsTerminal = Math.max(3, rows - CHROME_ROWS);
46+
const pageSize = maxPageSize ? Math.min(fitsTerminal, maxPageSize) : fitsTerminal;
4347

4448
const [state, setState] = useState<PaginationState>(() => initialPagination(pageSize));
4549
const pageSizeChanged = state.pageSize !== pageSize;

0 commit comments

Comments
 (0)