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
This commit is contained in:
Scott Idem
2026-05-11 13:48:54 -04:00
parent 8ed7e0f8d7
commit c5c5292715
4 changed files with 180 additions and 6 deletions

View File

@@ -64,11 +64,68 @@ export async function launch_from_cache({
hash,
temp_root,
filename,
hash_prefix_length = 2
}: any) {
hash_prefix_length = 2,
script_template = null
}: {
cache_root: string;
hash: string;
temp_root: string;
filename: string;
hash_prefix_length?: number;
/**
* Optional data-driven launch script. If provided, Electron runs this instead of
* its hardcoded extension-based logic — no app rebuild needed for script changes.
*
* Two formats:
* - AppleScript: multi-line string with {{path}} placeholder (macOS only)
* - Shell command: prefix with "shell:" → e.g. "shell:open \"{{path}}\""
*
* Configure via event_device.data_json.launch_scripts or $events_loc.launcher.launch_scripts.
* If null, Electron falls through to its built-in hardcoded defaults.
*/
script_template?: string | null;
}) {
if (!native)
return { success: false, error: 'Native bridge not available' };
return await native.launch_from_cache({
cache_root,
hash,
temp_root,
filename,
hash_prefix_length,
script_template
});
}
/**
* Thin cache primitive — copies a cached file to the temp directory and returns
* the resolved path. The caller decides what happens next.
*
* Preferred building block for composable launch flows on the Svelte side:
* 1. copy_from_cache_to_temp(...) → { path }
* 2. run_osascript(template.replace('{{path}}', path))
* OR run_cmd(`open "${path}"`)
* OR whatever you need
*
* Use launch_from_cache when the built-in hardcoded logic is sufficient.
* Use this when you want full control over what happens after the file lands in temp.
*/
export async function copy_from_cache_to_temp({
cache_root,
hash,
temp_root,
filename,
hash_prefix_length = 2
}: {
cache_root: string;
hash: string;
temp_root: string;
filename: string;
hash_prefix_length?: number;
}): Promise<{ success: boolean; path?: string; error?: string }> {
if (!native)
return { success: false, error: 'Native bridge not available' };
return await native.copy_from_cache_to_temp({
cache_root,
hash,
temp_root,
@@ -129,6 +186,18 @@ export async function cleanup_tmp_files({
return await native.run_cmd({ cmd, timeout: 30000, return_stdout: false });
}
/**
* Executes an AppleScript string. macOS only.
*
* HARDENED (2026-05-11): The Electron handler now writes the script to a temp .scpt
* file and runs `osascript "/path/to/file.scpt"` rather than passing it inline via
* the -e flag. This means:
* - Multi-line scripts work correctly
* - Paths with spaces or special characters work correctly
* - No shell escaping required in the script string you pass here
*
* The .scpt file is deleted immediately after execution.
*/
export async function run_osascript(script: string) {
if (!native)
return { success: false, error: 'Native bridge not available' };