extra-desktop 0.3.2__tar.gz → 0.3.3__tar.gz

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 (190) hide show
  1. {extra_desktop-0.3.2/extra_desktop.egg-info → extra_desktop-0.3.3}/PKG-INFO +16 -8
  2. {extra_desktop-0.3.2 → extra_desktop-0.3.3}/README.md +5 -5
  3. {extra_desktop-0.3.2 → extra_desktop-0.3.3}/pyproject.toml +27 -12
  4. {extra_desktop-0.3.2 → extra_desktop-0.3.3/src/extra}/__init__.py +1 -1
  5. {extra_desktop-0.3.2 → extra_desktop-0.3.3/src/extra}/cli.py +100 -6
  6. {extra_desktop-0.3.2 → extra_desktop-0.3.3/src/extra}/core/distiller.py +43 -13
  7. {extra_desktop-0.3.2 → extra_desktop-0.3.3/src/extra}/core/platform/linux/shell.py +1 -0
  8. {extra_desktop-0.3.2 → extra_desktop-0.3.3/src/extra}/core/platform/macos/shell.py +3 -0
  9. {extra_desktop-0.3.2 → extra_desktop-0.3.3/src/extra}/core/platform/windows/input_engine.py +49 -30
  10. {extra_desktop-0.3.2 → extra_desktop-0.3.3/src/extra}/core/platform/windows/shell.py +14 -0
  11. {extra_desktop-0.3.2 → extra_desktop-0.3.3/src/extra}/core/platform/windows/uia_plane.py +27 -0
  12. {extra_desktop-0.3.2 → extra_desktop-0.3.3/src/extra}/core/soul/vision_parser.py +17 -6
  13. {extra_desktop-0.3.2 → extra_desktop-0.3.3/src/extra}/fastpath/__init__.py +22 -0
  14. {extra_desktop-0.3.2 → extra_desktop-0.3.3/src/extra}/fastpath/fs.py +42 -7
  15. extra_desktop-0.3.3/src/extra/fastpath/relay.py +26 -0
  16. extra_desktop-0.3.3/src/extra/fastpath/tunnel.py +24 -0
  17. extra_desktop-0.3.3/src/extra/mcp/server.py +2859 -0
  18. extra_desktop-0.3.3/src/extra/relay/__init__.py +42 -0
  19. extra_desktop-0.3.3/src/extra/relay/client.py +246 -0
  20. extra_desktop-0.3.3/src/extra/relay/tunnel.py +257 -0
  21. {extra_desktop-0.3.2 → extra_desktop-0.3.3/src/extra_desktop.egg-info}/PKG-INFO +16 -8
  22. extra_desktop-0.3.3/src/extra_desktop.egg-info/SOURCES.txt +186 -0
  23. {extra_desktop-0.3.2 → extra_desktop-0.3.3/src}/extra_desktop.egg-info/requires.txt +14 -2
  24. {extra_desktop-0.3.2 → extra_desktop-0.3.3}/tests/test_composer_director_soul.py +4 -2
  25. {extra_desktop-0.3.2 → extra_desktop-0.3.3}/tests/test_hierarchical_benchmarks.py +5 -4
  26. {extra_desktop-0.3.2 → extra_desktop-0.3.3}/tests/test_issue4_bottlenecks.py +2 -6
  27. extra_desktop-0.3.3/tests/test_mcp_sse_transport.py +201 -0
  28. {extra_desktop-0.3.2 → extra_desktop-0.3.3}/tests/test_pillar1_funnel.py +10 -3
  29. {extra_desktop-0.3.2 → extra_desktop-0.3.3}/tests/test_pillar2_installer.py +1 -1
  30. {extra_desktop-0.3.2 → extra_desktop-0.3.3}/tests/test_security_hardening.py +94 -0
  31. {extra_desktop-0.3.2 → extra_desktop-0.3.3}/tests/test_tiered_verification.py +1 -1
  32. {extra_desktop-0.3.2 → extra_desktop-0.3.3}/tests/test_tiered_verification_benchmarks.py +6 -4
  33. extra_desktop-0.3.2/extra_desktop.egg-info/SOURCES.txt +0 -250
  34. extra_desktop-0.3.2/mcp/server.py +0 -2521
  35. {extra_desktop-0.3.2 → extra_desktop-0.3.3}/LICENSE +0 -0
  36. {extra_desktop-0.3.2 → extra_desktop-0.3.3}/NOTICE +0 -0
  37. {extra_desktop-0.3.2 → extra_desktop-0.3.3}/setup.cfg +0 -0
  38. {extra_desktop-0.3.2 → extra_desktop-0.3.3/src/extra}/core/__init__.py +0 -0
  39. {extra_desktop-0.3.2 → extra_desktop-0.3.3/src/extra}/core/capture.py +0 -0
  40. {extra_desktop-0.3.2 → extra_desktop-0.3.3/src/extra}/core/composer/__init__.py +0 -0
  41. {extra_desktop-0.3.2 → extra_desktop-0.3.3/src/extra}/core/composer/blueprint.py +0 -0
  42. {extra_desktop-0.3.2 → extra_desktop-0.3.3/src/extra}/core/composer/folding.py +0 -0
  43. {extra_desktop-0.3.2 → extra_desktop-0.3.3/src/extra}/core/composer/lineage.py +0 -0
  44. {extra_desktop-0.3.2 → extra_desktop-0.3.3/src/extra}/core/composer/verifier.py +0 -0
  45. {extra_desktop-0.3.2 → extra_desktop-0.3.3/src/extra}/core/evolution/__init__.py +0 -0
  46. {extra_desktop-0.3.2 → extra_desktop-0.3.3/src/extra}/core/evolution/analyzer.py +0 -0
  47. {extra_desktop-0.3.2 → extra_desktop-0.3.3/src/extra}/core/evolution/api_synthesizer.py +0 -0
  48. {extra_desktop-0.3.2 → extra_desktop-0.3.3/src/extra}/core/evolution/crystallizer.py +0 -0
  49. {extra_desktop-0.3.2 → extra_desktop-0.3.3/src/extra}/core/evolution/curator.py +0 -0
  50. {extra_desktop-0.3.2 → extra_desktop-0.3.3/src/extra}/core/evolution/harness_generator.py +0 -0
  51. {extra_desktop-0.3.2 → extra_desktop-0.3.3/src/extra}/core/focus.py +0 -0
  52. {extra_desktop-0.3.2 → extra_desktop-0.3.3/src/extra}/core/geometry.py +0 -0
  53. {extra_desktop-0.3.2 → extra_desktop-0.3.3/src/extra}/core/indicators.py +0 -0
  54. {extra_desktop-0.3.2 → extra_desktop-0.3.3/src/extra}/core/input_engine.py +0 -0
  55. {extra_desktop-0.3.2 → extra_desktop-0.3.3/src/extra}/core/memory/__init__.py +0 -0
  56. {extra_desktop-0.3.2 → extra_desktop-0.3.3/src/extra}/core/memory/db.py +0 -0
  57. {extra_desktop-0.3.2 → extra_desktop-0.3.3/src/extra}/core/memory/embeddings.py +0 -0
  58. {extra_desktop-0.3.2 → extra_desktop-0.3.3/src/extra}/core/memory/ingest.py +0 -0
  59. {extra_desktop-0.3.2 → extra_desktop-0.3.3/src/extra}/core/memory/recall.py +0 -0
  60. {extra_desktop-0.3.2 → extra_desktop-0.3.3/src/extra}/core/metrics/__init__.py +0 -0
  61. {extra_desktop-0.3.2 → extra_desktop-0.3.3/src/extra}/core/metrics/auditor.py +0 -0
  62. {extra_desktop-0.3.2 → extra_desktop-0.3.3/src/extra}/core/metrics/config.py +0 -0
  63. {extra_desktop-0.3.2 → extra_desktop-0.3.3/src/extra}/core/metrics/dashboard.py +0 -0
  64. {extra_desktop-0.3.2 → extra_desktop-0.3.3/src/extra}/core/metrics/ledger.py +0 -0
  65. {extra_desktop-0.3.2 → extra_desktop-0.3.3/src/extra}/core/metrics/sanitizer.py +0 -0
  66. {extra_desktop-0.3.2 → extra_desktop-0.3.3/src/extra}/core/metrics/telemetry.py +0 -0
  67. {extra_desktop-0.3.2 → extra_desktop-0.3.3/src/extra}/core/motion/__init__.py +0 -0
  68. {extra_desktop-0.3.2 → extra_desktop-0.3.3/src/extra}/core/motion/curves.py +0 -0
  69. {extra_desktop-0.3.2 → extra_desktop-0.3.3/src/extra}/core/motion/profiler.py +0 -0
  70. {extra_desktop-0.3.2 → extra_desktop-0.3.3/src/extra}/core/motion/stroke.py +0 -0
  71. {extra_desktop-0.3.2 → extra_desktop-0.3.3/src/extra}/core/motion/windmouse.py +0 -0
  72. {extra_desktop-0.3.2 → extra_desktop-0.3.3/src/extra}/core/platform/__init__.py +0 -0
  73. {extra_desktop-0.3.2 → extra_desktop-0.3.3/src/extra}/core/platform/base.py +0 -0
  74. {extra_desktop-0.3.2 → extra_desktop-0.3.3/src/extra}/core/platform/linux/__init__.py +0 -0
  75. {extra_desktop-0.3.2 → extra_desktop-0.3.3/src/extra}/core/platform/linux/atspi_plane.py +0 -0
  76. {extra_desktop-0.3.2 → extra_desktop-0.3.3/src/extra}/core/platform/linux/capture.py +0 -0
  77. {extra_desktop-0.3.2 → extra_desktop-0.3.3/src/extra}/core/platform/linux/focus.py +0 -0
  78. {extra_desktop-0.3.2 → extra_desktop-0.3.3/src/extra}/core/platform/linux/geometry.py +0 -0
  79. {extra_desktop-0.3.2 → extra_desktop-0.3.3/src/extra}/core/platform/linux/indicators.py +0 -0
  80. {extra_desktop-0.3.2 → extra_desktop-0.3.3/src/extra}/core/platform/linux/input_engine.py +0 -0
  81. {extra_desktop-0.3.2 → extra_desktop-0.3.3/src/extra}/core/platform/linux/permissions.py +0 -0
  82. {extra_desktop-0.3.2 → extra_desktop-0.3.3/src/extra}/core/platform/macos/__init__.py +0 -0
  83. {extra_desktop-0.3.2 → extra_desktop-0.3.3/src/extra}/core/platform/macos/ax_plane.py +0 -0
  84. {extra_desktop-0.3.2 → extra_desktop-0.3.3/src/extra}/core/platform/macos/capture.py +0 -0
  85. {extra_desktop-0.3.2 → extra_desktop-0.3.3/src/extra}/core/platform/macos/focus.py +0 -0
  86. {extra_desktop-0.3.2 → extra_desktop-0.3.3/src/extra}/core/platform/macos/geometry.py +0 -0
  87. {extra_desktop-0.3.2 → extra_desktop-0.3.3/src/extra}/core/platform/macos/indicators.py +0 -0
  88. {extra_desktop-0.3.2 → extra_desktop-0.3.3/src/extra}/core/platform/macos/input_engine.py +0 -0
  89. {extra_desktop-0.3.2 → extra_desktop-0.3.3/src/extra}/core/platform/macos/permissions.py +0 -0
  90. {extra_desktop-0.3.2 → extra_desktop-0.3.3/src/extra}/core/platform/windows/__init__.py +0 -0
  91. {extra_desktop-0.3.2 → extra_desktop-0.3.3/src/extra}/core/platform/windows/capture.py +0 -0
  92. {extra_desktop-0.3.2 → extra_desktop-0.3.3/src/extra}/core/platform/windows/child_session.py +0 -0
  93. {extra_desktop-0.3.2 → extra_desktop-0.3.3/src/extra}/core/platform/windows/focus.py +0 -0
  94. {extra_desktop-0.3.2 → extra_desktop-0.3.3/src/extra}/core/platform/windows/geometry.py +0 -0
  95. {extra_desktop-0.3.2 → extra_desktop-0.3.3/src/extra}/core/platform/windows/indicators.py +0 -0
  96. {extra_desktop-0.3.2 → extra_desktop-0.3.3/src/extra}/core/platform/windows/pip_window.py +0 -0
  97. {extra_desktop-0.3.2 → extra_desktop-0.3.3/src/extra}/core/scout/__init__.py +0 -0
  98. {extra_desktop-0.3.2 → extra_desktop-0.3.3/src/extra}/core/scout/cdp_sniffer.py +0 -0
  99. {extra_desktop-0.3.2 → extra_desktop-0.3.3/src/extra}/core/scout/detector.py +0 -0
  100. {extra_desktop-0.3.2 → extra_desktop-0.3.3/src/extra}/core/scout/scraper.py +0 -0
  101. {extra_desktop-0.3.2 → extra_desktop-0.3.3/src/extra}/core/scout/session_vault.py +0 -0
  102. {extra_desktop-0.3.2 → extra_desktop-0.3.3/src/extra}/core/scout/synthesizer.py +0 -0
  103. {extra_desktop-0.3.2 → extra_desktop-0.3.3/src/extra}/core/soul/__init__.py +0 -0
  104. {extra_desktop-0.3.2 → extra_desktop-0.3.3/src/extra}/core/soul/compressor.py +0 -0
  105. {extra_desktop-0.3.2 → extra_desktop-0.3.3/src/extra}/core/soul/critic.py +0 -0
  106. {extra_desktop-0.3.2 → extra_desktop-0.3.3/src/extra}/core/soul/decider.py +0 -0
  107. {extra_desktop-0.3.2 → extra_desktop-0.3.3/src/extra}/core/soul/eyes.py +0 -0
  108. {extra_desktop-0.3.2 → extra_desktop-0.3.3/src/extra}/core/soul/gateman.py +0 -0
  109. {extra_desktop-0.3.2 → extra_desktop-0.3.3/src/extra}/core/soul/grammar.py +0 -0
  110. {extra_desktop-0.3.2 → extra_desktop-0.3.3/src/extra}/core/soul/runtime.py +0 -0
  111. {extra_desktop-0.3.2 → extra_desktop-0.3.3/src/extra}/core/soul/schemas.py +0 -0
  112. {extra_desktop-0.3.2 → extra_desktop-0.3.3/src/extra}/core/soul/state_critic.py +0 -0
  113. {extra_desktop-0.3.2 → extra_desktop-0.3.3/src/extra}/core/stall_breaker.py +0 -0
  114. {extra_desktop-0.3.2 → extra_desktop-0.3.3/src/extra}/core/subtask.py +0 -0
  115. {extra_desktop-0.3.2 → extra_desktop-0.3.3/src/extra}/core/uia_plane.py +0 -0
  116. {extra_desktop-0.3.2 → extra_desktop-0.3.3/src/extra}/core/uninstall.py +0 -0
  117. {extra_desktop-0.3.2 → extra_desktop-0.3.3/src/extra}/fastpath/bridge.py +0 -0
  118. {extra_desktop-0.3.2 → extra_desktop-0.3.3/src/extra}/fastpath/browser.py +0 -0
  119. {extra_desktop-0.3.2 → extra_desktop-0.3.3/src/extra}/fastpath/cli_harness.py +0 -0
  120. {extra_desktop-0.3.2 → extra_desktop-0.3.3/src/extra}/fastpath/cli_hub.py +0 -0
  121. {extra_desktop-0.3.2 → extra_desktop-0.3.3/src/extra}/fastpath/clipboard_html.py +0 -0
  122. {extra_desktop-0.3.2 → extra_desktop-0.3.3/src/extra}/fastpath/shell.py +0 -0
  123. {extra_desktop-0.3.2 → extra_desktop-0.3.3/src/extra}/fastpath/vectorizer.py +0 -0
  124. {extra_desktop-0.3.2 → extra_desktop-0.3.3/src/extra}/fastpath/visualizer.py +0 -0
  125. {extra_desktop-0.3.2 → extra_desktop-0.3.3/src/extra}/fastpath/web/__init__.py +0 -0
  126. {extra_desktop-0.3.2 → extra_desktop-0.3.3/src/extra}/fastpath/web_workspace.py +0 -0
  127. {extra_desktop-0.3.2 → extra_desktop-0.3.3/src/extra}/mcp/__init__.py +0 -0
  128. {extra_desktop-0.3.2 → extra_desktop-0.3.3/src}/extra_desktop.egg-info/dependency_links.txt +0 -0
  129. {extra_desktop-0.3.2 → extra_desktop-0.3.3/src}/extra_desktop.egg-info/entry_points.txt +0 -0
  130. {extra_desktop-0.3.2 → extra_desktop-0.3.3/src}/extra_desktop.egg-info/top_level.txt +0 -0
  131. {extra_desktop-0.3.2 → extra_desktop-0.3.3}/tests/test_acceleration_suite.py +0 -0
  132. {extra_desktop-0.3.2 → extra_desktop-0.3.3}/tests/test_app_registry_dynamic.py +0 -0
  133. {extra_desktop-0.3.2 → extra_desktop-0.3.3}/tests/test_bridge_sandbox.py +0 -0
  134. {extra_desktop-0.3.2 → extra_desktop-0.3.3}/tests/test_canvas_stall_breaker.py +0 -0
  135. {extra_desktop-0.3.2 → extra_desktop-0.3.3}/tests/test_child_session.py +0 -0
  136. {extra_desktop-0.3.2 → extra_desktop-0.3.3}/tests/test_cli_harness.py +0 -0
  137. {extra_desktop-0.3.2 → extra_desktop-0.3.3}/tests/test_core_engine.py +0 -0
  138. {extra_desktop-0.3.2 → extra_desktop-0.3.3}/tests/test_dynamic_batching.py +0 -0
  139. {extra_desktop-0.3.2 → extra_desktop-0.3.3}/tests/test_evolution.py +0 -0
  140. {extra_desktop-0.3.2 → extra_desktop-0.3.3}/tests/test_evolution_harness.py +0 -0
  141. {extra_desktop-0.3.2 → extra_desktop-0.3.3}/tests/test_geometry_exhaustive.py +0 -0
  142. {extra_desktop-0.3.2 → extra_desktop-0.3.3}/tests/test_human_mouse.py +0 -0
  143. {extra_desktop-0.3.2 → extra_desktop-0.3.3}/tests/test_input_engine_exhaustive.py +0 -0
  144. {extra_desktop-0.3.2 → extra_desktop-0.3.3}/tests/test_launch_maximize.py +0 -0
  145. {extra_desktop-0.3.2 → extra_desktop-0.3.3}/tests/test_lineage.py +0 -0
  146. {extra_desktop-0.3.2 → extra_desktop-0.3.3}/tests/test_linux_atspi.py +0 -0
  147. {extra_desktop-0.3.2 → extra_desktop-0.3.3}/tests/test_linux_capture.py +0 -0
  148. {extra_desktop-0.3.2 → extra_desktop-0.3.3}/tests/test_linux_focus.py +0 -0
  149. {extra_desktop-0.3.2 → extra_desktop-0.3.3}/tests/test_linux_geometry.py +0 -0
  150. {extra_desktop-0.3.2 → extra_desktop-0.3.3}/tests/test_linux_indicators.py +0 -0
  151. {extra_desktop-0.3.2 → extra_desktop-0.3.3}/tests/test_linux_input.py +0 -0
  152. {extra_desktop-0.3.2 → extra_desktop-0.3.3}/tests/test_linux_shell.py +0 -0
  153. {extra_desktop-0.3.2 → extra_desktop-0.3.3}/tests/test_macos_ax.py +0 -0
  154. {extra_desktop-0.3.2 → extra_desktop-0.3.3}/tests/test_macos_core.py +0 -0
  155. {extra_desktop-0.3.2 → extra_desktop-0.3.3}/tests/test_macos_focus_shell.py +0 -0
  156. {extra_desktop-0.3.2 → extra_desktop-0.3.3}/tests/test_macos_indicators.py +0 -0
  157. {extra_desktop-0.3.2 → extra_desktop-0.3.3}/tests/test_macos_input.py +0 -0
  158. {extra_desktop-0.3.2 → extra_desktop-0.3.3}/tests/test_macos_perception.py +0 -0
  159. {extra_desktop-0.3.2 → extra_desktop-0.3.3}/tests/test_macos_safety.py +0 -0
  160. {extra_desktop-0.3.2 → extra_desktop-0.3.3}/tests/test_mcp_schemas_exhaustive.py +0 -0
  161. {extra_desktop-0.3.2 → extra_desktop-0.3.3}/tests/test_memory_kuzu.py +0 -0
  162. {extra_desktop-0.3.2 → extra_desktop-0.3.3}/tests/test_memory_subsystem_exhaustive.py +0 -0
  163. {extra_desktop-0.3.2 → extra_desktop-0.3.3}/tests/test_metrics.py +0 -0
  164. {extra_desktop-0.3.2 → extra_desktop-0.3.3}/tests/test_milestone_budgeting.py +0 -0
  165. {extra_desktop-0.3.2 → extra_desktop-0.3.3}/tests/test_paint_remediation.py +0 -0
  166. {extra_desktop-0.3.2 → extra_desktop-0.3.3}/tests/test_perception_vision_exhaustive.py +0 -0
  167. {extra_desktop-0.3.2 → extra_desktop-0.3.3}/tests/test_pillar3_tcc_playbook.py +0 -0
  168. {extra_desktop-0.3.2 → extra_desktop-0.3.3}/tests/test_pillar4_web_edge.py +0 -0
  169. {extra_desktop-0.3.2 → extra_desktop-0.3.3}/tests/test_pillar5_package_ecosystem.py +0 -0
  170. {extra_desktop-0.3.2 → extra_desktop-0.3.3}/tests/test_pre_release_fixes.py +0 -0
  171. {extra_desktop-0.3.2 → extra_desktop-0.3.3}/tests/test_process_shell_exhaustive.py +0 -0
  172. {extra_desktop-0.3.2 → extra_desktop-0.3.3}/tests/test_scout.py +0 -0
  173. {extra_desktop-0.3.2 → extra_desktop-0.3.3}/tests/test_scout_evolution_exhaustive.py +0 -0
  174. {extra_desktop-0.3.2 → extra_desktop-0.3.3}/tests/test_soul_benchmarks.py +0 -0
  175. {extra_desktop-0.3.2 → extra_desktop-0.3.3}/tests/test_soul_evolution.py +0 -0
  176. {extra_desktop-0.3.2 → extra_desktop-0.3.3}/tests/test_soul_runtime.py +0 -0
  177. {extra_desktop-0.3.2 → extra_desktop-0.3.3}/tests/test_stall_breaker_cognitive.py +0 -0
  178. {extra_desktop-0.3.2 → extra_desktop-0.3.3}/tests/test_stall_breaker_exhaustive.py +0 -0
  179. {extra_desktop-0.3.2 → extra_desktop-0.3.3}/tests/test_state_critic.py +0 -0
  180. {extra_desktop-0.3.2 → extra_desktop-0.3.3}/tests/test_subtask_runner.py +0 -0
  181. {extra_desktop-0.3.2 → extra_desktop-0.3.3}/tests/test_telemetry.py +0 -0
  182. {extra_desktop-0.3.2 → extra_desktop-0.3.3}/tests/test_ui_distiller.py +0 -0
  183. {extra_desktop-0.3.2 → extra_desktop-0.3.3}/tests/test_uninstall.py +0 -0
  184. {extra_desktop-0.3.2 → extra_desktop-0.3.3}/tests/test_universal_tasksuite.py +0 -0
  185. {extra_desktop-0.3.2 → extra_desktop-0.3.3}/tests/test_verification_mcp.py +0 -0
  186. {extra_desktop-0.3.2 → extra_desktop-0.3.3}/tests/test_vision_parser.py +0 -0
  187. {extra_desktop-0.3.2 → extra_desktop-0.3.3}/tests/test_visual_grounding.py +0 -0
  188. {extra_desktop-0.3.2 → extra_desktop-0.3.3}/tests/test_web_to_api.py +0 -0
  189. {extra_desktop-0.3.2 → extra_desktop-0.3.3}/tests/test_web_workspace.py +0 -0
  190. {extra_desktop-0.3.2 → extra_desktop-0.3.3}/tests/test_windows_indicators.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: extra-desktop
3
- Version: 0.3.2
3
+ Version: 0.3.3
4
4
  Summary: Flashless Linux, Windows & macOS Computer-Use Engine & Model Context Protocol (MCP) Server
5
5
  Author-email: AIYantra Engineering Team <engineering@yantraos.com>
6
6
  License-Expression: MIT
@@ -11,14 +11,15 @@ Requires-Python: >=3.10
11
11
  Description-Content-Type: text/markdown
12
12
  License-File: LICENSE
13
13
  License-File: NOTICE
14
- Requires-Dist: mcp>=1.0.0
14
+ Requires-Dist: mcp<2,>=1.0.0
15
+ Requires-Dist: websockets>=13.0
15
16
  Requires-Dist: playwright>=1.47.0
16
17
  Requires-Dist: pillow>=10.4.0
17
18
  Requires-Dist: beautifulsoup4>=4.12.3
18
19
  Requires-Dist: imagehash>=4.3.1
19
20
  Requires-Dist: numpy>=1.26.0
20
21
  Requires-Dist: psutil>=6.0.0
21
- Requires-Dist: kuzu>=0.11.0
22
+ Requires-Dist: kuzu>=0.11.0; python_version < "3.14"
22
23
  Requires-Dist: fastembed>=0.8.0
23
24
  Requires-Dist: onnxruntime>=1.19.0
24
25
  Requires-Dist: pywin32>=306; sys_platform == "win32"
@@ -32,12 +33,19 @@ Requires-Dist: pyobjc-framework-ScreenCaptureKit>=10.0; sys_platform == "darwin"
32
33
  Requires-Dist: evdev>=1.7.0; sys_platform == "linux"
33
34
  Requires-Dist: mss>=9.0.1; sys_platform == "linux"
34
35
  Requires-Dist: jeepney>=0.8.0; sys_platform == "linux"
36
+ Provides-Extra: dev
37
+ Requires-Dist: pytest>=8.0.0; extra == "dev"
38
+ Requires-Dist: pytest-asyncio>=0.24.0; extra == "dev"
39
+ Requires-Dist: ruff>=0.6.0; extra == "dev"
40
+ Requires-Dist: mypy>=1.11.0; extra == "dev"
41
+ Provides-Extra: benchmark
42
+ Requires-Dist: pytest-benchmark>=4.0.0; extra == "benchmark"
35
43
  Dynamic: license-file
36
44
 
37
45
  <div align="center">
38
46
 
39
47
  <a href="https://extra.yantraos.com">
40
- <img src="https://raw.githubusercontent.com/AIYantra/extra/main/assets/logo.png" alt="Extra — Get extra from your AI" width="420" />
48
+ <img src="assets/logo.png" alt="Extra — Get extra from your AI" width="420" />
41
49
  </a>
42
50
 
43
51
  # Extra
@@ -54,7 +62,7 @@ Dynamic: license-file
54
62
  [![Ecosystem: yantraOS](https://img.shields.io/badge/Ecosystem-yantraOS-8A2BE2.svg)](https://yantraos.com)
55
63
  [![Status: Open Source](https://img.shields.io/badge/Open%20Source-%E2%99%A5-emerald.svg)](https://github.com/AIYantra/extra)
56
64
 
57
- [Website](https://extra.yantraos.com) • [Quickstart](#-quickstart-1-minute) • [Linux Guide](README_LINUX.md) • [macOS Guide](README_MACOS.md) • [Architecture](ARCHITECTURE.md) • [License](LICENSE)
65
+ [Website](https://extra.yantraos.com) • [Quickstart](#-quickstart-1-minute) • [Linux Guide](README_LINUX.md) • [macOS Guide](README_MACOS.md) • [Architecture](docs/ARCHITECTURE.md) • [License](LICENSE)
58
66
 
59
67
  <br/>
60
68
  <br/>
@@ -278,14 +286,14 @@ extra/
278
286
  ├── cli.py # CLI runner (extra doctor, test, indicators, run, inspect, snap)
279
287
  ├── install.ps1 # 1-line PowerShell installer (Windows)
280
288
  ├── install.sh # 1-line bash installer (macOS)
281
- ├── pyproject.toml # PyPI package manifest (extra-desktop v0.3.1)
282
- ├── package.json # npm package manifest (@yantraos/extra-desktop v0.3.1)
289
+ ├── pyproject.toml # PyPI package manifest (extra-desktop v0.3.3)
290
+ ├── package.json # npm package manifest (@yantraos/extra-desktop v0.3.3)
283
291
  ├── server.json # Official MCP registry manifest
284
292
  ├── glama.json # Glama MCP ecosystem manifest
285
293
  ├── smithery.yaml # Smithery 1-click deployment manifest
286
294
  ├── requirements.txt # Enterprise-audited dependency manifest
287
295
  ├── STARTER_PROMPT.md # Master AI system prompt & setup directive
288
- ├── ARCHITECTURE.md # Full System Architecture Specification
296
+ ├── docs/ # Full System Architecture & Specifications
289
297
  └── RELEASE_NOTES.md # Comprehensive version release notes
290
298
  ```
291
299
 
@@ -1,7 +1,7 @@
1
1
  <div align="center">
2
2
 
3
3
  <a href="https://extra.yantraos.com">
4
- <img src="https://raw.githubusercontent.com/AIYantra/extra/main/assets/logo.png" alt="Extra — Get extra from your AI" width="420" />
4
+ <img src="assets/logo.png" alt="Extra — Get extra from your AI" width="420" />
5
5
  </a>
6
6
 
7
7
  # Extra
@@ -18,7 +18,7 @@
18
18
  [![Ecosystem: yantraOS](https://img.shields.io/badge/Ecosystem-yantraOS-8A2BE2.svg)](https://yantraos.com)
19
19
  [![Status: Open Source](https://img.shields.io/badge/Open%20Source-%E2%99%A5-emerald.svg)](https://github.com/AIYantra/extra)
20
20
 
21
- [Website](https://extra.yantraos.com) • [Quickstart](#-quickstart-1-minute) • [Linux Guide](README_LINUX.md) • [macOS Guide](README_MACOS.md) • [Architecture](ARCHITECTURE.md) • [License](LICENSE)
21
+ [Website](https://extra.yantraos.com) • [Quickstart](#-quickstart-1-minute) • [Linux Guide](README_LINUX.md) • [macOS Guide](README_MACOS.md) • [Architecture](docs/ARCHITECTURE.md) • [License](LICENSE)
22
22
 
23
23
  <br/>
24
24
  <br/>
@@ -242,14 +242,14 @@ extra/
242
242
  ├── cli.py # CLI runner (extra doctor, test, indicators, run, inspect, snap)
243
243
  ├── install.ps1 # 1-line PowerShell installer (Windows)
244
244
  ├── install.sh # 1-line bash installer (macOS)
245
- ├── pyproject.toml # PyPI package manifest (extra-desktop v0.3.1)
246
- ├── package.json # npm package manifest (@yantraos/extra-desktop v0.3.1)
245
+ ├── pyproject.toml # PyPI package manifest (extra-desktop v0.3.3)
246
+ ├── package.json # npm package manifest (@yantraos/extra-desktop v0.3.3)
247
247
  ├── server.json # Official MCP registry manifest
248
248
  ├── glama.json # Glama MCP ecosystem manifest
249
249
  ├── smithery.yaml # Smithery 1-click deployment manifest
250
250
  ├── requirements.txt # Enterprise-audited dependency manifest
251
251
  ├── STARTER_PROMPT.md # Master AI system prompt & setup directive
252
- ├── ARCHITECTURE.md # Full System Architecture Specification
252
+ ├── docs/ # Full System Architecture & Specifications
253
253
  └── RELEASE_NOTES.md # Comprehensive version release notes
254
254
  ```
255
255
 
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "extra-desktop"
7
- version = "0.3.2"
7
+ version = "0.3.3"
8
8
  description = "Flashless Linux, Windows & macOS Computer-Use Engine & Model Context Protocol (MCP) Server"
9
9
  readme = "README.md"
10
10
  requires-python = ">=3.10"
@@ -13,14 +13,15 @@ authors = [
13
13
  { name = "AIYantra Engineering Team", email = "engineering@yantraos.com" }
14
14
  ]
15
15
  dependencies = [
16
- "mcp>=1.0.0",
16
+ "mcp>=1.0.0,<2",
17
+ "websockets>=13.0",
17
18
  "playwright>=1.47.0",
18
19
  "pillow>=10.4.0",
19
20
  "beautifulsoup4>=4.12.3",
20
21
  "imagehash>=4.3.1",
21
22
  "numpy>=1.26.0",
22
23
  "psutil>=6.0.0",
23
- "kuzu>=0.11.0",
24
+ "kuzu>=0.11.0; python_version < '3.14'",
24
25
  "fastembed>=0.8.0",
25
26
  "onnxruntime>=1.19.0",
26
27
  "pywin32>=306; sys_platform == 'win32'",
@@ -36,6 +37,17 @@ dependencies = [
36
37
  "jeepney>=0.8.0; sys_platform == 'linux'",
37
38
  ]
38
39
 
40
+ [project.optional-dependencies]
41
+ dev = [
42
+ "pytest>=8.0.0",
43
+ "pytest-asyncio>=0.24.0",
44
+ "ruff>=0.6.0",
45
+ "mypy>=1.11.0",
46
+ ]
47
+ benchmark = [
48
+ "pytest-benchmark>=4.0.0",
49
+ ]
50
+
39
51
  [project.scripts]
40
52
  extra = "extra.cli:main"
41
53
  extra-desktop = "extra.cli:main"
@@ -45,17 +57,20 @@ Homepage = "https://extra.yantraos.com"
45
57
  Repository = "https://github.com/AIYantra/extra"
46
58
  Documentation = "https://extra.yantraos.com/docs"
47
59
 
48
- [tool.setuptools.package-dir]
49
- "extra" = "."
50
- "extra.core" = "core"
51
- "extra.core.platform" = "core/platform"
52
- "extra.core.platform.windows" = "core/platform/windows"
53
- "extra.core.platform.macos" = "core/platform/macos"
54
- "extra.core.platform.linux" = "core/platform/linux"
55
- "extra.fastpath" = "fastpath"
56
- "extra.mcp" = "mcp"
60
+ [tool.setuptools.packages.find]
61
+ where = ["src"]
57
62
 
58
63
  [tool.pytest.ini_options]
59
64
  testpaths = ["tests"]
65
+ pythonpath = ["src"]
60
66
  addopts = "-p no:faulthandler"
67
+ markers = [
68
+ "unit: Fast isolated unit tests with zero system dependencies",
69
+ "integration: Subsystem integration tests",
70
+ "windows: Tests requiring Windows Win32 / UIA subsystems",
71
+ "linux: Tests requiring Linux X11 / Wayland / AT-SPI2 subsystems",
72
+ "macos: Tests requiring macOS Quartz / Accessibility subsystems",
73
+ "benchmark: Performance profiling and stress benchmark suites",
74
+ "security: Security audit and vulnerability verification tests",
75
+ ]
61
76
 
@@ -2,4 +2,4 @@
2
2
  Project Extra — Flashless Linux, Windows & macOS Computer-Use Engine & MCP Server.
3
3
  """
4
4
 
5
- __version__ = "0.3.2"
5
+ __version__ = "0.3.3"
@@ -22,7 +22,7 @@ sys.path.insert(0, str(Path(__file__).resolve().parent.parent))
22
22
  try:
23
23
  from extra import __version__
24
24
  except Exception:
25
- __version__ = "0.3.1"
25
+ __version__ = "0.3.3"
26
26
 
27
27
  from extra.core.capture import capture_screen
28
28
  from extra.core.focus import (
@@ -359,8 +359,45 @@ def cmd_run(args: argparse.Namespace) -> int:
359
359
  os.environ["EXTRA_ISOLATED_DESKTOP"] = "1"
360
360
  if getattr(args, "pip", False):
361
361
  os.environ["EXTRA_PIP_PREVIEW"] = "1"
362
+
363
+ transport = getattr(args, "transport", "stdio")
364
+ host = getattr(args, "host", "127.0.0.1")
365
+ port = getattr(args, "port", 8000)
366
+ auth_token = getattr(args, "auth_token", None)
367
+ allowed_hosts = getattr(args, "allowed_hosts", None)
368
+ tunnel = getattr(args, "tunnel", False)
369
+ if allowed_hosts and isinstance(allowed_hosts, str):
370
+ allowed_hosts = [h.strip() for h in allowed_hosts.split(",") if h.strip()]
371
+
362
372
  from extra.mcp.server import main
363
- main()
373
+ main(transport=transport, host=host, port=port, auth_token=auth_token, allowed_hosts=allowed_hosts, tunnel=tunnel)
374
+ return 0
375
+
376
+
377
+ def cmd_serve(args: argparse.Namespace) -> int:
378
+ """Starts the Extra MCP server over SSE HTTP transport (for ChatGPT plugins & web agents)."""
379
+ setattr(args, "transport", "sse")
380
+ return cmd_run(args)
381
+
382
+
383
+ def cmd_connect(args: argparse.Namespace) -> int:
384
+ """Connects this desktop to the Extra Cloud Hub for ChatGPT automation."""
385
+ from extra.relay import start_relay, DEFAULT_RELAY_URL
386
+ hub_url = getattr(args, "hub", None) or os.environ.get("EXTRA_HUB_URL") or DEFAULT_RELAY_URL
387
+ token = getattr(args, "token", None)
388
+ if not token or token == "anonymous":
389
+ token = os.environ.get("EXTRA_KEY") or os.environ.get("EXTRA_RELAY_KEY")
390
+ if not token or token == "anonymous":
391
+ print("[!] Error: Workstation link key is required to connect to the Extra Cloud Hub.")
392
+ print(" Get your key from your dashboard at: https://extra.yantraos.com/dashboard")
393
+ print(" Then run:")
394
+ print(" extra connect --key <your_key>")
395
+ return 1
396
+ start_relay(
397
+ hub_url=hub_url,
398
+ token=token,
399
+ device_name=getattr(args, "name", None),
400
+ )
364
401
  return 0
365
402
 
366
403
 
@@ -836,7 +873,7 @@ def cmd_update(args: argparse.Namespace) -> int:
836
873
  if pyproject_file.exists():
837
874
  try:
838
875
  subprocess.run(
839
- [sys.executable, "-m", "pip", "install", "--quiet", "-e", str(repo_dir)],
876
+ [sys.executable, "-m", "pip", "install", "--quiet", "--no-deps", "-e", str(repo_dir)],
840
877
  check=True,
841
878
  )
842
879
  print(" [OK] Package editable link verified.")
@@ -845,6 +882,32 @@ def cmd_update(args: argparse.Namespace) -> int:
845
882
  else:
846
883
  print("\n[Dependency Refresh] Skipped (--skip-deps specified).")
847
884
 
885
+ # Phase 2b: Refresh CLI executable wrappers with updated PYTHONPATH
886
+ try:
887
+ bin_dir = Path.home() / ".extra" / "bin"
888
+ if bin_dir.exists():
889
+ if platform.system() == "Windows":
890
+ cmd_wrapper = bin_dir / "extra.cmd"
891
+ cmd_content = (
892
+ f"@echo off\n"
893
+ f"set PYTHONPATH={repo_dir}\\src;{repo_dir};%PYTHONPATH%\n"
894
+ f'"{sys.executable}" -m extra.cli %*\n'
895
+ )
896
+ cmd_wrapper.write_text(cmd_content, encoding="ascii")
897
+ print(" [OK] Updated Windows CLI wrapper in ~/.extra/bin/extra.cmd.")
898
+ else:
899
+ sh_wrapper = bin_dir / "extra"
900
+ sh_content = (
901
+ f"#!/usr/bin/env bash\n"
902
+ f'export PYTHONPATH="{repo_dir}/src:{repo_dir}:$PYTHONPATH"\n'
903
+ f'exec "{sys.executable}" -m extra.cli "$@"\n'
904
+ )
905
+ sh_wrapper.write_text(sh_content, encoding="utf-8")
906
+ sh_wrapper.chmod(0o755)
907
+ print(" [OK] Updated Unix CLI wrapper in ~/.extra/bin/extra.")
908
+ except Exception as ex:
909
+ print(f" [WARN] Could not refresh CLI wrapper: {ex}")
910
+
848
911
  # Phase 3: AI Rules & Skills Synchronization
849
912
  sync_ai_rules_and_skills(repo_dir)
850
913
 
@@ -1035,7 +1098,8 @@ def check_first_run_consent() -> None:
1035
1098
  save_config(cfg)
1036
1099
 
1037
1100
 
1038
- def main() -> None:
1101
+ def build_parser() -> argparse.ArgumentParser:
1102
+ """Builds and returns the top-level ArgumentParser for Extra CLI."""
1039
1103
  parser = argparse.ArgumentParser(
1040
1104
  prog="extra",
1041
1105
  description="Extra — Flashless macOS, Windows & Linux Computer-Use Engine & MCP Server",
@@ -1069,11 +1133,32 @@ def main() -> None:
1069
1133
  update_parser.add_argument("--force", action="store_true", help="Force update and reset local changes to upstream")
1070
1134
 
1071
1135
  # run
1072
- run_parser = subparsers.add_parser("run", help="Start the MCP Server on stdio")
1073
- run_parser.add_argument("--transport", default="stdio", choices=["stdio"], help="Transport mode")
1136
+ run_parser = subparsers.add_parser("run", help="Start the MCP Server on stdio or SSE")
1137
+ run_parser.add_argument("--transport", default="stdio", choices=["stdio", "sse", "streamable-http"], help="Transport mode (default: stdio)")
1138
+ run_parser.add_argument("--host", default="127.0.0.1", help="Host IP to bind to when running SSE or HTTP transport (default: 127.0.0.1)")
1139
+ run_parser.add_argument("--port", "-p", type=int, default=8000, help="Port to bind to when running SSE or HTTP transport (default: 8000)")
1140
+ run_parser.add_argument("--auth-token", "--auth", default=None, help="Bearer authorization token to secure SSE transport")
1141
+ run_parser.add_argument("--allowed-hosts", default=None, help="Comma-separated allowed host header values for DNS rebinding protection")
1142
+ run_parser.add_argument("--tunnel", "-t", action="store_true", help="Launch secure Cloudflare Quick Tunnel (public HTTPS) for pairing with ChatGPT or remote agents")
1074
1143
  run_parser.add_argument("--isolate", action="store_true", help="Confine automation to isolated child session / virtual desktop")
1075
1144
  run_parser.add_argument("--pip", action="store_true", help="Launch live Picture-in-Picture floating preview window")
1076
1145
 
1146
+ # serve (ChatGPT Plugin & Remote Web Agent bridge)
1147
+ serve_parser = subparsers.add_parser("serve", help="Start the Extra MCP Server over SSE HTTP transport (for ChatGPT plugins & web agents)")
1148
+ serve_parser.add_argument("--host", default="127.0.0.1", help="Host IP to bind to (default: 127.0.0.1; use 0.0.0.0 for public/tunnel)")
1149
+ serve_parser.add_argument("--port", "-p", type=int, default=8000, help="Port to bind to (default: 8000)")
1150
+ serve_parser.add_argument("--auth-token", "--auth", default=None, help="Bearer authorization token to secure SSE transport")
1151
+ serve_parser.add_argument("--allowed-hosts", default=None, help="Comma-separated allowed host headers (e.g. *.trycloudflare.com)")
1152
+ serve_parser.add_argument("--tunnel", "-t", action="store_true", help="Launch secure Cloudflare Quick Tunnel (public HTTPS) for pairing with ChatGPT or remote agents")
1153
+ serve_parser.add_argument("--isolate", action="store_true", help="Confine automation to isolated child session / virtual desktop")
1154
+ serve_parser.add_argument("--pip", action="store_true", help="Launch live Picture-in-Picture floating preview window")
1155
+
1156
+ # connect (Desktop Relay Client for ChatGPT & Cloud Hub)
1157
+ connect_parser = subparsers.add_parser("connect", help="Connect this desktop to the Extra Cloud Hub for ChatGPT automation")
1158
+ connect_parser.add_argument("--hub", default=None, help="WebSocket Relay Hub URL (default: official extra.yantraos.com relay)")
1159
+ connect_parser.add_argument("--key", "--token", dest="token", default="anonymous", help="Setup key or authentication token from extra.yantraos.com account")
1160
+ connect_parser.add_argument("--name", default=None, help="Custom friendly device name for this workstation")
1161
+
1077
1162
  # pip (Picture-in-Picture floating preview window)
1078
1163
  pip_parser = subparsers.add_parser("pip", help="Control hardware-accelerated Picture-in-Picture floating preview window")
1079
1164
  pip_parser.add_argument("action", nargs="?", default="demo", choices=["demo", "start", "stop"], help="Action: demo (live 3s preview), start, stop")
@@ -1154,6 +1239,11 @@ def main() -> None:
1154
1239
  audit_parser.add_argument("--record", "-r", action="store_true", help="Record audited mission metrics into local impact_ledger.json")
1155
1240
  audit_parser.add_argument("--json", action="store_true", help="Output audit results in raw JSON")
1156
1241
 
1242
+ return parser
1243
+
1244
+
1245
+ def main() -> None:
1246
+ parser = build_parser()
1157
1247
  args = parser.parse_args()
1158
1248
 
1159
1249
  if not args.command:
@@ -1195,6 +1285,10 @@ def main() -> None:
1195
1285
  sys.exit(cmd_uninstall(args))
1196
1286
  elif args.command == "run":
1197
1287
  sys.exit(cmd_run(args))
1288
+ elif args.command == "serve":
1289
+ sys.exit(cmd_serve(args))
1290
+ elif args.command == "connect":
1291
+ sys.exit(cmd_connect(args))
1198
1292
  elif args.command == "pip":
1199
1293
  sys.exit(cmd_pip(args))
1200
1294
  elif args.command == "inspect":
@@ -242,19 +242,7 @@ class UIDistiller:
242
242
  except Exception as ex:
243
243
  logger.debug("Edge Vision fallback skipped: %s", ex)
244
244
 
245
- # Truncate to max_elements limit
246
- final_elements = filtered_elements[:max_elements]
247
-
248
- # 4. Spatially Sort (Top-to-Bottom, Left-to-Right by rows)
249
- sorted_elements = self._spatial_sort(final_elements)
250
-
251
- # Count interactives
252
- interactive_count = sum(
253
- 1 for el in sorted_elements
254
- if el.control_type in ACTION_CONTROL_TYPES or el.control_type in INPUT_CONTROL_TYPES
255
- )
256
-
257
- # 4.5 Discover High-Contrast Work Surface / Canvas Viewport (< 5ms)
245
+ # 4. Discover High-Contrast Work Surface / Custom Canvas Viewport (< 5ms)
258
246
  work_surface_dict = None
259
247
  if target_hwnd:
260
248
  try:
@@ -264,9 +252,51 @@ class UIDistiller:
264
252
  ws = self._vision_parser.detect_work_surface_in_window(target_hwnd)
265
253
  if ws:
266
254
  work_surface_dict = ws.to_dict()
255
+
256
+ # Universal Hybrid Canvas Vision:
257
+ # Detect opaque, custom-rendered surfaces (e.g. Qt/WebGL/DirectX canvas viewports,
258
+ # chat lists, artboards) where accessibility provides zero/minimal child elements.
259
+ ws_left, ws_top, ws_right, ws_bottom = ws.left, ws.top, ws.right, ws.bottom
260
+ elements_inside_ws = [
261
+ el for el in filtered_elements
262
+ if ws_left <= el.center[0] <= ws_right and ws_top <= el.center[1] <= ws_bottom
263
+ ]
264
+
265
+ # If the work surface spans a major area (>= 160x160 px) but has <= 2 accessibility controls inside it:
266
+ if ws.width >= 160 and ws.height >= 160 and len(elements_inside_ws) <= 2:
267
+ try:
268
+ canvas_crop = (ws_left, ws_top, ws_right, ws_bottom)
269
+ vision_canvas_elements = self._vision_parser.parse_window(
270
+ target_hwnd, max_elements=max_elements, crop_box=canvas_crop
271
+ )
272
+ if vision_canvas_elements:
273
+ existing_names = {
274
+ (el.name or "").lower().strip()
275
+ for el in filtered_elements if el.name
276
+ }
277
+ for vel in vision_canvas_elements:
278
+ vel_name = (vel.name or "").strip().lower()
279
+ if not vel_name or vel_name not in existing_names:
280
+ filtered_elements.append(vel)
281
+ if vel_name:
282
+ existing_names.add(vel_name)
283
+ except Exception as c_ex:
284
+ logger.debug("Canvas vision parsing skipped: %s", c_ex)
267
285
  except Exception as ex:
268
286
  logger.debug("Work surface viewport detection skipped: %s", ex)
269
287
 
288
+ # Truncate to max_elements limit
289
+ final_elements = filtered_elements[:max_elements]
290
+
291
+ # 4.5 Spatially Sort (Top-to-Bottom, Left-to-Right by rows)
292
+ sorted_elements = self._spatial_sort(final_elements)
293
+
294
+ # Count interactives
295
+ interactive_count = sum(
296
+ 1 for el in sorted_elements
297
+ if el.control_type in ACTION_CONTROL_TYPES or el.control_type in INPUT_CONTROL_TYPES
298
+ )
299
+
270
300
  # 5. Format Content
271
301
  fmt_clean = format.lower().strip()
272
302
  if fmt_clean == "compact":
@@ -400,6 +400,7 @@ BUILTIN_APP_FALLBACKS: Dict[str, Dict[str, Any]] = {
400
400
  },
401
401
  "blender": {"candidates": ["blender"], "type": "bin", "proc": "blender"},
402
402
  "vlc": {"candidates": ["vlc"], "type": "bin", "proc": "vlc"},
403
+ "canva": {"candidates": ["canva"], "type": "bin", "proc": "canva"},
403
404
  }
404
405
 
405
406
  # Compatibility dictionary mapping directly to target names
@@ -41,6 +41,9 @@ BUILTIN_MAC_APP_REGISTRY: Dict[str, Dict[str, str]] = {
41
41
  "photos": {"target": "Photos", "bundle": "/System/Applications/Photos.app", "proc": "Photos", "type": "app"},
42
42
  "code": {"target": "Visual Studio Code", "bundle": "/Applications/Visual Studio Code.app", "proc": "Code", "type": "app"},
43
43
  "vscode": {"target": "Visual Studio Code", "bundle": "/Applications/Visual Studio Code.app", "proc": "Code", "type": "app"},
44
+ "vlc": {"target": "VLC", "bundle": "/Applications/VLC.app", "proc": "VLC", "type": "app"},
45
+ "blender": {"target": "Blender", "bundle": "/Applications/Blender.app", "proc": "Blender", "type": "app"},
46
+ "canva": {"target": "Canva", "bundle": "/Applications/Canva.app", "proc": "Canva", "type": "app"},
44
47
  }
45
48
 
46
49
  MAC_APP_REGISTRY: Dict[str, Dict[str, str]] = dict(BUILTIN_MAC_APP_REGISTRY)
@@ -211,52 +211,71 @@ def _send_inputs(inputs: List[INPUT]) -> int:
211
211
  return int(user32.SendInput(n, arr, ctypes.sizeof(INPUT)))
212
212
 
213
213
 
214
+ def _release_stuck_modifiers() -> None:
215
+ """Releases any lingering modifier keys (Ctrl, Alt, Shift, Win) to avoid keystroke corruption."""
216
+ mods = (
217
+ win32con.VK_CONTROL, win32con.VK_LCONTROL, win32con.VK_RCONTROL,
218
+ win32con.VK_MENU, win32con.VK_LMENU, win32con.VK_RMENU,
219
+ win32con.VK_SHIFT, win32con.VK_LSHIFT, win32con.VK_RSHIFT,
220
+ win32con.VK_LWIN, win32con.VK_RWIN,
221
+ )
222
+ for vk in mods:
223
+ if user32.GetAsyncKeyState(vk) & 0x8000:
224
+ ku = INPUT(type=INPUT_KEYBOARD)
225
+ ku.ki = KEYBDINPUT(wVk=vk, wScan=0, dwFlags=KEYEVENTF_KEYUP, time=0, dwExtraInfo=0)
226
+ _send_inputs([ku])
227
+
228
+
214
229
  def instant_type(text: str, press_enter: bool = False) -> None:
215
230
  """
216
- Injects Unicode strings directly into the Windows input queue in sub-5ms.
231
+ Injects Unicode strings directly into the Windows input queue with atomic hardware delivery.
217
232
  Uses Win32 KEYEVENTF_UNICODE with UTF-16 code units (supporting surrogate pairs for emojis).
233
+ Batches KeyDown and KeyUp pairs atomically to prevent WinUI 3 input buffer desynchronization.
234
+ Translates newlines to physical Enter keystrokes to ensure 100% fidelity across native editors.
218
235
  """
219
236
  if not text and not press_enter:
220
237
  return
221
238
 
222
- inputs: List[INPUT] = []
239
+ _release_stuck_modifiers()
223
240
 
224
- # Encode to UTF-16-LE words to cleanly capture surrogate pairs (e.g., emojis)
225
- utf16_bytes = text.encode("utf-16-le")
226
- code_units = [
227
- int.from_bytes(utf16_bytes[i : i + 2], "little")
228
- for i in range(0, len(utf16_bytes), 2)
229
- ]
241
+ # Normalize line endings to avoid duplicate carriage returns
242
+ clean_text = text.replace("\r\n", "\n").replace("\r", "\n")
230
243
 
231
- for unit in code_units:
232
- # Key down
233
- kd = INPUT(type=INPUT_KEYBOARD)
234
- kd.ki = KEYBDINPUT(
235
- wVk=0,
236
- wScan=unit,
237
- dwFlags=KEYEVENTF_UNICODE,
238
- time=0,
239
- dwExtraInfo=0,
240
- )
241
- # Key up
242
- ku = INPUT(type=INPUT_KEYBOARD)
243
- ku.ki = KEYBDINPUT(
244
- wVk=0,
245
- wScan=unit,
246
- dwFlags=KEYEVENTF_UNICODE | KEYEVENTF_KEYUP,
247
- time=0,
248
- dwExtraInfo=0,
249
- )
250
- inputs.extend([kd, ku])
244
+ for ch in clean_text:
245
+ if ch == "\n":
246
+ # Translate newline to physical Enter keystroke for native Win32 & WinUI edit controls
247
+ kd_enter = INPUT(type=INPUT_KEYBOARD)
248
+ kd_enter.ki = KEYBDINPUT(wVk=win32con.VK_RETURN, wScan=0, dwFlags=0, time=0, dwExtraInfo=0)
249
+ ku_enter = INPUT(type=INPUT_KEYBOARD)
250
+ ku_enter.ki = KEYBDINPUT(wVk=win32con.VK_RETURN, wScan=0, dwFlags=KEYEVENTF_KEYUP, time=0, dwExtraInfo=0)
251
+ _send_inputs([kd_enter, ku_enter])
252
+ time.sleep(0.003)
253
+ continue
254
+
255
+ # Encode character to UTF-16-LE words to capture surrogate pairs
256
+ utf16_bytes = ch.encode("utf-16-le")
257
+ code_units = [
258
+ int.from_bytes(utf16_bytes[i : i + 2], "little")
259
+ for i in range(0, len(utf16_bytes), 2)
260
+ ]
261
+
262
+ for unit in code_units:
263
+ kd = INPUT(type=INPUT_KEYBOARD)
264
+ kd.ki = KEYBDINPUT(wVk=0, wScan=unit, dwFlags=KEYEVENTF_UNICODE, time=0, dwExtraInfo=0)
265
+ ku = INPUT(type=INPUT_KEYBOARD)
266
+ ku.ki = KEYBDINPUT(wVk=0, wScan=unit, dwFlags=KEYEVENTF_UNICODE | KEYEVENTF_KEYUP, time=0, dwExtraInfo=0)
267
+ # Dispatch KeyDown and KeyUp atomically together in a single OS SendInput batch
268
+ _send_inputs([kd, ku])
269
+ time.sleep(0.001)
251
270
 
252
271
  if press_enter:
272
+ time.sleep(0.005)
253
273
  kd_enter = INPUT(type=INPUT_KEYBOARD)
254
274
  kd_enter.ki = KEYBDINPUT(wVk=win32con.VK_RETURN, wScan=0, dwFlags=0, time=0, dwExtraInfo=0)
255
275
  ku_enter = INPUT(type=INPUT_KEYBOARD)
256
276
  ku_enter.ki = KEYBDINPUT(wVk=win32con.VK_RETURN, wScan=0, dwFlags=KEYEVENTF_KEYUP, time=0, dwExtraInfo=0)
257
- inputs.extend([kd_enter, ku_enter])
277
+ _send_inputs([kd_enter, ku_enter])
258
278
 
259
- _send_inputs(inputs)
260
279
  _notify_indicator("type")
261
280
 
262
281
 
@@ -63,6 +63,9 @@ BUILTIN_APP_REGISTRY: Dict[str, Dict[str, str]] = {
63
63
  "mspaint": {"target": "mspaint.exe", "type": "exe", "proc": "mspaint.exe"},
64
64
  "photos": {"target": "ms-photos:", "type": "uri", "proc": "Photos.exe"},
65
65
  "store": {"target": "ms-windows-store:", "type": "uri", "proc": "WinStore.App.exe"},
66
+ "vlc": {"target": "vlc.exe", "type": "exe", "proc": "vlc.exe"},
67
+ "blender": {"target": "blender.exe", "type": "exe", "proc": "blender.exe"},
68
+ "canva": {"target": "canva.exe", "type": "exe", "proc": "canva.exe"},
66
69
  }
67
70
 
68
71
  APP_REGISTRY: Dict[str, Dict[str, str]] = dict(BUILTIN_APP_REGISTRY)
@@ -511,12 +514,23 @@ def launch_app(
511
514
  if wait_for_window:
512
515
  deadline = time.perf_counter() + timeout
513
516
  expected_proc = APP_REGISTRY.get(clean_app, {}).get("proc", "")
517
+ EXCLUDED_SHELL_CLASSES = {
518
+ "progman", "workerw", "shell_traywnd", "shell_secondarytraywnd",
519
+ "windows.ui.core.corewindow", "dv2controlhost",
520
+ }
521
+ EXCLUDED_SHELL_TITLES = {"program manager", "", "taskbar", "start"}
514
522
 
515
523
  while time.perf_counter() < deadline:
516
524
  time.sleep(0.1)
517
525
  current_windows = list_windows(visible_only=True)
518
526
 
519
527
  for win in current_windows:
528
+ # Universal System Shell Filter: ignore desktop backdrops and taskbars
529
+ win_class = (getattr(win, "class_name", "") or "").lower()
530
+ win_title = (getattr(win, "title", "") or "").strip().lower()
531
+ if win_class in EXCLUDED_SHELL_CLASSES or win_title in EXCLUDED_SHELL_TITLES:
532
+ continue
533
+
520
534
  # Prioritize newly created window handles
521
535
  if win.hwnd not in existing_hwnds:
522
536
  matched_hwnd = win.hwnd
@@ -114,6 +114,7 @@ CONTROL_TYPE_NAMES: Dict[int, str] = {
114
114
  INTERACTIVE_TYPES: Set[str] = {
115
115
  "Button",
116
116
  "Edit",
117
+ "Document",
117
118
  "Hyperlink",
118
119
  "CheckBox",
119
120
  "RadioButton",
@@ -129,6 +130,7 @@ INTERACTIVE_TYPES: Set[str] = {
129
130
  UIA_INVOKE_PATTERN_ID = 10000
130
131
  UIA_TOGGLE_PATTERN_ID = 10015
131
132
  UIA_VALUE_PATTERN_ID = 10002
133
+ UIA_TEXT_PATTERN_ID = 10014
132
134
 
133
135
 
134
136
  class UIAutomationPlane(AbstractAccessibilityPlane):
@@ -240,6 +242,31 @@ class UIAutomationPlane(AbstractAccessibilityPlane):
240
242
  continue
241
243
 
242
244
  name = str(elem.CurrentName or "").strip()
245
+
246
+ # Fallback A: ValuePattern (for Edit / ComboBox controls with empty Name)
247
+ if not name:
248
+ try:
249
+ val_pat = elem.GetCurrentPattern(UIA_VALUE_PATTERN_ID)
250
+ if val_pat:
251
+ val_iface = val_pat.QueryInterface(self._mod.IUIAutomationValuePattern)
252
+ name = str(val_iface.CurrentValue or "").strip()
253
+ except Exception:
254
+ pass
255
+
256
+ # Fallback B: TextPattern (for Terminal buffers, PowerShell console, and rich text documents)
257
+ if not name and c_type_name in ("Document", "Text", "Edit"):
258
+ try:
259
+ txt_pat = elem.GetCurrentPattern(UIA_TEXT_PATTERN_ID)
260
+ if txt_pat:
261
+ txt_iface = txt_pat.QueryInterface(self._mod.IUIAutomationTextPattern)
262
+ doc_range = txt_iface.DocumentRange
263
+ full_txt = str(doc_range.GetText(300) or "").strip()
264
+ if full_txt:
265
+ lines = [ln.strip() for ln in full_txt.splitlines() if ln.strip()]
266
+ name = " \n ".join(lines[-3:]) if len(lines) > 3 else full_txt
267
+ except Exception:
268
+ pass
269
+
243
270
  auto_id = str(elem.CurrentAutomationId or "").strip()
244
271
  class_name = str(elem.CurrentClassName or "").strip()
245
272
  is_enabled = bool(elem.CurrentIsEnabled)