Skip to main content
hrmnjt.dev::sttp://secure_thought_transfer_protocol

ivanti, clickops and quirks of osascript

This is a short and sharp one.

One of the necessary evils at work is VPN. I’m yet to experience a non-clunky VPN and I believe this is by design. The VPN software is generally gatekeeped and obscured to make it difficult for the end-user to bypass security controls. What I crave is a better developer experience when using VPN. (I’ve heard good things about Tailscale; but I’m yet to try it or other alternatives.) But, here I’m digresssing. At work, we need to use Ivanti VPN which on MacOS installs and provides a convenient menubar icon.

My workflow:

The clunky part is that there are times when I need to look at reference documentation and adjust my approach/code on Databricks and that doesn’t open or works too slow on VPN. And I have to continiously “suspend” and “resume” VPN when I need to do so. So, for my weekend side project quest, I spent sometime to learn osascript which is a quirky language and my only option to avoid clicks on menubar icon all the time. The final script is here.

I enjoyed writing it and trying it out until it got serious and then I felt constrained. If I want to list running processes, I should do

tell application "System Events" to get name of every process

instead of

system_events.processes.map(p => p.name)

This is because osascript or AppleScript was designed in early 90s for non-programmers to express automations. It reads like a sentence and reminded me of “DHH explaining Ruby” on Lex’s podcast. But beyond the basics the “natural” language syntax becomes more confusing that it needs to be. It also becomes hard to compose. e.g.

click menu item "Connect" of menu "VPN" of menu item "VPN" of menu 1 of menu bar item 1 of menu bar 2

Understanding the exact heirarchy requires you to build the mental model of the UI tree or design pattern in context. Its noun and verbs to parse UI AST and that is the biggest quirks in my view. Some other things that were weird

tell ... end tell: who am I “talking to”? objects?

of vs .: I can say parent.child or I can say child of parent which requires me to reverse the heirarchy and change my mental model

every instead of loops: I should say name of every process instead of an iteration/iterator

killall to prevent freezing application: I’m amazed that this is the suggested approach from Apple to unstuck applications or ignore application responses.

I just scratched the surface of osascript and I don’t think I want to go deeper. In fact, after implementing the whole solution, I learned that Apple launced JavaScript for Automation (JXA) in 2014 as an alternative to AppleScript. I don’t want to rewrite the current implementation because I’m equally noob in JavaScript (at least not right now).