axilio 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 (262) hide show
  1. axilio-0.1.1/.github/workflows/publish.yml +80 -0
  2. axilio-0.1.1/.github/workflows/quality.yml +53 -0
  3. axilio-0.1.1/.github/workflows/regen.yml +171 -0
  4. axilio-0.1.1/.github/workflows/tag-on-merge.yml +67 -0
  5. axilio-0.1.1/.github/workflows/test.yml +24 -0
  6. axilio-0.1.1/.gitignore +32 -0
  7. axilio-0.1.1/PKG-INFO +128 -0
  8. axilio-0.1.1/README.md +113 -0
  9. axilio-0.1.1/pyproject.toml +86 -0
  10. axilio-0.1.1/scripts/regen.sh +71 -0
  11. axilio-0.1.1/specs/production/openapi.json +8123 -0
  12. axilio-0.1.1/specs/staging/openapi.json +8123 -0
  13. axilio-0.1.1/src/axilio/__init__.py +50 -0
  14. axilio-0.1.1/src/axilio/_errors.py +92 -0
  15. axilio-0.1.1/src/axilio/_generated/__init__.py +8 -0
  16. axilio-0.1.1/src/axilio/_generated/api/__init__.py +1 -0
  17. axilio-0.1.1/src/axilio/_generated/api/api_keys/__init__.py +1 -0
  18. axilio-0.1.1/src/axilio/_generated/api/api_keys/apikeys_create.py +171 -0
  19. axilio-0.1.1/src/axilio/_generated/api/api_keys/apikeys_delete.py +161 -0
  20. axilio-0.1.1/src/axilio/_generated/api/api_keys/apikeys_list_get.py +185 -0
  21. axilio-0.1.1/src/axilio/_generated/api/api_keys/apikeys_list_post.py +167 -0
  22. axilio-0.1.1/src/axilio/_generated/api/api_keys/apikeys_regenerate.py +165 -0
  23. axilio-0.1.1/src/axilio/_generated/api/auth/__init__.py +1 -0
  24. axilio-0.1.1/src/axilio/_generated/api/auth/user_sign_in.py +171 -0
  25. axilio-0.1.1/src/axilio/_generated/api/auth/user_sign_up.py +175 -0
  26. axilio-0.1.1/src/axilio/_generated/api/auth/user_validate_invite.py +189 -0
  27. axilio-0.1.1/src/axilio/_generated/api/auth/user_waitlist.py +171 -0
  28. axilio-0.1.1/src/axilio/_generated/api/billing/__init__.py +1 -0
  29. axilio-0.1.1/src/axilio/_generated/api/billing/billing_add_funds.py +167 -0
  30. axilio-0.1.1/src/axilio/_generated/api/billing/billing_cancel_downgrade.py +134 -0
  31. axilio-0.1.1/src/axilio/_generated/api/billing/billing_cancel_rental_subscriptions.py +171 -0
  32. axilio-0.1.1/src/axilio/_generated/api/billing/billing_cancel_subscription.py +134 -0
  33. axilio-0.1.1/src/axilio/_generated/api/billing/billing_create_checkout.py +167 -0
  34. axilio-0.1.1/src/axilio/_generated/api/billing/billing_create_rental_checkout.py +173 -0
  35. axilio-0.1.1/src/axilio/_generated/api/billing/billing_customer_portal.py +138 -0
  36. axilio-0.1.1/src/axilio/_generated/api/billing/billing_download_invoice.py +167 -0
  37. axilio-0.1.1/src/axilio/_generated/api/billing/billing_get_balance.py +134 -0
  38. axilio-0.1.1/src/axilio/_generated/api/billing/billing_get_history.py +271 -0
  39. axilio-0.1.1/src/axilio/_generated/api/billing/billing_get_rental_subscriptions.py +136 -0
  40. axilio-0.1.1/src/axilio/_generated/api/billing/billing_get_subscription.py +134 -0
  41. axilio-0.1.1/src/axilio/_generated/api/billing/billing_initiate_downgrade.py +167 -0
  42. axilio-0.1.1/src/axilio/_generated/api/billing/billing_renew_rental_subscriptions.py +171 -0
  43. axilio-0.1.1/src/axilio/_generated/api/billing/billing_sync_history.py +138 -0
  44. axilio-0.1.1/src/axilio/_generated/api/billing/billing_validate_plan_change.py +170 -0
  45. axilio-0.1.1/src/axilio/_generated/api/devices/__init__.py +1 -0
  46. axilio-0.1.1/src/axilio/_generated/api/devices/device_allocate.py +175 -0
  47. axilio-0.1.1/src/axilio/_generated/api/devices/device_allocation_status.py +193 -0
  48. axilio-0.1.1/src/axilio/_generated/api/devices/device_available.py +170 -0
  49. axilio-0.1.1/src/axilio/_generated/api/devices/device_available_by_location.py +138 -0
  50. axilio-0.1.1/src/axilio/_generated/api/devices/device_connect.py +175 -0
  51. axilio-0.1.1/src/axilio/_generated/api/devices/device_counts.py +170 -0
  52. axilio-0.1.1/src/axilio/_generated/api/devices/device_deallocate_user.py +174 -0
  53. axilio-0.1.1/src/axilio/_generated/api/devices/device_for_workflow.py +170 -0
  54. axilio-0.1.1/src/axilio/_generated/api/devices/device_get.py +165 -0
  55. axilio-0.1.1/src/axilio/_generated/api/devices/device_interaction.py +165 -0
  56. axilio-0.1.1/src/axilio/_generated/api/devices/device_my.py +170 -0
  57. axilio-0.1.1/src/axilio/_generated/api/devices/device_nickname.py +187 -0
  58. axilio-0.1.1/src/axilio/_generated/api/devices/device_supported_apps.py +185 -0
  59. axilio-0.1.1/src/axilio/_generated/api/devices/device_wipe.py +169 -0
  60. axilio-0.1.1/src/axilio/_generated/api/organizations/__init__.py +1 -0
  61. axilio-0.1.1/src/axilio/_generated/api/organizations/organization_add_member.py +187 -0
  62. axilio-0.1.1/src/axilio/_generated/api/organizations/organization_create.py +171 -0
  63. axilio-0.1.1/src/axilio/_generated/api/organizations/organization_create_invitation.py +187 -0
  64. axilio-0.1.1/src/axilio/_generated/api/organizations/organization_get.py +161 -0
  65. axilio-0.1.1/src/axilio/_generated/api/organizations/organization_list.py +134 -0
  66. axilio-0.1.1/src/axilio/_generated/api/organizations/organization_list_invitations.py +161 -0
  67. axilio-0.1.1/src/axilio/_generated/api/organizations/organization_list_members.py +161 -0
  68. axilio-0.1.1/src/axilio/_generated/api/organizations/organization_remove_member.py +179 -0
  69. axilio-0.1.1/src/axilio/_generated/api/organizations/organization_revoke_invitation.py +179 -0
  70. axilio-0.1.1/src/axilio/_generated/api/organizations/organization_update.py +187 -0
  71. axilio-0.1.1/src/axilio/_generated/api/organizations/organization_update_member_role.py +201 -0
  72. axilio-0.1.1/src/axilio/_generated/api/runs/__init__.py +1 -0
  73. axilio-0.1.1/src/axilio/_generated/api/runs/run_cancel.py +165 -0
  74. axilio-0.1.1/src/axilio/_generated/api/runs/run_create.py +191 -0
  75. axilio-0.1.1/src/axilio/_generated/api/runs/run_get_for_user.py +159 -0
  76. axilio-0.1.1/src/axilio/_generated/api/runs/run_list.py +171 -0
  77. axilio-0.1.1/src/axilio/_generated/api/runs/run_list_events.py +171 -0
  78. axilio-0.1.1/src/axilio/_generated/api/runs/run_list_historic.py +171 -0
  79. axilio-0.1.1/src/axilio/_generated/api/runs/run_stats.py +161 -0
  80. axilio-0.1.1/src/axilio/_generated/api/usage/__init__.py +1 -0
  81. axilio-0.1.1/src/axilio/_generated/api/usage/usage_get_metrics.py +235 -0
  82. axilio-0.1.1/src/axilio/_generated/api/usage/usage_list_sessions.py +175 -0
  83. axilio-0.1.1/src/axilio/_generated/api/usage/usage_post_metrics.py +167 -0
  84. axilio-0.1.1/src/axilio/_generated/api/user/__init__.py +1 -0
  85. axilio-0.1.1/src/axilio/_generated/api/user/user_delete_me.py +138 -0
  86. axilio-0.1.1/src/axilio/_generated/api/user/user_get_me.py +134 -0
  87. axilio-0.1.1/src/axilio/_generated/api/user/user_provision.py +142 -0
  88. axilio-0.1.1/src/axilio/_generated/api/user_settings/__init__.py +1 -0
  89. axilio-0.1.1/src/axilio/_generated/api/user_settings/user_settings_get.py +138 -0
  90. axilio-0.1.1/src/axilio/_generated/api/user_settings/user_settings_update.py +175 -0
  91. axilio-0.1.1/src/axilio/_generated/api/workflows/__init__.py +1 -0
  92. axilio-0.1.1/src/axilio/_generated/api/workflows/workflow_create.py +171 -0
  93. axilio-0.1.1/src/axilio/_generated/api/workflows/workflow_delete.py +161 -0
  94. axilio-0.1.1/src/axilio/_generated/api/workflows/workflow_get.py +165 -0
  95. axilio-0.1.1/src/axilio/_generated/api/workflows/workflow_get_code.py +165 -0
  96. axilio-0.1.1/src/axilio/_generated/api/workflows/workflow_get_revision.py +179 -0
  97. axilio-0.1.1/src/axilio/_generated/api/workflows/workflow_list_get.py +246 -0
  98. axilio-0.1.1/src/axilio/_generated/api/workflows/workflow_list_post.py +167 -0
  99. axilio-0.1.1/src/axilio/_generated/api/workflows/workflow_list_revisions.py +201 -0
  100. axilio-0.1.1/src/axilio/_generated/api/workflows/workflow_restore_revision.py +187 -0
  101. axilio-0.1.1/src/axilio/_generated/api/workflows/workflow_save_code.py +187 -0
  102. axilio-0.1.1/src/axilio/_generated/api/workflows/workflow_update.py +187 -0
  103. axilio-0.1.1/src/axilio/_generated/api/workflows/workflow_update_run.py +183 -0
  104. axilio-0.1.1/src/axilio/_generated/client.py +268 -0
  105. axilio-0.1.1/src/axilio/_generated/errors.py +16 -0
  106. axilio-0.1.1/src/axilio/_generated/models/__init__.py +257 -0
  107. axilio-0.1.1/src/axilio/_generated/models/apikey_api_key_create_request.py +53 -0
  108. axilio-0.1.1/src/axilio/_generated/models/apikey_api_key_create_response.py +79 -0
  109. axilio-0.1.1/src/axilio/_generated/models/apikey_api_key_list_item.py +95 -0
  110. axilio-0.1.1/src/axilio/_generated/models/apikey_api_key_list_request.py +146 -0
  111. axilio-0.1.1/src/axilio/_generated/models/apikey_api_key_list_response.py +110 -0
  112. axilio-0.1.1/src/axilio/_generated/models/apikey_api_key_regenerate_response.py +87 -0
  113. axilio-0.1.1/src/axilio/_generated/models/apikey_api_key_sort_spec.py +50 -0
  114. axilio-0.1.1/src/axilio/_generated/models/billing_history_billing_history_item.py +176 -0
  115. axilio-0.1.1/src/axilio/_generated/models/billing_history_billing_history_response.py +101 -0
  116. axilio-0.1.1/src/axilio/_generated/models/billing_history_invoice_download_request.py +53 -0
  117. axilio-0.1.1/src/axilio/_generated/models/billing_history_invoice_download_response.py +63 -0
  118. axilio-0.1.1/src/axilio/_generated/models/billing_sync_history_response.py +61 -0
  119. axilio-0.1.1/src/axilio/_generated/models/delete_api_key_output_body.py +53 -0
  120. axilio-0.1.1/src/axilio/_generated/models/device_allocate_device_request.py +70 -0
  121. axilio-0.1.1/src/axilio/_generated/models/device_allocate_device_response.py +88 -0
  122. axilio-0.1.1/src/axilio/_generated/models/device_allocation_status_response.py +115 -0
  123. axilio-0.1.1/src/axilio/_generated/models/device_available_devices_by_location_response.py +94 -0
  124. axilio-0.1.1/src/axilio/_generated/models/device_available_devices_response.py +101 -0
  125. axilio-0.1.1/src/axilio/_generated/models/device_connect_device_request.py +61 -0
  126. axilio-0.1.1/src/axilio/_generated/models/device_deallocate_device_response.py +87 -0
  127. axilio-0.1.1/src/axilio/_generated/models/device_device_app_summary.py +259 -0
  128. axilio-0.1.1/src/axilio/_generated/models/device_device_app_summary_app_metadata.py +46 -0
  129. axilio-0.1.1/src/axilio/_generated/models/device_device_counts_response.py +69 -0
  130. axilio-0.1.1/src/axilio/_generated/models/device_device_counts_response_android.py +46 -0
  131. axilio-0.1.1/src/axilio/_generated/models/device_device_counts_response_iphone.py +46 -0
  132. axilio-0.1.1/src/axilio/_generated/models/device_device_summary.py +456 -0
  133. axilio-0.1.1/src/axilio/_generated/models/device_device_summary_device_metadata.py +46 -0
  134. axilio-0.1.1/src/axilio/_generated/models/device_location_device_count.py +82 -0
  135. axilio-0.1.1/src/axilio/_generated/models/device_private_devices_response.py +94 -0
  136. axilio-0.1.1/src/axilio/_generated/models/device_rental_cancel_device_rental_subscriptions_request.py +72 -0
  137. axilio-0.1.1/src/axilio/_generated/models/device_rental_cancel_device_rental_subscriptions_response.py +121 -0
  138. axilio-0.1.1/src/axilio/_generated/models/device_rental_create_device_rental_checkout_request.py +53 -0
  139. axilio-0.1.1/src/axilio/_generated/models/device_rental_create_device_rental_checkout_response.py +53 -0
  140. axilio-0.1.1/src/axilio/_generated/models/device_rental_device_rental_subscription_list_response.py +90 -0
  141. axilio-0.1.1/src/axilio/_generated/models/device_rental_device_rental_subscription_response.py +161 -0
  142. axilio-0.1.1/src/axilio/_generated/models/device_rental_renew_device_rental_subscriptions_request.py +72 -0
  143. axilio-0.1.1/src/axilio/_generated/models/device_rental_renew_device_rental_subscriptions_response.py +121 -0
  144. axilio-0.1.1/src/axilio/_generated/models/device_success_response.py +53 -0
  145. axilio-0.1.1/src/axilio/_generated/models/device_supported_device_apps_response.py +94 -0
  146. axilio-0.1.1/src/axilio/_generated/models/device_update_nickname_request.py +53 -0
  147. axilio-0.1.1/src/axilio/_generated/models/message_output_body.py +53 -0
  148. axilio-0.1.1/src/axilio/_generated/models/organization_add_org_member_request.py +61 -0
  149. axilio-0.1.1/src/axilio/_generated/models/organization_create_invitation_request.py +70 -0
  150. axilio-0.1.1/src/axilio/_generated/models/organization_invitation_list_response.py +86 -0
  151. axilio-0.1.1/src/axilio/_generated/models/organization_invitation_response.py +121 -0
  152. axilio-0.1.1/src/axilio/_generated/models/organization_organization_create_request.py +78 -0
  153. axilio-0.1.1/src/axilio/_generated/models/organization_organization_list_response.py +88 -0
  154. axilio-0.1.1/src/axilio/_generated/models/organization_organization_member_list_response.py +86 -0
  155. axilio-0.1.1/src/axilio/_generated/models/organization_organization_member_response.py +113 -0
  156. axilio-0.1.1/src/axilio/_generated/models/organization_organization_response.py +128 -0
  157. axilio-0.1.1/src/axilio/_generated/models/organization_organization_update_request.py +69 -0
  158. axilio-0.1.1/src/axilio/_generated/models/organization_update_org_member_role_request.py +53 -0
  159. axilio-0.1.1/src/axilio/_generated/models/run_historic_run.py +166 -0
  160. axilio-0.1.1/src/axilio/_generated/models/run_historic_runs_request.py +128 -0
  161. axilio-0.1.1/src/axilio/_generated/models/run_historic_runs_response.py +109 -0
  162. axilio-0.1.1/src/axilio/_generated/models/run_run_config.py +86 -0
  163. axilio-0.1.1/src/axilio/_generated/models/run_run_config_variables_type_0_item.py +46 -0
  164. axilio-0.1.1/src/axilio/_generated/models/run_run_create_request.py +103 -0
  165. axilio-0.1.1/src/axilio/_generated/models/run_run_create_response.py +72 -0
  166. axilio-0.1.1/src/axilio/_generated/models/run_run_event_summary.py +69 -0
  167. axilio-0.1.1/src/axilio/_generated/models/run_run_events_request.py +100 -0
  168. axilio-0.1.1/src/axilio/_generated/models/run_run_events_response.py +110 -0
  169. axilio-0.1.1/src/axilio/_generated/models/run_run_list_request.py +186 -0
  170. axilio-0.1.1/src/axilio/_generated/models/run_run_list_response.py +109 -0
  171. axilio-0.1.1/src/axilio/_generated/models/run_run_response.py +270 -0
  172. axilio-0.1.1/src/axilio/_generated/models/run_run_response_run_metadata.py +46 -0
  173. axilio-0.1.1/src/axilio/_generated/models/run_run_response_variables.py +46 -0
  174. axilio-0.1.1/src/axilio/_generated/models/run_run_sort_spec.py +50 -0
  175. axilio-0.1.1/src/axilio/_generated/models/run_run_stats_response.py +61 -0
  176. axilio-0.1.1/src/axilio/_generated/models/run_run_time_config.py +69 -0
  177. axilio-0.1.1/src/axilio/_generated/models/run_success_response.py +53 -0
  178. axilio-0.1.1/src/axilio/_generated/models/subscription_add_funds_request.py +53 -0
  179. axilio-0.1.1/src/axilio/_generated/models/subscription_add_funds_response.py +53 -0
  180. axilio-0.1.1/src/axilio/_generated/models/subscription_balance_response.py +61 -0
  181. axilio-0.1.1/src/axilio/_generated/models/subscription_cancel_downgrade_response.py +53 -0
  182. axilio-0.1.1/src/axilio/_generated/models/subscription_cancel_subscription_response.py +63 -0
  183. axilio-0.1.1/src/axilio/_generated/models/subscription_create_checkout_session_request.py +61 -0
  184. axilio-0.1.1/src/axilio/_generated/models/subscription_create_checkout_session_response.py +53 -0
  185. axilio-0.1.1/src/axilio/_generated/models/subscription_customer_portal_response.py +53 -0
  186. axilio-0.1.1/src/axilio/_generated/models/subscription_downgrade_request.py +62 -0
  187. axilio-0.1.1/src/axilio/_generated/models/subscription_downgrade_response.py +71 -0
  188. axilio-0.1.1/src/axilio/_generated/models/subscription_downgrade_validation.py +163 -0
  189. axilio-0.1.1/src/axilio/_generated/models/subscription_limit_impact.py +74 -0
  190. axilio-0.1.1/src/axilio/_generated/models/subscription_subscription_response.py +248 -0
  191. axilio-0.1.1/src/axilio/_generated/models/usage_billing_session.py +169 -0
  192. axilio-0.1.1/src/axilio/_generated/models/usage_billing_session_session_metadata.py +46 -0
  193. axilio-0.1.1/src/axilio/_generated/models/usage_billing_sessions_request.py +235 -0
  194. axilio-0.1.1/src/axilio/_generated/models/usage_billing_sessions_response.py +109 -0
  195. axilio-0.1.1/src/axilio/_generated/models/usage_chart_data_point.py +68 -0
  196. axilio-0.1.1/src/axilio/_generated/models/usage_compute_minutes.py +90 -0
  197. axilio-0.1.1/src/axilio/_generated/models/usage_get_metrics_granularity.py +9 -0
  198. axilio-0.1.1/src/axilio/_generated/models/usage_infrastructure_costs.py +98 -0
  199. axilio-0.1.1/src/axilio/_generated/models/usage_session_sort_spec.py +50 -0
  200. axilio-0.1.1/src/axilio/_generated/models/usage_usage_metrics_request.py +79 -0
  201. axilio-0.1.1/src/axilio/_generated/models/usage_usage_metrics_response.py +95 -0
  202. axilio-0.1.1/src/axilio/_generated/models/user_auth_response.py +70 -0
  203. axilio-0.1.1/src/axilio/_generated/models/user_invite_code_validation_response.py +62 -0
  204. axilio-0.1.1/src/axilio/_generated/models/user_provision_response.py +62 -0
  205. axilio-0.1.1/src/axilio/_generated/models/user_settings_user_settings_response.py +60 -0
  206. axilio-0.1.1/src/axilio/_generated/models/user_settings_user_settings_update_request.py +60 -0
  207. axilio-0.1.1/src/axilio/_generated/models/user_settings_user_settings_update_response.py +61 -0
  208. axilio-0.1.1/src/axilio/_generated/models/user_sign_in_request.py +61 -0
  209. axilio-0.1.1/src/axilio/_generated/models/user_sign_up_request.py +86 -0
  210. axilio-0.1.1/src/axilio/_generated/models/user_user_response.py +93 -0
  211. axilio-0.1.1/src/axilio/_generated/models/user_waitlist_request.py +62 -0
  212. axilio-0.1.1/src/axilio/_generated/models/user_waitlist_response.py +61 -0
  213. axilio-0.1.1/src/axilio/_generated/models/v2_error_detail.py +60 -0
  214. axilio-0.1.1/src/axilio/_generated/models/v2_error_model.py +133 -0
  215. axilio-0.1.1/src/axilio/_generated/models/workflow_get_code_response.py +85 -0
  216. axilio-0.1.1/src/axilio/_generated/models/workflow_list_revisions_response.py +86 -0
  217. axilio-0.1.1/src/axilio/_generated/models/workflow_restore_revision_request.py +53 -0
  218. axilio-0.1.1/src/axilio/_generated/models/workflow_revision_detail.py +117 -0
  219. axilio-0.1.1/src/axilio/_generated/models/workflow_revision_summary.py +98 -0
  220. axilio-0.1.1/src/axilio/_generated/models/workflow_save_code_request.py +62 -0
  221. axilio-0.1.1/src/axilio/_generated/models/workflow_save_code_response.py +69 -0
  222. axilio-0.1.1/src/axilio/_generated/models/workflow_workflow_create_request.py +77 -0
  223. axilio-0.1.1/src/axilio/_generated/models/workflow_workflow_create_response.py +53 -0
  224. axilio-0.1.1/src/axilio/_generated/models/workflow_workflow_list_request.py +177 -0
  225. axilio-0.1.1/src/axilio/_generated/models/workflow_workflow_list_response.py +109 -0
  226. axilio-0.1.1/src/axilio/_generated/models/workflow_workflow_response.py +69 -0
  227. axilio-0.1.1/src/axilio/_generated/models/workflow_workflow_run_update_request.py +60 -0
  228. axilio-0.1.1/src/axilio/_generated/models/workflow_workflow_sort_spec.py +50 -0
  229. axilio-0.1.1/src/axilio/_generated/models/workflow_workflow_stats.py +50 -0
  230. axilio-0.1.1/src/axilio/_generated/models/workflow_workflow_summary.py +155 -0
  231. axilio-0.1.1/src/axilio/_generated/models/workflow_workflow_update_request.py +78 -0
  232. axilio-0.1.1/src/axilio/_generated/py.typed +1 -0
  233. axilio-0.1.1/src/axilio/_generated/types.py +54 -0
  234. axilio-0.1.1/src/axilio/_http.py +100 -0
  235. axilio-0.1.1/src/axilio/_mode.py +43 -0
  236. axilio-0.1.1/src/axilio/_resource.py +35 -0
  237. axilio-0.1.1/src/axilio/api_keys/__init__.py +8 -0
  238. axilio-0.1.1/src/axilio/api_keys/resource.py +33 -0
  239. axilio-0.1.1/src/axilio/api_keys/types.py +32 -0
  240. axilio-0.1.1/src/axilio/billing/__init__.py +11 -0
  241. axilio-0.1.1/src/axilio/billing/resource.py +45 -0
  242. axilio-0.1.1/src/axilio/billing/types.py +52 -0
  243. axilio-0.1.1/src/axilio/client.py +126 -0
  244. axilio-0.1.1/src/axilio/devices/__init__.py +37 -0
  245. axilio-0.1.1/src/axilio/devices/allocation.py +64 -0
  246. axilio-0.1.1/src/axilio/devices/device.py +200 -0
  247. axilio-0.1.1/src/axilio/devices/resource.py +89 -0
  248. axilio-0.1.1/src/axilio/devices/types.py +73 -0
  249. axilio-0.1.1/src/axilio/org/__init__.py +11 -0
  250. axilio-0.1.1/src/axilio/org/resource.py +29 -0
  251. axilio-0.1.1/src/axilio/org/types.py +45 -0
  252. axilio-0.1.1/src/axilio/runs/__init__.py +8 -0
  253. axilio-0.1.1/src/axilio/runs/resource.py +39 -0
  254. axilio-0.1.1/src/axilio/runs/types.py +46 -0
  255. axilio-0.1.1/src/axilio/usage/__init__.py +8 -0
  256. axilio-0.1.1/src/axilio/usage/resource.py +38 -0
  257. axilio-0.1.1/src/axilio/usage/types.py +45 -0
  258. axilio-0.1.1/src/axilio/workflows/__init__.py +8 -0
  259. axilio-0.1.1/src/axilio/workflows/resource.py +39 -0
  260. axilio-0.1.1/src/axilio/workflows/types.py +33 -0
  261. axilio-0.1.1/tests/test_client.py +124 -0
  262. axilio-0.1.1/uv.lock +245 -0
@@ -0,0 +1,80 @@
1
+ name: Publish to PyPI
2
+
3
+ # Fires when a backend/vX.Y.Z tag lands on the repo. Builds the wheel
4
+ # + sdist for that revision and uploads them to PyPI.
5
+ #
6
+ # Tag origin: the tag-on-merge.yml workflow creates the tag on every
7
+ # push to main that moves pyproject.toml's version (AXI-590). The
8
+ # version was itself stamped by the regen workflow from
9
+ # backend/internal/version/version.go via the spec — so by the time
10
+ # this workflow fires, SDK version, tag, and the backend revision
11
+ # that produced the spec are all in lockstep.
12
+ #
13
+ # Auth: `PYPI_API_TOKEN` repo secret. Generated at
14
+ # pypi.org/manage/account/token, scoped to the `axilio` project.
15
+ # The first publish (0.1.0) registers the project name on PyPI; once
16
+ # that's done, subsequent uploads inherit project-scope.
17
+ #
18
+ # If the secret is unset, the upload step fails — visible failure
19
+ # beats a silent skip that masks a broken publish path forever.
20
+
21
+ on:
22
+ push:
23
+ tags:
24
+ - 'backend/v*.*.*'
25
+
26
+ permissions:
27
+ contents: read
28
+
29
+ jobs:
30
+ publish:
31
+ name: Build + upload ${{ github.ref_name }}
32
+ runs-on: ubuntu-latest
33
+ environment: pypi
34
+ steps:
35
+ - name: Checkout tag
36
+ uses: actions/checkout@v4
37
+ with:
38
+ # The push event targets the tag itself; checkout@v4 reads
39
+ # `github.ref` by default which is `refs/tags/backend/vX.Y.Z`.
40
+ # No extra config needed — just verify.
41
+ ref: ${{ github.ref }}
42
+
43
+ - name: Set up Python
44
+ uses: actions/setup-python@v5
45
+ with:
46
+ python-version: '3.12'
47
+
48
+ - name: Verify tag version matches pyproject.toml
49
+ # Belt-and-braces: tag-on-merge.yml derives the tag FROM
50
+ # pyproject.toml, so they should always agree. If a future
51
+ # workflow change breaks that invariant we'd rather fail
52
+ # loudly here than ship a mis-versioned wheel.
53
+ run: |
54
+ # Tag is like `backend/v0.1.0`; strip the prefix.
55
+ TAG_VERSION="${GITHUB_REF_NAME#backend/v}"
56
+ FILE_VERSION=$(awk -F'"' '/^version = ".+"/{print $2; exit}' pyproject.toml)
57
+ if [[ "$TAG_VERSION" != "$FILE_VERSION" ]]; then
58
+ echo "::error::Tag version ($TAG_VERSION) does not match pyproject.toml ($FILE_VERSION)"
59
+ exit 1
60
+ fi
61
+ echo "tag $GITHUB_REF_NAME matches pyproject.toml $FILE_VERSION ✓"
62
+
63
+ - name: Install build tools
64
+ run: pip install --upgrade build twine
65
+
66
+ - name: Build wheel + sdist
67
+ run: python -m build
68
+
69
+ - name: Validate artifacts
70
+ run: twine check dist/*
71
+
72
+ - name: Upload to PyPI
73
+ env:
74
+ TWINE_USERNAME: __token__
75
+ TWINE_PASSWORD: ${{ secrets.PYPI_API_TOKEN }}
76
+ # twine returns nonzero if PyPI rejects (incl. duplicate file
77
+ # uploads — re-tagging the same version is operator error,
78
+ # not silent-success territory). Caller can re-run the
79
+ # workflow after fixing the underlying issue.
80
+ run: twine upload --non-interactive dist/*
@@ -0,0 +1,53 @@
1
+ name: Quality
2
+
3
+ # Black + ruff + mypy with versions pinned in pyproject.toml's [dev]
4
+ # extras. Versions match axilioai/axilio's python_checks.yml so a PR
5
+ # that's clean here is clean there and vice versa — bumping in one
6
+ # place without the other will drift; see AXI-588's ticket body for
7
+ # why we mirror manually rather than sharing a script.
8
+ #
9
+ # Generated code under src/axilio/_generated/ is excluded from all
10
+ # three tools via pyproject.toml — the regen workflow replaces it
11
+ # wholesale on each backend deploy, so any formatting opinions we
12
+ # inflict on it get reverted next regen.
13
+
14
+ on:
15
+ push:
16
+ branches: [main]
17
+ pull_request:
18
+
19
+ permissions:
20
+ contents: read
21
+
22
+ jobs:
23
+ quality:
24
+ name: Black + Ruff + MyPy
25
+ runs-on: ubuntu-latest
26
+ steps:
27
+ - uses: actions/checkout@v4
28
+
29
+ - name: Set up Python
30
+ uses: actions/setup-python@v5
31
+ with:
32
+ python-version: '3.12'
33
+ cache: pip
34
+ cache-dependency-path: pyproject.toml
35
+
36
+ - name: Install dev deps
37
+ run: pip install -e '.[dev]'
38
+
39
+ # No --auto-fix in CI; humans run the fixer locally and commit.
40
+ # All three checks run even if an earlier one fails (`if: always()`)
41
+ # so a single CI run surfaces every category at once instead of
42
+ # forcing the contributor to push-fix-push-fix once per tool.
43
+
44
+ - name: black --check
45
+ run: black --check src/axilio tests
46
+
47
+ - name: ruff check
48
+ if: always()
49
+ run: ruff check src/axilio tests
50
+
51
+ - name: mypy
52
+ if: always()
53
+ run: mypy src/axilio tests
@@ -0,0 +1,171 @@
1
+ name: SDK Regenerate
2
+
3
+ # Fires when the axilio monorepo's publish-sdk-spec job lands a new
4
+ # *production* spec. Steps:
5
+ #
6
+ # 1. Pull the just-committed specs/production/openapi.json.
7
+ # 2. Run scripts/regen.sh against it — replaces src/axilio/_generated/
8
+ # with the freshly regenerated package.
9
+ # 3. Stamp pyproject.toml's version field from info.version.
10
+ # 4. If anything moved, open a PR. Merging it tags + publishes
11
+ # via tag-on-merge.yml + publish.yml.
12
+ #
13
+ # Staging dispatches are intentionally ignored. The publish-sdk-spec
14
+ # job on axilio still PUTs specs/staging/openapi.json on each staging
15
+ # deploy for audit + visibility, but no regen PR opens — staging may
16
+ # carry endpoints prod doesn't have yet, and publishing a staging-
17
+ # driven SDK would give customers types for endpoints that 404 in
18
+ # production. The SDK should reflect what's actually live behind
19
+ # api.axilio.ai, which is the production deploy.
20
+ #
21
+ # Two triggers reach this workflow on each spec sync:
22
+ # - The Contents-API push from axilio commits a file under specs/**,
23
+ # which surfaces here as a normal push.
24
+ # - The explicit repository_dispatch openapi-spec-updated arrives
25
+ # immediately after, carrying axilio_sha for the regen PR.
26
+ #
27
+ # We listen for the dispatch only and ignore the push — the dispatch
28
+ # carries the upstream SHA we want in the PR title / body. Without it
29
+ # the PR would just say "spec changed" with no link back to which
30
+ # backend revision drove the change.
31
+
32
+ on:
33
+ repository_dispatch:
34
+ types: [openapi-spec-updated]
35
+ # Manual trigger only allows production. Local staging regen is
36
+ # `bash scripts/regen.sh staging` on a developer laptop — useful for
37
+ # eyeballing what's coming, doesn't produce a PR or publish.
38
+ workflow_dispatch: {}
39
+
40
+ permissions:
41
+ contents: write
42
+ pull-requests: write
43
+
44
+ jobs:
45
+ regenerate:
46
+ name: Regenerate production SDK
47
+ # Skip everything when the dispatch carries any environment other
48
+ # than production. Job-level `if` short-circuits before the rest
49
+ # of the workflow consumes any minutes.
50
+ if: |
51
+ github.event_name == 'workflow_dispatch' ||
52
+ github.event.client_payload.environment == 'production'
53
+ runs-on: ubuntu-latest
54
+ steps:
55
+ - uses: actions/checkout@v4
56
+
57
+ - name: Set up Python
58
+ uses: actions/setup-python@v5
59
+ with:
60
+ python-version: '3.12'
61
+
62
+ - name: Install dev deps
63
+ run: pip install -e '.[dev]'
64
+
65
+ - name: Regenerate SDK
66
+ run: bash scripts/regen.sh production
67
+
68
+ - name: Stamp pyproject.toml version from spec
69
+ id: stamp
70
+ # Reads info.version from specs/production/openapi.json (driven
71
+ # by backend/internal/version/version.go via axilio's wireRoutes
72
+ # — see AXI-589) and writes it into pyproject.toml so the SDK
73
+ # version matches the backend revision that produced the spec.
74
+ #
75
+ # Reads from the file rather than from client_payload.backend_version
76
+ # so manual workflow_dispatch runs work without needing the
77
+ # dispatch payload — the spec on disk is the source of truth.
78
+ run: |
79
+ BACKEND_VERSION=$(jq -r '.info.version' "specs/production/openapi.json")
80
+ if [[ "$BACKEND_VERSION" == "null" || -z "$BACKEND_VERSION" ]]; then
81
+ echo "::error::Could not read info.version from specs/production/openapi.json"
82
+ exit 1
83
+ fi
84
+ python3 - <<PY
85
+ import pathlib, re
86
+ v = "$BACKEND_VERSION"
87
+ p = pathlib.Path("pyproject.toml")
88
+ text = p.read_text()
89
+ # Use subn so we can tell "regex didn't match" (count == 0,
90
+ # an error — file lost its version line somehow) apart from
91
+ # "regex matched but replacement was a no-op" (count == 1,
92
+ # `new == text`, which is the common case where the spec's
93
+ # version equals what's already in pyproject.toml and there's
94
+ # nothing to do).
95
+ new, count = re.subn(r'^version = ".*"$', f'version = "{v}"', text, count=1, flags=re.MULTILINE)
96
+ if count == 0:
97
+ raise SystemExit("pyproject.toml has no top-level version line to stamp")
98
+ if new != text:
99
+ p.write_text(new)
100
+ PY
101
+ echo "backend_version=$BACKEND_VERSION" >> "$GITHUB_OUTPUT"
102
+ echo "Stamped pyproject.toml version = $BACKEND_VERSION"
103
+
104
+ - name: Detect changes
105
+ id: diff
106
+ # Open a PR if EITHER the generated code OR the stamped version
107
+ # moved. pyproject.toml-only changes happen when the backend's
108
+ # version constant bumped without altering the public surface
109
+ # (rare but possible — e.g. a meaningful internal refactor the
110
+ # engineer wanted to mark). Still worth a release.
111
+ run: |
112
+ if git diff --quiet src/axilio/_generated/ pyproject.toml; then
113
+ echo "Generated code + version unchanged; nothing to PR."
114
+ echo "changed=false" >> "$GITHUB_OUTPUT"
115
+ else
116
+ echo "Changes detected; opening PR."
117
+ echo "changed=true" >> "$GITHUB_OUTPUT"
118
+ fi
119
+
120
+ - name: Open regen PR
121
+ if: steps.diff.outputs.changed == 'true'
122
+ # peter-evans/create-pull-request finds an existing PR on the
123
+ # same branch and updates it instead of opening duplicates, so
124
+ # successive dispatches for the same backend version don't
125
+ # accumulate PRs. Branch name keys on the backend version
126
+ # because the merged regen PR will be tagged backend/vX.Y.Z
127
+ # and a second commit on top would tag the same version —
128
+ # confusing.
129
+ uses: peter-evans/create-pull-request@v6
130
+ env:
131
+ BACKEND_VERSION: ${{ steps.stamp.outputs.backend_version }}
132
+ AXILIO_SHA: ${{ github.event.client_payload.axilio_sha || 'manual' }}
133
+ AXILIO_REF: ${{ github.event.client_payload.axilio_ref || 'manual' }}
134
+ with:
135
+ branch: regen/v${{ env.BACKEND_VERSION }}
136
+ base: main
137
+ title: "chore(sdk): regenerate from backend v${{ env.BACKEND_VERSION }}"
138
+ commit-message: |
139
+ chore(sdk): regenerate from backend v${{ env.BACKEND_VERSION }}
140
+
141
+ Auto-generated by .github/workflows/regen.yml.
142
+ Source: axilio@${{ env.AXILIO_SHA }} (${{ env.AXILIO_REF }})
143
+ body: |
144
+ Auto-generated by the SDK regen pipeline. Merging this PR
145
+ tags `backend/v${{ env.BACKEND_VERSION }}` on main and
146
+ publishes that tag to PyPI via publish.yml.
147
+
148
+ | | |
149
+ |---|---|
150
+ | Backend version | `${{ env.BACKEND_VERSION }}` |
151
+ | Axilio source SHA | `${{ env.AXILIO_SHA }}` |
152
+ | Axilio source ref | `${{ env.AXILIO_REF }}` |
153
+ | Spec path | `specs/production/openapi.json` |
154
+ | Generator | `openapi-python-client` |
155
+
156
+ ## What changed
157
+
158
+ The production OpenAPI spec moved since the last regen.
159
+ `src/axilio/_generated/` and `pyproject.toml`'s version
160
+ field have been updated to match. Nothing hand-written
161
+ changed in this PR.
162
+
163
+ ## What still needs human attention
164
+
165
+ Resource methods in `src/axilio/billing/`,
166
+ `src/axilio/devices/`, etc. don't change automatically
167
+ when the spec moves — those are the hand-written wrappers
168
+ around the generated typed call functions. If this regen
169
+ surfaces new endpoints (or removes some), the corresponding
170
+ resource wrapper needs to be updated by hand in a
171
+ follow-up PR.
@@ -0,0 +1,67 @@
1
+ name: Tag on Version Bump
2
+
3
+ # Fires when a push to main moves pyproject.toml. Reads the version
4
+ # out of the file and creates an annotated `backend/vX.Y.Z` tag
5
+ # pointing at the merge commit.
6
+ #
7
+ # The receiver workflow (regen.yml) stamps pyproject.toml's version
8
+ # field to match the backend revision that produced the spec. Each
9
+ # merged regen PR is therefore a single deterministic version bump,
10
+ # and this workflow turns that into a tag — the PyPI publish workflow
11
+ # (AXI-591) hooks the tag to ship to PyPI.
12
+ #
13
+ # Idempotent: if a tag for the current version already exists, the
14
+ # workflow skips quietly. Important because someone might land a
15
+ # README-only PR that re-touches pyproject.toml's body without moving
16
+ # the version line — we don't want to fail those.
17
+
18
+ on:
19
+ push:
20
+ branches: [main]
21
+ paths:
22
+ - pyproject.toml
23
+
24
+ permissions:
25
+ contents: write
26
+
27
+ jobs:
28
+ tag:
29
+ name: Tag backend/vX.Y.Z
30
+ runs-on: ubuntu-latest
31
+ steps:
32
+ - uses: actions/checkout@v4
33
+ with:
34
+ fetch-depth: 0
35
+
36
+ - name: Read version from pyproject.toml
37
+ id: ver
38
+ run: |
39
+ # awk over a regex is enough for the single canonical
40
+ # `version = "..."` line. Avoids depending on tomllib /
41
+ # tomlkit and works on every runner Python.
42
+ VERSION=$(awk -F'"' '/^version = ".+"/{print $2; exit}' pyproject.toml)
43
+ if [[ -z "$VERSION" ]]; then
44
+ echo "::error::Could not read version from pyproject.toml"
45
+ exit 1
46
+ fi
47
+ echo "version=$VERSION" >> "$GITHUB_OUTPUT"
48
+ echo "Read version: $VERSION"
49
+
50
+ - name: Create + push tag (idempotent)
51
+ env:
52
+ VERSION: ${{ steps.ver.outputs.version }}
53
+ run: |
54
+ TAG="backend/v${VERSION}"
55
+
56
+ # Check remote first — local clone post-checkout may or may
57
+ # not have fetched all tags depending on the runner cache.
58
+ if git ls-remote --tags origin "refs/tags/${TAG}" | grep -q "${TAG}"; then
59
+ echo "tag ${TAG} already exists on origin; nothing to do"
60
+ exit 0
61
+ fi
62
+
63
+ git config user.name "axilio-bot"
64
+ git config user.email "bot@axilio.ai"
65
+ git tag -a "${TAG}" -m "Backend version ${VERSION}"
66
+ git push origin "${TAG}"
67
+ echo "tagged ${TAG} on $(git rev-parse HEAD)"
@@ -0,0 +1,24 @@
1
+ name: test
2
+
3
+ on:
4
+ push:
5
+ branches: [main]
6
+ pull_request:
7
+
8
+ jobs:
9
+ pytest:
10
+ runs-on: ubuntu-latest
11
+ strategy:
12
+ matrix:
13
+ python-version: ["3.10", "3.11", "3.12"]
14
+ steps:
15
+ - uses: actions/checkout@v4
16
+ - uses: actions/setup-python@v5
17
+ with:
18
+ python-version: ${{ matrix.python-version }}
19
+ - name: Install
20
+ run: |
21
+ python -m pip install --upgrade pip
22
+ python -m pip install -e '.[dev]'
23
+ - name: Test
24
+ run: pytest -q
@@ -0,0 +1,32 @@
1
+ __pycache__/
2
+ *.py[cod]
3
+ *$py.class
4
+
5
+ # Build / dist
6
+ build/
7
+ dist/
8
+ *.egg-info/
9
+ *.egg
10
+
11
+ # Test / coverage
12
+ .pytest_cache/
13
+ .coverage
14
+ .coverage.*
15
+ htmlcov/
16
+
17
+ # Environments
18
+ .venv/
19
+ venv/
20
+ env/
21
+
22
+ # Tooling
23
+ .mypy_cache/
24
+ .ruff_cache/
25
+
26
+ # OS
27
+ .DS_Store
28
+ Thumbs.db
29
+
30
+ # IDE
31
+ .idea/
32
+ .vscode/
axilio-0.1.1/PKG-INFO ADDED
@@ -0,0 +1,128 @@
1
+ Metadata-Version: 2.4
2
+ Name: axilio
3
+ Version: 0.1.1
4
+ Summary: Axilio Python SDK — manage workflows, runs, devices, usage, and control devices remotely
5
+ Requires-Python: >=3.10
6
+ Requires-Dist: httpx>=0.27
7
+ Provides-Extra: dev
8
+ Requires-Dist: black==25.1.0; extra == 'dev'
9
+ Requires-Dist: mypy==1.16.0; extra == 'dev'
10
+ Requires-Dist: openapi-python-client>=0.21; extra == 'dev'
11
+ Requires-Dist: pytest-httpx>=0.30.0; extra == 'dev'
12
+ Requires-Dist: pytest>=8.0.0; extra == 'dev'
13
+ Requires-Dist: ruff==0.12.1; extra == 'dev'
14
+ Description-Content-Type: text/markdown
15
+
16
+ # Axilio Python SDK
17
+
18
+ The official Python SDK for [Axilio](https://axilio.ai). One pip package covers account management, device discovery, allocation, and the full remote control surface (tap, swipe, OCR, screenshot, ...).
19
+
20
+ ## Installation
21
+
22
+ ```bash
23
+ pip install axilio
24
+ ```
25
+
26
+ ## Quick start
27
+
28
+ ```python
29
+ from axilio import Client
30
+
31
+ # Reads AXILIO_API_KEY from the environment.
32
+ client = Client()
33
+
34
+ # One-shot device session — allocate, connect, control, clean up.
35
+ with client.devices.session(platform="android") as device:
36
+ device.tap(540, 1200)
37
+ device.type("hello world")
38
+ device.screenshot("after.png")
39
+ ```
40
+
41
+ The same code runs unmodified inside an Axilio sandbox VM — when you schedule a workflow in the dashboard, Hephaestus injects scoped auth and the paired Atlas endpoint at boot, and the Client auto-detects sandbox mode. No `if local: ... else: ...` branches in your code.
42
+
43
+ ## Authentication
44
+
45
+ ```bash
46
+ export AXILIO_API_KEY=ax_live_...
47
+ ```
48
+
49
+ Or pass explicitly:
50
+
51
+ ```python
52
+ client = Client(api_key="ax_...")
53
+ client = Client(api_key="ax_...", base_url="https://staging-api.axilio.ai")
54
+ ```
55
+
56
+ Generate keys from the [Axilio dashboard](https://app.axilio.ai/settings/api-keys). API keys are scoped to one organization — multi-org users mint one key per org.
57
+
58
+ ## Resources
59
+
60
+ | Resource | Methods | Notes |
61
+ |---|---|---|
62
+ | `client.billing` | `balance()`, `subscription()`, `invoices()`, `upgrade()`, `downgrade()` | Reads everywhere; upgrade/downgrade local-only |
63
+ | `client.usage` | `metrics()`, `sessions()` | Both modes |
64
+ | `client.api_keys` | `list()`, `create()`, `revoke()` | Local-only |
65
+ | `client.org` | `current()`, `members()`, `invite()`, `remove_member()` | Reads everywhere; member writes local-only |
66
+ | `client.devices` | `available()`, `locations()`, `supported_apps()`, `allocate()`, `connect()`, `deallocate()`, `session()` | Sandbox mode short-circuits `allocate()` |
67
+ | `client.workflows` | `list()`, `get()`, `create()`, `update()`, `delete()` | Both modes |
68
+ | `client.runs` | `list()`, `get()`, `create()`, `cancel()` | Both modes |
69
+
70
+ For the full method surface, type signatures, and the device control vocabulary, see the [SDK design doc](https://github.com/axilioai/axilio/blob/main/.docs/sdk-design.md) in the monorepo.
71
+
72
+ ## Errors
73
+
74
+ All errors raised by the SDK subclass `axilio.AxilioError`:
75
+
76
+ ```python
77
+ from axilio import Client, NotFoundError, RateLimitError, SandboxPermissionError
78
+
79
+ client = Client()
80
+
81
+ try:
82
+ run = client.runs.get("run_does_not_exist")
83
+ except NotFoundError:
84
+ print("run not found")
85
+ except RateLimitError:
86
+ # backoff + retry
87
+ ...
88
+ except SandboxPermissionError:
89
+ # tried a local-only write from inside a sandbox VM
90
+ ...
91
+ ```
92
+
93
+ | Exception | HTTP | Notes |
94
+ |---|---|---|
95
+ | `UnauthorizedError` | 401 | API key missing, malformed, or rejected |
96
+ | `NotFoundError` | 404 | Resource doesn't exist or isn't visible |
97
+ | `RateLimitError` | 429 | Retry after backoff |
98
+ | `ServerError` | 5xx | Transient; safe to retry |
99
+ | `SandboxPermissionError` | — | Local-only operation attempted from sandbox |
100
+ | `AllocationMismatchError` | — | Sandbox kwargs don't match the bound allocation |
101
+ | `AxilioError` | other | Catch-all |
102
+
103
+ ## Configuration
104
+
105
+ | Env var | Description |
106
+ |---|---|
107
+ | `AXILIO_API_KEY` | API key for authentication. Required in local mode. |
108
+ | `AXILIO_BASE_URL` | Override the API host (defaults to `https://api.axilio.ai`). |
109
+ | `AXILIO_SANDBOX_TOKEN` | Scoped sandbox token. Injected by Hephaestus inside a sandbox VM; not set in local mode. |
110
+ | `AXILIO_ATLAS_ENDPOINT` | Paired Atlas endpoint inside a sandbox VM. Injected by Hephaestus. |
111
+
112
+ | Constructor kwarg | Default | Description |
113
+ |---|---|---|
114
+ | `api_key` | `AXILIO_API_KEY` env, then `AXILIO_SANDBOX_TOKEN` (sandbox mode) | Bearer token |
115
+ | `base_url` | `AXILIO_BASE_URL` env, then `https://api.axilio.ai` | API host |
116
+ | `timeout` | `30.0` | Request timeout in seconds |
117
+ | `max_retries` | `3` | Retry budget for 429 + 5xx (TODO: wire) |
118
+ | `retry_base_delay` | `0.5` | Exponential backoff base |
119
+
120
+ ## Status
121
+
122
+ This is the v0 scaffold. The public surface (Client + resource classes) matches the design doc; method bodies are stubs (`NotImplementedError`) until codegen lands the typed wire calls. The codegen pipeline is driven by `repository_dispatch` from the monorepo's `publish-sdk-spec` job on each backend deploy — see `.github/workflows/sdk-regenerate.yml` (landing next).
123
+
124
+ ## What's not here
125
+
126
+ - **Async client** — sync only for v0.
127
+ - **In-sandbox device drivers** (the implementation that actually drives a phone screen) — those live inside Atlas + the runner image, not in this SDK.
128
+ - **SSE / WebSocket subscriptions** for live run events — REST only; SSE lands when a consumer needs it.
axilio-0.1.1/README.md ADDED
@@ -0,0 +1,113 @@
1
+ # Axilio Python SDK
2
+
3
+ The official Python SDK for [Axilio](https://axilio.ai). One pip package covers account management, device discovery, allocation, and the full remote control surface (tap, swipe, OCR, screenshot, ...).
4
+
5
+ ## Installation
6
+
7
+ ```bash
8
+ pip install axilio
9
+ ```
10
+
11
+ ## Quick start
12
+
13
+ ```python
14
+ from axilio import Client
15
+
16
+ # Reads AXILIO_API_KEY from the environment.
17
+ client = Client()
18
+
19
+ # One-shot device session — allocate, connect, control, clean up.
20
+ with client.devices.session(platform="android") as device:
21
+ device.tap(540, 1200)
22
+ device.type("hello world")
23
+ device.screenshot("after.png")
24
+ ```
25
+
26
+ The same code runs unmodified inside an Axilio sandbox VM — when you schedule a workflow in the dashboard, Hephaestus injects scoped auth and the paired Atlas endpoint at boot, and the Client auto-detects sandbox mode. No `if local: ... else: ...` branches in your code.
27
+
28
+ ## Authentication
29
+
30
+ ```bash
31
+ export AXILIO_API_KEY=ax_live_...
32
+ ```
33
+
34
+ Or pass explicitly:
35
+
36
+ ```python
37
+ client = Client(api_key="ax_...")
38
+ client = Client(api_key="ax_...", base_url="https://staging-api.axilio.ai")
39
+ ```
40
+
41
+ Generate keys from the [Axilio dashboard](https://app.axilio.ai/settings/api-keys). API keys are scoped to one organization — multi-org users mint one key per org.
42
+
43
+ ## Resources
44
+
45
+ | Resource | Methods | Notes |
46
+ |---|---|---|
47
+ | `client.billing` | `balance()`, `subscription()`, `invoices()`, `upgrade()`, `downgrade()` | Reads everywhere; upgrade/downgrade local-only |
48
+ | `client.usage` | `metrics()`, `sessions()` | Both modes |
49
+ | `client.api_keys` | `list()`, `create()`, `revoke()` | Local-only |
50
+ | `client.org` | `current()`, `members()`, `invite()`, `remove_member()` | Reads everywhere; member writes local-only |
51
+ | `client.devices` | `available()`, `locations()`, `supported_apps()`, `allocate()`, `connect()`, `deallocate()`, `session()` | Sandbox mode short-circuits `allocate()` |
52
+ | `client.workflows` | `list()`, `get()`, `create()`, `update()`, `delete()` | Both modes |
53
+ | `client.runs` | `list()`, `get()`, `create()`, `cancel()` | Both modes |
54
+
55
+ For the full method surface, type signatures, and the device control vocabulary, see the [SDK design doc](https://github.com/axilioai/axilio/blob/main/.docs/sdk-design.md) in the monorepo.
56
+
57
+ ## Errors
58
+
59
+ All errors raised by the SDK subclass `axilio.AxilioError`:
60
+
61
+ ```python
62
+ from axilio import Client, NotFoundError, RateLimitError, SandboxPermissionError
63
+
64
+ client = Client()
65
+
66
+ try:
67
+ run = client.runs.get("run_does_not_exist")
68
+ except NotFoundError:
69
+ print("run not found")
70
+ except RateLimitError:
71
+ # backoff + retry
72
+ ...
73
+ except SandboxPermissionError:
74
+ # tried a local-only write from inside a sandbox VM
75
+ ...
76
+ ```
77
+
78
+ | Exception | HTTP | Notes |
79
+ |---|---|---|
80
+ | `UnauthorizedError` | 401 | API key missing, malformed, or rejected |
81
+ | `NotFoundError` | 404 | Resource doesn't exist or isn't visible |
82
+ | `RateLimitError` | 429 | Retry after backoff |
83
+ | `ServerError` | 5xx | Transient; safe to retry |
84
+ | `SandboxPermissionError` | — | Local-only operation attempted from sandbox |
85
+ | `AllocationMismatchError` | — | Sandbox kwargs don't match the bound allocation |
86
+ | `AxilioError` | other | Catch-all |
87
+
88
+ ## Configuration
89
+
90
+ | Env var | Description |
91
+ |---|---|
92
+ | `AXILIO_API_KEY` | API key for authentication. Required in local mode. |
93
+ | `AXILIO_BASE_URL` | Override the API host (defaults to `https://api.axilio.ai`). |
94
+ | `AXILIO_SANDBOX_TOKEN` | Scoped sandbox token. Injected by Hephaestus inside a sandbox VM; not set in local mode. |
95
+ | `AXILIO_ATLAS_ENDPOINT` | Paired Atlas endpoint inside a sandbox VM. Injected by Hephaestus. |
96
+
97
+ | Constructor kwarg | Default | Description |
98
+ |---|---|---|
99
+ | `api_key` | `AXILIO_API_KEY` env, then `AXILIO_SANDBOX_TOKEN` (sandbox mode) | Bearer token |
100
+ | `base_url` | `AXILIO_BASE_URL` env, then `https://api.axilio.ai` | API host |
101
+ | `timeout` | `30.0` | Request timeout in seconds |
102
+ | `max_retries` | `3` | Retry budget for 429 + 5xx (TODO: wire) |
103
+ | `retry_base_delay` | `0.5` | Exponential backoff base |
104
+
105
+ ## Status
106
+
107
+ This is the v0 scaffold. The public surface (Client + resource classes) matches the design doc; method bodies are stubs (`NotImplementedError`) until codegen lands the typed wire calls. The codegen pipeline is driven by `repository_dispatch` from the monorepo's `publish-sdk-spec` job on each backend deploy — see `.github/workflows/sdk-regenerate.yml` (landing next).
108
+
109
+ ## What's not here
110
+
111
+ - **Async client** — sync only for v0.
112
+ - **In-sandbox device drivers** (the implementation that actually drives a phone screen) — those live inside Atlas + the runner image, not in this SDK.
113
+ - **SSE / WebSocket subscriptions** for live run events — REST only; SSE lands when a consumer needs it.