Files
assistenza/tasklist_fase1.txt

321 lines
11 KiB
Plaintext
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
Prompt 00 — Monorepo + shared protocol (TypeScript ovunque)
Crea una PR: “Monorepo bootstrap + shared protocol v1”.
Obiettivo:
- Impostare un monorepo con TypeScript ovunque e un pacchetto condiviso `shared/protocol` con tipi e helper per il protocollo WebSocket v1.
Struttura richiesta:
- /server (Node.js + TS)
- /windows (Electron + TS)
- /shared/protocol (TS, esporta tipi e validator base)
- /android NON in TS (verrà dopo), ma lascia spazio /android
Requisiti tecnici:
- Usa pnpm workspaces (preferibile) oppure yarn workspaces.
- In `shared/protocol` definisci:
- `ProtocolVersion = 1`
- tipi per tutti i messaggi Fase 1:
- agent_register, agent_registered, agent_heartbeat
- server_connect_request
- agent_connect_response
- client_login, client_login_result
- client_list_devices, client_device_list
- client_connect_request
- client_connect_status, client_connect_result
- error
- un `type AnyMessage = ...` union
- helper `isMessage(obj): obj is AnyMessage` con validazione minimale (no librerie pesanti; ok zod se vuoi ma tienilo semplice)
- helper `makeRequestId()` (uuid v4) e `nowTs()`
Output:
- Workspace funzionante con `pnpm i` e build TS.
- README root con comandi base.
Acceptance:
- `pnpm -r build` passa.
- Import da `shared/protocol` funziona da server e windows (anche se windows non è ancora implementato).
- Niente codice Android in questa PR.
Prompt 01 — Server skeleton + healthcheck + Docker
Crea una PR: “Server signaling TS skeleton + /health + Docker”.
Contesto:
- Monorepo esiste con `shared/protocol`.
Obiettivo:
- Creare `server` Node.js TypeScript che espone:
- HTTP GET `/health` => 200 “ok”
- WebSocket upgrade pronto (endpoints verranno dopo)
Requisiti:
- Framework HTTP: Fastify o Express (scegli uno e mantienilo semplice).
- Logger: pino (consigliato) o console strutturata.
- Config `.env` (usa dotenv).
- Dockerfile per server.
- Script: `pnpm --filter server dev` e `build` e `start`.
Acceptance:
- Avvio locale: `pnpm --filter server dev`
- `curl localhost:<port>/health` => 200
- `docker build` e `docker run` funzionano.
- Nessun endpoint WS richiesto ancora (solo scaffolding).
Prompt 02 — WebSocket endpoints /agent e /client + parsing robusto
Crea una PR: “WS endpoints /agent e /client + parsing robusto”.
Obiettivo:
- Implementare WebSocket su server con due path:
- ws://.../agent
- ws://.../client
Requisiti:
- Usa libreria `ws` (preferita) integrata con il server HTTP scelto.
- Per ogni connessione, identifica “kind” (agent/client) in base al path.
- Parsing JSON robusto: se arriva JSON invalido o messaggio non conforme, rispondi con:
{ v:1, type:"error", requestId: <se presente>, code:"BAD_REQUEST", message:"..." }
- Non deve crashare.
Acceptance:
- Connessione WS a /agent e /client possibile.
- Invio messaggio non JSON => ricevo error e connessione resta (o chiude con motivo, ok entrambe purché non crashi).
- Logga connect/disconnect con ip e path.
Prompt 03 — Auth Fase 1 (client login + agent pairingKey)
Crea una PR: “Auth F1: client_login + agent_register pairingKey”.
Obiettivo:
- Implementare autenticazione minimale:
- Client: `client_login` con username/password contro seed in env
- Agent: `agent_register` con pairingKey contro seed in env, associato allo stesso userId del seed
Env richieste (server):
- SEED_USERNAME
- SEED_PASSWORD (plaintext per ora, MVP)
- SEED_USER_ID (es. "user-1")
- SEED_PAIRING_KEY (string)
Comportamento:
- client_login ok => memorizza sessione in-memory (map ws -> userId).
- richieste client senza login => error UNAUTHORIZED.
- agent_register con pairingKey errata => error UNAUTHORIZED.
Usa i tipi di `shared/protocol`.
Acceptance:
- client senza login che fa list => UNAUTHORIZED
- login corretto => ok:true
- agent_register con pairingKey corretto => agent_registered ok:true
- agent_register con pairingKey errata => UNAUTHORIZED
Prompt 04 — Presence registry + heartbeat + lastSeen
Crea una PR: “Presence registry: devices online/offline + heartbeat”.
Obiettivo:
- Mantenere registry in memoria:
devicesById: deviceId -> { deviceId, deviceName, userId, online, lastSeenIso, wsRef? }
Regole:
- Su agent_register: crea/aggiorna entry, set online=true, lastSeen=now, salva wsRef.
- Su agent_heartbeat: aggiorna lastSeen=now (se deviceId noto), altrimenti error BAD_REQUEST.
- Su WS disconnect agent: online=false, lastSeen=now, wsRef=null.
Extra:
- Aggiungi un cleanup interval che marca offline se non riceve heartbeat entro 90s (anche se la socket resta “mezzo morta”).
Acceptance:
- Simulando heartbeat si aggiorna lastSeen.
- Chiudendo connessione agent => online=false.
- Se heartbeat manca >90s => online=false.
Prompt 05 — client_list_devices + filtro per userId
Crea una PR: “client_list_devices + device list filtrata”.
Obiettivo:
- Implementare:
- client_list_devices => server risponde client_device_list
Dettagli:
- Deve essere richiesto solo da client autenticato.
- Ritorna SOLO device associati al userId del client.
- Ogni device include: deviceId, deviceName, online, lastSeenIso.
Acceptance:
- Un client loggato vede la lista.
- Un client non loggato riceve UNAUTHORIZED.
- Se ci sono più device (simulati) appartenenti a userId diversi, ne vede solo i suoi.
Prompt 06 — Connect request routing + session state + timeout expired
Crea una PR: “Connect request routing + session state + timeout”.
Obiettivo:
- Implementare client_connect_request:
- verifica auth client
- verifica ownership (device.userId == client.userId)
- se device offline => error DEVICE_OFFLINE
- se device online => crea sessionId e status=pending
- inoltra a agent `server_connect_request` con {sessionId, fromUser: username}
- al client risponde `client_connect_status` status=pending
Session store in memory:
- sessionsById: sessionId -> { sessionId, userId, deviceId, status, createdAt, clientWsRef }
Timeout:
- dopo 60s se ancora pending => status=expired e invia al client `client_connect_result` status=expired
Acceptance:
- Richiesta verso device offline => DEVICE_OFFLINE
- Richiesta verso device online => agent riceve server_connect_request e client riceve pending
- Se agent non risponde entro 60s => client riceve expired
Prompt 07 — Agent accept/deny -> client result
Crea una PR: “agent_connect_response accept/deny -> client_connect_result”.
Obiettivo:
- Implementare agent_connect_response:
- valida sessionId esistente
- valida che session.deviceId == msg.deviceId
- valida che session.status == pending
- se decision accept => status=accepted
- se decision deny => status=denied
- invia a client `client_connect_result` con status accepted/denied
Edge cases:
- sessionId inesistente => error NOT_FOUND
- session non pending => error CONFLICT
- deviceId mismatch => error FORBIDDEN
Acceptance:
- Agent accept => client riceve accepted
- Agent deny => client riceve denied
- Risposte duplicate => CONFLICT
Prompt 08 — docker-compose dev (server + nginx reverse proxy WS)
Crea una PR: “Dev compose: nginx reverse proxy -> server (WS upgrade ok)”.
Obiettivo:
- Aggiungere `infra/docker-compose.yml` con:
- server
- nginx reverse proxy davanti
Requisiti:
- Nginx deve supportare WebSocket upgrade per /agent e /client.
- In dev, niente TLS (http + ws).
- Documenta in README come testare:
- ws://localhost/agent
- ws://localhost/client
(oppure con porte diverse, ma deve essere chiaro)
Acceptance:
- Connessione WS tramite nginx funziona (upgrade ok).
- /health raggiungibile via nginx.
Prompt Windows (Electron) — Fase 1
Prompt W1 — Electron skeleton + pagine login/devices
Crea una PR: “Electron TS skeleton: login page + devices page”.
Obiettivo:
- Creare app Electron in /windows con TypeScript.
- UI minimale con due schermate:
- Login (username/password, serverUrl)
- Devices (placeholder lista)
Requisiti:
- Puoi usare React+Vite oppure Electron Forge. Scegli stack semplice e moderno.
- Config serverUrl persistente (localStorage ok).
Acceptance:
- `pnpm --filter windows dev` avvia lapp.
- Navigazione login -> devices (anche finta).
Prompt W2 — WS client + client_login
Crea una PR: “Windows: WS client + client_login”.
Obiettivo:
- Connettere Electron a ws://<serverUrl>/client
- Implementare invio client_login e gestione client_login_result usando `shared/protocol`.
Requisiti:
- Gestisci reconnect base (retry ogni 2s fino a successo) o almeno errore UI.
- Dopo login ok => vai a schermata Devices.
Acceptance:
- Login corretto => ok e vai a Devices.
- Login errato => mostra errore.
Prompt W3 — Lista device (client_list_devices + refresh)
Crea una PR: “Windows: device list + refresh/poll”.
Obiettivo:
- Implementare:
- client_list_devices allapertura della schermata
- poll ogni 5s (o pulsante refresh) per aggiornare
- Render tabella: deviceName, deviceId, online, lastSeenIso.
Acceptance:
- Vedo device comparire e cambiare stato online/offline.
Prompt W4 — Connect request UI + stato pending/accepted/denied/expired
Crea una PR: “Windows: connect request + status UI”.
Obiettivo:
- Aggiungere un bottone “Connect” per ogni device.
- Click => invia client_connect_request.
- Mostra stato:
- pending (spinner o testo)
- accepted / denied / expired
Requisiti:
- Gestisci più richieste (almeno una alla volta; se già pending, disabilita altri connect).
Acceptance:
- Pending appare subito.
- Alla risposta dellagent si aggiorna la UI.
Prompt Android (Kotlin) — Fase 1
Prompt A1 — Foreground service + WS + deviceId persistente
Crea una PR Android: “Agent: ForegroundService + WS client + deviceId persistente”.
Obiettivo:
- App Android Kotlin con:
- ForegroundService “Remote Agent”
- WebSocket OkHttp
- deviceId UUID persistente in SharedPreferences
- schermata settings: serverUrl, pairingKey, deviceName (persistenti)
Comportamento:
- Avvio app -> start service -> connect ws://serverUrl/agent
- Log utile.
Acceptance:
- Service parte e resta in foreground con notifica.
- deviceId resta uguale tra riavvii app.
Prompt A2 — agent_register + heartbeat + reconnect
Crea una PR Android: “Agent: agent_register + heartbeat + reconnect”.
Obiettivo:
- Dopo WS open => invia agent_register {deviceId, deviceName, pairingKey}
- Heartbeat ogni 30s: agent_heartbeat
- Reconnect automatico se WS cade (retry con backoff semplice).
Acceptance:
- Server mostra device online.
- Stacco/riattacco rete => torna online senza riaprire app.
Prompt A3 — Notifica connect_request con azioni + agent_connect_response
Crea una PR Android: “Agent: notifica connect request + accept/deny”.
Obiettivo:
- Ricevere server_connect_request dal WS
- Mostrare notifica con azioni:
- ACCETTA
- RIFIUTA
- Click azione => invia agent_connect_response con {sessionId, deviceId, decision}
Requisiti:
- Deve funzionare anche con app in background.
- Usa PendingIntent + BroadcastReceiver per gestire azioni.
Acceptance:
- Notifica appare quando il client richiede connessione.
- Accept/deny inviano risposta e il client riceve lesito.