docs(openspec): fpagent-tools-ui change — proposal, specs, design, tasks
Synced main specs (tools-app, fingerprint-wizard); change archived at 2026-09-30-fpagent-tools-ui. Includes openspec project config and the agent skills installed with it.
This commit is contained in:
@@ -0,0 +1,32 @@
|
||||
# Tasks
|
||||
|
||||
## 1. Scaffold the tools app
|
||||
|
||||
- [x] 1.1 Create `tools/` with `package.json` (express, pocketbase; ESM), `server.js` with hardcoded constants (port 4173, portal, PocketBase, a2loft, agent URL, domain catalog) and static serving; verify `node server.js` boots and `GET /` serves the landing page linking `https://smart-pxa.yrka.app` and `/fingerprint`
|
||||
|
||||
## 2. YOUreka Auth session
|
||||
|
||||
- [x] 2.1 Port `yrka-auth.js` from `one-school-chat/src/lib/server/yrka-auth.ts` (cookie → PocketBase session load, refresh when invalid/expiring <5 min); verify with `node --test` and a stubbed PocketBase client covering valid, refresh-needed, and unrecoverable paths
|
||||
- [x] 2.2 Add `GET /auth/login` (create portal web-auth session with callback URL, redirect to login URL), `GET /auth/callback` (redeem session id, set httpOnly `session` + profile cookies 30d, redirect `/fingerprint`; error page on unredeemable id), `GET /auth/logout` (clear cookies, redirect `/`); verify with a stubbed portal upstream that cookies are set on success and not set on failure
|
||||
- [x] 2.3 Add session middleware (recover/refresh/clear per design) and route protection: `/fingerprint` redirects anonymous to login, API routes reject anonymous; verify with curl both with and without a session cookie
|
||||
- [x] 2.4 Add `permit.js` (OpenFGA verify with `domain:<code>` then `domain:idid` fallback, fail-closed per domain, phone `+`-stripped), cache permitted domains per operator phone for 5 minutes (in-memory map storing the allowed set + checked time), have `GET /auth/session` return token, phone, name, agent URL, the cached domain list, and the last-checked time, and add a force-refresh endpoint that re-checks all domains bypassing the cache but refuses while the cache is fresh; verify with `node --test` and stubbed fetch covering allow, deny, fallback-allow, transport-error, cache-hit-with-zero-portal-calls, re-check-after-expiry, and refresh-refused-while-fresh cases
|
||||
|
||||
## 3. Fingerprint wizard page
|
||||
|
||||
- [x] 3.1 Port `public/capture/` UI to `tools/public/fingerprint/` (Vue 2 + Bootstrap 4 + socket.io + axios + swal via CDN); fetch `/auth/session` on load; replace the fixed domain with a dropdown of permitted domains; show the no-permitted-domains message when the list is empty; verify the page renders for a sessioned user and redirects anonymous users
|
||||
- [x] 3.2 Wire the socket to the agent URL from the session: `agent-hello` → show version/`captureAgentId`; capture buttons emit `fprint-enroll {token, domain, finger, reference}`; `fprint-enroll-status` → "Scan N of 5" progress; `fprint-enroll-done` ok:true → store fingerprint/image/id and show enrollment id, ok:false → swal the failure message and reset to idle; cancel emits `fprint-enroll-stop`; disable both fingers' capture controls while a capture is in flight; verify against a running fpagent (hardware or its virtual-driver harness) for capture, fail, and cancel paths
|
||||
- [x] 3.3 Add the permitted-domains refresh control to the wizard: disabled until 5 minutes have elapsed since the session response's last-checked time; on click call the force-refresh endpoint and update the dropdown and last-checked time (a refused refresh leaves the list and selection unchanged); verify in the browser that the control is disabled within the TTL, enables after, and a click refreshes the list
|
||||
|
||||
## 4. a2loft proxies
|
||||
|
||||
- [x] 4.1 Add session-required proxies `POST /api/:domain/staff/:reference` and `POST /api/:domain/staff/:reference/fingerprints` that call a2loft with `?phone=<session phone>&otp=<query>` and unchanged request/response shapes; verify with `node --test` and a fake a2loft upstream asserting the forwarded query and body
|
||||
- [x] 4.2 Switch the wizard's lookup and save to the proxies with per-call OTP inputs (lookup step and save step), keeping the both-fingers-required rule and the reset-on-new-enrollment behavior; verify the full lookup → details → save round trip against a2loft
|
||||
|
||||
## 5. Cleanup and docs
|
||||
|
||||
- [x] 5.1 Run the parity walkthrough on field hardware (login → filtered domains → lookup → capture thumb + index → save; saved fingerprints verify as before) and only then delete `app.py`, `requirement.txt`, and `public/`
|
||||
- [x] 5.2 Update README: run instructions (`node tools/server.js`, port 4173), hardcoded endpoints, and the fpagent `-allowed-origins` requirement including `http://localhost:4173`; verify a fresh clone can follow it end to end
|
||||
|
||||
## 6. End-to-end verification
|
||||
|
||||
- [x] 6.1 Run fpagent with `-allowed-origins http://localhost:4173` and the tools app; walk login → permitted domains → staff lookup → two captures → save against the real portal/PocketBase/a2loft, and confirm the saved fingerprints appear in a2loft for the profile
|
||||
Reference in New Issue
Block a user