androidctl 0.1.0__tar.gz → 0.1.1__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 (198) hide show
  1. {androidctl-0.1.0 → androidctl-0.1.1}/PKG-INFO +9 -1
  2. {androidctl-0.1.0 → androidctl-0.1.1}/README.md +8 -0
  3. androidctl-0.1.1/VERSION +1 -0
  4. androidctl-0.1.1/androidctl/src/androidctl/_version.py +5 -0
  5. {androidctl-0.1.0 → androidctl-0.1.1}/androidctl/src/androidctl/daemon/owner.py +111 -19
  6. androidctl-0.1.1/androidctl/src/androidctl/resources/androidctl-agent-0.1.1-release.apk +0 -0
  7. {androidctl-0.1.0 → androidctl-0.1.1}/androidctl.egg-info/PKG-INFO +9 -1
  8. {androidctl-0.1.0 → androidctl-0.1.1}/androidctl.egg-info/SOURCES.txt +3 -1
  9. androidctl-0.1.1/androidctld/src/androidctld/_version.py +5 -0
  10. {androidctl-0.1.0 → androidctl-0.1.1}/androidctld/src/androidctld/commands/handlers/observe.py +1 -2
  11. {androidctl-0.1.0 → androidctl-0.1.1}/androidctld/src/androidctld/daemon/server.py +140 -8
  12. androidctl-0.1.1/contracts/src/androidctl_contracts/_version.py +5 -0
  13. androidctl-0.1.1/contracts/src/androidctl_contracts/_version_source.py +57 -0
  14. {androidctl-0.1.0 → androidctl-0.1.1}/pyproject.toml +4 -1
  15. androidctl-0.1.0/androidctl/src/androidctl/_version.py +0 -1
  16. androidctl-0.1.0/androidctl/src/androidctl/resources/androidctl-agent-0.1.0-release.apk +0 -0
  17. androidctl-0.1.0/androidctld/src/androidctld/_version.py +0 -1
  18. androidctl-0.1.0/contracts/src/androidctl_contracts/_version.py +0 -1
  19. {androidctl-0.1.0 → androidctl-0.1.1}/LICENSE +0 -0
  20. {androidctl-0.1.0 → androidctl-0.1.1}/androidctl/src/androidctl/__init__.py +0 -0
  21. {androidctl-0.1.0 → androidctl-0.1.1}/androidctl/src/androidctl/__main__.py +0 -0
  22. {androidctl-0.1.0 → androidctl-0.1.1}/androidctl/src/androidctl/app.py +0 -0
  23. {androidctl-0.1.0 → androidctl-0.1.1}/androidctl/src/androidctl/cli_options.py +0 -0
  24. {androidctl-0.1.0 → androidctl-0.1.1}/androidctl/src/androidctl/command_payloads.py +0 -0
  25. {androidctl-0.1.0 → androidctl-0.1.1}/androidctl/src/androidctl/command_views.py +0 -0
  26. {androidctl-0.1.0 → androidctl-0.1.1}/androidctl/src/androidctl/commands/__init__.py +0 -0
  27. {androidctl-0.1.0 → androidctl-0.1.1}/androidctl/src/androidctl/commands/actions.py +0 -0
  28. {androidctl-0.1.0 → androidctl-0.1.1}/androidctl/src/androidctl/commands/adb_wireless.py +0 -0
  29. {androidctl-0.1.0 → androidctl-0.1.1}/androidctl/src/androidctl/commands/close.py +0 -0
  30. {androidctl-0.1.0 → androidctl-0.1.1}/androidctl/src/androidctl/commands/connect.py +0 -0
  31. {androidctl-0.1.0 → androidctl-0.1.1}/androidctl/src/androidctl/commands/execute.py +0 -0
  32. {androidctl-0.1.0 → androidctl-0.1.1}/androidctl/src/androidctl/commands/list_apps.py +0 -0
  33. {androidctl-0.1.0 → androidctl-0.1.1}/androidctl/src/androidctl/commands/observe.py +0 -0
  34. {androidctl-0.1.0 → androidctl-0.1.1}/androidctl/src/androidctl/commands/open.py +0 -0
  35. {androidctl-0.1.0 → androidctl-0.1.1}/androidctl/src/androidctl/commands/plumbing.py +0 -0
  36. {androidctl-0.1.0 → androidctl-0.1.1}/androidctl/src/androidctl/commands/run_pipeline.py +0 -0
  37. {androidctl-0.1.0 → androidctl-0.1.1}/androidctl/src/androidctl/commands/screenshot.py +0 -0
  38. {androidctl-0.1.0 → androidctl-0.1.1}/androidctl/src/androidctl/commands/setup.py +0 -0
  39. {androidctl-0.1.0 → androidctl-0.1.1}/androidctl/src/androidctl/commands/wait.py +0 -0
  40. {androidctl-0.1.0 → androidctl-0.1.1}/androidctl/src/androidctl/daemon/__init__.py +0 -0
  41. {androidctl-0.1.0 → androidctl-0.1.1}/androidctl/src/androidctl/daemon/client.py +0 -0
  42. {androidctl-0.1.0 → androidctl-0.1.1}/androidctl/src/androidctl/daemon/discovery.py +0 -0
  43. {androidctl-0.1.0 → androidctl-0.1.1}/androidctl/src/androidctl/daemon/launcher.py +0 -0
  44. {androidctl-0.1.0 → androidctl-0.1.1}/androidctl/src/androidctl/errors/__init__.py +0 -0
  45. {androidctl-0.1.0 → androidctl-0.1.1}/androidctl/src/androidctl/errors/mapping.py +0 -0
  46. {androidctl-0.1.0 → androidctl-0.1.1}/androidctl/src/androidctl/errors/models.py +0 -0
  47. {androidctl-0.1.0 → androidctl-0.1.1}/androidctl/src/androidctl/exit_codes.py +0 -0
  48. {androidctl-0.1.0 → androidctl-0.1.1}/androidctl/src/androidctl/output.py +0 -0
  49. {androidctl-0.1.0 → androidctl-0.1.1}/androidctl/src/androidctl/parsing/__init__.py +0 -0
  50. {androidctl-0.1.0 → androidctl-0.1.1}/androidctl/src/androidctl/parsing/duration.py +0 -0
  51. {androidctl-0.1.0 → androidctl-0.1.1}/androidctl/src/androidctl/parsing/open_target.py +0 -0
  52. {androidctl-0.1.0 → androidctl-0.1.1}/androidctl/src/androidctl/parsing/refs.py +0 -0
  53. {androidctl-0.1.0 → androidctl-0.1.1}/androidctl/src/androidctl/parsing/screen_id.py +0 -0
  54. {androidctl-0.1.0 → androidctl-0.1.1}/androidctl/src/androidctl/parsing/wait.py +0 -0
  55. {androidctl-0.1.0 → androidctl-0.1.1}/androidctl/src/androidctl/renderers/__init__.py +0 -0
  56. {androidctl-0.1.0 → androidctl-0.1.1}/androidctl/src/androidctl/renderers/_paths.py +0 -0
  57. {androidctl-0.1.0 → androidctl-0.1.1}/androidctl/src/androidctl/renderers/xml.py +0 -0
  58. {androidctl-0.1.0 → androidctl-0.1.1}/androidctl/src/androidctl/renderers/xml_projection.py +0 -0
  59. {androidctl-0.1.0 → androidctl-0.1.1}/androidctl/src/androidctl/resources/__init__.py +0 -0
  60. {androidctl-0.1.0 → androidctl-0.1.1}/androidctl/src/androidctl/setup/__init__.py +0 -0
  61. {androidctl-0.1.0 → androidctl-0.1.1}/androidctl/src/androidctl/setup/accessibility.py +0 -0
  62. {androidctl-0.1.0 → androidctl-0.1.1}/androidctl/src/androidctl/setup/adb.py +0 -0
  63. {androidctl-0.1.0 → androidctl-0.1.1}/androidctl/src/androidctl/setup/apk_resource.py +0 -0
  64. {androidctl-0.1.0 → androidctl-0.1.1}/androidctl/src/androidctl/setup/pairing.py +0 -0
  65. {androidctl-0.1.0 → androidctl-0.1.1}/androidctl/src/androidctl/setup/verify.py +0 -0
  66. {androidctl-0.1.0 → androidctl-0.1.1}/androidctl/src/androidctl/workspace/__init__.py +0 -0
  67. {androidctl-0.1.0 → androidctl-0.1.1}/androidctl/src/androidctl/workspace/resolve.py +0 -0
  68. {androidctl-0.1.0 → androidctl-0.1.1}/androidctl.egg-info/dependency_links.txt +0 -0
  69. {androidctl-0.1.0 → androidctl-0.1.1}/androidctl.egg-info/entry_points.txt +0 -0
  70. {androidctl-0.1.0 → androidctl-0.1.1}/androidctl.egg-info/requires.txt +0 -0
  71. {androidctl-0.1.0 → androidctl-0.1.1}/androidctl.egg-info/top_level.txt +0 -0
  72. {androidctl-0.1.0 → androidctl-0.1.1}/androidctld/src/androidctld/__init__.py +0 -0
  73. {androidctl-0.1.0 → androidctl-0.1.1}/androidctld/src/androidctld/__main__.py +0 -0
  74. {androidctl-0.1.0 → androidctl-0.1.1}/androidctld/src/androidctld/actions/__init__.py +0 -0
  75. {androidctl-0.1.0 → androidctl-0.1.1}/androidctld/src/androidctld/actions/action_target.py +0 -0
  76. {androidctl-0.1.0 → androidctl-0.1.1}/androidctld/src/androidctld/actions/capabilities.py +0 -0
  77. {androidctl-0.1.0 → androidctl-0.1.1}/androidctld/src/androidctld/actions/executor.py +0 -0
  78. {androidctl-0.1.0 → androidctl-0.1.1}/androidctld/src/androidctld/actions/focus_confirmation.py +0 -0
  79. {androidctl-0.1.0 → androidctl-0.1.1}/androidctld/src/androidctld/actions/focused_input_admissibility.py +0 -0
  80. {androidctl-0.1.0 → androidctl-0.1.1}/androidctld/src/androidctld/actions/fresh_current.py +0 -0
  81. {androidctl-0.1.0 → androidctl-0.1.1}/androidctld/src/androidctld/actions/postconditions.py +0 -0
  82. {androidctl-0.1.0 → androidctl-0.1.1}/androidctld/src/androidctld/actions/repair.py +0 -0
  83. {androidctl-0.1.0 → androidctl-0.1.1}/androidctld/src/androidctld/actions/request_builder.py +0 -0
  84. {androidctl-0.1.0 → androidctl-0.1.1}/androidctld/src/androidctld/actions/settle.py +0 -0
  85. {androidctl-0.1.0 → androidctl-0.1.1}/androidctld/src/androidctld/actions/submit_confirmation.py +0 -0
  86. {androidctl-0.1.0 → androidctl-0.1.1}/androidctld/src/androidctld/actions/submit_routing.py +0 -0
  87. {androidctl-0.1.0 → androidctl-0.1.1}/androidctld/src/androidctld/actions/type_confirmation.py +0 -0
  88. {androidctl-0.1.0 → androidctl-0.1.1}/androidctld/src/androidctld/app_targets.py +0 -0
  89. {androidctl-0.1.0 → androidctl-0.1.1}/androidctld/src/androidctld/artifacts/__init__.py +0 -0
  90. {androidctl-0.1.0 → androidctl-0.1.1}/androidctld/src/androidctld/artifacts/models.py +0 -0
  91. {androidctl-0.1.0 → androidctl-0.1.1}/androidctld/src/androidctld/artifacts/screen_lookup.py +0 -0
  92. {androidctl-0.1.0 → androidctl-0.1.1}/androidctld/src/androidctld/artifacts/screen_payloads.py +0 -0
  93. {androidctl-0.1.0 → androidctl-0.1.1}/androidctld/src/androidctld/artifacts/writer.py +0 -0
  94. {androidctl-0.1.0 → androidctl-0.1.1}/androidctld/src/androidctld/auth/__init__.py +0 -0
  95. {androidctl-0.1.0 → androidctl-0.1.1}/androidctld/src/androidctld/auth/active_registry.py +0 -0
  96. {androidctl-0.1.0 → androidctl-0.1.1}/androidctld/src/androidctld/auth/secret_files.py +0 -0
  97. {androidctl-0.1.0 → androidctl-0.1.1}/androidctld/src/androidctld/auth/token_store.py +0 -0
  98. {androidctl-0.1.0 → androidctl-0.1.1}/androidctld/src/androidctld/commands/__init__.py +0 -0
  99. {androidctl-0.1.0 → androidctl-0.1.1}/androidctld/src/androidctld/commands/assembly.py +0 -0
  100. {androidctl-0.1.0 → androidctl-0.1.1}/androidctld/src/androidctld/commands/command_models.py +0 -0
  101. {androidctl-0.1.0 → androidctl-0.1.1}/androidctld/src/androidctld/commands/dispatch.py +0 -0
  102. {androidctl-0.1.0 → androidctl-0.1.1}/androidctld/src/androidctld/commands/executor.py +0 -0
  103. {androidctl-0.1.0 → androidctl-0.1.1}/androidctld/src/androidctld/commands/from_boundary.py +0 -0
  104. {androidctl-0.1.0 → androidctl-0.1.1}/androidctld/src/androidctld/commands/handlers/__init__.py +0 -0
  105. {androidctl-0.1.0 → androidctl-0.1.1}/androidctld/src/androidctld/commands/handlers/action.py +0 -0
  106. {androidctl-0.1.0 → androidctl-0.1.1}/androidctld/src/androidctld/commands/handlers/connect.py +0 -0
  107. {androidctl-0.1.0 → androidctl-0.1.1}/androidctld/src/androidctld/commands/handlers/list_apps.py +0 -0
  108. {androidctl-0.1.0 → androidctl-0.1.1}/androidctld/src/androidctld/commands/handlers/screenshot.py +0 -0
  109. {androidctl-0.1.0 → androidctl-0.1.1}/androidctld/src/androidctld/commands/handlers/wait.py +0 -0
  110. {androidctl-0.1.0 → androidctl-0.1.1}/androidctld/src/androidctld/commands/models.py +0 -0
  111. {androidctl-0.1.0 → androidctl-0.1.1}/androidctld/src/androidctld/commands/open_targets.py +0 -0
  112. {androidctl-0.1.0 → androidctl-0.1.1}/androidctld/src/androidctld/commands/orchestration.py +0 -0
  113. {androidctl-0.1.0 → androidctl-0.1.1}/androidctld/src/androidctld/commands/registry.py +0 -0
  114. {androidctl-0.1.0 → androidctl-0.1.1}/androidctld/src/androidctld/commands/result_builders.py +0 -0
  115. {androidctl-0.1.0 → androidctl-0.1.1}/androidctld/src/androidctld/commands/result_models.py +0 -0
  116. {androidctl-0.1.0 → androidctl-0.1.1}/androidctld/src/androidctld/commands/results.py +0 -0
  117. {androidctl-0.1.0 → androidctl-0.1.1}/androidctld/src/androidctld/commands/semantic_command_names.py +0 -0
  118. {androidctl-0.1.0 → androidctl-0.1.1}/androidctld/src/androidctld/commands/semantic_error_mapping.py +0 -0
  119. {androidctl-0.1.0 → androidctl-0.1.1}/androidctld/src/androidctld/commands/semantic_truth.py +0 -0
  120. {androidctl-0.1.0 → androidctl-0.1.1}/androidctld/src/androidctld/commands/service.py +0 -0
  121. {androidctl-0.1.0 → androidctl-0.1.1}/androidctld/src/androidctld/config.py +0 -0
  122. {androidctl-0.1.0 → androidctl-0.1.1}/androidctld/src/androidctld/daemon/__init__.py +0 -0
  123. {androidctl-0.1.0 → androidctl-0.1.1}/androidctld/src/androidctld/daemon/active_slot.py +0 -0
  124. {androidctl-0.1.0 → androidctl-0.1.1}/androidctld/src/androidctld/daemon/envelope.py +0 -0
  125. {androidctl-0.1.0 → androidctl-0.1.1}/androidctld/src/androidctld/daemon/http_host.py +0 -0
  126. {androidctl-0.1.0 → androidctl-0.1.1}/androidctld/src/androidctld/daemon/ingress.py +0 -0
  127. {androidctl-0.1.0 → androidctl-0.1.1}/androidctld/src/androidctld/daemon/ownership_probe.py +0 -0
  128. {androidctl-0.1.0 → androidctl-0.1.1}/androidctld/src/androidctld/daemon/service.py +0 -0
  129. {androidctl-0.1.0 → androidctl-0.1.1}/androidctld/src/androidctld/device/__init__.py +0 -0
  130. {androidctl-0.1.0 → androidctl-0.1.1}/androidctld/src/androidctld/device/action_models.py +0 -0
  131. {androidctl-0.1.0 → androidctl-0.1.1}/androidctld/src/androidctld/device/action_serialization.py +0 -0
  132. {androidctl-0.1.0 → androidctl-0.1.1}/androidctld/src/androidctld/device/adapters.py +0 -0
  133. {androidctl-0.1.0 → androidctl-0.1.1}/androidctld/src/androidctld/device/bootstrap.py +0 -0
  134. {androidctl-0.1.0 → androidctl-0.1.1}/androidctld/src/androidctld/device/connectors.py +0 -0
  135. {androidctl-0.1.0 → androidctl-0.1.1}/androidctld/src/androidctld/device/errors.py +0 -0
  136. {androidctl-0.1.0 → androidctl-0.1.1}/androidctld/src/androidctld/device/interfaces.py +0 -0
  137. {androidctl-0.1.0 → androidctl-0.1.1}/androidctld/src/androidctld/device/parsing.py +0 -0
  138. {androidctl-0.1.0 → androidctl-0.1.1}/androidctld/src/androidctld/device/rpc.py +0 -0
  139. {androidctl-0.1.0 → androidctl-0.1.1}/androidctld/src/androidctld/device/schema.py +0 -0
  140. {androidctl-0.1.0 → androidctl-0.1.1}/androidctld/src/androidctld/device/types.py +0 -0
  141. {androidctl-0.1.0 → androidctl-0.1.1}/androidctld/src/androidctld/errors/__init__.py +0 -0
  142. {androidctl-0.1.0 → androidctl-0.1.1}/androidctld/src/androidctld/logging/__init__.py +0 -0
  143. {androidctl-0.1.0 → androidctl-0.1.1}/androidctld/src/androidctld/observation.py +0 -0
  144. {androidctl-0.1.0 → androidctl-0.1.1}/androidctld/src/androidctld/protocol.py +0 -0
  145. {androidctl-0.1.0 → androidctl-0.1.1}/androidctld/src/androidctld/refs/__init__.py +0 -0
  146. {androidctl-0.1.0 → androidctl-0.1.1}/androidctld/src/androidctld/refs/models.py +0 -0
  147. {androidctl-0.1.0 → androidctl-0.1.1}/androidctld/src/androidctld/refs/repair.py +0 -0
  148. {androidctl-0.1.0 → androidctl-0.1.1}/androidctld/src/androidctld/refs/service.py +0 -0
  149. {androidctl-0.1.0 → androidctl-0.1.1}/androidctld/src/androidctld/rendering/__init__.py +0 -0
  150. {androidctl-0.1.0 → androidctl-0.1.1}/androidctld/src/androidctld/rendering/screen_xml.py +0 -0
  151. {androidctl-0.1.0 → androidctl-0.1.1}/androidctld/src/androidctld/runtime/__init__.py +0 -0
  152. {androidctl-0.1.0 → androidctl-0.1.1}/androidctld/src/androidctld/runtime/kernel.py +0 -0
  153. {androidctl-0.1.0 → androidctl-0.1.1}/androidctld/src/androidctld/runtime/lifecycle.py +0 -0
  154. {androidctl-0.1.0 → androidctl-0.1.1}/androidctld/src/androidctld/runtime/models.py +0 -0
  155. {androidctl-0.1.0 → androidctl-0.1.1}/androidctld/src/androidctld/runtime/screen_state.py +0 -0
  156. {androidctl-0.1.0 → androidctl-0.1.1}/androidctld/src/androidctld/runtime/state_repo.py +0 -0
  157. {androidctl-0.1.0 → androidctl-0.1.1}/androidctld/src/androidctld/runtime/store.py +0 -0
  158. {androidctl-0.1.0 → androidctl-0.1.1}/androidctld/src/androidctld/runtime_policy.py +0 -0
  159. {androidctl-0.1.0 → androidctl-0.1.1}/androidctld/src/androidctld/schema/__init__.py +0 -0
  160. {androidctl-0.1.0 → androidctl-0.1.1}/androidctld/src/androidctld/schema/base.py +0 -0
  161. {androidctl-0.1.0 → androidctl-0.1.1}/androidctld/src/androidctld/schema/core.py +0 -0
  162. {androidctl-0.1.0 → androidctl-0.1.1}/androidctld/src/androidctld/schema/daemon_api.py +0 -0
  163. {androidctl-0.1.0 → androidctl-0.1.1}/androidctld/src/androidctld/schema/persistence.py +0 -0
  164. {androidctl-0.1.0 → androidctl-0.1.1}/androidctld/src/androidctld/schema/persistence_io.py +0 -0
  165. {androidctl-0.1.0 → androidctl-0.1.1}/androidctld/src/androidctld/schema/validation_errors.py +0 -0
  166. {androidctl-0.1.0 → androidctl-0.1.1}/androidctld/src/androidctld/semantics/__init__.py +0 -0
  167. {androidctl-0.1.0 → androidctl-0.1.1}/androidctld/src/androidctld/semantics/compiler.py +0 -0
  168. {androidctl-0.1.0 → androidctl-0.1.1}/androidctld/src/androidctld/semantics/continuity.py +0 -0
  169. {androidctl-0.1.0 → androidctl-0.1.1}/androidctld/src/androidctld/semantics/labels.py +0 -0
  170. {androidctl-0.1.0 → androidctl-0.1.1}/androidctld/src/androidctld/semantics/models.py +0 -0
  171. {androidctl-0.1.0 → androidctl-0.1.1}/androidctld/src/androidctld/semantics/policy.py +0 -0
  172. {androidctl-0.1.0 → androidctl-0.1.1}/androidctld/src/androidctld/semantics/public_models.py +0 -0
  173. {androidctl-0.1.0 → androidctl-0.1.1}/androidctld/src/androidctld/semantics/registries.py +0 -0
  174. {androidctl-0.1.0 → androidctl-0.1.1}/androidctld/src/androidctld/semantics/submit_refs.py +0 -0
  175. {androidctl-0.1.0 → androidctl-0.1.1}/androidctld/src/androidctld/semantics/surface.py +0 -0
  176. {androidctl-0.1.0 → androidctl-0.1.1}/androidctld/src/androidctld/semantics/targets.py +0 -0
  177. {androidctl-0.1.0 → androidctl-0.1.1}/androidctld/src/androidctld/snapshots/__init__.py +0 -0
  178. {androidctl-0.1.0 → androidctl-0.1.1}/androidctld/src/androidctld/snapshots/models.py +0 -0
  179. {androidctl-0.1.0 → androidctl-0.1.1}/androidctld/src/androidctld/snapshots/refresh.py +0 -0
  180. {androidctl-0.1.0 → androidctl-0.1.1}/androidctld/src/androidctld/snapshots/schema.py +0 -0
  181. {androidctl-0.1.0 → androidctl-0.1.1}/androidctld/src/androidctld/snapshots/service.py +0 -0
  182. {androidctl-0.1.0 → androidctl-0.1.1}/androidctld/src/androidctld/text_equivalence.py +0 -0
  183. {androidctl-0.1.0 → androidctl-0.1.1}/androidctld/src/androidctld/waits/__init__.py +0 -0
  184. {androidctl-0.1.0 → androidctl-0.1.1}/androidctld/src/androidctld/waits/evaluators.py +0 -0
  185. {androidctl-0.1.0 → androidctl-0.1.1}/androidctld/src/androidctld/waits/loop.py +0 -0
  186. {androidctl-0.1.0 → androidctl-0.1.1}/androidctld/src/androidctld/waits/matcher.py +0 -0
  187. {androidctl-0.1.0 → androidctl-0.1.1}/contracts/src/androidctl_contracts/__init__.py +0 -0
  188. {androidctl-0.1.0 → androidctl-0.1.1}/contracts/src/androidctl_contracts/_wire_helpers.py +0 -0
  189. {androidctl-0.1.0 → androidctl-0.1.1}/contracts/src/androidctl_contracts/base.py +0 -0
  190. {androidctl-0.1.0 → androidctl-0.1.1}/contracts/src/androidctl_contracts/command_catalog.py +0 -0
  191. {androidctl-0.1.0 → androidctl-0.1.1}/contracts/src/androidctl_contracts/command_results.py +0 -0
  192. {androidctl-0.1.0 → androidctl-0.1.1}/contracts/src/androidctl_contracts/daemon_api.py +0 -0
  193. {androidctl-0.1.0 → androidctl-0.1.1}/contracts/src/androidctl_contracts/errors.py +0 -0
  194. {androidctl-0.1.0 → androidctl-0.1.1}/contracts/src/androidctl_contracts/paths.py +0 -0
  195. {androidctl-0.1.0 → androidctl-0.1.1}/contracts/src/androidctl_contracts/public_screen.py +0 -0
  196. {androidctl-0.1.0 → androidctl-0.1.1}/contracts/src/androidctl_contracts/user_state.py +0 -0
  197. {androidctl-0.1.0 → androidctl-0.1.1}/contracts/src/androidctl_contracts/vocabulary.py +0 -0
  198. {androidctl-0.1.0 → androidctl-0.1.1}/setup.cfg +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: androidctl
3
- Version: 0.1.0
3
+ Version: 0.1.1
4
4
  Summary: A Playwright-like CLI for Android automation, built for AI agents.
5
5
  Author: Azure99
6
6
  License-Expression: GPL-3.0-or-later
@@ -64,6 +64,14 @@ androidctl observe
64
64
  - Android SDK platform tools, especially `adb`.
65
65
  - An Android 11+ device or emulator with USB debugging enabled.
66
66
 
67
+ ## Installing skills
68
+
69
+ For agents that support skills, ask your agent to install the AndroidCtl skill:
70
+
71
+ ```text
72
+ Install this skill: https://github.com/Azure99/androidctl/tree/main/skills/androidctl
73
+ ```
74
+
67
75
  ## Install From PyPI
68
76
 
69
77
  Install the released host tools with the single public distribution:
@@ -28,6 +28,14 @@ androidctl observe
28
28
  - Android SDK platform tools, especially `adb`.
29
29
  - An Android 11+ device or emulator with USB debugging enabled.
30
30
 
31
+ ## Installing skills
32
+
33
+ For agents that support skills, ask your agent to install the AndroidCtl skill:
34
+
35
+ ```text
36
+ Install this skill: https://github.com/Azure99/androidctl/tree/main/skills/androidctl
37
+ ```
38
+
31
39
  ## Install From PyPI
32
40
 
33
41
  Install the released host tools with the single public distribution:
@@ -0,0 +1 @@
1
+ 0.1.1
@@ -0,0 +1,5 @@
1
+ from __future__ import annotations
2
+
3
+ from androidctl_contracts._version_source import resolve_release_version
4
+
5
+ __version__ = resolve_release_version()
@@ -10,6 +10,7 @@ from pathlib import Path
10
10
  from typing import Any
11
11
 
12
12
  OWNER_ENV = "ANDROIDCTL_OWNER_ID"
13
+ WINDOWS_OWNER_ANCHOR_ENV = "ANDROIDCTL_OWNER_ANCHOR_PROCESSES"
13
14
  DEFAULT_OWNER_HINT = "Set ANDROIDCTL_OWNER_ID explicitly."
14
15
  _MAX_OWNER_PROCESS_HOPS = 64
15
16
  _SHELL_PROCESS_NAMES = frozenset(
@@ -18,6 +19,7 @@ _SHELL_PROCESS_NAMES = frozenset(
18
19
  _WINDOWS_SHELL_PROCESS_NAMES = frozenset(
19
20
  {"bash.exe", "cmd.exe", "powershell.exe", "pwsh.exe", "sh.exe"}
20
21
  )
22
+ _DEFAULT_WINDOWS_OWNER_ANCHOR_PROCESS_NAMES = frozenset({"claude.exe", "codex.exe"})
21
23
  _TH32CS_SNAPPROCESS = 0x00000002
22
24
  _PROCESS_QUERY_LIMITED_INFORMATION = 0x1000
23
25
  _ERROR_NO_MORE_FILES = 18
@@ -30,6 +32,19 @@ class _WindowsProcessInfo:
30
32
  process_name: str
31
33
 
32
34
 
35
+ @dataclass(frozen=True)
36
+ class _WindowsAncestorProcess:
37
+ pid: int
38
+ process_name: str
39
+
40
+
41
+ @dataclass(frozen=True)
42
+ class _WindowsOwnerTarget:
43
+ kind: str
44
+ pid: int
45
+ process_name: str
46
+
47
+
33
48
  class _WindowsFileTime(ctypes.Structure):
34
49
  _fields_ = [
35
50
  ("dwLowDateTime", wintypes.DWORD),
@@ -58,8 +73,8 @@ def derive_owner_id(*, env: Mapping[str, str]) -> str:
58
73
  candidate = configured.strip()
59
74
  if candidate:
60
75
  return candidate
61
- if sys.platform == "win32":
62
- owner_id = _derive_windows_owner_id()
76
+ if _is_windows_platform():
77
+ owner_id = _derive_windows_owner_id(env=env)
63
78
  if owner_id is None:
64
79
  raise ValueError(
65
80
  "Unable to derive a safe owner identity automatically. "
@@ -81,6 +96,10 @@ def derive_owner_id(*, env: Mapping[str, str]) -> str:
81
96
  return f"shell:{shell_pid}:{lifetime}"
82
97
 
83
98
 
99
+ def _is_windows_platform() -> bool:
100
+ return sys.platform == "win32"
101
+
102
+
84
103
  def _find_interactive_shell_ancestor_pid(env: Mapping[str, str]) -> int | None:
85
104
  del env
86
105
  current_pid = os.getpid()
@@ -108,39 +127,112 @@ def _find_interactive_shell_ancestor_pid(env: Mapping[str, str]) -> int | None:
108
127
  return None
109
128
 
110
129
 
111
- def _derive_windows_owner_id() -> str | None:
112
- shell_pid = _find_windows_shell_ancestor_pid()
113
- if shell_pid is None:
114
- return None
115
- lifetime = _read_windows_process_creation_filetime(shell_pid)
116
- if lifetime is None:
117
- return None
118
- return f"shell:win32:{shell_pid}:{lifetime}"
119
-
120
-
121
- def _find_windows_shell_ancestor_pid() -> int | None:
130
+ def _derive_windows_owner_id(*, env: Mapping[str, str]) -> str | None:
122
131
  process_table = _read_windows_process_table()
123
132
  if process_table is None:
124
133
  return None
134
+ for target in _find_windows_owner_targets(process_table, env=env):
135
+ lifetime = _read_windows_process_creation_filetime(target.pid)
136
+ if lifetime is None:
137
+ continue
138
+ if target.kind == "agent":
139
+ return f"agent:win32:{target.process_name}:{target.pid}:{lifetime}"
140
+ return f"shell:win32:{target.pid}:{lifetime}"
141
+ return None
142
+
143
+
144
+ def _find_windows_owner_targets(
145
+ process_table: dict[int, _WindowsProcessInfo],
146
+ *,
147
+ env: Mapping[str, str],
148
+ ) -> list[_WindowsOwnerTarget]:
149
+ anchor_names = _windows_owner_anchor_process_names(env)
150
+ agent_targets: list[_WindowsOwnerTarget] = []
151
+ shell_target: _WindowsOwnerTarget | None = None
152
+ for ancestor in _windows_ancestor_chain(process_table):
153
+ if ancestor.process_name in anchor_names:
154
+ agent_targets.append(
155
+ _WindowsOwnerTarget(
156
+ kind="agent",
157
+ pid=ancestor.pid,
158
+ process_name=ancestor.process_name,
159
+ )
160
+ )
161
+ if (
162
+ shell_target is None
163
+ and ancestor.process_name in _WINDOWS_SHELL_PROCESS_NAMES
164
+ ):
165
+ shell_target = _WindowsOwnerTarget(
166
+ kind="shell",
167
+ pid=ancestor.pid,
168
+ process_name=ancestor.process_name,
169
+ )
170
+ if shell_target is not None:
171
+ return [*agent_targets, shell_target]
172
+ return agent_targets
173
+
174
+
175
+ def _windows_ancestor_chain(
176
+ process_table: dict[int, _WindowsProcessInfo],
177
+ ) -> list[_WindowsAncestorProcess]:
125
178
  current_pid = os.getpid()
126
179
  current = process_table.get(current_pid)
127
180
  if current is None:
128
- return None
181
+ return []
129
182
  ancestor_pid = current.parent_pid
130
183
  seen: set[int] = {current_pid}
184
+ ancestors: list[_WindowsAncestorProcess] = []
131
185
  hops = 0
132
186
  while ancestor_pid > 0 and hops < _MAX_OWNER_PROCESS_HOPS:
133
187
  if ancestor_pid in seen:
134
- return None
188
+ return []
135
189
  seen.add(ancestor_pid)
136
190
  ancestor = process_table.get(ancestor_pid)
137
191
  if ancestor is None:
138
- return None
139
- process_name = _normalize_windows_process_name(ancestor.process_name)
140
- if process_name in _WINDOWS_SHELL_PROCESS_NAMES:
141
- return ancestor_pid
192
+ break
193
+ ancestors.append(
194
+ _WindowsAncestorProcess(
195
+ pid=ancestor_pid,
196
+ process_name=_normalize_windows_process_name(ancestor.process_name),
197
+ )
198
+ )
142
199
  ancestor_pid = ancestor.parent_pid
143
200
  hops += 1
201
+ return ancestors
202
+
203
+
204
+ def _windows_owner_anchor_process_names(env: Mapping[str, str]) -> set[str]:
205
+ process_names = set(_DEFAULT_WINDOWS_OWNER_ANCHOR_PROCESS_NAMES)
206
+ configured = env.get(WINDOWS_OWNER_ANCHOR_ENV)
207
+ if configured is None:
208
+ return process_names
209
+ for raw_name in configured.replace(";", ",").split(","):
210
+ normalized = _normalize_windows_process_name(raw_name)
211
+ if normalized:
212
+ process_names.add(normalized)
213
+ return process_names
214
+
215
+
216
+ def _find_windows_shell_ancestor_pid() -> int | None:
217
+ process_table = _read_windows_process_table()
218
+ if process_table is None:
219
+ return None
220
+ target = _find_windows_shell_ancestor(process_table)
221
+ if target is None:
222
+ return None
223
+ return target.pid
224
+
225
+
226
+ def _find_windows_shell_ancestor(
227
+ process_table: dict[int, _WindowsProcessInfo],
228
+ ) -> _WindowsOwnerTarget | None:
229
+ for ancestor in _windows_ancestor_chain(process_table):
230
+ if ancestor.process_name in _WINDOWS_SHELL_PROCESS_NAMES:
231
+ return _WindowsOwnerTarget(
232
+ kind="shell",
233
+ pid=ancestor.pid,
234
+ process_name=ancestor.process_name,
235
+ )
144
236
  return None
145
237
 
146
238
 
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: androidctl
3
- Version: 0.1.0
3
+ Version: 0.1.1
4
4
  Summary: A Playwright-like CLI for Android automation, built for AI agents.
5
5
  Author: Azure99
6
6
  License-Expression: GPL-3.0-or-later
@@ -64,6 +64,14 @@ androidctl observe
64
64
  - Android SDK platform tools, especially `adb`.
65
65
  - An Android 11+ device or emulator with USB debugging enabled.
66
66
 
67
+ ## Installing skills
68
+
69
+ For agents that support skills, ask your agent to install the AndroidCtl skill:
70
+
71
+ ```text
72
+ Install this skill: https://github.com/Azure99/androidctl/tree/main/skills/androidctl
73
+ ```
74
+
67
75
  ## Install From PyPI
68
76
 
69
77
  Install the released host tools with the single public distribution:
@@ -1,5 +1,6 @@
1
1
  LICENSE
2
2
  README.md
3
+ VERSION
3
4
  pyproject.toml
4
5
  androidctl.egg-info/PKG-INFO
5
6
  androidctl.egg-info/SOURCES.txt
@@ -49,7 +50,7 @@ androidctl/src/androidctl/renderers/_paths.py
49
50
  androidctl/src/androidctl/renderers/xml.py
50
51
  androidctl/src/androidctl/renderers/xml_projection.py
51
52
  androidctl/src/androidctl/resources/__init__.py
52
- androidctl/src/androidctl/resources/androidctl-agent-0.1.0-release.apk
53
+ androidctl/src/androidctl/resources/androidctl-agent-0.1.1-release.apk
53
54
  androidctl/src/androidctl/setup/__init__.py
54
55
  androidctl/src/androidctl/setup/accessibility.py
55
56
  androidctl/src/androidctl/setup/adb.py
@@ -178,6 +179,7 @@ androidctld/src/androidctld/waits/loop.py
178
179
  androidctld/src/androidctld/waits/matcher.py
179
180
  contracts/src/androidctl_contracts/__init__.py
180
181
  contracts/src/androidctl_contracts/_version.py
182
+ contracts/src/androidctl_contracts/_version_source.py
181
183
  contracts/src/androidctl_contracts/_wire_helpers.py
182
184
  contracts/src/androidctl_contracts/base.py
183
185
  contracts/src/androidctl_contracts/command_catalog.py
@@ -0,0 +1,5 @@
1
+ from __future__ import annotations
2
+
3
+ from androidctl_contracts._version_source import resolve_release_version
4
+
5
+ __version__ = resolve_release_version()
@@ -54,7 +54,6 @@ class ObserveCommandHandler:
54
54
  artifacts=None if source_basis is None else source_basis.artifacts,
55
55
  ).model_dump(by_alias=True, mode="json")
56
56
  lifecycle_lease = self._runtime_kernel.capture_lifecycle_lease(runtime)
57
- force_refresh = source_basis is None
58
57
  self._runtime_kernel.acquire_progress_lane(
59
58
  runtime,
60
59
  occupant_kind=command.kind.value,
@@ -62,7 +61,7 @@ class ObserveCommandHandler:
62
61
  try:
63
62
  snapshot = self._snapshot_service.fetch(
64
63
  runtime,
65
- force_refresh=force_refresh,
64
+ force_refresh=True,
66
65
  lifecycle_lease=lifecycle_lease,
67
66
  )
68
67
  previous_screen = (
@@ -2,6 +2,7 @@
2
2
 
3
3
  from __future__ import annotations
4
4
 
5
+ import errno
5
6
  import json
6
7
  import logging
7
8
  import threading
@@ -27,6 +28,18 @@ from androidctld.runtime_policy import (
27
28
  DAEMON_HTTP_SOCKET_TIMEOUT_SECONDS,
28
29
  )
29
30
 
31
+ _CLIENT_DISCONNECT_ERRNOS = {
32
+ errno.EPIPE,
33
+ errno.ECONNRESET,
34
+ errno.ECONNABORTED,
35
+ errno.ETIMEDOUT,
36
+ }
37
+ _CLIENT_DISCONNECT_WINERRORS = {
38
+ 10053, # WSAECONNABORTED
39
+ 10054, # WSAECONNRESET
40
+ 10058, # WSAESHUTDOWN
41
+ }
42
+
30
43
 
31
44
  class AndroidctldHttpServer:
32
45
  def __init__(
@@ -84,6 +97,7 @@ class AndroidctldHttpServer:
84
97
  and method == "POST"
85
98
  and path
86
99
  in {
100
+ "/health",
87
101
  "/runtime/get",
88
102
  "/runtime/close",
89
103
  "/commands/run",
@@ -176,24 +190,31 @@ class AndroidctldHttpServer:
176
190
  )
177
191
  if result.shutdown_after_write:
178
192
  self._enter_closing_gate()
193
+ self._retire_active_slot_for_close()
179
194
  try:
180
- self._write_json(
195
+ self._try_write_json_response(
181
196
  handler,
182
197
  result.status_code,
183
198
  success_envelope(result.payload),
199
+ response_kind="close_success",
184
200
  )
185
- except OSError:
186
- self._logger.info("close response write failed", exc_info=True)
187
201
  finally:
188
202
  self._request_shutdown_after_close()
189
203
  return
190
- self._write_json(
204
+ if not self._try_write_json_response(
191
205
  handler,
192
206
  result.status_code,
193
207
  success_envelope(result.payload),
194
- )
208
+ response_kind="success",
209
+ ):
210
+ return
195
211
  except DaemonError as error:
196
- self._write_json(handler, error.http_status, error_envelope(error))
212
+ self._try_write_json_response(
213
+ handler,
214
+ error.http_status,
215
+ error_envelope(error),
216
+ response_kind="daemon_error",
217
+ )
197
218
  except Exception: # pragma: no cover - defensive fallback
198
219
  self._logger.exception("unexpected daemon failure")
199
220
  daemon_error = DaemonError(
@@ -203,8 +224,11 @@ class AndroidctldHttpServer:
203
224
  details={},
204
225
  http_status=500,
205
226
  )
206
- self._write_json(
207
- handler, daemon_error.http_status, error_envelope(daemon_error)
227
+ self._try_write_json_response(
228
+ handler,
229
+ daemon_error.http_status,
230
+ error_envelope(daemon_error),
231
+ response_kind="internal_error",
208
232
  )
209
233
 
210
234
  def _enter_closing_gate(self) -> None:
@@ -212,6 +236,22 @@ class AndroidctldHttpServer:
212
236
  return
213
237
  self._closing = True
214
238
 
239
+ def _retire_active_slot_for_close(self) -> None:
240
+ try:
241
+ self._active_slot.clear_record()
242
+ except Exception:
243
+ self._logger.warning(
244
+ "failed to clear active daemon record while closing",
245
+ exc_info=True,
246
+ )
247
+ try:
248
+ self._active_slot.release_owner()
249
+ except Exception:
250
+ self._logger.warning(
251
+ "failed to release active daemon owner lock while closing",
252
+ exc_info=True,
253
+ )
254
+
215
255
  def _request_shutdown_after_close(self) -> None:
216
256
  if self._shutdown_after_close_requested:
217
257
  return
@@ -284,3 +324,95 @@ class AndroidctldHttpServer:
284
324
  handler.send_header("Content-Length", str(len(body)))
285
325
  handler.end_headers()
286
326
  handler.wfile.write(body)
327
+
328
+ def _try_write_json_response(
329
+ self,
330
+ handler: BaseHTTPRequestHandler,
331
+ status_code: int,
332
+ payload: dict[str, Any],
333
+ *,
334
+ response_kind: str,
335
+ ) -> bool:
336
+ try:
337
+ self._write_json(handler, status_code, payload)
338
+ return True
339
+ except OSError as error:
340
+ if _is_response_write_client_disconnect(error):
341
+ self._log_client_disconnect(
342
+ handler,
343
+ status_code=status_code,
344
+ response_kind=response_kind,
345
+ error=error,
346
+ )
347
+ handler.close_connection = True
348
+ return False
349
+ self._log_response_write_failure(
350
+ handler,
351
+ status_code=status_code,
352
+ response_kind=response_kind,
353
+ error=error,
354
+ )
355
+ handler.close_connection = True
356
+ return False
357
+
358
+ def _log_client_disconnect(
359
+ self,
360
+ handler: BaseHTTPRequestHandler,
361
+ *,
362
+ status_code: int,
363
+ response_kind: str,
364
+ error: OSError,
365
+ ) -> None:
366
+ self._logger.info(
367
+ "client disconnected before response write completed "
368
+ "method=%s path=%s status=%s response=%s exception=%s",
369
+ _handler_method(handler),
370
+ _handler_path(handler),
371
+ status_code,
372
+ response_kind,
373
+ type(error).__name__,
374
+ )
375
+
376
+ def _log_response_write_failure(
377
+ self,
378
+ handler: BaseHTTPRequestHandler,
379
+ *,
380
+ status_code: int,
381
+ response_kind: str,
382
+ error: OSError,
383
+ ) -> None:
384
+ self._logger.warning(
385
+ "response write failed method=%s path=%s status=%s response=%s "
386
+ "exception=%s",
387
+ _handler_method(handler),
388
+ _handler_path(handler),
389
+ status_code,
390
+ response_kind,
391
+ type(error).__name__,
392
+ exc_info=True,
393
+ )
394
+
395
+
396
+ def _is_response_write_client_disconnect(error: OSError) -> bool:
397
+ if isinstance(
398
+ error,
399
+ (
400
+ BrokenPipeError,
401
+ ConnectionAbortedError,
402
+ ConnectionResetError,
403
+ TimeoutError,
404
+ ),
405
+ ):
406
+ return True
407
+ if error.errno in _CLIENT_DISCONNECT_ERRNOS:
408
+ return True
409
+ winerror = getattr(error, "winerror", None)
410
+ return winerror in _CLIENT_DISCONNECT_WINERRORS
411
+
412
+
413
+ def _handler_method(handler: BaseHTTPRequestHandler) -> str:
414
+ return str(getattr(handler, "command", "<unknown>"))
415
+
416
+
417
+ def _handler_path(handler: BaseHTTPRequestHandler) -> str:
418
+ return str(getattr(handler, "path", "<unknown>"))
@@ -0,0 +1,5 @@
1
+ from __future__ import annotations
2
+
3
+ from ._version_source import resolve_release_version
4
+
5
+ __version__ = resolve_release_version()
@@ -0,0 +1,57 @@
1
+ from __future__ import annotations
2
+
3
+ import re
4
+ from importlib.metadata import PackageNotFoundError
5
+ from importlib.metadata import version as distribution_version
6
+ from pathlib import Path
7
+
8
+ _DISTRIBUTION_NAME = "androidctl"
9
+ _VERSION_FILE_NAME = "VERSION"
10
+ _VERSION_PATTERN = re.compile(r"\d+\.\d+\.\d+\Z")
11
+
12
+
13
+ def resolve_release_version() -> str:
14
+ source_version = _read_source_version()
15
+ if source_version is not None:
16
+ return source_version
17
+ try:
18
+ return distribution_version(_DISTRIBUTION_NAME)
19
+ except PackageNotFoundError as error:
20
+ raise RuntimeError("unable to resolve androidctl release version") from error
21
+
22
+
23
+ def _read_source_version() -> str | None:
24
+ for candidate_root in Path(__file__).resolve().parents:
25
+ version_path = candidate_root / _VERSION_FILE_NAME
26
+ if not version_path.is_file() or not _looks_like_repo_root(candidate_root):
27
+ continue
28
+ return _parse_version_text(version_path.read_text(encoding="utf-8"))
29
+ return None
30
+
31
+
32
+ def _looks_like_repo_root(path: Path) -> bool:
33
+ return (
34
+ (path / "pyproject.toml").is_file()
35
+ and (path / "androidctl").is_dir()
36
+ and (path / "androidctld").is_dir()
37
+ and (path / "contracts").is_dir()
38
+ )
39
+
40
+
41
+ def _parse_version_text(raw_text: str) -> str:
42
+ if raw_text.endswith("\r\n"):
43
+ candidate = raw_text.removesuffix("\r\n")
44
+ elif raw_text.endswith("\n"):
45
+ candidate = raw_text.removesuffix("\n")
46
+ else:
47
+ candidate = raw_text
48
+ if (
49
+ "\n" in candidate
50
+ or "\r" in candidate
51
+ or _VERSION_PATTERN.fullmatch(candidate) is None
52
+ ):
53
+ raise RuntimeError(
54
+ "canonical VERSION must contain exactly MAJOR.MINOR.PATCH with "
55
+ "at most one terminating newline"
56
+ )
57
+ return candidate
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "androidctl"
7
- version = "0.1.0"
7
+ dynamic = ["version"]
8
8
  description = "A Playwright-like CLI for Android automation, built for AI agents."
9
9
  authors = [{ name = "Azure99" }]
10
10
  keywords = ["agent", "android", "adb", "automation", "cli", "accessibility"]
@@ -58,6 +58,9 @@ namespaces = false
58
58
  [tool.setuptools.package-data]
59
59
  "androidctl.resources" = ["*.apk"]
60
60
 
61
+ [tool.setuptools.dynamic]
62
+ version = { file = ["VERSION"] }
63
+
61
64
  [tool.pytest.ini_options]
62
65
  pythonpath = [
63
66
  "contracts/src",
@@ -1 +0,0 @@
1
- __version__ = "0.1.0"
@@ -1 +0,0 @@
1
- __version__ = "0.1.0"
@@ -1 +0,0 @@
1
- __version__ = "0.1.0"
File without changes