@zenalexa/unicli 0.217.0 → 0.217.3

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 (341) hide show
  1. package/AGENTS.md +7 -6
  2. package/README.md +59 -19
  3. package/README.zh-CN.md +36 -15
  4. package/crates/unicli-atspi/Cargo.toml +47 -0
  5. package/crates/unicli-atspi/README.md +6 -0
  6. package/crates/unicli-atspi/src/errors.rs +213 -0
  7. package/crates/unicli-atspi/src/input.rs +1004 -0
  8. package/crates/unicli-atspi/src/invoke.rs +1132 -0
  9. package/crates/unicli-atspi/src/main.rs +130 -0
  10. package/crates/unicli-atspi/src/refs.rs +24 -0
  11. package/crates/unicli-atspi/src/screenshot.rs +756 -0
  12. package/crates/unicli-atspi/src/tree.rs +2319 -0
  13. package/crates/unicli-shared/Cargo.toml +13 -0
  14. package/crates/unicli-shared/src/lib.rs +77 -0
  15. package/crates/unicli-uia/Cargo.toml +29 -0
  16. package/crates/unicli-uia/README.md +6 -0
  17. package/crates/unicli-uia/src/errors.rs +179 -0
  18. package/crates/unicli-uia/src/input.rs +790 -0
  19. package/crates/unicli-uia/src/invoke.rs +977 -0
  20. package/crates/unicli-uia/src/main.rs +130 -0
  21. package/crates/unicli-uia/src/refs.rs +24 -0
  22. package/crates/unicli-uia/src/screenshot.rs +685 -0
  23. package/crates/unicli-uia/src/tree.rs +2135 -0
  24. package/dist/adapters/macos/actions.d.ts +9 -0
  25. package/dist/adapters/macos/actions.d.ts.map +1 -0
  26. package/dist/adapters/macos/actions.js +55 -0
  27. package/dist/adapters/macos/actions.js.map +1 -0
  28. package/dist/browser/bridge.d.ts +2 -0
  29. package/dist/browser/bridge.d.ts.map +1 -1
  30. package/dist/browser/bridge.js +39 -0
  31. package/dist/browser/bridge.js.map +1 -1
  32. package/dist/browser/cdp-client.d.ts +2 -0
  33. package/dist/browser/cdp-client.d.ts.map +1 -1
  34. package/dist/browser/cdp-client.js +7 -0
  35. package/dist/browser/cdp-client.js.map +1 -1
  36. package/dist/browser/page.d.ts +2 -0
  37. package/dist/browser/page.d.ts.map +1 -1
  38. package/dist/browser/page.js +35 -0
  39. package/dist/browser/page.js.map +1 -1
  40. package/dist/cli.d.ts.map +1 -1
  41. package/dist/cli.js +13 -1
  42. package/dist/cli.js.map +1 -1
  43. package/dist/commands/approvals.d.ts +3 -0
  44. package/dist/commands/approvals.d.ts.map +1 -0
  45. package/dist/commands/approvals.js +123 -0
  46. package/dist/commands/approvals.js.map +1 -0
  47. package/dist/commands/browser-operator-runtime.d.ts.map +1 -1
  48. package/dist/commands/browser-operator-runtime.js +1 -0
  49. package/dist/commands/browser-operator-runtime.js.map +1 -1
  50. package/dist/commands/browser-operator.d.ts.map +1 -1
  51. package/dist/commands/browser-operator.js +84 -12
  52. package/dist/commands/browser-operator.js.map +1 -1
  53. package/dist/commands/compute.d.ts +3 -0
  54. package/dist/commands/compute.d.ts.map +1 -0
  55. package/dist/commands/compute.js +324 -0
  56. package/dist/commands/compute.js.map +1 -0
  57. package/dist/commands/dispatch.d.ts.map +1 -1
  58. package/dist/commands/dispatch.js +10 -4
  59. package/dist/commands/dispatch.js.map +1 -1
  60. package/dist/commands/doctor-compute.d.ts +38 -0
  61. package/dist/commands/doctor-compute.d.ts.map +1 -0
  62. package/dist/commands/doctor-compute.js +376 -0
  63. package/dist/commands/doctor-compute.js.map +1 -0
  64. package/dist/commands/lint.d.ts.map +1 -1
  65. package/dist/commands/lint.js +69 -1
  66. package/dist/commands/lint.js.map +1 -1
  67. package/dist/commands/mcp.d.ts.map +1 -1
  68. package/dist/commands/mcp.js +4 -0
  69. package/dist/commands/mcp.js.map +1 -1
  70. package/dist/commands/runs.d.ts +3 -0
  71. package/dist/commands/runs.d.ts.map +1 -0
  72. package/dist/commands/runs.js +367 -0
  73. package/dist/commands/runs.js.map +1 -0
  74. package/dist/core/envelope.d.ts +8 -0
  75. package/dist/core/envelope.d.ts.map +1 -1
  76. package/dist/core/envelope.js +1 -0
  77. package/dist/core/envelope.js.map +1 -1
  78. package/dist/discovery/aliases.d.ts.map +1 -1
  79. package/dist/discovery/aliases.js +15 -0
  80. package/dist/discovery/aliases.js.map +1 -1
  81. package/dist/discovery/loader.d.ts.map +1 -1
  82. package/dist/discovery/loader.js +9 -0
  83. package/dist/discovery/loader.js.map +1 -1
  84. package/dist/discovery/macos-dynamic.d.ts +58 -0
  85. package/dist/discovery/macos-dynamic.d.ts.map +1 -0
  86. package/dist/discovery/macos-dynamic.js +429 -0
  87. package/dist/discovery/macos-dynamic.js.map +1 -0
  88. package/dist/discovery/search.d.ts.map +1 -1
  89. package/dist/discovery/search.js +152 -3
  90. package/dist/discovery/search.js.map +1 -1
  91. package/dist/electron-apps.d.ts +1 -0
  92. package/dist/electron-apps.d.ts.map +1 -1
  93. package/dist/electron-apps.js +1 -0
  94. package/dist/electron-apps.js.map +1 -1
  95. package/dist/engine/approval-store.d.ts +43 -0
  96. package/dist/engine/approval-store.d.ts.map +1 -0
  97. package/dist/engine/approval-store.js +193 -0
  98. package/dist/engine/approval-store.js.map +1 -0
  99. package/dist/engine/browser/action-evidence.d.ts +2 -0
  100. package/dist/engine/browser/action-evidence.d.ts.map +1 -1
  101. package/dist/engine/browser/action-evidence.js +35 -1
  102. package/dist/engine/browser/action-evidence.js.map +1 -1
  103. package/dist/engine/browser/evidence.d.ts +22 -0
  104. package/dist/engine/browser/evidence.d.ts.map +1 -1
  105. package/dist/engine/browser/evidence.js +72 -0
  106. package/dist/engine/browser/evidence.js.map +1 -1
  107. package/dist/engine/browser/session-lease.d.ts +53 -0
  108. package/dist/engine/browser/session-lease.d.ts.map +1 -0
  109. package/dist/engine/browser/session-lease.js +100 -0
  110. package/dist/engine/browser/session-lease.js.map +1 -0
  111. package/dist/engine/browser/session-lock.d.ts +17 -0
  112. package/dist/engine/browser/session-lock.d.ts.map +1 -0
  113. package/dist/engine/browser/session-lock.js +114 -0
  114. package/dist/engine/browser/session-lock.js.map +1 -0
  115. package/dist/engine/browser/session-runtime.d.ts +10 -0
  116. package/dist/engine/browser/session-runtime.d.ts.map +1 -0
  117. package/dist/engine/browser/session-runtime.js +87 -0
  118. package/dist/engine/browser/session-runtime.js.map +1 -0
  119. package/dist/engine/capability-policy.d.ts +13 -2
  120. package/dist/engine/capability-policy.d.ts.map +1 -1
  121. package/dist/engine/capability-policy.js +113 -3
  122. package/dist/engine/capability-policy.js.map +1 -1
  123. package/dist/engine/executor.d.ts +8 -3
  124. package/dist/engine/executor.d.ts.map +1 -1
  125. package/dist/engine/executor.js +9 -2
  126. package/dist/engine/executor.js.map +1 -1
  127. package/dist/engine/kernel/execute.d.ts +1 -0
  128. package/dist/engine/kernel/execute.d.ts.map +1 -1
  129. package/dist/engine/kernel/execute.js +125 -3
  130. package/dist/engine/kernel/execute.js.map +1 -1
  131. package/dist/engine/kernel/types.d.ts +13 -0
  132. package/dist/engine/kernel/types.d.ts.map +1 -1
  133. package/dist/engine/operation-policy.d.ts +9 -1
  134. package/dist/engine/operation-policy.d.ts.map +1 -1
  135. package/dist/engine/operation-policy.js +6 -2
  136. package/dist/engine/operation-policy.js.map +1 -1
  137. package/dist/engine/permission-rules.d.ts +43 -0
  138. package/dist/engine/permission-rules.d.ts.map +1 -0
  139. package/dist/engine/permission-rules.js +401 -0
  140. package/dist/engine/permission-rules.js.map +1 -0
  141. package/dist/engine/permission-runtime.d.ts +11 -0
  142. package/dist/engine/permission-runtime.d.ts.map +1 -0
  143. package/dist/engine/permission-runtime.js +21 -0
  144. package/dist/engine/permission-runtime.js.map +1 -0
  145. package/dist/engine/repair/remedies.d.ts +4 -0
  146. package/dist/engine/repair/remedies.d.ts.map +1 -0
  147. package/dist/engine/repair/remedies.js +169 -0
  148. package/dist/engine/repair/remedies.js.map +1 -0
  149. package/dist/engine/runtime-resource-guard.d.ts +23 -0
  150. package/dist/engine/runtime-resource-guard.d.ts.map +1 -0
  151. package/dist/engine/runtime-resource-guard.js +85 -0
  152. package/dist/engine/runtime-resource-guard.js.map +1 -0
  153. package/dist/engine/session/args.d.ts +3 -0
  154. package/dist/engine/session/args.d.ts.map +1 -0
  155. package/dist/engine/session/args.js +17 -0
  156. package/dist/engine/session/args.js.map +1 -0
  157. package/dist/engine/session/compare.d.ts +92 -0
  158. package/dist/engine/session/compare.d.ts.map +1 -0
  159. package/dist/engine/session/compare.js +324 -0
  160. package/dist/engine/session/compare.js.map +1 -0
  161. package/dist/engine/session/environment.d.ts +4 -0
  162. package/dist/engine/session/environment.d.ts.map +1 -0
  163. package/dist/engine/session/environment.js +25 -0
  164. package/dist/engine/session/environment.js.map +1 -0
  165. package/dist/engine/session/events.d.ts +2 -0
  166. package/dist/engine/session/events.d.ts.map +1 -1
  167. package/dist/engine/session/events.js +12 -0
  168. package/dist/engine/session/events.js.map +1 -1
  169. package/dist/engine/session/query.d.ts +47 -0
  170. package/dist/engine/session/query.d.ts.map +1 -0
  171. package/dist/engine/session/query.js +299 -0
  172. package/dist/engine/session/query.js.map +1 -0
  173. package/dist/engine/session/replay.d.ts +35 -0
  174. package/dist/engine/session/replay.d.ts.map +1 -0
  175. package/dist/engine/session/replay.js +144 -0
  176. package/dist/engine/session/replay.js.map +1 -0
  177. package/dist/engine/session/run-loop.d.ts.map +1 -1
  178. package/dist/engine/session/run-loop.js +62 -23
  179. package/dist/engine/session/run-loop.js.map +1 -1
  180. package/dist/engine/session/store.d.ts +7 -0
  181. package/dist/engine/session/store.d.ts.map +1 -1
  182. package/dist/engine/session/store.js +131 -1
  183. package/dist/engine/session/store.js.map +1 -1
  184. package/dist/engine/session/types.d.ts +3 -1
  185. package/dist/engine/session/types.d.ts.map +1 -1
  186. package/dist/engine/steps/compute.d.ts +41 -0
  187. package/dist/engine/steps/compute.d.ts.map +1 -0
  188. package/dist/engine/steps/compute.js +55 -0
  189. package/dist/engine/steps/compute.js.map +1 -0
  190. package/dist/engine/steps/desktop-ax.d.ts +8 -0
  191. package/dist/engine/steps/desktop-ax.d.ts.map +1 -1
  192. package/dist/engine/steps/desktop-ax.js +16 -0
  193. package/dist/engine/steps/desktop-ax.js.map +1 -1
  194. package/dist/engine/steps/desktop-sidecar.d.ts +49 -0
  195. package/dist/engine/steps/desktop-sidecar.d.ts.map +1 -0
  196. package/dist/engine/steps/desktop-sidecar.js +50 -0
  197. package/dist/engine/steps/desktop-sidecar.js.map +1 -0
  198. package/dist/engine/steps/download.d.ts +1 -1
  199. package/dist/engine/steps/download.d.ts.map +1 -1
  200. package/dist/engine/steps/download.js +24 -2
  201. package/dist/engine/steps/download.js.map +1 -1
  202. package/dist/engine/steps/exec.d.ts +1 -1
  203. package/dist/engine/steps/exec.d.ts.map +1 -1
  204. package/dist/engine/steps/exec.js +23 -7
  205. package/dist/engine/steps/exec.js.map +1 -1
  206. package/dist/engine/steps/fetch-text.d.ts +1 -1
  207. package/dist/engine/steps/fetch-text.d.ts.map +1 -1
  208. package/dist/engine/steps/fetch-text.js +12 -4
  209. package/dist/engine/steps/fetch-text.js.map +1 -1
  210. package/dist/engine/steps/fetch.d.ts +2 -1
  211. package/dist/engine/steps/fetch.d.ts.map +1 -1
  212. package/dist/engine/steps/fetch.js +29 -6
  213. package/dist/engine/steps/fetch.js.map +1 -1
  214. package/dist/engine/steps/index.d.ts +2 -0
  215. package/dist/engine/steps/index.d.ts.map +1 -1
  216. package/dist/engine/steps/index.js +2 -0
  217. package/dist/engine/steps/index.js.map +1 -1
  218. package/dist/engine/steps/navigate.d.ts +1 -1
  219. package/dist/engine/steps/navigate.d.ts.map +1 -1
  220. package/dist/engine/steps/navigate.js +29 -2
  221. package/dist/engine/steps/navigate.js.map +1 -1
  222. package/dist/fast-path.d.ts.map +1 -1
  223. package/dist/fast-path.js +96 -12
  224. package/dist/fast-path.js.map +1 -1
  225. package/dist/manifest-compact.txt +2 -2
  226. package/dist/manifest-search.json +1 -1
  227. package/dist/manifest.json +1024 -1
  228. package/dist/mcp/handler.d.ts +2 -2
  229. package/dist/mcp/handler.d.ts.map +1 -1
  230. package/dist/mcp/handler.js +59 -5
  231. package/dist/mcp/handler.js.map +1 -1
  232. package/dist/mcp/profiles/computer-use.d.ts +4 -0
  233. package/dist/mcp/profiles/computer-use.d.ts.map +1 -0
  234. package/dist/mcp/profiles/computer-use.js +305 -0
  235. package/dist/mcp/profiles/computer-use.js.map +1 -0
  236. package/dist/mcp/server.d.ts.map +1 -1
  237. package/dist/mcp/server.js +30 -6
  238. package/dist/mcp/server.js.map +1 -1
  239. package/dist/mcp/tools.d.ts +9 -0
  240. package/dist/mcp/tools.d.ts.map +1 -1
  241. package/dist/mcp/tools.js +20 -0
  242. package/dist/mcp/tools.js.map +1 -1
  243. package/dist/output/envelope.d.ts +6 -0
  244. package/dist/output/envelope.d.ts.map +1 -1
  245. package/dist/output/envelope.js.map +1 -1
  246. package/dist/output/error-map.d.ts.map +1 -1
  247. package/dist/output/error-map.js +4 -0
  248. package/dist/output/error-map.js.map +1 -1
  249. package/dist/registry.d.ts +1 -0
  250. package/dist/registry.d.ts.map +1 -1
  251. package/dist/registry.js +5 -0
  252. package/dist/registry.js.map +1 -1
  253. package/dist/transport/adapters/cdp-browser.d.ts +38 -2
  254. package/dist/transport/adapters/cdp-browser.d.ts.map +1 -1
  255. package/dist/transport/adapters/cdp-browser.js +349 -22
  256. package/dist/transport/adapters/cdp-browser.js.map +1 -1
  257. package/dist/transport/adapters/desktop-atspi.d.ts +23 -17
  258. package/dist/transport/adapters/desktop-atspi.d.ts.map +1 -1
  259. package/dist/transport/adapters/desktop-atspi.js +143 -32
  260. package/dist/transport/adapters/desktop-atspi.js.map +1 -1
  261. package/dist/transport/adapters/desktop-ax-helpers.d.ts +24 -0
  262. package/dist/transport/adapters/desktop-ax-helpers.d.ts.map +1 -0
  263. package/dist/transport/adapters/desktop-ax-helpers.js +190 -0
  264. package/dist/transport/adapters/desktop-ax-helpers.js.map +1 -0
  265. package/dist/transport/adapters/desktop-ax-swift.d.ts +13 -0
  266. package/dist/transport/adapters/desktop-ax-swift.d.ts.map +1 -1
  267. package/dist/transport/adapters/desktop-ax-swift.js +176 -2
  268. package/dist/transport/adapters/desktop-ax-swift.js.map +1 -1
  269. package/dist/transport/adapters/desktop-ax.d.ts +11 -2
  270. package/dist/transport/adapters/desktop-ax.d.ts.map +1 -1
  271. package/dist/transport/adapters/desktop-ax.js +131 -16
  272. package/dist/transport/adapters/desktop-ax.js.map +1 -1
  273. package/dist/transport/adapters/desktop-sidecar-errors.d.ts +3 -0
  274. package/dist/transport/adapters/desktop-sidecar-errors.d.ts.map +1 -0
  275. package/dist/transport/adapters/desktop-sidecar-errors.js +34 -0
  276. package/dist/transport/adapters/desktop-sidecar-errors.js.map +1 -0
  277. package/dist/transport/adapters/desktop-sidecar-snapshot.d.ts +10 -0
  278. package/dist/transport/adapters/desktop-sidecar-snapshot.d.ts.map +1 -0
  279. package/dist/transport/adapters/desktop-sidecar-snapshot.js +89 -0
  280. package/dist/transport/adapters/desktop-sidecar-snapshot.js.map +1 -0
  281. package/dist/transport/adapters/desktop-uia.d.ts +23 -17
  282. package/dist/transport/adapters/desktop-uia.d.ts.map +1 -1
  283. package/dist/transport/adapters/desktop-uia.js +142 -32
  284. package/dist/transport/adapters/desktop-uia.js.map +1 -1
  285. package/dist/transport/adapters/subprocess.d.ts +7 -0
  286. package/dist/transport/adapters/subprocess.d.ts.map +1 -1
  287. package/dist/transport/adapters/subprocess.js +64 -0
  288. package/dist/transport/adapters/subprocess.js.map +1 -1
  289. package/dist/transport/bus.d.ts +2 -0
  290. package/dist/transport/bus.d.ts.map +1 -1
  291. package/dist/transport/bus.js +7 -11
  292. package/dist/transport/bus.js.map +1 -1
  293. package/dist/transport/capability.d.ts.map +1 -1
  294. package/dist/transport/capability.js +123 -98
  295. package/dist/transport/capability.js.map +1 -1
  296. package/dist/transport/cascade.d.ts +5 -0
  297. package/dist/transport/cascade.d.ts.map +1 -0
  298. package/dist/transport/cascade.js +550 -0
  299. package/dist/transport/cascade.js.map +1 -0
  300. package/dist/transport/cdp-session.d.ts +11 -0
  301. package/dist/transport/cdp-session.d.ts.map +1 -0
  302. package/dist/transport/cdp-session.js +52 -0
  303. package/dist/transport/cdp-session.js.map +1 -0
  304. package/dist/transport/refs.d.ts +51 -0
  305. package/dist/transport/refs.d.ts.map +1 -0
  306. package/dist/transport/refs.js +135 -0
  307. package/dist/transport/refs.js.map +1 -0
  308. package/dist/transport/sidecar-binary.d.ts +18 -0
  309. package/dist/transport/sidecar-binary.d.ts.map +1 -0
  310. package/dist/transport/sidecar-binary.js +55 -0
  311. package/dist/transport/sidecar-binary.js.map +1 -0
  312. package/dist/transport/sidecar.d.ts +35 -0
  313. package/dist/transport/sidecar.d.ts.map +1 -0
  314. package/dist/transport/sidecar.js +134 -0
  315. package/dist/transport/sidecar.js.map +1 -0
  316. package/dist/transport/snapshot-encoder.d.ts +34 -0
  317. package/dist/transport/snapshot-encoder.d.ts.map +1 -0
  318. package/dist/transport/snapshot-encoder.js +139 -0
  319. package/dist/transport/snapshot-encoder.js.map +1 -0
  320. package/dist/transport/types.d.ts +6 -1
  321. package/dist/transport/types.d.ts.map +1 -1
  322. package/dist/types.d.ts +2 -0
  323. package/dist/types.d.ts.map +1 -1
  324. package/dist/types.js.map +1 -1
  325. package/docs/mcp/clients/claude-code.md +29 -0
  326. package/docs/mcp/clients/claude-desktop.md +47 -0
  327. package/docs/mcp/clients/codex.md +29 -0
  328. package/docs/mcp/clients/cursor.md +38 -0
  329. package/docs/mcp/clients/gemini-cli.md +38 -0
  330. package/docs/operate/compute.md +172 -0
  331. package/docs/operate/electron.md +87 -0
  332. package/docs/operate/focus-behavior.md +40 -0
  333. package/docs/operate/troubleshooting.md +379 -0
  334. package/package.json +29 -4
  335. package/src/adapters/juejin/hot.test.ts +25 -0
  336. package/src/adapters/juejin/hot.yaml +52 -0
  337. package/src/adapters/juejin/search.test.ts +27 -0
  338. package/src/adapters/juejin/search.yaml +58 -0
  339. package/src/adapters/leetcode/discuss-search.test.ts +29 -0
  340. package/src/adapters/leetcode/discuss-search.yaml +56 -0
  341. package/src/adapters/macos/actions.ts +63 -0
package/AGENTS.md CHANGED
@@ -6,11 +6,12 @@
6
6
  Agent routing: unicli agents matrix && unicli agents recommend <agent>
7
7
  Run: unicli <site> <command> [args]
8
8
  Output is structured Markdown by default (non-TTY + agent UA auto-detected). Use -f json for JSON. Errors are structured envelopes to stderr.
9
+ Style: load skills/talk-normal/SKILL.md before writing docs or user-facing text; keep prose direct and avoid filler.
9
10
  You can fix broken adapters yourself — read the Self-Repair section. -->
10
11
 
11
12
  <!-- BEGIN COUNTS -->
12
13
 
13
- > <!-- STATS:site_count -->235<!-- /STATS --> sites, <!-- STATS:command_count -->1448<!-- /STATS --> commands, <!-- STATS:pipeline_step_count -->59<!-- /STATS --> pipeline steps, BM25 bilingual search. `npm install -g @zenalexa/unicli`
14
+ > <!-- STATS:site_count -->237<!-- /STATS --> sites, <!-- STATS:command_count -->1453<!-- /STATS --> commands, <!-- STATS:pipeline_step_count -->101<!-- /STATS --> pipeline steps, BM25 bilingual search. `npm install -g @zenalexa/unicli`
14
15
 
15
16
  <!-- END COUNTS -->
16
17
 
@@ -39,7 +40,7 @@ npm install -g @zenalexa/unicli
39
40
 
40
41
  **Chinese**: zhihu (24), xiaohongshu (22), bilibili (20), douyin (13), douban (12), v2ex (12), linux-do (11), jike (10), +31 more (`unicli list`)
41
42
 
42
- **International**: twitter (38), instagram (26), reddit (20), tiktok (16), youtube (16), nowcoder (16), discord-app (15), lesswrong (15), +48 more (`unicli list`)
43
+ **International**: twitter (38), instagram (26), reddit (20), tiktok (16), youtube (16), nowcoder (16), discord-app (15), lesswrong (15), +50 more (`unicli list`)
43
44
 
44
45
  **AI / ML**: antigravity (16), chatwise (16), chatgpt (15), notebooklm (15), doubao-app (13), doubao (9), doubao-web (9), perplexity (8), +13 more (`unicli list`)
45
46
 
@@ -51,9 +52,9 @@ npm install -g @zenalexa/unicli
51
52
 
52
53
  **Reference**: spotify (23), netease-music (17), linear (10), imdb (7), bitwarden (7), todoist (7), wikipedia (5), xiaoyuzhou (5), +11 more (`unicli list`)
53
54
 
54
- ### macOS (58 cmds)
55
+ ### macOS (60 cmds)
55
56
 
56
- active-app, apps, apps-list, battery, bluetooth, brightness, caffeinate, calendar-create, calendar-list, calendar-today, clipboard, contacts-search, … (`unicli list --site macos`)
57
+ active-app, app-actions, apps, apps-list, automation-smoke, battery, bluetooth, brightness, caffeinate, calendar-create, calendar-list, calendar-today, … (`unicli list --site macos`)
57
58
 
58
59
  ### Desktop (28 apps)
59
60
 
@@ -185,7 +186,7 @@ Full reference: [`docs/ADAPTER-FORMAT.md`](docs/ADAPTER-FORMAT.md).
185
186
  ```bash
186
187
  npx @zenalexa/unicli mcp serve # stdio (4 meta-tools)
187
188
  npx @zenalexa/unicli mcp serve --transport streamable --port 19826
188
- npx @zenalexa/unicli mcp serve --transport sse --port 19826 # SSE for remote
189
+ npx @zenalexa/unicli mcp serve --transport sse --port 19826 # legacy alias for streamable
189
190
  npx @zenalexa/unicli mcp serve --auth # OAuth 2.1 PKCE
190
191
  ```
191
192
 
@@ -212,4 +213,4 @@ unicli lark-cli calendar +agenda # Direct passthrough
212
213
 
213
214
  ## Version
214
215
 
215
- 0.217.0 — Apollo · Lovell
216
+ 0.217.3 — Apollo · Shepard
package/README.md CHANGED
@@ -28,7 +28,7 @@
28
28
  </p>
29
29
 
30
30
  <p align="center">
31
- <sub><!-- STATS:site_count -->235<!-- /STATS --> sites · <!-- STATS:command_count -->1448<!-- /STATS --> commands · <!-- STATS:pipeline_step_count -->59<!-- /STATS --> pipeline steps · <!-- STATS:test_count -->7471<!-- /STATS --> tests</sub>
31
+ <sub><!-- STATS:site_count -->237<!-- /STATS --> sites · <!-- STATS:command_count -->1453<!-- /STATS --> commands · <!-- STATS:pipeline_step_count -->101<!-- /STATS --> pipeline steps · <!-- STATS:test_count -->7747<!-- /STATS --> tests</sub>
32
32
  </p>
33
33
 
34
34
  <p align="center">
@@ -131,26 +131,28 @@ Prefer native CLI / JSON stream / MCP for agent runtimes. Use ACP as an editor c
131
131
 
132
132
  Uni-CLI sits under agent applications and turns software surfaces into commands that agents can discover, execute, record, and repair.
133
133
 
134
- | Surface | What you get |
135
- | ------------------ | -------------------------------------------------------------------------------------------------- |
136
- | Websites and APIs | Declarative adapters for public, cookie, header, and browser-intercept workflows |
137
- | Browser automation | CDP steps for navigate, click, type, intercept, snapshot, extract, wait, and related browser work |
138
- | Desktop and macOS | System commands, app adapters, screenshots, clipboard, calendar, brightness, and local tools |
139
- | External CLIs | 58 registered pass-through bridges with install/status discovery |
140
- | Agent backends | Route matrix for native CLI, JSON stream, MCP, ACP, HTTP API, OpenAI-compatible, and bridge routes |
141
- | Operation policy | `open`, `confirm`, and `locked` profiles with effect, risk, approval, and capability scope |
142
- | Evidence | Optional run traces plus browser pre/post evidence, movement detection, stale-ref details |
143
- | Output | v2 `AgentEnvelope` in Markdown, JSON, YAML, CSV, or compact format |
144
- | Repair | Structured errors with `adapter_path`, failing `step`, retryability, suggestions, and alternatives |
134
+ | Surface | What you get |
135
+ | ------------------ | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
136
+ | Websites and APIs | Declarative adapters for public, cookie, header, and browser-intercept workflows |
137
+ | Browser automation | CDP steps for navigate, click, type, intercept, snapshot, extract, wait, and related browser work |
138
+ | Desktop and macOS | System commands, app adapters, real-time Shortcuts/App Intent discovery, screenshots, clipboard, calendar, brightness, and local tools |
139
+ | External CLIs | 58 registered pass-through bridges with install/status discovery |
140
+ | Agent backends | Route matrix for native CLI, JSON stream, MCP, ACP, HTTP API, OpenAI-compatible, and bridge routes |
141
+ | Operation policy | `open`, `confirm`, and `locked` profiles with effect/risk scopes, local deny rules, `--yes`, and persisted approval memory |
142
+ | Evidence | Run traces with environment snapshots, probe/replay/compare scores, structured gate results, browser session leases with tab/auth posture, render-aware evidence, movement checks, and stale-ref details |
143
+ | Output | v2 `AgentEnvelope` in Markdown, JSON, YAML, CSV, or compact format |
144
+ | Repair | Structured errors with `adapter_path`, failing `step`, retryability, suggestions, and alternatives |
145
145
 
146
146
  ## For Agents
147
147
 
148
148
  Use search first, then run the smallest matching command.
149
149
 
150
150
  ```bash
151
- unicli search "twitter trending" --limit 5
152
- unicli twitter search "coding agents" -f json
153
- unicli repair twitter search
151
+ unicli search "connect slack messages" --limit 5
152
+ unicli slack search "deploy incident" -f json
153
+ unicli macos app-actions --app WhatsApp -f json
154
+ unicli macos automation-smoke -f json
155
+ unicli repair slack search
154
156
  ```
155
157
 
156
158
  Output defaults to structured Markdown for non-TTY and agent-user-agent runs. Force a machine format when you need one:
@@ -159,6 +161,16 @@ Output defaults to structured Markdown for non-TTY and agent-user-agent runs. Fo
159
161
  UNICLI_OUTPUT=json unicli reddit hot --limit 10
160
162
  unicli hackernews top --limit 5 -f yaml
161
163
  unicli --record --permission-profile confirm twitter search "coding agents" -f json
164
+ unicli runs list -f json
165
+ unicli runs show <run_id> -f json
166
+ unicli runs probe <run_id> -f json
167
+ unicli runs replay <run_id> --permission-profile confirm --yes --min-score 1 --min-context-score 1 --min-overall-score 1 -f json
168
+ unicli runs compare <run_id> <replay_run_id> -f json
169
+ unicli runs compare <run_id> <replay_run_id> --min-score 1 --min-context-score 1 --min-overall-score 1 -f json
170
+ unicli --permission-profile locked --yes --remember-approval word set-font "Inter"
171
+ unicli approvals list -f json
172
+ unicli approvals revoke <approval_key> -f json
173
+ unicli browser evidence --render-aware --expect-domain example.com -f json
162
174
  ```
163
175
 
164
176
  Protocol entry points:
@@ -173,9 +185,25 @@ unicli agents matrix
173
185
 
174
186
  ACP is supported for editors and bridge tooling. The primary runtime path stays native CLI, JSON stream, or MCP when those routes are available.
175
187
 
188
+ ## Local Computer Control
189
+
190
+ `unicli compute` controls installed apps through native accessibility, Electron CDP, and visual fallback transports.
191
+
192
+ ```bash
193
+ unicli compute apps
194
+ unicli compute snapshot --app Calculator --format compact
195
+ unicli compute find --role button --name 5 --first
196
+ unicli compute find --role input --text 8 --first
197
+ unicli compute click @e7
198
+ unicli doctor compute --json
199
+ npx @zenalexa/unicli mcp serve --profile computer-use
200
+ ```
201
+
202
+ Start with [Compute](docs/operate/compute.md), [Electron App Control](docs/operate/electron.md), and [Compute Troubleshooting](docs/operate/troubleshooting.md).
203
+
176
204
  ## Coverage
177
205
 
178
- The catalog is intentionally broad, but the important point is not the count. The important point is that every command is discoverable, typed, and repairable.
206
+ The catalog is intentionally broad. Every command is discoverable, typed, and repairable.
179
207
 
180
208
  | Area | Examples |
181
209
  | ----------------------- | ----------------------------------------------------------------------------------------------------------- |
@@ -293,12 +321,24 @@ Docs:
293
321
  - Browser adapters require a reachable Chrome/CDP session.
294
322
  - Permission profiles are user-selected runtime policy. The default is `open`;
295
323
  stricter `confirm` and `locked` profiles require `--yes` or `UNICLI_APPROVE=1`
296
- for blocked operations.
324
+ for blocked operations. Add `--remember-approval` with `--yes` to store the
325
+ same command capability and resource scope under `~/.unicli/approvals.jsonl`.
326
+ Resource scope covers stable metadata such as domain, account surface, app,
327
+ process family, and path argument slots. Use
328
+ `unicli approvals list`, `revoke`, and `clear` to inspect or remove remembered
329
+ scopes. The file stores scope metadata; runtime args stay out of approval
330
+ memory.
331
+ - Local deny rules live at `~/.unicli/permission-rules.json`, or at
332
+ `UNICLI_PERMISSION_RULES_PATH`. They match site, command, effect, capability
333
+ dimensions, and resource metadata, then block before `--yes` and remembered
334
+ approvals. Runtime guards also check fetched domains, browser navigation
335
+ targets, download and output paths, and subprocess executables before the
336
+ request, write, or process spawn happens.
297
337
  - Run recording is opt-in. Use `--record` or `UNICLI_RECORD_RUN=1` when you need
298
338
  append-only evidence under `~/.unicli/runs`.
299
339
  - CUA routes require a configured real backend. Declared-but-unavailable providers fail closed with structured errors.
300
340
  - User adapters and repairs live in `~/.unicli/adapters/`; committed adapters remain the package baseline.
301
- - If a site blocks automation or changes a private API, the correct behavior is a clear failure envelope, not a fabricated success.
341
+ - If a site blocks automation or changes a private API, Uni-CLI returns a clear failure envelope.
302
342
 
303
343
  ## Development
304
344
 
@@ -314,5 +354,5 @@ npm run verify
314
354
  [Apache-2.0](./LICENSE)
315
355
 
316
356
  <p align="center">
317
- <sub>v0.217.0 — Apollo · Lovell</sub>
357
+ <sub>v0.217.3 — Apollo · Shepard</sub>
318
358
  </p>
package/README.zh-CN.md CHANGED
@@ -28,7 +28,7 @@
28
28
  </p>
29
29
 
30
30
  <p align="center">
31
- <sub><!-- STATS:site_count -->235<!-- /STATS --> 个站点 · <!-- STATS:command_count -->1448<!-- /STATS --> 条命令 · <!-- STATS:pipeline_step_count -->59<!-- /STATS --> 个 pipeline step · <!-- STATS:test_count -->7471<!-- /STATS --> 个测试</sub>
31
+ <sub><!-- STATS:site_count -->237<!-- /STATS --> 个站点 · <!-- STATS:command_count -->1453<!-- /STATS --> 条命令 · <!-- STATS:pipeline_step_count -->101<!-- /STATS --> 个 pipeline step · <!-- STATS:test_count -->7747<!-- /STATS --> 个测试</sub>
32
32
  </p>
33
33
 
34
34
  <p align="center">
@@ -131,17 +131,17 @@ Prefer native CLI / JSON stream / MCP for agent runtimes. Use ACP as an editor c
131
131
 
132
132
  Uni-CLI 位于 Agent 应用之下,把软件表面收敛成 Agent 能发现、能执行、能记录、能修的命令。
133
133
 
134
- | 表面 | 能力 |
135
- | ------------ | ------------------------------------------------------------------------------- |
136
- | 网站和 API | public、cookie、header、browser-intercept 等 adapter |
137
- | 浏览器自动化 | CDP 的 navigate、click、type、intercept、snapshot、extract、wait 等步骤 |
138
- | 桌面和 macOS | 系统命令、App adapter、截图、剪贴板、日历、亮度、本地工具 |
139
- | 外部 CLI | 58 个已登记的 passthrough bridge,支持安装和状态发现 |
140
- | Agent 后端 | native CLI、JSON stream、MCP、ACP、HTTP API、OpenAI-compatible、bridge 路由矩阵 |
141
- | 操作策略 | `open`、`confirm`、`locked` profile,暴露 effectrisk、approval、capability |
142
- | 执行证据 | 可选 run trace,浏览器动作的前后证据、移动检测、stale-ref 细节 |
143
- | 输出 | v2 `AgentEnvelope`,支持 Markdown、JSON、YAML、CSV、compact |
144
- | 修复 | 错误里带 `adapter_path`、失败 `step`、是否可重试、修复建议和替代命令 |
134
+ | 表面 | 能力 |
135
+ | ------------ | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
136
+ | 网站和 API | public、cookie、header、browser-intercept 等 adapter |
137
+ | 浏览器自动化 | CDP 的 navigate、click、type、intercept、snapshot、extract、wait 等步骤 |
138
+ | 桌面和 macOS | 系统命令、App adapter、实时 Shortcuts/App Intent 发现、截图、剪贴板、日历、亮度、本地工具 |
139
+ | 外部 CLI | 58 个已登记的 passthrough bridge,支持安装和状态发现 |
140
+ | Agent 后端 | native CLI、JSON stream、MCP、ACP、HTTP API、OpenAI-compatible、bridge 路由矩阵 |
141
+ | 操作策略 | `open`、`confirm`、`locked` profile,暴露 effect/risk scope、本地 deny 规则、`--yes` 和持久审批记忆 |
142
+ | 执行证据 | run trace 会记录环境快照,也能 probe/replay/compare 打分并输出结构化 gate 结果;浏览器 session lease 带 tab/auth 姿态,还支持 render-aware 证据、移动检测和 stale-ref 细节 |
143
+ | 输出 | v2 `AgentEnvelope`,支持 Markdown、JSON、YAML、CSV、compact |
144
+ | 修复 | 错误里带 `adapter_path`、失败 `step`、是否可重试、修复建议和替代命令 |
145
145
 
146
146
  ## 给 Agent 的入口
147
147
 
@@ -150,6 +150,8 @@ Uni-CLI 位于 Agent 应用之下,把软件表面收敛成 Agent 能发现、
150
150
  ```bash
151
151
  unicli search "推特热门" --limit 5
152
152
  unicli twitter search "coding agents" -f json
153
+ unicli macos app-actions --app WhatsApp -f json
154
+ unicli macos automation-smoke -f json
153
155
  unicli repair twitter search
154
156
  ```
155
157
 
@@ -159,6 +161,16 @@ unicli repair twitter search
159
161
  UNICLI_OUTPUT=json unicli reddit hot --limit 10
160
162
  unicli hackernews top --limit 5 -f yaml
161
163
  unicli --record --permission-profile confirm twitter search "coding agents" -f json
164
+ unicli runs list -f json
165
+ unicli runs show <run_id> -f json
166
+ unicli runs probe <run_id> -f json
167
+ unicli runs replay <run_id> --permission-profile confirm --yes --min-score 1 --min-context-score 1 --min-overall-score 1 -f json
168
+ unicli runs compare <run_id> <replay_run_id> -f json
169
+ unicli runs compare <run_id> <replay_run_id> --min-score 1 --min-context-score 1 --min-overall-score 1 -f json
170
+ unicli --permission-profile locked --yes --remember-approval word set-font "Inter"
171
+ unicli approvals list -f json
172
+ unicli approvals revoke <approval_key> -f json
173
+ unicli browser evidence --render-aware --expect-domain example.com -f json
162
174
  ```
163
175
 
164
176
  协议入口:
@@ -175,7 +187,7 @@ ACP 作为编辑器和桥接兼容层保留。真正跑任务时,优先 native
175
187
 
176
188
  ## 覆盖范围
177
189
 
178
- 数量不是重点,重点是每条命令都能搜索、可声明、可验证、可修。
190
+ 每条命令都能搜索、可声明、可验证、可修。
179
191
 
180
192
  | 领域 | 例子 |
181
193
  | ------------- | ----------------------------------------------------------------------------------------------------------- |
@@ -293,11 +305,20 @@ columns: [title, url]
293
305
  - Browser adapter 需要可连接的 Chrome/CDP。
294
306
  - Permission profile 是用户选择的运行时策略。默认是 `open`;更严格的
295
307
  `confirm` 和 `locked` profile 会要求 `--yes` 或 `UNICLI_APPROVE=1`。
308
+ `--yes` 加 `--remember-approval` 会把同一条命令的 capability 和资源 scope
309
+ 记到 `~/.unicli/approvals.jsonl`。资源 scope 来自稳定 metadata,比如域名、
310
+ 账号面、应用、进程族和路径参数槽。用 `unicli approvals list`、`revoke`、
311
+ `clear` 查看或移除已记住的 scope。文件只存 scope metadata,原始运行参数留在审批记忆之外。
312
+ - 本地 deny 规则放在 `~/.unicli/permission-rules.json`,也可以用
313
+ `UNICLI_PERMISSION_RULES_PATH` 指定。规则按站点、命令、effect、capability
314
+ 维度和资源 metadata 匹配,优先级高于 `--yes` 和已记住的审批。运行时还会检查
315
+ fetch 域名、浏览器跳转目标、下载和输出路径、子进程可执行文件,命中后在请求、写入或
316
+ 启动进程之前停下。
296
317
  - Run recording 是显式启用能力。需要可审查证据时使用 `--record` 或
297
318
  `UNICLI_RECORD_RUN=1`,追加写入 `~/.unicli/runs`。
298
319
  - CUA 路由必须配置真实 backend。声明了但不可用的 provider 会失败关闭,并返回结构化错误。
299
320
  - 用户 adapter 和修复放在 `~/.unicli/adapters/`;包内 adapter 是基线。
300
- - 如果网站阻止自动化或私有 API 变了,正确行为是清楚失败,不是伪装成功。
321
+ - 如果网站阻止自动化或私有 API 变了,Uni-CLI 会返回清楚的失败 envelope。
301
322
 
302
323
  ## 开发
303
324
 
@@ -313,5 +334,5 @@ npm run verify
313
334
  [Apache-2.0](./LICENSE)
314
335
 
315
336
  <p align="center">
316
- <sub>v0.217.0 — Apollo · Lovell</sub>
337
+ <sub>v0.217.3 — Apollo · Shepard</sub>
317
338
  </p>
@@ -0,0 +1,47 @@
1
+ [package]
2
+ name = "unicli-atspi"
3
+ version.workspace = true
4
+ edition.workspace = true
5
+ rust-version.workspace = true
6
+ license.workspace = true
7
+ homepage.workspace = true
8
+ repository.workspace = true
9
+
10
+ [[bin]]
11
+ name = "unicli-atspi"
12
+ path = "src/main.rs"
13
+
14
+ [dependencies]
15
+ anyhow.workspace = true
16
+ clap.workspace = true
17
+ serde.workspace = true
18
+ serde_json.workspace = true
19
+ tokio.workspace = true
20
+ tracing.workspace = true
21
+ tracing-subscriber.workspace = true
22
+ unicli-shared.workspace = true
23
+
24
+ [target.'cfg(target_os = "linux")'.dependencies]
25
+ atspi = { version = "0.29.0", default-features = false, features = [
26
+ "connection",
27
+ "proxies",
28
+ "wrappers",
29
+ ] }
30
+ async-lock = "=3.4.1"
31
+ async-signal = "=0.2.13"
32
+ futures-lite = "=2.6.1"
33
+ indexmap = "=2.11.3"
34
+ proc-macro-crate = "=3.4.0"
35
+ tempfile = "=3.22.0"
36
+ toml_datetime = "=0.7.1"
37
+ toml_edit = "=0.23.5"
38
+ toml_parser = "=1.0.2"
39
+ toml_writer = "=1.0.2"
40
+ wasip2 = "=1.0.1"
41
+ winnow = "=0.7.13"
42
+ wit-bindgen = "=0.46.0"
43
+ zbus = { version = "=5.11.0", features = ["async-io"], default-features = false }
44
+ zbus_macros = "=5.11.0"
45
+ zvariant = "=5.7.0"
46
+ zvariant_derive = "=5.7.0"
47
+ zvariant_utils = "=3.2.1"
@@ -0,0 +1,6 @@
1
+ # unicli-atspi
2
+
3
+ Linux AT-SPI sidecar for Uni-CLI compute.
4
+
5
+ P0 only establishes the buildable crate. The JSON-Lines stdio protocol and
6
+ AT-SPI actions land in P4.
@@ -0,0 +1,213 @@
1
+ use serde_json::Value;
2
+ use unicli_shared::{SidecarError, SidecarResponse};
3
+
4
+ pub type HandlerResult = Result<Value, AtspiError>;
5
+
6
+ #[derive(Debug, Clone)]
7
+ pub struct AtspiError {
8
+ reason: String,
9
+ suggestion: String,
10
+ minimum_capability: Option<String>,
11
+ r#ref: Option<String>,
12
+ exit_code: u8,
13
+ }
14
+
15
+ impl AtspiError {
16
+ pub fn unavailable(reason: impl Into<String>) -> Self {
17
+ Self {
18
+ reason: reason.into(),
19
+ suggestion:
20
+ "run on Linux with the native AT-SPI backend available, or fall back to CUA".into(),
21
+ minimum_capability: None,
22
+ r#ref: None,
23
+ exit_code: 69,
24
+ }
25
+ }
26
+
27
+ pub fn invalid_input(reason: impl Into<String>) -> Self {
28
+ Self {
29
+ reason: reason.into(),
30
+ suggestion:
31
+ "pass a desktop-atspi stable top-level window ref from atspi_snapshot or atspi_find"
32
+ .into(),
33
+ minimum_capability: Some("desktop-atspi.invalid_input".into()),
34
+ r#ref: None,
35
+ exit_code: 78,
36
+ }
37
+ }
38
+
39
+ #[allow(dead_code)] // Used by native Linux AT-SPI handlers.
40
+ pub fn no_element(r#ref: impl Into<String>) -> Self {
41
+ let r#ref = r#ref.into();
42
+ Self {
43
+ reason: format!("no AT-SPI element matched {ref}"),
44
+ suggestion: "re-snapshot; the ref may be stale".into(),
45
+ minimum_capability: Some("desktop-atspi.no_element".into()),
46
+ r#ref: Some(r#ref),
47
+ exit_code: 66,
48
+ }
49
+ }
50
+
51
+ pub fn not_invokable(r#ref: impl Into<String>) -> Self {
52
+ let r#ref = r#ref.into();
53
+ Self {
54
+ reason: format!("AT-SPI element {ref} does not expose a native action"),
55
+ suggestion: "try CUA fallback, set-value, press, or focus before retrying".into(),
56
+ minimum_capability: Some("desktop-atspi.not_invokable".into()),
57
+ r#ref: Some(r#ref),
58
+ exit_code: 69,
59
+ }
60
+ }
61
+
62
+ #[allow(dead_code)] // Used by the native Linux AT-SPI connection path.
63
+ pub fn dbus_blocked(reason: impl Into<String>) -> Self {
64
+ Self {
65
+ reason: reason.into(),
66
+ suggestion: "start the user AT-SPI D-Bus daemon".into(),
67
+ minimum_capability: Some("desktop-atspi.dbus_blocked".into()),
68
+ r#ref: None,
69
+ exit_code: 69,
70
+ }
71
+ }
72
+
73
+ #[allow(dead_code)] // Used when a target app exposes no useful AT-SPI tree.
74
+ pub fn no_a11y_attr(reason: impl Into<String>) -> Self {
75
+ Self {
76
+ reason: reason.into(),
77
+ suggestion: "enable accessibility support for the target app".into(),
78
+ minimum_capability: Some("desktop-atspi.no_a11y_attr".into()),
79
+ r#ref: None,
80
+ exit_code: 69,
81
+ }
82
+ }
83
+
84
+ #[allow(dead_code)] // Used by Wayland fallback input dispatch.
85
+ pub fn wayland_input_missing(reason: impl Into<String>) -> Self {
86
+ Self {
87
+ reason: reason.into(),
88
+ suggestion: "install ydotool or another supported Wayland input helper".into(),
89
+ minimum_capability: Some("desktop-atspi.wayland-input".into()),
90
+ r#ref: None,
91
+ exit_code: 69,
92
+ }
93
+ }
94
+
95
+ #[allow(dead_code)] // Used by X11 fallback input dispatch.
96
+ pub fn x11_input_missing(reason: impl Into<String>) -> Self {
97
+ Self {
98
+ reason: reason.into(),
99
+ suggestion: "install xdotool for X11 fallback input".into(),
100
+ minimum_capability: Some("desktop-atspi.x11-input".into()),
101
+ r#ref: None,
102
+ exit_code: 69,
103
+ }
104
+ }
105
+ }
106
+
107
+ pub trait IntoSidecarResponse {
108
+ fn into_response(self, id: u64, kind: String) -> SidecarResponse;
109
+ }
110
+
111
+ impl IntoSidecarResponse for HandlerResult {
112
+ fn into_response(self, id: u64, kind: String) -> SidecarResponse {
113
+ match self {
114
+ Ok(data) => SidecarResponse::ok(id, kind, data),
115
+ Err(err) => {
116
+ let minimum_capability = err
117
+ .minimum_capability
118
+ .unwrap_or_else(|| format!("desktop-atspi.{kind}"));
119
+ SidecarResponse {
120
+ id,
121
+ kind: kind.clone(),
122
+ ok: false,
123
+ data: None,
124
+ error: Some(SidecarError {
125
+ transport: "desktop-atspi".into(),
126
+ action: kind,
127
+ reason: err.reason,
128
+ suggestion: err.suggestion,
129
+ minimum_capability,
130
+ exit_code: err.exit_code,
131
+ stable_token: None,
132
+ r#ref: err.r#ref,
133
+ }),
134
+ }
135
+ }
136
+ }
137
+ }
138
+ }
139
+
140
+ pub fn backend_unavailable() -> AtspiError {
141
+ if cfg!(target_os = "linux") {
142
+ AtspiError::unavailable(backend_unavailable_reason("linux"))
143
+ } else {
144
+ AtspiError::unavailable(backend_unavailable_reason(std::env::consts::OS))
145
+ }
146
+ }
147
+
148
+ fn backend_unavailable_reason(target_os: &str) -> String {
149
+ if target_os == "linux" {
150
+ "Linux AT-SPI backend is unavailable for this request".into()
151
+ } else {
152
+ format!("desktop-atspi is only available on Linux; current target is {target_os}")
153
+ }
154
+ }
155
+
156
+ #[cfg(test)]
157
+ mod tests {
158
+ use super::*;
159
+
160
+ fn error_for(err: AtspiError) -> unicli_shared::SidecarError {
161
+ let response = Err::<Value, _>(err).into_response(9, "atspi_invoke".into());
162
+ response.error.expect("error response")
163
+ }
164
+
165
+ #[test]
166
+ fn no_element_uses_semantic_minimum_capability() {
167
+ let error = error_for(AtspiError::no_element("@e42"));
168
+ assert_eq!(error.minimum_capability, "desktop-atspi.no_element");
169
+ assert_eq!(error.r#ref.as_deref(), Some("@e42"));
170
+ assert_eq!(error.exit_code, 66);
171
+ }
172
+
173
+ #[test]
174
+ fn not_invokable_uses_semantic_minimum_capability() {
175
+ let error = error_for(AtspiError::not_invokable("@e42"));
176
+ assert_eq!(error.minimum_capability, "desktop-atspi.not_invokable");
177
+ assert_eq!(error.r#ref.as_deref(), Some("@e42"));
178
+ assert_eq!(error.exit_code, 69);
179
+ }
180
+
181
+ #[test]
182
+ fn dbus_blocked_uses_semantic_minimum_capability() {
183
+ let error = error_for(AtspiError::dbus_blocked("AT-SPI D-Bus is unavailable"));
184
+ assert_eq!(error.minimum_capability, "desktop-atspi.dbus_blocked");
185
+ assert_eq!(error.exit_code, 69);
186
+ }
187
+
188
+ #[test]
189
+ fn wayland_input_uses_semantic_minimum_capability() {
190
+ let error = error_for(AtspiError::wayland_input_missing("ydotool is missing"));
191
+ assert_eq!(error.minimum_capability, "desktop-atspi.wayland-input");
192
+ }
193
+
194
+ #[test]
195
+ fn no_a11y_attr_uses_semantic_minimum_capability() {
196
+ let error = error_for(AtspiError::no_a11y_attr("no accessibility attributes"));
197
+ assert_eq!(error.minimum_capability, "desktop-atspi.no_a11y_attr");
198
+ }
199
+
200
+ #[test]
201
+ fn x11_input_uses_semantic_minimum_capability() {
202
+ let error = error_for(AtspiError::x11_input_missing("xdotool is missing"));
203
+ assert_eq!(error.minimum_capability, "desktop-atspi.x11-input");
204
+ }
205
+
206
+ #[test]
207
+ fn backend_unavailable_reason_does_not_claim_native_traversal_pending() {
208
+ let reason = backend_unavailable_reason("linux");
209
+
210
+ assert!(!reason.contains("pending"));
211
+ assert!(reason.contains("unavailable"));
212
+ }
213
+ }