Test and deploy / test-and-deploy (push) Successful in 49s
- New server/llm.js consolidates ModelRuntime + model selection: admin setting (app_settings.llm_model) > LLM_MODEL/PREFILL_MODEL env > first available; used by both prefill and roast evaluation - GET/PUT /api/admin/llm lists configured models and stores the choice (validated against the list; empty = auto; audited); admin page gains an LLM section with a model picker - All user-facing 'Pi agent' wording is now 'LLM'; no_model error message no longer references the pi CLI - /roasts table: Review column removed (review lives in the detail view) Co-Authored-By: Claude Fable 5 <[email protected]>
90 lines
6.0 KiB
Markdown
90 lines
6.0 KiB
Markdown
# Roast Planner Webapp
|
|
|
|
A fillable, live-computing web version of the manual coffee roast plan worksheet
|
|
(`hope_roaster/roast-planner/manual-roast-planner.html`), plus:
|
|
|
|
- **Prefill from a bean URL** — paste a roaster's product page URL; the server fetches it and
|
|
uses the Pi Coding Agent SDK (zero-tool, one-turn extraction) to pull out whatever it can
|
|
find (cultivar, origin, processing, roast level), then deterministically maps that to
|
|
worksheet field IDs using the same reference tables as the paper worksheet. It never
|
|
invents a first-crack anchor or a modifier — those only ever come from the reference
|
|
tables, keyed off what the page actually states.
|
|
- **Reference curve from an Artisan `.alog`** — upload a real roast log (or browse a local
|
|
library directory) to overlay its actual milestones/curve on the same SVG grid as your
|
|
plan, dashed and in a different color.
|
|
- **Finished-roast log with Pi agent review** — upload one or more finished Artisan `.alog`
|
|
files against a plan (or standalone from `/roasts`); each is stored verbatim (downloadable
|
|
as a backup), parsed, and deep-reviewed asynchronously by the same zero-tool Pi agent
|
|
pattern as prefill (grade, highlights, concerns, next-batch suggestions, plan-vs-actual).
|
|
The `/roasts` page lists every actual roast and renders its curve with the plan overlaid.
|
|
- **Live ledger** — the worksheet's time-ledger math (first crack, yellow, Maillard,
|
|
development, drop, and the four sanity-check ratios) recomputes as you type, using the
|
|
exact same arithmetic as the paper worksheet (verified against both its worked examples).
|
|
- **Print** — bakes typed values into the print layout and reuses the paper worksheet's
|
|
print CSS, so `File > Print` produces the same 2-page front/back sheet.
|
|
|
|
## Run it
|
|
|
|
```bash
|
|
cp .env.example .env # set strong secrets
|
|
npm install
|
|
npm test
|
|
# local PostgreSQL stack (database is not published to the host)
|
|
docker compose --env-file .env up --build
|
|
```
|
|
|
|
The public landing page is at `/`; plans require an account at `/app`. Production is configured for `https://roast.srmr.xyz`: retain `APP_ORIGIN=https://roast.srmr.xyz` and `COOKIE_SECURE=true` behind its HTTPS proxy. Database migrations in `db/migrations/` run at application startup exactly once.
|
|
|
|
### First administrator
|
|
|
|
Generate `BOOTSTRAP_SETUP_TOKEN` with `openssl rand -base64 48`, keep it only in the deployment environment, then call `POST /api/auth/bootstrap` with that token, `[email protected]`, and a 12+ character password. The endpoint can create that account only once. Remove the setup token after success; it is optional thereafter and no administrator password is stored in source control.
|
|
|
|
### Pi agent configuration in Docker
|
|
|
|
The `app` service mounts `PI_AGENT_CONFIG_DIR` (default `./appdata/pi-agent`) read-only at `/run/pi-agent-config`. Its entrypoint copies that seed into the non-root Node user's writable runtime configuration directory before startup: Pi's credential storage needs to create a lock beside `auth.json`. This lets `/api/prefill` use the configured model without baking credentials into the image or mutating the host configuration. The directory is ignored by Git and Docker build context; do not commit its contents.
|
|
|
|
Before bringing up the stack, sync only the local Pi agent configuration you intend to make available to the container:
|
|
|
|
```bash
|
|
mkdir -p appdata/pi-agent
|
|
rsync -a --delete ~/.pi/agent/ appdata/pi-agent/
|
|
docker compose --env-file .env up --build
|
|
```
|
|
|
|
Set `PI_AGENT_CONFIG_DIR` to another protected host directory instead if preferred. Restrict access to that directory because it can contain provider credentials. The mount is read-only, so Pi cannot alter the host configuration. If deployed behind a reverse proxy, set `TRUST_PROXY` only to that proxy's specific IP/CIDR (or keep it blank when the app is directly exposed).
|
|
|
|
## Mobile and PWA use
|
|
|
|
The planner is responsive and caches its app shell for offline use after the first visit. Browser installation and service-worker caching require HTTPS in production (localhost is exempt). Put the Docker container behind an HTTPS reverse proxy before using it as an installable PWA on a phone.
|
|
|
|
Prefill requires a model configured via the `pi` CLI (`~/.pi/agent/{models,auth}.json`) —
|
|
without one, `/api/prefill` returns `503 no_model` rather than crashing. Everything else
|
|
(the form, ledger, curve, `.alog` upload) works with no model configured. Which configured
|
|
model runs prefill and roast reviews is chosen in the admin page's **LLM** section
|
|
(`app_settings.llm_model`, "Auto" = first available); the UI refers to the model backend
|
|
simply as "the LLM".
|
|
|
|
## Layout
|
|
|
|
- `shared/` — browser-safe ESM modules imported by *both* the Node server and the browser
|
|
(served statically at `/shared/`). This is the single source of truth for the ledger math,
|
|
time parsing, and reference data tables — never duplicate this logic in `server/` or `public/js/`.
|
|
- `server/` — Express routes: `/api/prefill`, `/api/alog`, `/api/alog/library[/:filename]`.
|
|
- `public/` — the static frontend: `index.html` (the worksheet as a form), `worksheet.css`
|
|
(a verbatim copy of the paper worksheet's print CSS — port changes from there, don't
|
|
hand-edit divergently), `app.css` (screen-only chrome + form-control styling), `js/`.
|
|
|
|
## Where the reference numbers come from
|
|
|
|
`shared/reference-data.js` is ported from
|
|
`/Users/shane/dev/hope_roaster/roast-planner/manual-roast-planner.html`'s Step 1 (cultivars),
|
|
Step 3 (processing), Step 4 (roast levels), Step 8 (machine bands), and Step 7 (sanity bands).
|
|
If those tables change in the paper worksheet, port the change here too.
|
|
|
|
## Known gaps (v1)
|
|
|
|
- Offline drafts are intentionally scoped to the authenticated browser account and are cleared on logout; account-backed plans remain the authoritative copy.
|
|
- Roastetta (roastetta.com) integration is intentionally out of scope — it needs a headed,
|
|
Cloudflare-clearing browser and the operator's own credentials. Use the `.alog` file picker,
|
|
or point `ALOG_DIR` at wherever the `roastetta` skill already downloaded files.
|