Skip to content

Promote develop to main: iOS channel management, desktop connector fixes, client docs#268

Merged
haowei2000 merged 11 commits into
mainfrom
develop
Jul 19, 2026
Merged

Promote develop to main: iOS channel management, desktop connector fixes, client docs#268
haowei2000 merged 11 commits into
mainfrom
develop

Conversation

@haowei2000

Copy link
Copy Markdown
Collaborator

Routine developmain promotion. 8 commits since the last promotion (#264), all already reviewed and merged into develop.

What's in it

iOS (#263 work, fd5b7bf7)

  • Unstick touch input and tame auto-scroll in the chat view.
  • Finish channel management: members sheet, channel settings sheet, viewboards.

Bots (e2d99bac)

  • Creating a bot now works the same way on web, macOS and iOS — shared onboarding flow instead of three divergent paths.

Desktop / connector (#265, #266)

  • 3fcbcbe5 — create workspace dirs during onboarding so setup is zero-prep.
  • 02a5fe17 — resolve the opencode adapter before minting a token.
  • 27872bfb — carry --config on every connector restart (previously dropped on restart).
  • 4209abdf — collapse "Open in …" into a split button with app tiles.

Docs (#267, a03a3a43)

  • New #clients section on the landing page covering Web / macOS / iOS / Android, plus prominent download and web-app entry points in the README and the hero CTA row.
  • CLAUDE.md rewritten as a standalone contributor guide.

Note

Merging this triggers pages.yml, which republishes https://eleperson.github.io/Cheers/ — the landing-page changes from #267 go live at that point.

No migrations in this range. main has no commits that aren't in develop beyond prior promotion merges, so this is a clean fast-forward-shaped merge.

🤖 Generated with Claude Code

haowei2000 and others added 11 commits July 18, 2026 18:00
A connector created from the desktop New-connector flow failed to start:
the gateway's config points policy.workspace.allowed_roots (and default_cwd)
at ~/.cheers/workspace/<bot>, and the connector validates those dirs exist at
startup (resolve_existing_dirs) — but nothing created them, so the daemon
exited with 'does not exist (os error 2)'. Creating a connector from the
desktop should need no manual prep.

connector_write_onboarded now creates the referenced workspace dirs after
writing the config. Best-effort and scoped to the user's home dir so a
gateway-supplied config can't make us mkdir arbitrary system paths. Adds unit
tests for the pure home-scoping collector.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
fix(desktop): zero-prep connector onboarding (create workspace dirs)
Onboarding a bot as "opencode" stranded it on a token nobody held.

The gateway preset named `opencode-acp`, a binary that does not exist —
OpenCode exposes ACP as a subcommand of its own binary (`opencode acp`).
The comment on that preset said "best-effort name; verify against your
build", and it never was.

That alone would only be a failed setup, but redeeming an enrollment code
mints a new bot token, which destructively replaces the old one and kicks
any live connector. The desktop app then failed writing the config,
because it could not resolve the adapter — after the token had already
rotated. Net effect: the gateway expects a token no host has, the
connector that used to work is offline, and the code is spent. Trying a
different agent type on an existing bot was enough to break it.

- Give AgentPreset an `args` field and render it, so opencode is
  `command = "opencode", args = ["acp"]`. It is a real resolvable command
  now, so it no longer needs the hand-edit banner.
- Drop the opencode fallback in `detect_agents`. It accepted a command
  that `absolutize_adapter_command` still refused, so detection and the
  write path disagreed about what "installed" meant — which would have
  let the new pre-flight check wave the same config straight through.

Both halves are required: fixing either alone leaves the disagreement.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Creating a bot had two implementations that shared no code, and iOS had
none at all. Underneath, the only real variable is where the connector
runs — this makes that the explicit question and routes on it.

Safety first, since these paths mint irreversible credentials:

- Carry the agent type through the "Set up on this Mac" hand-off, and read
  an existing bot's bridge_provider instead of re-defaulting. A bot created
  as codex was getting a claude connector; nothing downstream catches that,
  because the connector runs whatever adapter the config names.
- Create the bot when a mode is picked, not on Continue. Backing out of the
  wizard left an orphan bot with no way to delete it from there.
- Pre-flight the adapter before any server call, so "not installed" stays a
  side-effect-free error rather than a spent code and a rotated token.
- End the wizard on live `bridge_connected`, not a Done button that closed
  the dialog and confirmed nothing. The step was already called "Connect".

iOS can create a bot and hand it off, but never hosts a connector — a
phone cannot run a long-lived ACP child process. The steps mirror the web
wizard; the carrier changes, because a shell one-liner is useless on a
phone: the code is shown as a QR, and configs go through the share sheet.

That needs somewhere to land, so macOS gains "I have a code" — the missing
half of the remote-machine story. The desktop could previously only mint
and redeem for itself in one shot, so a code from anywhere else had
nowhere to go.

Also drops jargon from user-facing copy: "Rust ACP connector", "Agent
Bridge token", "bot_token_env", "0600", and a reachability warning that
told whoever was adding a bot to run kubectl port-forward. DESIGN.md
already banned raw field names in UI copy; this pays that down. Code
comments keep the precise terms.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…anagement

Three real-device bugs and the feature gaps behind them.

**Touch input died app-wide.** The drawer's drag offset was `@State` and only
reset in `.onEnded`, which SwiftUI never calls when a gesture is cancelled — the
system swipe-back steals an in-flight edge drag, the offset sticks non-zero, and
the drawer backdrop stays mounted over the whole screen at ~7% opacity,
swallowing every tap and scroll. `@GestureState` resets on cancellation, which
is what it exists for; `allowsHitTesting(drawerOpen)` is the belt-and-braces.

**Streaming yanked the reader back.** Every `message_stream` delta bumped one
scroll tick that the view honoured unconditionally, so reading history while a
bot typed was impossible. Split into `forceBottomTick` (the reader's own action:
sending, opening a channel) and `followBottomTick` (incoming: honoured only
while already parked at the bottom), plus a jump-to-latest button so suppressing
auto-follow can't strand anyone.

**Workspace switching was long-press only** despite rendering a chevron. A
chevron promises a dropdown, so a tap now delivers one; long-press still works.

**Audit rows carried no information** — every row read "Resolved /
allow_always". Rows now lead with what was actually approved (dug out of
`detail.tool`, never the server's hardcoded `detail.title`), who asked, and the
outcome; the rest moved to a detail sheet.

Channel management was never implemented on iOS (settings/files/workbench all
rendered the same placeholder). Adds the real Members sheet — direct invite, add
bot, invite links, role changes, removal — and Channel settings, with
`canManage` derived client-side from the member list exactly as the web does,
since no DTO carries it. Adds the four missing ViewBoard boards, which needed
the gateway's `resource_req`/`resource_res` verbs implemented on the socket
first (15s timeout, req_id correlation, `board_signal` refresh coalescing).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
The CLI's `restart` fails with "restart requires --config" when there is
no prior daemon.json — exactly the case right after onboarding or after a
crash, which is when a revive matters most. Resolve the config path (the
daemon's recorded one, else the onboarded ~/.cheers location) and pass it
on the restart/add-allowed-roots/supervisor-revive paths, and ensure the
workspace dirs exist first so a hand-picked .toml is zero-prep too.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
One icon per installed opener crowded the file header. Replace the row
with a split button: the remembered app opens on click, the chevron drops
down the full list. Move the glyphs to openerIcons.tsx as self-contained
SVG app tiles (no network, no asset files) so each opener is recognizable
instead of a generic lucide stroke.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
The Desktop Check `cargo fmt` gate failed: collapsing the tuple onto one
line in 02a5fe1 exceeded what rustfmt keeps inline. Formatting only.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
feat(bots): cross-platform bot creation + desktop connector reliability
The landing page and README only mentioned macOS and the PWA in passing —
there was no coverage of the native iOS and Android clients, and no
prominent place to click through to the web app or the .dmg.

website/ (both language mirrors):
- new #clients section ("Cheers on every screen"): Web / macOS / iOS /
  Android cards, each with what the client actually is today and where to
  get it. iOS and Android say plainly they are not in the app stores yet
  and link to their build instructions.
- hero CTA row gains a "Download for macOS" button next to the web app
  button; the meta line links through to #clients.
- the old "Desktop app & mobile PWA" feature card becomes a short overview
  that points at the new section instead of duplicating it.
- nav gains an Apps / 客户端 anchor.

README.md / README.zh-CN.md:
- new "Get Cheers" table right under the badges: browser, macOS download,
  iOS/Android, self-host.
- the buried desktop section now covers the native mobile clients too.
- download links point at /releases/latest rather than the releases list.

CLAUDE.md: rewritten as a standalone guide (repo layout, the gateway's
three faces and supporting layers, per-crate fmt gate, sqlx migration
discipline, branch flow) instead of a topic index.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
docs: surface all four clients and the download/web entry points
@haowei2000
haowei2000 merged commit 628b6a2 into main Jul 19, 2026
13 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant