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.
4.6 KiB
4.6 KiB
Tasks
1. Scaffold the tools app
- 1.1 Create
tools/withpackage.json(express, pocketbase; ESM),server.jswith hardcoded constants (port 4173, portal, PocketBase, a2loft, agent URL, domain catalog) and static serving; verifynode server.jsboots andGET /serves the landing page linkinghttps://smart-pxa.yrka.appand/fingerprint
2. YOUreka Auth session
- 2.1 Port
yrka-auth.jsfromone-school-chat/src/lib/server/yrka-auth.ts(cookie → PocketBase session load, refresh when invalid/expiring <5 min); verify withnode --testand a stubbed PocketBase client covering valid, refresh-needed, and unrecoverable paths - 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 httpOnlysession+ 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 - 2.3 Add session middleware (recover/refresh/clear per design) and route protection:
/fingerprintredirects anonymous to login, API routes reject anonymous; verify with curl both with and without a session cookie - 2.4 Add
permit.js(OpenFGA verify withdomain:<code>thendomain:ididfallback, fail-closed per domain, phone+-stripped), cache permitted domains per operator phone for 5 minutes (in-memory map storing the allowed set + checked time), haveGET /auth/sessionreturn 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 withnode --testand 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
- 3.1 Port
public/capture/UI totools/public/fingerprint/(Vue 2 + Bootstrap 4 + socket.io + axios + swal via CDN); fetch/auth/sessionon 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 - 3.2 Wire the socket to the agent URL from the session:
agent-hello→ show version/captureAgentId; capture buttons emitfprint-enroll {token, domain, finger, reference};fprint-enroll-status→ "Scan N of 5" progress;fprint-enroll-doneok:true → store fingerprint/image/id and show enrollment id, ok:false → swal the failure message and reset to idle; cancel emitsfprint-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 - 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
- 4.1 Add session-required proxies
POST /api/:domain/staff/:referenceandPOST /api/:domain/staff/:reference/fingerprintsthat call a2loft with?phone=<session phone>&otp=<query>and unchanged request/response shapes; verify withnode --testand a fake a2loft upstream asserting the forwarded query and body - 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
- 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, andpublic/ - 5.2 Update README: run instructions (
node tools/server.js, port 4173), hardcoded endpoints, and the fpagent-allowed-originsrequirement includinghttp://localhost:4173; verify a fresh clone can follow it end to end
6. End-to-end verification
- 6.1 Run fpagent with
-allowed-origins http://localhost:4173and 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