Make Cobble a local install: setup flow, bots-first admin, settings

Local, password-only auth:
- No public pages; everything outside /admin and /auth redirects in
- First run (no users) sends every page to /auth/signup, which creates
  the super admin (user #1) once and then locks
- Login is email/username + password; removed the emailed-code flow,
  /auth/sso and email password reset
- Google login kept for existing accounts only, never creates them
- seed-super-admin now requires SEED_SUPER_ADMIN_PASSWORD

Bots-first admin:
- /admin and closed sections (people, logs, backups) redirect to
  /admin/bots; only bots and settings are live (AdminSections)
- Removed the old admin layout; every /admin page uses the bots rail
- Coordinator bot created at signup/seed and by /admin/bots
  (ensureCoordinatorBot), which redirects to its chat
- bots.system_prompt (markdown) with a default coordinator prompt
- seed-bots reuses the existing coordinator

Settings:
- Sub pages general (default), profile, change-password, listed in the
  rail with a back link; unknown settings paths redirect to general
- Redesigned settings UI: pane header, grouped rows, sticky save bar
- General: System/Light/Dark theme, saved per device

Co-Authored-By: Claude Opus 5.5 <[email protected]>
This commit is contained in:
2026-10-18 17:49:25 +01:00
co-authored by Claude Opus 5.5
parent 3286b80029
commit 694c83346f
159 changed files with 1891 additions and 2964 deletions
+16 -23
View File
@@ -2,43 +2,36 @@ import { useContext } from "react";
import Stack from "@moduletrace/twui/layout/Stack";
import { AppContext } from "@/src/pages/__root";
import RailHeader from "../(partials)/rail-header";
import CoordinatorRow from "../(partials)/coordinator-row";
import BotRow from "../(partials)/bot-row";
import RailCrewList from "../(partials)/rail-crew-list";
import RailSettingsList from "../(partials)/rail-settings-list";
import RailUserRow from "../(partials)/rail-user-row";
/**
* Left rail: wordmark, the pinned coordinator, then the crew. Collapses to a
* 64px icon rail below 1024px.
* Left rail: wordmark, then the crew (pinned coordinator first). On
* `/admin/settings/*` the crew is swapped for a back link and the settings
* sub pages. Collapses to a 64px icon rail below 1024px.
*/
export default function BotsRail() {
const { pageProps } = useContext(AppContext);
const bots = pageProps?.bots || [];
const selected_id = pageProps?.bot?.id;
const coordinator = bots.find((bot) => bot.kind === "coordinator");
const workers = bots.filter((bot) => bot.kind !== "coordinator");
const pathname = pageProps?.url?.pathname;
const is_settings_page = Boolean(pathname?.startsWith("/admin/settings"));
return (
<Stack
className="w-16 lg:w-[260px] shrink-0 h-full gap-0! items-stretch border-0 border-r border-solid border-hairline"
aria-label="Bots"
aria-label={is_settings_page ? "Settings" : "Bots"}
>
<RailHeader />
<RailHeader show_new_bot={!is_settings_page} />
<Stack className="grow min-h-0 overflow-y-auto gap-0! items-stretch px-2 lg:px-3 pt-1">
{coordinator ? (
<CoordinatorRow
bot={coordinator}
bots={bots}
selected={coordinator.id === selected_id}
{is_settings_page ? (
<RailSettingsList pathname={pathname} />
) : (
<RailCrewList
bots={pageProps?.bots || []}
selected_id={pageProps?.bot?.id || undefined}
/>
) : null}
{workers.map((bot) => (
<BotRow
key={bot.id}
bot={bot}
selected={bot.id === selected_id}
/>
))}
)}
</Stack>
<RailUserRow />
</Stack>