Public: anyone can read this; the crew can change it.
The terminal's deploys screen lists an app's custom domains, adds, checks and removes one, as MCP `app_domains` does #7421
Check · Review · Land · Live done — Landed and live in production.
The terminal's deploys screen now lists the app's own domains for its admin, and `n` opens a domains screen that adds, checks and removes one. It calls the same actions the settings page and MCP `app_domains` use, and says the page's words. The tests listed below pass and the type check is clean. I did not open a preview to look at it.
shipped production is serving it deploy 78f82e5
The terminal's deploys screen now lists the app's own domains for its admin, and n opens a domains screen that adds, checks and removes one. It calls the same actions the settings page and MCP app_domains use, and says the page's words. The tests listed below pass and the type check is clean. I did not open a preview to look at it.
Commit 1 (no behaviour change):
- The domain sentences that lived in
src/web/pages/settings.ts(the refusal codes, the records in words, the added and removed messages) move tosrc/actions/domain_words.ts, following the pattern ofenv_words.ts. The settings page reads them from there, word for word. yesNomoves fromwaves.tstocommon.tsso both screens can use it.
Commit 2 (the feature):
- Domains line: for the app's admin, the deploys screen shows a line like
Domains: app.example.com live · shop.example.com waiting for DNS. - Domains screen (
n): each domain is one row with its state and what the last check said.
- + asks for a name and calls addAppDomain.
- c calls checkAppDomain, then shows what the check said and Still needed: <records> until the domain is verified. The selected row's footer shows the same.
- - asks remove <host>? y/n and calls removeAppDomain.
- Refusals: a bad name and a name another app already has are refused in the page's words. Each action checks admin rights again.
- Who sees it:
listAppDomainsonly answers the app's admin, so anyone it refuses sees neither the domains norn. If they reach the domains screen directly, it says "Only the app's admin manages its domains." and offers no keys. docs/APPS.mdanddocs/TOUR.mdname the new keys.
Choices to look at:
c, notk, checks a domain. The issue asked fork, butj/kalready move the selection on every list screen.- A separate screen. The domains get their own screen because the deploys list already owns
j/k. - Non-admins see no list. The issue says people who cannot admin should see the list without the keys, but the action refuses them the list itself. I kept the action's gate rather than widening it.
Tests: a new case in test/tui-deploys.test.ts drives the screen with keystrokes, using a stubbed DNS resolver. It covers a bad name refused, an add, the domains line, a taken name refused on a second app, a check showing the record still needed, a non-admin refused, and - then n keeping the domain while - then y removes it. I ran tsc, design-lint, mcp-size, the tui-deploys, waves, settings and domains test files, and the other tui and words test files they share code with. The full suite was not run here; the forge's check runs it on this push.
To try it: ssh into the app's terminal as the app's admin, open Deploys, press n, then +.
🤖 Generated with Claude Code
Approved. The domain words move to one module, the terminal calls the same actions as the page and MCP, every key is gated again by the action, and the new test covers add, bad name, taken name, check, reader refusal and y/n remove. Two things I would do next: (1) src/tui/screens/deploys.ts has a toast line in remove over 120 characters, and settings.ts has oddly wrapped domainRefusalText(...) calls, so run the formatter on both. (2) The issue asked non-admins to see the list without the keys; the builder kept the action gate and said so, so file a follow-up if that read-only view is wanted. I did not drive the preview: this change only refactors words on the web side, and the terminal is not reachable from curl.
The terminal's deploys screen now lists the app's own domains for its admin, and
nopens a domains screen that adds, checks and removes one. It calls the same actions the settings page and MCPapp_domainsuse, and says the page's words. The tests listed below pass and the type check is clean. I did not open a preview to look at it.Commit 1 (no behaviour change):
src/web/pages/settings.ts(the refusal codes, the records in words, the added and removed messages) move tosrc/actions/domain_words.ts, following the pattern ofenv_words.ts. The settings page reads them from there, word for word.yesNomoves fromwaves.tstocommon.tsso both screens can use it.Commit 2 (the feature):
Domains: app.example.com live · shop.example.com waiting for DNS.n): each domain is one row with its state and what the last check said.-
+asks for a name and callsaddAppDomain. -ccallscheckAppDomain, then shows what the check said andStill needed: <records>until the domain is verified. The selected row's footer shows the same. --asksremove <host>? y/nand callsremoveAppDomain.listAppDomainsonly answers the app's admin, so anyone it refuses sees neither the domains norn. If they reach the domains screen directly, it says "Only the app's admin manages its domains." and offers no keys.docs/APPS.mdanddocs/TOUR.mdname the new keys.Choices to look at:
c, notk, checks a domain. The issue asked fork, butj/kalready move the selection on every list screen.j/k.Tests: a new case in
test/tui-deploys.test.tsdrives the screen with keystrokes, using a stubbed DNS resolver. It covers a bad name refused, an add, the domains line, a taken name refused on a second app, a check showing the record still needed, a non-admin refused, and-thennkeeping the domain while-thenyremoves it. I rantsc,design-lint,mcp-size, the tui-deploys, waves, settings and domains test files, and the other tui and words test files they share code with. The full suite was not run here; the forge's check runs it on this push.To try it: ssh into the app's terminal as the app's admin, open Deploys, press
n, then+.🤖 Generated with Claude Code
Closes #7322