Tracking issue for bringing the Maple Agent app (apps/maple-agent, formerly benthecarman/maple-gpui) to feature parity with the web app's account, billing, and settings features.
Done (merged as benthecarman/maple-gpui #55–#60, now in apps/maple-agent)
- Settings → Account: email with verified badge and resend, sign-in method, member since, sign out (sends
POST /logout; the backend does not revoke the refresh token yet).
- Change password for email and anonymous accounts, with the rotated token pair persisted.
- Delete account: type DELETE, emailed code plus client secret, runtime stopped before the irreversible call.
- Billing: full subscription status, Stripe portal, Stripe checkout for individual plans, credit balance, plans on sale.
- API keys: list, create (shown once), delete; gated on Pro/Max/Team.
- CLI:
maple-gpui login [--email] so acp works without the desktop.
Remaining
1. Deep links and OAuth loopback redirect
OAuth still asks the user to paste the redirect URL back; billing return URLs point at trymaple.ai, as in the Tauri build; reset and pass-redeem email links land in a browser. Decide custom scheme (maple://) versus loopback listener (or both); register per platform (CFBundleURLTypes, .desktop x-scheme-handler, Windows registry); single-instance handoff for a URL arriving while the app runs; route to OAuth completion, billing refresh, reset, redeem; validate state end to end. Test on all three OSes from a bundle and a dev binary. Most of the items below lean on this.
2. Plan gating and quota exhaustion
The web gates images, documents, voice, and models by tier and handles "out of messages" with an upgrade dialog. The agent only has an image gate by plan-label string match. Move gating onto PlanTier, find out what the backend returns when the quota is exhausted mid-task (429 from inference, can_chat: false, or both), and design the recovery: pause the task, offer Upgrade / Buy credits, resume. Needs an exhausted fixture, probably staging.
3. Auth coherence across desktop, acp, and CLI login
auth.json is written by three processes. A password change rotates the token pair, so a running acp for the same account starts failing without explanation. login can switch the account under a running desktop, whose rotation sink may write its own account back. Decide the ownership model (last writer wins, or react to a replaced file, e.g. watch it with notify), and give acp a clear "sign-in changed" message. Validate: desktop + acp during a password change; CLI login as another account while the desktop runs.
4. Delete account: cleanup parity and end-to-end confirm
The web wipes agent data, the proxy key, and workspace preferences before the irreversible call. The agent stops the runtime and drops credentials but leaves the account-scoped local directories (agent/accounts/<scope>/, the ACP config). Decide whether deletion wipes them and whether to stop a running proxy/acp. The confirm step has not been exercised end to end (the local stack has no Resend key).
5. Login screen: signup, guest accounts, forgot password
Backend calls are simple; the rules are not. Guest: warning dialog (Bitcoin-only, no support, back up credentials), show-the-id-once, blocked on Free, no guest → email conversion exists. Password reset destroys encrypted data (hardening warning) and needs the two-step code + client secret flow. Email verification: decide whether the desktop enforces the web's blocking modal and gates, and whether to add an enter-the-code UI. Decide what name signup sends (the web sends "ANON").
6. Billing completeness
Team seat checkout (2–100 seats as quantity), Bitcoin via Zaprite (guests are Bitcoin-only), API credit purchases with the web's rules (10k–1M credits, multiples of 1k, $1 = 1k), pending_plan_change display, auto-refresh after returning from checkout (needs 1), discount/promo, subscription-pass redemption. Local fixtures are not Stripe products, so this needs Stripe test mode on staging and a Zaprite sandbox.
7. Team management and seat-mismatch gating
Client for /v1/maple/team/*; a Team section shown on team plans with admin and member views (create, rename, members, invites, revoke, remove, leave, accept). Seat mismatch means usage is paused on the web: the agent must gate itself the same way, not just show a badge. Invite acceptance from an email link needs 1. Needs a team fixture on staging.
Suggested order
1 and 3 first: deep links unlock 4–7's return and link flows, and the auth coherence gap is the one most likely to bite someone using the CLI and desktop together.
Out of scope here: the backend POST /logout TODO (long-standing, tracked separately).
Tracking issue for bringing the Maple Agent app (
apps/maple-agent, formerly benthecarman/maple-gpui) to feature parity with the web app's account, billing, and settings features.Done (merged as benthecarman/maple-gpui #55–#60, now in
apps/maple-agent)POST /logout; the backend does not revoke the refresh token yet).maple-gpui login [--email]soacpworks without the desktop.Remaining
1. Deep links and OAuth loopback redirect
OAuth still asks the user to paste the redirect URL back; billing return URLs point at trymaple.ai, as in the Tauri build; reset and pass-redeem email links land in a browser. Decide custom scheme (
maple://) versus loopback listener (or both); register per platform (CFBundleURLTypes,.desktopx-scheme-handler, Windows registry); single-instance handoff for a URL arriving while the app runs; route to OAuth completion, billing refresh, reset, redeem; validatestateend to end. Test on all three OSes from a bundle and a dev binary. Most of the items below lean on this.2. Plan gating and quota exhaustion
The web gates images, documents, voice, and models by tier and handles "out of messages" with an upgrade dialog. The agent only has an image gate by plan-label string match. Move gating onto
PlanTier, find out what the backend returns when the quota is exhausted mid-task (429 from inference,can_chat: false, or both), and design the recovery: pause the task, offer Upgrade / Buy credits, resume. Needs an exhausted fixture, probably staging.3. Auth coherence across desktop,
acp, and CLI loginauth.jsonis written by three processes. A password change rotates the token pair, so a runningacpfor the same account starts failing without explanation.logincan switch the account under a running desktop, whose rotation sink may write its own account back. Decide the ownership model (last writer wins, or react to a replaced file, e.g. watch it withnotify), and giveacpa clear "sign-in changed" message. Validate: desktop + acp during a password change; CLI login as another account while the desktop runs.4. Delete account: cleanup parity and end-to-end confirm
The web wipes agent data, the proxy key, and workspace preferences before the irreversible call. The agent stops the runtime and drops credentials but leaves the account-scoped local directories (
agent/accounts/<scope>/, the ACP config). Decide whether deletion wipes them and whether to stop a running proxy/acp. The confirm step has not been exercised end to end (the local stack has no Resend key).5. Login screen: signup, guest accounts, forgot password
Backend calls are simple; the rules are not. Guest: warning dialog (Bitcoin-only, no support, back up credentials), show-the-id-once, blocked on Free, no guest → email conversion exists. Password reset destroys encrypted data (hardening warning) and needs the two-step code + client secret flow. Email verification: decide whether the desktop enforces the web's blocking modal and gates, and whether to add an enter-the-code UI. Decide what
namesignup sends (the web sends "ANON").6. Billing completeness
Team seat checkout (2–100 seats as
quantity), Bitcoin via Zaprite (guests are Bitcoin-only), API credit purchases with the web's rules (10k–1M credits, multiples of 1k, $1 = 1k),pending_plan_changedisplay, auto-refresh after returning from checkout (needs 1), discount/promo, subscription-pass redemption. Local fixtures are not Stripe products, so this needs Stripe test mode on staging and a Zaprite sandbox.7. Team management and seat-mismatch gating
Client for
/v1/maple/team/*; a Team section shown on team plans with admin and member views (create, rename, members, invites, revoke, remove, leave, accept). Seat mismatch means usage is paused on the web: the agent must gate itself the same way, not just show a badge. Invite acceptance from an email link needs 1. Needs a team fixture on staging.Suggested order
1 and 3 first: deep links unlock 4–7's return and link flows, and the auth coherence gap is the one most likely to bite someone using the CLI and desktop together.
Out of scope here: the backend
POST /logoutTODO (long-standing, tracked separately).