Repository navigation
feat(console): list the AI assistants you authorized and your API keys under Connect an Agent - #31054
Conversation
…an Agent Co-Authored-By: Claude <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01L9qibz28PqZhuQmVQEvmY2
Co-Authored-By: Claude <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01L9qibz28PqZhuQmVQEvmY2
…pure Co-Authored-By: Claude <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01L9qibz28PqZhuQmVQEvmY2
|
domain:ui#3 · os-tesla · session_01L9qibxW6qraU2Z3mwupUGp Contract reviewServed-tier: ① Derived judgments
② Semver level
③ Boundary flagsNone. No server field, no ④ Design judgmentThe card's list half, item by item: authorized assistants (name, scopes, authorized, last used or "not recorded") and API keys (name, created, last used, revoke) under the setup steps, which already sit in Account's AI Assistant group. Not offering a withdraw that does not exist matches the card's own fallback. Implemented-by: VERDICT: PASS |
…t-free The write-site census enters the key list's Revoke as a write of the caller's own sys_api_key row. The truncated note no longer carries a count (the list answers no total), and the de quote-pairing pin counts the new revoke title. Co-Authored-By: Claude <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01L9qibz28PqZhuQmVQEvmY2
|
domain:ui#3 · os-tesla · session_01L9qibxW6qraU2Z3mwupUGp 维护者速读
席位核对 — ACCEPT · head 092e2d9
|
Requested by Tesla · project thread
Part of #30894 (part 1 of 2: the list. Part 2 adds the assistants' Revoke once the server's withdraw door, #31048, lands.)
Clause-②: yes (widening)
Metadata-protocol: none
维护者速读
What changes
AuthorizedAccessList(ui/packages/app-shell/src/console/connect/), mounted under the setup steps of Connect an Agent, which already sits in Account's AI Assistant group (no nav change).oauth.authorizedClients.list()through the adapter's own client: the client's name (its client id beside an "Unnamed assistant" fallback when it declares none), each granted scope in the consent page's own words (the existingoauth.consent.scope.*keys; an unknown scope shows as unrecognised, never dropped), when authorized, and the last use as "in the hour from …" because the server records the hour start, or "Last use not recorded". The organization is named only when it is not the active one. No Revoke on an assistant row; one line says withdrawing from this page is not available yet. Atruncatedanswer says only the newest are shown.find('sys_api_key')filtered touser_id= the signed-in person and selecting onlyid, name, prefix, created_at, last_used_at, expires_at, revoked(never the secret). Revoked and expired keys show their state and no Revoke. Revoke on an active key asks first, naming the key, then writesrevoked: truethrough the adapter; the adapter's mutation event re-reads the list in place (no remount).ConnectAgentWidget.tsx: mounts the list, and after a successful mint callsnotifyDataChanged({ objectName: 'sys_api_key' })(the mint posts to/api/v1/keysoutside the adapter) so the new key shows without a reload. No existing line or string is edited.connectAgent.access.*keys in all eleven packs;zh-Hantregenerated fromzh-Hanswithgen:zh-hant..changeset/30894-authorized-access-list.md(@object-ui/app-shell,@object-ui/i18n: patch).Tests
AuthorizedAccessList.test.tsx(12 cases): assistants render name, consent-worded scopes, authorized and hour-coarse last use or not recorded; organization only when not the active one; nameless client shows its id; no button on any assistant row; truncated note. Keys: the read filters on the signed-in user and never selects the secret; revoked/expired state with no Revoke; Revoke asks first naming the key, Cancel writes nothing, confirm writesrevoked: trueand re-reads in place without re-reading assistants. Empty, failed (the error is named, not an empty list) and signed-out/no-adapter (renders and reads nothing) states. The widget's existing suite still passes with the list mounted.Verification
Live reading on origin/main 115a43a (showcase, fresh, member and platform admin)
GET /api/v1/auth/authorized-clients: admin and member with no grants answer 200{clients: [], truncated: false}. Member after granting Claude Desktop (one MCP call) and Cursor (never used): 200, newest first, CursorlastUsedAt: null, Claude DesktoplastUsedAt= the hour start. With?userId=x: 400 VALIDATION_ERROR. Admin's own read lists only the admin's grants.GET /api/v1/auth/admin/authorized-clients?userId=: member 403; platform admin over the member 200 with the member's rows; unknown id 404.sys_api_keyvia the data API: member reads own rows only; the platform admin's unfiltered read answers every person's keys,?user_id=self answers its own (hence the filter). Using a key stampslast_used_at;PATCH {revoked: true}answers 200 and the key is then refused (403).Console, live (Vite dev against that backend), head before the lint fix
As the member: the card lists both assistants with their scopes and "Last used in the hour from Nov 6, 2026, 9:00 PM" / "Last use not recorded", and the member's keys. As the platform admin: only the admin's own keys; Revoke opened the confirm "Revoke the API key "admin ci key"?", confirming flipped that row to Revoked in place.
Local gates and tests, head e903d94
pnpm --dir ui exec vitest run packages/app-shell/src/console/connect/: 2 files, 24 tests passed.pnpm --filter @object-ui/app-shell type-check: exit 0 (bothtscprograms);@object-ui/i18ntype-check exit 0 (on d06a9fc, packs unchanged since).node scripts/pm/dispatch-gates.mjs --commandsderived 38 commands; all exit 0 (--ranreconciliation: 38 derived, 38 run, 0 NOT-MEASURED, 0 UNRUN), includingnode scripts/check-sdui-manifest.mjsandpnpm --dir ui exec vitest run scripts/(170 files passed, 2 skipped). Ratchet families re-run on e903d94:check:type-check-coverage,check:type-check-debt,check:nul-bytes,check:test-source-aliasexit 0.pnpm check:new-line-citations(ui): 0 new citations.i18n-locale-eneager-closure ceiling (needs the Console production build; CI'sBuild & E2E UIstepcheck:eager-closureis the verdict). The ceiling is not changed.Acceptance notes
POST /oauth2/delete-consent) has no ledgered route or client method yet (Auth: the console cannot withdraw an authorized AI assistant: /oauth2/delete-consent has no ledger row or client method, and no admin door #31048), so no button is offered rather than one that leaves access in place; the card says withdrawing here is not available yet. Part 2 removes that line and adds the button, and closes the card.listForUser): Connect an Agent has no place where an administrator names a person, so it is left to part 2 as the claim allows.ConnectAgentWidget.tsxgets the mount, one import pair and the post-mintnotifyDataChanged; no existing key in any pack is edited. Whichever lands second mergesmain.Human Minimum
packages/specholds no runtime logic. (No spec change.)pnpm lintis CI's).main, one pull request.packages/spectouched: n/a.content/docs/references/,packages/*/CHANGELOG.mdorcontent/docs/releases/.🤖 Generated with Claude Code
https://claude.ai/code/session_01L9qibz28PqZhuQmVQEvmY2
Generated by Claude Code