- Add set_display_layout (+ other missing system handler methods) to AetherNativeBridge interface in types.ts - README: clarify configStr source (event_device.data_json) and no-op behaviour when absent - TODO_AGENTS: correct 7z→bsdtar throughout; mark Electron-side set_display_layout items done; remove completed bsdtar doc item Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
8.2 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.tswas wrong and has been corrected. - The packaging blocker has been diagnosed and fixed (see below).
What Was Fixed
- Updated the native bootstrap flow to use the direct
site_domain/searchrequest body expected by API V3. - Standardized the account-bypass header to
x-no-account-id: bypasswhere that narrow bypass is intended. - Removed a redundant
x-no-account-idheader 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-packagerwith@electron/packager20.0.0. - Added a
package:linuxsmoke test path so packaging failures can be isolated from macOS-specific behavior. - Fixed packaging hang on Node 26:
yauzl2.10.0 (used byextract-zipin@electron/packager) emits nodataevents on Node 26 streams, causing zip extraction to hang indefinitely. Fix: patched@electron/packager/dist/unzip.jsto usebsdtar(libarchive) instead ofextract-zip.bsdtarwas chosen over7zbecause7zrefuses macOS.appbundles with chained symlinks inside framework bundles. Patch is re-applied on everynpm installvia thepostinstallscript atscripts/patch-packager-unzip.js.
Verified So Far
npm run devworks 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}lookupsite_domain/search?limit=1with the directSearchQuerybody
- The returned
site_domain.account_idmatches 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:linuxnow producesbuilds/aether_launcher-linux-x64/with a complete bundle (confirmed 2026-05-11).npm run package:macnow producesbuilds/aether_launcher-darwin-x64/andbuilds/aether_launcher-darwin-arm64/withaether_launcher.appinside each (confirmed 2026-05-11). Initial fix used7zbut it refused chained symlinks inside macOS framework bundles; switched tobsdtar(libarchive) which handles both Linux and macOS zips correctly.deploy/deploy.shoutput directory names (aether_launcher-darwin-x64,aether_launcher-darwin-arm64) match packager output — no script changes needed.
Remaining Items
- 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/packager20.0.0 +extract-zip2.0.1 +yauzl2.10.0 - Symptom:
npm run package:linuxexits 0 but produces no output. Debug log shows it starts extraction but never finishes. - Root cause:
yauzlopens a read stream for the first zip entry, but on Node 26, nodataevents are ever emitted on that stream. Thepipeline(readStream, writeStream)call inextract-zipblocks forever. - Fix: Replace the one-liner
extractElectronZipfunction innode_modules/@electron/packager/dist/unzip.jswith achild_process.execSynccall tobsdtar -xf.bsdtarwas chosen over7zbecause7zrefuses macOS.appbundles with chained symlinks (e.g.Electron Framework.framework/Libraries → Versions/Current/Libraries). Apostinstallnpm script re-applies this patch after eachnpm install. - Build-time dependency:
libarchive(providesbsdtar) must be installed on the build host. On Arch:pacman -S libarchive; macOS: included in Xcode CLT orbrew install libarchive; Ubuntu/Debian:apt install libarchive-tools.
References
- Electron 42 release notes: https://www.electronjs.org/blog/electron-42-0
- Related Electron packaging discussion: https://github.com/aaddrick/claude-desktop-debian/pull/587
- Electron packaging/runtime change reference: https://github.com/electron/electron/pull/49328
- yauzl Node 26 stream issue:
yauzl2.10.0 uses legacy Node streams (streams1 style); Node 26 changed stream internal behavior soopenReadStreamreturns a stream that never emitsdatawithout a proper pipeline consumer.
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/packagerorextract-zipreleases a Node 26-compatible version, thepostinstallscript 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:
-
Capture
configStrper room Mac. On each presentation Mac, run:displayplacer listThis 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).
-
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}/). -
✅
set_display_layoutinsrc/main/system_handlers.tsalready acceptsconfigStr. Handler signature is{ mode, configStr }and uses it correctly — no change needed. -
✅ Electron relay (
src/preload/index.ts) already forwardsconfigStr— args passed as-is.AetherNativeBridgetype insrc/shared/types.tsupdated to includeset_display_layoutwith correct signature (2026-05-12). -
Pass
configStrfrom Svelte. The Svelte call site inlauncher_file_cont.svelte(Step 3 inhandle_open_file) currently calls:await native.set_display_layout({ mode: profile.display_mode }).catch(() => {});It needs to be updated to thread
configStrfrom$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_deviceis theevent_deviceobject loaded during native app bootstrapdata_jsonis an open JSON field on that objecthandle_open_file()already callsset_display_layoutat 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 stringdisplayplacer <configStr>— applies layout; the configStr fromlistis what you pass back- Current Electron handler:
src/main/system_handlers.ts— find theset_display_layoutIPC handler - Svelte call site:
src/routes/events/[event_id]/(launcher)/launcher_file_cont.svelte— Step 3 comment inhandle_open_file()