@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,670 @@
1
+ "use strict";
2
+ /**
3
+ * Accessibility Bridge — queries the native accessibility tree.
4
+ *
5
+ * Windows: uses PSRunner (persistent powershell process via ps-bridge.ps1).
6
+ * One-time assembly load cost ~800ms, then each call is <50ms.
7
+ * macOS: spawns osascript per call (unchanged).
8
+ *
9
+ * v4: PSRunner replaces per-call powershell.exe spawning on Windows.
10
+ * MaxDepth raised to 4 so nested elements are visible to the LLM.
11
+ */
12
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
13
+ if (k2 === undefined) k2 = k;
14
+ var desc = Object.getOwnPropertyDescriptor(m, k);
15
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
16
+ desc = { enumerable: true, get: function() { return m[k]; } };
17
+ }
18
+ Object.defineProperty(o, k2, desc);
19
+ }) : (function(o, m, k, k2) {
20
+ if (k2 === undefined) k2 = k;
21
+ o[k2] = m[k];
22
+ }));
23
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
24
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
25
+ }) : function(o, v) {
26
+ o["default"] = v;
27
+ });
28
+ var __importStar = (this && this.__importStar) || (function () {
29
+ var ownKeys = function(o) {
30
+ ownKeys = Object.getOwnPropertyNames || function (o) {
31
+ var ar = [];
32
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
33
+ return ar;
34
+ };
35
+ return ownKeys(o);
36
+ };
37
+ return function (mod) {
38
+ if (mod && mod.__esModule) return mod;
39
+ var result = {};
40
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
41
+ __setModuleDefault(result, mod);
42
+ return result;
43
+ };
44
+ })();
45
+ Object.defineProperty(exports, "__esModule", { value: true });
46
+ exports.AccessibilityBridge = void 0;
47
+ const child_process_1 = require("child_process");
48
+ const os = __importStar(require("os"));
49
+ const path = __importStar(require("path"));
50
+ const util_1 = require("util");
51
+ const ps_runner_1 = require("./ps-runner");
52
+ const paths_1 = require("../paths");
53
+ const execFileAsync = (0, util_1.promisify)(child_process_1.execFile);
54
+ const PLATFORM = os.platform();
55
+ const IS_WIN = PLATFORM === 'win32';
56
+ const IS_MAC = PLATFORM === 'darwin';
57
+ const IS_LINUX = PLATFORM === 'linux';
58
+ const SCRIPTS_DIR = path.join((0, paths_1.getPackageRoot)(), 'scripts');
59
+ const MAC_SCRIPTS_DIR = path.join(SCRIPTS_DIR, 'mac');
60
+ // macOS JXA can be slow on first call; 30s gives headroom.
61
+ const MAC_SCRIPT_TIMEOUT = 30000;
62
+ const MAX_DEPTH = 8; // raised to 8 — Electron/WebView2 apps (Outlook olk) nest deeply: Window > Pane > Pane > Pane > Button
63
+ /** Cached shell availability (macOS only — Windows uses psRunner) */
64
+ let macShellAvailable = null;
65
+ let macShellCheckedAt = 0;
66
+ const MAC_SHELL_TTL = 30000; // Re-check every 30s if previously denied (permission may be granted mid-session)
67
+ // ── Linux AT-SPI backend ────────────────────────────────────────────────────
68
+ // Linux AT-SPI backend is planned. Currently returns safe empty results for
69
+ // tree-walking methods (findElement, invokeElement, getScreenContext, etc.).
70
+ // Clipboard already works via wl-paste/wl-copy, xclip, or xsel.
71
+ // getWindows() has a basic wmctrl fallback for window awareness.
72
+ //
73
+ // To contribute a full AT-SPI backend: implement via `dbus-next` (pure JS
74
+ // D-Bus client) or `gi` (GObject introspection) bindings. The AT-SPI2 bus
75
+ // lives at org.a]11y.Bus and exposes Accessible, Component, Action, Text,
76
+ // and Value interfaces that map cleanly onto UIElement / WindowInfo.
77
+ // ─────────────────────────────────────────────────────────────────────────────
78
+ function unsupportedLinuxResult(fallback, feature) {
79
+ console.debug(`[A11y] Linux accessibility feature not yet implemented: ${feature}`);
80
+ return fallback;
81
+ }
82
+ class AccessibilityBridge {
83
+ windowCache = null;
84
+ WINDOW_CACHE_TTL = 2000;
85
+ screenContextCache = null;
86
+ SCREEN_CONTEXT_CACHE_TTL = 2000;
87
+ taskbarCache = null;
88
+ TASKBAR_CACHE_TTL = 30000;
89
+ explorerProcessId = null;
90
+ /**
91
+ * Check if the platform's shell is available.
92
+ * Windows: always true (PSRunner starts lazily).
93
+ * macOS: checks osascript + Accessibility permissions.
94
+ */
95
+ async isShellAvailable() {
96
+ if (IS_WIN)
97
+ return true; // PSRunner handles availability
98
+ if (IS_LINUX)
99
+ return false; // AT-SPI bridge not yet implemented
100
+ // If previously granted, trust it. If denied, re-check periodically (user may grant mid-session).
101
+ if (macShellAvailable === true)
102
+ return true;
103
+ if (macShellAvailable === false && (Date.now() - macShellCheckedAt) < MAC_SHELL_TTL)
104
+ return false;
105
+ try {
106
+ // Must test actual window access — processes.length works WITHOUT assistive access,
107
+ // but accessing windows/UI elements requires it. This catches the false-positive
108
+ // where osascript runs fine but all A11y queries return empty.
109
+ await execFileAsync('osascript', ['-l', 'JavaScript', '-e',
110
+ 'var se = Application("System Events"); ' +
111
+ 'var p = se.processes.whose({frontmost: true})[0]; ' +
112
+ 'p.windows.length; true'], { timeout: 5000 });
113
+ macShellAvailable = true;
114
+ macShellCheckedAt = Date.now();
115
+ console.log('✅ Accessibility bridge ready (osascript)');
116
+ }
117
+ catch (err) {
118
+ macShellAvailable = false;
119
+ macShellCheckedAt = Date.now();
120
+ const msg = (err.stderr || '') + (err.message || '');
121
+ const isAuthError = msg.includes('not authorized') || msg.includes('not allowed assistive access') || msg.includes('assistive');
122
+ if (isAuthError) {
123
+ console.error('❌ Accessibility: osascript is not allowed assistive access.\n' +
124
+ ' → System Settings → Privacy & Security → Accessibility\n' +
125
+ ' → Enable your terminal app (Terminal.app / iTerm / etc.)\n' +
126
+ ' A11y tree will be unavailable — falling back to OCR-only mode.');
127
+ }
128
+ else {
129
+ console.error(`❌ osascript not available: ${msg.slice(0, 200)}\n Accessibility bridge disabled.`);
130
+ }
131
+ }
132
+ return macShellAvailable;
133
+ }
134
+ /** Start the PSRunner bridge early so the 800ms assembly load happens in background. */
135
+ async warmup() {
136
+ if (IS_WIN) {
137
+ ps_runner_1.psRunner.start().catch(() => { }); // fire-and-forget — errors surface on first actual call
138
+ }
139
+ }
140
+ /**
141
+ * Invalidate caches — call after every action so the next read sees fresh UI state.
142
+ */
143
+ invalidateCache() {
144
+ this.windowCache = null;
145
+ this.screenContextCache = null;
146
+ }
147
+ // ── Windows bridge helper ──────────────────────────────────────────────────
148
+ async winCmd(command) {
149
+ return ps_runner_1.psRunner.run(command);
150
+ }
151
+ // ── macOS script helper ────────────────────────────────────────────────────
152
+ runMacScript(scriptName, args = []) {
153
+ return new Promise((resolve, reject) => {
154
+ const scriptPath = path.join(MAC_SCRIPTS_DIR, scriptName);
155
+ (0, child_process_1.execFile)('osascript', ['-l', 'JavaScript', scriptPath, ...args], {
156
+ timeout: MAC_SCRIPT_TIMEOUT,
157
+ maxBuffer: 1024 * 1024 * 5,
158
+ }, (error, stdout, stderr) => {
159
+ if (error) {
160
+ const detail = stderr?.trim() ? ` — ${stderr.trim()}` : '';
161
+ reject(new Error(error.message + detail));
162
+ return;
163
+ }
164
+ try {
165
+ const result = JSON.parse(stdout.trim());
166
+ if (result.error)
167
+ reject(new Error(result.error));
168
+ else
169
+ resolve(result);
170
+ }
171
+ catch (pe) {
172
+ reject(pe);
173
+ }
174
+ });
175
+ });
176
+ }
177
+ // ── Public API ─────────────────────────────────────────────────────────────
178
+ async getWindows(forceRefresh = false) {
179
+ if (!forceRefresh &&
180
+ this.windowCache &&
181
+ Date.now() - this.windowCache.timestamp < this.WINDOW_CACHE_TTL) {
182
+ return this.windowCache.windows;
183
+ }
184
+ let windows;
185
+ if (IS_WIN) {
186
+ const result = await this.winCmd({ cmd: 'get-screen-context', maxDepth: 0 });
187
+ windows = result.windows ?? [];
188
+ // Update screen context cache timestamp so we don't double-fetch
189
+ this.windowCache = { windows, timestamp: Date.now() };
190
+ }
191
+ else if (IS_MAC) {
192
+ windows = await this.runMacScript('get-windows.jxa');
193
+ this.windowCache = { windows, timestamp: Date.now() };
194
+ }
195
+ else {
196
+ // Linux: try wmctrl for basic window awareness before falling back to empty
197
+ windows = await this.linuxGetWindowsViaWmctrl();
198
+ this.windowCache = { windows, timestamp: Date.now() };
199
+ }
200
+ return windows;
201
+ }
202
+ /**
203
+ * Linux fallback: parse `wmctrl -l -p` output into WindowInfo[].
204
+ * Returns [] if wmctrl is not installed or fails.
205
+ */
206
+ async linuxGetWindowsViaWmctrl() {
207
+ try {
208
+ const { execFileSync } = require('child_process');
209
+ const output = execFileSync('wmctrl', ['-l', '-p'], {
210
+ timeout: 3000,
211
+ encoding: 'utf-8',
212
+ });
213
+ const windows = [];
214
+ for (const line of output.trim().split('\n')) {
215
+ if (!line.trim())
216
+ continue;
217
+ // Format: 0x03c00003 0 12345 hostname Window Title Here
218
+ const parts = line.trim().split(/\s+/);
219
+ if (parts.length < 5)
220
+ continue;
221
+ const handle = parseInt(parts[0], 16) || 0;
222
+ const pid = parseInt(parts[2], 10) || 0;
223
+ const title = parts.slice(4).join(' ');
224
+ if (!title || title === 'Desktop')
225
+ continue;
226
+ windows.push({
227
+ handle,
228
+ title,
229
+ processName: '', // wmctrl doesn't provide process names
230
+ processId: pid,
231
+ bounds: { x: 0, y: 0, width: 0, height: 0 }, // wmctrl -l doesn't include geometry
232
+ isMinimized: false,
233
+ });
234
+ }
235
+ return windows;
236
+ }
237
+ catch {
238
+ // wmctrl not installed or failed — return empty
239
+ return unsupportedLinuxResult([], 'getWindows (wmctrl fallback)');
240
+ }
241
+ }
242
+ async findElement(opts) {
243
+ // v0.9.0 a11y scope fix: when the caller omits processId, default to the
244
+ // foreground window. The PSBridge / mac native script enforce a results
245
+ // cap during a descendant scan; without a window scope, the cap can be
246
+ // hit before the real target is reached if other windows have matching
247
+ // names earlier in the desktop tree. Foreground-window scope is almost
248
+ // always the right default for an agent that just said "click X".
249
+ let processId = opts.processId;
250
+ if (!processId) {
251
+ try {
252
+ const fg = await this.getActiveWindow();
253
+ if (fg?.processId)
254
+ processId = fg.processId;
255
+ }
256
+ catch { /* tolerate failure — fall through to original behaviour */ }
257
+ }
258
+ if (IS_WIN) {
259
+ const result = await this.winCmd({
260
+ cmd: 'find-element',
261
+ ...(opts.name && { name: opts.name }),
262
+ ...(opts.automationId && { automationId: opts.automationId }),
263
+ ...(opts.controlType && { controlType: opts.controlType }),
264
+ ...(processId && { processId }),
265
+ });
266
+ return Array.isArray(result) ? result : [];
267
+ }
268
+ if (IS_MAC) {
269
+ const args = [];
270
+ if (opts.name)
271
+ args.push('-Name', opts.name);
272
+ if (opts.automationId)
273
+ args.push('-AutomationId', opts.automationId);
274
+ if (opts.controlType)
275
+ args.push('-ControlType', opts.controlType);
276
+ if (processId)
277
+ args.push('-ProcessId', String(processId));
278
+ return this.runMacScript('find-element.jxa', args);
279
+ }
280
+ return unsupportedLinuxResult([], 'findElement');
281
+ }
282
+ async invokeElement(opts) {
283
+ let processId = opts.processId;
284
+ // v0.9.0 a11y scope fix: prefer foreground window when caller omits
285
+ // processId. findElement() now also defaults to foreground, so this
286
+ // path mostly handles the rare case where the foreground lookup fails.
287
+ if (!processId) {
288
+ try {
289
+ const fg = await this.getActiveWindow();
290
+ if (fg?.processId)
291
+ processId = fg.processId;
292
+ }
293
+ catch { /* fall through to find-element resolution */ }
294
+ }
295
+ if (!processId) {
296
+ const elements = await this.findElement({
297
+ name: opts.name,
298
+ automationId: opts.automationId,
299
+ controlType: opts.controlType,
300
+ });
301
+ if (!elements?.length) {
302
+ return { success: false, error: `Element not found: ${opts.name ?? opts.automationId}` };
303
+ }
304
+ const el = elements[0];
305
+ processId = el.processId;
306
+ if (!processId && el.bounds?.width > 0 && opts.action === 'click') {
307
+ const cx = el.bounds.x + Math.floor(el.bounds.width / 2);
308
+ const cy = el.bounds.y + Math.floor(el.bounds.height / 2);
309
+ return { success: true, clickPoint: { x: cx, y: cy } };
310
+ }
311
+ if (!processId) {
312
+ return { success: false, error: `No processId for: ${opts.name ?? opts.automationId}` };
313
+ }
314
+ }
315
+ if (IS_WIN) {
316
+ const result = await this.winCmd({
317
+ cmd: 'invoke-element',
318
+ processId,
319
+ action: opts.action,
320
+ ...(opts.name && { name: opts.name }),
321
+ ...(opts.automationId && { automationId: opts.automationId }),
322
+ ...(opts.controlType && { controlType: opts.controlType }),
323
+ ...(opts.value && { value: opts.value }),
324
+ });
325
+ return result;
326
+ }
327
+ if (IS_MAC) {
328
+ const args = ['-Action', opts.action, '-ProcessId', String(processId)];
329
+ if (opts.name)
330
+ args.push('-Name', opts.name);
331
+ if (opts.automationId)
332
+ args.push('-AutomationId', opts.automationId);
333
+ if (opts.controlType)
334
+ args.push('-ControlType', opts.controlType);
335
+ if (opts.value)
336
+ args.push('-Value', opts.value);
337
+ return this.runMacScript('invoke-element.jxa', args);
338
+ }
339
+ return unsupportedLinuxResult({ success: false, error: 'Linux accessibility bridge not implemented' }, 'invokeElement');
340
+ }
341
+ async focusWindow(title, processId) {
342
+ try {
343
+ let result;
344
+ if (IS_WIN) {
345
+ result = await this.winCmd({
346
+ cmd: 'focus-window',
347
+ restore: true,
348
+ ...(title && { title }),
349
+ ...(processId && { processId }),
350
+ });
351
+ }
352
+ else if (IS_MAC) {
353
+ const args = [];
354
+ if (title)
355
+ args.push('-Title', title);
356
+ if (processId)
357
+ args.push('-ProcessId', String(processId));
358
+ args.push('-Restore');
359
+ result = await this.runMacScript('focus-window.jxa', args);
360
+ }
361
+ else {
362
+ result = unsupportedLinuxResult({ success: false, error: 'Linux accessibility bridge not implemented' }, 'focusWindow');
363
+ }
364
+ this.invalidateCache();
365
+ return result;
366
+ }
367
+ catch (err) {
368
+ return { success: false, error: String(err) };
369
+ }
370
+ }
371
+ async getActiveWindow() {
372
+ try {
373
+ let fg;
374
+ if (IS_WIN) {
375
+ fg = await this.winCmd({ cmd: 'get-foreground-window' });
376
+ }
377
+ else if (IS_MAC) {
378
+ fg = await this.runMacScript('get-foreground-window.jxa');
379
+ }
380
+ else {
381
+ return null;
382
+ }
383
+ if (!fg?.success)
384
+ return null;
385
+ const windows = await this.getWindows(true);
386
+ const match = windows.find(w => w.processId === fg.processId);
387
+ if (match)
388
+ return match;
389
+ return {
390
+ handle: fg.handle,
391
+ title: fg.title,
392
+ processName: fg.processName,
393
+ processId: fg.processId,
394
+ bounds: { x: 0, y: 0, width: 0, height: 0 },
395
+ isMinimized: false,
396
+ };
397
+ }
398
+ catch {
399
+ try {
400
+ const windows = await this.getWindows(true);
401
+ return windows.find(w => !w.isMinimized) ?? null;
402
+ }
403
+ catch {
404
+ return null;
405
+ }
406
+ }
407
+ }
408
+ async findWindow(appNameOrTitle) {
409
+ const lower = appNameOrTitle.toLowerCase();
410
+ const windows = await this.getWindows();
411
+ return (windows.find(w => w.processName.toLowerCase() === lower) ??
412
+ windows.find(w => w.title.toLowerCase().includes(lower)) ??
413
+ windows.find(w => w.processName.toLowerCase().includes(lower)) ??
414
+ null);
415
+ }
416
+ /**
417
+ * Restore an off-screen window to a visible state.
418
+ * Uses focusWindow with restore:true — already cross-platform via winCmd/runMacScript.
419
+ * Caller should additionally press 'super+up' if bounds are still negative after this call.
420
+ */
421
+ async restoreWindow(processId, title) {
422
+ return this.focusWindow(title, processId);
423
+ }
424
+ async getFocusedElement() {
425
+ if (IS_WIN) {
426
+ try {
427
+ const result = await this.winCmd({ cmd: 'get-focused-element' });
428
+ if (!result?.success)
429
+ return null;
430
+ return {
431
+ name: result.name ?? '',
432
+ automationId: result.automationId ?? '',
433
+ controlType: result.controlType ?? '',
434
+ className: result.className ?? '',
435
+ processId: result.processId ?? 0,
436
+ isEnabled: result.isEnabled ?? true,
437
+ bounds: result.bounds ?? { x: 0, y: 0, width: 0, height: 0 },
438
+ value: result.value ?? '',
439
+ };
440
+ }
441
+ catch {
442
+ return null;
443
+ }
444
+ }
445
+ if (IS_MAC) {
446
+ try {
447
+ const script = path.join(MAC_SCRIPTS_DIR, 'get-focused-element.jxa');
448
+ const { stdout } = await execFileAsync('osascript', ['-l', 'JavaScript', script], {
449
+ timeout: MAC_SCRIPT_TIMEOUT,
450
+ });
451
+ const result = JSON.parse(stdout.trim());
452
+ if (!result)
453
+ return null;
454
+ return {
455
+ name: result.name ?? '',
456
+ automationId: result.automationId ?? '',
457
+ controlType: result.controlType ?? '',
458
+ className: result.className ?? '',
459
+ processId: result.processId ?? 0,
460
+ isEnabled: result.isEnabled ?? true,
461
+ bounds: result.bounds ?? { x: 0, y: 0, width: 0, height: 0 },
462
+ value: result.value ?? '',
463
+ };
464
+ }
465
+ catch {
466
+ return null;
467
+ }
468
+ }
469
+ // Linux: not yet implemented (AT-SPI planned)
470
+ return null;
471
+ }
472
+ // ── Clipboard ─────────────────────────────────────────────────────────────
473
+ /**
474
+ * Read text from the OS clipboard.
475
+ * Returns empty string on error, timeout, or non-text content.
476
+ */
477
+ async readClipboard() {
478
+ try {
479
+ if (IS_WIN) {
480
+ const { stdout } = await execFileAsync('powershell.exe', [
481
+ '-NoProfile', '-Command', 'Get-Clipboard',
482
+ ], { timeout: 2000 });
483
+ return stdout?.trim() ?? '';
484
+ }
485
+ if (IS_MAC) {
486
+ const { stdout } = await execFileAsync('pbpaste', [], { timeout: 2000 });
487
+ return stdout?.trim() ?? '';
488
+ }
489
+ if (IS_LINUX) {
490
+ // Linux clipboard: tries wl-paste (Wayland), xclip, xsel (X11) in order
491
+ const { stdout } = await execFileAsync('sh', ['-lc', 'if command -v wl-paste >/dev/null 2>&1; then wl-paste --no-newline; elif command -v xclip >/dev/null 2>&1; then xclip -selection clipboard -o; elif command -v xsel >/dev/null 2>&1; then xsel --clipboard --output; fi'], { timeout: 2000 });
492
+ return stdout?.trim() ?? '';
493
+ }
494
+ return '';
495
+ }
496
+ catch {
497
+ return '';
498
+ }
499
+ }
500
+ /**
501
+ * Write text to the OS clipboard.
502
+ * Silently fails on error or timeout.
503
+ */
504
+ async writeClipboard(text) {
505
+ try {
506
+ if (IS_WIN) {
507
+ // Use -EncodedCommand with Base64-encoded UTF-16LE to safely handle
508
+ // all characters (quotes, newlines, special chars) without escaping issues.
509
+ const utf16 = Buffer.from(`Set-Clipboard -Value '${text.replace(/'/g, "''")}'`, 'utf16le');
510
+ await execFileAsync('powershell.exe', [
511
+ '-NoProfile', '-EncodedCommand', utf16.toString('base64'),
512
+ ], { timeout: 2000 });
513
+ }
514
+ else if (IS_MAC) {
515
+ // macOS: pipe to pbcopy via shell
516
+ await new Promise((resolve, reject) => {
517
+ const proc = (0, child_process_1.execFile)('pbcopy', [], { timeout: 2000 }, (err) => {
518
+ if (err)
519
+ reject(err);
520
+ else
521
+ resolve();
522
+ });
523
+ proc.stdin?.write(text);
524
+ proc.stdin?.end();
525
+ });
526
+ }
527
+ else if (IS_LINUX) {
528
+ // Linux clipboard: tries wl-copy (Wayland), xclip, xsel (X11) in order
529
+ await new Promise((resolve, reject) => {
530
+ const proc = (0, child_process_1.execFile)('sh', ['-lc', 'if command -v wl-copy >/dev/null 2>&1; then wl-copy; elif command -v xclip >/dev/null 2>&1; then xclip -selection clipboard; elif command -v xsel >/dev/null 2>&1; then xsel --clipboard --input; else exit 1; fi'], { timeout: 2000 }, (err) => {
531
+ if (err)
532
+ reject(err);
533
+ else
534
+ resolve();
535
+ });
536
+ proc.stdin?.write(text);
537
+ proc.stdin?.end();
538
+ });
539
+ }
540
+ }
541
+ catch {
542
+ // Silently fail — clipboard write is best-effort
543
+ }
544
+ }
545
+ /**
546
+ * Get a text summary of the UI for the LLM.
547
+ * Always reads fresh on Windows (PSRunner is cheap); respects 2s cache otherwise.
548
+ */
549
+ async getScreenContext(focusedProcessId) {
550
+ if (this.screenContextCache &&
551
+ Date.now() - this.screenContextCache.timestamp < this.SCREEN_CONTEXT_CACHE_TTL) {
552
+ return this.screenContextCache.context;
553
+ }
554
+ let context = '';
555
+ try {
556
+ if (IS_WIN) {
557
+ const combined = await this.winCmd({
558
+ cmd: 'get-screen-context',
559
+ maxDepth: MAX_DEPTH,
560
+ ...(focusedProcessId && { focusedProcessId }),
561
+ });
562
+ if (combined.windows?.length) {
563
+ this.windowCache = { windows: combined.windows, timestamp: Date.now() };
564
+ context += 'WINDOWS:\n';
565
+ for (const w of combined.windows) {
566
+ context += ` ${w.isMinimized ? '🔽' : '🟢'} [${w.processName}] "${w.title}" pid:${w.processId}`;
567
+ if (!w.isMinimized)
568
+ context += ` at (${w.bounds.x},${w.bounds.y}) ${w.bounds.width}x${w.bounds.height}`;
569
+ context += '\n';
570
+ }
571
+ }
572
+ if (combined.uiTree) {
573
+ context += '\nFOCUSED WINDOW UI TREE:\n';
574
+ context += this.formatTree(Array.isArray(combined.uiTree) ? combined.uiTree : [combined.uiTree], ' ');
575
+ }
576
+ }
577
+ else if (IS_MAC) {
578
+ // macOS — separate script calls
579
+ const windows = await this.getWindows();
580
+ context += 'WINDOWS:\n';
581
+ for (const w of windows) {
582
+ context += ` ${w.isMinimized ? '🔽' : '🟢'} [${w.processName}] "${w.title}" pid:${w.processId}`;
583
+ if (!w.isMinimized)
584
+ context += ` at (${w.bounds.x},${w.bounds.y}) ${w.bounds.width}x${w.bounds.height}`;
585
+ context += '\n';
586
+ }
587
+ if (focusedProcessId) {
588
+ try {
589
+ const result = await this.runMacScript('get-screen-context.jxa', [
590
+ '-FocusedProcessId', String(focusedProcessId),
591
+ '-MaxDepth', String(MAX_DEPTH),
592
+ ]);
593
+ const tree = result?.uiTree ? [result.uiTree] : [];
594
+ context += `\nFOCUSED WINDOW UI TREE (pid:${focusedProcessId}):\n`;
595
+ context += this.formatTree(tree, ' ');
596
+ }
597
+ catch { /* skip */ }
598
+ }
599
+ }
600
+ else {
601
+ context += '(Accessibility unavailable on Linux — browser CDP and OCR remain available)';
602
+ }
603
+ }
604
+ catch (err) {
605
+ context += `\n[A11y tree unavailable: ${err}]\n`;
606
+ }
607
+ // Always append focused element — even when the tree query failed, focus info is critical
608
+ if (IS_WIN) {
609
+ try {
610
+ const focused = await this.getFocusedElement();
611
+ if (focused) {
612
+ context += '\nFOCUSED ELEMENT:\n';
613
+ context += ` [${focused.controlType}] "${focused.name}" id:${focused.automationId} @${focused.bounds.x},${focused.bounds.y}`;
614
+ if (!focused.isEnabled)
615
+ context += ' DISABLED';
616
+ if (focused.value)
617
+ context += ` value="${focused.value.substring(0, 100)}"`;
618
+ context += ` pid:${focused.processId}\n`;
619
+ }
620
+ }
621
+ catch { /* non-critical */ }
622
+ }
623
+ if (!context.trim()) {
624
+ return '(Accessibility unavailable)';
625
+ }
626
+ this.screenContextCache = { context, timestamp: Date.now() };
627
+ return context;
628
+ }
629
+ static INTERACTIVE_TYPES = new Set([
630
+ 'ControlType.Button', 'ControlType.Edit', 'ControlType.ComboBox',
631
+ 'ControlType.CheckBox', 'ControlType.RadioButton', 'ControlType.Hyperlink',
632
+ 'ControlType.MenuItem', 'ControlType.Menu', 'ControlType.Tab',
633
+ 'ControlType.TabItem', 'ControlType.ListItem', 'ControlType.TreeItem',
634
+ 'ControlType.Slider', 'ControlType.ScrollBar', 'ControlType.ToolBar',
635
+ 'ControlType.Document', 'ControlType.DataItem',
636
+ 'ControlType.Pane', 'ControlType.Custom', 'ControlType.Group',
637
+ 'ControlType.Text',
638
+ ]);
639
+ static MAX_CONTEXT_CHARS = 12000; // raised for deep Electron/WebView2/Office trees — LLM needs full field visibility
640
+ formatTree(elements, indent) {
641
+ let result = '';
642
+ for (const el of elements) {
643
+ const isInteractive = AccessibilityBridge.INTERACTIVE_TYPES.has(el.controlType);
644
+ const hasName = !!(el.name?.trim());
645
+ const hasChildren = el.children && el.children.length > 0;
646
+ // Show element if interactive or named; skip unnamed non-interactive LEAVES only
647
+ if (isInteractive || hasName) {
648
+ const name = el.name ? `"${el.name}"` : '';
649
+ const id = el.automationId ? `id:${el.automationId}` : '';
650
+ const bounds = `@${el.bounds.x},${el.bounds.y}`;
651
+ const disabled = el.isEnabled === false ? ' DISABLED' : '';
652
+ result += `${indent}[${el.controlType}] ${name} ${id} ${bounds}${disabled}\n`;
653
+ if (result.length > AccessibilityBridge.MAX_CONTEXT_CHARS) {
654
+ result += `${indent}... (truncated)\n`;
655
+ return result;
656
+ }
657
+ }
658
+ // Always recurse into children — unnamed containers (Pane/Group) in Electron apps
659
+ // often wrap the actual interactive elements several levels deep
660
+ if (hasChildren) {
661
+ result += this.formatTree(el.children, indent + ' ');
662
+ if (result.length > AccessibilityBridge.MAX_CONTEXT_CHARS)
663
+ return result;
664
+ }
665
+ }
666
+ return result;
667
+ }
668
+ }
669
+ exports.AccessibilityBridge = AccessibilityBridge;
670
+ //# sourceMappingURL=accessibility.js.map