5.0: avvio in locale nella sandbox deploy/local, script di avvio e screenshot, arresto pulito del container verificato
Per vedere e provare il bot senza Unraid: F5 in VS Code, scripts/run-dev e scripts/run-docker usano tutti deploy/local come /config e /data, con la stessa disposizione del container, così chiavi e configurazione valgono in ogni modo. ENCELADO_DATA_DIR separa i dati anche fuori dal container. scripts/screenshots rigenera docs/img dal server campione; /api/health in --sample non finge più un motore acceso. docker stop ora termina in un secondo con «SIGTERM: arresto» (PosixSignalRegistration), provato sull'immagine 5.0.0 costruita dalla catena. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
@@ -1,8 +1,12 @@
|
||||
{
|
||||
// F5 avvia il server (motore + interfaccia web) come processo locale, fuori dal
|
||||
// container, con la cartella dati in Documenti\Encelado. Il browser si apre da solo
|
||||
// appena Kestrel è in ascolto. "Encelado (campione)" serve l'interfaccia con dati
|
||||
// finti, senza chiavi né mercato: è quello che si usa per lavorare sulle pagine.
|
||||
// container, con configurazione e dati nella sandbox deploy/local (ignorata da git,
|
||||
// stesse cartelle che il compose monta come /config e /data). Il browser si apre da
|
||||
// solo appena Kestrel è in ascolto; senza token il server ascolta solo su localhost e
|
||||
// non chiede il login. "Encelado (campione)" serve l'interfaccia con dati finti, senza
|
||||
// chiavi né mercato: è quello che si usa per lavorare sulle pagine.
|
||||
// Le chiavi eToro: variabili ETORO_API_KEY/ETORO_USER_KEY nell'ambiente di VS Code,
|
||||
// oppure Impostazioni ▸ Chiavi eToro con ENCELADO_KEY_PASSPHRASE impostata qui sotto.
|
||||
"version": "0.2.0",
|
||||
"configurations": [
|
||||
{
|
||||
@@ -17,6 +21,8 @@
|
||||
"stopAtEntry": false,
|
||||
"env": {
|
||||
"ENCELADO_WEB_PORT": "8080",
|
||||
"ENCELADO_CONFIG_DIR": "${workspaceFolder}/deploy/local/config",
|
||||
"ENCELADO_DATA_DIR": "${workspaceFolder}/deploy/local/data",
|
||||
"DOTNET_ENVIRONMENT": "Development"
|
||||
},
|
||||
"serverReadyAction": {
|
||||
|
||||
@@ -112,10 +112,37 @@
|
||||
"problemMatcher": "$msCompile"
|
||||
},
|
||||
{
|
||||
"label": "docker compose up",
|
||||
"detail": "Avvia il container dalla docker-compose.yml alla radice (immagine locale, cartelle ./deploy/local/config e ./deploy/local/data).",
|
||||
"label": "avvia in locale (script)",
|
||||
"detail": "scripts/run-dev.ps1: compila e avvia il server nella sandbox deploy/local, senza container. Ctrl+C per fermare.",
|
||||
"type": "shell",
|
||||
"command": "docker compose up --build",
|
||||
"command": "powershell -ExecutionPolicy Bypass -File scripts/run-dev.ps1",
|
||||
"options": { "cwd": "${workspaceFolder}" },
|
||||
"presentation": { "reveal": "always", "panel": "dedicated" },
|
||||
"problemMatcher": []
|
||||
},
|
||||
{
|
||||
"label": "avvia in docker",
|
||||
"detail": "scripts/run-docker.ps1: costruisce l'immagine e avvia il container dalla docker-compose.yml (deploy/local come /config e /data), poi segue il log.",
|
||||
"type": "shell",
|
||||
"command": "powershell -ExecutionPolicy Bypass -File scripts/run-docker.ps1",
|
||||
"options": { "cwd": "${workspaceFolder}" },
|
||||
"presentation": { "reveal": "always", "panel": "dedicated" },
|
||||
"problemMatcher": []
|
||||
},
|
||||
{
|
||||
"label": "ferma docker",
|
||||
"detail": "docker compose down: SIGTERM al bot, stop pulito, container rimosso (cartelle in deploy/local conservate).",
|
||||
"type": "shell",
|
||||
"command": "docker compose down",
|
||||
"options": { "cwd": "${workspaceFolder}" },
|
||||
"presentation": { "reveal": "always", "panel": "dedicated" },
|
||||
"problemMatcher": []
|
||||
},
|
||||
{
|
||||
"label": "screenshot",
|
||||
"detail": "scripts/screenshots.ps1: server in modalità campione e Edge headless, rigenera docs/img/*.png.",
|
||||
"type": "shell",
|
||||
"command": "powershell -ExecutionPolicy Bypass -File scripts/screenshots.ps1",
|
||||
"options": { "cwd": "${workspaceFolder}" },
|
||||
"presentation": { "reveal": "always", "panel": "dedicated" },
|
||||
"problemMatcher": []
|
||||
|
||||
@@ -10,6 +10,7 @@ Formato: una voce per sessione di lavoro, con data. Le voci più recenti in alto
|
||||
- **Strumento**: comando `learn` (ciclo di apprendimento in ombra su un ledger esportato).
|
||||
- **Pulizia**: rimossi WPF, DPAPI, i test di rendering e di binding, ~45 membri mai usati nel Core e nell'Engine (statistiche di ricerca, `Psi`, `EmptyContextProvider`, `EnvironmentKeyStore`, display helper dello snapshot, `INotifyPropertyChanged`), `build/Encelado.iss`. `PROMPT.md` → `docs/PROMPT_5.0.md`.
|
||||
- **Documenti**: ADR-0007, ADR-0008, `docs/DOCKER.md`, `docs/UI_GUIDELINES.md`, `README.md`, `CLAUDE.md`, ARCHITECTURE, RUNBOOK, KNOWN_ISSUES, GLOSSARY, DATA_SOURCES, ML_AND_LEARNING, QUESTIONS (D-28, D-29, D-36, D-37), POSTMORTEM (gli accrediti del demo erano uno per ordine ridotto), skill, `build/README.md`.
|
||||
- **Avvio in locale**: sandbox `deploy/local/` con la stessa disposizione del container (`ENCELADO_DATA_DIR` vale anche fuori dal container), F5 in VS Code sulla sandbox, script `scripts/run-dev`, `run-docker`, `screenshots` (`.ps1` e `.sh`), attività di VS Code per avviare, fermare e fotografare. Arresto pulito su SIGTERM (`PosixSignalRegistration`) verificato con `docker stop`; `/api/health` in `--sample` non finge un motore acceso.
|
||||
- Test nuovi: `EmbeddedUiTests`, `WebHostTests` (token, cookie, stream, storico), `HistoryBuilderTests`, `KeyStoreTests`; 210 verdi, anche su Linux dentro `docker build`. Versione di sviluppo 5.0.0.
|
||||
|
||||
## 2026-09-23 — 5.0, apprendimento in ombra (ADR-0006) e skill di progetto
|
||||
|
||||
@@ -45,10 +45,10 @@ deploy/docker, deploy/unraid entrypoint del container, template Unraid
|
||||
- **Tempo**: UTC ovunque; conversione solo in UI. `CultureInfo.InvariantCulture` per ogni parsing e formattazione su file.
|
||||
- **Concorrenza**: un solo thread di decisione; I/O asincrono; `Channel<T>` fra ingestion, strategia, esecuzione e UI.
|
||||
- **Riproducibilità**: seed fisso 42 per ogni componente stocastica; ogni run scrive `run_id`, hash della configurazione e versione del codice nel ledger.
|
||||
- **Cartelle a runtime**: nel container `/config` (encelado.json, strategy.json, instruments.json, `etoro.keys.enc`, file `STOP`) e `/data` (`data/`, `knowledge/`, `reports/`, `results/`, `logs/`); fuori dal container (solo sviluppo) `Documenti\Encelado\` o `ENCELADO_CONFIG_DIR`. Credenziali solo nelle variabili `ETORO_API_KEY`/`ETORO_USER_KEY` o nel file cifrato (AES-256-GCM, passphrase `ENCELADO_KEY_PASSPHRASE`); token Telegram solo in `TELEGRAM_BOT_TOKEN` (chat in `TELEGRAM_CHAT_ID`); token dell'interfaccia in `ENCELADO_WEB_TOKEN`.
|
||||
- **Cartelle a runtime**: nel container `/config` (encelado.json, strategy.json, instruments.json, `etoro.keys.enc`, file `STOP`) e `/data` (`data/`, `knowledge/`, `reports/`, `results/`, `logs/`); fuori dal container (solo sviluppo) la sandbox `deploy/local/` (`ENCELADO_CONFIG_DIR` + `ENCELADO_DATA_DIR`, usata da F5 e dagli script) o, senza variabili, `Documenti\Encelado\`. Credenziali solo nelle variabili `ETORO_API_KEY`/`ETORO_USER_KEY` o nel file cifrato (AES-256-GCM, passphrase `ENCELADO_KEY_PASSPHRASE`); token Telegram solo in `TELEGRAM_BOT_TOKEN` (chat in `TELEGRAM_CHAT_ID`); token dell'interfaccia in `ENCELADO_WEB_TOKEN`.
|
||||
- **Esecuzione (5.0)**: ogni ordine entra nel registro `data/state/pending_orders.json` prima dell'HTTP e l'esito si legge per `orderId` (il server non registra il `referenceId`); una gamba senza esito porta il basket in `PendingA`/`PendingB`, mai in un rifiuto; ogni posizione del conto è `basket`, `orfana-bot` (adottata e chiusa) o `esterna` (mai toccata); la size è il minimo fra rischio e margine (`strategy.json` → `risk`); il kill-switch verifica la piattezza sul conto e dichiara `Halted-Residuo` se resta qualcosa; heartbeat e recupero dopo inattività (`recovery`); un solo bot per cartella dati (`instance.lock`).
|
||||
- **Interfaccia**: navigation rail a sinistra (Dashboard, Storico ordini, Log, Impostazioni), barra in alto (titolo, ora UTC e nel fuso, valuta, AVVIA/FERMA, KILL-SWITCH), pagine sotto. Nella dashboard solo le informazioni principali; i dettagli nei tooltip e nel log. La UI non decide niente: legge lo snapshot via SSE e manda comandi via `POST /api/commands/<nome>`. Gli orari a schermo sono nel fuso scelto (`ui.timeZone`, `TZ`); il log porta l'offset, il ledger è UTC. Regole complete in `docs/UI_GUIDELINES.md`.
|
||||
- **Verifica visiva**: `dotnet run --project src/Encelado.Server -- --sample --port 8085` e screenshot con Edge headless (`docs/UI_GUIDELINES.md`); i test `EmbeddedUiTests` e `WebHostTests` coprono HTML, JSON, token e stream.
|
||||
- **Verifica visiva**: `scripts\screenshots.ps1` (server `--sample` + Edge headless, `docs/UI_GUIDELINES.md`); i test `EmbeddedUiTests` e `WebHostTests` coprono HTML, JSON, token e stream.
|
||||
|
||||
## Comandi
|
||||
|
||||
@@ -57,6 +57,9 @@ dotnet build Encelado.slnx # compilazione
|
||||
dotnet test tests/Encelado.Tests --no-restore # test (xunit)
|
||||
dotnet msbuild build/Release.proj -t:Verifica # compilazione + test nella cartella di verifica
|
||||
dotnet run --project src/Encelado.Server -- [--no-autostart] [--sample] [--port 8080] [--minutes 240] [--bonifica] [--confirm-live "CONFERMO LIVE"] # server locale; comandi da tastiera: status, close, kill, residuo, preset, reset, bonifica, stop
|
||||
scripts\run-dev.ps1 [-Sample] [-Autostart] # server locale nella sandbox deploy/local (= F5 in VS Code); .sh per Git Bash
|
||||
scripts\run-docker.ps1 [-Down] # il container dalla compose, stessa sandbox, token "sviluppo"
|
||||
scripts\screenshots.ps1 # docs/img dal server campione
|
||||
dotnet run --project tools/Encelado.Backtest -- ticks --data "A:\Download\Trading" --out "%USERPROFILE%\Documents\Encelado\data\market"
|
||||
dotnet run --project tools/Encelado.Backtest -- baskets --data "%USERPROFILE%\Documents\Encelado\data\market" --out results
|
||||
dotnet run --project tools/Encelado.Backtest -- falsify --data "%USERPROFILE%\Documents\Encelado\data\market" --out reports [--costs api]
|
||||
|
||||
@@ -30,18 +30,27 @@ Senza token il server ascolta solo su localhost del container. La modalità `Liv
|
||||
|  |  |
|
||||
| **Impostazioni**: configurazione validata, chiavi eToro cifrate, ripristino in cinque passi, Ricerca (modello in ombra, ciclo di apprendimento), Diagnostica (percorsi, quote, bonifica), Informazioni (versione, build, commit). | Sotto i 600 px il rail diventa un menu e i basket diventano cards. |
|
||||
|
||||
## Sviluppo
|
||||
## Provarlo in locale
|
||||
|
||||
Tre modi, tutti sulla stessa sandbox `deploy/local/` (ignorata da git: `config/` è il `/config` del container, `data/` il `/data`), così configurazione e chiavi salvate valgono ovunque:
|
||||
|
||||
| Modo | Come | Cosa ottieni |
|
||||
|---|---|---|
|
||||
| **F5 in VS Code** | configurazione `Encelado (server)` | server locale su `http://localhost:8080/`, browser aperto da solo, motore fermo finché non premi **AVVIA**; `Encelado (campione)` mostra le pagine con dati finti senza chiavi |
|
||||
| **Script** | `scripts\run-dev.ps1` (`-Sample`, `-Autostart`, `-Port`) o `sh scripts/run-dev.sh` | lo stesso processo da terminale, con i comandi da tastiera (`status`, `kill`, `stop`…) |
|
||||
| **Container** | `scripts\run-docker.ps1` (o `sh scripts/run-docker.sh`, o `docker compose up -d --build`) | l'immagine vera su `http://localhost:8080/`, token `sviluppo`; `-Down` per fermare con SIGTERM |
|
||||
|
||||
Le chiavi eToro: variabili `ETORO_API_KEY`/`ETORO_USER_KEY` nell'ambiente (per il compose in un file `.env` accanto alla `docker-compose.yml`), oppure **Impostazioni ▸ Chiavi eToro** nella pagina con `ENCELADO_KEY_PASSPHRASE` impostata (vengono verificate e salvate cifrate in `deploy/local/config/etoro.keys.enc`). Senza chiavi il motore non parte e la pagina lo dice; lo storico mostra solo il ledger.
|
||||
|
||||
```powershell
|
||||
dotnet build Encelado.slnx # compilazione
|
||||
dotnet test tests/Encelado.Tests --no-restore # test (xunit)
|
||||
dotnet run --project src/Encelado.Server -- --no-autostart # server locale (Documenti\Encelado), poi http://localhost:8080/
|
||||
dotnet run --project src/Encelado.Server -- --sample # interfaccia con dati finti, senza chiavi
|
||||
dotnet msbuild build/Release.proj -t:Verifica # compilazione + test nella cartella di verifica
|
||||
dotnet msbuild build/Release.proj -t:Docker # l'immagine, con i test dentro la build
|
||||
scripts\screenshots.ps1 # rigenera docs/img dal server campione
|
||||
```
|
||||
|
||||
In VS Code: **F5** (`Encelado (server)` apre il browser da solo; `Encelado (campione)` per lavorare sulle pagine), attività `verifica`, `backtest`, `immagine docker`, `pacchetto`, `rilascia su Gitea`.
|
||||
Attività di VS Code: `verifica`, `backtest`, `avvia in locale (script)`, `avvia in docker`, `ferma docker`, `screenshot`, `immagine docker`, `pacchetto`, `rilascia su Gitea`.
|
||||
|
||||
```
|
||||
src/Encelado.Core logica pura: basket, cross sintetici, decisore, cost gate, sizing, esecutore, registro ordini, kill-switch, apprendimento, notifiche
|
||||
@@ -50,7 +59,8 @@ src/Encelado.Engine motore, ledger, feed, configurazione, impostazioni, stori
|
||||
src/Encelado.Server l'eseguibile: Kestrel, API JSON, SSE, interfaccia incorporata (Material 3, vanilla)
|
||||
tools/Encelado.Backtest ticks, baskets, falsify, learn
|
||||
tests/Encelado.Tests xunit
|
||||
deploy/ docker (entrypoint), unraid (template), local (ignorato: volumi del compose)
|
||||
deploy/ docker (entrypoint), unraid (template), local (ignorato: sandbox di F5 e del compose)
|
||||
scripts/ run-dev, run-docker, screenshots (.ps1 e .sh)
|
||||
build/ catena di verifica, pacchetto e rilascio (MSBuild)
|
||||
docs/ STATE, ARCHITECTURE, STRATEGY, RUNBOOK, DOCKER, UI_GUIDELINES, ADR…
|
||||
```
|
||||
|
||||
@@ -1,10 +1,11 @@
|
||||
# Encelado in locale con Docker Compose: immagine costruita dalla Dockerfile alla
|
||||
# radice, cartelle di configurazione e dati sotto deploy/local (ignorate da git).
|
||||
#
|
||||
# docker compose up --build # costruisce e avvia, log a video
|
||||
# docker compose up -d # in background
|
||||
# docker compose up --build # costruisce e avvia, log a video (Ctrl+C = stop pulito)
|
||||
# docker compose up -d --build # in background → http://localhost:8080/ (token "sviluppo")
|
||||
# docker compose logs -f encelado # segue il log
|
||||
# docker compose down # ferma (SIGTERM: stop pulito del motore)
|
||||
# Oppure: scripts/run-docker.ps1 (o .sh), che fa le stesse cose e stampa l'indirizzo.
|
||||
#
|
||||
# Le chiavi eToro e il token Telegram NON stanno qui: mettili in un file .env
|
||||
# accanto a questo (ignorato da git) con ETORO_API_KEY=…, ETORO_USER_KEY=…,
|
||||
@@ -38,8 +39,9 @@ services:
|
||||
ENCELADO_CONFIRM_LIVE: ${ENCELADO_CONFIRM_LIVE:-}
|
||||
ENCELADO_DISPLAY_CURRENCY: ${ENCELADO_DISPLAY_CURRENCY:-USD}
|
||||
# Senza token l'interfaccia è raggiungibile solo da localhost del container:
|
||||
# per usarla dal browser il token serve.
|
||||
ENCELADO_WEB_TOKEN: ${ENCELADO_WEB_TOKEN:-}
|
||||
# per usarla dal browser il token serve. "sviluppo" è il default delle prove
|
||||
# in locale; in .env mettine uno vero.
|
||||
ENCELADO_WEB_TOKEN: ${ENCELADO_WEB_TOKEN:-sviluppo}
|
||||
ENCELADO_KEY_PASSPHRASE: ${ENCELADO_KEY_PASSPHRASE:-}
|
||||
ETORO_API_KEY: ${ETORO_API_KEY:-}
|
||||
ETORO_USER_KEY: ${ETORO_USER_KEY:-}
|
||||
|
||||
@@ -67,6 +67,16 @@ Il server ascolta su tutte le interfacce **solo** quando `ENCELADO_WEB_TOKEN` è
|
||||
|
||||
Il Gitea dell'utente è raggiunto in HTTP: chi fa `docker pull` deve dichiararlo `insecure-registry` (Docker Desktop: *Settings ▸ Docker Engine* → `"insecure-registries": ["192.168.30.23:3000"]`; Unraid: vedi `deploy/unraid/README.md`). Il push dalla catena di rilascio (`-t:Rilascia`) fa `docker login` con il token di `build/gitea.json` e `docker logout` subito dopo.
|
||||
|
||||
## Esecuzione senza container
|
||||
## Prove in locale: la sandbox `deploy/local`
|
||||
|
||||
Sconsigliata e non supportata in produzione, ma utile per lo sviluppo: F5 in VS Code (`Encelado (server)`) o `dotnet run --project src/Encelado.Server -- --no-autostart`. Configurazione e dati vanno in `Documenti\Encelado` (o `ENCELADO_CONFIG_DIR`). `Encelado (campione)` avvia il server con `--sample`: snapshot finto, nessuna chiave, per lavorare sulle pagine e fotografarle.
|
||||
`deploy/local/config` e `deploy/local/data` (ignorate da git) sono il `/config` e il `/data` delle prove sul PC, in tutti e tre i modi di avvio:
|
||||
|
||||
| Modo | Comando | Note |
|
||||
|---|---|---|
|
||||
| F5 in VS Code | `Encelado (server)` | processo locale, `ENCELADO_CONFIG_DIR`/`ENCELADO_DATA_DIR` puntati alla sandbox, browser aperto da solo, motore fermo finché non premi AVVIA (senza token: solo localhost, nessun login) |
|
||||
| script | `scripts\run-dev.ps1` / `sh scripts/run-dev.sh` (`--sample`, `--autostart`) | come F5 ma da terminale, con i comandi da tastiera |
|
||||
| container | `scripts\run-docker.ps1` / `sh scripts/run-docker.sh` / `docker compose up -d --build` | l'immagine vera; token `sviluppo` salvo `ENCELADO_WEB_TOKEN` in `.env`; `--down` ferma con SIGTERM |
|
||||
|
||||
`ENCELADO_DATA_DIR` vale anche fuori dal container: separa i dati dalla configurazione come fa `/data`. Le chiavi salvate da Impostazioni finiscono in `deploy/local/config/etoro.keys.enc` e valgono per F5 e per il container (stessa `ENCELADO_KEY_PASSPHRASE`). `Encelado (campione)` e `--sample` servono lo snapshot finto: nessuna chiave, per lavorare sulle pagine e fotografarle (`scripts\screenshots.ps1`).
|
||||
|
||||
Fuori dalla sandbox, senza variabili, il processo locale usa `Documenti\Encelado`: sconsigliato, è la cartella dell'installazione 4.0.0.
|
||||
|
||||
@@ -12,6 +12,10 @@ Aggiornato: 2026-09-23 (5.0). Come si avvia, si ferma, si sblocca e si ripara il
|
||||
|
||||
Per Telegram: `TELEGRAM_BOT_TOKEN` e `TELEGRAM_CHAT_ID` (vedi la sezione Telegram).
|
||||
|
||||
## Sviluppo sul PC
|
||||
|
||||
Per vedere e provare il bot senza Unraid: **F5** in VS Code (`Encelado (server)`), `scripts\run-dev.ps1` o `scripts\run-docker.ps1` (il container dalla compose). Tutti usano la sandbox `deploy/local/` come `/config` e `/data`; dettagli in `docs/DOCKER.md`. Con `--sample` (o `Encelado (campione)`) la pagina mostra dati finti senza chiavi.
|
||||
|
||||
## Modalità
|
||||
|
||||
| Modalità | Ordini | Conferma all'avvio |
|
||||
|
||||
@@ -10,22 +10,21 @@ Aggiornato: 2026-09-23 (sessione 5.0, Fasi 6-9 concluse: il piano 5.0 è complet
|
||||
|
||||
- **Fase 6 — Engine + Server**: `src/Encelado.Bot` → `src/Encelado.Engine` (libreria, senza UI); `src/Encelado.Server` (Sdk.Web, nessun NuGet) con `WebHost` (token in cookie HttpOnly o Bearer, senza token solo localhost), API JSON scritta a mano, SSE `/api/stream` a uno snapshot al secondo, `LogBuffer`, `HistoryService`; chiavi da variabili o file cifrato `etoro.keys.enc` (AES-256-GCM, `ENCELADO_KEY_PASSPHRASE`) al posto di DPAPI; `AppPaths` con `/config` e `/data`; `HeadlessRunner` ridotto a console (stato, comandi, bonifica); `--sample`, `--health`, `--no-autostart`, `ENCELADO_AUTOSTART`, `ENCELADO_CONFIRM_LIVE`.
|
||||
- **Fase 7 — interfaccia web Material 3** (`Web/wwwroot`, vanilla): navigation rail 80/256 px (drawer sotto 600 px), barra con orologi UTC e fuso, selettore della valuta (USD, EUR, GBP, CHF, JPY, AUD, CAD, NZD con tasso dalle quotazioni e tooltip in USD), AVVIA/FERMA, KILL-SWITCH con «chiudi anche le esterne»; dashboard con sei KPI (incluso il margine), tabella dei basket (cards da telefono), card evento/eToro/Telegram, attività, banner; **Storico ordini** (ordini, posizioni classificate, profitti per periodo con curva SVG, CSV); Log con filtri; Impostazioni con configurazione validata, chiavi eToro, ripristino in cinque passi, Ricerca, Diagnostica (con bonifica), Informazioni (versione, build, commit da `AssemblyMetadata`); temi scuro/chiaro. `HistoryBuilder` nel Core; `SnapshotJson`, `SampleSnapshot`, `SettingsService` nell'Engine; comando `learn` nello strumento (`Learn.cs`); `ui.displayCurrency/theme/navExpanded` e coppie di conversione nel polling.
|
||||
- **Fase 8 — Docker e Unraid**: `Dockerfile` multi-stage (build, **test**, publish, runtime `aspnet:10.0` + gosu + tzdata), `deploy/docker/entrypoint.sh` (TZ, PUID/PGID), `docker-compose.yml`, `.dockerignore`, `HEALTHCHECK --health`; `deploy/unraid/encelado.xml` (Container v2, icona `assets/encelado.png` raw da Gitea, D-29) e README; `build/Release.proj` senza Inno Setup: `Pubblica` framework-dependent, `Docker`, `Pacchetto` (zip + template + tag), `Rilascia` (push dell'immagine sul registro di Gitea + release); VS Code con F5 sul server (`Encelado (server)`, `Encelado (campione)`, backtest) e attività Docker. Immagine `encelado:dev` costruita in locale con i test verdi nello stadio Linux e provata (avvio, semina di `/config`, permessi 99:100, `/api/health`, token, healthcheck). Il primo arresto con `docker stop` **non** era pulito (SIGKILL dopo la grazia): corretto con `PosixSignalRegistration` per SIGTERM in `Program.cs`; la correzione compila ed è committata ma **non è stata riprovata nel container** perché il disco C: del PC si è riempito (0 byte liberi) e il demone Docker ha iniziato a dare errori di I/O.
|
||||
- **Fase 8 — Docker e Unraid**: `Dockerfile` multi-stage (build, **test**, publish, runtime `aspnet:10.0` + gosu + tzdata), `deploy/docker/entrypoint.sh` (TZ, PUID/PGID), `docker-compose.yml`, `.dockerignore`, `HEALTHCHECK --health`; `deploy/unraid/encelado.xml` (Container v2, icona `assets/encelado.png` raw da Gitea, D-29) e README; `build/Release.proj` senza Inno Setup: `Pubblica` framework-dependent, `Docker`, `Pacchetto` (zip + template + tag), `Rilascia` (push dell'immagine sul registro di Gitea + release); VS Code con F5 sul server (`Encelado (server)`, `Encelado (campione)`, backtest) e attività Docker. Immagine `192.168.30.23:3000/alby96/encelado:5.0.0` (346 MB) costruita dalla catena (`-t:Docker -p:Versione=5.0.0`, senza tag) con 210 test verdi nello stadio Linux e provata: avvio, semina di `/config`, permessi 99:100, `/api/health`, token, sonda `--health`, nessun avviso di Kestrel o di `useradd`. Il primo arresto con `docker stop` **non** era pulito (SIGKILL dopo la grazia): corretto con `PosixSignalRegistration` per SIGTERM in `Program.cs` e **verificato**: `docker stop` chiude in 1 s con «SIGTERM: arresto» ed exit 0.
|
||||
- **Avvio in locale (richiesta dell'utente)**: sandbox `deploy/local/` (`config/` = `/config`, `data/` = `/data`, ignorata da git) usata da F5 (`Encelado (server)`, `ENCELADO_CONFIG_DIR` + `ENCELADO_DATA_DIR`, che ora vale anche fuori dal container), dagli script `scripts/run-dev.ps1|.sh`, `run-docker.ps1|.sh` (compose, token `sviluppo`), `screenshots.ps1|.sh` e dalle attività di VS Code (`avvia in locale (script)`, `avvia in docker`, `ferma docker`, `screenshot`); provati tutti e tre i modi e lo script degli screenshot. `/api/health` in `--sample` non finge più un motore acceso.
|
||||
- **Fase 9 — documenti e pulizia**: ADR-0007, ADR-0008, `docs/DOCKER.md`, `docs/UI_GUIDELINES.md`, `README.md` con gli screenshot (`docs/img/`), `CLAUDE.md`, ARCHITECTURE, RUNBOOK, KNOWN_ISSUES, GLOSSARY, DATA_SOURCES, ML_AND_LEARNING, QUESTIONS (D-28, D-29, D-36, D-37 risposte), POSTMORTEM (gli «accrediti» erano crediti demo per ogni ordine ridotto), skill aggiornate, `build/README.md`. **Codice morto rimosso**: progetto WPF, DPAPI, test WPF, `UiRenderTests`, ~45 membri mai usati (statistiche di ricerca, `Psi`, `EmptyContextProvider`, `EnvironmentKeyStore`, display helper dello snapshot, `INotifyPropertyChanged` in `SettingField`, …). Test nuovi: `EmbeddedUiTests`, `WebHostTests`, `HistoryBuilderTests`, `KeyStoreTests`; suite a **210 verdi**.
|
||||
|
||||
## Prossimi passi
|
||||
|
||||
1. Liberare spazio su C:, riavviare Docker, ricostruire l'immagine e verificare l'arresto pulito con `docker stop` (vedi problemi aperti).
|
||||
2. **Rilascio 5.0.0** (utente): push del ramo, `-t:Rilascia -p:Versione=5.0.0` (richiede Docker acceso, `build/gitea.json` con permesso `package`, registro `192.168.30.23:3000` dichiarato `insecure-registry`), poi installazione su Unraid dal template.
|
||||
3. Riaccendere il Demo nel container per 24 ore di verifica: contatore «orfane» a 0, `orders.jsonl` senza `Unknown` irrisolti, `sizing_bound = margin` sui primi ingressi; kill-switch a mano con verifica di piattezza; fermare il container un'ora con un basket aperto per vedere il recupero; messaggi Telegram reali.
|
||||
4. Revisione visiva dell'interfaccia con l'utente (gli screenshot sono dal campione `--sample`); eventuali ritocchi ai testi e ai tooltip.
|
||||
5. Avviso Telegram sui feed fermi da più di un'ora (resta in KNOWN_ISSUES).
|
||||
6. Rifare il backtest (`backtest baskets`) con i limiti di margine e aggiornare §3 di `docs/STRATEGY.md`.
|
||||
1. **Rilascio 5.0.0** (utente): push del ramo, `-t:Rilascia -p:Versione=5.0.0` (richiede Docker acceso, `build/gitea.json` con permesso `package`, registro `192.168.30.23:3000` dichiarato `insecure-registry`), poi installazione su Unraid dal template.
|
||||
2. Riaccendere il Demo nel container per 24 ore di verifica: contatore «orfane» a 0, `orders.jsonl` senza `Unknown` irrisolti, `sizing_bound = margin` sui primi ingressi; kill-switch a mano con verifica di piattezza; fermare il container un'ora con un basket aperto per vedere il recupero; messaggi Telegram reali.
|
||||
3. Revisione visiva dell'interfaccia con l'utente (gli screenshot sono dal campione `--sample`); eventuali ritocchi ai testi e ai tooltip.
|
||||
4. Avviso Telegram sui feed fermi da più di un'ora (resta in KNOWN_ISSUES).
|
||||
5. Rifare il backtest (`backtest baskets`) con i limiti di margine e aggiornare §3 di `docs/STRATEGY.md`.
|
||||
|
||||
## Problemi aperti
|
||||
|
||||
- **Il disco C: del PC di sviluppo è pieno** (0 byte liberi a fine sessione): Docker Desktop ha dato errori di I/O e l'ultima immagine non si è costruita. Liberare spazio, riavviare Docker Desktop, rifare `dotnet msbuild build/Release.proj -t:Docker` e riprovare `docker stop` (deve loggare «SIGTERM: arresto» e uscire con 0).
|
||||
|
||||
- Il disco C: del PC di sviluppo si era riempito (0 byte) a metà sessione; ora ha 12 GB liberi. Le immagini e la cache di build di Docker pesano: `docker system prune` ogni tanto.
|
||||
- Backtest negativo: la strategia non regge i costi (`docs/STRATEGY.md`).
|
||||
- Il conto reale vale 224,90 USD: il Live non è praticabile.
|
||||
- Google News blocca le ricerche RSS; RBA 403 a intermittenza; Fed 404 a tratti.
|
||||
|
||||
@@ -55,8 +55,8 @@ Angoli 12 px (card, banner), 16 px (dialoghi grandi 28 px come da M3), pulsanti
|
||||
| Classe | Larghezza | Layout |
|
||||
|---|---|---|
|
||||
| compact | < 600 px | rail nascosto (menu ☰), KPI su due colonne, basket a cards, orologi nascosti |
|
||||
| medium | 600-839 px | rail 80 px, KPI su tre colonne, contesto su una colonna, impostazioni su una colonna |
|
||||
| expanded | ≥ 840 px | rail 80/256 px, KPI su sei colonne, contesto su tre, impostazioni su due |
|
||||
| medium | 600-839 px | rail 80 px, KPI su due colonne, contesto su una colonna, impostazioni su una colonna |
|
||||
| expanded | ≥ 840 px | rail 80/256 px, KPI su tre colonne (due righe), contesto su tre, impostazioni su due |
|
||||
|
||||
### Accessibilità
|
||||
|
||||
@@ -76,7 +76,7 @@ Tastiera completa (Tab su rail, pulsanti, tabelle, dialoghi; Esc chiude drawer e
|
||||
|
||||
## Screenshot
|
||||
|
||||
`docs/img/` (dashboard, storico, log, impostazioni, mobile), presi dal server in modalità campione:
|
||||
`docs/img/` (dashboard, storico, log, impostazioni, mobile), presi dal server in modalità campione con `scripts\screenshots.ps1` (o `sh scripts/screenshots.sh`): avvia il server con `--sample` su una porta libera, apre ogni pagina con Edge headless (`?once=1`, 1440×900 e 420×860) e lo ferma. A mano, una pagina:
|
||||
|
||||
```powershell
|
||||
dotnet run --project src/Encelado.Server -- --sample --port 8085
|
||||
|
||||
|
Before Width: | Height: | Size: 125 KiB After Width: | Height: | Size: 126 KiB |
|
Before Width: | Height: | Size: 131 KiB After Width: | Height: | Size: 131 KiB |
|
Before Width: | Height: | Size: 53 KiB After Width: | Height: | Size: 53 KiB |
|
Before Width: | Height: | Size: 52 KiB After Width: | Height: | Size: 52 KiB |
@@ -0,0 +1,42 @@
|
||||
<#
|
||||
.SYNOPSIS
|
||||
Compila e avvia Encelado in locale, senza container, nella sandbox deploy/local.
|
||||
|
||||
.DESCRIPTION
|
||||
Stessa disposizione del container: deploy/local/config (encelado.json, strategy.json,
|
||||
chiavi cifrate) e deploy/local/data (ledger, stato, log). La cartella è ignorata da git.
|
||||
Senza ENCELADO_WEB_TOKEN il server ascolta solo su localhost e non chiede il login.
|
||||
Le chiavi eToro: variabili ETORO_API_KEY/ETORO_USER_KEY già nell'ambiente, oppure
|
||||
Impostazioni ▸ Chiavi eToro con ENCELADO_KEY_PASSPHRASE impostata.
|
||||
|
||||
scripts\run-dev.ps1 # motore fermo: premi AVVIA nella pagina
|
||||
scripts\run-dev.ps1 -Autostart # motore avviato subito (servono le chiavi)
|
||||
scripts\run-dev.ps1 -Sample # dati finti, nessuna chiave: per guardare le pagine
|
||||
scripts\run-dev.ps1 -Port 8090
|
||||
#>
|
||||
param(
|
||||
[int] $Port = 8080,
|
||||
[switch] $Autostart,
|
||||
[switch] $Sample,
|
||||
[switch] $NoBrowser
|
||||
)
|
||||
$ErrorActionPreference = "Stop"
|
||||
$root = Split-Path -Parent $PSScriptRoot
|
||||
Set-Location $root
|
||||
|
||||
$env:ENCELADO_CONFIG_DIR = Join-Path $root "deploy\local\config"
|
||||
$env:ENCELADO_DATA_DIR = Join-Path $root "deploy\local\data"
|
||||
$env:ENCELADO_WEB_PORT = "$Port"
|
||||
New-Item -ItemType Directory -Force $env:ENCELADO_CONFIG_DIR, $env:ENCELADO_DATA_DIR | Out-Null
|
||||
|
||||
dotnet build src/Encelado.Server -c Debug -nologo -v q
|
||||
if ($LASTEXITCODE -ne 0) { exit $LASTEXITCODE }
|
||||
|
||||
$args = @()
|
||||
if ($Sample) { $args += "--sample" } elseif (-not $Autostart) { $args += "--no-autostart" }
|
||||
$url = "http://localhost:$Port/"
|
||||
Write-Host "Encelado in locale: $url (configurazione in deploy\local\config, dati in deploy\local\data)" -ForegroundColor Cyan
|
||||
Write-Host "Comandi da tastiera: status, close <basket>, kill, residuo, preset <nome>, reset <motivazione>, bonifica, stop. Ctrl+C = arresto." -ForegroundColor DarkGray
|
||||
if (-not $NoBrowser) { Start-Job -ScriptBlock { param($u) Start-Sleep -Seconds 3; Start-Process $u } -ArgumentList $url | Out-Null }
|
||||
|
||||
dotnet src/Encelado.Server/bin/Debug/net10.0/Encelado.Server.dll @args
|
||||
@@ -0,0 +1,25 @@
|
||||
#!/usr/bin/env sh
|
||||
# Builds and starts Encelado locally, without the container, in the deploy/local sandbox
|
||||
# (deploy/local/config as /config, deploy/local/data as /data; ignored by git).
|
||||
# sh scripts/run-dev.sh # engine stopped: press AVVIA in the page
|
||||
# sh scripts/run-dev.sh --sample # fake data, no keys: to look at the pages
|
||||
# sh scripts/run-dev.sh --autostart # engine started at once (keys needed)
|
||||
# Without ENCELADO_WEB_TOKEN the server listens on localhost only and asks no login.
|
||||
set -eu
|
||||
cd "$(dirname "$0")/.."
|
||||
export ENCELADO_CONFIG_DIR="$(pwd)/deploy/local/config"
|
||||
export ENCELADO_DATA_DIR="$(pwd)/deploy/local/data"
|
||||
export ENCELADO_WEB_PORT="${ENCELADO_WEB_PORT:-8080}"
|
||||
mkdir -p "$ENCELADO_CONFIG_DIR" "$ENCELADO_DATA_DIR"
|
||||
|
||||
MODE="--no-autostart"
|
||||
for a in "$@"; do
|
||||
case "$a" in
|
||||
--sample) MODE="--sample" ;;
|
||||
--autostart) MODE="" ;;
|
||||
esac
|
||||
done
|
||||
|
||||
dotnet build src/Encelado.Server -c Debug -nologo -v q
|
||||
echo "Encelado in locale: http://localhost:$ENCELADO_WEB_PORT/ (config in deploy/local/config, dati in deploy/local/data)"
|
||||
exec dotnet src/Encelado.Server/bin/Debug/net10.0/Encelado.Server.dll $MODE
|
||||
@@ -0,0 +1,40 @@
|
||||
<#
|
||||
.SYNOPSIS
|
||||
Costruisce l'immagine e avvia Encelado nel container con la docker-compose.yml alla radice.
|
||||
|
||||
.DESCRIPTION
|
||||
Le cartelle deploy/local/config e deploy/local/data vengono montate come /config e /data:
|
||||
sono le stesse di scripts\run-dev.ps1, quindi configurazione e chiavi salvate valgono
|
||||
in entrambi i modi. Il token dell'interfaccia è "sviluppo" salvo ENCELADO_WEB_TOKEN in
|
||||
un file .env accanto alla compose (dove vanno anche ETORO_API_KEY, ETORO_USER_KEY,
|
||||
TELEGRAM_*). Segue il log finché non premi Ctrl+C; il container resta acceso:
|
||||
scripts\run-docker.ps1 -Down per fermarlo (SIGTERM, stop pulito).
|
||||
|
||||
scripts\run-docker.ps1 # build + up -d + log
|
||||
scripts\run-docker.ps1 -NoBuild # senza ricostruire l'immagine
|
||||
scripts\run-docker.ps1 -Down # docker compose down
|
||||
#>
|
||||
param(
|
||||
[switch] $NoBuild,
|
||||
[switch] $Down,
|
||||
[switch] $NoBrowser
|
||||
)
|
||||
$ErrorActionPreference = "Stop"
|
||||
$root = Split-Path -Parent $PSScriptRoot
|
||||
Set-Location $root
|
||||
|
||||
if ($Down) { docker compose down; exit $LASTEXITCODE }
|
||||
|
||||
New-Item -ItemType Directory -Force "deploy\local\config", "deploy\local\data" | Out-Null
|
||||
$env:GIT_COMMIT = (git rev-parse --short=12 HEAD 2>$null); if (-not $env:GIT_COMMIT) { $env:GIT_COMMIT = "n/d" }
|
||||
$env:PUID = "1000"; $env:PGID = "1000"
|
||||
|
||||
if ($NoBuild) { docker compose up -d } else { docker compose up -d --build }
|
||||
if ($LASTEXITCODE -ne 0) { exit $LASTEXITCODE }
|
||||
|
||||
$port = if ($env:ENCELADO_WEB_PORT) { $env:ENCELADO_WEB_PORT } else { "8080" }
|
||||
$token = if ($env:ENCELADO_WEB_TOKEN) { "(da ENCELADO_WEB_TOKEN)" } else { "sviluppo" }
|
||||
$url = "http://localhost:$port/"
|
||||
Write-Host "Encelado nel container: $url token: $token (docker compose down per fermare)" -ForegroundColor Cyan
|
||||
if (-not $NoBrowser) { Start-Job -ScriptBlock { param($u) Start-Sleep -Seconds 4; Start-Process $u } -ArgumentList $url | Out-Null }
|
||||
docker compose logs -f encelado
|
||||
@@ -0,0 +1,18 @@
|
||||
#!/usr/bin/env sh
|
||||
# Builds the image and starts Encelado in the container with the docker-compose.yml at the root.
|
||||
# deploy/local/config and deploy/local/data are mounted as /config and /data (the same
|
||||
# folders scripts/run-dev.sh uses). Token "sviluppo" unless ENCELADO_WEB_TOKEN is in .env.
|
||||
# sh scripts/run-docker.sh # build + up -d + follow the log (Ctrl+C leaves it running)
|
||||
# sh scripts/run-docker.sh --no-build
|
||||
# sh scripts/run-docker.sh --down # docker compose down (SIGTERM, clean stop)
|
||||
set -eu
|
||||
cd "$(dirname "$0")/.."
|
||||
case "${1:-}" in
|
||||
--down) exec docker compose down ;;
|
||||
esac
|
||||
mkdir -p deploy/local/config deploy/local/data
|
||||
export GIT_COMMIT="$(git rev-parse --short=12 HEAD 2>/dev/null || echo n/d)"
|
||||
export PUID="${PUID:-1000}" PGID="${PGID:-1000}"
|
||||
if [ "${1:-}" = "--no-build" ]; then docker compose up -d; else docker compose up -d --build; fi
|
||||
echo "Encelado nel container: http://localhost:${ENCELADO_WEB_PORT:-8080}/ token: ${ENCELADO_WEB_TOKEN:-sviluppo} (sh scripts/run-docker.sh --down per fermare)"
|
||||
exec docker compose logs -f encelado
|
||||
@@ -0,0 +1,47 @@
|
||||
<#
|
||||
.SYNOPSIS
|
||||
Rigenera docs/img/*.png: server in modalità campione (--sample) e Edge headless su ogni pagina.
|
||||
|
||||
.DESCRIPTION
|
||||
Niente chiavi, niente mercato: lo snapshot è finto (SampleSnapshot). Serve Microsoft Edge.
|
||||
La pagina viene aperta con ?once=1 (una sola lettura dello snapshot, nessuno stream), altrimenti
|
||||
il browser headless non considera mai il caricamento concluso.
|
||||
|
||||
scripts\screenshots.ps1 [-Port 58085]
|
||||
#>
|
||||
param([int] $Port = 58085)
|
||||
$ErrorActionPreference = "Stop"
|
||||
$root = Split-Path -Parent $PSScriptRoot
|
||||
Set-Location $root
|
||||
$edge = @("$env:ProgramFiles (x86)\Microsoft\Edge\Application\msedge.exe", "$env:ProgramFiles\Microsoft\Edge\Application\msedge.exe") | Where-Object { Test-Path $_ } | Select-Object -First 1
|
||||
if (-not $edge) { throw "Microsoft Edge non trovato" }
|
||||
|
||||
dotnet build src/Encelado.Server -c Debug -nologo -v q
|
||||
$work = Join-Path $env:TEMP ("encelado-shot-" + [guid]::NewGuid().ToString("N"))
|
||||
New-Item -ItemType Directory -Force "$work\config", "docs\img" | Out-Null
|
||||
$env:ENCELADO_CONFIG_DIR = "$work\config"
|
||||
$server = Start-Process -FilePath "dotnet" -ArgumentList "src/Encelado.Server/bin/Debug/net10.0/Encelado.Server.dll", "--sample", "--port", "$Port" -PassThru -WindowStyle Hidden
|
||||
try {
|
||||
$ready = $false
|
||||
for ($i = 0; $i -lt 30 -and -not $ready; $i++) { Start-Sleep -Seconds 1; try { $ready = (Invoke-WebRequest -UseBasicParsing "http://127.0.0.1:$Port/api/health" -TimeoutSec 2).StatusCode -eq 200 } catch {} }
|
||||
if (-not $ready) { throw "il server campione non risponde sulla porta $Port" }
|
||||
$shots = @(
|
||||
@{ name = "dashboard"; page = "dashboard"; size = "1440,900" },
|
||||
@{ name = "storico"; page = "storico"; size = "1440,900" },
|
||||
@{ name = "log"; page = "log"; size = "1440,900" },
|
||||
@{ name = "impostazioni"; page = "impostazioni"; size = "1440,900" },
|
||||
@{ name = "mobile"; page = "dashboard"; size = "420,860" }
|
||||
)
|
||||
foreach ($s in $shots) {
|
||||
$out = Join-Path $root ("docs\img\" + $s.name + ".png")
|
||||
$profile = Join-Path $work ("edge-" + $s.name)
|
||||
# Start-Process: Edge scrive avvisi su stderr e in PowerShell 5.1 diventerebbero errori bloccanti.
|
||||
$edgeArgs = @("--headless=new", "--disable-gpu", "--no-first-run", "--hide-scrollbars", "--user-data-dir=$profile", "--window-size=$($s.size)", "--virtual-time-budget=5000", "--screenshot=$out", "http://127.0.0.1:$Port/?once=1#/$($s.page)")
|
||||
Start-Process -FilePath $edge -ArgumentList $edgeArgs -Wait -NoNewWindow -RedirectStandardError "$profile.err" -RedirectStandardOutput "$profile.out"
|
||||
if (Test-Path $out) { Write-Host (" {0,-14} {1,7:N0} byte" -f $s.name, (Get-Item $out).Length) } else { Write-Warning "$($s.name): screenshot non scritto" }
|
||||
}
|
||||
}
|
||||
finally {
|
||||
if ($server -and -not $server.HasExited) { Stop-Process -Id $server.Id -Force }
|
||||
Remove-Item $work -Recurse -Force -ErrorAction SilentlyContinue
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
#!/usr/bin/env sh
|
||||
# Regenerates docs/img/*.png: the server in sample mode (--sample) and a headless Edge/Chrome on every page.
|
||||
# No keys, no market: the snapshot is fake. Pages are opened with ?once=1 (one snapshot, no stream),
|
||||
# otherwise the headless browser never considers the page loaded.
|
||||
# sh scripts/screenshots.sh [port]
|
||||
set -eu
|
||||
cd "$(dirname "$0")/.."
|
||||
PORT="${1:-58085}"
|
||||
BROWSER="${ENCELADO_BROWSER:-}"
|
||||
for c in "/c/Program Files (x86)/Microsoft/Edge/Application/msedge.exe" "/c/Program Files/Microsoft/Edge/Application/msedge.exe" "$(command -v microsoft-edge || true)" "$(command -v google-chrome || true)" "$(command -v chromium || true)"; do
|
||||
[ -n "$BROWSER" ] && break
|
||||
[ -n "$c" ] && [ -x "$c" ] && BROWSER="$c"
|
||||
done
|
||||
[ -n "$BROWSER" ] || { echo "nessun browser Chromium trovato (imposta ENCELADO_BROWSER)" >&2; exit 1; }
|
||||
|
||||
dotnet build src/Encelado.Server -c Debug -nologo -v q
|
||||
WORK="$(mktemp -d)"
|
||||
trap '[ -n "${PID:-}" ] && kill "$PID" 2>/dev/null; rm -rf "$WORK"' EXIT
|
||||
mkdir -p "$WORK/config" docs/img
|
||||
ENCELADO_CONFIG_DIR="$WORK/config" dotnet src/Encelado.Server/bin/Debug/net10.0/Encelado.Server.dll --sample --port "$PORT" >/dev/null 2>&1 &
|
||||
PID=$!
|
||||
i=0; until curl -sf "http://127.0.0.1:$PORT/api/health" >/dev/null 2>&1; do i=$((i+1)); [ "$i" -lt 30 ] || { echo "il server campione non risponde" >&2; exit 1; }; sleep 1; done
|
||||
|
||||
shot() { # shot <name> <page> <size>
|
||||
"$BROWSER" --headless=new --disable-gpu --no-first-run --hide-scrollbars "--user-data-dir=$WORK/edge-$1" "--window-size=$3" --virtual-time-budget=5000 "--screenshot=$(pwd)/docs/img/$1.png" "http://127.0.0.1:$PORT/?once=1#/$2" >/dev/null 2>&1 || true
|
||||
[ -s "docs/img/$1.png" ] && echo " $1: $(wc -c < "docs/img/$1.png") byte" || echo " $1: screenshot non scritto" >&2
|
||||
}
|
||||
shot dashboard dashboard 1440,900
|
||||
shot storico storico 1440,900
|
||||
shot log log 1440,900
|
||||
shot impostazioni impostazioni 1440,900
|
||||
shot mobile dashboard 420,860
|
||||
@@ -9,9 +9,10 @@ namespace Encelado.Engine.Configuration;
|
||||
/// <c>instruments.json</c> and the encrypted keys; everything the bot produces
|
||||
/// (<c>data/</c>, <c>knowledge/</c>, <c>reports/</c>, <c>logs/</c>) resolves against
|
||||
/// it unless the configuration says otherwise. In the container it is <c>/config</c>
|
||||
/// (and the data folder <c>/data</c>); anywhere else it is <c>ENCELADO_CONFIG_DIR</c>,
|
||||
/// or <c>Documenti\Encelado</c> for a developer on Windows, where a backup catches it
|
||||
/// and a reinstall cannot overwrite it.
|
||||
/// (and the data folder <c>/data</c>); anywhere else it is <c>ENCELADO_CONFIG_DIR</c>
|
||||
/// (with <c>ENCELADO_DATA_DIR</c> for a separate data folder, as the local sandbox
|
||||
/// under <c>deploy/local</c> does), or <c>Documenti\Encelado</c> for a developer on
|
||||
/// Windows, where a backup catches it and a reinstall cannot overwrite it.
|
||||
/// </para>
|
||||
/// </summary>
|
||||
public static class AppPaths
|
||||
@@ -63,11 +64,15 @@ public static class AppPaths
|
||||
Config = ConfigLoader.Load(ConfigPath, out List<string> warnings);
|
||||
ConfigWarnings = warnings;
|
||||
|
||||
// In the container the data volume is separate from the configuration volume:
|
||||
// relative folders in the file resolve against /data, not /config.
|
||||
if (InContainer && string.IsNullOrEmpty(Environment.GetEnvironmentVariable("ENCELADO_CONFIG_DIR")))
|
||||
// A data folder separate from the configuration folder: /data in the container,
|
||||
// ENCELADO_DATA_DIR anywhere (the local sandbox in deploy/local mirrors the
|
||||
// container's layout, so F5 and docker compose write the same tree).
|
||||
string? dataDir = Environment.GetEnvironmentVariable("ENCELADO_DATA_DIR");
|
||||
bool separateData = !string.IsNullOrWhiteSpace(dataDir)
|
||||
|| (InContainer && string.IsNullOrEmpty(Environment.GetEnvironmentVariable("ENCELADO_CONFIG_DIR")));
|
||||
if (separateData)
|
||||
{
|
||||
string data = Environment.GetEnvironmentVariable("ENCELADO_DATA_DIR") is { Length: > 0 } d ? d : ContainerDataDirectory;
|
||||
string data = !string.IsNullOrWhiteSpace(dataDir) ? dataDir.Trim() : ContainerDataDirectory;
|
||||
Config.Run.BaseDirectory = ConfigDirectory;
|
||||
if (!Path.IsPathRooted(Config.Run.DataDirectory)) { Config.Run.DataDirectory = Path.Combine(data, Config.Run.DataDirectory); }
|
||||
if (!Path.IsPathRooted(Config.Run.KnowledgeDirectory)) { Config.Run.KnowledgeDirectory = Path.Combine(data, Config.Run.KnowledgeDirectory); }
|
||||
|
||||
@@ -597,7 +597,7 @@ public static class WebHost
|
||||
string heartbeatPath = Path.Combine(ctx.Config.Run.DataPath, "state", "heartbeat.json");
|
||||
Heartbeat? hb = HeartbeatFile.Read(heartbeatPath);
|
||||
double age = hb is null ? -1 : (DateTime.UtcNow - hb.Utc).TotalSeconds;
|
||||
bool running = s.State == BotState.Running;
|
||||
bool running = !ctx.Sample && s.State == BotState.Running; // the sample snapshot says "running" with no engine behind it
|
||||
bool heartbeatFresh = !running || (hb is not null && age < 3 * 30 + 15);
|
||||
bool disk = DiskWritable(ctx.Config.Run.DataPath);
|
||||
bool ok = disk && heartbeatFresh;
|
||||
|
||||