# Shell & process verbs

Run shell commands, scripts, WSL, and launch/locate apps on the laptop.

_Part of the [Adom Desktop verb reference](VERB-REFERENCE.md). Invoke as `adom-desktop <verb> '<json>'`._

### `find_exe`

Find an executable without launching it. Searches: (1) absolute path as-is, (2) glob expansion for webdeploy-style version directories, (3) Start Menu .lnk targets, (4) PATH. Returns the resolved absolute path or an error.

**Args:**
  - `name` — required: bare name ('Fusion360.exe'), glob ('.../webdeploy/production/*/Fusion360.exe'), or full path
  - `searchPaths` — optional array of extra dirs to scan

**Returns:** path, source (absolute|glob|startmenu|path)


## launch

### `args`


### `run_script`

v1.8.46+: Execute a base64-encoded script via a chosen interpreter (cmd / powershell / bash). The script bytes never touch a shell-string parser, so NO quoting/escaping survives to be mangled by cmd.exe, PowerShell's parser quirks, or MSYS path conversion — caller never hand-escapes anything. Host-side: cmd / powershell get a temp file (.cmd / .ps1); bash gets the script via stdin (no temp file). Output decoded as UTF-8 (UTF-16LE detection for PowerShell 5.1 output). Returns stdout/stderr/exitCode and cleans up any temp file.

**Args:**
  - `allowPopupRisk` — optional bool (v1.8.117+) — bypass the popup-risk pre-flight on cmd scripts. AD refuses (errorCode `popup_risk`, failedAt `popup_scan`, scriptLine N) any `start <target>` line whose target doesn't resolve — that flashes a modal 'Windows cannot find' dialog on the user's desktop. powershell/bash scripts aren't scanned (no ShellExecute popup failure mode).
  - `interpreter` — required string: 'cmd' | 'powershell' | 'bash'. PowerShell prefers pwsh (PS 7+) if on PATH, falls back to powershell.exe (5.1). Bash uses whichever is first on PATH — on Windows that's typically WSL2's System32\bash.exe (the intended interpreter; stdin piping means no path-form mismatch). For explicit distro / user control on WSL, use wsl_exec.
  - `scriptB64` — required string: base64-encoded script content (no length limit beyond practical WS message size — keep under a few MB for sanity).
  - `timeoutSeconds` — optional int (default 60). The CLI's WS timeout is auto-sized to scriptTimeout + 15s margin.

**Returns:** On success: {success:true, ok:true, stdout, stderr, exitCode, durationMs, interpreter, [_hint?]}. On failure: {success:false, ok:false, stdout:'', stderr:'', exitCode:-1, error, failedAt, _hint}. failedAt is one of: 'args' (interpreter unknown / scriptB64 empty), 'decode' (base64 not parseable), 'tempfile' (cmd/powershell temp-file write failed — see tempPath in payload), 'spawn' (interpreter exe couldn't launch — see spawn_failure_hint output), 'timeout' (script ran longer than timeoutSeconds). EVERY failure path carries a _hint with the specific recovery recipe — READ the hint, don't guess. **v1.8.60+ ADVISORY**: success responses may also include an optional `_hint` field when the script looks like a direct HD build (raw `cargo`/`pnpm` against `hydrogen-desktop`/`crates/hd-*` paths). The hint is additive — exit code / stdout / stderr are UNCHANGED — and points at the dedicated `hd_build_*` / `hd_ship` verbs.

**Note:** Requires shell auto-approve to be active — same trust level as shell_execute. Enable with `shell_auto_approve '{"duration_secs": 86400}'` (24h) or `'{"permanent": true}'` (no expiry). Embedded mode defaults to permanent. Refuses cleanly with an actionable error if not approved (no per-call modal — these are programmatic verbs, not interactive).


### `shell_execute`

Execute a shell command on the user's desktop (requires user approval). Escape hatch for things that don't have a structured verb yet — most common operations are covered by desktop_install_node / desktop_install_kicad / desktop_list_files / desktop_watch_files / desktop_list_browsers etc., which DON'T require per-call approval. Use shell_execute only when no structured verb exists for the task.

**Args:**
  - `allowPopupRisk` — optional bool (v1.8.117+) — bypass the popup-risk pre-flight. AD refuses (errorCode `popup_risk`) any `start <target>` whose target doesn't resolve, because that flashes a modal 'Windows cannot find' dialog on the user's desktop. Verify targets first (find_exe / launch / desktop_open_url); only pass true when the command creates its own target and the scan misjudged it.
  - `command` — required string — full shell command (Windows: runs through `cmd /c`; macOS/Linux: through `sh -c`)
  - `cwd` — optional working directory
  - `timeoutSeconds` — optional int (default 30, max effectively unbounded — pass whatever your task needs). v1.7.3+ this is actually honored end-to-end. Pre-v1.7.3 the CLI accepted it for its own HTTP socket but dropped it before forwarding, so the desktop side defaulted to 120s and killed long-running tasks regardless. Long rust builds / ffmpeg renders / docker pulls etc. should set 600-1800s.

**Returns:** stdout, stderr, exitCode

**Note:** User must approve via in-app dialog unless auto-approve is active. v1.7.3+ timeout forwarding bug fixed — your timeoutSeconds is now respected by the desktop-side shell killer. v1.7.16+ approval-polling fixed — the CLI now polls the relay's deferred-result endpoint with the original requestId instead of re-sending the command every second (which v1.7.15 did, causing infinite 'Another shell command is already waiting for approval' loops because every retry created a fresh approval the user could never out-click).


### `shell_kill_all`

Kill all running shell command processes


### `wsl_exec`

v1.8.46+: Execute a base64-encoded script inside a WSL distro via the canonical `wsl -d <distro> -u <user> -- bash -lc <decoded>` argv array. Eliminates the --, nested-quote, and $_ pitfalls because Rust's std::process::Command on Windows applies proper CommandLineToArgvW quoting per argv element — wsl.exe sees `bash -lc <decoded>` as three distinct argv items, then bash's -c sees the entire script as ONE string with no outer-shell interpretation. The exact 50×-per-session call pattern.

**Args:**
  - `distro` — required string: WSL distro name (e.g. 'Adom-Workspace', 'Ubuntu'). `wsl -l -v` enumerates installed distros.
  - `scriptB64` — required string: base64-encoded bash script. Decoded content must be valid UTF-8.
  - `timeoutSeconds` — optional int (default 60). The CLI's WS timeout auto-sizes to scriptTimeout + 15s margin.
  - `user` — required string: WSL user to run as (e.g. 'adom', 'root'). Must exist in the distro.

**Returns:** On success: {success:true, ok:true, stdout, stderr, exitCode, durationMs, interpreter:'wsl', distro, user, [_hint?]}. wsl.exe's UTF-16LE output is decoded as text automatically (BOM-detect + odd-position-NULL heuristic, fall back to UTF-8 lossy). On failure: {success:false, ok:false, stdout:'', stderr:'', exitCode:-1, error, failedAt, _hint}. failedAt is one of: 'args' (distro/user/scriptB64 empty), 'decode' (base64 invalid), 'utf8' (decoded bytes not UTF-8), 'spawn' (wsl.exe couldn't launch — see wsl_error_hint), 'timeout' (script exceeded timeoutSeconds). EVERY failure carries an actionable _hint. The most common spawn failure is 'distro not installed' — the hint will tell you to run `wsl -l -v` to enumerate, or fall back to run_script interpreter:bash for the default distro. **v1.8.60+ ADVISORY**: success responses may carry an additive `_hint` when the script looks like a direct HD build (raw `cargo`/`pnpm` targeting `hydrogen-desktop`/`crates/hd-*`); the hint points at the dedicated `hd_*` verbs that handle the exe lock + SHA verification.

