diff --git a/documentation/PROJECT__AE_Events_Launcher_Native_integration.md b/documentation/PROJECT__AE_Events_Launcher_Native_integration.md index 0c937496..00cd0381 100644 --- a/documentation/PROJECT__AE_Events_Launcher_Native_integration.md +++ b/documentation/PROJECT__AE_Events_Launcher_Native_integration.md @@ -9,6 +9,7 @@ The Aether Events Launcher utilizes an Electron-based "Native Shell" to provide OS-level capabilities that are normally restricted by browser sandboxing. This enables persistent file caching, direct control of presentation software (Keynote, PowerPoint), and hardware telemetry. ### Operational Modes + | Mode | Purpose | File Handling | | :--- | :--- | :--- | | **Default** | Standard web browser access. | Direct downloads; no local caching. | @@ -49,9 +50,9 @@ The integration is built on a decoupled three-layer communication model to ensur To support rapid onsite deployment, the native app requires zero manual setup. 1. **Seed:** On launch, the Main process reads a local `seed.json` (Device ID + API Key). -2. **Identity:** Calls `GET /v3/data_store/code/{device_code}` or `GET /v3/crud/event_device/{id}` to pull operational context. -3. **Hydrate:** Authenticates with the Aether V3 API and injects the **JWT** and **Device Config** into the UI environment. -4. **Launch:** Navigates the SvelteKit frontend directly to the assigned Event Launcher route. +2. **Identity:** Calls `GET /v3/crud/event_device/{id}` to pull device config and extract `app_base_url` (the event FQDN) and `account_id`. +3. **Site Context:** POSTs to `/v3/crud/site_domain/search?limit=1` with the FQDN to resolve the correct site. No JWT — auth is `x-aether-api-key` + `x-account-id` throughout. +4. **Launch:** Navigates the SvelteKit frontend directly to the assigned Event Launcher route (`/events/{eventId}/launcher/{locationId}`). --- @@ -85,11 +86,12 @@ When a user clicks "Open", the system follows a non-destructive sequence: The native shell provides specialized handlers for controlling the "Podium Experience." ### 5.1 Presentation Acts + | Action | Handler | Actuator (macOS) | | :--- | :--- | :--- | | **Launch** | `launch_presentation` | `open` or `osascript` (slideshow start) | | **Control** | `control_presentation` | `osascript` (next/prev slide) | -| **Clean Up**| `kill_processes` | `killall -INT` (graceful exit) | +| **Clean Up** | `kill_processes` | `killall -INT` (graceful exit) | ### 5.2 System Management - **Telemetry:** Pushes `cpu_usage`, `memory_free_gb`, and `foreground_app` via heartbeats using the `get_device_info` relay. @@ -142,11 +144,10 @@ no-op when `window.aetherNative` is not present (i.e., in browser/non-native mod - `get_device_info()` — Returns OS metadata, IP list, hostname, and path placeholders (`[home]`, `[tmp]`). ### File Cache -- `check_hash_file_cache({cache_root, hash, hash_prefix_length?})` — Verifies a file exists in the local hashed cache. -- `download_to_cache({url, cache_root, hash, api_key, account_id, hash_prefix_length?})` — Streams a file download to the hashed cache with SHA-256 integrity check. +- `check_cache({cache_root, hash, hash_prefix_length?, verify_hash?})` — Verifies a file exists in the local hashed cache. `verify_hash: true` re-hashes to confirm integrity. +- `download_to_cache({url, cache_root, hash, api_key, account_id, hash_prefix_length?})` — Streams a file download to the hashed cache with SHA-256 integrity check. Stale `.tmp` files (older than 5 min) from crashed downloads are cleaned up automatically on each call. - `copy_from_cache_to_temp({cache_root, hash, temp_root, filename, hash_prefix_length?})` — **Preferred primitive.** Copies a cached file to temp and returns `{ success, path }`. The Svelte caller decides what to do next (run a script, open it, etc.). - `launch_from_cache({cache_root, hash, temp_root, filename, hash_prefix_length?, script_template?})` — Combines copy + launch in one call. Uses `script_template` if provided, otherwise falls back to hardcoded extension logic. See **Configurable Launch Scripts** below. -- `cleanup_tmp_files({cache_root, max_age_minutes?})` — Removes stale `*.tmp` download artifacts. Default: 1440 min (24h). Called at launcher startup. > `hash_prefix_length` defaults to `2` throughout. Do not change without coordinating all devices — mismatched values create orphaned cache subdirectories. @@ -225,16 +226,18 @@ unrecognised extensions. } ``` -### Known Issue: `launch_presentation` vs `launch_from_cache` Inconsistency +### AppleScript Execution — All Handlers Hardened (2026-05-11) -`shell_handlers.ts` `native:launch-presentation` still uses the old `osascript -e ""` approach -for its AppleScript execution. `file_handlers.ts` `native:launch-from-cache` uses the hardened -temp-`.scpt`-file approach. These two handlers behave differently for identical file types. +All AppleScript execution in the native shell now writes scripts to a temp `.scpt` file and +runs `osascript ""` rather than the old `osascript -e ""` approach. -- **`launch_from_cache`** (used by the "Open" button in the Launcher file list) — hardened, correct. -- **`launch_presentation`** (used by `electron_relay.launch_presentation`) — legacy `-e` flag, fragile on paths with spaces or special characters. +- **`run_osascript`** — hardened (2026-05-11, earlier batch) +- **`launch_from_cache`** — hardened (same batch) +- **`launch_presentation`** — hardened (2026-05-11, follow-up fix; was the last handler still using `-e`) +- **`control_presentation`** — uses single-line scripts with no path interpolation; `-e` is safe here and retained for simplicity -**Recommendation:** `launch_presentation` should be updated to use the temp-`.scpt` approach in a future Electron build. It is not used in the primary file-open flow today, so this is not blocking. +The `-e` approach breaks on (1) multi-line scripts and (2) file paths containing spaces, +quotes, or parentheses — common in conference presentation filenames. ### Not Exposed via Relay (intentional) - `get_seed_config` / `get_jwt` — Exposed in the preload but not relayed to the UI. The JWT and seed are injected into the environment at startup; components should not call these directly.