# Aether Native Launcher (Electron) The Aether Native Launcher is a specialized Electron-based shell for the Aether Platform. It provides a secure bridge between the SvelteKit web UI and the local operating system, enabling features restricted by browser sandboxing. ## 🚀 Overview This application serves as the "Native Mode" runtime for Aether podiums and devices. It handles: - **Local File Orchestration:** Managed cache for presentation files (PPTx, Keynote, PDF). - **Automation:** Specialized AppleScript handlers for PowerPoint and Keynote. - **Hardware Telemetry:** Direct access to CPU, RAM, and Network interface data. - **Remote Control:** Slide navigation and application control via WebSocket intents. ## 🖥️ Onsite Deployment **Current hardware:** MacBook Air 2018 — Intel x64. All current deployments use `aether_launcher-darwin-x64`. **Future hardware:** Apple Silicon Macs use `aether_launcher-darwin-arm64`. Windows and Linux builds are planned. SSH user on all laptops: **`speaker ready`** IP pattern: `192.168.32.1XX` (XX = zero-padded laptop number, e.g. 03 → `.103`). Find/replace this prefix for other onsite environments. Deploy files live in `deploy/`: | File | Purpose | | --- | --- | | `deploy/deploy.sh` | Deploy script — handles arch detection, scp, and seed.json | | `deploy/devices.conf` | Laptop list: number, IP, `event_device_id` | | `deploy/event.env` | **Gitignored** — per-event API key and URLs (create from example) | | `deploy/event.env.example` | Template for `event.env` | ### Step 1 — Build the app (workstation) ```bash cd ~/OSIT_dev/aether_app_native_electron npm run package:mac # Produces builds/aether_launcher-darwin-x64/aether_launcher.app ← the one to deploy ``` Only rebuild if source code has changed. The `.app` bundle is identical for all Intel laptops — only `~/seed.json` differs per device. ### Step 2 — Create event.env ```bash cp deploy/event.env.example deploy/event.env # Edit deploy/event.env — fill in AETHER_API_KEY ``` Create the API key in the Aether admin panel before the show (Core → Accounts or Events → Devices API key section). All laptops share one key per event. Delete it after the show. ### Step 3 — Run the deploy script ```bash # Deploy specific laptops: ./deploy/deploy.sh 01 02 03 # Deploy all laptops in devices.conf: ./deploy/deploy.sh all # Update seed.json only (no .app copy — e.g. when rotating the API key): ./deploy/deploy.sh --seed-only all ``` The script auto-detects each Mac's CPU architecture, copies the correct `.app` build, writes `seed.json`, and verifies. One SSH connection failure won't abort the batch — it logs and continues, then reports which laptops need a retry. ### Step 4 — Verify and launch After the script completes, launch the app on each laptop and confirm it connects and shows the correct device name in the Launcher UI. ### Adding SSH key to a new laptop (first time only) ```bash ssh-copy-id "speaker ready"@192.168.32.1XX ``` Run once per laptop before deploying. --- ### Manual deploy reference The script covers the normal case. For one-off fixes or if the script isn't available: ```bash # Detect arch ssh "speaker ready"@192.168.32.103 "uname -m" # x86_64 → darwin-x64 | arm64 → darwin-arm64 # Copy .app (Intel example): scp -r builds/aether_launcher-darwin-x64/aether_launcher.app \ "speaker ready"@192.168.32.103:/Applications/aether_launcher.app # Write seed.json: ssh "speaker ready"@192.168.32.103 "cat > ~/seed.json" << 'EOF' { "event_device_id": "DEVICE_ID_FOR_THIS_LAPTOP", "aether_api_key": "YOUR_API_KEY", "primary_api_base_url": "https://api.oneskyit.com", "backup_api_base_url": "https://bak-api.oneskyit.com", "onsite_api_base_url": null } EOF # Verify: ssh "speaker ready"@192.168.32.103 "cat ~/seed.json" ``` `event_device_id` values by laptop — see the **Device Reference** table below. --- ## 📋 Device Reference | Laptop | IP Address | event_device_id | Notes | |--------|------------------|-----------------|--------------------------| | 01 | 192.168.32.101 | tFLL1fLQfnk | | | 02 | 192.168.32.102 | rpbfunVPEzw | | | 03 | 192.168.32.103 | 1EPfPX8kfw8 | | | 04 | 192.168.32.104 | zvgyLM5yieU | | | 05 | 192.168.32.105 | QOc046GoeSc | | | 06 | 192.168.32.106 | 2o8j6eb0L6s | | | 07 | 192.168.32.107 | Oa1tlxPEVSQ | | | 08 | 192.168.32.108 | fY4yznpUZ48 | | | 09 | 192.168.32.109 | YlgGCyjo9bY | | | 10 | 192.168.32.110 | GcTnFsp1mHI | | | 11 | 192.168.32.111 | 6z88m9oEZio | | | 12 | 192.168.32.112 | EggJqL2kWkA | | | 13 | 192.168.32.113 | O11eckHFdVE | | | 14 | 192.168.32.114 | reI0SecUEhI | | | 15 | 192.168.32.115 | crozxT8mA44 | | | 16 | 192.168.32.116 | 0nP4VZsvr2Q | | | 17 | 192.168.32.117 | Gm2gNqPGzLA | | | 19 | 192.168.32.119 | 6tpukvRVugU | (no laptop 18) | | x20 | 192.168.32.120 | rwLYnKUNd1M | old 04, spare/retired | `aether_api_key`: all laptops share a single key per event deployment. The key is created in the Aether admin panel before the show and deleted after. Check `builds/seed.json` for the current key, or create a new one in Aether (Core → Accounts or Events → Devices API key section) before each deployment. --- ## ⚙️ Configuration The application requires a `seed.json` file to identify the device and connect to the Aether API. ### 1. Seed Configuration **Location: `~/seed.json`** (user's home directory — external to the app bundle by design) This file is intentionally kept outside the application bundle so it can be edited per-device without re-signing or repackaging the app. On macOS this is `/Users/speaker ready/seed.json`. Seed file format: ```json { "event_device_id": "tFLL1fLQfnk", "aether_api_key": "YOUR_API_KEY", "primary_api_base_url": "https://api.oneskyit.com", "backup_api_base_url": "https://bak-api.oneskyit.com", "onsite_api_base_url": null } ``` `event_device_id` is the `id_random` from the Aether `event_device` record for that physical laptop — see the Device Reference table above. `aether_api_key` is a shared key created per event deployment and deleted after the show. ### 2. Development Setup ```bash npm install npm start # Compiles TypeScript (tsc) then launches Electron ``` ### 3. File Cache Layout Presentation files are cached locally under `hash_prefix_length`-char subdirectories (default: 2): ```text [local_file_cache_path]/ 4a/ 4a228ef8ac1a...sha256hash...file 1d/ 1d720916a831...sha256hash...file ``` **Important:** `hash_prefix_length` must be consistent. If it changes, files in old directories become orphaned and will be re-downloaded. The default is `2` and should not be changed unless explicitly coordinated across all devices. ## 🌉 The Native Bridge (`aetherNative`) The bridge is exposed to the renderer via `contextBridge`. It can be accessed in the web UI via `window.aetherNative`. ### Core Methods | Method | Description | | --- | --- | | `list_tools()` | Returns a JSON manifest of all available native functions. | | `launch_presentation({path, app})` | Launches a presentation with auto-focus and slideshow start. | | `control_presentation({app, action})` | Sends `next`, `prev`, `start`, or `stop` to active decks. | | `open_folder(path)` | Opens a local directory in the OS file explorer. | | `get_device_info()` | Returns hardware metadata (RAM, IPs, Hostname). | ### Example Usage (UI Relay) ```typescript import * as native from '$lib/electron/electron_relay'; // Launch a file from local cache await native.launch_presentation({ path: '[tmp]/my_deck.pptx', app: 'powerpoint' }); // Navigate slides await native.control_presentation({ app: 'powerpoint', action: 'next' }); ``` ## 🛠️ Development - **Preload:** Logic defined in `src/preload/index.ts`. - **Handlers:** OS-level logic in `src/main/shell_handlers.ts` and `src/main/file_handlers.ts`. - **Types:** Shared TypeScript interfaces in `src/shared/types.ts`.