I’ve written a script that starts by asking for the user’s password, which gets stored in a variable and used to populate multiple password dialogs as they appear; quitting after 2min of no dialogs found. The problem is that only one dialog is ever found, while I can see many more onscreen.
Technically this is JavaScript, but it follows AppleScript:
const GET_CREDENTIALS_WINDOW_INTERVAL = 0.25;
const GET_CREDENTIALS_WINDOW_TIMEOUT = 120;
let app;
class TimedOutError extends Error {
constructor() {
super('Timed out looking for a credentials window');
}
}
const getCredentials = () => ({
password: app.displayDialog('Enter your password', { defaultAnswer: '', hiddenAnswer: true }).textReturned,
username: app.systemInfo().shortUserName,
});
let attempt = 0;
const populateCredentialsWindows = (password, duration = 0) => {
attempt++;
if (duration >= GET_CREDENTIALS_WINDOW_TIMEOUT) {
throw new TimedOutError();
}
const systemEvents = Application('System Events');
systemEvents
.processes()
.filter(process => process.name() === 'SecurityAgent')
// Not sure if there's ever more than one process, but this should cover such
.forEach((process, _, { length: numProcesses }) => {
console.log(`(${attempt}) numProcesses: ${numProcesses}`);
process.windows().forEach((win, _, { length: numWindows }) => {
// Avoids issue where subsequent windows wouldn't get focused
//delay(GET_CREDENTIALS_WINDOW_INTERVAL);
//systemEvents.activate();
//app.frontmost = true;
//process.frontmost = true;
//win.actions['AXRaise'].perform();
console.log(`(${attempt}) numWindows: ${numWindows}`);
win.textFields[0].value = password;
win.buttons['Allow'].click();
// Reset timeout
duration = 0;
});
});
delay(GET_CREDENTIALS_WINDOW_INTERVAL);
duration += GET_CREDENTIALS_WINDOW_INTERVAL;
populateCredentialsWindows(password, duration);
};
const run = () => {
app = Application.currentApplication();
app.includeStandardAdditions = true;
const { password } = getCredentials();
try {
return populateCredentialsWindows(password);
} catch (error) {
if (!(error instanceof TimedOutError)) {
throw error;
}
}
};
run();
AppleScript: 2.7
Browser: Safari 537.36
Operating System: macOS 10.14
All is simpler when using AppleScript instead of JXA. JXA has many bugs and is abandoned as well. Run following script within Script Editor or as application:
property GET_CREDENTIALS_WINDOW_INTERVAL : 2
property GET_CREDENTIALS_WINDOW_TIMEOUT : 120
set myPassword to text returned of (display dialog "Enter your password" default answer "" giving up after GET_CREDENTIALS_WINDOW_TIMEOUT with hidden answer)
if myPassword = "" then error number -1712
tell application "System Events" to tell process "SecurityAgent"
repeat (count of windows) times
set frontmost to true
keystroke myPassword & return
delay GET_CREDENTIALS_WINDOW_INTERVAL
end repeat
end tell
Thanks! Although, I’m getting:
Also, the 2min timeout is for the absence of the last SecurityAgent dialog, not the initial input from the script.
I’ll try a few things, but I’m really new to AppleScript.
You get this error because no any security dialog opened by you before running my script.
To test do following: create 2 empty folders on the desktop, make them read-only, then lock them, then right click them to trash. Will pop up 2 security prompts to enter the password. Run my script.
I’m trying to write a script that can be ran before the popups appear, as they will continue to appear during a complicated build/compile.
This won’t populate anything:
property GET_INTERVAL : 2
property INACTIVITY_TIMEOUT : 120
property TIMEOUT_ERROR : -1712
set userPassword to text returned of (display dialog "Enter your password" default answer "" with hidden answer)
tell application "System Events"
set inactivityDuration to 0
repeat until exists process "SecurityAgent"
delay GET_INTERVAL
set inactivityDuration to inactivityDuration + GET_INTERVAL
if inactivityDuration ≥ INACTIVITY_TIMEOUT then error number TIMEOUT_ERROR
end repeat
# Reset
set inactivityDuration to 0
repeat until inactivityDuration ≥ INACTIVITY_TIMEOUT
if (count of windows) = 0 then
delay GET_INTERVAL
set inactivityDuration to inactivityDuration + GET_INTERVAL
else
tell process "SecurityAgent"
repeat (count of windows) times
set frontmost to true
keystroke userPassword & return
delay GET_INTERVAL
end repeat
end tell
# Reset
set inactivityDuration to 0
end if
end repeat
error number TIMEOUT_ERROR
end tell
if (count of windows of process "SecurityAgent") = 0 then -- EDITED
and add
set frontmost of process "SecurityAgent" to frontmost
before
repeat until inactivityDuration ≥ INACTIVITY_TIMEOUT line
Thanks, that makes it at least sort of work. This populates the first two dialogs, which is better than the JavaScript version which only populated the first. Any subsequent dialogs are not getting populated. I think it’s because SecurityAgent eventually closes when there’re no windows
Simplified:
property GET_INTERVAL : 2
property INACTIVITY_TIMEOUT : 120
property TIMEOUT_ERROR : -1712
set userPassword to text returned of (display dialog "Enter your password" default answer "" with hidden answer)
tell application "System Events"
set inactivityDuration to 0
repeat until inactivityDuration ≥ INACTIVITY_TIMEOUT
if not (exists process "SecurityAgent") or (count of windows of process "SecurityAgent") = 0 then
delay GET_INTERVAL
set inactivityDuration to inactivityDuration + GET_INTERVAL
else
tell process "SecurityAgent"
#activate
#set frontmost to true
repeat with win in windows
set frontmost to true
#perform action "AXRaise" of win
#click at {100, 100}
#delay 0.1
#keystroke userPassword & return
keystroke userPassword
#delay 0.1
keystroke return
delay GET_INTERVAL
end repeat
# Reset
set inactivityDuration to 0
end tell
end if
end repeat
# Done
error number TIMEOUT_ERROR
end tell
… but only ever finds one of the many visible password dialogs:
… before repeating this:
I can get it to work by manually clicking the window, entering a single character into the password field and selecting all text in it so that it can be replaced by the script. This is obviously not ideal.