Files
OSIT-AE-App-Native-Electron/documentation/TODO_AGENTS.md
Scott Idem 9df9d884e5 fix(display): fix mirror_of_display syntax, add Homebrew fallback, update docs
- `mirror:` was wrong; correct displayplacer param is `mirror_of_display:<uuid>`
- Same fix applied to the strip regex (extend path) and mirror detection check
- Binary lookup now tries resources/bin → /opt/homebrew/bin → /usr/local/bin
  so dev/venue Macs with `brew install displayplacer` work without bundling
- Updated TODO_AGENTS.md: marks auto-detection complete, documents one-time
  brew install step, bundling path, and optional per-device configStr override

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

8.0 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 — displayplacer Setup & Status (updated 2026-05-20)

Reference: jakehilborn/displayplacer

Current state (2026-05-20):

  • Handler auto-detects displays via displayplacer list — no per-device configStr required for normal use
  • Binary lookup order: resources/bin/displayplacer (bundled) → /opt/homebrew/bin/displayplacer (Apple Silicon Homebrew) → /usr/local/bin/displayplacer (Intel Homebrew)
  • Correct mirror_of_display:<uuid> syntax used (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

One-time setup on each venue Mac:

brew install displayplacer

The binary is not bundled in the Electron build yet. Homebrew installs it to /opt/homebrew/bin/ (Apple Silicon) or /usr/local/bin/ (Intel) — both are in the fallback lookup chain.

Bundling the binary (future): To ship displayplacer inside the .app bundle without requiring Homebrew on venue Macs:

  1. Copy the displayplacer binary to resources/bin/displayplacer
  2. Mark it executable: chmod +x resources/bin/displayplacer
  3. Add to package.json extraResources so @electron/packager includes it

Optional per-device override (manual tuning): 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>"
}

The handler accepts configStr and uses it directly, bypassing auto-detection. Pass it from the Svelte call site if needed.

displayplacer quick reference:

  • displayplacer list — prints current display info and a ready-to-run config string at the bottom
  • displayplacer "<display_string>" "<display_string>" — applies layout
  • Mirror syntax: add mirror_of_display:<primary_uuid> to the secondary display string
  • Extend syntax: set origin:(<x>,0) with non-overlapping x offsets per display