thrustlab 0.3.0__tar.gz → 0.3.2__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 (241) hide show
  1. {thrustlab-0.3.0 → thrustlab-0.3.2}/CHANGELOG.md +54 -0
  2. {thrustlab-0.3.0 → thrustlab-0.3.2}/PKG-INFO +19 -15
  3. {thrustlab-0.3.0 → thrustlab-0.3.2}/README.md +17 -13
  4. {thrustlab-0.3.0 → thrustlab-0.3.2}/examples/dynamic/run_and_poll.py +18 -8
  5. {thrustlab-0.3.0 → thrustlab-0.3.2}/examples/outputs/read_dynamic.py +11 -5
  6. {thrustlab-0.3.0 → thrustlab-0.3.2}/examples/outputs/read_steady_state.py +1 -1
  7. {thrustlab-0.3.0 → thrustlab-0.3.2}/examples/quickstart.py +1 -1
  8. {thrustlab-0.3.0 → thrustlab-0.3.2}/examples/simulations/run_sync.py +4 -4
  9. {thrustlab-0.3.0 → thrustlab-0.3.2}/examples/sweeps/run_and_poll.py +1 -1
  10. {thrustlab-0.3.0 → thrustlab-0.3.2}/pyproject.toml +1 -1
  11. {thrustlab-0.3.0 → thrustlab-0.3.2}/scripts/publish_public_repo.sh +3 -2
  12. {thrustlab-0.3.0 → thrustlab-0.3.2}/tests/test_resources/test_projects.py +4 -2
  13. {thrustlab-0.3.0 → thrustlab-0.3.2}/tests/test_resources/test_simulations.py +4 -1
  14. thrustlab-0.3.2/tests/test_resources/test_starred_components.py +74 -0
  15. {thrustlab-0.3.0 → thrustlab-0.3.2}/tests/test_resources/test_sweeps.py +4 -1
  16. {thrustlab-0.3.0 → thrustlab-0.3.2}/thrustlab/_http.py +3 -2
  17. {thrustlab-0.3.0 → thrustlab-0.3.2}/thrustlab/_version.py +1 -1
  18. {thrustlab-0.3.0 → thrustlab-0.3.2}/thrustlab/resources/dynamic_simulations.py +3 -1
  19. {thrustlab-0.3.0 → thrustlab-0.3.2}/thrustlab/resources/projects.py +8 -3
  20. {thrustlab-0.3.0 → thrustlab-0.3.2}/thrustlab/resources/simulations.py +9 -1
  21. thrustlab-0.3.2/thrustlab/resources/starred_components.py +95 -0
  22. thrustlab-0.3.2/thrustlab/resources/submissions.py +94 -0
  23. {thrustlab-0.3.0 → thrustlab-0.3.2}/thrustlab/resources/sweeps.py +3 -1
  24. {thrustlab-0.3.0 → thrustlab-0.3.2}/thrustlab/resources/webhook_endpoints.py +35 -10
  25. thrustlab-0.3.0/tests/test_resources/test_starred_components.py +0 -37
  26. thrustlab-0.3.0/thrustlab/resources/starred_components.py +0 -57
  27. thrustlab-0.3.0/thrustlab/resources/submissions.py +0 -44
  28. {thrustlab-0.3.0 → thrustlab-0.3.2}/.gitignore +0 -0
  29. {thrustlab-0.3.0 → thrustlab-0.3.2}/.impeccable/hook.cache.json +0 -0
  30. {thrustlab-0.3.0 → thrustlab-0.3.2}/LICENSE +0 -0
  31. {thrustlab-0.3.0 → thrustlab-0.3.2}/Makefile +0 -0
  32. {thrustlab-0.3.0 → thrustlab-0.3.2}/NOTICE +0 -0
  33. {thrustlab-0.3.0 → thrustlab-0.3.2}/ci/public-ci.yml +0 -0
  34. {thrustlab-0.3.0 → thrustlab-0.3.2}/examples/components/create_component.py +0 -0
  35. {thrustlab-0.3.0 → thrustlab-0.3.2}/examples/projects/create.py +0 -0
  36. {thrustlab-0.3.0 → thrustlab-0.3.2}/examples/projects/list.py +0 -0
  37. {thrustlab-0.3.0 → thrustlab-0.3.2}/examples/projects/retrieve.py +0 -0
  38. {thrustlab-0.3.0 → thrustlab-0.3.2}/examples/simulations/wait.py +0 -0
  39. {thrustlab-0.3.0 → thrustlab-0.3.2}/examples/webhooks/verify_handler.py +0 -0
  40. {thrustlab-0.3.0 → thrustlab-0.3.2}/scripts/regen_models.py +0 -0
  41. {thrustlab-0.3.0 → thrustlab-0.3.2}/tests/__init__.py +0 -0
  42. {thrustlab-0.3.0 → thrustlab-0.3.2}/tests/conftest.py +0 -0
  43. {thrustlab-0.3.0 → thrustlab-0.3.2}/tests/test_async_polling.py +0 -0
  44. {thrustlab-0.3.0 → thrustlab-0.3.2}/tests/test_client.py +0 -0
  45. {thrustlab-0.3.0 → thrustlab-0.3.2}/tests/test_components_create.py +0 -0
  46. {thrustlab-0.3.0 → thrustlab-0.3.2}/tests/test_components_find.py +0 -0
  47. {thrustlab-0.3.0 → thrustlab-0.3.2}/tests/test_dynamic_resource.py +0 -0
  48. {thrustlab-0.3.0 → thrustlab-0.3.2}/tests/test_exceptions.py +0 -0
  49. {thrustlab-0.3.0 → thrustlab-0.3.2}/tests/test_http.py +0 -0
  50. {thrustlab-0.3.0 → thrustlab-0.3.2}/tests/test_pagination.py +0 -0
  51. {thrustlab-0.3.0 → thrustlab-0.3.2}/tests/test_resources/__init__.py +0 -0
  52. {thrustlab-0.3.0 → thrustlab-0.3.2}/tests/test_resources/test_components.py +0 -0
  53. {thrustlab-0.3.0 → thrustlab-0.3.2}/tests/test_resources/test_compute_units.py +0 -0
  54. {thrustlab-0.3.0 → thrustlab-0.3.2}/tests/test_resources/test_submissions.py +0 -0
  55. {thrustlab-0.3.0 → thrustlab-0.3.2}/tests/test_resources/test_users.py +0 -0
  56. {thrustlab-0.3.0 → thrustlab-0.3.2}/tests/test_resources/test_webhook_endpoints.py +0 -0
  57. {thrustlab-0.3.0 → thrustlab-0.3.2}/tests/test_spec_coverage.py +0 -0
  58. {thrustlab-0.3.0 → thrustlab-0.3.2}/tests/test_webhooks.py +0 -0
  59. {thrustlab-0.3.0 → thrustlab-0.3.2}/thrustlab/__init__.py +0 -0
  60. {thrustlab-0.3.0 → thrustlab-0.3.2}/thrustlab/_async_polling.py +0 -0
  61. {thrustlab-0.3.0 → thrustlab-0.3.2}/thrustlab/_models/__init__.py +0 -0
  62. {thrustlab-0.3.0 → thrustlab-0.3.2}/thrustlab/_models/airfoil_breakpoint.py +0 -0
  63. {thrustlab-0.3.0 → thrustlab-0.3.2}/thrustlab/_models/airfoil_list_response.py +0 -0
  64. {thrustlab-0.3.0 → thrustlab-0.3.2}/thrustlab/_models/airfoil_resource.py +0 -0
  65. {thrustlab-0.3.0 → thrustlab-0.3.2}/thrustlab/_models/analyze_geometry_request.py +0 -0
  66. {thrustlab-0.3.0 → thrustlab-0.3.2}/thrustlab/_models/analyze_geometry_request_mode.py +0 -0
  67. {thrustlab-0.3.0 → thrustlab-0.3.2}/thrustlab/_models/analyze_geometry_request_target_type_type_0.py +0 -0
  68. {thrustlab-0.3.0 → thrustlab-0.3.2}/thrustlab/_models/analyze_geometry_response.py +0 -0
  69. {thrustlab-0.3.0 → thrustlab-0.3.2}/thrustlab/_models/analyze_station_row.py +0 -0
  70. {thrustlab-0.3.0 → thrustlab-0.3.2}/thrustlab/_models/body_import_airfoil_v1_airfoils_import_post.py +0 -0
  71. {thrustlab-0.3.0 → thrustlab-0.3.2}/thrustlab/_models/calibrate_accepted_response.py +0 -0
  72. {thrustlab-0.3.0 → thrustlab-0.3.2}/thrustlab/_models/component_brand.py +0 -0
  73. {thrustlab-0.3.0 → thrustlab-0.3.2}/thrustlab/_models/component_brands_response.py +0 -0
  74. {thrustlab-0.3.0 → thrustlab-0.3.2}/thrustlab/_models/component_create_body.py +0 -0
  75. {thrustlab-0.3.0 → thrustlab-0.3.2}/thrustlab/_models/component_create_body_spec_json.py +0 -0
  76. {thrustlab-0.3.0 → thrustlab-0.3.2}/thrustlab/_models/component_ids_response.py +0 -0
  77. {thrustlab-0.3.0 → thrustlab-0.3.2}/thrustlab/_models/component_list_response.py +0 -0
  78. {thrustlab-0.3.0 → thrustlab-0.3.2}/thrustlab/_models/component_patch.py +0 -0
  79. {thrustlab-0.3.0 → thrustlab-0.3.2}/thrustlab/_models/component_patch_spec_json_type_0.py +0 -0
  80. {thrustlab-0.3.0 → thrustlab-0.3.2}/thrustlab/_models/component_resource.py +0 -0
  81. {thrustlab-0.3.0 → thrustlab-0.3.2}/thrustlab/_models/component_resource_spec_json.py +0 -0
  82. {thrustlab-0.3.0 → thrustlab-0.3.2}/thrustlab/_models/component_sweep_axis_in.py +0 -0
  83. {thrustlab-0.3.0 → thrustlab-0.3.2}/thrustlab/_models/component_sweep_axis_in_axis.py +0 -0
  84. {thrustlab-0.3.0 → thrustlab-0.3.2}/thrustlab/_models/component_write_response.py +0 -0
  85. {thrustlab-0.3.0 → thrustlab-0.3.2}/thrustlab/_models/component_write_response_spec_json.py +0 -0
  86. {thrustlab-0.3.0 → thrustlab-0.3.2}/thrustlab/_models/component_write_warning.py +0 -0
  87. {thrustlab-0.3.0 → thrustlab-0.3.2}/thrustlab/_models/create_webhook_endpoint_v1_webhook_endpoints_post_response_create_webhook_endpoint_v1_webhook_endpoints_post.py +0 -0
  88. {thrustlab-0.3.0 → thrustlab-0.3.2}/thrustlab/_models/credit_balance_resource.py +0 -0
  89. {thrustlab-0.3.0 → thrustlab-0.3.2}/thrustlab/_models/credit_bucket_breakdown.py +0 -0
  90. {thrustlab-0.3.0 → thrustlab-0.3.2}/thrustlab/_models/credit_bucket_breakdown_type.py +0 -0
  91. {thrustlab-0.3.0 → thrustlab-0.3.2}/thrustlab/_models/credit_usage_event_resource.py +0 -0
  92. {thrustlab-0.3.0 → thrustlab-0.3.2}/thrustlab/_models/credit_usage_event_resource_type.py +0 -0
  93. {thrustlab-0.3.0 → thrustlab-0.3.2}/thrustlab/_models/credit_usage_event_resource_unit_type.py +0 -0
  94. {thrustlab-0.3.0 → thrustlab-0.3.2}/thrustlab/_models/credit_usage_list_response.py +0 -0
  95. {thrustlab-0.3.0 → thrustlab-0.3.2}/thrustlab/_models/credit_usage_related_resource.py +0 -0
  96. {thrustlab-0.3.0 → thrustlab-0.3.2}/thrustlab/_models/credit_usage_related_resource_object.py +0 -0
  97. {thrustlab-0.3.0 → thrustlab-0.3.2}/thrustlab/_models/credit_usage_summary_resource.py +0 -0
  98. {thrustlab-0.3.0 → thrustlab-0.3.2}/thrustlab/_models/credit_usage_summary_resource_window.py +0 -0
  99. {thrustlab-0.3.0 → thrustlab-0.3.2}/thrustlab/_models/design_request.py +0 -0
  100. {thrustlab-0.3.0 → thrustlab-0.3.2}/thrustlab/_models/design_request_target_mode.py +0 -0
  101. {thrustlab-0.3.0 → thrustlab-0.3.2}/thrustlab/_models/dynamic_create_body.py +0 -0
  102. {thrustlab-0.3.0 → thrustlab-0.3.2}/thrustlab/_models/dynamic_estimate_body.py +0 -0
  103. {thrustlab-0.3.0 → thrustlab-0.3.2}/thrustlab/_models/dynamic_estimate_resource.py +0 -0
  104. {thrustlab-0.3.0 → thrustlab-0.3.2}/thrustlab/_models/dynamic_list_response.py +0 -0
  105. {thrustlab-0.3.0 → thrustlab-0.3.2}/thrustlab/_models/dynamic_patch.py +0 -0
  106. {thrustlab-0.3.0 → thrustlab-0.3.2}/thrustlab/_models/dynamic_resource.py +0 -0
  107. {thrustlab-0.3.0 → thrustlab-0.3.2}/thrustlab/_models/dynamic_resource_display_labels_type_0.py +0 -0
  108. {thrustlab-0.3.0 → thrustlab-0.3.2}/thrustlab/_models/dynamic_resource_error_type_0.py +0 -0
  109. {thrustlab-0.3.0 → thrustlab-0.3.2}/thrustlab/_models/dynamic_resource_input_snapshot_type_0.py +0 -0
  110. {thrustlab-0.3.0 → thrustlab-0.3.2}/thrustlab/_models/dynamic_resource_result_type_0.py +0 -0
  111. {thrustlab-0.3.0 → thrustlab-0.3.2}/thrustlab/_models/dynamic_resource_status.py +0 -0
  112. {thrustlab-0.3.0 → thrustlab-0.3.2}/thrustlab/_models/dynamic_rotor_group_in.py +0 -0
  113. {thrustlab-0.3.0 → thrustlab-0.3.2}/thrustlab/_models/dynamic_rotor_group_in_motor_cooling_source.py +0 -0
  114. {thrustlab-0.3.0 → thrustlab-0.3.2}/thrustlab/_models/entitlements_resource.py +0 -0
  115. {thrustlab-0.3.0 → thrustlab-0.3.2}/thrustlab/_models/export_geometry_request.py +0 -0
  116. {thrustlab-0.3.0 → thrustlab-0.3.2}/thrustlab/_models/export_geometry_request_format.py +0 -0
  117. {thrustlab-0.3.0 → thrustlab-0.3.2}/thrustlab/_models/export_geometry_request_rotation_type_0.py +0 -0
  118. {thrustlab-0.3.0 → thrustlab-0.3.2}/thrustlab/_models/fire_test_event_v1_webhook_endpoints_public_id_test_post_response_fire_test_event_v1_webhook_endpoints_public_id_test_post.py +0 -0
  119. {thrustlab-0.3.0 → thrustlab-0.3.2}/thrustlab/_models/generate_geometry_request.py +0 -0
  120. {thrustlab-0.3.0 → thrustlab-0.3.2}/thrustlab/_models/geometry_preview_response.py +0 -0
  121. {thrustlab-0.3.0 → thrustlab-0.3.2}/thrustlab/_models/geometry_response.py +0 -0
  122. {thrustlab-0.3.0 → thrustlab-0.3.2}/thrustlab/_models/geometry_style_resource.py +0 -0
  123. {thrustlab-0.3.0 → thrustlab-0.3.2}/thrustlab/_models/geometry_styles_list_response.py +0 -0
  124. {thrustlab-0.3.0 → thrustlab-0.3.2}/thrustlab/_models/get_delivery_v1_webhook_endpoints_public_id_deliveries_delivery_public_id_get_response_get_delivery_v1_webhook_endpoints_public_id_deliveries_delivery_public_id_get.py +0 -0
  125. {thrustlab-0.3.0 → thrustlab-0.3.2}/thrustlab/_models/get_event_v1_events_event_id_get_response_get_event_v1_events_event_id_get.py +0 -0
  126. {thrustlab-0.3.0 → thrustlab-0.3.2}/thrustlab/_models/get_public_result_v1_public_results_slug_get_response_get_public_result_v1_public_results_slug_get.py +0 -0
  127. {thrustlab-0.3.0 → thrustlab-0.3.2}/thrustlab/_models/get_shared_result_v1_public_share_share_token_get_response_get_shared_result_v1_public_share_share_token_get.py +0 -0
  128. {thrustlab-0.3.0 → thrustlab-0.3.2}/thrustlab/_models/get_webhook_endpoint_v1_webhook_endpoints_public_id_get_response_get_webhook_endpoint_v1_webhook_endpoints_public_id_get.py +0 -0
  129. {thrustlab-0.3.0 → thrustlab-0.3.2}/thrustlab/_models/http_validation_error.py +0 -0
  130. {thrustlab-0.3.0 → thrustlab-0.3.2}/thrustlab/_models/list_deliveries_v1_webhook_endpoints_public_id_deliveries_get_response_list_deliveries_v1_webhook_endpoints_public_id_deliveries_get.py +0 -0
  131. {thrustlab-0.3.0 → thrustlab-0.3.2}/thrustlab/_models/list_events_v1_events_get_response_list_events_v1_events_get.py +0 -0
  132. {thrustlab-0.3.0 → thrustlab-0.3.2}/thrustlab/_models/list_public_propellers_v1_public_propellers_get_response_list_public_propellers_v1_public_propellers_get.py +0 -0
  133. {thrustlab-0.3.0 → thrustlab-0.3.2}/thrustlab/_models/list_webhook_endpoints_v1_webhook_endpoints_get_response_list_webhook_endpoints_v1_webhook_endpoints_get.py +0 -0
  134. {thrustlab-0.3.0 → thrustlab-0.3.2}/thrustlab/_models/patch_webhook_endpoint_v1_webhook_endpoints_public_id_patch_response_patch_webhook_endpoint_v1_webhook_endpoints_public_id_patch.py +0 -0
  135. {thrustlab-0.3.0 → thrustlab-0.3.2}/thrustlab/_models/preview_request.py +0 -0
  136. {thrustlab-0.3.0 → thrustlab-0.3.2}/thrustlab/_models/project_create_body.py +0 -0
  137. {thrustlab-0.3.0 → thrustlab-0.3.2}/thrustlab/_models/project_list_response.py +0 -0
  138. {thrustlab-0.3.0 → thrustlab-0.3.2}/thrustlab/_models/project_patch.py +0 -0
  139. {thrustlab-0.3.0 → thrustlab-0.3.2}/thrustlab/_models/project_resource.py +0 -0
  140. {thrustlab-0.3.0 → thrustlab-0.3.2}/thrustlab/_models/retry_delivery_v1_webhook_endpoints_public_id_deliveries_delivery_public_id_retry_post_response_retry_delivery_v1_webhook_endpoints_public_id_deliveries_delivery_public_id_retry_post.py +0 -0
  141. {thrustlab-0.3.0 → thrustlab-0.3.2}/thrustlab/_models/rotate_webhook_secret_v1_webhook_endpoints_public_id_rotate_secret_post_response_rotate_webhook_secret_v1_webhook_endpoints_public_id_rotate_secret_post.py +0 -0
  142. {thrustlab-0.3.0 → thrustlab-0.3.2}/thrustlab/_models/rotor_group_in.py +0 -0
  143. {thrustlab-0.3.0 → thrustlab-0.3.2}/thrustlab/_models/rotor_group_in_esc_timing.py +0 -0
  144. {thrustlab-0.3.0 → thrustlab-0.3.2}/thrustlab/_models/rotor_group_in_esc_type.py +0 -0
  145. {thrustlab-0.3.0 → thrustlab-0.3.2}/thrustlab/_models/rotor_group_in_motor_cooling_source.py +0 -0
  146. {thrustlab-0.3.0 → thrustlab-0.3.2}/thrustlab/_models/rotor_group_resource.py +0 -0
  147. {thrustlab-0.3.0 → thrustlab-0.3.2}/thrustlab/_models/run_queued_v1v1_simulations_run_queued_post_response_run_queued_v1v1_simulations_run_queued_post.py +0 -0
  148. {thrustlab-0.3.0 → thrustlab-0.3.2}/thrustlab/_models/run_selected_body.py +0 -0
  149. {thrustlab-0.3.0 → thrustlab-0.3.2}/thrustlab/_models/run_selected_v1v1_simulations_run_selected_post_response_run_selected_v1v1_simulations_run_selected_post.py +0 -0
  150. {thrustlab-0.3.0 → thrustlab-0.3.2}/thrustlab/_models/schedule_in.py +0 -0
  151. {thrustlab-0.3.0 → thrustlab-0.3.2}/thrustlab/_models/schedule_in_interpolation_type_0.py +0 -0
  152. {thrustlab-0.3.0 → thrustlab-0.3.2}/thrustlab/_models/schedule_in_mode.py +0 -0
  153. {thrustlab-0.3.0 → thrustlab-0.3.2}/thrustlab/_models/segment_group_command_in.py +0 -0
  154. {thrustlab-0.3.0 → thrustlab-0.3.2}/thrustlab/_models/segment_group_command_in_throttle_ramp.py +0 -0
  155. {thrustlab-0.3.0 → thrustlab-0.3.2}/thrustlab/_models/segment_group_command_in_tilt_ramp.py +0 -0
  156. {thrustlab-0.3.0 → thrustlab-0.3.2}/thrustlab/_models/segment_in.py +0 -0
  157. {thrustlab-0.3.0 → thrustlab-0.3.2}/thrustlab/_models/segment_in_airspeed_ramp.py +0 -0
  158. {thrustlab-0.3.0 → thrustlab-0.3.2}/thrustlab/_models/segment_in_per_group.py +0 -0
  159. {thrustlab-0.3.0 → thrustlab-0.3.2}/thrustlab/_models/segment_in_throttle_ramp.py +0 -0
  160. {thrustlab-0.3.0 → thrustlab-0.3.2}/thrustlab/_models/segment_in_vertical_speed_ramp.py +0 -0
  161. {thrustlab-0.3.0 → thrustlab-0.3.2}/thrustlab/_models/share_resource.py +0 -0
  162. {thrustlab-0.3.0 → thrustlab-0.3.2}/thrustlab/_models/simulation_cancel_body.py +0 -0
  163. {thrustlab-0.3.0 → thrustlab-0.3.2}/thrustlab/_models/simulation_create_body.py +0 -0
  164. {thrustlab-0.3.0 → thrustlab-0.3.2}/thrustlab/_models/simulation_create_body_cooling_source_type_0.py +0 -0
  165. {thrustlab-0.3.0 → thrustlab-0.3.2}/thrustlab/_models/simulation_create_body_flight_regime.py +0 -0
  166. {thrustlab-0.3.0 → thrustlab-0.3.2}/thrustlab/_models/simulation_create_body_inflow_mode.py +0 -0
  167. {thrustlab-0.3.0 → thrustlab-0.3.2}/thrustlab/_models/simulation_create_body_launch_intent.py +0 -0
  168. {thrustlab-0.3.0 → thrustlab-0.3.2}/thrustlab/_models/simulation_inputs.py +0 -0
  169. {thrustlab-0.3.0 → thrustlab-0.3.2}/thrustlab/_models/simulation_list_response.py +0 -0
  170. {thrustlab-0.3.0 → thrustlab-0.3.2}/thrustlab/_models/simulation_patch.py +0 -0
  171. {thrustlab-0.3.0 → thrustlab-0.3.2}/thrustlab/_models/simulation_patch_plot_config_json_type_0_item.py +0 -0
  172. {thrustlab-0.3.0 → thrustlab-0.3.2}/thrustlab/_models/simulation_resource.py +0 -0
  173. {thrustlab-0.3.0 → thrustlab-0.3.2}/thrustlab/_models/simulation_resource_display_labels_type_0.py +0 -0
  174. {thrustlab-0.3.0 → thrustlab-0.3.2}/thrustlab/_models/simulation_resource_error_type_0.py +0 -0
  175. {thrustlab-0.3.0 → thrustlab-0.3.2}/thrustlab/_models/simulation_resource_input_snapshot_type_0.py +0 -0
  176. {thrustlab-0.3.0 → thrustlab-0.3.2}/thrustlab/_models/simulation_resource_plot_config_json_type_0_item.py +0 -0
  177. {thrustlab-0.3.0 → thrustlab-0.3.2}/thrustlab/_models/simulation_resource_result_type_0.py +0 -0
  178. {thrustlab-0.3.0 → thrustlab-0.3.2}/thrustlab/_models/simulation_resource_result_type_0_additional_property.py +0 -0
  179. {thrustlab-0.3.0 → thrustlab-0.3.2}/thrustlab/_models/simulation_resource_status.py +0 -0
  180. {thrustlab-0.3.0 → thrustlab-0.3.2}/thrustlab/_models/spline_data.py +0 -0
  181. {thrustlab-0.3.0 → thrustlab-0.3.2}/thrustlab/_models/starred_component_create_body.py +0 -0
  182. {thrustlab-0.3.0 → thrustlab-0.3.2}/thrustlab/_models/starred_component_list_response.py +0 -0
  183. {thrustlab-0.3.0 → thrustlab-0.3.2}/thrustlab/_models/starred_component_resource.py +0 -0
  184. {thrustlab-0.3.0 → thrustlab-0.3.2}/thrustlab/_models/submission_create_body.py +0 -0
  185. {thrustlab-0.3.0 → thrustlab-0.3.2}/thrustlab/_models/submission_create_body_data_json.py +0 -0
  186. {thrustlab-0.3.0 → thrustlab-0.3.2}/thrustlab/_models/submission_list_response.py +0 -0
  187. {thrustlab-0.3.0 → thrustlab-0.3.2}/thrustlab/_models/submission_patch.py +0 -0
  188. {thrustlab-0.3.0 → thrustlab-0.3.2}/thrustlab/_models/submission_patch_data_json_type_0.py +0 -0
  189. {thrustlab-0.3.0 → thrustlab-0.3.2}/thrustlab/_models/submission_resource.py +0 -0
  190. {thrustlab-0.3.0 → thrustlab-0.3.2}/thrustlab/_models/submission_resource_data_json.py +0 -0
  191. {thrustlab-0.3.0 → thrustlab-0.3.2}/thrustlab/_models/sweep_cancel_body.py +0 -0
  192. {thrustlab-0.3.0 → thrustlab-0.3.2}/thrustlab/_models/sweep_config_in.py +0 -0
  193. {thrustlab-0.3.0 → thrustlab-0.3.2}/thrustlab/_models/sweep_config_in_esc_timing_values_type_0_item.py +0 -0
  194. {thrustlab-0.3.0 → thrustlab-0.3.2}/thrustlab/_models/sweep_create_body.py +0 -0
  195. {thrustlab-0.3.0 → thrustlab-0.3.2}/thrustlab/_models/sweep_create_body_cooling_source.py +0 -0
  196. {thrustlab-0.3.0 → thrustlab-0.3.2}/thrustlab/_models/sweep_create_body_inflow_mode.py +0 -0
  197. {thrustlab-0.3.0 → thrustlab-0.3.2}/thrustlab/_models/sweep_create_body_launch_intent.py +0 -0
  198. {thrustlab-0.3.0 → thrustlab-0.3.2}/thrustlab/_models/sweep_inputs.py +0 -0
  199. {thrustlab-0.3.0 → thrustlab-0.3.2}/thrustlab/_models/sweep_list_response.py +0 -0
  200. {thrustlab-0.3.0 → thrustlab-0.3.2}/thrustlab/_models/sweep_param_range_in.py +0 -0
  201. {thrustlab-0.3.0 → thrustlab-0.3.2}/thrustlab/_models/sweep_param_range_in_mode.py +0 -0
  202. {thrustlab-0.3.0 → thrustlab-0.3.2}/thrustlab/_models/sweep_patch.py +0 -0
  203. {thrustlab-0.3.0 → thrustlab-0.3.2}/thrustlab/_models/sweep_patch_plot_config_json_type_0_item.py +0 -0
  204. {thrustlab-0.3.0 → thrustlab-0.3.2}/thrustlab/_models/sweep_point_list_response.py +0 -0
  205. {thrustlab-0.3.0 → thrustlab-0.3.2}/thrustlab/_models/sweep_point_list_response_display_labels_type_0.py +0 -0
  206. {thrustlab-0.3.0 → thrustlab-0.3.2}/thrustlab/_models/sweep_point_patch.py +0 -0
  207. {thrustlab-0.3.0 → thrustlab-0.3.2}/thrustlab/_models/sweep_point_resource.py +0 -0
  208. {thrustlab-0.3.0 → thrustlab-0.3.2}/thrustlab/_models/sweep_point_resource_inputs.py +0 -0
  209. {thrustlab-0.3.0 → thrustlab-0.3.2}/thrustlab/_models/sweep_point_resource_rotors_type_0.py +0 -0
  210. {thrustlab-0.3.0 → thrustlab-0.3.2}/thrustlab/_models/sweep_point_resource_rotors_type_0_additional_property.py +0 -0
  211. {thrustlab-0.3.0 → thrustlab-0.3.2}/thrustlab/_models/sweep_resource.py +0 -0
  212. {thrustlab-0.3.0 → thrustlab-0.3.2}/thrustlab/_models/sweep_resource_error_type_0.py +0 -0
  213. {thrustlab-0.3.0 → thrustlab-0.3.2}/thrustlab/_models/sweep_resource_input_snapshot_type_0.py +0 -0
  214. {thrustlab-0.3.0 → thrustlab-0.3.2}/thrustlab/_models/sweep_resource_plot_config_json_type_0_item.py +0 -0
  215. {thrustlab-0.3.0 → thrustlab-0.3.2}/thrustlab/_models/sweep_resource_status.py +0 -0
  216. {thrustlab-0.3.0 → thrustlab-0.3.2}/thrustlab/_models/sweep_resource_summary_type_0.py +0 -0
  217. {thrustlab-0.3.0 → thrustlab-0.3.2}/thrustlab/_models/sweep_resource_sweep_config_type_0.py +0 -0
  218. {thrustlab-0.3.0 → thrustlab-0.3.2}/thrustlab/_models/sweep_rotor_group_in.py +0 -0
  219. {thrustlab-0.3.0 → thrustlab-0.3.2}/thrustlab/_models/sweep_rotor_group_in_esc_timing.py +0 -0
  220. {thrustlab-0.3.0 → thrustlab-0.3.2}/thrustlab/_models/sweep_rotor_group_in_esc_type.py +0 -0
  221. {thrustlab-0.3.0 → thrustlab-0.3.2}/thrustlab/_models/sweep_rotor_group_in_motor_cooling_source.py +0 -0
  222. {thrustlab-0.3.0 → thrustlab-0.3.2}/thrustlab/_models/termination_in.py +0 -0
  223. {thrustlab-0.3.0 → thrustlab-0.3.2}/thrustlab/_models/termination_in_mode.py +0 -0
  224. {thrustlab-0.3.0 → thrustlab-0.3.2}/thrustlab/_models/types.py +0 -0
  225. {thrustlab-0.3.0 → thrustlab-0.3.2}/thrustlab/_models/user_resource.py +0 -0
  226. {thrustlab-0.3.0 → thrustlab-0.3.2}/thrustlab/_models/user_resource_unit_system.py +0 -0
  227. {thrustlab-0.3.0 → thrustlab-0.3.2}/thrustlab/_models/v1_custom_component_override.py +0 -0
  228. {thrustlab-0.3.0 → thrustlab-0.3.2}/thrustlab/_models/v1_custom_component_override_spec_json.py +0 -0
  229. {thrustlab-0.3.0 → thrustlab-0.3.2}/thrustlab/_models/v1_health_v1_health_get_response_v1_health_v1_health_get.py +0 -0
  230. {thrustlab-0.3.0 → thrustlab-0.3.2}/thrustlab/_models/validation_error.py +0 -0
  231. {thrustlab-0.3.0 → thrustlab-0.3.2}/thrustlab/_models/webhook_endpoint_create.py +0 -0
  232. {thrustlab-0.3.0 → thrustlab-0.3.2}/thrustlab/_models/webhook_endpoint_patch.py +0 -0
  233. {thrustlab-0.3.0 → thrustlab-0.3.2}/thrustlab/_pagination.py +0 -0
  234. {thrustlab-0.3.0 → thrustlab-0.3.2}/thrustlab/client.py +0 -0
  235. {thrustlab-0.3.0 → thrustlab-0.3.2}/thrustlab/exceptions.py +0 -0
  236. {thrustlab-0.3.0 → thrustlab-0.3.2}/thrustlab/resources/__init__.py +0 -0
  237. {thrustlab-0.3.0 → thrustlab-0.3.2}/thrustlab/resources/_base.py +0 -0
  238. {thrustlab-0.3.0 → thrustlab-0.3.2}/thrustlab/resources/components.py +0 -0
  239. {thrustlab-0.3.0 → thrustlab-0.3.2}/thrustlab/resources/compute_units.py +0 -0
  240. {thrustlab-0.3.0 → thrustlab-0.3.2}/thrustlab/resources/users.py +0 -0
  241. {thrustlab-0.3.0 → thrustlab-0.3.2}/thrustlab/webhooks.py +0 -0
@@ -8,6 +8,60 @@ Format follows [Keep a Changelog](https://keepachangelog.com/en/1.1.0/).
8
8
 
9
9
  ## [Unreleased]
10
10
 
11
+ ## [0.3.2] - 2026-07-16
12
+
13
+ Bug-fix release: a docs-vs-code audit found several SDK surfaces that
14
+ silently did nothing (the server ignores unknown query params) or targeted
15
+ endpoints that don't exist. No new API surface beyond the fixes below.
16
+
17
+ ### Fixed
18
+
19
+ - **`list(project_id=...)` on `simulations`, `sweeps`, and
20
+ `dynamic_simulations` now actually filters.** The SDK sent a `project_id`
21
+ query key but the endpoints read `project` — the filter was silently
22
+ ignored and every listing returned ALL of the caller's runs. The kwarg
23
+ name is unchanged; the SDK now sends the right param.
24
+ - **`projects.list`, `submissions.list`, `webhook_endpoints.list`,
25
+ `list_events`, and `list_deliveries` pagination kwarg renamed
26
+ `starting_after=` → `cursor=`.** The endpoints read `cursor`;
27
+ `starting_after` was silently ignored (same defect the 0.3.0 changelog
28
+ fixed for sims/sweeps — these five were missed).
29
+ - **BREAKING — `client.starred_components` rewritten against the real API.**
30
+ The previous resource targeted `GET/POST /v1/projects/{pid}/starred-components`,
31
+ which does not exist under `/v1/` — every call 404'd. Stars are flat,
32
+ first-class resources:
33
+ - `list(project_id=..., component_type=..., cursor=...)` →
34
+ `GET /v1/starred_components`;
35
+ - `add(project_id=..., component_id=..., component_type=...)` →
36
+ `POST /v1/starred_components` (component_type is required; duplicate
37
+ star raises `ConflictError` 409 `already_starred`);
38
+ - `remove(star_id)` → `DELETE /v1/starred_components/{star_id}` — keys on
39
+ the star's own `star_<ksuid>` id (from `list()`/`add()`), not the
40
+ component id.
41
+ - **`APIError.request_id` is now populated.** It is read from the error
42
+ envelope's `request_id` (with the `X-Request-ID` response header as a
43
+ fallback); previously the SDK only read the header, which the server did
44
+ not send, so `request_id` was always `None`.
45
+
46
+ ### Added
47
+
48
+ - `simulations.list(status=...)` — filter by `queued` / `running` /
49
+ `completed` / `failed` / `canceled`.
50
+ - `submissions.list(status=...)` — filter by moderation status.
51
+ - `submissions.update(id, ...)` / `submissions.withdraw(id)` — edit or
52
+ withdraw a submission while it is still `submitted`.
53
+ - `webhook_endpoints.list_deliveries(..., event_type=..., created_at_gte=...)`
54
+ — the endpoint's remaining filters, previously unreachable from the SDK.
55
+
56
+ ## [0.3.1] - 2026-07-16
57
+
58
+ Packaging-metadata fix, no code changes:
59
+
60
+ - The `Repository` project URL shipped in 0.3.0 carried a literal
61
+ `<owner>` placeholder; it now points to the public SDK repo,
62
+ `https://github.com/kbedrich/thrustlab-sdk`.
63
+ - README links corrected to the same repo.
64
+
11
65
  ## [0.3.0] - 2026-07-15
12
66
 
13
67
  The ThrustLab public API was refocused to a **product-only** surface: account,
@@ -1,10 +1,10 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: thrustlab
3
- Version: 0.3.0
3
+ Version: 0.3.2
4
4
  Summary: Official Python SDK for the ThrustLab API
5
5
  Project-URL: Homepage, https://thrustlab.com
6
6
  Project-URL: Documentation, https://thrustlab.com/docs
7
- Project-URL: Repository, https://github.com/<owner>/thrustlab
7
+ Project-URL: Repository, https://github.com/kbedrich/thrustlab-sdk
8
8
  Project-URL: Changelog, https://thrustlab.com/docs/changelog
9
9
  Author-email: ThrustLab <support@thrustlab.com>
10
10
  License: Apache-2.0
@@ -34,7 +34,10 @@ Description-Content-Type: text/markdown
34
34
 
35
35
  [![PyPI](https://img.shields.io/pypi/v/thrustlab.svg)](https://pypi.org/project/thrustlab/)
36
36
  [![Python](https://img.shields.io/pypi/pyversions/thrustlab.svg)](https://pypi.org/project/thrustlab/)
37
- [![License](https://img.shields.io/pypi/l/thrustlab.svg)](https://github.com/kylebedrich/thrustlab-sdk/blob/main/LICENSE)
37
+ [![License](https://img.shields.io/pypi/l/thrustlab.svg)](https://github.com/kbedrich/thrustlab-sdk/blob/main/LICENSE)
38
+
39
+ The official Python client for [ThrustLab](https://thrustlab.com) —
40
+ electric-UAV powertrain simulation as an API.
38
41
 
39
42
  ## Install
40
43
 
@@ -88,18 +91,17 @@ Runnable end-to-end examples for every simulation type live in
88
91
 
89
92
  ## Why the SDK (and not `urllib` / `wget`)?
90
93
 
91
- Use this SDK — or, if you hand-roll a client, an HTTP library that is **not**
92
- stdlib `urllib`. Requests from Python's `urllib.request` (and `wget`) are
93
- **blocked at the Cloudflare edge** by a managed WAF rule matching the literal
94
- `Python-urllib/*` User-Agent. The block is terminated at the edge: the request
95
- **never reaches the ThrustLab API**, so you get an opaque HTML *error 1010*
96
- challenge page — **not** a JSON error envelope. The API cannot return a
97
- structured error for a request it never sees.
94
+ The SDK gives you automatic retries with backoff, idempotency keys, cursor
95
+ pagination, polling helpers, webhook signature verification, and typed
96
+ errors. It is built on [`httpx`](https://www.python-httpx.org/) and sends
97
+ its own `thrustlab-python/<version>` User-Agent.
98
98
 
99
- This SDK is built on [`httpx`](https://www.python-httpx.org/) and sends its own
100
- `thrustlab-python/<version>` User-Agent, so it is **unaffected**. If you must
101
- hand-roll a client, set an explicit non-`urllib` `User-Agent` header (e.g.
102
- `requests`, `httpx`, or `curl`) and you will reach the API normally.
99
+ If you hand-roll a client anyway, use an HTTP library with a descriptive
100
+ User-Agent (`httpx`, `requests`, or `curl`). ThrustLab's edge protection
101
+ screens non-browser User-Agents such as stdlib `Python-urllib/*` and `wget`:
102
+ API paths under `/v1/` are exempt from the screening, but every other route
103
+ blocks these at the edge with an opaque *error 1010* page — the request never
104
+ reaches ThrustLab, so no JSON error envelope is possible there.
103
105
 
104
106
  ## Configuration
105
107
 
@@ -216,11 +218,13 @@ client = Client(max_retries=0)
216
218
 
217
219
  ## Links
218
220
 
221
+ - Website: https://thrustlab.com
219
222
  - Documentation: https://thrustlab.com/docs
220
223
  - API reference: https://thrustlab.com/docs/reference
221
224
  - SDK guide: https://thrustlab.com/docs/sdk/python
222
225
  - Changelog: https://thrustlab.com/docs/changelog
223
- - Issue tracker: https://github.com/kylebedrich/thrustlab-sdk/issues
226
+ - Source: https://github.com/kbedrich/thrustlab-sdk
227
+ - Issue tracker: https://github.com/kbedrich/thrustlab-sdk/issues
224
228
 
225
229
  ## License
226
230
 
@@ -2,7 +2,10 @@
2
2
 
3
3
  [![PyPI](https://img.shields.io/pypi/v/thrustlab.svg)](https://pypi.org/project/thrustlab/)
4
4
  [![Python](https://img.shields.io/pypi/pyversions/thrustlab.svg)](https://pypi.org/project/thrustlab/)
5
- [![License](https://img.shields.io/pypi/l/thrustlab.svg)](https://github.com/kylebedrich/thrustlab-sdk/blob/main/LICENSE)
5
+ [![License](https://img.shields.io/pypi/l/thrustlab.svg)](https://github.com/kbedrich/thrustlab-sdk/blob/main/LICENSE)
6
+
7
+ The official Python client for [ThrustLab](https://thrustlab.com) —
8
+ electric-UAV powertrain simulation as an API.
6
9
 
7
10
  ## Install
8
11
 
@@ -56,18 +59,17 @@ Runnable end-to-end examples for every simulation type live in
56
59
 
57
60
  ## Why the SDK (and not `urllib` / `wget`)?
58
61
 
59
- Use this SDK — or, if you hand-roll a client, an HTTP library that is **not**
60
- stdlib `urllib`. Requests from Python's `urllib.request` (and `wget`) are
61
- **blocked at the Cloudflare edge** by a managed WAF rule matching the literal
62
- `Python-urllib/*` User-Agent. The block is terminated at the edge: the request
63
- **never reaches the ThrustLab API**, so you get an opaque HTML *error 1010*
64
- challenge page — **not** a JSON error envelope. The API cannot return a
65
- structured error for a request it never sees.
62
+ The SDK gives you automatic retries with backoff, idempotency keys, cursor
63
+ pagination, polling helpers, webhook signature verification, and typed
64
+ errors. It is built on [`httpx`](https://www.python-httpx.org/) and sends
65
+ its own `thrustlab-python/<version>` User-Agent.
66
66
 
67
- This SDK is built on [`httpx`](https://www.python-httpx.org/) and sends its own
68
- `thrustlab-python/<version>` User-Agent, so it is **unaffected**. If you must
69
- hand-roll a client, set an explicit non-`urllib` `User-Agent` header (e.g.
70
- `requests`, `httpx`, or `curl`) and you will reach the API normally.
67
+ If you hand-roll a client anyway, use an HTTP library with a descriptive
68
+ User-Agent (`httpx`, `requests`, or `curl`). ThrustLab's edge protection
69
+ screens non-browser User-Agents such as stdlib `Python-urllib/*` and `wget`:
70
+ API paths under `/v1/` are exempt from the screening, but every other route
71
+ blocks these at the edge with an opaque *error 1010* page — the request never
72
+ reaches ThrustLab, so no JSON error envelope is possible there.
71
73
 
72
74
  ## Configuration
73
75
 
@@ -184,11 +186,13 @@ client = Client(max_retries=0)
184
186
 
185
187
  ## Links
186
188
 
189
+ - Website: https://thrustlab.com
187
190
  - Documentation: https://thrustlab.com/docs
188
191
  - API reference: https://thrustlab.com/docs/reference
189
192
  - SDK guide: https://thrustlab.com/docs/sdk/python
190
193
  - Changelog: https://thrustlab.com/docs/changelog
191
- - Issue tracker: https://github.com/kylebedrich/thrustlab-sdk/issues
194
+ - Source: https://github.com/kbedrich/thrustlab-sdk
195
+ - Issue tracker: https://github.com/kbedrich/thrustlab-sdk/issues
192
196
 
193
197
  ## License
194
198
 
@@ -5,8 +5,9 @@ Run it:
5
5
  python examples/dynamic/run_and_poll.py
6
6
 
7
7
  A dynamic run integrates the powertrain through a throttle/airspeed *schedule*
8
- until a *termination* condition (here: full-throttle hover until the pack SOC
9
- falls to 20%). It returns per-step `samples` (each a full steady-shaped result),
8
+ until a *termination* condition (here: a 2 s spin-up ramp, then a 20 s hover
9
+ at 70% — a fixed-duration mission). It returns per-step `samples` (each a full
10
+ steady-shaped result),
10
11
  time `series`, a `scorecard`, and `events`. Swap in your own component IDs, or
11
12
  resolve them by name with client.components.find(...).
12
13
  """
@@ -20,7 +21,7 @@ project = client.projects.create(name="sdk dynamic example")
20
21
  # Resolve by name, or paste explicit IDs: motor_id = "comp_motor_xxx"
21
22
  motor = client.components.find(name="Spektrum Avian 4260 800Kv")
22
23
  prop = client.components.find(name="10.5x4.5")
23
- battery = client.components.find(name="4S 5000mAh")
24
+ battery = client.components.find(name="Liperior 5000mAh 4S 35C")
24
25
 
25
26
  dyn = client.dynamic_simulations.create(
26
27
  project_id=project["id"],
@@ -36,19 +37,28 @@ dyn = client.dynamic_simulations.create(
36
37
  "propeller_component_id": prop["id"],
37
38
  }
38
39
  ],
39
- # One segment: hold 70% throttle, static, run until the pack depletes.
40
+ # Ramp to 70% over 2 s, then hold for 20 s. The soft-start matters:
41
+ # stepping 70% onto a stationary rotor draws an in-rush that can sag the
42
+ # pack below the low-voltage cutoff and end the run immediately — exactly
43
+ # as it would on real hardware. Endurance ("until depleted") missions work
44
+ # the same way but integrate minutes of pack time; keep examples short.
40
45
  schedule={
41
46
  "mode": "segments",
42
47
  "segments": [
43
48
  {
44
- "until_depleted": True,
49
+ "duration_s": 2.0,
50
+ "airspeed_target": 0.0,
51
+ "per_group": {"main": {"throttle_target": 70, "throttle_ramp": "linear"}},
52
+ },
53
+ {
54
+ "duration_s": 20.0,
45
55
  "airspeed_target": 0.0,
46
56
  "per_group": {"main": {"throttle_target": 70}},
47
- }
57
+ },
48
58
  ],
49
59
  },
50
- # Stop when the worst cell reaches 20% SOC.
51
- termination={"mode": "until_depleted", "soc_cutoff_pct": 20.0},
60
+ # Fixed-duration mission: run the schedule to its end.
61
+ termination={"mode": "fixed"},
52
62
  )
53
63
  print(f"created {dyn['id']}, status={dyn['status']}")
54
64
 
@@ -29,7 +29,7 @@ project = client.projects.create(name="sdk read-dynamic example")
29
29
  # Resolve by name, or paste explicit IDs: motor_id = "comp_motor_xxx"
30
30
  motor = client.components.find(name="Spektrum Avian 4260 800Kv")
31
31
  prop = client.components.find(name="10.5x4.5")
32
- battery = client.components.find(name="4S 5000mAh")
32
+ battery = client.components.find(name="Liperior 5000mAh 4S 35C")
33
33
 
34
34
  dyn = client.dynamic_simulations.create(
35
35
  project_id=project["id"],
@@ -45,18 +45,24 @@ dyn = client.dynamic_simulations.create(
45
45
  "propeller_component_id": prop["id"],
46
46
  }
47
47
  ],
48
- # Hold 70% throttle, static, until the pack depletes.
48
+ # Ramp to 70% over 2 s (soft-start — a throttle step onto a stationary
49
+ # rotor sags the pack below the low-voltage cutoff), then hold for 20 s.
49
50
  schedule={
50
51
  "mode": "segments",
51
52
  "segments": [
52
53
  {
53
- "until_depleted": True,
54
+ "duration_s": 2.0,
55
+ "airspeed_target": 0.0,
56
+ "per_group": {"main": {"throttle_target": 70, "throttle_ramp": "linear"}},
57
+ },
58
+ {
59
+ "duration_s": 20.0,
54
60
  "airspeed_target": 0.0,
55
61
  "per_group": {"main": {"throttle_target": 70}},
56
- }
62
+ },
57
63
  ],
58
64
  },
59
- termination={"mode": "until_depleted", "soc_cutoff_pct": 20.0},
65
+ termination={"mode": "fixed"},
60
66
  )
61
67
  result = client.dynamic_simulations.wait(dyn["id"], timeout=600)
62
68
  print(f"dynamic status: {result['status']}")
@@ -42,7 +42,7 @@ project = client.projects.create(name="sdk read-steady-state example")
42
42
  # Resolve by name, or paste explicit IDs: motor_id = "comp_motor_xxx"
43
43
  motor = client.components.find(name="Spektrum Avian 4260 800Kv")
44
44
  prop = client.components.find(name="10.5x4.5")
45
- battery = client.components.find(name="4S 5000mAh")
45
+ battery = client.components.find(name="Liperior 5000mAh 4S 35C")
46
46
 
47
47
  # ---- single point ---------------------------------------------------------
48
48
  sim = client.simulations.create(
@@ -23,7 +23,7 @@ for component in islice(client.components.list(type="motor"), 5):
23
23
  # round-trip the list yourself. Or paste explicit IDs from the catalog.
24
24
  motor = client.components.find(name="Spektrum Avian 4260 800Kv")
25
25
  prop = client.components.find(name="10.5x4.5")
26
- battery = client.components.find(name="4S 5000mAh")
26
+ battery = client.components.find(name="Liperior 5000mAh 4S 35C")
27
27
 
28
28
  # Run a single-point simulation and wait for the result.
29
29
  sim = client.simulations.create(
@@ -4,9 +4,9 @@ Run it:
4
4
  export THRUSTLAB_API_KEY=key_... # never hard-code the key
5
5
  python examples/simulations/run_sync.py
6
6
 
7
- The verification combo below (Spektrum Avian 4260 800 kV + 10.5x4.5 + 4S 5000 mAh
8
- @ 70% throttle, static) converges to ~9.81 N / ~8658 rpm. Swap in your own
9
- component IDs, or resolve them by name with client.components.find(...).
7
+ The verification combo below (Spektrum Avian 4260 800 kV + 10.5x4.5 + Liperior
8
+ 4S 5000 mAh @ 70% throttle, static) converges to ~9.78 N / ~8645 rpm. Swap in
9
+ your own component IDs, or resolve them by name with client.components.find(...).
10
10
  """
11
11
 
12
12
  from thrustlab import Client
@@ -20,7 +20,7 @@ project = client.projects.create(name="sdk single-point example")
20
20
  # motor_id = "comp_motor_xxx"
21
21
  motor = client.components.find(name="Spektrum Avian 4260 800Kv")
22
22
  prop = client.components.find(name="10.5x4.5")
23
- battery = client.components.find(name="4S 5000mAh")
23
+ battery = client.components.find(name="Liperior 5000mAh 4S 35C")
24
24
 
25
25
  sim = client.simulations.create(
26
26
  project_id=project["id"],
@@ -18,7 +18,7 @@ project = client.projects.create(name="sdk sweep example")
18
18
  # Resolve by name, or paste explicit IDs: motor_id = "comp_motor_xxx"
19
19
  motor = client.components.find(name="Spektrum Avian 4260 800Kv")
20
20
  prop = client.components.find(name="10.5x4.5")
21
- battery = client.components.find(name="4S 5000mAh")
21
+ battery = client.components.find(name="Liperior 5000mAh 4S 35C")
22
22
 
23
23
  sweep = client.sweeps.create(
24
24
  project_id=project["id"],
@@ -39,7 +39,7 @@ dev = [
39
39
  [project.urls]
40
40
  Homepage = "https://thrustlab.com"
41
41
  Documentation = "https://thrustlab.com/docs"
42
- Repository = "https://github.com/<owner>/thrustlab"
42
+ Repository = "https://github.com/kbedrich/thrustlab-sdk"
43
43
  Changelog = "https://thrustlab.com/docs/changelog"
44
44
 
45
45
  [tool.hatch.version]
@@ -149,12 +149,13 @@ if [ -n "$ADVISORY" ]; then
149
149
  fi
150
150
 
151
151
  # --- 6. fresh git repo: single squashed commit, NO remote, NO push (D-23) -----
152
+ SDK_VERSION="$(sed -n 's/^__version__ = "\(.*\)"/\1/p' "$SRC/thrustlab/_version.py")"
152
153
  (
153
154
  cd "$TARGET"
154
155
  git init -b main -q
155
156
  git add -A
156
- git -c user.name="kylebedrich" -c user.email="kyle@bedrich.net" \
157
- commit -q -m "Initial public release: ThrustLab Python SDK v0.3.0"
157
+ git -c user.name="kbedrich" -c user.email="kyle@bedrich.net" \
158
+ commit -q -m "Initial public release: ThrustLab Python SDK v${SDK_VERSION}"
158
159
  )
159
160
 
160
161
  # --- post-build assertions ----------------------------------------------------
@@ -10,10 +10,12 @@ def test_list_passes_params(mock_router, monkeypatch):
10
10
  route = mock_router.get("/v1/projects").mock(
11
11
  return_value=Response(200, json={"data": [], "has_more": False})
12
12
  )
13
- client.projects.list(limit=10, starting_after="proj_5").data # trigger fetch
13
+ client.projects.list(limit=10, cursor="proj_5").data # trigger fetch
14
14
  qs = dict(route.calls[0].request.url.params)
15
15
  assert qs["limit"] == "10"
16
- assert qs["starting_after"] == "proj_5"
16
+ # The endpoint reads `cursor` — the pre-0.3.2 `starting_after` was ignored.
17
+ assert qs["cursor"] == "proj_5"
18
+ assert "starting_after" not in qs
17
19
 
18
20
 
19
21
  def test_create_calls_post(mock_router, monkeypatch):
@@ -13,7 +13,10 @@ def test_list_passes_project_filter(mock_router, monkeypatch):
13
13
  )
14
14
  client.simulations.list(project_id="proj_1", limit=5).data # trigger fetch
15
15
  qs = dict(route.calls[0].request.url.params)
16
- assert qs["project_id"] == "proj_1"
16
+ # The endpoint's filter param is `project` — the pre-0.3.2 `project_id`
17
+ # key was silently ignored server-side (filter did nothing).
18
+ assert qs["project"] == "proj_1"
19
+ assert "project_id" not in qs
17
20
  assert qs["limit"] == "5"
18
21
 
19
22
 
@@ -0,0 +1,74 @@
1
+ from httpx import Response
2
+
3
+ from thrustlab import Client
4
+
5
+
6
+ def test_list_calls_get(mock_router, monkeypatch):
7
+ monkeypatch.setenv("THRUSTLAB_API_KEY", "key_test")
8
+ monkeypatch.setenv("THRUSTLAB_BASE_URL", "https://api.test.local")
9
+ client = Client()
10
+ route = mock_router.get("/v1/starred_components").mock(
11
+ return_value=Response(200, json={"data": [], "has_more": False})
12
+ )
13
+ client.starred_components.list(project_id="proj_1").data # trigger fetch
14
+ assert route.call_count == 1
15
+ qs = dict(route.calls[0].request.url.params)
16
+ # The endpoint's filter param is `project` (the SDK kwarg stays project_id).
17
+ assert qs["project"] == "proj_1"
18
+
19
+
20
+ def test_list_passes_component_type_and_cursor(mock_router, monkeypatch):
21
+ monkeypatch.setenv("THRUSTLAB_API_KEY", "key_test")
22
+ monkeypatch.setenv("THRUSTLAB_BASE_URL", "https://api.test.local")
23
+ client = Client()
24
+ route = mock_router.get("/v1/starred_components").mock(
25
+ return_value=Response(200, json={"data": [], "has_more": False})
26
+ )
27
+ client.starred_components.list(
28
+ component_type="motor", cursor="star_abc"
29
+ ).data
30
+ qs = dict(route.calls[0].request.url.params)
31
+ assert qs["component_type"] == "motor"
32
+ assert qs["cursor"] == "star_abc"
33
+
34
+
35
+ def test_add_calls_post(mock_router, monkeypatch):
36
+ monkeypatch.setenv("THRUSTLAB_API_KEY", "key_test")
37
+ monkeypatch.setenv("THRUSTLAB_BASE_URL", "https://api.test.local")
38
+ client = Client()
39
+ route = mock_router.post("/v1/starred_components").mock(
40
+ return_value=Response(
41
+ 201,
42
+ json={
43
+ "id": "star_1",
44
+ "project_id": "proj_1",
45
+ "component_id": "cmp_1",
46
+ "component_type": "motor",
47
+ },
48
+ )
49
+ )
50
+ out = client.starred_components.add(
51
+ project_id="proj_1", component_id="cmp_1", component_type="motor"
52
+ )
53
+ assert out["id"] == "star_1"
54
+ assert route.calls[0].request.headers["Idempotency-Key"]
55
+ import json
56
+
57
+ body = json.loads(route.calls[0].request.content)
58
+ assert body == {
59
+ "project_id": "proj_1",
60
+ "component_id": "cmp_1",
61
+ "component_type": "motor",
62
+ }
63
+
64
+
65
+ def test_remove_calls_delete(mock_router, monkeypatch):
66
+ monkeypatch.setenv("THRUSTLAB_API_KEY", "key_test")
67
+ monkeypatch.setenv("THRUSTLAB_BASE_URL", "https://api.test.local")
68
+ client = Client()
69
+ route = mock_router.delete("/v1/starred_components/star_1").mock(
70
+ return_value=Response(204)
71
+ )
72
+ # Removal keys on the star's own id, not the component id.
73
+ client.starred_components.remove("star_1")
74
+ assert route.call_count == 1
@@ -13,7 +13,10 @@ def test_list_passes_project_filter(mock_router, monkeypatch):
13
13
  )
14
14
  client.sweeps.list(project_id="proj_1").data # trigger fetch
15
15
  qs = dict(route.calls[0].request.url.params)
16
- assert qs["project_id"] == "proj_1"
16
+ # The endpoint's filter param is `project` — the pre-0.3.2 `project_id`
17
+ # key was silently ignored server-side (filter did nothing).
18
+ assert qs["project"] == "proj_1"
19
+ assert "project_id" not in qs
17
20
 
18
21
 
19
22
  def test_create_returns_pending(mock_router, monkeypatch):
@@ -119,14 +119,15 @@ class Transport:
119
119
  return None
120
120
  return resp.json()
121
121
 
122
- # Error path. Parse envelope: { "error": { "type": ..., "code": ..., "message": ..., "param": ... } }.
123
- request_id = resp.headers.get("X-Request-ID")
122
+ # Error path. Parse envelope: { "error": { "type": ..., "code": ..., "message": ..., "param": ..., "request_id": ... } }.
124
123
  body: dict[str, Any] = {}
125
124
  try:
126
125
  body = resp.json()
127
126
  except ValueError:
128
127
  pass
129
128
  env = body.get("error", {}) if isinstance(body, dict) else {}
129
+ # The envelope's request_id is authoritative; the header is a fallback.
130
+ request_id = env.get("request_id") or resp.headers.get("X-Request-ID")
130
131
 
131
132
  kwargs = dict(
132
133
  code=env.get("code"),
@@ -1,3 +1,3 @@
1
1
  """Single source of truth for the package version. Hatchling reads this."""
2
2
 
3
- __version__ = "0.3.0"
3
+ __version__ = "0.3.2"
@@ -44,7 +44,9 @@ class DynamicSimulationsResource(Resource):
44
44
  """
45
45
  params: dict[str, Any] = {"limit": limit}
46
46
  if project_id:
47
- params["project_id"] = project_id
47
+ # The endpoint's filter param is ``project`` (pre-0.3.2 the SDK
48
+ # sent an ignored ``project_id`` key — the filter did nothing).
49
+ params["project"] = project_id
48
50
  if cursor:
49
51
  params["cursor"] = cursor
50
52
  return CursorPager(
@@ -17,16 +17,21 @@ from thrustlab.resources._base import Resource
17
17
 
18
18
 
19
19
  class ProjectsResource(Resource):
20
- def list(self, *, limit: int = 20, starting_after: Optional[str] = None) -> CursorPager[dict[str, Any]]:
20
+ def list(self, *, limit: int = 20, cursor: Optional[str] = None) -> CursorPager[dict[str, Any]]:
21
21
  """List projects (cursor-paginated).
22
22
 
23
+ Args:
24
+ cursor: Opaque pagination token from a prior response's
25
+ ``next_cursor`` (the backend reads ``cursor``; the pre-0.3.2
26
+ ``starting_after`` kwarg was silently ignored server-side).
27
+
23
28
  Returns a :class:`CursorPager` that lazily fetches subsequent pages.
24
29
  Iterate to consume all pages, or access ``.data`` / ``.has_more`` for
25
30
  manual cursor control.
26
31
  """
27
32
  params: dict[str, Any] = {"limit": limit}
28
- if starting_after:
29
- params["starting_after"] = starting_after
33
+ if cursor:
34
+ params["cursor"] = cursor
30
35
  return CursorPager(
31
36
  fetch_page=lambda p: self._transport.request("GET", "/v1/projects", params=p),
32
37
  initial_params=params,
@@ -21,12 +21,18 @@ class SimulationsResource(Resource):
21
21
  self,
22
22
  *,
23
23
  project_id: Optional[str] = None,
24
+ status: Optional[str] = None,
24
25
  limit: int = 20,
25
26
  cursor: Optional[str] = None,
26
27
  ) -> CursorPager[dict[str, Any]]:
27
28
  """List simulations (cursor-paginated).
28
29
 
29
30
  Args:
31
+ project_id: Optional project filter (sent as the endpoint's
32
+ ``project`` query param; pre-0.3.2 the SDK sent an ignored
33
+ ``project_id`` key, so this filter silently did nothing).
34
+ status: Optional status filter (``queued`` / ``running`` /
35
+ ``completed`` / ``failed`` / ``canceled``).
30
36
  cursor: Opaque pagination token from a prior response's
31
37
  ``next_cursor`` (the ``/v1/simulations`` endpoint reads
32
38
  ``cursor``). Omit to start from the first page.
@@ -35,7 +41,9 @@ class SimulationsResource(Resource):
35
41
  """
36
42
  params: dict[str, Any] = {"limit": limit}
37
43
  if project_id:
38
- params["project_id"] = project_id
44
+ params["project"] = project_id
45
+ if status:
46
+ params["status"] = status
39
47
  if cursor:
40
48
  params["cursor"] = cursor
41
49
  return CursorPager(