From ba51b6bf99d601d39f3a7b96aa1d7941c35e4c62 Mon Sep 17 00:00:00 2001 From: Ryan Kegel Date: Sun, 11 Oct 2026 23:49:46 -0400 Subject: [PATCH] Add app launcher job handling and update app stats structure --- AGENTS.md | 32 ++ .../app-stats.json | 400 +++++++++++++----- dynamic-deck/src/utils/app-launcher.ts | 42 +- 3 files changed, 351 insertions(+), 123 deletions(-) create mode 100644 AGENTS.md diff --git a/AGENTS.md b/AGENTS.md new file mode 100644 index 0000000..b92f04e --- /dev/null +++ b/AGENTS.md @@ -0,0 +1,32 @@ +# Repository Guidelines + +## Project Structure & Module Organization + +The Stream Deck plugin lives in `dynamic-deck/`; run its commands from that directory. TypeScript entrypoint and action handlers are in `src/plugin.ts` and `src/actions/`, shared logic is in `src/services/` and `src/utils/`, and Property Inspector files are in `src/ui/`. Artwork is under `imgs/`. `com.ryan-kegel.dynamic-deck.sdPlugin/` is the Stream Deck bundle; Rollup writes the plugin bundle there and copies UI and supporting assets. The nested `dynamic-deck/AGENTS.md` has detailed implementation notes; follow it when changing that project. + +## Build, Test, and Development Commands + +From `dynamic-deck/`: + +- `npm install` installs the plugin dependencies. +- `npm run build` creates the production bundle at `com.ryan-kegel.dynamic-deck.sdPlugin/bin/plugin.js`. +- `npm run watch` rebuilds on changes and asks the Stream Deck CLI to restart the plugin. +- `node generate-assets.cjs` creates placeholder PNG assets if they are missing. + +There is no configured test runner, linter, or standalone typecheck script. Use the build to check plugin compilation; do not run `tsc` directly. + +## Coding Style & Naming Conventions + +Use TypeScript ES modules, two-space indentation in TypeScript and Rollup configuration, and descriptive kebab-case filenames (for example, `app-monitor.ts`). Keep each Stream Deck action in its own file under `src/actions/`, and match its registered UUID to the plugin ID in the manifest. Update source files under `src/`; generated bundle files are build output. Some app discovery and window-focus features rely on Windows PowerShell and Win32 APIs. + +## Testing Guidelines + +No automated tests or coverage requirements are configured. For changes to plugin code, run `npm run build` from `dynamic-deck/` and inspect the generated bundle or test the action in Stream Deck when available. UI changes should be checked in the corresponding Property Inspector. + +## Commit & Pull Request Guidelines + +Git history currently contains only the initial commit, so no commit convention is established. Use a short, imperative summary (for example, `Fix app launcher title sync`). Pull requests should explain the behavior change, list relevant verification steps, link related issues, and include screenshots for visible Property Inspector or key-face changes. + +## Configuration & Runtime Data + +Keep API credentials in Stream Deck action settings, not committed files. The app tracker persists runtime statistics in `com.ryan-kegel.dynamic-deck.sdPlugin/app-stats.json`; avoid removing it unless resetting saved rankings is intended. diff --git a/dynamic-deck/com.ryan-kegel.dynamic-deck.sdPlugin/app-stats.json b/dynamic-deck/com.ryan-kegel.dynamic-deck.sdPlugin/app-stats.json index 6bb4c80..1b50d6e 100644 --- a/dynamic-deck/com.ryan-kegel.dynamic-deck.sdPlugin/app-stats.json +++ b/dynamic-deck/com.ryan-kegel.dynamic-deck.sdPlugin/app-stats.json @@ -1,154 +1,330 @@ { - "c:\\portableapps\\visual studio code\\code.exe": { - "name": "Visual Studio Code", - "path": "c:\\portableapps\\visual studio code\\code.exe", - "launchCount": 12, - "focusCount": 186, - "activeDurationSeconds": 205450, - "lastUsedTimestamp": 1788926512701 - }, - "c:\\program files\\google\\chrome\\application\\chrome.exe": { - "name": "Google Chrome", - "path": "c:\\program files\\google\\chrome\\application\\chrome.exe", - "launchCount": 34, - "focusCount": 253, - "activeDurationSeconds": 208334, - "lastUsedTimestamp": 1788926520418 - }, - "c:\\portableapps\\blender\\blender.exe": { - "name": "Blender", - "path": "c:\\portableapps\\blender\\blender.exe", - "launchCount": 2, - "focusCount": 2, - "activeDurationSeconds": 6, - "lastUsedTimestamp": 1788410154017 - }, - "c:\\godot4\\godot_v4.7.1-stable_win64.exe": { - "name": "Godot Engine", - "path": "c:\\godot4\\godot_v4.7.1-stable_win64.exe", - "launchCount": 115, - "focusCount": 83, - "activeDurationSeconds": 49481, - "lastUsedTimestamp": 1788919885095 + "d:\\python utilities\\icue\\app\\dist\\icue assigner\\icue assigner.exe": { + "name": "icue assigner", + "path": "d:\\python utilities\\icue\\app\\dist\\icue assigner\\icue assigner.exe", + "launchCount": 5, + "focusCount": 7, + "activeDurationSeconds": 436, + "lastUsedTimestamp": 1791397964729 }, "c:\\program files\\nomachine\\bin\\nxplayer.bin": { "name": "NoMachine Player Process", "path": "c:\\program files\\nomachine\\bin\\nxplayer.bin", - "launchCount": 5, - "focusCount": 26, - "activeDurationSeconds": 3607, - "lastUsedTimestamp": 1788900860130 + "launchCount": 19, + "focusCount": 125, + "activeDurationSeconds": 36304, + "lastUsedTimestamp": 1791776324356 }, - "c:\\users\\ryan\\appdata\\local\\programs\\obsidian\\obsidian.exe": { - "name": "Obsidian", - "path": "c:\\users\\ryan\\appdata\\local\\programs\\obsidian\\obsidian.exe", - "launchCount": 3, - "focusCount": 28, - "activeDurationSeconds": 2708, - "lastUsedTimestamp": 1788924951152 + "c:\\program files\\google\\chrome\\application\\chrome.exe": { + "name": "Google Chrome", + "path": "c:\\program files\\google\\chrome\\application\\chrome.exe", + "launchCount": 66, + "focusCount": 498, + "activeDurationSeconds": 291389, + "lastUsedTimestamp": 1791776318801 + }, + "c:\\portableapps\\visual studio code\\code.exe": { + "name": "Visual Studio Code", + "path": "c:\\portableapps\\visual studio code\\code.exe", + "launchCount": 10, + "focusCount": 569, + "activeDurationSeconds": 338873, + "lastUsedTimestamp": 1791776982303 }, "c:\\program files\\windowsapps\\microsoft.windowsterminal_1.24.11911.0_x64__8wekyb3d8bbwe\\windowsterminal.exe": { "name": "Windows Terminal Host", "path": "c:\\program files\\windowsapps\\microsoft.windowsterminal_1.24.11911.0_x64__8wekyb3d8bbwe\\windowsterminal.exe", - "launchCount": 3, - "focusCount": 78, - "activeDurationSeconds": 5848, - "lastUsedTimestamp": 1788925695681 + "launchCount": 7, + "focusCount": 185, + "activeDurationSeconds": 110465, + "lastUsedTimestamp": 1791776932626 }, - "h:\\games\\the darkest files\\the darkest files.exe": { - "name": "the darkest files", - "path": "h:\\games\\the darkest files\\the darkest files.exe", + "c:\\godot4\\godot_v4.7.1-stable_win64.exe": { + "name": "Godot Engine", + "path": "c:\\godot4\\godot_v4.7.1-stable_win64.exe", + "launchCount": 111, + "focusCount": 186, + "activeDurationSeconds": 49534, + "lastUsedTimestamp": 1791776950093 + }, + "c:\\python\\3.12.0\\python.exe": { + "name": "Python", + "path": "c:\\python\\3.12.0\\python.exe", + "launchCount": 51, + "focusCount": 132, + "activeDurationSeconds": 13038, + "lastUsedTimestamp": 1791734276556 + }, + "c:\\portableapps\\affinity publisher\\data\\local\\stubexe\\0xa311ff49a2412aaa\\publisher.exe": { + "name": "Turbo Virtual Machine Executable", + "path": "c:\\portableapps\\affinity publisher\\data\\local\\stubexe\\0xa311ff49a2412aaa\\publisher.exe", + "launchCount": 6, + "focusCount": 65, + "activeDurationSeconds": 1195, + "lastUsedTimestamp": 1791406867625 + }, + "c:\\portableapps\\affinity publisher\\data\\local\\stubexe\\0xff83c46c4db01a13\\notepad++.exe": { + "name": "Turbo Virtual Machine Executable", + "path": "c:\\portableapps\\affinity publisher\\data\\local\\stubexe\\0xff83c46c4db01a13\\notepad++.exe", "launchCount": 2, - "focusCount": 4, - "activeDurationSeconds": 2381, - "lastUsedTimestamp": 1788838622190 + "focusCount": 7, + "activeDurationSeconds": 25, + "lastUsedTimestamp": 1791606075113 }, - "c:\\portableapps\\gimpportable\\app\\gimp\\bin\\gimp-2.10.exe": { - "name": "GNU Image Manipulation Program", - "path": "c:\\portableapps\\gimpportable\\app\\gimp\\bin\\gimp-2.10.exe", + "c:\\program files (x86)\\microsoft\\edge\\application\\msedge.exe": { + "name": "Microsoft Edge", + "path": "c:\\program files (x86)\\microsoft\\edge\\application\\msedge.exe", + "launchCount": 6, + "focusCount": 24, + "activeDurationSeconds": 629, + "lastUsedTimestamp": 1791406873658 + }, + "c:\\portableapps\\vlcportable3\\vlcportable.exe": { + "name": "VLC Media Player Portable (PortableApps.com Launcher)", + "path": "c:\\portableapps\\vlcportable3\\vlcportable.exe", + "launchCount": 19, + "focusCount": 19, + "activeDurationSeconds": 21, + "lastUsedTimestamp": 1791734352007 + }, + "c:\\portableapps\\vlcportable3\\app\\vlc\\vlc.exe": { + "name": "VLC media player", + "path": "c:\\portableapps\\vlcportable3\\app\\vlc\\vlc.exe", + "launchCount": 37, + "focusCount": 28, + "activeDurationSeconds": 1420, + "lastUsedTimestamp": 1791734502899 + }, + "c:\\program files\\vegas\\vegas pro 16.0\\vegas160.exe": { + "name": "VEGAS Pro", + "path": "c:\\program files\\vegas\\vegas pro 16.0\\vegas160.exe", + "launchCount": 2, + "focusCount": 12, + "activeDurationSeconds": 443, + "lastUsedTimestamp": 1791087959585 + }, + "c:\\program files\\7-zip\\7zg.exe": { + "name": "7-Zip GUI", + "path": "c:\\program files\\7-zip\\7zg.exe", "launchCount": 1, "focusCount": 1, - "activeDurationSeconds": 3, - "lastUsedTimestamp": 1788575227396 + "activeDurationSeconds": 37, + "lastUsedTimestamp": 1790980172911 }, - "c:\\program files\\bambu studio\\bambu-studio.exe": { - "name": "BambuStudio", - "path": "c:\\program files\\bambu studio\\bambu-studio.exe", - "launchCount": 2, - "focusCount": 9, - "activeDurationSeconds": 27027, - "lastUsedTimestamp": 1788923561849 + "c:\\program files\\windowsapps\\microsoft.gamingservices_38.117.27001.0_x64__8wekyb3d8bbwe\\gamingservicesui\\gamingservicesui.exe": { + "name": "GamingServicesUI.exe", + "path": "c:\\program files\\windowsapps\\microsoft.gamingservices_38.117.27001.0_x64__8wekyb3d8bbwe\\gamingservicesui\\gamingservicesui.exe", + "launchCount": 4, + "focusCount": 4, + "activeDurationSeconds": 24, + "lastUsedTimestamp": 1791773956910 + }, + "c:\\xboxgames\\minecraft launcher\\content\\minecraft.exe": { + "name": "minecraft", + "path": "c:\\xboxgames\\minecraft launcher\\content\\minecraft.exe", + "launchCount": 4, + "focusCount": 16, + "activeDurationSeconds": 358, + "lastUsedTimestamp": 1791776311763 + }, + "c:\\users\\ryan\\appdata\\local\\packages\\microsoft.4297127d64ec6_8wekyb3d8bbwe\\localcache\\local\\runtime\\java-runtime-epsilon\\windows-x64\\java-runtime-epsilon\\bin\\javaw.exe": { + "name": "OpenJDK Platform binary", + "path": "c:\\users\\ryan\\appdata\\local\\packages\\microsoft.4297127d64ec6_8wekyb3d8bbwe\\localcache\\local\\runtime\\java-runtime-epsilon\\windows-x64\\java-runtime-epsilon\\bin\\javaw.exe", + "launchCount": 6, + "focusCount": 12, + "activeDurationSeconds": 7640, + "lastUsedTimestamp": 1791776307738 + }, + "h:\\steam\\steamapps\\common\\among us\\among us.exe": { + "name": "among us", + "path": "h:\\steam\\steamapps\\common\\among us\\among us.exe", + "launchCount": 1, + "focusCount": 1, + "activeDurationSeconds": 22, + "lastUsedTimestamp": 1791084105438 + }, + "c:\\program files\\cheat engine\\cheatengine-x86_64.exe": { + "name": "Cheat Engine", + "path": "c:\\program files\\cheat engine\\cheatengine-x86_64.exe", + "launchCount": 1, + "focusCount": 2, + "activeDurationSeconds": 11, + "lastUsedTimestamp": 1791084325807 + }, + "c:\\program files (x86)\\steam\\steamapps\\common\\ravenfield\\ravenfield.exe": { + "name": "ravenfield", + "path": "c:\\program files (x86)\\steam\\steamapps\\common\\ravenfield\\ravenfield.exe", + "launchCount": 1, + "focusCount": 2, + "activeDurationSeconds": 2147, + "lastUsedTimestamp": 1791086272467 + }, + "c:\\portableapps\\affinity designer\\app\\designer\\designer.exe": { + "name": "Affinity Designer", + "path": "c:\\portableapps\\affinity designer\\app\\designer\\designer.exe", + "launchCount": 1, + "focusCount": 1, + "activeDurationSeconds": 111, + "lastUsedTimestamp": 1791088167181 + }, + "c:\\users\\ryan\\appdata\\local\\temp\\procexp64.exe": { + "name": "Sysinternals Process Explorer", + "path": "c:\\users\\ryan\\appdata\\local\\temp\\procexp64.exe", + "launchCount": 1, + "focusCount": 1, + "activeDurationSeconds": 92, + "lastUsedTimestamp": 1791088259775 + }, + "c:\\users\\ryan\\appdata\\local\\programs\\obsidian\\obsidian.exe": { + "name": "Obsidian", + "path": "c:\\users\\ryan\\appdata\\local\\programs\\obsidian\\obsidian.exe", + "launchCount": 4, + "focusCount": 27, + "activeDurationSeconds": 31202, + "lastUsedTimestamp": 1791737325568 + }, + "c:\\users\\ryan\\.codex\\packages\\app-server-daemon\\releases\\0.157.1-x86_64-pc-windows-msvc\\bin\\codex.exe": { + "name": "codex", + "path": "c:\\users\\ryan\\.codex\\packages\\app-server-daemon\\releases\\0.157.1-x86_64-pc-windows-msvc\\bin\\codex.exe", + "launchCount": 6, + "focusCount": 6, + "activeDurationSeconds": 6, + "lastUsedTimestamp": 1791227118678 + }, + "c:\\program files\\powershell\\7\\pwsh.exe": { + "name": "PowerShell 7", + "path": "c:\\program files\\powershell\\7\\pwsh.exe", + "launchCount": 0, + "focusCount": 1, + "activeDurationSeconds": 3, + "lastUsedTimestamp": 1791092072540 }, "c:\\portableapps\\freecad\\bin\\freecad.exe": { "name": "freecad", "path": "c:\\portableapps\\freecad\\bin\\freecad.exe", - "launchCount": 3, - "focusCount": 17, - "activeDurationSeconds": 4871, - "lastUsedTimestamp": 1788923536820 + "launchCount": 5, + "focusCount": 12, + "activeDurationSeconds": 567, + "lastUsedTimestamp": 1791164538716 }, - "c:\\portableapps\\irfanview\\app\\irfanview64\\i_view64.exe": { - "name": "IrfanView 64-bit", - "path": "c:\\portableapps\\irfanview\\app\\irfanview64\\i_view64.exe", - "launchCount": 3, + "c:\\program files\\nvidia corporation\\nvidia app\\cef\\nvidia app.exe": { + "name": "NVIDIA App", + "path": "c:\\program files\\nvidia corporation\\nvidia app\\cef\\nvidia app.exe", + "launchCount": 1, + "focusCount": 4, + "activeDurationSeconds": 34, + "lastUsedTimestamp": 1791163077450 + }, + "c:\\program files\\git\\cmd\\git.exe": { + "name": "Git for Windows", + "path": "c:\\program files\\git\\cmd\\git.exe", + "launchCount": 15, + "focusCount": 14, + "activeDurationSeconds": 15, + "lastUsedTimestamp": 1791229434986 + }, + "c:\\users\\ryan\\.codex\\packages\\app-server-daemon\\releases\\0.157.1-x86_64-pc-windows-msvc\\codex-path\\rg.exe": { + "name": "rg", + "path": "c:\\users\\ryan\\.codex\\packages\\app-server-daemon\\releases\\0.157.1-x86_64-pc-windows-msvc\\codex-path\\rg.exe", + "launchCount": 1, "focusCount": 1, - "activeDurationSeconds": 95, - "lastUsedTimestamp": 1788586171205 + "activeDurationSeconds": 1, + "lastUsedTimestamp": 1791229405819 }, - "h:\\steam\\steamapps\\common\\content warning\\content warning.exe": { - "name": "content warning", - "path": "h:\\steam\\steamapps\\common\\content warning\\content warning.exe", + "c:\\program files\\windowsapps\\canva.affinity_3.0.3.4027_x64__8a0j1tnjnt4a4\\app\\affinity.exe": { + "name": "Affinity", + "path": "c:\\program files\\windowsapps\\canva.affinity_3.0.3.4027_x64__8a0j1tnjnt4a4\\app\\affinity.exe", + "launchCount": 1, + "focusCount": 30, + "activeDurationSeconds": 3249, + "lastUsedTimestamp": 1791674556686 + }, + "c:\\program files\\tailscale\\tailscale-ipn.exe": { + "name": "Tailscale GUI client", + "path": "c:\\program files\\tailscale\\tailscale-ipn.exe", "launchCount": 2, - "focusCount": 10, - "activeDurationSeconds": 3812, - "lastUsedTimestamp": 1788754135308 + "focusCount": 6, + "activeDurationSeconds": 355, + "lastUsedTimestamp": 1791691505062 + }, + "c:\\program files\\nomachine\\bin\\nxrunner.bin": { + "name": "NoMachine Tray Monitor", + "path": "c:\\program files\\nomachine\\bin\\nxrunner.bin", + "launchCount": 1, + "focusCount": 2, + "activeDurationSeconds": 3, + "lastUsedTimestamp": 1791382888458 }, "c:\\program files\\7-zip\\7zfm.exe": { "name": "7-Zip File Manager", "path": "c:\\program files\\7-zip\\7zfm.exe", "launchCount": 1, - "focusCount": 3, - "activeDurationSeconds": 35, - "lastUsedTimestamp": 1788751440759 + "focusCount": 9, + "activeDurationSeconds": 20, + "lastUsedTimestamp": 1791434432754 }, - "c:\\program files\\cheat engine 7.5\\cheatengine-x86_64-sse4-avx2.exe": { - "name": "Cheat Engine", - "path": "c:\\program files\\cheat engine 7.5\\cheatengine-x86_64-sse4-avx2.exe", + "c:\\program files\\internet explorer\\iexplore.exe": { + "name": "Internet Explorer", + "path": "c:\\program files\\internet explorer\\iexplore.exe", + "launchCount": 11, + "focusCount": 10, + "activeDurationSeconds": 84, + "lastUsedTimestamp": 1791520033017 + }, + "c:\\portableapps\\irfanview\\app\\irfanview64\\i_view64.exe": { + "name": "IrfanView 64-bit", + "path": "c:\\portableapps\\irfanview\\app\\irfanview64\\i_view64.exe", + "launchCount": 7, + "focusCount": 6, + "activeDurationSeconds": 193, + "lastUsedTimestamp": 1791603651815 + }, + "c:\\users\\ryan\\appdata\\roaming\\spotify\\spotify.exe": { + "name": "Spotify", + "path": "c:\\users\\ryan\\appdata\\roaming\\spotify\\spotify.exe", "launchCount": 1, - "focusCount": 7, - "activeDurationSeconds": 32, - "lastUsedTimestamp": 1788754937452 + "focusCount": 4, + "activeDurationSeconds": 227, + "lastUsedTimestamp": 1791666292289 }, - "c:\\program files (x86)\\steam\\steam.exe": { - "name": "Steam", - "path": "c:\\program files (x86)\\steam\\steam.exe", + "c:\\portableapps\\gimpportable\\gimpportable.exe": { + "name": "GIMP Portable (PortableApps.com Launcher)", + "path": "c:\\portableapps\\gimpportable\\gimpportable.exe", "launchCount": 1, "focusCount": 1, - "activeDurationSeconds": 174, - "lastUsedTimestamp": 1788838072451 + "activeDurationSeconds": 2, + "lastUsedTimestamp": 1791604589829 }, - "h:\\steam\\steamapps\\common\\peak\\peak.exe": { - "name": "peak", - "path": "h:\\steam\\steamapps\\common\\peak\\peak.exe", + "c:\\portableapps\\gimpportable\\app\\gimp\\bin\\gimp-2.10.exe": { + "name": "GNU Image Manipulation Program", + "path": "c:\\portableapps\\gimpportable\\app\\gimp\\bin\\gimp-2.10.exe", + "launchCount": 1, + "focusCount": 13, + "activeDurationSeconds": 1473, + "lastUsedTimestamp": 1791683169760 + }, + "c:\\portableapps\\gimpportable\\app\\gimp\\lib\\gimp\\2.0\\plug-ins\\file-png\\file-png.exe": { + "name": "GNU Image Manipulation Program Plug-In", + "path": "c:\\portableapps\\gimpportable\\app\\gimp\\lib\\gimp\\2.0\\plug-ins\\file-png\\file-png.exe", "launchCount": 1, "focusCount": 1, - "activeDurationSeconds": 22, - "lastUsedTimestamp": 1788838292184 + "activeDurationSeconds": 1, + "lastUsedTimestamp": 1791605126035 }, - "d:\\python utilities\\icue\\app\\dist\\icue assigner\\icue assigner.exe": { - "name": "icue assigner", - "path": "d:\\python utilities\\icue\\app\\dist\\icue assigner\\icue assigner.exe", + "c:\\program files\\mythicsoft\\agent ransack\\agentransack.exe": { + "name": "Agent Ransack", + "path": "c:\\program files\\mythicsoft\\agent ransack\\agentransack.exe", "launchCount": 1, - "focusCount": 1, - "activeDurationSeconds": 8, - "lastUsedTimestamp": 1788923506351 + "focusCount": 6, + "activeDurationSeconds": 291, + "lastUsedTimestamp": 1791737327579 }, - "c:\\portableapps\\dimmer\\dimmer.exe": { - "name": "Dimmer - Brightness control software", - "path": "c:\\portableapps\\dimmer\\dimmer.exe", - "launchCount": 1, - "focusCount": 1, - "activeDurationSeconds": 6, - "lastUsedTimestamp": 1788924847522 + "c:\\program files (x86)\\windows media player\\wmplayer.exe": { + "name": "Windows Media Player", + "path": "c:\\program files (x86)\\windows media player\\wmplayer.exe", + "launchCount": 3, + "focusCount": 2, + "activeDurationSeconds": 69, + "lastUsedTimestamp": 1791672161738 } } \ No newline at end of file diff --git a/dynamic-deck/src/utils/app-launcher.ts b/dynamic-deck/src/utils/app-launcher.ts index ed96613..fd8c911 100644 --- a/dynamic-deck/src/utils/app-launcher.ts +++ b/dynamic-deck/src/utils/app-launcher.ts @@ -8,6 +8,33 @@ import streamDeck from "@elgato/streamdeck"; const execFileAsync = promisify(execFile); +async function launchOutsidePluginJob(commandLine: string, currentDirectory: string): Promise { + // Stream Deck can place its plugin process tree in a Windows job object. Node's + // `detached` option only detaches the console, so ask the WMI service to create + // the app process instead; Win32_Process.Create does not inherit that job. + const encodedCommandLine = Buffer.from(commandLine, "utf16le").toString("base64"); + const encodedCurrentDirectory = Buffer.from(currentDirectory, "utf16le").toString("base64"); + const script = ` +$commandLine = [Text.Encoding]::Unicode.GetString([Convert]::FromBase64String('${encodedCommandLine}')) +$currentDirectory = [Text.Encoding]::Unicode.GetString([Convert]::FromBase64String('${encodedCurrentDirectory}')) +$result = Invoke-CimMethod -ClassName Win32_Process -MethodName Create -Arguments @{ + CommandLine = $commandLine + CurrentDirectory = $currentDirectory +} +if ($result.ReturnValue -ne 0) { + throw "Win32_Process.Create failed with code $($result.ReturnValue)" +} +`; + const encodedScript = Buffer.from(script, "utf16le").toString("base64"); + + await execFileAsync("powershell.exe", [ + "-NoProfile", + "-NonInteractive", + "-EncodedCommand", + encodedScript + ], { windowsHide: true, timeout: 15000 }); +} + export interface InstalledApp { name: string; path: string; @@ -311,18 +338,11 @@ static async activateOrLaunchApp(targetPath: string, bringToFront = true): Promi if (!activated) { if (isUrlShortcut) { logger.info(`[AppLauncher] Launching URL shortcut at path "${cleanPath}"`); - execFile("cmd.exe", ["/c", "start", "", cleanPath], (err: Error | null) => { - if (err) { - streamDeck.logger.error(`[AppLauncher] Failed to launch URL shortcut at path "${cleanPath}":`, err); - } - }); + const explorerPath = path.join(process.env.WINDIR || "C:\\Windows", "explorer.exe"); + await launchOutsidePluginJob(`"${explorerPath}" "${cleanPath}"`, path.dirname(cleanPath)); } else { logger.info(`[AppLauncher] Launching executable at path "${cleanPath}"`); - execFile(cleanPath, (err: Error | null) => { - if (err) { - streamDeck.logger.error(`[AppLauncher] Failed to launch executable at path "${cleanPath}":`, err); - } - }); + await launchOutsidePluginJob(`"${cleanPath}"`, path.dirname(cleanPath)); } } } @@ -671,4 +691,4 @@ public static async getInstalledApps(): Promise { return null; } -} \ No newline at end of file +}