From 1a5cec2de2d33b244763cbe3b8502a5419f5f8fc Mon Sep 17 00:00:00 2001 From: Nathan Broadbent Date: Fri, 9 Oct 2026 16:20:26 +1300 Subject: [PATCH] CLI: focus the PIN field in the macOS security key dialog When the CLI runs in the background (e.g. from an agent), osascript isn't the frontmost app, so the dialog appeared without keyboard focus and the PIN field had to be clicked before typing. The script now runs `activate` first, which brings the dialog to the front with the field focused. No extra macOS permissions are needed. --- internal/cli/pinentry/pinentry.go | 4 +++- internal/cli/pinentry/pinentry_test.go | 9 +++++++++ 2 files changed, 12 insertions(+), 1 deletion(-) diff --git a/internal/cli/pinentry/pinentry.go b/internal/cli/pinentry/pinentry.go index 5dea2a98..6df0dd25 100644 --- a/internal/cli/pinentry/pinentry.go +++ b/internal/cli/pinentry/pinentry.go @@ -31,8 +31,10 @@ const ( ) // dialogScript shows the message passed as the first argument (never interpolated into the script) -// and prints the entered PIN to stdout. +// and prints the entered PIN to stdout. `activate` brings osascript to the front first: when the CLI +// runs in the background (e.g. from an agent) the dialog otherwise opens without keyboard focus. const dialogScript = `on run argv + activate set reply to display dialog (item 1 of argv) with title "rack-gateway" default answer "" ¬ with hidden answer buttons {"Cancel", "Approve"} default button "Approve" cancel button "Cancel" ¬ with icon caution giving up after 170 diff --git a/internal/cli/pinentry/pinentry_test.go b/internal/cli/pinentry/pinentry_test.go index 5a416504..fa867095 100644 --- a/internal/cli/pinentry/pinentry_test.go +++ b/internal/cli/pinentry/pinentry_test.go @@ -97,3 +97,12 @@ func TestDescribeCommandTruncatesLongCommands(t *testing.T) { t.Fatalf("describeCommand length = %d, want %d ending in ...", len(got), maxCommandLength+3) } } + +func TestDialogScriptActivatesBeforeShowingDialog(t *testing.T) { + activate := strings.Index(dialogScript, "\tactivate\n") + dialog := strings.Index(dialogScript, "display dialog") + if activate < 0 || activate > dialog { + t.Fatalf("dialog script must activate osascript before the dialog so the PIN field has focus:\n%s", + dialogScript) + } +}