Imported from SteveVitali/Demiurge (
desktop/AGENTS.md). Install upstream withnpx skills add SteveVitali/Demiurge --skill desktop. Copyright stays with the author.
AGENTS.md β desktop/ (Tauri GUI)
Purpose
Tauri 2 desktop app (React 19 + Vite 6 frontend, Rust core) with full CLI parity.
It spawns the Scala backend as a fat-JAR sidecar and talks to it over the local API
(REST/SSE on 127.0.0.1:19440, WebSocket on 127.0.0.1:19441 β see
src/lib/constants.ts).
Key Files
| File | Lines | Purpose |
|---|---|---|
src-tauri/src/sidecar.rs |
~350 | Spawns/monitors/stops the backend sidecar process |
src-tauri/src/tray.rs |
~250 | System tray menu, synced to run state |
src-tauri/src/commands.rs |
~100 | Tauri invoke commands exposed to the frontend |
src/api/types.ts |
~450 | TS mirror of every backend DTO β keep in sync with modules/core-model |
src/api/endpoints.ts |
~200 | All REST calls (TanStack Query fetchers) |
src/api/websocket.ts |
~200 | WS client with reconnect/heartbeat |
src/screens/SettingsScreen.tsx |
~450 | Largest screen; settings + API key persistence |
src/lib/constants.ts |
~20 | API/WS URLs, poll intervals, page sizes |
src-tauri/tauri.conf.json |
β | Window, tray, updater endpoint, externalBin sidecar |
scripts/package-sidecar.sh |
β | Builds demiurge_deploy.jar via Bazel, writes launcher into src-tauri/binaries/ |
Build & Test
npm install
npx tsc --noEmit # THE CI gate β no unit tests exist
npm run tauri dev # Vite on localhost:1420 + Tauri window (needs Rust toolchain)
npm run dev # frontend only, no Tauri shell
bash scripts/package-sidecar.sh && npm run tauri build # production bundle
Code Conventions
- State: zustand stores in
src/stores/*.store.ts(6 stores: app, run, agent, auth, logs, preferences); server state via TanStack Query (src/lib/query-client.ts, keys insrc/lib/query-keys.ts). - Screens vs components: 7 top-level screens in
src/screens/; reusable pieces grouped by domain insrc/components/<domain>/(15 domain dirs). - Styling: Tailwind CSS 4 via
@tailwindcss/vite; class merging withclsx+tailwind-merge(src/lib/utils.ts). - Backend access: never fetch ad hoc β add the endpoint to
src/api/endpoints.tsand its DTO tosrc/api/types.ts.
Critical Gotchas
- Strictest tsconfig in the repo:
noUnusedLocals,noUnusedParameters,noUncheckedIndexedAccess,noFallthroughCasesInSwitch. An unused import orarr[i]without an undefined-check fails the CI gate (npx tsc --noEmit). src-tauri/binaries/is gitignored βnpm run tauri buildfails or ships a broken app unlessscripts/package-sidecar.shran first (it names the launcher with the Tauri target triple, e.g.demiurge-sidecar-aarch64-apple-darwin).- In dev mode the sidecar placeholder is skipped β the app uses a Bazel wrapper
instead of a packaged JAR (see
sidecar.rs); don't assume dev == release spawn path. src/api/types.tsdrifts silently β the backend's circe DTOs are the source of truth; a Scala field rename won't fail any desktop build until runtime.- Updater/signing config in
tauri.conf.jsonis release-critical β theplugins.updaterendpoint and pubkey pair withTAURI_SIGNING_PRIVATE_KEYsecrets inrelease.yml. Ask before touching. - Deep links use the
demiurge://scheme (plugin-deep-link) for the webβdesktop auth callback; changing it breaks login from demiurge.dev.
Source Layout
desktop/
βββ src/
β βββ main.tsx # entry
β βββ api/ # client, endpoints, sse, websocket, types (5 files)
β βββ screens/ # 7 screens (Dashboard, RunDetail, Config, Settings, Auth, AuthCallback, DetachedLog)
β βββ components/ # 15 domain dirs + PlanTierBadge.tsx
β βββ stores/ # 6 zustand stores (*.store.ts)
β βββ hooks/ # 10 hooks (SSE, WS, transcript, notifications, β¦)
β βββ lib/ # constants, query-client, query-keys, routes, utils
βββ src-tauri/
β βββ src/ # main.rs, lib.rs, commands.rs, sidecar.rs, tray.rs
β βββ tauri.conf.json # window/tray/updater/externalBin config
βββ scripts/package-sidecar.sh