Skip to content

feat(console): list the AI assistants you authorized and your API keys under Connect an Agent - #31054

Merged
objectstack-fleet[bot] merged 5 commits into
mainfrom
claude/project-thread-oqumno
Nov 6, 2026
Merged

objectstack-fleet[bot] merged 5 commits into
mainfrom
claude/project-thread-oqumno

Conversation

@objectstack-fleet

Copy link
Copy Markdown
Contributor

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

维护者速读

  • 改了什么:「连接 AI 助手」页面在设置步骤下方新增「可以代表你操作的助手和密钥」卡片,列出本人已授权的 AI 助手(名称、授予的权限、授权时间、最近使用时间)和本人的 API 密钥(名称、前缀、创建时间、最近使用、已撤销/已过期),API 密钥可确认后撤销。
  • 为什么改:OAuth 授权页承诺「可以随时在账户设置中撤销访问权限」,但此前没有任何页面能看到自己授权过哪些助手;该页面已在账户的 AI 助手分组下,这张列表就是那句承诺背后的页面。
  • 风险与回滚:只读两条已上线的服务端接口,写操作只有 API 密钥撤销(沿用数据接口原有的 revoked 写法,先确认);助手行没有撤销按钮,等 Auth: the console cannot withdraw an authorized AI assistant: /oauth2/delete-consent has no ledger row or client method, and no admin door #31048。回滚即还原本 PR,无数据迁移。
  • 先看:ui/packages/app-shell/src/console/connect/AuthorizedAccessList.tsx(按本人 user_id 过滤密钥,平台管理员不过滤会看到所有人的密钥)、ConnectAgentWidget.tsx(只挂载列表并在创建密钥后通知刷新)、ui/packages/i18n/src/locales/en.ts(新增 connectAgent.access.*)。
  • 两分钟验证:pnpm dev 后以任意用户登录,打开 账户 → 连接 AI 助手,页面底部应出现该卡片;创建一个 API 密钥后它立即出现在列表里,点「撤销」确认后显示「已撤销」。
  • 需要你做的:无,席位落地。

What changes

  • New 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).
    • Assistants from 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 existing oauth.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. A truncated answer says only the newest are shown.
    • API keys: the adapter's find('sys_api_key') filtered to user_id = the signed-in person and selecting only id, 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 writes revoked: true through 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 calls notifyDataChanged({ objectName: 'sys_api_key' }) (the mint posts to /api/v1/keys outside the adapter) so the new key shows without a reload. No existing line or string is edited.
  • Language packs: 23 new connectAgent.access.* keys in all eleven packs; zh-Hant regenerated from zh-Hans with gen:zh-hant.
  • Changeset .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 writes revoked: true and 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, Cursor lastUsedAt: null, Claude Desktop lastUsedAt = 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_key via 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 stamps last_used_at; PATCH {revoked: true} answers 200 and the key is then refused (403).
  • No server field the list needs is missing.
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 (both tsc programs); @object-ui/i18n type-check exit 0 (on d06a9fc, packs unchanged since).
  • eslint on the two new files: exit 0.
  • node scripts/pm/dispatch-gates.mjs --commands derived 38 commands; all exit 0 (--ran reconciliation: 38 derived, 38 run, 0 NOT-MEASURED, 0 UNRUN), including node scripts/check-sdui-manifest.mjs and pnpm --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-alias exit 0.
  • pnpm check:new-line-citations (ui): 0 new citations.
  • NOT MEASURED locally: the i18n-locale-en eager-closure ceiling (needs the Console production build; CI's Build & E2E UI step check:eager-closure is the verdict). The ceiling is not changed.

Acceptance notes

Human Minimum

  • 1. Schemas start as Zod; packages/spec holds no runtime logic. (No spec change.)
  • 2. Every name I added uses the casing for its kind.
  • 3. No formatter ran; eslint on the new files passes (the repo-wide pnpm lint is CI's).
  • 4. One issue, one branch off an up-to-date main, one pull request.
  • 5. A change that publishes carries a changeset, and both declaration lines are filled in here and in the changeset.
  • 6. packages/spec touched: n/a.
  • 7. No hand edits to content/docs/references/, packages/*/CHANGELOG.md or content/docs/releases/.
  • 8. Title, body and comments are in English.

🤖 Generated with Claude Code

https://claude.ai/code/session_01L9qibz28PqZhuQmVQEvmY2


Generated by Claude Code

@github-actions github-actions Bot added size/l documentation Improvements or additions to documentation tooling Fix lands in tooling, not a product package — enters pm:queue only with Unblocks: or a named surface labels Nov 6, 2026
@objectstack-fleet

Copy link
Copy Markdown
Contributor Author

domain:ui#3 · os-tesla · session_01L9qibxW6qraU2Z3mwupUGp

Contract review

Served-tier: TIER_DEFAULT
Head-sha: e903d944683a378db4be43d579e14e32ad7834a3
Local-runs: none

① Derived judgments

② Semver level

@object-ui/app-shell patch and @object-ui/i18n patch, Clause-②: yes (widening), Metadata-protocol: none: additive keys and an additive console card, no stored shape or protocol change. Correct.

③ Boundary flags

None. No server field, no packages/**, no existing connectAgent.* key edited (seat 2's serial ask for #30898, 6695602143, is kept).

④ Design judgment

The 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: claude/project-thread-oqumno
Reviewed-by: session_01L9qibxW6qraU2Z3mwupUGp

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
@github-actions github-actions Bot added the tests label Nov 6, 2026
@objectstack-fleet

Copy link
Copy Markdown
Contributor Author

domain:ui#3 · os-tesla · session_01L9qibxW6qraU2Z3mwupUGp

维护者速读

席位核对 — ACCEPT · head 092e2d9

  • 形态:17 个文件,+991/−3。check-governed-merges --pr 31054:NOT governed,994 行 ≤ 3000。Part of #30894,目标 main。与认领 6695600228 及修订 6695631915(分支与 Connect an Agent never says what an assistant can do once connected, or whether the connection worked #30898 串行约定)一致:列表在独立组件文件里,小部件只加挂载和一次刷新通知,未改任何已有文案键。本 PR 由本席派出的卡线程实现,席位不写码。
  • 契约:Clause-②: yes (widening)(23 个新键),Metadata-protocol: none,app-shell + i18n patch。TIER_DEFAULT 复核 6697235650 在 e903d94 上 PASS;之后的增量(e903d944 → 092e2d9)是修 CI 的三处:de 引号配对计数 107 → 108(新增 revokeTitle),truncated 文案去掉 {{count}}(count-families 规则),plugin-form 写入点普查登记撤销密钥为 outOfFamily(写本人的行)。席位逐行读过,契约判断不变。
  • 席位自己核对:读组件全文:助手列表只调 oauth.authorizedClients.list(),不带参数;密钥读取带 user_id 过滤和显式字段列表,不取密钥本身;撤销只对有效密钥显示,先确认,写 revoked: true。
  • 前提实测:dev 报告 6697208427(status done,前提成立),现场读取记录 ui-seat-3/r2026-11-06/30894/reading-115a43ad.md。修复后 i18n、普查与 connect 测试 101 文件 / 1,722 条通过,app-shell + i18n type-check 绿;e903d944 上的 eager-closure 体积检查已绿,未提高上限,派生门禁 38/38 exit 0。
  • 门禁 check-runs:本 head 每个检查最新一次 44 success、11 skipped,0 红;mergeable_state clean。
  • 落地条款:维护者常设令 「你应该是使用 pm-dispatch 技能自主审核和落地 pr」(2026-11-06T14:09Z)。中继 pr_ready + automerge_enable。Part of:合并后 The OAuth consent page promises you can revoke access from your account settings, but no page lists the AI assistants you authorized #30894 转回 pm:blocked(正文加 Blocked-by: #31048),清 assignee,第二部分(助手撤回)等 Auth: the console cannot withdraw an authorized AI assistant: /oauth2/delete-consent has no ledger row or client method, and no admin door #31048 落地再认领。

@objectstack-fleet
objectstack-fleet Bot marked this pull request as ready for review November 6, 2026 22:54
@objectstack-fleet
objectstack-fleet Bot added this pull request to the merge queue Nov 6, 2026
Merged via the queue into main with commit 78d4fe9 Nov 6, 2026
56 checks passed
@objectstack-fleet
objectstack-fleet Bot deleted the claude/project-thread-oqumno branch November 6, 2026 23:12
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

documentation Improvements or additions to documentation size/l tests tooling Fix lands in tooling, not a product package — enters pm:queue only with Unblocks: or a named surface

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant