Files
OSIT-AE-App-Native-Electron/documentation/TODO_AGENTS.md
2026-05-13 11:25:55 -04:00

9.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 template string accurately. The current IPC arg name in source is launch_profiles, but it carries a single native template 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.

Pending Feature: set_display_layout — displayplacer Per-Device Config

Background (added 2026-05-12, from Svelte-side LaunchProfile work):

The Svelte Events Launcher (launcher_file_cont.svelte) now resolves a LaunchProfile per file extension and calls native.set_display_layout({ mode }) before opening a presentation. The underlying handler in src/main/system_handlers.ts uses a bundled displayplacer macOS binary. The wiring is complete on both ends — but it silently no-ops on every device because displayplacer requires a per-machine configStr (the output of displayplacer list on that specific Mac) to identify the exact display UUIDs and pixel positions. Without it, displayplacer cannot apply any layout.

What's needed:

  1. Capture configStr per room Mac. On each presentation Mac, run:

    displayplacer list
    

    This prints the current display configuration. Copy the full output for both the "extend" and "mirror" layouts (they differ in which display is primary and how they're positioned).

  2. Store configs in the API. The Svelte side reads device config from event_device.data_json. Store the captured strings there:

    {
      "displayplacer_config_extend": "<full configStr for extended layout>",
      "displayplacer_config_mirror": "<full configStr for mirrored layout>"
    }
    

    Admin UI to set these values already exists via the normal event_device edit flow. You can also set them directly via the V3 CRUD API (PATCH /v3/crud/event_device/{id}/).

  3. set_display_layout in src/main/system_handlers.ts already accepts configStr. Handler signature is { mode, configStr } and uses it correctly — no change needed.

  4. Electron relay (src/preload/index.ts) already forwards configStr — args passed as-is. AetherNativeBridge type in src/shared/types.ts updated to include set_display_layout with correct signature (2026-05-12).

  5. Pass configStr from Svelte. The Svelte call site in launcher_file_cont.svelte (Step 3 in handle_open_file) currently calls:

    await native.set_display_layout({ mode: profile.display_mode }).catch(() => {});
    

    It needs to be updated to thread configStr from $ae_loc.native_device.data_json:

    const cfg_key = profile.display_mode === 'mirror'
        ? 'displayplacer_config_mirror'
        : 'displayplacer_config_extend';
    const configStr = ($ae_loc as any).native_device?.data_json?.[cfg_key] ?? null;
    await native.set_display_layout({ mode: profile.display_mode, configStr }).catch(() => {});
    

Contract already in place (Svelte side — no action needed):

  • $ae_loc.native_device is the event_device object loaded during native app bootstrap
  • data_json is an open JSON field on that object
  • handle_open_file() already calls set_display_layout at the right point — it just needs the configStr threaded through

Resources:

  • displayplacer GitHub (usage + examples): https://github.com/jakehilborn/displayplacer
  • displayplacer list — prints current layout as a re-runnable config string
  • displayplacer <configStr> — applies layout; the configStr from list is what you pass back
  • Current Electron handler: src/main/system_handlers.ts — find the set_display_layout IPC handler
  • Svelte call site: src/routes/events/[event_id]/(launcher)/launcher_file_cont.svelte — Step 3 comment in handle_open_file()