Repository navigation
Commit a0b40f2
feat(console): Home offers Connect an AI Assistant until the person has connected one; the first-run checklist gains its row (#31236)
<!-- claude-projects-attribution: {"github_login":"os-project-manager"}
-->
_Requested by **Leehom** · [project
thread](https://claude.ai/code/project/chan_01TenzsRopZj4jMbvjX8uz1X?thread=cmsg_01TenzsRopZj4jMbvjX8uz1XUajPDb8jMKTWyncZpJ2wTS)_
Fixes #30895
Clause-②: yes (widening)
Metadata-protocol: none
## 维护者速读
- 改了什么:首页新增一张可关闭的「在 AI 助手里使用本系统」卡片,一句大白话加一个按钮直达「连接 AI 助手」;本人连接过助手或持有有效
API 密钥后自动不再显示,关闭后在本浏览器对本人不再显示。管理员的首次设置清单新增「连接 AI
助手」一行,完成状态读真实记录(已授权的助手或有效 API 密钥),不靠点击。
- 为什么改:卡 #30895 期望 2 与期望 3(期望 1、4、5 已由 PR #30935 落地);两者都要知道「此人是否已连接」,复用
PR #31198 的同一份读取。
- 风险与回滚:只读两份已有记录,不新增服务端接口;读不到时卡片不出现、清单行不出现(不误报「未连接」);MCP
关闭的部署两者都不出现。回滚即还原本 PR,无数据迁移。
-
先看:ui/packages/app-shell/src/console/home/ConnectAssistantCard.tsx(何时出现)、ui/apps/console/src/pages/settings/FirstRunChecklist.tsx
的 connect_assistant
行、ui/packages/app-shell/src/console/connect/connectAssistantPath.ts(从头像菜单抽出的同一条路径规则)。
- 两分钟验证:pnpm dev 后用一个从未连接过的账号打开首页,看到卡片并点按钮进入「连接 AI 助手」;建一把 API
密钥后回首页,卡片消失,管理员清单该行变为已完成。
- 需要你做的:无,席位落地。
## What changes
- **Home card** (`console/home/ConnectAssistantCard.tsx`, mounted in
`HomePage.tsx` under the recovery-password reminder, in both the empty
and the full Home). For every signed-in person: "Work here from your AI
assistant", one sentence that they can look up and change their records
from Claude, Doubao and similar assistants, always as themselves, and
one button, Connect an AI Assistant, which opens the page. It is drawn
only when all of these hold:
- the served Account app carries the page (MCP turned on);
- the person has not closed it on this browser;
- `useConnectedAccess` answered that they have connected nothing: no
authorized assistant and no API key that still works.
While the read is out, or when it fails, the card stays away rather than
offering a connection the person may already have. A dismissed card
reads nothing.
- **Dismissal** (`connectAssistantCardGate.ts`). Measured before adding
anything: Home keeps no server-side preference; its two existing
dismissible items (the recovery-password reminder and the first-run
checklist) remember a dismissal in the browser's `localStorage`. The
card does the same, keyed by the person
(`os:home:connect-assistant-dismissed:USER`), so one person's dismissal
on a shared browser does not hide it for the next.
- **First-run checklist row** (`FirstRunChecklist.tsx`, row 7,
`connect_assistant`). "Connect an AI assistant": done once this
administrator has authorized an assistant or holds an API key that still
works, read from those records, never from a click; links to the page
("Connect" / "View"). Left out, like the other rows, when its state
cannot be read, and left out where MCP is turned off. The read starts
only once the checklist itself is going to draw, so a member's Home asks
nothing for it.
- **One read, reused** (`connectedAccess.ts`). Both use
`useConnectedAccess` from PR #31198; no second copy. It gains `{ enabled
}` so a caller that will not draw (a dismissed card, a member's
checklist) costs no request, and an adapter without the auth client is
now a failed read instead of a throw out of the effect.
- **One path rule** (`connectAssistantPath.ts`, new). The avatar menu's
"does the Account app carry the page, and what is its address" logic
moves out of `AppHeader.tsx` unchanged, and the avatar item, the Home
card and the checklist row all read it.
- `@object-ui/app-shell` now exports `useConnectedAccess` (with its
types) and `connectAssistantPath`, for the console's checklist.
- **Language packs**, all eleven: `home.connectAssistant.title`, `body`,
`action`, `dismiss` and
`console.firstRunChecklist.connectAssistant.title`, `todo`, `done`,
`action` added; nothing reworded or removed. `zh-Hant` regenerated with
`gen:zh-hant`.
- Changeset `.changeset/30895-home-connect-assistant.md`
(`@object-ui/app-shell`, `@object-ui/i18n`, `@object-ui/console`:
patch).
## Why
The card's Expected 2 ("Home shows a dismissible card … hidden once
dismissed or once the person has connected one") and Expected 3 ("the
administrator's first-run checklist on Home gains a row for it. Its done
state is read from a real record … never from a click"). Expected 1, 4
and 5 landed in PR #30935; 2 and 3 waited for the "has connected" read,
which PR #31198 built.
## Tests
- `ConnectAssistantCard-30895.test.tsx`, 9 cases, real router, real data
bus and the real hook (only the adapter, the person and the served apps
are stubbed): §1 nothing connected (only a revoked and an expired key)
shows the card, and clicking its button lands on the Connect route; it
reads only the person's own keys; §2 an authorized assistant, or a key
that still works: no card; §3 while the read is out, or when it fails:
no card; §4 the close button hides it, a later mount stays hidden and
reads nothing, another person on the same browser still sees it; §5 no
page in the Account app: no card and no read; §6 a key minted while it
is shown (the data bus) takes it away.
- `FirstRunChecklist.test.tsx`, 7 new cases on the real settings client:
not done with nothing connected (link to
`/apps/account/page/connect_agent`, "0 of 7 done"); done from an
authorized assistant; done from a working API key; clicking the row's
link writes nothing and leaves it not done; a failed read leaves the row
out; MCP off leaves the row out and reads nothing; a member reads
nothing of it.
- Ablations (one-off, `scripts/ablation-replace.mjs`, restored blob
equal to HEAD each time): showing the card while connected (`||
access.connected` removed) turned three cases red (3 failed, 6 passed);
ignoring the stored dismissal turned §4 red (1 failed, 8 passed); a
checklist row that is never done (`const done = false`) turned two cases
red (2 failed, 15 passed).
## Acceptance notes
- **Both the card and the row for an administrator.** The card is for
everyone and the row is the administrator's checklist, so an
administrator who has connected nothing sees both. On their Home each
reads the two records once (two reads more than a member).
- **An API key counts as connected**, as the card says ("an
authorization or an API key the person holds"); a revoked or expired key
does not.
- **File surface**: `layout/AppHeader.tsx` and `index.ts` are outside
the claim's listed files. AppHeader only gives up its copy of the path
rule to `connectAssistantPath.ts` (21 lines out, 4 in, behaviour
unchanged, its tests pass); `index.ts` adds the two exports the console
needs.
- `Clause-②: yes (widening)`: eight new catalogue keys per published
`@object-ui/i18n` pack, and two new exports from `@object-ui/app-shell`
(a private package); nothing removed.
## Verification
<details><summary>Live, showcase on this branch (Vite console against
it), admin and member, en and zh</summary>
- Administrator, en, nothing connected: Home shows the card ("Work here
from your AI assistant … Connect an AI Assistant") and the checklist row
"Connect an AI assistant" not done ("2 of 7 done"); the card's button
opened `/apps/com.objectstack.account/page/connect_agent`.
- Administrator after minting an API key (`POST /api/v1/keys`): no card;
the row is done ("An AI assistant or API key of yours is connected.",
"View"), "3 of 7 done". In zh the same row reads 「连接 AI 助手 / 你已连接了 AI
助手或 API 密钥。/ 查看」, 「已完成 3/7」.
- Member (created with `admin/create-user`, password changed), zh,
nothing connected: the card 「在 AI 助手里使用本系统 … 连接 AI 助手」, no checklist.
Closing it removed it; after a reload it stayed hidden.
- The server was the showcase built from the earlier base of this
thread, with this branch's console; the change is console-only.
</details>
<details><summary>Local tests and gates</summary>
- `pnpm --dir ui exec vitest run packages/app-shell/src/console/home/
packages/app-shell/src/console/connect/ packages/app-shell/src/layout/
apps/console/src/pages/settings/ packages/i18n/` on head 68ff0b2: 318
files passed, 3263 tests passed, 13 skipped.
- `type-check` for `@object-ui/app-shell`, `@object-ui/i18n` and
`@object-ui/console`: exit 0 (after building the console's dependency
closure). eslint on the touched files: 0 errors; the new files carry no
warnings.
- `node scripts/pm/dispatch-gates.mjs --commands` derived 38 families on
head 68ff0b2; all 38 exit 0, `--ran` reconciliation: 38 derived, 38
run, 0 NOT-MEASURED, 0 UNRUN (includes `pnpm --dir ui exec vitest run
scripts/` and `node scripts/check-sdui-manifest.mjs`).
</details>
## Human Minimum
- [x] 1. Schemas start as Zod; `packages/spec` holds no runtime logic.
(No spec change.)
- [x] 2. Every name I added uses the casing for its kind.
- [x] 3. No formatter ran; eslint on the touched files passes (the
repo-wide `pnpm lint` is CI's).
- [x] 4. One issue, one branch off an up-to-date `main`, one pull
request.
- [x] 5. A change that publishes carries a changeset, and both
declaration lines are filled in here and in the changeset.
- [x] 6. `packages/spec` touched: n/a.
- [x] 7. No hand edits to `content/docs/references/`,
`packages/*/CHANGELOG.md` or `content/docs/releases/`.
- [x] 8. Title, body and comments are in English.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
https://claude.ai/code/session_01FEkQ5pMhSEyeAKtt6btsBe
---
_Generated by [Claude
Code](https://claude.ai/code/session_01FEkQ5pMhSEyeAKtt6btsBe)_
---------
Co-authored-by: Claude <noreply@anthropic.com>1 parent fba4c00 commit a0b40f2
22 files changed
Lines changed: 694 additions & 29 deletions
File tree
- .changeset
- ui
- apps/console/src/pages/settings
- __tests__
- packages
- app-shell/src
- console
- connect
- home
- __tests__
- layout
- i18n/src/locales
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
| 1 | + | |
| 2 | + | |
| 3 | + | |
| 4 | + | |
| 5 | + | |
| 6 | + | |
| 7 | + | |
| 8 | + | |
| 9 | + | |
| 10 | + | |
| 11 | + | |
| 12 | + | |
| 13 | + | |
| 14 | + | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
33 | 33 | | |
34 | 34 | | |
35 | 35 | | |
| 36 | + | |
| 37 | + | |
| 38 | + | |
| 39 | + | |
| 40 | + | |
36 | 41 | | |
37 | 42 | | |
38 | 43 | | |
| |||
63 | 68 | | |
64 | 69 | | |
65 | 70 | | |
66 | | - | |
| 71 | + | |
67 | 72 | | |
68 | 73 | | |
69 | 74 | | |
| |||
403 | 408 | | |
404 | 409 | | |
405 | 410 | | |
| 411 | + | |
| 412 | + | |
| 413 | + | |
| 414 | + | |
406 | 415 | | |
407 | 416 | | |
408 | 417 | | |
| |||
546 | 555 | | |
547 | 556 | | |
548 | 557 | | |
| 558 | + | |
| 559 | + | |
| 560 | + | |
| 561 | + | |
| 562 | + | |
| 563 | + | |
| 564 | + | |
| 565 | + | |
| 566 | + | |
| 567 | + | |
| 568 | + | |
| 569 | + | |
| 570 | + | |
| 571 | + | |
| 572 | + | |
| 573 | + | |
| 574 | + | |
| 575 | + | |
| 576 | + | |
| 577 | + | |
| 578 | + | |
| 579 | + | |
| 580 | + | |
| 581 | + | |
| 582 | + | |
| 583 | + | |
| 584 | + | |
| 585 | + | |
549 | 586 | | |
550 | 587 | | |
551 | 588 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
27 | 27 | | |
28 | 28 | | |
29 | 29 | | |
30 | | - | |
| 30 | + | |
| 31 | + | |
| 32 | + | |
| 33 | + | |
31 | 34 | | |
32 | 35 | | |
33 | 36 | | |
| |||
47 | 50 | | |
48 | 51 | | |
49 | 52 | | |
50 | | - | |
| 53 | + | |
51 | 54 | | |
52 | 55 | | |
53 | 56 | | |
| 57 | + | |
| 58 | + | |
| 59 | + | |
| 60 | + | |
| 61 | + | |
54 | 62 | | |
55 | 63 | | |
56 | 64 | | |
| |||
221 | 229 | | |
222 | 230 | | |
223 | 231 | | |
| 232 | + | |
| 233 | + | |
224 | 234 | | |
225 | 235 | | |
226 | 236 | | |
| |||
411 | 421 | | |
412 | 422 | | |
413 | 423 | | |
| 424 | + | |
| 425 | + | |
| 426 | + | |
| 427 | + | |
| 428 | + | |
| 429 | + | |
| 430 | + | |
| 431 | + | |
| 432 | + | |
| 433 | + | |
| 434 | + | |
| 435 | + | |
| 436 | + | |
| 437 | + | |
| 438 | + | |
| 439 | + | |
| 440 | + | |
| 441 | + | |
| 442 | + | |
| 443 | + | |
| 444 | + | |
| 445 | + | |
| 446 | + | |
| 447 | + | |
| 448 | + | |
| 449 | + | |
| 450 | + | |
| 451 | + | |
| 452 | + | |
| 453 | + | |
| 454 | + | |
| 455 | + | |
| 456 | + | |
| 457 | + | |
| 458 | + | |
| 459 | + | |
| 460 | + | |
| 461 | + | |
| 462 | + | |
| 463 | + | |
| 464 | + | |
| 465 | + | |
| 466 | + | |
| 467 | + | |
| 468 | + | |
| 469 | + | |
| 470 | + | |
| 471 | + | |
| 472 | + | |
| 473 | + | |
| 474 | + | |
| 475 | + | |
| 476 | + | |
| 477 | + | |
| 478 | + | |
| 479 | + | |
| 480 | + | |
| 481 | + | |
| 482 | + | |
| 483 | + | |
| 484 | + | |
| 485 | + | |
| 486 | + | |
| 487 | + | |
| 488 | + | |
| 489 | + | |
| 490 | + | |
| 491 | + | |
| 492 | + | |
| 493 | + | |
| 494 | + | |
| 495 | + | |
| 496 | + | |
| 497 | + | |
| 498 | + | |
| 499 | + | |
| 500 | + | |
| 501 | + | |
| 502 | + | |
| 503 | + | |
| 504 | + | |
| 505 | + | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
| 1 | + | |
| 2 | + | |
| 3 | + | |
| 4 | + | |
| 5 | + | |
| 6 | + | |
| 7 | + | |
| 8 | + | |
| 9 | + | |
| 10 | + | |
| 11 | + | |
| 12 | + | |
| 13 | + | |
| 14 | + | |
| 15 | + | |
| 16 | + | |
| 17 | + | |
| 18 | + | |
| 19 | + | |
| 20 | + | |
| 21 | + | |
| 22 | + | |
| 23 | + | |
| 24 | + | |
| 25 | + | |
| 26 | + | |
| 27 | + | |
| 28 | + | |
| 29 | + | |
| 30 | + | |
| 31 | + | |
| 32 | + | |
| 33 | + | |
| 34 | + | |
| 35 | + | |
| 36 | + | |
| 37 | + | |
| 38 | + | |
| 39 | + | |
| 40 | + | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
26 | 26 | | |
27 | 27 | | |
28 | 28 | | |
| 29 | + | |
| 30 | + | |
| 31 | + | |
| 32 | + | |
29 | 33 | | |
30 | 34 | | |
31 | 35 | | |
| |||
111 | 115 | | |
112 | 116 | | |
113 | 117 | | |
114 | | - | |
| 118 | + | |
| 119 | + | |
| 120 | + | |
| 121 | + | |
| 122 | + | |
| 123 | + | |
| 124 | + | |
115 | 125 | | |
116 | 126 | | |
117 | | - | |
| 127 | + | |
118 | 128 | | |
119 | 129 | | |
120 | 130 | | |
| |||
124 | 134 | | |
125 | 135 | | |
126 | 136 | | |
127 | | - | |
128 | | - | |
129 | | - | |
| 137 | + | |
| 138 | + | |
| 139 | + | |
| 140 | + | |
130 | 141 | | |
131 | 142 | | |
132 | 143 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
| 1 | + | |
| 2 | + | |
| 3 | + | |
| 4 | + | |
| 5 | + | |
| 6 | + | |
| 7 | + | |
| 8 | + | |
| 9 | + | |
| 10 | + | |
| 11 | + | |
| 12 | + | |
| 13 | + | |
| 14 | + | |
| 15 | + | |
| 16 | + | |
| 17 | + | |
| 18 | + | |
| 19 | + | |
| 20 | + | |
| 21 | + | |
| 22 | + | |
| 23 | + | |
| 24 | + | |
| 25 | + | |
| 26 | + | |
| 27 | + | |
| 28 | + | |
| 29 | + | |
| 30 | + | |
| 31 | + | |
| 32 | + | |
| 33 | + | |
| 34 | + | |
| 35 | + | |
| 36 | + | |
| 37 | + | |
| 38 | + | |
| 39 | + | |
| 40 | + | |
| 41 | + | |
| 42 | + | |
| 43 | + | |
| 44 | + | |
| 45 | + | |
| 46 | + | |
| 47 | + | |
| 48 | + | |
| 49 | + | |
| 50 | + | |
| 51 | + | |
| 52 | + | |
| 53 | + | |
| 54 | + | |
| 55 | + | |
| 56 | + | |
| 57 | + | |
| 58 | + | |
| 59 | + | |
| 60 | + | |
| 61 | + | |
| 62 | + | |
| 63 | + | |
| 64 | + | |
| 65 | + | |
| 66 | + | |
| 67 | + | |
| 68 | + | |
| 69 | + | |
| 70 | + | |
| 71 | + | |
| 72 | + | |
| 73 | + | |
| 74 | + | |
| 75 | + | |
| 76 | + | |
| 77 | + | |
| 78 | + | |
| 79 | + | |
| 80 | + | |
| 81 | + | |
| 82 | + | |
| 83 | + | |
| 84 | + | |
0 commit comments