Files
OSIT-AE-App-Native-Electron/documentation/TODO_AGENTS.md
Scott Idem a14c7c7a3f feat(display): add display_control CoreGraphics binary, replace displayplacer as primary path
Derived from OSIT MasterKey app (LegacyUtilities.m, Ian Kohl 2019).
Uses CGConfigureDisplayMirrorOfDisplay — native macOS API, no Homebrew dependency.
Handler now tries display_control first; falls back to displayplacer for missing binary
or per-device configStr overrides. Build the binary via scripts/build-display-control.sh
on a Mac (requires Xcode CLT only), then commit resources/bin/display_control.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-20 16:48:19 -04:00

8.1 KiB

Native App Agent Task List

Use this file to track steps for complex features or bug fixes. Status: Stable - ongoing development.

Current Investigation

  • This started as an API contract review for the native Electron bootstrap path and expanded into a packaging/runtime issue after the deploy step stopped producing bundles.
  • We now know the API side was not the root cause. The bootstrap request shape in src/main/api_client.ts was wrong and has been corrected.
  • The packaging blocker has been diagnosed and fixed (see below).

Launcher Terminology Cleanup

  • Align the native docs/comments with the Svelte-side terminology: Launch Profile = the Svelte config object keyed by extension; Native Template = the AppleScript or shell string Electron actually executes after the file lands in temp.
  • Update src/main/file_handlers.ts comments and any bridge-facing wording so they describe the resolved native_template string accurately. launch_profiles is only the Svelte-side map; the IPC payload is the single executable string. Do not reintroduce launch_scripts as the public term.
  • Keep the source of truth in Svelte. Electron should remain a thin executor/copy layer.
  • After source/docs updates, rebuild/regenerate dist/main/file_handlers.js from source; do not hand-edit the generated file.
  • If any parameter names remain awkward in source, preserve runtime behavior first and rename only when the signature ripple is understood.

What Was Fixed

  • Updated the native bootstrap flow to use the direct site_domain/search request body expected by API V3.
  • Standardized the account-bypass header to x-no-account-id: bypass where that narrow bypass is intended.
  • Removed a redundant x-no-account-id header from file download calls.
  • Rewrote the device lookup smoke test so it validates the real two-step bootstrap path end to end.
  • Upgraded Electron from 34.x to 42.0.1.
  • Replaced deprecated electron-packager with @electron/packager 20.0.0.
  • Added a package:linux smoke test path so packaging failures can be isolated from macOS-specific behavior.
  • Fixed packaging hang on Node 26: yauzl 2.10.0 (used by extract-zip in @electron/packager) emits no data events on Node 26 streams, causing zip extraction to hang indefinitely. Fix: patched @electron/packager/dist/unzip.js to use bsdtar (libarchive) instead of extract-zip. bsdtar was chosen over 7z because 7z refuses macOS .app bundles with chained symlinks inside framework bundles. Patch is re-applied on every npm install via the postinstall script at scripts/patch-packager-unzip.js.

Verified So Far

  • npm run dev works once the Electron binary is present locally.
  • Manual Electron cache extraction restored a runnable checkout on this machine.
  • API validation confirmed the backend responds correctly for:
    • event_device/{id} lookup
    • site_domain/search?limit=1 with the direct SearchQuery body
  • The returned site_domain.account_id matches the device account context in the verified bootstrap flow.
  • The SvelteKit frontend bootstrap path already follows the correct API contract and does not need the same fix.
  • npm run package:linux now produces builds/aether_launcher-linux-x64/ with a complete bundle (confirmed 2026-05-11).
  • npm run package:mac now produces builds/aether_launcher-darwin-x64/ and builds/aether_launcher-darwin-arm64/ with aether_launcher.app inside each (confirmed 2026-05-11). Initial fix used 7z but it refused chained symlinks inside macOS framework bundles; switched to bsdtar (libarchive) which handles both Linux and macOS zips correctly.
  • deploy/deploy.sh output directory names (aether_launcher-darwin-x64, aether_launcher-darwin-arm64) match packager output — no script changes needed.

Remaining Items

  1. Test that the packaged Linux binary runs end-to-end against the dev API.

Root Cause Summary (Packaging Hang)

  • Tool chain: Node 26.1.0 + @electron/packager 20.0.0 + extract-zip 2.0.1 + yauzl 2.10.0
  • Symptom: npm run package:linux exits 0 but produces no output. Debug log shows it starts extraction but never finishes.
  • Root cause: yauzl opens a read stream for the first zip entry, but on Node 26, no data events are ever emitted on that stream. The pipeline(readStream, writeStream) call in extract-zip blocks forever.
  • Fix: Replace the one-liner extractElectronZip function in node_modules/@electron/packager/dist/unzip.js with a child_process.execSync call to bsdtar -xf. bsdtar was chosen over 7z because 7z refuses macOS .app bundles with chained symlinks (e.g. Electron Framework.framework/Libraries → Versions/Current/Libraries). A postinstall npm script re-applies this patch after each npm install.
  • Build-time dependency: libarchive (provides bsdtar) must be installed on the build host. On Arch: pacman -S libarchive; macOS: included in Xcode CLT or brew install libarchive; Ubuntu/Debian: apt install libarchive-tools.

References

Notes

  • Was on Electron 34.
  • The problem is not the backend API keys or the frontend site bootstrap flow.
  • The packaging fix is a node_modules patch, not upstream. If @electron/packager or extract-zip releases a Node 26-compatible version, the postinstall script should be removed.

set_display_layout — Setup & Status (updated 2026-05-20)

Primary approach: display_control (native CoreGraphics — no Homebrew required)

  • Source: scripts/display_control.m (derived from OSIT MasterKey app, Ian Kohl 2019)
  • Build: run scripts/build-display-control.sh on a Mac (requires Xcode CLT only)
  • Output: resources/bin/display_control — commit this binary to the repo
  • Uses CGConfigureDisplayMirrorOfDisplay — same CoreGraphics API macOS uses internally
  • Supports 3+ displays; auto-detects all connected displays; no config string needed

Fallback approach: displayplacer (requires brew install displayplacer on each venue Mac)

  • Reference: jakehilborn/displayplacer
  • Still used when display_control binary is not present
  • Also used for per-device configStr overrides (displayplacer-format strings in event_device.data_json)

Current state (2026-05-20):

  • Correct mirror_of_display:<uuid> syntax used in displayplacer fallback (was mirror: — wrong, now fixed)
  • Failures logged to Electron console ([Launcher] set_display_layout:) instead of silently swallowed
  • Display Mode toggle added to Launcher config (Native OS section) — Extend/Mirror buttons always visible, no Technical Mode required
  • display_control binary not yet built — must be compiled on a Mac and committed

To build display_control (do this on a Mac):

# One-time: install Xcode Command Line Tools if not already installed
xcode-select --install

# Then:
./scripts/build-display-control.sh

# Test it with a second display connected:
./resources/bin/display_control status
./resources/bin/display_control extend
./resources/bin/display_control mirror

# Commit the binary:
git add resources/bin/display_control
git commit -m "build: add display_control binary (macOS CoreGraphics)"

Optional per-device override (displayplacer format, for edge cases): For rooms where auto-detection produces the wrong result, store the raw configStr in event_device.data_json:

{
  "displayplacer_config_extend": "<output of displayplacer list in extended layout>",
  "displayplacer_config_mirror": "<output of displayplacer list in mirrored layout>"
}

configStr is passed from the Svelte call site and uses the displayplacer fallback path directly.