Help with this JXA code to access an action's "thenactions"?

I'm trying to come up with a fast way to find a specific action inside a macro. I have something that inspects a macro for all actions, very quickly by traversing a Plist:

Click to view the results
[Running] /usr/bin/env osascript -l JavaScript [snipped]
macro.actions[0].actionUID = 13129674
macro.actions[1].actionUID = 13129675
macro.actions[1].thenActions[0].actionUID = 13129676
macro.actions[1].thenActions[1].actionUID = 13129677
macro.actions[2].actionUID = 13129678
macro.actions[3].actionUID = 12010
macro.actions[4].actionUID = 76110
macro.actions[5].actionUID = 13129508
macro.actions[5].thenActions[0].actionUID = 13129668
macro.actions[5].thenActions[0].thenActions[0].actionUID = 13129669
macro.actions[5].thenActions[1].actionUID = 13129670
macro.actions[5].thenActions[2].actionUID = 13129671
macro.actions[5].thenActions[3].actionUID = 13129673
macro.actions[5].thenActions[4].actionUID = 13129519
macro.actions[5].thenActions[5].actionUID = 13129520
macro.actions[5].thenActions[5].actions[0].actionUID = 13129521
macro.actions[5].thenActions[5].actions[1].actionUID = 13129522
macro.actions[5].thenActions[5].actions[2].actionUID = 13129523
macro.actions[5].thenActions[5].actions[2].thenActions[0].actionUID = 13129524
macro.actions[5].thenActions[5].actions[3].actionUID = 13129525
macro.actions[5].thenActions[5].actions[4].actionUID = 13129526
macro.actions[5].thenActions[5].actions[4].thenActions[0].actionUID = 13129527
macro.actions[5].thenActions[6].actionUID = 13129528
macro.actions[5].thenActions[7].actionUID = 13129679
macro.actions[5].thenActions[8].actionUID = 13129672
macro.actions[6].actionUID = 514315
macro.actions[7].actionUID = 13104348
macro.actions[7].thenActions[0].actionUID = 13119412
macro.actions[7].thenActions[0].thenActions[0].actionUID = 13119427
macro.actions[7].thenActions[0].thenActions[1].actionUID = 13119428
macro.actions[7].thenActions[1].actionUID = 13100626
macro.actions[7].thenActions[1].thenActions[0].actionUID = 13148586
macro.actions[7].thenActions[1].thenActions[0].thenActions[0].actionUID = 13148708
macro.actions[7].thenActions[1].thenActions[0].thenActions[1].actionUID = 13148709
macro.actions[7].thenActions[1].thenActions[0].thenActions[2].actionUID = 13148710
macro.actions[7].thenActions[1].thenActions[0].thenActions[2].thenActions[0].actionUID = 13148711
macro.actions[7].thenActions[1].thenActions[0].thenActions[3].actionUID = 13162475
macro.actions[7].thenActions[1].thenActions[0].thenActions[3].thenActions[0].actionUID = 13164868
macro.actions[7].thenActions[1].thenActions[0].thenActions[3].thenActions[1].actionUID = 13162522
macro.actions[7].thenActions[1].thenActions[1].actionUID = 13132385
macro.actions[7].thenActions[1].thenActions[2].actionUID = 13104350
macro.actions[7].thenActions[2].actionUID = 13108988
macro.actions[7].thenActions[2].thenActions[0].actionUID = 13108989
macro.actions[7].thenActions[2].thenActions[1].actionUID = 13108990
macro.actions[8].actionUID = 13175334
macro.actions[8].thenActions[0].actionUID = 13175489
macro.actions[8].thenActions[1].actionUID = 13175578
macro.actions[9].actionUID = 15807943
macro.actions[9].thenActions[0].actionUID = 15807942
macro.actions[10].actionUID = 15807262
macro.actions[10].thenActions[0].actionUID = 15807263
macro.actions[10].thenActions[1].actionUID = 15807264
macro.actions[11].caseEntries[0].actions[0].actionUID = 15757423
macro.actions[11].caseEntries[0].actions[0].actions[0].actionUID = 15757444
macro.actions[11].caseEntries[0].actions[0].actions[1].actionUID = 15761897
macro.actions[11].caseEntries[0].actions[0].actions[2].actionUID = 15761856
macro.actions[11].caseEntries[0].actions[0].actions[3].actionUID = 15761787
macro.actions[11].caseEntries[0].actions[0].actions[4].actions[0].actionUID = 15759383
macro.actions[11].caseEntries[0].actions[0].actions[4].actions[1].actionUID = 15759398
macro.actions[11].caseEntries[0].actions[0].actions[4].actions[2].actions[0].actionUID = 15759385
macro.actions[11].caseEntries[0].actions[0].actions[4].actions[2].actions[1].actionUID = 15759359
macro.actions[11].caseEntries[0].actions[0].actions[4].actions[2].actionUID = 15759384
macro.actions[11].caseEntries[0].actions[0].actions[4].actionUID = 15759382
macro.actions[11].caseEntries[0].actions[0].actions[5].actionUID = 15758184
macro.actions[11].caseEntries[0].actions[0].actions[6].actionUID = 15758758
macro.actions[11].caseEntries[0].actions[0].actions[7].actionUID = 15759219
macro.actions[11].caseEntries[0].actions[0].actions[8].actionUID = 15761287
macro.actions[11].caseEntries[0].actions[0].actions[9].actionUID = 15758751
macro.actions[11].caseEntries[0].actions[0].actions[10].actionUID = 15758185
macro.actions[11].caseEntries[0].actions[0].actions[11].actionUID = 15758186
macro.actions[11].caseEntries[0].actions[0].actions[12].actionUID = 15757443
macro.actions[11].caseEntries[0].actions[0].actions[13].actionUID = 15761314
macro.actions[11].caseEntries[0].actions[0].actions[14].actionUID = 15757448
macro.actions[11].caseEntries[0].actions[0].actions[15].actionUID = 15757447
macro.actions[11].caseEntries[0].actions[0].actions[16].actionUID = 15757446
macro.actions[11].caseEntries[0].actions[0].actions[17].actionUID = 15758750
macro.actions[11].caseEntries[0].actions[0].actions[18].actionUID = 15758757
macro.actions[11].caseEntries[0].actions[0].actions[19].actionUID = 15792477
macro.actions[11].caseEntries[0].actions[0].actions[20].actions[0].actionUID = 15761255
macro.actions[11].caseEntries[0].actions[0].actions[20].actions[1].actionUID = 15761256
macro.actions[11].caseEntries[0].actions[0].actions[20].actionUID = 15761303
macro.actions[11].caseEntries[0].actions[0].actions[21].actionUID = 15757438
macro.actions[11].caseEntries[1].actions[0].actionUID = 15807044
macro.actions[11].caseEntries[1].actions[0].actions[0].actionUID = 15817972
macro.actions[11].caseEntries[1].actions[0].actions[1].actionUID = 15817974
macro.actions[11].caseEntries[1].actions[0].actions[2].actionUID = 15818565
macro.actions[11].caseEntries[1].actions[0].actions[3].actionUID = 15818566
macro.actions[11].caseEntries[1].actions[0].actions[4].actionUID = 15817971
macro.actions[11].caseEntries[1].actions[0].actions[5].actions[0].actionUID = 15808178
macro.actions[11].caseEntries[1].actions[0].actions[5].actions[1].actionUID = 15808658
macro.actions[11].caseEntries[1].actions[0].actions[5].actions[2].actionUID = 15808125
macro.actions[11].caseEntries[1].actions[0].actions[5].actions[3].actionUID = 15810396
macro.actions[11].caseEntries[1].actions[0].actions[5].actions[4].actionUID = 15808157
macro.actions[11].caseEntries[1].actions[0].actions[5].actions[5].actionUID = 15817368
macro.actions[11].caseEntries[1].actions[0].actions[5].actions[6].actionUID = 15807076
macro.actions[11].caseEntries[1].actions[0].actions[5].actions[7].caseEntries[0].actions[0].actionUID = 15808159
macro.actions[11].caseEntries[1].actions[0].actions[5].actions[7].caseEntries[1].actions[0].actionUID = 15808165
macro.actions[11].caseEntries[1].actions[0].actions[5].actions[7].caseEntries[2].actions[0].actionUID = 15808160
macro.actions[11].caseEntries[1].actions[0].actions[5].actions[7].caseEntries[3].actions[0].actionUID = 15808162
macro.actions[11].caseEntries[1].actions[0].actions[5].actions[7].caseEntries[3].actions[1].actionUID = 15808161
macro.actions[11].caseEntries[1].actions[0].actions[5].actions[7].caseEntries[4].actions[0].actionUID = 15808163
macro.actions[11].caseEntries[1].actions[0].actions[5].actions[7].caseEntries[4].actions[1].actionUID = 15808164
macro.actions[11].caseEntries[1].actions[0].actions[5].actions[7].actionUID = 15808158
macro.actions[11].caseEntries[1].actions[0].actions[5].actionUID = 15817970
macro.actions[11].caseEntries[2].actions[0].actionUID = 15807963
macro.actions[11].caseEntries[2].actions[0].actions[0].actionUID = 15808113
macro.actions[11].caseEntries[2].actions[0].actions[1].actionUID = 15807969
macro.actions[11].caseEntries[2].actions[0].actions[1].thenActions[0].actionUID = 15807970
macro.actions[11].caseEntries[3].actions[0].actionUID = 15790908
macro.actions[11].caseEntries[3].actions[0].actions[0].actionUID = 15793113
macro.actions[11].caseEntries[3].actions[0].actions[1].actions[0].actionUID = 15826507
macro.actions[11].caseEntries[3].actions[0].actions[1].actions[1].actionUID = 15826530
macro.actions[11].caseEntries[3].actions[0].actions[1].actionUID = 15826575
macro.actions[11].caseEntries[3].actions[0].actions[2].actionUID = 15793116
macro.actions[11].caseEntries[3].actions[0].actions[3].actionUID = 15826642
macro.actions[11].caseEntries[3].actions[0].actions[4].actions[0].actionUID = 15793118
macro.actions[11].caseEntries[3].actions[0].actions[4].actions[1].actionUID = 15793158
macro.actions[11].caseEntries[3].actions[0].actions[4].actions[2].actionUID = 15793105
macro.actions[11].caseEntries[3].actions[0].actions[4].actions[3].actionUID = 15793106
macro.actions[11].caseEntries[3].actions[0].actions[4].actions[4].actionUID = 15793107
macro.actions[11].caseEntries[3].actions[0].actions[4].actions[5].actionUID = 15793108
macro.actions[11].caseEntries[3].actions[0].actions[4].actions[6].actionUID = 15793115
macro.actions[11].caseEntries[3].actions[0].actions[4].actionUID = 15793114
macro.actions[11].caseEntries[3].actions[0].actions[5].actionUID = 15791864
macro.actions[11].caseEntries[4].actions[0].actionUID = 15808035
macro.actions[11].caseEntries[4].actions[0].actions[0].actionUID = 15808064
macro.actions[11].caseEntries[5].actions[0].actionUID = 15808051
macro.actions[11].caseEntries[5].actions[0].actions[0].actionUID = 15808085
macro.actions[11].caseEntries[6].actions[0].actionUID = 15807940
macro.actions[11].caseEntries[6].actions[0].actions[0].actionUID = 15808120
macro.actions[11].caseEntries[6].actions[0].actions[0].elseactions[0].actionUID = 15808123
macro.actions[11].caseEntries[6].actions[0].actions[0].thenActions[0].actionUID = 15807941
macro.actions[11].caseEntries[7].actions[0].actionUID = 15766301
macro.actions[11].caseEntries[7].actions[0].actions[0].actionUID = 15766321
macro.actions[11].caseEntries[7].actions[0].actions[1].actionUID = 15766322
macro.actions[11].caseEntries[7].actions[0].actions[2].actionUID = 15766324
macro.actions[11].caseEntries[7].actions[0].actions[3].actionUID = 15766327
macro.actions[11].caseEntries[7].actions[0].actions[4].actionUID = 15776445
macro.actions[11].caseEntries[7].actions[0].actions[5].actionUID = 15799578
macro.actions[11].caseEntries[7].actions[0].actions[6].actionUID = 15766300
macro.actions[11].caseEntries[7].actions[0].actions[7].elseactions[0].actionUID = 15799579
macro.actions[11].caseEntries[7].actions[0].actions[7].thenActions[0].actionUID = 15766323
macro.actions[11].caseEntries[7].actions[0].actions[7].thenActions[1].actionUID = 15766302
macro.actions[11].caseEntries[7].actions[0].actions[7].actionUID = 15766325
macro.actions[11].caseEntries[8].actions[0].actionUID = 514356
macro.actions[11].caseEntries[8].actions[0].actions[0].actionUID = 514319
macro.actions[11].caseEntries[8].actions[0].actions[1].actionUID = 514320
macro.actions[11].caseEntries[8].actions[0].actions[2].actionUID = 514321
macro.actions[11].caseEntries[8].actions[0].actions[3].actionUID = 514322
macro.actions[11].caseEntries[8].actions[0].actions[4].actionUID = 514323
macro.actions[11].caseEntries[8].actions[0].actions[5].actionUID = 514324
macro.actions[11].caseEntries[8].actions[0].actions[6].actions[0].actionUID = 514326
macro.actions[11].caseEntries[8].actions[0].actions[6].actions[0].thenActions[0].actionUID = 514327
macro.actions[11].caseEntries[8].actions[0].actions[6].actions[1].actionUID = 514328
macro.actions[11].caseEntries[8].actions[0].actions[6].actions[1].elseactions[0].actions[0].actionUID = 514333
macro.actions[11].caseEntries[8].actions[0].actions[6].actions[1].elseactions[0].actionUID = 514332
macro.actions[11].caseEntries[8].actions[0].actions[6].actions[1].thenActions[0].actions[0].actionUID = 514330
macro.actions[11].caseEntries[8].actions[0].actions[6].actions[1].thenActions[0].actionUID = 514329
macro.actions[11].caseEntries[8].actions[0].actions[6].actions[1].thenActions[1].actionUID = 514331
macro.actions[11].caseEntries[8].actions[0].actions[6].actionUID = 514325
macro.actions[11].caseEntries[9].actions[0].actionUID = 521310
macro.actions[11].caseEntries[9].actions[0].actions[0].actionUID = 521355
macro.actions[11].caseEntries[10].actions[0].actionUID = 1512616
macro.actions[11].caseEntries[10].actions[0].actions[0].actionUID = 1512617
macro.actions[11].caseEntries[10].actions[0].actions[0].thenActions[0].actionUID = 1512618
macro.actions[11].caseEntries[10].actions[0].actions[0].thenActions[1].actionUID = 12420761
macro.actions[11].caseEntries[10].actions[0].actions[0].thenActions[2].actionUID = 1512619
macro.actions[11].caseEntries[10].actions[0].actions[1].actionUID = 13097260
macro.actions[11].caseEntries[10].actions[0].actions[2].actionUID = 1527952
macro.actions[11].caseEntries[11].actions[0].actionUID = 1512549
macro.actions[11].caseEntries[11].actions[0].actions[0].actionUID = 1512340
macro.actions[11].caseEntries[12].actions[0].actionUID = 1483214
macro.actions[11].caseEntries[12].actions[0].actions[0].actionUID = 1483341
macro.actions[11].caseEntries[12].actions[0].actions[1].actionUID = 1483218
macro.actions[11].caseEntries[13].actions[0].actionUID = 514334
macro.actions[11].caseEntries[13].actions[0].actions[0].actionUID = 514336
macro.actions[11].caseEntries[13].actions[0].actions[1].actionUID = 514337
macro.actions[11].caseEntries[13].actions[0].actions[2].actions[0].actions[0].actionUID = 514340
macro.actions[11].caseEntries[13].actions[0].actions[2].actions[0].actions[1].actionUID = 514341
macro.actions[11].caseEntries[13].actions[0].actions[2].actions[0].actions[2].actionUID = 514342
macro.actions[11].caseEntries[13].actions[0].actions[2].actions[0].actions[3].actionUID = 514343
macro.actions[11].caseEntries[13].actions[0].actions[2].actions[0].actionUID = 514339
macro.actions[11].caseEntries[13].actions[0].actions[2].actions[1].actions[0].actionUID = 514345
macro.actions[11].caseEntries[13].actions[0].actions[2].actions[1].actions[1].actionUID = 514346
macro.actions[11].caseEntries[13].actions[0].actions[2].actions[1].actions[2].actionUID = 514347
macro.actions[11].caseEntries[13].actions[0].actions[2].actions[1].actions[3].actionUID = 514348
macro.actions[11].caseEntries[13].actions[0].actions[2].actions[1].actionUID = 514344
macro.actions[11].caseEntries[13].actions[0].actions[2].actionUID = 514338
macro.actions[11].caseEntries[13].actions[0].actions[3].actionUID = 514349
macro.actions[11].caseEntries[13].actions[0].actions[4].actionUID = 514350
macro.actions[11].caseEntries[13].actions[0].actions[5].actionUID = 514351
macro.actions[11].caseEntries[13].actions[0].actions[5].thenActions[0].actionUID = 514352
macro.actions[11].caseEntries[13].actions[0].actions[5].thenActions[1].actionUID = 514353
macro.actions[11].caseEntries[13].actions[0].actions[5].thenActions[2].actionUID = 514354
macro.actions[11].caseEntries[14].actions[0].actionUID = 514401
macro.actions[11].caseEntries[15].actions[0].actions[0].actionUID = 15808124
macro.actions[11].caseEntries[15].actions[0].actionUID = 514402
macro.actions[11].actionUID = 514314

[Done] exited with code=0 in 0.309 seconds

I'll post the code when I get my problem solved, which I'll describe here:

The Problem

Consider this macro:

Click to view the image

image

 
Nested Actions Test.kmmacros (2.9 KB)

Now this JXA code:

const kmEditor = Application("Keyboard Maestro");
const macro = kmEditor.macros.whose({id: {"=": "ED3D49DC-76E0-4B1F-BF8A-6745499214C5"}})[0]

console.log(macro.actions[0].name());
// returns "Group action"

console.log(macro.actions[0].actions[0].name());
// returns "Enclosed "IF" action"

So far, so good. What I can't figure out is how to access "elseactions" and "thenactions":

console.log(macro.actions[0].actions[0].thenactions[0].name());
// fails with error "Can't convert types"

I've tried a bunch of capitalizations, and nothing seems to work. If I can get this to work, then I'll have something that works pretty damn fast, which of course I'll share with the community.


OK, I found the problem. This is nuts, but it's the truth.

"thenactions" should be an array. It says so in the Dictionary, and it works that way in AppleScript.

But not in JXA (by now I shouldn't be surprised). "thenactions" isn't an array in JXA. But it has a property called "actions", and that is an array.

So this works:

console.log(macro.actions[0].actions[0].thenactions.actions[0].name());

It's not anywhere in the XML, but it's there in the actual automation object. @peternlewis, got any ideas why this happens?

How did I figure this out? I haven't the first clue. I've tried so many things, there's no telling which one led me to this.

So hopefully later today I'll have some code that works for everything.

1 Like

@ComplexPoint I have a question that I hope I can explain properly.

I'm walking through all the properties of a macro's Plist, generating a list of all actions and the path to get to them, like this (and please forgive any typos):

[
	{"id":13129675,"path":["actions",1]},
	{"id":13129676,"path":["actions",1,"thenactions","actions",0]}
]

So, for example, the first item above corresponds to

const action = macro.actions[1];
console.log(action.id());

and the second item corresponds to

const action = macro.actions[1].thenactions.actions[0];
console.log(action.id());

etc.

So my question is, how do I actually get an action using one of the "paths" I mentioned above? How do I turn one of the paths into code that actually gets a result?

I hope this makes sense. It's taken me forever to come up with what I hope is a clear question.

For the executable version (in the sense of something to which you could apply the KM Engine doScript method) I guess you will need to fetch or derive the XML.

I suppose that the shortest route to that might be from a parse either of the XML returned through the osascript interface (the value of the xml property of an action object), or simply by putting osascript aside for a moment, and starting with an XML parse of the whole plist, from which you can pick out the XML of a particular action ?

I already have the XML and I've already parsed it. I just don't know how to turn this:

["actions",1,"thenactions","actions",0]

into this:

macro.actions[1].thenactions.actions[0]

at runtime. I can just type it in and it will work, but I need to do it at runtime from an array like above.

(Assume I've already retrieved the "macro" automation object from the KM Editor).

I knew this was going to be hard to explain...

Here's an example, but I'm sure there should be a better way:

var lastObject = macro;
paths.forEach(path => lastObject = lastObject[path]);

// lastObject should now contain the Action.
console.log(lastObject.name());

I believe I just mentioned elsewhere that AppleScript land is an entertaining place to be.

Yes, it appears all the action lists (except actions) within an action, are actually a struct containing an actions entry.

So I guess it is a bug which I should try to fix if I can figure it out.

Forgive me, I'm being slow :slight_smile:

To reduce a path of keys, over an object, to a component value, I might define a refFromPath (reference from path) function in terms of .reduce:

// refFromPath :: [(String|Int)] -> Object -> a
const refFromPath = path =>
    obj => path.reduce(
        (a, k) => a[k],
        obj
    );

as in:

Expand disclosure triangle to view JS source
(() => {
    "use strict";

    // refFromPath :: [(String|Int)] -> Object -> a
    const refFromPath = path =>
        obj => path.reduce(
            (a, k) => (
                showLog(k, a[k]),
                a[k]
            ),
            obj
        );

    // ---------------------- TEST -----------------------
    const main = () => {
        const macro = {
            "actions": [
                "alpha", {
                    "thenActions": {
                        "actions": ["beta", "gamma"]
                    }
                }
            ]
        };

        const path = ["actions", 1, "thenActions", "actions", 0];


        return refFromPath(path)(macro);
    };

    // --------------------- GENERIC ---------------------

    // showLog :: a -> IO ()
    const showLog = (...args) =>
        // eslint-disable-next-line no-console
        console.log(
            args
                .map(JSON.stringify)
                .join(" -> ")
        );


    return main();
})();

I doubt that, but if it's true, welcome to my world!

Thanks - that's the type of thing I was looking for. I may decide to keep what I have, because 1) I think it's easier to understand for us mortals ;p , 2) it's basically the same thing just stated differently.

Thanks for your help!

1 Like

Yes it is. In this case, though, it appears that this only happens in JXA. But that's neither here nor there:

I probably haven't encountered every type of list of actions, although I did test all 2200+ of my macros, and here's what I've found:

These all have the issue:

  • elseactions
  • thenactions
  • tryactions
  • catchactions

Oddly enough

  • caseEntries

does not have that issue, and it's also the only one spelled in camelCase (not that that's related - I just wanted to mention it).

I'm not sure it's worth your time, for a couple of reasons:

  1. It seems in all the life of KM, I'm the only one to come across this? Maybe a mention of this in the Dictionary, or then again, who cares besides me? :stuck_out_tongue:

  2. If you change it, then I'll have to modify my code to check KM version numbers - oh the horrors! (I'm actually kidding here - it would be such a minor tweak for me that it wouldn't matter.)

With that said, if you're anything like me (God forbid), you'll have to fix it whether you want to or not, "because it's wrong". No judgment from me in either case.

PS: Throughout my entire professional programming career (starting in 1979), I've always managed to encounter problems it seems nobody else does. I think it's because I have "Star Trek Syndrome" - always going where no one has gone before". :joy:

PPS: Thanks for your help, and sorry for being a PITA. :smirk:

1 Like