@mseep/clawdcursor 1.5.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (354) hide show
  1. package/CHANGELOG.md +2264 -0
  2. package/LICENSE +21 -0
  3. package/README.md +385 -0
  4. package/SECURITY.md +44 -0
  5. package/SKILL.md +503 -0
  6. package/dist/core/agent-loop/agent.d.ts +42 -0
  7. package/dist/core/agent-loop/agent.js +1023 -0
  8. package/dist/core/agent-loop/agent.js.map +1 -0
  9. package/dist/core/agent-loop/batch-tool.d.ts +25 -0
  10. package/dist/core/agent-loop/batch-tool.js +218 -0
  11. package/dist/core/agent-loop/batch-tool.js.map +1 -0
  12. package/dist/core/agent-loop/coord-scale.d.ts +72 -0
  13. package/dist/core/agent-loop/coord-scale.js +89 -0
  14. package/dist/core/agent-loop/coord-scale.js.map +1 -0
  15. package/dist/core/agent-loop/focus-guard.d.ts +24 -0
  16. package/dist/core/agent-loop/focus-guard.js +29 -0
  17. package/dist/core/agent-loop/focus-guard.js.map +1 -0
  18. package/dist/core/agent-loop/project-mcp.d.ts +97 -0
  19. package/dist/core/agent-loop/project-mcp.js +253 -0
  20. package/dist/core/agent-loop/project-mcp.js.map +1 -0
  21. package/dist/core/agent-loop/prompt.d.ts +45 -0
  22. package/dist/core/agent-loop/prompt.js +426 -0
  23. package/dist/core/agent-loop/prompt.js.map +1 -0
  24. package/dist/core/agent-loop/tool-meta.d.ts +93 -0
  25. package/dist/core/agent-loop/tool-meta.js +651 -0
  26. package/dist/core/agent-loop/tool-meta.js.map +1 -0
  27. package/dist/core/agent-loop/tools.d.ts +38 -0
  28. package/dist/core/agent-loop/tools.js +2134 -0
  29. package/dist/core/agent-loop/tools.js.map +1 -0
  30. package/dist/core/agent-loop/types.d.ts +170 -0
  31. package/dist/core/agent-loop/types.js +12 -0
  32. package/dist/core/agent-loop/types.js.map +1 -0
  33. package/dist/core/agent.d.ts +51 -0
  34. package/dist/core/agent.js +245 -0
  35. package/dist/core/agent.js.map +1 -0
  36. package/dist/core/app-categories.d.ts +67 -0
  37. package/dist/core/app-categories.js +108 -0
  38. package/dist/core/app-categories.js.map +1 -0
  39. package/dist/core/banner.d.ts +70 -0
  40. package/dist/core/banner.js +245 -0
  41. package/dist/core/banner.js.map +1 -0
  42. package/dist/core/classify/capability.d.ts +45 -0
  43. package/dist/core/classify/capability.js +78 -0
  44. package/dist/core/classify/capability.js.map +1 -0
  45. package/dist/core/decompose/llm-decomposer.d.ts +35 -0
  46. package/dist/core/decompose/llm-decomposer.js +156 -0
  47. package/dist/core/decompose/llm-decomposer.js.map +1 -0
  48. package/dist/core/decompose/parser.d.ts +27 -0
  49. package/dist/core/decompose/parser.js +101 -0
  50. package/dist/core/decompose/parser.js.map +1 -0
  51. package/dist/core/observability/correlation.d.ts +19 -0
  52. package/dist/core/observability/correlation.js +36 -0
  53. package/dist/core/observability/correlation.js.map +1 -0
  54. package/dist/core/observability/cost-meter.d.ts +51 -0
  55. package/dist/core/observability/cost-meter.js +134 -0
  56. package/dist/core/observability/cost-meter.js.map +1 -0
  57. package/dist/core/observability/logger.d.ts +61 -0
  58. package/dist/core/observability/logger.js +550 -0
  59. package/dist/core/observability/logger.js.map +1 -0
  60. package/dist/core/router/aliases.d.ts +50 -0
  61. package/dist/core/router/aliases.js +104 -0
  62. package/dist/core/router/aliases.js.map +1 -0
  63. package/dist/core/router/normalize.d.ts +41 -0
  64. package/dist/core/router/normalize.js +80 -0
  65. package/dist/core/router/normalize.js.map +1 -0
  66. package/dist/core/safety.d.ts +126 -0
  67. package/dist/core/safety.js +568 -0
  68. package/dist/core/safety.js.map +1 -0
  69. package/dist/core/sense/a11y-resolver.d.ts +73 -0
  70. package/dist/core/sense/a11y-resolver.js +76 -0
  71. package/dist/core/sense/a11y-resolver.js.map +1 -0
  72. package/dist/core/sense/fingerprint.d.ts +41 -0
  73. package/dist/core/sense/fingerprint.js +123 -0
  74. package/dist/core/sense/fingerprint.js.map +1 -0
  75. package/dist/core/sense/rank.d.ts +70 -0
  76. package/dist/core/sense/rank.js +192 -0
  77. package/dist/core/sense/rank.js.map +1 -0
  78. package/dist/core/sense/reactive-check.d.ts +40 -0
  79. package/dist/core/sense/reactive-check.js +48 -0
  80. package/dist/core/sense/reactive-check.js.map +1 -0
  81. package/dist/core/sense/snapshot.d.ts +19 -0
  82. package/dist/core/sense/snapshot.js +100 -0
  83. package/dist/core/sense/snapshot.js.map +1 -0
  84. package/dist/core/sense/types.d.ts +66 -0
  85. package/dist/core/sense/types.js +9 -0
  86. package/dist/core/sense/types.js.map +1 -0
  87. package/dist/core/sense/ui-map-anchors.d.ts +7 -0
  88. package/dist/core/sense/ui-map-anchors.js +24 -0
  89. package/dist/core/sense/ui-map-anchors.js.map +1 -0
  90. package/dist/core/sense/ui-map-elements.d.ts +5 -0
  91. package/dist/core/sense/ui-map-elements.js +33 -0
  92. package/dist/core/sense/ui-map-elements.js.map +1 -0
  93. package/dist/core/sense/ui-map-find.d.ts +56 -0
  94. package/dist/core/sense/ui-map-find.js +153 -0
  95. package/dist/core/sense/ui-map-find.js.map +1 -0
  96. package/dist/core/sense/ui-map-fuse.d.ts +4 -0
  97. package/dist/core/sense/ui-map-fuse.js +44 -0
  98. package/dist/core/sense/ui-map-fuse.js.map +1 -0
  99. package/dist/core/sense/ui-map-geom.d.ts +3 -0
  100. package/dist/core/sense/ui-map-geom.js +16 -0
  101. package/dist/core/sense/ui-map-geom.js.map +1 -0
  102. package/dist/core/sense/ui-map-holder.d.ts +58 -0
  103. package/dist/core/sense/ui-map-holder.js +87 -0
  104. package/dist/core/sense/ui-map-holder.js.map +1 -0
  105. package/dist/core/sense/ui-map-normalize.d.ts +19 -0
  106. package/dist/core/sense/ui-map-normalize.js +65 -0
  107. package/dist/core/sense/ui-map-normalize.js.map +1 -0
  108. package/dist/core/sense/ui-map-render.d.ts +4 -0
  109. package/dist/core/sense/ui-map-render.js +34 -0
  110. package/dist/core/sense/ui-map-render.js.map +1 -0
  111. package/dist/core/sense/ui-map-resolve.d.ts +41 -0
  112. package/dist/core/sense/ui-map-resolve.js +59 -0
  113. package/dist/core/sense/ui-map-resolve.js.map +1 -0
  114. package/dist/core/sense/ui-map-types.d.ts +66 -0
  115. package/dist/core/sense/ui-map-types.js +11 -0
  116. package/dist/core/sense/ui-map-types.js.map +1 -0
  117. package/dist/core/sense/ui-map.d.ts +29 -0
  118. package/dist/core/sense/ui-map.js +113 -0
  119. package/dist/core/sense/ui-map.js.map +1 -0
  120. package/dist/core/verify/assertions.d.ts +132 -0
  121. package/dist/core/verify/assertions.js +284 -0
  122. package/dist/core/verify/assertions.js.map +1 -0
  123. package/dist/index.d.ts +21 -0
  124. package/dist/index.js +24 -0
  125. package/dist/index.js.map +1 -0
  126. package/dist/llm/browser-config.d.ts +36 -0
  127. package/dist/llm/browser-config.js +83 -0
  128. package/dist/llm/browser-config.js.map +1 -0
  129. package/dist/llm/client.d.ts +268 -0
  130. package/dist/llm/client.js +1094 -0
  131. package/dist/llm/client.js.map +1 -0
  132. package/dist/llm/config.d.ts +79 -0
  133. package/dist/llm/config.js +375 -0
  134. package/dist/llm/config.js.map +1 -0
  135. package/dist/llm/credentials.d.ts +35 -0
  136. package/dist/llm/credentials.js +491 -0
  137. package/dist/llm/credentials.js.map +1 -0
  138. package/dist/llm/external-creds.d.ts +42 -0
  139. package/dist/llm/external-creds.js +169 -0
  140. package/dist/llm/external-creds.js.map +1 -0
  141. package/dist/llm/providers.d.ts +123 -0
  142. package/dist/llm/providers.js +717 -0
  143. package/dist/llm/providers.js.map +1 -0
  144. package/dist/paths.d.ts +31 -0
  145. package/dist/paths.js +147 -0
  146. package/dist/paths.js.map +1 -0
  147. package/dist/platform/accessibility.d.ts +139 -0
  148. package/dist/platform/accessibility.js +670 -0
  149. package/dist/platform/accessibility.js.map +1 -0
  150. package/dist/platform/cdp-driver.d.ts +318 -0
  151. package/dist/platform/cdp-driver.js +1179 -0
  152. package/dist/platform/cdp-driver.js.map +1 -0
  153. package/dist/platform/index.d.ts +11 -0
  154. package/dist/platform/index.js +69 -0
  155. package/dist/platform/index.js.map +1 -0
  156. package/dist/platform/keys.d.ts +17 -0
  157. package/dist/platform/keys.js +129 -0
  158. package/dist/platform/keys.js.map +1 -0
  159. package/dist/platform/launch-poll.d.ts +101 -0
  160. package/dist/platform/launch-poll.js +177 -0
  161. package/dist/platform/launch-poll.js.map +1 -0
  162. package/dist/platform/linux.d.ts +173 -0
  163. package/dist/platform/linux.js +1253 -0
  164. package/dist/platform/linux.js.map +1 -0
  165. package/dist/platform/macos.d.ts +136 -0
  166. package/dist/platform/macos.js +976 -0
  167. package/dist/platform/macos.js.map +1 -0
  168. package/dist/platform/native-desktop.d.ts +145 -0
  169. package/dist/platform/native-desktop.js +936 -0
  170. package/dist/platform/native-desktop.js.map +1 -0
  171. package/dist/platform/native-helper.d.ts +130 -0
  172. package/dist/platform/native-helper.js +592 -0
  173. package/dist/platform/native-helper.js.map +1 -0
  174. package/dist/platform/ocr-engine.d.ts +78 -0
  175. package/dist/platform/ocr-engine.js +363 -0
  176. package/dist/platform/ocr-engine.js.map +1 -0
  177. package/dist/platform/ps-runner.d.ts +28 -0
  178. package/dist/platform/ps-runner.js +228 -0
  179. package/dist/platform/ps-runner.js.map +1 -0
  180. package/dist/platform/types.d.ts +397 -0
  181. package/dist/platform/types.js +15 -0
  182. package/dist/platform/types.js.map +1 -0
  183. package/dist/platform/uri-handler.d.ts +75 -0
  184. package/dist/platform/uri-handler.js +273 -0
  185. package/dist/platform/uri-handler.js.map +1 -0
  186. package/dist/platform/wayland-backend.d.ts +53 -0
  187. package/dist/platform/wayland-backend.js +348 -0
  188. package/dist/platform/wayland-backend.js.map +1 -0
  189. package/dist/platform/windows.d.ts +232 -0
  190. package/dist/platform/windows.js +1210 -0
  191. package/dist/platform/windows.js.map +1 -0
  192. package/dist/postbuild.d.ts +10 -0
  193. package/dist/postbuild.js +98 -0
  194. package/dist/postbuild.js.map +1 -0
  195. package/dist/schema/snapshot.d.ts +33 -0
  196. package/dist/schema/snapshot.js +90 -0
  197. package/dist/schema/snapshot.js.map +1 -0
  198. package/dist/shortcuts.d.ts +30 -0
  199. package/dist/shortcuts.js +261 -0
  200. package/dist/shortcuts.js.map +1 -0
  201. package/dist/surface/cli.d.ts +7 -0
  202. package/dist/surface/cli.js +1556 -0
  203. package/dist/surface/cli.js.map +1 -0
  204. package/dist/surface/dashboard.d.ts +8 -0
  205. package/dist/surface/dashboard.js +1193 -0
  206. package/dist/surface/dashboard.js.map +1 -0
  207. package/dist/surface/doctor.d.ts +29 -0
  208. package/dist/surface/doctor.js +1514 -0
  209. package/dist/surface/doctor.js.map +1 -0
  210. package/dist/surface/format.d.ts +10 -0
  211. package/dist/surface/format.js +37 -0
  212. package/dist/surface/format.js.map +1 -0
  213. package/dist/surface/http-utility.d.ts +65 -0
  214. package/dist/surface/http-utility.js +336 -0
  215. package/dist/surface/http-utility.js.map +1 -0
  216. package/dist/surface/mcp-server.d.ts +91 -0
  217. package/dist/surface/mcp-server.js +280 -0
  218. package/dist/surface/mcp-server.js.map +1 -0
  219. package/dist/surface/onboarding.d.ts +15 -0
  220. package/dist/surface/onboarding.js +184 -0
  221. package/dist/surface/onboarding.js.map +1 -0
  222. package/dist/surface/pidfile.d.ts +79 -0
  223. package/dist/surface/pidfile.js +263 -0
  224. package/dist/surface/pidfile.js.map +1 -0
  225. package/dist/surface/readiness.d.ts +45 -0
  226. package/dist/surface/readiness.js +230 -0
  227. package/dist/surface/readiness.js.map +1 -0
  228. package/dist/surface/report.d.ts +68 -0
  229. package/dist/surface/report.js +341 -0
  230. package/dist/surface/report.js.map +1 -0
  231. package/dist/surface/skill-register.d.ts +14 -0
  232. package/dist/surface/skill-register.js +150 -0
  233. package/dist/surface/skill-register.js.map +1 -0
  234. package/dist/surface/version.d.ts +6 -0
  235. package/dist/surface/version.js +27 -0
  236. package/dist/surface/version.js.map +1 -0
  237. package/dist/tools/a11y.d.ts +8 -0
  238. package/dist/tools/a11y.js +545 -0
  239. package/dist/tools/a11y.js.map +1 -0
  240. package/dist/tools/a11y_depth.d.ts +19 -0
  241. package/dist/tools/a11y_depth.js +455 -0
  242. package/dist/tools/a11y_depth.js.map +1 -0
  243. package/dist/tools/agent.d.ts +15 -0
  244. package/dist/tools/agent.js +248 -0
  245. package/dist/tools/agent.js.map +1 -0
  246. package/dist/tools/batch.d.ts +46 -0
  247. package/dist/tools/batch.js +230 -0
  248. package/dist/tools/batch.js.map +1 -0
  249. package/dist/tools/cdp.d.ts +8 -0
  250. package/dist/tools/cdp.js +233 -0
  251. package/dist/tools/cdp.js.map +1 -0
  252. package/dist/tools/compact.d.ts +63 -0
  253. package/dist/tools/compact.js +418 -0
  254. package/dist/tools/compact.js.map +1 -0
  255. package/dist/tools/cost-class.d.ts +38 -0
  256. package/dist/tools/cost-class.js +117 -0
  257. package/dist/tools/cost-class.js.map +1 -0
  258. package/dist/tools/desktop.d.ts +9 -0
  259. package/dist/tools/desktop.js +346 -0
  260. package/dist/tools/desktop.js.map +1 -0
  261. package/dist/tools/electron_bridge.d.ts +41 -0
  262. package/dist/tools/electron_bridge.js +261 -0
  263. package/dist/tools/electron_bridge.js.map +1 -0
  264. package/dist/tools/extras.d.ts +22 -0
  265. package/dist/tools/extras.js +942 -0
  266. package/dist/tools/extras.js.map +1 -0
  267. package/dist/tools/favorites.d.ts +13 -0
  268. package/dist/tools/favorites.js +137 -0
  269. package/dist/tools/favorites.js.map +1 -0
  270. package/dist/tools/introspection.d.ts +13 -0
  271. package/dist/tools/introspection.js +55 -0
  272. package/dist/tools/introspection.js.map +1 -0
  273. package/dist/tools/ocr.d.ts +8 -0
  274. package/dist/tools/ocr.js +66 -0
  275. package/dist/tools/ocr.js.map +1 -0
  276. package/dist/tools/orchestration.d.ts +7 -0
  277. package/dist/tools/orchestration.js +377 -0
  278. package/dist/tools/orchestration.js.map +1 -0
  279. package/dist/tools/playbooks/extract-compose.d.ts +22 -0
  280. package/dist/tools/playbooks/extract-compose.js +85 -0
  281. package/dist/tools/playbooks/extract-compose.js.map +1 -0
  282. package/dist/tools/playbooks/find-replace.d.ts +11 -0
  283. package/dist/tools/playbooks/find-replace.js +56 -0
  284. package/dist/tools/playbooks/find-replace.js.map +1 -0
  285. package/dist/tools/playbooks/index.d.ts +63 -0
  286. package/dist/tools/playbooks/index.js +70 -0
  287. package/dist/tools/playbooks/index.js.map +1 -0
  288. package/dist/tools/playbooks/keys-blocklist.d.ts +24 -0
  289. package/dist/tools/playbooks/keys-blocklist.js +89 -0
  290. package/dist/tools/playbooks/keys-blocklist.js.map +1 -0
  291. package/dist/tools/registry.d.ts +40 -0
  292. package/dist/tools/registry.js +560 -0
  293. package/dist/tools/registry.js.map +1 -0
  294. package/dist/tools/safety-gate.d.ts +16 -0
  295. package/dist/tools/safety-gate.js +70 -0
  296. package/dist/tools/safety-gate.js.map +1 -0
  297. package/dist/tools/scheduler.d.ts +76 -0
  298. package/dist/tools/scheduler.js +413 -0
  299. package/dist/tools/scheduler.js.map +1 -0
  300. package/dist/tools/shortcuts.d.ts +13 -0
  301. package/dist/tools/shortcuts.js +205 -0
  302. package/dist/tools/shortcuts.js.map +1 -0
  303. package/dist/tools/smart.d.ts +15 -0
  304. package/dist/tools/smart.js +785 -0
  305. package/dist/tools/smart.js.map +1 -0
  306. package/dist/tools/types.d.ts +174 -0
  307. package/dist/tools/types.js +67 -0
  308. package/dist/tools/types.js.map +1 -0
  309. package/dist/tools/window-text.d.ts +15 -0
  310. package/dist/tools/window-text.js +39 -0
  311. package/dist/tools/window-text.js.map +1 -0
  312. package/dist/types.d.ts +122 -0
  313. package/dist/types.js +41 -0
  314. package/dist/types.js.map +1 -0
  315. package/native/Package.swift +38 -0
  316. package/native/README.md +113 -0
  317. package/native/Sources/ClawdCursorHelper/main.swift +602 -0
  318. package/native/Sources/ClawdCursorHost/main.swift +182 -0
  319. package/native/Sources/PermissionCheck/main.swift +53 -0
  320. package/native/Sources/ScreenshotHelper/main.swift +219 -0
  321. package/native/build.sh +139 -0
  322. package/native/entitlements.plist +12 -0
  323. package/package.json +115 -0
  324. package/scripts/banner.ps1 +112 -0
  325. package/scripts/coord-accuracy.ps1 +140 -0
  326. package/scripts/coord-uwp.ps1 +80 -0
  327. package/scripts/edge-glow.ps1 +180 -0
  328. package/scripts/find-element.ps1 +198 -0
  329. package/scripts/get-foreground-window.ps1 +71 -0
  330. package/scripts/get-screen-context.ps1 +183 -0
  331. package/scripts/get-windows.ps1 +66 -0
  332. package/scripts/install-panic-hotkey.ps1 +46 -0
  333. package/scripts/interact-element.ps1 +431 -0
  334. package/scripts/invoke-element.ps1 +314 -0
  335. package/scripts/linux/atspi-bridge.py +356 -0
  336. package/scripts/linux/ocr-recognize.py +154 -0
  337. package/scripts/mac/_window-picker.jxa +163 -0
  338. package/scripts/mac/find-element.jxa +0 -0
  339. package/scripts/mac/find-element.sh +161 -0
  340. package/scripts/mac/focus-window.jxa +284 -0
  341. package/scripts/mac/get-focused-element.jxa +102 -0
  342. package/scripts/mac/get-foreground-window.jxa +173 -0
  343. package/scripts/mac/get-screen-context.jxa +197 -0
  344. package/scripts/mac/get-ui-tree.sh +141 -0
  345. package/scripts/mac/get-windows.jxa +117 -0
  346. package/scripts/mac/interact-element.sh +235 -0
  347. package/scripts/mac/invoke-element.jxa +408 -0
  348. package/scripts/mac/ocr-recognize.swift +124 -0
  349. package/scripts/ocr-recognize.ps1 +102 -0
  350. package/scripts/postinstall-native.js +48 -0
  351. package/scripts/ps-bridge.ps1 +830 -0
  352. package/scripts/smoke-mcp.ps1 +119 -0
  353. package/scripts/sync-version.ts +178 -0
  354. package/scripts/verify-install.js +81 -0
@@ -0,0 +1,197 @@
1
+ #!/usr/bin/env osascript -l JavaScript
2
+
3
+ /**
4
+ * get-screen-context.jxa
5
+ *
6
+ * Combined script: gets windows list + focused window UI tree in ONE osascript spawn.
7
+ * macOS equivalent of get-screen-context.ps1.
8
+ *
9
+ * Output shape (matches PS1 exactly so accessibility.ts needs no changes):
10
+ * { windows: WindowInfo[], uiTree: UINode | null }
11
+ *
12
+ * Parameters (read from argv):
13
+ * -FocusedProcessId <number> — if provided, include UI tree for that process
14
+ * -MaxDepth <number> — max UI tree depth (default 2)
15
+ *
16
+ * Output: script result (last expression) goes to stdout for Node.js execFile.
17
+ *
18
+ * Usage: osascript -l JavaScript get-screen-context.jxa -FocusedProcessId 1234 -MaxDepth 2
19
+ */
20
+
21
+ ObjC.import('stdlib');
22
+
23
+ // ─── Argument parsing ────────────────────────────────────────────────────────
24
+
25
+ function parseArgs() {
26
+ var args = ObjC.unwrap($.NSProcessInfo.processInfo.arguments);
27
+ var result = { focusedProcessId: 0, maxDepth: 2 };
28
+ for (var i = 0; i < args.length; i++) {
29
+ var arg = ObjC.unwrap(args[i]);
30
+ if (arg === '-FocusedProcessId' && i + 1 < args.length) {
31
+ result.focusedProcessId = parseInt(ObjC.unwrap(args[i + 1]), 10) || 0;
32
+ } else if (arg === '-MaxDepth' && i + 1 < args.length) {
33
+ result.maxDepth = parseInt(ObjC.unwrap(args[i + 1]), 10) || 2;
34
+ }
35
+ }
36
+ return result;
37
+ }
38
+
39
+ // ─── Helpers ─────────────────────────────────────────────────────────────────
40
+
41
+ function safeGet(obj, prop, def) {
42
+ try { var v = obj[prop](); return (v !== undefined && v !== null) ? v : def; }
43
+ catch (e) { return def; }
44
+ }
45
+
46
+ function safeProp(el, prop, def) {
47
+ try { return el[prop] ? el[prop]() : def; }
48
+ catch (e) { return def; }
49
+ }
50
+
51
+ function getBounds(el) {
52
+ try {
53
+ var pos = el.position();
54
+ var sz = el.size();
55
+ return { x: pos[0], y: pos[1], width: sz[0], height: sz[1] };
56
+ } catch (e) {
57
+ return { x: 0, y: 0, width: 0, height: 0 };
58
+ }
59
+ }
60
+
61
+ // ─── Part 1: windows list (same logic as get-windows.jxa) ────────────────────
62
+
63
+ function getWindows(SystemEvents) {
64
+ var processes = SystemEvents.processes.where({ backgroundOnly: false });
65
+ var results = [];
66
+
67
+ for (var i = 0; i < processes.length; i++) {
68
+ try {
69
+ var proc = processes[i];
70
+ var procName = safeGet(proc, 'name', 'unknown');
71
+ var procId = safeGet(proc, 'unixId', 0);
72
+ var windows = proc.windows;
73
+
74
+ for (var j = 0; j < windows.length; j++) {
75
+ try {
76
+ var win = windows[j];
77
+ var title = safeProp(win, 'name', '');
78
+ if (!title || title.trim().length === 0) continue;
79
+
80
+ var isMinimized = false;
81
+ try { isMinimized = win.miniaturized(); } catch (e) {}
82
+
83
+ results.push({
84
+ handle: procId,
85
+ title: title,
86
+ processName: procName,
87
+ processId: procId,
88
+ bounds: getBounds(win),
89
+ isMinimized: isMinimized
90
+ });
91
+ } catch (e) {}
92
+ }
93
+ } catch (e) {}
94
+ }
95
+
96
+ return results;
97
+ }
98
+
99
+ // ─── Part 2: UI tree for focused process ─────────────────────────────────────
100
+
101
+ // Interactive macOS roles worth sending to the LLM
102
+ var INTERACTIVE_ROLES = {
103
+ 'AXButton': true, 'AXTextField': true, 'AXTextArea': true,
104
+ 'AXComboBox': true, 'AXCheckBox': true, 'AXRadioButton': true,
105
+ 'AXLink': true, 'AXMenuItem': true, 'AXMenu': true,
106
+ 'AXTab': true, 'AXTabGroup': true, 'AXList': true,
107
+ 'AXOutline': true, 'AXSlider': true, 'AXScrollBar': true,
108
+ 'AXToolbar': true, 'AXWebArea': true, 'AXStaticText': true,
109
+ 'AXGroup': true
110
+ };
111
+
112
+ function buildUINode(element, maxDepth, depth) {
113
+ if (depth > maxDepth) return null;
114
+
115
+ var name = '';
116
+ var role = '';
117
+ var value = '';
118
+ var subrole = '';
119
+
120
+ try { name = element.name() || ''; } catch (e) {}
121
+ try { role = element.role() || ''; } catch (e) {}
122
+ try { value = element.value() || ''; } catch (e) {}
123
+ // Secureness lives in the AX SUBROLE: a password field reports
124
+ // role=AXTextField, subrole=AXSecureTextField. Surface it so the snapshot
125
+ // builder can redact the value (audit 2026-06-11, M2) — without this the
126
+ // cleartext password reached the LLM prompt + fingerprint on macOS.
127
+ try { subrole = element.subrole() || ''; } catch (e) {}
128
+
129
+ var isInteractive = !!INTERACTIVE_ROLES[role];
130
+ var hasName = name.trim().length > 0;
131
+ var isSecure = (subrole === 'AXSecureTextField');
132
+ if (isSecure) { value = ''; } // never emit the secret, even pre-redaction
133
+
134
+ // Build children first so we can decide whether to include this node
135
+ var children = [];
136
+ if (depth < maxDepth) {
137
+ try {
138
+ var kids = element.uiElements();
139
+ for (var i = 0; i < kids.length; i++) {
140
+ try {
141
+ var child = buildUINode(kids[i], maxDepth, depth + 1);
142
+ if (child) children.push(child);
143
+ } catch (e) {}
144
+ }
145
+ } catch (e) {}
146
+ }
147
+
148
+ // Skip uninteresting, unnamed leaf nodes (mirrors PS1 behaviour)
149
+ if (!isInteractive && !hasName && children.length === 0) return null;
150
+
151
+ return {
152
+ name: name,
153
+ automationId: '', // macOS has no AutomationId concept
154
+ controlType: role,
155
+ subrole: subrole || undefined,
156
+ secure: isSecure || undefined,
157
+ bounds: getBounds(element),
158
+ value: value || undefined,
159
+ children: children.length > 0 ? children : undefined
160
+ };
161
+ }
162
+
163
+ function getUITree(SystemEvents, focusedProcessId, maxDepth) {
164
+ if (!focusedProcessId) return null;
165
+
166
+ try {
167
+ var procs = SystemEvents.processes.where({ unixId: focusedProcessId });
168
+ if (procs.length === 0) return null;
169
+
170
+ var proc = procs[0];
171
+ var windows = proc.windows;
172
+ if (windows.length === 0) return null;
173
+
174
+ // Use the first (frontmost) window
175
+ return buildUINode(windows[0], maxDepth, 0);
176
+ } catch (e) {
177
+ return null;
178
+ }
179
+ }
180
+
181
+ // ─── Main ────────────────────────────────────────────────────────────────────
182
+
183
+ var output;
184
+ try {
185
+ var params = parseArgs();
186
+ var SystemEvents = Application('System Events');
187
+ SystemEvents.includeStandardAdditions = true;
188
+
189
+ var windows = getWindows(SystemEvents);
190
+ var uiTree = getUITree(SystemEvents, params.focusedProcessId, params.maxDepth);
191
+
192
+ output = JSON.stringify({ windows: windows, uiTree: uiTree });
193
+ } catch (error) {
194
+ output = JSON.stringify({ error: error.toString() });
195
+ $.exit(1);
196
+ }
197
+ output;
@@ -0,0 +1,141 @@
1
+ #!/usr/bin/env bash
2
+ # get-ui-tree.sh — Get the accessibility tree of the frontmost app on macOS
3
+ #
4
+ # Uses JXA (JavaScript for Automation) + System Events to walk the UI element
5
+ # hierarchy of the frontmost application.
6
+ #
7
+ # Returns JSON with name, role, subrole, description, position, size for each element.
8
+ #
9
+ # Parameters:
10
+ # -ProcessId <number> Optional. If specified, get tree for this process.
11
+ # Otherwise uses the frontmost app.
12
+ # -MaxDepth <number> Optional. Maximum traversal depth (default 6).
13
+ #
14
+ # Usage:
15
+ # chmod +x scripts/mac/get-ui-tree.sh
16
+ # ./scripts/mac/get-ui-tree.sh
17
+ # ./scripts/mac/get-ui-tree.sh -ProcessId 1234
18
+
19
+ set -euo pipefail
20
+
21
+ # Parse arguments
22
+ PROCESS_ID=""
23
+ MAX_DEPTH="6"
24
+
25
+ while [[ $# -gt 0 ]]; do
26
+ case "$1" in
27
+ -ProcessId) PROCESS_ID="$2"; shift 2 ;;
28
+ -MaxDepth) MAX_DEPTH="$2"; shift 2 ;;
29
+ *) shift ;;
30
+ esac
31
+ done
32
+
33
+ # Run JXA inline to walk the accessibility tree
34
+ osascript -l JavaScript - "$PROCESS_ID" "$MAX_DEPTH" <<'ENDSCRIPT'
35
+ ObjC.import('stdlib');
36
+
37
+ var args = ObjC.unwrap($.NSProcessInfo.processInfo.arguments);
38
+ // Arguments: [osascript, -, processId, maxDepth]
39
+ var targetPid = args.length > 2 ? parseInt(ObjC.unwrap(args[2]), 10) : 0;
40
+ var maxDepth = args.length > 3 ? parseInt(ObjC.unwrap(args[3]), 10) : 6;
41
+
42
+ function safeGet(obj, property, defaultValue) {
43
+ try {
44
+ var val = obj[property]();
45
+ return val !== undefined && val !== null ? val : defaultValue;
46
+ } catch (e) {
47
+ return defaultValue;
48
+ }
49
+ }
50
+
51
+ function walkTree(element, depth) {
52
+ if (depth > maxDepth) return null;
53
+
54
+ var node = {};
55
+
56
+ try { node.name = element.name() || ''; } catch (e) { node.name = ''; }
57
+ try { node.role = element.role() || ''; } catch (e) { node.role = ''; }
58
+ try { node.subrole = element.subrole() || ''; } catch (e) { node.subrole = ''; }
59
+ try { node.description = element.description() || ''; } catch (e) { node.description = ''; }
60
+ try { node.value = String(element.value() || ''); } catch (e) { node.value = ''; }
61
+ try { node.enabled = element.enabled(); } catch (e) { node.enabled = true; }
62
+
63
+ // Position and size
64
+ try {
65
+ var pos = element.position();
66
+ var sz = element.size();
67
+ node.position = { x: pos[0] || 0, y: pos[1] || 0 };
68
+ node.size = { width: sz[0] || 0, height: sz[1] || 0 };
69
+ } catch (e) {
70
+ node.position = { x: 0, y: 0 };
71
+ node.size = { width: 0, height: 0 };
72
+ }
73
+
74
+ // Recurse into children
75
+ var children = [];
76
+ try {
77
+ var uiElements = element.uiElements();
78
+ for (var i = 0; i < uiElements.length && i < 50; i++) {
79
+ var child = walkTree(uiElements[i], depth + 1);
80
+ if (child) children.push(child);
81
+ }
82
+ } catch (e) {
83
+ // No children accessible
84
+ }
85
+
86
+ if (children.length > 0) {
87
+ node.children = children;
88
+ }
89
+
90
+ return node;
91
+ }
92
+
93
+ try {
94
+ var SystemEvents = Application('System Events');
95
+ SystemEvents.includeStandardAdditions = true;
96
+
97
+ var targetProcess;
98
+ if (targetPid && targetPid > 0) {
99
+ var procs = SystemEvents.processes.where({ unixId: targetPid });
100
+ if (procs.length === 0) {
101
+ console.log(JSON.stringify({ error: 'No process found with ID ' + targetPid }));
102
+ $.exit(1);
103
+ }
104
+ targetProcess = procs[0];
105
+ } else {
106
+ // Use frontmost process
107
+ var frontProcs = SystemEvents.processes.where({ frontmost: true });
108
+ if (frontProcs.length === 0) {
109
+ console.log(JSON.stringify({ error: 'No frontmost process found' }));
110
+ $.exit(1);
111
+ }
112
+ targetProcess = frontProcs[0];
113
+ }
114
+
115
+ var processName = safeGet(targetProcess, 'name', 'unknown');
116
+ var processId = safeGet(targetProcess, 'unixId', 0);
117
+
118
+ var tree = {
119
+ processName: processName,
120
+ processId: processId,
121
+ windows: []
122
+ };
123
+
124
+ var windows = targetProcess.windows;
125
+ for (var w = 0; w < windows.length && w < 5; w++) {
126
+ try {
127
+ var winNode = walkTree(windows[w], 0);
128
+ if (winNode) {
129
+ tree.windows.push(winNode);
130
+ }
131
+ } catch (e) {
132
+ // Window not accessible
133
+ }
134
+ }
135
+
136
+ console.log(JSON.stringify(tree, null, 0));
137
+ } catch (error) {
138
+ console.log(JSON.stringify({ error: error.toString() }));
139
+ $.exit(1);
140
+ }
141
+ ENDSCRIPT
@@ -0,0 +1,117 @@
1
+ #!/usr/bin/env osascript -l JavaScript
2
+
3
+ /**
4
+ * get-windows.jxa
5
+ *
6
+ * Lists all visible top-level windows with their properties.
7
+ * Returns a JSON array of all visible windows including:
8
+ * - title: Window title (name)
9
+ * - processName: Name of the owning application
10
+ * - processId: Unix process ID
11
+ * - bounds: {x, y, width, height} window position and size
12
+ * - isMinimized: Boolean indicating if window is minimized
13
+ *
14
+ * Usage: osascript -l JavaScript get-windows.jxa
15
+ */
16
+
17
+ // Utility function to safely get property values
18
+ function safeGet(obj, property, defaultValue) {
19
+ try {
20
+ var val = obj[property]();
21
+ return val !== undefined && val !== null ? val : defaultValue;
22
+ } catch (e) {
23
+ return defaultValue;
24
+ }
25
+ }
26
+
27
+ // Utility function to safely get UI element properties
28
+ function safeElementProp(element, propName, defaultValue) {
29
+ try {
30
+ if (element[propName]) {
31
+ return element[propName]();
32
+ }
33
+ return defaultValue;
34
+ } catch (e) {
35
+ return defaultValue;
36
+ }
37
+ }
38
+
39
+ // run(argv) is the JXA entry point — osascript calls it and its return value becomes stdout.
40
+ function run(argv) {
41
+ try {
42
+ // Get System Events application
43
+ var SystemEvents = Application('System Events');
44
+ SystemEvents.includeStandardAdditions = true;
45
+
46
+ // Get all processes with UI
47
+ var processes = SystemEvents.processes.where({ backgroundOnly: false });
48
+ var results = [];
49
+
50
+ for (var i = 0; i < processes.length; i++) {
51
+ try {
52
+ var process = processes[i];
53
+ var processName = safeGet(process, 'name', 'unknown');
54
+ var processId = safeGet(process, 'unixId', 0);
55
+
56
+ // Get all windows for this process
57
+ var windows = process.windows;
58
+
59
+ for (var j = 0; j < windows.length; j++) {
60
+ try {
61
+ var win = windows[j];
62
+ var title = safeElementProp(win, 'name', '');
63
+
64
+ // Skip windows with no name (invisible/system windows)
65
+ if (!title || title.trim().length === 0) {
66
+ continue;
67
+ }
68
+
69
+ // Get window bounds
70
+ var bounds = { x: 0, y: 0, width: 0, height: 0 };
71
+ try {
72
+ var position = win.position();
73
+ var size = win.size();
74
+ bounds = {
75
+ x: position[0],
76
+ y: position[1],
77
+ width: size[0],
78
+ height: size[1]
79
+ };
80
+ } catch (e) {
81
+ // Bounds not available
82
+ }
83
+
84
+ // Check if minimized - macOS uses "miniaturized"
85
+ var isMinimized = false;
86
+ try {
87
+ isMinimized = win.miniaturized();
88
+ } catch (e) {
89
+ // Property not available, assume not minimized
90
+ }
91
+
92
+ // macOS doesn't have window handles like Windows,
93
+ // but we can use a combination of processId and index
94
+ results.push({
95
+ title: title,
96
+ processName: processName,
97
+ processId: processId,
98
+ bounds: bounds,
99
+ isMinimized: isMinimized
100
+ });
101
+ } catch (e) {
102
+ // Skip windows that throw on property access
103
+ }
104
+ }
105
+ } catch (e) {
106
+ // Skip processes that throw
107
+ }
108
+ }
109
+
110
+ // Return JSON — osascript captures the return value as stdout
111
+ return JSON.stringify(results);
112
+
113
+ } catch (error) {
114
+ // Return error as JSON
115
+ return JSON.stringify({ error: error.toString() });
116
+ }
117
+ }
@@ -0,0 +1,235 @@
1
+ #!/usr/bin/env bash
2
+ # interact-element.sh — Click, type, set value, focus elements on macOS
3
+ #
4
+ # Uses JXA (JavaScript for Automation) + System Events to interact with
5
+ # native macOS UI elements via the Accessibility API.
6
+ #
7
+ # Outputs JSON matching the Windows invoke-element.ps1 / interact-element.ps1 format.
8
+ #
9
+ # Parameters:
10
+ # -Name <string> Match elements by name (contains, case-insensitive)
11
+ # -AutomationId <string> Match by description/identifier
12
+ # -ControlType <string> Filter by control type (Button, Edit, etc.)
13
+ # -ProcessId <number> Required. Process ID of the target application
14
+ # -Action <string> Required. Action: click, set-value, get-value, focus,
15
+ # toggle, expand, collapse, select, sendkeys
16
+ # -Value <string> Value for set-value/sendkeys actions
17
+ #
18
+ # Usage:
19
+ # chmod +x scripts/mac/interact-element.sh
20
+ # ./scripts/mac/interact-element.sh -Name "Save" -Action click -ProcessId 1234
21
+ # ./scripts/mac/interact-element.sh -Name "Search" -Action set-value -Value "hello" -ProcessId 1234
22
+
23
+ set -euo pipefail
24
+
25
+ SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
26
+
27
+ # Parse arguments
28
+ NAME=""
29
+ AUTOMATION_ID=""
30
+ CONTROL_TYPE=""
31
+ PROCESS_ID=""
32
+ ACTION=""
33
+ VALUE=""
34
+
35
+ while [[ $# -gt 0 ]]; do
36
+ case "$1" in
37
+ -Name) NAME="$2"; shift 2 ;;
38
+ -AutomationId) AUTOMATION_ID="$2"; shift 2 ;;
39
+ -ControlType) CONTROL_TYPE="$2"; shift 2 ;;
40
+ -ProcessId) PROCESS_ID="$2"; shift 2 ;;
41
+ -Action) ACTION="$2"; shift 2 ;;
42
+ -Value) VALUE="$2"; shift 2 ;;
43
+ *) shift ;;
44
+ esac
45
+ done
46
+
47
+ # Validate required parameters
48
+ if [[ -z "$ACTION" ]]; then
49
+ echo '{"success":false,"error":"Missing required parameter: -Action"}'
50
+ exit 1
51
+ fi
52
+
53
+ if [[ -z "$PROCESS_ID" || "$PROCESS_ID" == "0" ]]; then
54
+ echo '{"success":false,"error":"Missing required parameter: -ProcessId"}'
55
+ exit 1
56
+ fi
57
+
58
+ # Map Windows ControlType to macOS role
59
+ map_control_type() {
60
+ local ct="$1"
61
+ case "$ct" in
62
+ Button) echo "button" ;;
63
+ Edit) echo "text field" ;;
64
+ Text) echo "static text" ;;
65
+ CheckBox) echo "checkbox" ;;
66
+ RadioButton) echo "radio button" ;;
67
+ ComboBox) echo "pop up button" ;;
68
+ MenuItem) echo "menu item" ;;
69
+ Menu) echo "menu" ;;
70
+ List) echo "list" ;;
71
+ ListItem) echo "row" ;;
72
+ Tree) echo "outline" ;;
73
+ TreeItem) echo "row" ;;
74
+ Tab) echo "tab group" ;;
75
+ TabItem) echo "radio button" ;;
76
+ Window) echo "window" ;;
77
+ *) echo "" ;;
78
+ esac
79
+ }
80
+
81
+ # Determine search name (use Name or AutomationId)
82
+ SEARCH_NAME="${NAME:-$AUTOMATION_ID}"
83
+
84
+ # For actions that can use the existing invoke-element.jxa
85
+ case "$ACTION" in
86
+ click|set-value|get-value|focus)
87
+ # Build JXA arguments
88
+ JXA_ARGS=("-action" "$ACTION" "-processId" "$PROCESS_ID")
89
+
90
+ if [[ -n "$SEARCH_NAME" ]]; then
91
+ JXA_ARGS+=("-name" "$SEARCH_NAME")
92
+ fi
93
+
94
+ if [[ -n "$CONTROL_TYPE" ]]; then
95
+ MAPPED_ROLE=$(map_control_type "$CONTROL_TYPE")
96
+ if [[ -n "$MAPPED_ROLE" ]]; then
97
+ JXA_ARGS+=("-role" "$MAPPED_ROLE")
98
+ fi
99
+ fi
100
+
101
+ if [[ -n "$VALUE" ]]; then
102
+ JXA_ARGS+=("-value" "$VALUE")
103
+ fi
104
+
105
+ # Run invoke-element.jxa
106
+ osascript -l JavaScript "$SCRIPT_DIR/invoke-element.jxa" "${JXA_ARGS[@]}" 2>/dev/null || \
107
+ echo '{"success":false,"error":"JXA script failed"}'
108
+ ;;
109
+
110
+ sendkeys)
111
+ # Focus the element, then use System Events keystroke
112
+ if [[ -z "$VALUE" ]]; then
113
+ echo '{"success":false,"error":"Value parameter required for sendkeys action"}'
114
+ exit 0
115
+ fi
116
+
117
+ # First focus the element
118
+ JXA_ARGS=("-action" "focus" "-processId" "$PROCESS_ID")
119
+ if [[ -n "$SEARCH_NAME" ]]; then
120
+ JXA_ARGS+=("-name" "$SEARCH_NAME")
121
+ fi
122
+
123
+ FOCUS_RESULT=$(osascript -l JavaScript "$SCRIPT_DIR/invoke-element.jxa" "${JXA_ARGS[@]}" 2>/dev/null || echo '{"success":false}')
124
+
125
+ # Check if focus succeeded
126
+ FOCUS_OK=$(echo "$FOCUS_RESULT" | /usr/bin/python3 -c "import sys,json; d=json.load(sys.stdin); print('true' if d.get('success') else 'false')" 2>/dev/null || echo "false")
127
+
128
+ if [[ "$FOCUS_OK" != "true" ]]; then
129
+ echo '{"success":false,"error":"Failed to focus element for sendkeys"}'
130
+ exit 0
131
+ fi
132
+
133
+ # Small delay for focus to take effect
134
+ sleep 0.1
135
+
136
+ # Send keystrokes via System Events (sanitize input via python3)
137
+ SAFE_VALUE=$(/usr/bin/python3 -c "import sys,json; print(json.dumps(sys.argv[1]))" "$VALUE" 2>/dev/null)
138
+ # SAFE_VALUE is now a JSON-quoted string like "hello \"world\""
139
+ # Strip outer quotes for AppleScript
140
+ SAFE_VALUE=${SAFE_VALUE:1:-1}
141
+ osascript -e "
142
+ tell application \"System Events\"
143
+ keystroke \"${SAFE_VALUE}\"
144
+ end tell
145
+ " 2>/dev/null
146
+
147
+ if [[ $? -eq 0 ]]; then
148
+ echo "{\"success\":true,\"action\":\"sendkeys\",\"method\":\"keystroke\",\"value\":$(echo "$VALUE" | /usr/bin/python3 -c 'import sys,json; print(json.dumps(sys.stdin.read().strip()))')}"
149
+ else
150
+ echo '{"success":false,"error":"keystroke failed"}'
151
+ fi
152
+ ;;
153
+
154
+ toggle)
155
+ # Toggle a checkbox: click it to toggle state
156
+ JXA_ARGS=("-action" "click" "-processId" "$PROCESS_ID")
157
+ if [[ -n "$SEARCH_NAME" ]]; then
158
+ JXA_ARGS+=("-name" "$SEARCH_NAME")
159
+ fi
160
+ if [[ -n "$CONTROL_TYPE" ]]; then
161
+ MAPPED_ROLE=$(map_control_type "$CONTROL_TYPE")
162
+ if [[ -n "$MAPPED_ROLE" ]]; then
163
+ JXA_ARGS+=("-role" "$MAPPED_ROLE")
164
+ fi
165
+ fi
166
+
167
+ RESULT=$(osascript -l JavaScript "$SCRIPT_DIR/invoke-element.jxa" "${JXA_ARGS[@]}" 2>/dev/null || echo '{"success":false}')
168
+
169
+ # Rewrite the result to indicate toggle action
170
+ echo "$RESULT" | /usr/bin/python3 -c "
171
+ import sys, json
172
+ try:
173
+ d = json.load(sys.stdin)
174
+ d['action'] = 'toggle'
175
+ if d.get('success'):
176
+ d['method'] = 'click-toggle'
177
+ print(json.dumps(d, separators=(',', ':')))
178
+ except:
179
+ print('{\"success\":false,\"error\":\"toggle failed\"}')" 2>/dev/null || echo '{"success":false,"error":"toggle failed"}'
180
+ ;;
181
+
182
+ expand|collapse)
183
+ # macOS doesn't have ExpandCollapsePattern directly.
184
+ # Try AXPress action (works for disclosure triangles) or click
185
+ JXA_ARGS=("-action" "click" "-processId" "$PROCESS_ID")
186
+ if [[ -n "$SEARCH_NAME" ]]; then
187
+ JXA_ARGS+=("-name" "$SEARCH_NAME")
188
+ fi
189
+
190
+ RESULT=$(osascript -l JavaScript "$SCRIPT_DIR/invoke-element.jxa" "${JXA_ARGS[@]}" 2>/dev/null || echo '{"success":false}')
191
+
192
+ echo "$RESULT" | /usr/bin/python3 -c "
193
+ import sys, json
194
+ try:
195
+ d = json.load(sys.stdin)
196
+ d['action'] = '${ACTION}'
197
+ if d.get('success'):
198
+ d['method'] = 'click-${ACTION}'
199
+ print(json.dumps(d, separators=(',', ':')))
200
+ except:
201
+ print('{\"success\":false,\"error\":\"${ACTION} failed\"}')" 2>/dev/null || echo "{\"success\":false,\"error\":\"${ACTION} failed\"}"
202
+ ;;
203
+
204
+ select)
205
+ # Select: click the item (works for list items, tabs, radio buttons)
206
+ JXA_ARGS=("-action" "click" "-processId" "$PROCESS_ID")
207
+ if [[ -n "$SEARCH_NAME" ]]; then
208
+ JXA_ARGS+=("-name" "$SEARCH_NAME")
209
+ fi
210
+ if [[ -n "$CONTROL_TYPE" ]]; then
211
+ MAPPED_ROLE=$(map_control_type "$CONTROL_TYPE")
212
+ if [[ -n "$MAPPED_ROLE" ]]; then
213
+ JXA_ARGS+=("-role" "$MAPPED_ROLE")
214
+ fi
215
+ fi
216
+
217
+ RESULT=$(osascript -l JavaScript "$SCRIPT_DIR/invoke-element.jxa" "${JXA_ARGS[@]}" 2>/dev/null || echo '{"success":false}')
218
+
219
+ echo "$RESULT" | /usr/bin/python3 -c "
220
+ import sys, json
221
+ try:
222
+ d = json.load(sys.stdin)
223
+ d['action'] = 'select'
224
+ if d.get('success'):
225
+ d['method'] = 'click-select'
226
+ print(json.dumps(d, separators=(',', ':')))
227
+ except:
228
+ print('{\"success\":false,\"error\":\"select failed\"}')" 2>/dev/null || echo '{"success":false,"error":"select failed"}'
229
+ ;;
230
+
231
+ *)
232
+ echo "{\"success\":false,\"error\":\"Unknown action: $ACTION\"}"
233
+ exit 0
234
+ ;;
235
+ esac