deploy/deploy.sh — automated deploy: arch detection, scp .app, write seed.json, verify. Supports single laptop, list, or "all"; --seed-only flag skips .app copy for key-rotation runs. deploy/devices.conf — all 19 laptops (num / IP / event_device_id). deploy/event.env.example — template for gitignored event.env (API key). README updated: deploy/ table, script usage, manual steps moved to reference section. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
229 lines
8.4 KiB
Markdown
229 lines
8.4 KiB
Markdown
# 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`.
|