I am using Script Editor and have it set to JavaScript and trying to read the value of a variable in Keyboard Maestro and have spent over 2 hours on this and after much searching and AI can not seem to do this simple task. Does anyone know how?
var daysToAdd = document.kmvar.daysToAdd;
console.log(daysToAdd);
Since you are requesting to get advice from an AI, here's the result I got. Give it a try.
Yes! If you’re running JavaScript in Script Editor on macOS using JavaScript for Automation (JXA), you can still set a Keyboard Maestro variable using the Keyboard Maestro Engine application.
Setting a Keyboard Maestro Variable from JXA in Script Editor
Open Script Editor (/Applications/Utilities/Script Editor.app).
Set a variable in Keyboard Maestro using JXA:
var km = Application("Keyboard Maestro Engine");
km.setvariable("MyKMVar", { to: "Hello from Script Editor!" });
Now, in Keyboard Maestro, you can use %Variable%MyKMVar% to access the value.
Reading a Keyboard Maestro Variable in Script Editor (JXA)
If you want to read a Keyboard Maestro variable inside Script Editor:
var km = Application("Keyboard Maestro Engine");
var myValue = km.getvariable("MyKMVar");
console.log(myValue); // Logs the KM variable value
Example: Modifying and Using the Value
If you want to modify the variable and update it in KM:
var km = Application("Keyboard Maestro Engine");
var myValue = km.getvariable("MyKMVar") || "Default Value";
myValue = myValue + " - Updated by JXA";
km.setvariable("MyKMVar", { to: myValue });
console.log("Updated KM Variable: " + myValue);
This method allows Script Editor, JXA scripts, and Keyboard Maestro to communicate with each other.
Is this to prepare and test code which you will use in an Execute JavaScript for Automation action with the default Modern Syntax setting ?
(JavaScript interpreters run in various contexts – browsers, Script Editor, Keyboard Maestro, Omni apps, etc etc and the mechanisms for getting named values from KM depend more on the context than on JavaScript itself.
The DOM document object for example, is not part of JS, and is defined only in the the API of browsers – so it might appear in code for Execute JS in Browser actions, but is not relevant to – or available in – Script Editor)
Presuming you are using the Execute a JavaScript For Automation action, select the Help item from the gear menu in the action and read the section Using Keyboard Maestro Variables.
JavaScript for Automation does not have a document.
I actually have no idea why other than I am trying to program some things for OmniFocus in their modern syntax of JavaScript rather than AppleScript because it seems like it can do more and runs faster. I don't know anything about JavaScript and have dozens of AppleScript macros for OmniFocus but the same thing with JavaScript seems to run much faster and be availble in other ways inside OmniFocus via right clicking.
I had no idea about any of this and that is great to know. I paid for Claude yesterday because it seemed to be getting me further than ChatGPT 4 or Gemini which for a short bit seemed to do well but 95% of the time gives me worse results then the former two.
Thank you for this. I am so clueless as to what I am doing other than knowing I want to get a variable from Keyboard Maestro and reference it in JavaScript to run several macros that will depend on these variables that I will change using Keyboard Maestro and will inturn create actions in OmniFocus. I am trying to glue together what I know and what I don't know and not even sure what to ask exactly.
So in the following is this not reading because I need to state a context of switching it to make Keyboard Maestro be what it pulls this from.
The "JavaScript for Automation" and "Execute JavaScript for Automation" actions in Keyboard Maestro are essentially the same thing. They both allow you to run JavaScript code within the Keyboard Maestro environment.
The difference lies in the name and how they are used:
Execute a JavaScript for Automation: This is the official name of the action in Keyboard Maestro. It's the most common and descriptive way to refer to this functionality.
JavaScript for Automation (JXA): This is a shorthand way of referring to the JavaScript for Automation concept. It's sometimes used informally.
Both terms refer to the same underlying technology: the ability to run JavaScript code within Keyboard Maestro to automate tasks and interact with applications.
The reason for the two names is likely historical. Keyboard Maestro may have originally used the "Execute JavaScript for Automation" name, and the shorter "JXA" form might have emerged as a more concise way to refer to it. However, both terms are widely understood to mean the same thing in the context of Keyboard Maestro.
So, you can use either term interchangeably. "Execute a JavaScript for Automation" is the official name, but "JXA" is a common shorthand.
ChatGPT 4.0
The difference between "JavaScript for Automation" (JXA) and "Execute JavaScript for Automation" in Keyboard Maestro is primarily about where and how the JavaScript code runs.
JavaScript for Automation (JXA)
This refers to Apple's JavaScript-based automation environment, introduced in macOS Yosemite, allowing you to automate Mac apps using JavaScript instead of AppleScript.
It runs in the macOS JavaScriptCore environment and can interact with macOS applications, files, and system services similarly to AppleScript.
It is not limited to web browsers—it can control Finder, Safari, and other macOS apps.
"Execute JavaScript for Automation" Action in Keyboard Maestro
This specific Keyboard Maestro action executes JavaScript in the macOS JXA environment (not inside a browser).
It allows you to run system-level automation scripts that interact with macOS applications.
Difference from "Execute JavaScript in Browser"
"Execute JavaScript in Browser" is used to run JavaScript inside a web browser (like Safari or Chrome).
"Execute JavaScript for Automation" runs JavaScript outside of a browser, in the macOS automation environment.
Example Use Case:
If you want to retrieve a Keyboard Maestro variable using JavaScript, you'd use:
javascript
CopyEdit
var km = Application("Keyboard Maestro Engine");
var value = km.getvariable("MyKMVariable");
console.log(value);
This would not work in browser-based JavaScript but runs fine in Execute JavaScript for Automation.
Claude 3.5 Sonnet
"JavaScript for Automation" (JXA) and "Execute JavaScript for Automation" in Keyboard Maestro refer to slightly different ways of running JavaScript code to automate macOS:
"JavaScript for Automation" action runs in a persistent context - meaning variables and state can be maintained between runs
"Execute JavaScript for Automation" runs in an isolated context each time - it's a one-off execution where the environment is fresh each time the script runs
The main practical differences are:
Persistence of variables and state between runs
Memory management (isolated execution cleans up automatically)
Performance (persistent context can be faster for repeated operations)
Let's start by making sure that we understand the exact context here.
OmniFocus actually provides interfaces to two entirely different JS Contexts
The old JavaScript for Automation interface which is accessible from Script Editor, and uses the same object model as AppleScript access to OmniFocus. This is osascript interacting with the OmniFocus application from outside.
Their more recent in-application JSContext (they call it Omni Automation), which runs faster (no inter-application Apple Event overheads – it's embedded inside OmniFocus itself), and uses the interface described here: OmniFocus & Omni Automation
When you say:
some things for OmniFocus in their modern syntax of JavaScript
Yes! Same with me, so I asked ChatGPT why that code was returning "undefined," and it replied that you have to grant permission for Script Editor to do this in your System Settings / Privacy and Security / Accessibility / Script Editor.
After doing that, it still didn't work, so I asked ChatGPT again, and it said to try this:
That worked. I think this implies that it's a permissions issue with Script Editor. Since I have never used Script Editor, I'm going to have to defer to other people to help you set up the permissions for that app.
In any case, we've taken a step forward. We seem to know what the problem is.
Again thank you I thought that if it was running JavaScript it was always running the Omni Automation. Thank you for filling in the gaps (and there are a lot).
I am not really sure that is a great question.
Here is an example of a JavaScript I am running to move selected tasks to an new Action and Subaction.
I have tried several things asking AI how to enable or what to do different and not sure. Perhaps I should be using some other editor then Script Editor.
Based on your screen snapshot, you didn't understand what I meant when I said, "That worked." I was referring to the osascript command, not the Script Editor. If you want my advice, open a terminal window and paste that command at a prompt in the terminal window. It will probably work.
Oh my bad and yes that works just fine in the terminal for me as well. I went back and forth for quite a while with AI and everything seems to work just fine in the Terminal but not in the Script Editor. Thanks for your help and feedback.
is that you are trying to return the value with console.log
console.log is not part of JavaScript itself – it's a library function which originates in browsers, and is sometimes, but not always, provided in other JS contexts, simply because people are used to it.
You don't need it in Script Editor or in the JavaScript flavour of osascript generally, and it doesn't return a defined value (just logs a string to a special channel).
All you needed there, to see a value appear in the Result panel at the foot of Script Editor, was:
var km = Application("Keyboard Maestro Engine");
km.getvariable("MyKMVar");
In fact, your existing version is logging Keyboard Maestro value to the system beyond the JavaScript interpreter, but not to the Result panel, and not in a very useful way.
If the Keyboard Maestro variable name "MyKMVar" is bound to a value like:
In the context of JXA, console.log is just the kind of wild goose chase that LLMs – artificial language in drag as artificial intelligence – inevitably waste our time with
Better to ask.
Good. That answers the question about which OmniFocus JSContext (and thus which OmniFocus scripting library) you are working with.
The answer is both
Let's call them:
The JXA interface (same interface library as AppleScript, just using JS). It's built on Apple Events passing between different applications. Easy enough to pass KM variable values this way, and your Application("Keyboard Maestro Engine").getvariable example shows this.
The omniJS interface (they now call it "Omni Automation" – too long) is used by a JSContext built into OmniFocus itself, which it make it fast, but not directly equipped to easily exchange information with other applications. A bit harder to get KM variable values into this, but not impossible.
Is using JXA's Application('OmniFocus').evaluateJavascript method to send a JS code string into omniJS for evaluation in there.
One JSContext for preparing and handing over the code,
and another, completely separate, JSContext, with different libraries, to run the code.
In that scenario:
We can't set KM variable values from inside the omniJS JSContext (it's largely isolated from inter-application communication - KM's setvariable method is not defined in it,
and we can't directlyread values from KM either – KM's getvariable method is not defined in it,
but we can squirrel values obtained from KM into the source code handed over from JXA into omniJS with the .evaluateJavaScript method.
Stepping back for a moment, if KM ⇄ OmniFocus value passing is central to the scripts which you want to set up, then the omniJS JSContext is not really the right one to use. It's fast and actively maintained, but it's not really built for that purpose.
You may be better off using a purely JXA solution.
That's because part of the JavaScript in which you are trying to get a reference to the KM Engine, and its .getvariable method, is inside the omniJSContext code, and evaluated in a (non-JXA) OmniJS JSContext where JXA references to other applications are not defined and not available.
You would have to read the KM value in JXA, before the Application('OmniFocus').evaluateJavascript line, and then
pass that obtained value in as an argument to the code string.
I'll try to write you an example this evening.
The basic pattern, for passing arguments in to omniJS code from JXA code is something like:
Thank you! You have helped me 16 years ago with AppleScript for OmniFocus and here were are with JavaScript and you don't even use OmniFocus anymore. I still use your AppleScript from years ago all the time.
Here is the updated working code. I next need to figure out why Keyboard Maestro runs a JavaScript file but doesn't do it directly in an action. I'll post that as soon as I figure that piece out.
JavaScript for OmniFocus Using Keyboard Maestro Variable DaysOffset Working!
(() => {
'use strict';
// Get the KM variable in the JXA context BEFORE passing to OmniJS
const kmApp = Application("Keyboard Maestro Engine");
const daysOffset = parseInt(kmApp.getvariable("DaysOffset")) || 1;
// OMNI JS CODE ---------------------------------------
const omniJSContext = (daysOffsetValue) => {
const main = () => {
// Get selected tasks first
const seln = document.windows[0].selection.tasks;
if (seln.length === 0) {
return "No tasks selected in OmniFocus";
}
// Use the passed in offset value directly
const daysOffset = daysOffsetValue;
// Calculate target date with offset
const targetDate = new Date();
targetDate.setDate(targetDate.getDate() + daysOffset);
const formattedDate = targetDate.getFullYear() + '-' +
String(targetDate.getMonth() + 1).padStart(2, '0') + '-' +
String(targetDate.getDate()).padStart(2, '0');
// Find target project with the date
let targetProject = flattenedProjects.find(p => p.name.includes(formattedDate));
if (!targetProject) {
return `No Project Found With the Date of ${formattedDate}`;
}
// Process each selected task
for (const task of seln) {
try {
let parentGroup = targetProject.tasks.find(t => t.name === task.parent.parent.name);
if (!parentGroup) {
parentGroup = new Task(task.parent.parent.name, targetProject);
}
let subGroup = parentGroup.children.find(t => t.name === task.parent.name);
if (!subGroup) {
subGroup = new Task(task.parent.name, parentGroup);
}
if (typeof moveTasks === 'function') {
moveTasks([task], subGroup);
} else {
task.move(subGroup);
}
} catch (error) {
// Silent error handling
}
}
return `Successfully Moved to the Project Named ${targetProject.name}`;
};
return main();
};
// OmniJS Context Evaluation ------------------------------------------------
return 0 < Application('OmniFocus').documents.length ? (
Application('OmniFocus').evaluateJavascript(
`(${omniJSContext})(${daysOffset})`
)
) : 'No documents open in OmniFocus';
})();
It is now referencing the Keyboard Maestro Variable, thanks to you, and is taking all the selected tasks and moving them to the same relative path on a folder with the reverse name date.