Build the phone audio gamebook pack (narration WAVs with spoken numbered
choices + a story menu WAV + per-book JSON + library.json) and stage it
on the PLIP SD. Bumps ESP32_ZACUS for the PLIP audio gamebook and the
master full-height scrolling nav.
Context:
The new firmware gamebook mode needs a book on the Freenove SD, plus a
reusable way to author one. We also want a playable demo to prove the
whole chain (button -> navigation -> WAV narration from SD -> screen).
Approach:
Author the book in a human-friendly YAML; a generator renders each
passage's narration with macOS say (16 kHz mono, canonical WAV — the
Apple FLLR chunk is stripped so the firmware parser decodes it),
emits a compact gamebook.json, and stages WAVs + JSON onto the master
SD via POST /game/file?path=sd/gamebook/.
Changes:
- game/gamebooks/zacus_demo.yaml: a branching mini-adventure (the
Professeur Zacus lab) — 8 passages, 3 endings.
- tools/gamebook/build_gamebook.py: reusable/idempotent generator
(--book/--ip/--sd-dir/--dry-run); ASCII-folds on-screen text (fonts
have no accents) while the WAV keeps the accented narration; choices
carry label + goto (navigation is generic, D-pad + click).
- .gitignore: ignore the generated WAV pack (tools/gamebook/build/).
- Bump ESP32_ZACUS to the gamebook-mode firmware.
Impact:
Authors can write a gamebook in YAML and stage it to the Freenove in
one command; the bundled demo plays end to end, fully offline.
Pre-render directory greetings + scene hints with macOS say (16 kHz
mono FR) and stage them on the PLIP microSD via POST /game/file — gives
the phone local voice content with no model in RAM. Reusable/idempotent
(tools/tts/generate_plip_sd_pack.py). Bumps ESP32_ZACUS for the SD write
path. Generated WAVs gitignored (reproducible).
Ajoute une barre de menu en haut de l'Atelier :
- Ouvrir : menu déroulant de 3 scénarios de départ embarqués (format
blocks_studio v2) — intro+énigme sonore, maître du jeu adaptatif,
énigmes→coffre.
- Charger / Enregistrer : import depuis / export vers un fichier .yaml local.
- Partager : publie le scénario sur la gateway et copie un lien court ?s= ;
useShareLink réimporte automatiquement le scénario au chargement du lien.
Gateway : endpoints publics sans token POST/GET /v1/share (taille plafonnée,
YAML validé), adossés à un store fichier (share-store/).
Corrige aussi l'inlining de VITE_GATEWAY_BASE_URL : accès statique
import.meta.env pour que le build prod vise la vraie gateway
(zacus.saillant.cc/api) — ce qui répare au passage le DeployPanel en prod.
Le build web prod utilise désormais un mode Vite dédié `webdeploy`
(.env.webdeploy) pour ne pas polluer le bundle desktop.
Ajoute ops/deploy-frontend.sh (typecheck → build → tar-over-ssh → reload →
vérif du hash live), le déploiement n'étant pas scripté jusqu'ici.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Problem: the LITELLM master key was committed in plain text across five
files. The .gitignore did not block .env files, leaving the door open
for future accidental commits of real credentials. Admin DELETE on the
voice-bridge cache and the hints engine sessions endpoints fall back to
no-auth when their ADMIN_KEY env is unset, with no warning to operators.
Approach: keep the placeholder default so the LAN-only dev workflow
keeps working without any breaking change, but make every operator
aware of the risk. Added gitignore patterns (.env*, *.secret,
secrets.{yaml,yml,json}, credentials.json, *-secret.*) with a
!.env.example whitelist for the template. Created
tools/macstudio/.env.example documenting the four secrets (master key,
two admin keys, CORS) and recommending ~/.zacus.env as the local store.
Voice-bridge and hints engine now log a boot WARN when the default key
or no admin key is in use. README gained a Security section listing the
LAN-only assumption, the no-auth surfaces, and a four-step
pre-deployment checklist (rotate key, source env, audit Cloudflare
tunnel routes, verify boot logs).
Tradeoffs: chose loud-warning over fail-fast because smoke runs and
tests would otherwise need the real key wired before they can boot at
all; loud warnings surface the issue without breaking dev. Did not
rewrite git history to scrub the placeholder — its value is now
treated as known-public, and any deployment outside the closed LAN
must rotate it before going live.
Local Node 25 breaks vitest/rollup ESM exports.
CI uses Node 22 which works. Pin to 22 for local
dev parity.
- Add .nvmrc pinning Node 22
- Update lockfile (minor dep bumps)
- Gitignore stale local directories
- Updated `validate_scenario.py` to handle V2 scenario YAML files, introducing new required keys and validation logic.
- Added support for legacy V1 scenarios with an option to allow migration.
- Modified `run_content_checks.sh` to validate V2 scenarios and associated audio and printables manifests.
- Introduced SceneGyrophareConfig structure for configuration parameters.
- Implemented SceneGyrophare class with methods for creating, destroying, and updating the gyrophare effect.
- Added rendering functions for background, beams, and warning triangle.
- Integrated timer for animation updates based on frame rate.
- Created a log file for ESP32 audio kit boot information.
- Refactor scenario handling in run_stress_tests.py to support multiple profiles (standard, combined_la).
- Introduce SCENE_LA_DETECTOR.json for visual feedback in the LA detector.
- Add verify_la_random_generator_test.py to generate random tones and validate LA detection.
- Implement verify_mic_live.py to validate microphone metrics over serial.
- Create verify_story_default_flow.py to check the default story screen flow on Freenove.
- Introduced a terminal test menu for Zacus tooling, allowing users to navigate options using curses or a fallback method.
- Implemented various action handlers for executing content checks, running serial suites, and managing UI links.
- Added support for user confirmation prompts and command execution with subprocess.
- Enhanced error handling for missing dependencies and invalid inputs.
- Included functionality for listing and selecting serial ports dynamically.