Files
OSIT-AE-App-Svelte/documentation/PROJECT__AE_Events_Launcher_Native_integration.md
Scott Idem c5c5292715 feat(launcher): configurable launch scripts + composable native primitives
- electron_relay: type launch_from_cache with script_template param;
  add copy_from_cache_to_temp export; add JSDoc for run_osascript hardening
- launcher_file_cont: add get_launch_script_template() helper reading from
  device-level (event_device.data_json.launch_scripts) and event-level
  (events_loc.launcher.launch_scripts) config; wire into handle_open_file()
- PROJECT__AE_Events_Launcher_Native_integration.md: add Section 8
  (Configurable Launch Scripts); update IPC reference for new/changed handlers
- MODULE__AE_Events_PressMgmt_Launcher.md: add configurable launch behavior
  note to Native Mode Safe Handover section
2026-05-11 13:48:54 -04:00

13 KiB

Aether Events Launcher: Native Electron Integration

Status: Operational / Phase 5 Implementation Last Updated: 2026-03-11 Primary Platform: macOS (Darwin) Fallback Platform: Linux / Windows

1. Overview

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.
Onsite Web access on event networks. Faster polling; browser-based file management.
Native Dedicated Podium Kiosk (Electron). Full background pre-caching; atomic safe-handover.

2. Architecture: The Three-Layer Bridge

The integration is built on a decoupled three-layer communication model to ensure security and cross-platform flexibility.

2.1 Layer 1: The Engine (Main Process)

  • Repo: ~/OSIT_dev/aether_app_native_electron/ (separate git repo)
  • File: aether_app_native_electron/src/main/*.ts
  • Role: Performs the heavy lifting (Filesystem, Shell, AppleScript).
  • Responsibilities:
    • Managing the Hashed Cache directory.
    • Executing osascript intents for presentation control.
    • Spawn/Kill process management.

2.2 Layer 2: The Gatekeeper (Preload Script)

  • Namespace: window.aetherNative
  • Role: Securely exposes whitelisted IPC channels to the Renderer.
  • Standards: Uses contextBridge.exposeInMainWorld to prevent arbitrary code execution.

2.3 Layer 3: The Messenger (SvelteKit Relay)

  • File: src/lib/electron/electron_relay.ts
  • Role: Provides a clean, typed API for Svelte components.
  • Responsibilities:
    • Mapping camelCase UI triggers to snake_case IPC calls.
    • Implementing "Smart Fallbacks" (e.g., resolving [home] placeholders if the bridge is partially hydrated).

3. The "Zero-Config" Lifecycle

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.

4. Podium Reliability Protocol

The system is designed to ensure that a presentation never fails due to network instability.

4.1 Hashed Cache Pattern

Files are stored persistently using their SHA-256 hash to prevent filename collisions and handle versioning.

  • Root: ~/Library/Caches/OSIT/file_cache/
  • Subdirectory: First 2 characters of hash (e.g., ab/)
  • Filename: {hash}.file

4.2 Background Sync (File Warming)

When a user navigates to a session in the Launcher UI, the LauncherBackgroundSync component:

  1. Extracts all event_file_id values for that session.
  2. Checks the native cache via aetherNative.check_cache.
  3. Triggers background downloads for missing files via aetherNative.download_to_cache.

4.3 Safe Handover (Launch Sequence)

When a user clicks "Open", the system follows a non-destructive sequence:

  1. Verify: Confirm hash exists in the permanent cache.
  2. Copy: Create an atomic copy in the system [tmp] directory.
  3. Restore: Rename the copy to its original filename (e.g., Abstract_101.pptx).
  4. Execute: Launch the file via the OS.

5. Automation & Actuators (Phase 5)

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)

5.2 System Management

  • Telemetry: Pushes cpu_usage, memory_free_gb, and foreground_app via heartbeats using the get_device_info relay.
  • Self-Update (Roadmap): Plan to monitor Syncthing admin_share for newer .app versions and perform atomic swaps.

5.3 Implemented Actuators (Phase 5 Complete)

  • Recording: manage_recording({action}) — Aperture session capture (start, stop, status). macOS only.
  • Display Layouts: set_display_layout({mode}) — Mirror / Extend via displayplacer. macOS only.
  • Power Control: power_control({action}) — Shutdown, reboot, sleep. macOS + Linux.
  • Window Control: window_control({action}) — Maximize, minimize, fullscreen, kiosk mode.
  • Wallpaper: set_wallpaper({path}) — macOS (AppleScript) + Linux (gsettings).

Note: update_app is implemented as a stub — downloads but does not install. Not yet functional for end users.


6. Launcher Configuration & Management

The Launcher features a standardized, responsive configuration interface designed for onsite technical management.

6.1 UI Architecture

  • Tabbed Navigation: Categorized into System, Sync, and General settings.
  • Section Wrapper (Launcher_Cfg_Section): A shared component providing a consistent header, icon, and responsive grid container.

6.2 3-Way State Logic

To manage screen real estate on varying laptop resolutions, all configuration sections utilize a 3-way visibility state:

  • collapsed: Content is hidden.
  • auto: Expanded by default, but automatically closes if another "auto" section is opened.
  • pinned: Expanded and remains open regardless of other section interactions.

6.3 Technical Mode (edit_mode)

The UI dynamically filters fields based on the user's focus. Enabling Technical Mode ($ae_loc.edit_mode) reveals advanced diagnostic and writeable fields.

Category Standard View (Read-Only) Technical Mode (Read/Write)
Health Heartbeat, RAM Usage, Sync Stats Hostname, IP List, Raw Device JSON
OS Bridge Folder Buttons, Recording Toggle Manual Terminal Commands, Reset Wallpaper
Sync Sync Completion Status Millisecond Timers, Cache Prefix Logic
Update Current Version Status Manual Update Paths, URL Overrides

7. Implementation Reference (IPC Whitelist)

All functions below are exported from src/lib/electron/electron_relay.ts and safely no-op when window.aetherNative is not present (i.e., in browser/non-native mode).

Config & Info

  • get_device_config() — Returns hydrated device settings injected by the native shell on startup.
  • 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.
  • 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.

Shell & OS

  • open_folder(path) — Opens a path in the OS file manager.
  • run_cmd({cmd, timeout?, return_stdout?}) — Async shell command execution.
  • run_cmd_sync({cmd, return_stdout?}) — Synchronous shell command execution.
  • run_osascript(script) — Executes an AppleScript string. macOS only. Hardened (2026-05-11): writes script to a temp .scpt file; multi-line scripts and paths with special characters now work correctly. No shell escaping needed in the passed string.
  • kill_processes({process_name_li}) — Terminates processes by name. macOS/Linux: pkill -f. Windows: taskkill /F.
  • open_local_file_v2(path) — Opens a file with its default OS application.

Presentations (Phase 5)

  • launch_presentation({path, app?, os?}) — Platform-aware launcher. macOS: PowerPoint/Keynote via AppleScript. Linux: LibreOffice Impress. Resolves [home]/[tmp] placeholders.
  • control_presentation({app, action}) — Slide navigation (next/prev/start/stop) for PowerPoint or Keynote via AppleScript.

System Management (Phase 5)

  • set_wallpaper({path}) — Sets desktop wallpaper. macOS (AppleScript) + Linux (gsettings).
  • window_control({action, value?}) — Electron window management: maximize, minimize, fullscreen, kiosk.
  • set_display_layout({mode, configStr?}) — Mirror or extend displays via displayplacer. macOS only.
  • power_control({action}) — Shutdown, reboot, or sleep the host machine. macOS + Linux.
  • manage_recording({action, options?}) — Aperture capture control (start/stop/status). macOS only.
  • open_external({url, app?}) — Opens a URL in Chrome, Firefox, or the default browser.
  • update_app(args)Stub only. Downloads but does not install. Not yet functional.
  • list_tools() — Returns a self-documenting manifest of all available native bridge functions.

Path Placeholders

All paths passed to native handlers should use tokens rather than hardcoded OS paths:

  • [home] — Resolved to the user's home directory by the native bridge.
  • [tmp] — Resolved to the system temporary directory.

8. Configurable Launch Scripts (No-Rebuild File Handling)

To avoid requiring a full Electron rebuild for changes to how files are opened, launch_from_cache supports an optional script_template parameter. When provided, Electron runs the template instead of its built-in hardcoded logic. The hardcoded logic remains intact as the fallback when no template is configured.

Template Formats

Format Example
AppleScript (macOS) Multi-line AppleScript string with {{path}} placeholder
Shell command String prefixed with shell: — e.g. shell:open "{{path}}"

The placeholder {{path}} is replaced with the full resolved path to the file in the temp directory (after the atomic copy from cache).

Where to Configure

Templates are resolved in priority order by get_launch_script_template() in launcher_file_cont.svelte:

  1. event_device.data_json.launch_scripts — API-driven, per-device. Highest priority. Set via the event_device record (Pres Mgmt → Device Management or direct DB edit).
  2. $events_loc.launcher.launch_scripts — Local persistent config. Editable via the Launcher config UI (planned) or direct localStorage manipulation.

If neither is set, script_template is null and Electron uses its built-in hardcoded defaults.

Key Format

Keys are lowercase file extensions without the dot. A "default" key catches all unrecognised extensions.

// event_device.data_json.launch_scripts example
{
  "launch_scripts": {
    "pptx": "tell application \"Microsoft PowerPoint\"\n  activate\n  open (POSIX file \"{{path}}\")\n  delay 3\nend tell\ntell application \"System Events\"\n  keystroke return using command down\nend tell",
    "key": "tell application \"Keynote\"\n  activate\n  open (POSIX file \"{{path}}\")\n  delay 1\n  start (front document)\nend tell",
    "pdf": "shell:open \"{{path}}\"",
    "default": "shell:open \"{{path}}\""
  }
}

Known Issue: launch_presentation vs launch_from_cache Inconsistency

shell_handlers.ts native:launch-presentation still uses the old osascript -e "<inline>" 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.

  • 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.

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.

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.