peekaboo dialog
dialog wraps DialogService so you can programmatically inspect, click, set text in, dismiss, or drive file dialogs without re-running see. Exact targeted click, non-forced dismiss, and input stay in the background by default. Global keyboard/coordinate paths are never implicit: targetless input, file, and dismiss --force require --foreground.
Without a target, dialog list and dialog click --button "Don't Allow" discover macOS system-permission alerts (including TCC) in the background, checking the focused owner and the allowlisted com.apple.UserNotificationCenter and com.apple.SecurityAgent hosts by exact bundle and executable identity. Discovery is bounded and read-only: it never focuses, raises, or switches Spaces. Listing includes owner, window receipt, text, and button availability; automatic clicks require complete discovery, one dialog, and one enabled AXPress button with the exact name (straight and curly apostrophes are equivalent), then use an exact receipt and verify disappearance.
#Subcommands
| Name | Purpose | Key options |
|---|---|---|
click | Press a dialog button with AX. | --button <exact label> is required; app/PID/window targets are optional; --foreground may focus first but never enables pointer fallback. |
input | Set text in a dialog field. | --text plus an app/PID/window target default to background AXValue. Use --foreground only for targetless/global keyboard input. Optional --field <label> or --index <0-based> and --clear. |
file | Drive NSOpenPanel/NSSavePanel style dialogs. | --foreground is required; an app/PID/window target is optional and recommended. Supports --path <dir>, --name <filename>, --select <button>, --ensure-expanded, and --timeout <duration>. Save-like actions verify the file exists and return saved_path. |
dismiss | Close the current dialog. | Normal dismissal requires a target and uniquely resolves one cancel/close AXPress button in the background. --force --foreground explicitly sends global Escape. |
list | Read dialog metadata (buttons, text fields, static text) without focusing or mutating it. | Optional --app/--pid, optional --window-id/--window-title/--window-index, and --timeout <duration>. |
#Implementation notes
- Every
dialog listform is read-only/background and creates no mutation or snapshot debt. A targeted list must resolve exactly one dialog; ambiguity reports candidate window IDs. - Static text includes nested
AXStaticTextdescendants of the resolved dialog in depth-first child order, once per AX element. Extraction follows structural children and does not enter other application/window roots. It uses the first nonblank string fromAXValue,AXTitle,AXLabel, thenAXDescription, preserving the selected string's whitespace; button labels and editable field values remain in their separate lists. Alert message and informative text do not replace the dialog's own title. - When an exact titled parent AX window contains one structural child sheet or alert, Peekaboo treats that child as the active dialog while retaining the parent window receipt. The parent remains targetable when it has no structural child, and multiple child dialogs remain ambiguous. If a blank-title transient sheet CGWindow ID does not resolve, use the titled parent AX window ID reported by
window list. - Targeted structural discovery reads Accessibility nodes on a bounded background worker lane. Local calls share the caller's monotonic timeout across windows and nested discovery/revalidation passes, without fixed node/depth cutoffs. CLI
listkeeps its 5-second default andfileits 20-second default, and explicit longer timeouts are preserved locally. Services without a local caller budget, including Bridge-hosted discovery, use a 20-second fallback. Bridge wire deadlines are unchanged: remote calls retain the existing transport timeout and cancellation behavior, not cross-process propagation of the CLI timeout or support for a host discovery budget above 20 seconds. Older hosts retain their own existing behavior. - Targeted listing reads the selected dialog's metadata on the same bounded background worker and remaining caller deadline as structural discovery. Stalled reads cannot hold the main actor or publish late candidates or metadata, and unreadable classification refuses before any mutation. An abandoned native read retains its worker slot until it actually returns, so another read for that process generation can refuse meanwhile. These bounds do not extend to metadata extraction through the legacy
windowTitle/appNameservice overload or to action verification. - Cancellation abandons the result, not the in-flight native AX call. The worker can continue read-only metadata work when that call returns, subject to checks of the original deadline; this is not a guarantee of native read cancellation.
- Targeted metadata accepts native strings and attributed strings as text without stringifying numeric or opaque values. Missing optional metadata keeps its existing defaults; this output-only reading policy does not weaken the complete structural classification used to establish a unique dialog.
- Dialog-list timeout failures use JSON code
TIMEOUT; unreadable structural evidence usesACCESSIBILITY_INCOMPLETE, not an empty successful inventory orUNKNOWN_ERROR. - Default background click and non-forced dismiss use Bridge protocol 1.25's two-phase contract: a read-only prepare operation uniquely upgrades app/PID/title selectors to one exact process-generation/window receipt and retains the raw AX window, dialog, and button identities under a short-lived one-shot token.
- Immediately before AXPress, Peekaboo consumes the token, reacquires the exact-window write lane, re-enumerates and compares all three raw AX identities, and rechecks owner generation, window bounds, enabled state, and AXPress support. It never falls back to the physical pointer, clipboard, focus, or global input.
- Success is confirmed only after the retained dialog or sheet disappears. An accepted press without a verified postcondition is retry-unsafe and requires fresh observation; planning or identity ambiguity is a retry-safe pre-dispatch refusal.
- Remote targeted list/prepare/click/dismiss require the exact advertised and enabled operation, not merely a 1.25 version number. Missing capabilities refuse before operation transport.
- Exact targeted
dialog inputresolves and revalidates one process-generation/window target, then sets the selected field through background AXValue. Targetless input uses global keyboard delivery and requires--foreground(orforeground: trueover MCP). dialog fileand forced dismissal use global keyboard or coordinate events and therefore require foreground consent. For compatibility with interactive foreground workflows, input and file may target the current dialog only in that foreground mode. Receipt-pinned non-forced dismiss requires a target.- Button clicks and text entry route through
services.dialogshelpers, which return dictionaries describing what happened; JSON output exposes those details verbatim (button,field,text_length, etc.). dialog inputaccepts either a field label (--field) or an index; when neither is provided it targets the first text field.--clearreplaces the value directly in background AXValue mode; the targetless foreground route uses Cmd+A/Delete before typing.dialog filecan both navigate to a path and fill the filename field, then clicks the action button you specify (--select Save,--select Open, etc.). Leave--pathblank to simply confirm the current directory.- App-targeted file operations select a unique file panel before checking ambiguity with other dialogs. Attached save/open sheets retain their parent window receipt; compatible non-structural file panels remain supported. Two file panels still refuse before input, and the selected panel and parent are revalidated together.
- Targeted file operations plan the original selector before focus. The file-dialog service owns focus and its dispatch accounting, preserving the document parent receipt even when macOS focuses a separate sheet.
--no-auto-focusverifies the existing focus without activating the app; it does not remove the foreground requirement. Native keyboard/pointer dispatch checks the retained parent and current sheet immediately before delivery. Go to Folder retains a separate nested sheet and field, without replacing the original file panel. - Exact file execution over Bridge requires protocol 1.43 plus the advertised and enabled
exactFileDialogExecutioncapability, an attested session, and a supporting provider. The version alone is insufficient; older hosts refuse without a legacy/global fallback. - Targeted save verification reads
AXDocumentonly from the retained raw parent, never another document window or an unrelated fallback directory. Explicit expected-path and requested-directory checks remain available. Verification gets its own five-second polling period; file freshness is still measured from the operation's original start. - If Save closes or temporarily rebuilds that parent, unavailable post-action document metadata does not abort expected-path or requested-directory verification. Parent metadata can recover during polling; pre-action and overwrite-dispatch identity checks remain strict.
- Native
open-panelandsave-panelidentifiers remain recognizable with blank or localized titles. File selection reuses that pass's complete classification evidence; revalidation reads it afresh. Batched AX reads preserve individual errors and full descendant traversal rather than treating partial reads as an empty or unique dialog inventory. dialog filedefaults to clicking the dialog’sOKButtonwhen--selectis omitted (or set todefault). Prefer this when you don’t want to guess whether the button is labeled “Save”, “Open”, “Choose”, etc.--ensure-expandedexpands the dialog (Show Details) before applying--path. If noPathTextFieldis present, Peekaboo falls back to the standard “Go to Folder…” shortcut to reliably land in the requested directory.- For save-like actions (resolved by the actual clicked button title),
dialog fileverifies that the saved file appears on disk (5s timeout). On success it returnssaved_pathandsaved_path_verified=true. If you provided--path+--name, Peekaboo also enforces that the file landed in the requested directory (symlinks like/tmp→/private/tmpare normalized). saved_path_matches_expectedcompares normalized, symlink-resolved file URLs, so equivalent aliases match without rewriting the reportedsaved_path. A filename mismatch within the requested directory is diagnostic only; directory enforcement still rejects a different parent directory.- JSON output includes additional provenance for debugging without screenshots, including
dialog_identifier,found_via,button_identifier,saved_path_found_via, andpath_navigation_method(e.g.path_textfield_typed+fallback_go_to_folder). dialog listis invaluable before scripting a dialog: it prints button titles, placeholders, and static text so you can pick stable labels instead of guessing.
#Examples
# Click "Don't Save" on a TextEdit sheet
peekaboo dialog click --button "Don't Save" --app TextEdit
# Set a targeted password field through background AXValue
peekaboo dialog input --text hunter2 --field "Password" --clear --app Safari
# Choose a file in an open panel and confirm
peekaboo dialog file --path ~/Downloads --name report.pdf --select Open --foreground
# Save a file and verify the resulting path exists
peekaboo dialog file --path /tmp --name poem.rtf --select Save --app TextEdit --foreground --json
# Click the default action (OKButton) and include dialog provenance in JSON output
peekaboo dialog file --path ~/Downloads --name report.pdf --ensure-expanded --app TextEdit --foreground --json
#Troubleshooting
- Verify Screen Recording + Accessibility permissions (
peekaboo permissions status). - Confirm your process with
peekaboo app list, its exact window withpeekaboo window list, and current UI withpeekaboo seebefore rerunning. - Re-run with
--jsonor--verboseto surface detailed errors.