mainsequence 3.19.3__tar.gz → 3.19.5__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 (186) hide show
  1. {mainsequence-3.19.3 → mainsequence-3.19.5}/PKG-INFO +3 -1
  2. {mainsequence-3.19.3 → mainsequence-3.19.5}/README.md +2 -0
  3. {mainsequence-3.19.3 → mainsequence-3.19.5}/agent_scaffold/AGENTS.md +2 -2
  4. {mainsequence-3.19.3 → mainsequence-3.19.5}/agent_scaffold/skills/command_center/workspace_builder/SKILL.md +16 -9
  5. {mainsequence-3.19.3 → mainsequence-3.19.5}/mainsequence/cli/api.py +51 -2
  6. {mainsequence-3.19.3 → mainsequence-3.19.5}/mainsequence/cli/cli.py +36 -68
  7. {mainsequence-3.19.3 → mainsequence-3.19.5}/mainsequence/cli/local_ops.py +6 -12
  8. {mainsequence-3.19.3 → mainsequence-3.19.5}/mainsequence/client/command_center/workspace.py +1 -1
  9. {mainsequence-3.19.3 → mainsequence-3.19.5}/mainsequence/client/models_helpers.py +1 -1
  10. {mainsequence-3.19.3 → mainsequence-3.19.5}/mainsequence.egg-info/PKG-INFO +3 -1
  11. {mainsequence-3.19.3 → mainsequence-3.19.5}/pyproject.toml +1 -1
  12. {mainsequence-3.19.3 → mainsequence-3.19.5}/tests/test_cli.py +64 -6
  13. {mainsequence-3.19.3 → mainsequence-3.19.5}/tests/test_project_batch_jobs_from_file.py +43 -0
  14. {mainsequence-3.19.3 → mainsequence-3.19.5}/LICENSE +0 -0
  15. {mainsequence-3.19.3 → mainsequence-3.19.5}/agent_scaffold/skills/a2a_communication/SKILL.md +0 -0
  16. {mainsequence-3.19.3 → mainsequence-3.19.5}/agent_scaffold/skills/application_surfaces/api_surfaces/SKILL.md +0 -0
  17. {mainsequence-3.19.3 → mainsequence-3.19.5}/agent_scaffold/skills/command_center/adapter_from_api/SKILL.md +0 -0
  18. {mainsequence-3.19.3 → mainsequence-3.19.5}/agent_scaffold/skills/command_center/api_mock_prototyping/SKILL.md +0 -0
  19. {mainsequence-3.19.3 → mainsequence-3.19.5}/agent_scaffold/skills/command_center/app_components/SKILL.md +0 -0
  20. {mainsequence-3.19.3 → mainsequence-3.19.5}/agent_scaffold/skills/command_center/connections/SKILL.md +0 -0
  21. {mainsequence-3.19.3 → mainsequence-3.19.5}/agent_scaffold/skills/command_center/workspace_analysis/SKILL.md +0 -0
  22. {mainsequence-3.19.3 → mainsequence-3.19.5}/agent_scaffold/skills/command_center/workspace_design/SKILL.md +0 -0
  23. {mainsequence-3.19.3 → mainsequence-3.19.5}/agent_scaffold/skills/dashboards/streamlit/SKILL.md +0 -0
  24. {mainsequence-3.19.3 → mainsequence-3.19.5}/agent_scaffold/skills/data_access/exploration/SKILL.md +0 -0
  25. {mainsequence-3.19.3 → mainsequence-3.19.5}/agent_scaffold/skills/data_publishing/data_nodes/SKILL.md +0 -0
  26. {mainsequence-3.19.3 → mainsequence-3.19.5}/agent_scaffold/skills/data_publishing/simple_tables/SKILL.md +0 -0
  27. {mainsequence-3.19.3 → mainsequence-3.19.5}/agent_scaffold/skills/maintenance/bug_auditor/SKILL.md +0 -0
  28. {mainsequence-3.19.3 → mainsequence-3.19.5}/agent_scaffold/skills/maintenance/local_journal/SKILL.md +0 -0
  29. {mainsequence-3.19.3 → mainsequence-3.19.5}/agent_scaffold/skills/markets_platform/assets_and_translation/SKILL.md +0 -0
  30. {mainsequence-3.19.3 → mainsequence-3.19.5}/agent_scaffold/skills/markets_platform/instruments_and_pricing/SKILL.md +0 -0
  31. {mainsequence-3.19.3 → mainsequence-3.19.5}/agent_scaffold/skills/markets_platform/virtualfundbuilder/SKILL.md +0 -0
  32. {mainsequence-3.19.3 → mainsequence-3.19.5}/agent_scaffold/skills/platform_operations/access_control_and_sharing/SKILL.md +0 -0
  33. {mainsequence-3.19.3 → mainsequence-3.19.5}/agent_scaffold/skills/platform_operations/orchestration_and_releases/SKILL.md +0 -0
  34. {mainsequence-3.19.3 → mainsequence-3.19.5}/agent_scaffold/skills/project_builder/SKILL.md +0 -0
  35. {mainsequence-3.19.3 → mainsequence-3.19.5}/agent_scaffold/skills/project_to_agent/SKILL.md +0 -0
  36. {mainsequence-3.19.3 → mainsequence-3.19.5}/mainsequence/__init__.py +0 -0
  37. {mainsequence-3.19.3 → mainsequence-3.19.5}/mainsequence/__main__.py +0 -0
  38. {mainsequence-3.19.3 → mainsequence-3.19.5}/mainsequence/bootstrap.py +0 -0
  39. {mainsequence-3.19.3 → mainsequence-3.19.5}/mainsequence/cli/__init__.py +0 -0
  40. {mainsequence-3.19.3 → mainsequence-3.19.5}/mainsequence/cli/browser_auth.py +0 -0
  41. {mainsequence-3.19.3 → mainsequence-3.19.5}/mainsequence/cli/config.py +0 -0
  42. {mainsequence-3.19.3 → mainsequence-3.19.5}/mainsequence/cli/docker_utils.py +0 -0
  43. {mainsequence-3.19.3 → mainsequence-3.19.5}/mainsequence/cli/doctor.py +0 -0
  44. {mainsequence-3.19.3 → mainsequence-3.19.5}/mainsequence/cli/model_filters.py +0 -0
  45. {mainsequence-3.19.3 → mainsequence-3.19.5}/mainsequence/cli/project_status.py +0 -0
  46. {mainsequence-3.19.3 → mainsequence-3.19.5}/mainsequence/cli/pydantic_cli.py +0 -0
  47. {mainsequence-3.19.3 → mainsequence-3.19.5}/mainsequence/cli/sdk_utils.py +0 -0
  48. {mainsequence-3.19.3 → mainsequence-3.19.5}/mainsequence/cli/ssh_utils.py +0 -0
  49. {mainsequence-3.19.3 → mainsequence-3.19.5}/mainsequence/cli/ui.py +0 -0
  50. {mainsequence-3.19.3 → mainsequence-3.19.5}/mainsequence/client/__init__.py +0 -0
  51. {mainsequence-3.19.3 → mainsequence-3.19.5}/mainsequence/client/agent_runtime_models.py +0 -0
  52. {mainsequence-3.19.3 → mainsequence-3.19.5}/mainsequence/client/base.py +0 -0
  53. {mainsequence-3.19.3 → mainsequence-3.19.5}/mainsequence/client/client.py +0 -0
  54. {mainsequence-3.19.3 → mainsequence-3.19.5}/mainsequence/client/command_center/__init__.py +0 -0
  55. {mainsequence-3.19.3 → mainsequence-3.19.5}/mainsequence/client/command_center/app_component.py +0 -0
  56. {mainsequence-3.19.3 → mainsequence-3.19.5}/mainsequence/client/command_center/connections.py +0 -0
  57. {mainsequence-3.19.3 → mainsequence-3.19.5}/mainsequence/client/command_center/data_models.py +0 -0
  58. {mainsequence-3.19.3 → mainsequence-3.19.5}/mainsequence/client/command_center/workspace_snapshot.py +0 -0
  59. {mainsequence-3.19.3 → mainsequence-3.19.5}/mainsequence/client/data_sources_interfaces/__init__.py +0 -0
  60. {mainsequence-3.19.3 → mainsequence-3.19.5}/mainsequence/client/data_sources_interfaces/duckdb.py +0 -0
  61. {mainsequence-3.19.3 → mainsequence-3.19.5}/mainsequence/client/data_sources_interfaces/timescale.py +0 -0
  62. {mainsequence-3.19.3 → mainsequence-3.19.5}/mainsequence/client/exceptions.py +0 -0
  63. {mainsequence-3.19.3 → mainsequence-3.19.5}/mainsequence/client/fastapi/__init__.py +0 -0
  64. {mainsequence-3.19.3 → mainsequence-3.19.5}/mainsequence/client/fastapi/auth.py +0 -0
  65. {mainsequence-3.19.3 → mainsequence-3.19.5}/mainsequence/client/models_simple_tables.py +0 -0
  66. {mainsequence-3.19.3 → mainsequence-3.19.5}/mainsequence/client/models_tdag.py +0 -0
  67. {mainsequence-3.19.3 → mainsequence-3.19.5}/mainsequence/client/models_user.py +0 -0
  68. {mainsequence-3.19.3 → mainsequence-3.19.5}/mainsequence/client/models_vam.py +0 -0
  69. {mainsequence-3.19.3 → mainsequence-3.19.5}/mainsequence/client/utils.py +0 -0
  70. {mainsequence-3.19.3 → mainsequence-3.19.5}/mainsequence/compute_validation.py +0 -0
  71. {mainsequence-3.19.3 → mainsequence-3.19.5}/mainsequence/dashboards/__init__.py +0 -0
  72. {mainsequence-3.19.3 → mainsequence-3.19.5}/mainsequence/dashboards/streamlit/__init__.py +0 -0
  73. {mainsequence-3.19.3 → mainsequence-3.19.5}/mainsequence/dashboards/streamlit/assets/config.toml +0 -0
  74. {mainsequence-3.19.3 → mainsequence-3.19.5}/mainsequence/dashboards/streamlit/assets/favicon.png +0 -0
  75. {mainsequence-3.19.3 → mainsequence-3.19.5}/mainsequence/dashboards/streamlit/assets/image_1_base64.txt +0 -0
  76. {mainsequence-3.19.3 → mainsequence-3.19.5}/mainsequence/dashboards/streamlit/assets/image_2_base64.txt +0 -0
  77. {mainsequence-3.19.3 → mainsequence-3.19.5}/mainsequence/dashboards/streamlit/assets/image_3_base64.txt +0 -0
  78. {mainsequence-3.19.3 → mainsequence-3.19.5}/mainsequence/dashboards/streamlit/assets/image_4_base64.txt +0 -0
  79. {mainsequence-3.19.3 → mainsequence-3.19.5}/mainsequence/dashboards/streamlit/assets/image_5_base64.txt +0 -0
  80. {mainsequence-3.19.3 → mainsequence-3.19.5}/mainsequence/dashboards/streamlit/assets/logo.png +0 -0
  81. {mainsequence-3.19.3 → mainsequence-3.19.5}/mainsequence/dashboards/streamlit/components/__init__.py +0 -0
  82. {mainsequence-3.19.3 → mainsequence-3.19.5}/mainsequence/dashboards/streamlit/components/asset_select.py +0 -0
  83. {mainsequence-3.19.3 → mainsequence-3.19.5}/mainsequence/dashboards/streamlit/components/date_settings.py +0 -0
  84. {mainsequence-3.19.3 → mainsequence-3.19.5}/mainsequence/dashboards/streamlit/components/logged_user.py +0 -0
  85. {mainsequence-3.19.3 → mainsequence-3.19.5}/mainsequence/dashboards/streamlit/core/__init__.py +0 -0
  86. {mainsequence-3.19.3 → mainsequence-3.19.5}/mainsequence/dashboards/streamlit/core/theme.py +0 -0
  87. {mainsequence-3.19.3 → mainsequence-3.19.5}/mainsequence/dashboards/streamlit/instruments/__init__.py +0 -0
  88. {mainsequence-3.19.3 → mainsequence-3.19.5}/mainsequence/dashboards/streamlit/instruments/streamlit_form_factory.py +0 -0
  89. {mainsequence-3.19.3 → mainsequence-3.19.5}/mainsequence/dashboards/streamlit/pages/__init__.py +0 -0
  90. {mainsequence-3.19.3 → mainsequence-3.19.5}/mainsequence/dashboards/streamlit/scaffold.py +0 -0
  91. {mainsequence-3.19.3 → mainsequence-3.19.5}/mainsequence/defaults.py +0 -0
  92. {mainsequence-3.19.3 → mainsequence-3.19.5}/mainsequence/instrumentation/__init__.py +0 -0
  93. {mainsequence-3.19.3 → mainsequence-3.19.5}/mainsequence/instrumentation/utils.py +0 -0
  94. {mainsequence-3.19.3 → mainsequence-3.19.5}/mainsequence/instruments/__init__.py +0 -0
  95. {mainsequence-3.19.3 → mainsequence-3.19.5}/mainsequence/instruments/data_interface/__init__.py +0 -0
  96. {mainsequence-3.19.3 → mainsequence-3.19.5}/mainsequence/instruments/data_interface/data_interface.py +0 -0
  97. {mainsequence-3.19.3 → mainsequence-3.19.5}/mainsequence/instruments/instruments/__init__.py +0 -0
  98. {mainsequence-3.19.3 → mainsequence-3.19.5}/mainsequence/instruments/instruments/base_instrument.py +0 -0
  99. {mainsequence-3.19.3 → mainsequence-3.19.5}/mainsequence/instruments/instruments/bond.py +0 -0
  100. {mainsequence-3.19.3 → mainsequence-3.19.5}/mainsequence/instruments/instruments/callability.py +0 -0
  101. {mainsequence-3.19.3 → mainsequence-3.19.5}/mainsequence/instruments/instruments/interest_rate_swap.py +0 -0
  102. {mainsequence-3.19.3 → mainsequence-3.19.5}/mainsequence/instruments/instruments/json_codec.py +0 -0
  103. {mainsequence-3.19.3 → mainsequence-3.19.5}/mainsequence/instruments/instruments/position.py +0 -0
  104. {mainsequence-3.19.3 → mainsequence-3.19.5}/mainsequence/instruments/instruments/ql_fields.py +0 -0
  105. {mainsequence-3.19.3 → mainsequence-3.19.5}/mainsequence/instruments/interest_rates/__init__.py +0 -0
  106. {mainsequence-3.19.3 → mainsequence-3.19.5}/mainsequence/instruments/interest_rates/etl/__init__.py +0 -0
  107. {mainsequence-3.19.3 → mainsequence-3.19.5}/mainsequence/instruments/interest_rates/etl/curve_codec.py +0 -0
  108. {mainsequence-3.19.3 → mainsequence-3.19.5}/mainsequence/instruments/interest_rates/etl/nodes.py +0 -0
  109. {mainsequence-3.19.3 → mainsequence-3.19.5}/mainsequence/instruments/interest_rates/etl/registry.py +0 -0
  110. {mainsequence-3.19.3 → mainsequence-3.19.5}/mainsequence/instruments/pricing_models/__init__.py +0 -0
  111. {mainsequence-3.19.3 → mainsequence-3.19.5}/mainsequence/instruments/pricing_models/bond_pricer.py +0 -0
  112. {mainsequence-3.19.3 → mainsequence-3.19.5}/mainsequence/instruments/pricing_models/indices.py +0 -0
  113. {mainsequence-3.19.3 → mainsequence-3.19.5}/mainsequence/instruments/pricing_models/indices_builders.py +0 -0
  114. {mainsequence-3.19.3 → mainsequence-3.19.5}/mainsequence/instruments/pricing_models/swap_pricer.py +0 -0
  115. {mainsequence-3.19.3 → mainsequence-3.19.5}/mainsequence/instruments/settings.py +0 -0
  116. {mainsequence-3.19.3 → mainsequence-3.19.5}/mainsequence/instruments/utils.py +0 -0
  117. {mainsequence-3.19.3 → mainsequence-3.19.5}/mainsequence/logconf.py +0 -0
  118. {mainsequence-3.19.3 → mainsequence-3.19.5}/mainsequence/runtime_flags.py +0 -0
  119. {mainsequence-3.19.3 → mainsequence-3.19.5}/mainsequence/tdag/__init__.py +0 -0
  120. {mainsequence-3.19.3 → mainsequence-3.19.5}/mainsequence/tdag/__main__.py +0 -0
  121. {mainsequence-3.19.3 → mainsequence-3.19.5}/mainsequence/tdag/base_persist_managers.py +0 -0
  122. {mainsequence-3.19.3 → mainsequence-3.19.5}/mainsequence/tdag/config.py +0 -0
  123. {mainsequence-3.19.3 → mainsequence-3.19.5}/mainsequence/tdag/configuration_models.py +0 -0
  124. {mainsequence-3.19.3 → mainsequence-3.19.5}/mainsequence/tdag/data_nodes/__init__.py +0 -0
  125. {mainsequence-3.19.3 → mainsequence-3.19.5}/mainsequence/tdag/data_nodes/build_operations.py +0 -0
  126. {mainsequence-3.19.3 → mainsequence-3.19.5}/mainsequence/tdag/data_nodes/data_nodes.py +0 -0
  127. {mainsequence-3.19.3 → mainsequence-3.19.5}/mainsequence/tdag/data_nodes/filters.py +0 -0
  128. {mainsequence-3.19.3 → mainsequence-3.19.5}/mainsequence/tdag/data_nodes/models.py +0 -0
  129. {mainsequence-3.19.3 → mainsequence-3.19.5}/mainsequence/tdag/data_nodes/namespacing.py +0 -0
  130. {mainsequence-3.19.3 → mainsequence-3.19.5}/mainsequence/tdag/data_nodes/persist_managers.py +0 -0
  131. {mainsequence-3.19.3 → mainsequence-3.19.5}/mainsequence/tdag/data_nodes/run_operations.py +0 -0
  132. {mainsequence-3.19.3 → mainsequence-3.19.5}/mainsequence/tdag/data_nodes/utils.py +0 -0
  133. {mainsequence-3.19.3 → mainsequence-3.19.5}/mainsequence/tdag/filters.py +0 -0
  134. {mainsequence-3.19.3 → mainsequence-3.19.5}/mainsequence/tdag/future_registry.py +0 -0
  135. {mainsequence-3.19.3 → mainsequence-3.19.5}/mainsequence/tdag/pydantic_metadata.py +0 -0
  136. {mainsequence-3.19.3 → mainsequence-3.19.5}/mainsequence/tdag/simple_tables/__init__.py +0 -0
  137. {mainsequence-3.19.3 → mainsequence-3.19.5}/mainsequence/tdag/simple_tables/filters.py +0 -0
  138. {mainsequence-3.19.3 → mainsequence-3.19.5}/mainsequence/tdag/simple_tables/models.py +0 -0
  139. {mainsequence-3.19.3 → mainsequence-3.19.5}/mainsequence/tdag/simple_tables/persist_managers.py +0 -0
  140. {mainsequence-3.19.3 → mainsequence-3.19.5}/mainsequence/tdag/simple_tables/schema.py +0 -0
  141. {mainsequence-3.19.3 → mainsequence-3.19.5}/mainsequence/tdag/simple_tables/table_nodes.py +0 -0
  142. {mainsequence-3.19.3 → mainsequence-3.19.5}/mainsequence/tdag/utils.py +0 -0
  143. {mainsequence-3.19.3 → mainsequence-3.19.5}/mainsequence/virtualfundbuilder/__init__.py +0 -0
  144. {mainsequence-3.19.3 → mainsequence-3.19.5}/mainsequence/virtualfundbuilder/contrib/__init__.py +0 -0
  145. {mainsequence-3.19.3 → mainsequence-3.19.5}/mainsequence/virtualfundbuilder/contrib/data_nodes/__init__.py +0 -0
  146. {mainsequence-3.19.3 → mainsequence-3.19.5}/mainsequence/virtualfundbuilder/contrib/data_nodes/external_weights.py +0 -0
  147. {mainsequence-3.19.3 → mainsequence-3.19.5}/mainsequence/virtualfundbuilder/contrib/data_nodes/intraday_trend.py +0 -0
  148. {mainsequence-3.19.3 → mainsequence-3.19.5}/mainsequence/virtualfundbuilder/contrib/data_nodes/market_cap.py +0 -0
  149. {mainsequence-3.19.3 → mainsequence-3.19.5}/mainsequence/virtualfundbuilder/contrib/data_nodes/mock_signal.py +0 -0
  150. {mainsequence-3.19.3 → mainsequence-3.19.5}/mainsequence/virtualfundbuilder/contrib/data_nodes/portfolio_replicator.py +0 -0
  151. {mainsequence-3.19.3 → mainsequence-3.19.5}/mainsequence/virtualfundbuilder/contrib/prices/__init__.py +0 -0
  152. {mainsequence-3.19.3 → mainsequence-3.19.5}/mainsequence/virtualfundbuilder/contrib/prices/data_nodes.py +0 -0
  153. {mainsequence-3.19.3 → mainsequence-3.19.5}/mainsequence/virtualfundbuilder/contrib/prices/utils.py +0 -0
  154. {mainsequence-3.19.3 → mainsequence-3.19.5}/mainsequence/virtualfundbuilder/contrib/rebalance_strategies/__init__.py +0 -0
  155. {mainsequence-3.19.3 → mainsequence-3.19.5}/mainsequence/virtualfundbuilder/contrib/rebalance_strategies/rebalance_strategies.py +0 -0
  156. {mainsequence-3.19.3 → mainsequence-3.19.5}/mainsequence/virtualfundbuilder/enums.py +0 -0
  157. {mainsequence-3.19.3 → mainsequence-3.19.5}/mainsequence/virtualfundbuilder/models.py +0 -0
  158. {mainsequence-3.19.3 → mainsequence-3.19.5}/mainsequence/virtualfundbuilder/portfolio_nodes.py +0 -0
  159. {mainsequence-3.19.3 → mainsequence-3.19.5}/mainsequence/virtualfundbuilder/resource_factory/__init__.py +0 -0
  160. {mainsequence-3.19.3 → mainsequence-3.19.5}/mainsequence/virtualfundbuilder/resource_factory/rebalance_factory.py +0 -0
  161. {mainsequence-3.19.3 → mainsequence-3.19.5}/mainsequence/virtualfundbuilder/resource_factory/signal_factory.py +0 -0
  162. {mainsequence-3.19.3 → mainsequence-3.19.5}/mainsequence/virtualfundbuilder/utils.py +0 -0
  163. {mainsequence-3.19.3 → mainsequence-3.19.5}/mainsequence.egg-info/SOURCES.txt +0 -0
  164. {mainsequence-3.19.3 → mainsequence-3.19.5}/mainsequence.egg-info/dependency_links.txt +0 -0
  165. {mainsequence-3.19.3 → mainsequence-3.19.5}/mainsequence.egg-info/entry_points.txt +0 -0
  166. {mainsequence-3.19.3 → mainsequence-3.19.5}/mainsequence.egg-info/requires.txt +0 -0
  167. {mainsequence-3.19.3 → mainsequence-3.19.5}/mainsequence.egg-info/top_level.txt +0 -0
  168. {mainsequence-3.19.3 → mainsequence-3.19.5}/setup.cfg +0 -0
  169. {mainsequence-3.19.3 → mainsequence-3.19.5}/tests/test_auth_precedence.py +0 -0
  170. {mainsequence-3.19.3 → mainsequence-3.19.5}/tests/test_build_operations_hashing.py +0 -0
  171. {mainsequence-3.19.3 → mainsequence-3.19.5}/tests/test_cli_browser_auth.py +0 -0
  172. {mainsequence-3.19.3 → mainsequence-3.19.5}/tests/test_client.py +0 -0
  173. {mainsequence-3.19.3 → mainsequence-3.19.5}/tests/test_command_center_app_component_models.py +0 -0
  174. {mainsequence-3.19.3 → mainsequence-3.19.5}/tests/test_command_center_data_models.py +0 -0
  175. {mainsequence-3.19.3 → mainsequence-3.19.5}/tests/test_command_center_models.py +0 -0
  176. {mainsequence-3.19.3 → mainsequence-3.19.5}/tests/test_dependency_extras.py +0 -0
  177. {mainsequence-3.19.3 → mainsequence-3.19.5}/tests/test_filter_normalization.py +0 -0
  178. {mainsequence-3.19.3 → mainsequence-3.19.5}/tests/test_instruments.py +0 -0
  179. {mainsequence-3.19.3 → mainsequence-3.19.5}/tests/test_logconf.py +0 -0
  180. {mainsequence-3.19.3 → mainsequence-3.19.5}/tests/test_logged_user_components.py +0 -0
  181. {mainsequence-3.19.3 → mainsequence-3.19.5}/tests/test_models_user_request_bound_auth.py +0 -0
  182. {mainsequence-3.19.3 → mainsequence-3.19.5}/tests/test_pod_project_resolution.py +0 -0
  183. {mainsequence-3.19.3 → mainsequence-3.19.5}/tests/test_run_configuration.py +0 -0
  184. {mainsequence-3.19.3 → mainsequence-3.19.5}/tests/test_simple_tables_configuration_hashing.py +0 -0
  185. {mainsequence-3.19.3 → mainsequence-3.19.5}/tests/test_simple_tables_persistence.py +0 -0
  186. {mainsequence-3.19.3 → mainsequence-3.19.5}/tests/test_workspace_snapshot.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: mainsequence
3
- Version: 3.19.3
3
+ Version: 3.19.5
4
4
  Summary: Main Sequence SDK
5
5
  Author-email: Main Sequence GmbH <dev@main-sequence.io>
6
6
  License: MainSequence GmbH SDK License Agreement
@@ -154,6 +154,7 @@ Recommended entry points:
154
154
  - [Working With Simple Tables](docs/tutorial/working_with_simple_tables.md)
155
155
  - [Create Your First API](docs/tutorial/create_your_first_api.md)
156
156
  - [Role-Based Access Control](docs/tutorial/role_based_access_control.md)
157
+ - [Turn Your Project Into an Agent](docs/tutorial/project_to_agent.md)
157
158
  - Knowledge:
158
159
  - [Data Nodes](docs/knowledge/data_nodes.md)
159
160
  - [Command Center](docs/knowledge/command_center/index.md)
@@ -209,6 +210,7 @@ From there, the normal learning path is:
209
210
  4. understand sharing and RBAC
210
211
  5. schedule jobs
211
212
  6. build dashboards or downstream consumers
213
+ 7. package the project as an agent-facing surface when the repository is ready
212
214
 
213
215
  ## Installation for development
214
216
 
@@ -64,6 +64,7 @@ Recommended entry points:
64
64
  - [Working With Simple Tables](docs/tutorial/working_with_simple_tables.md)
65
65
  - [Create Your First API](docs/tutorial/create_your_first_api.md)
66
66
  - [Role-Based Access Control](docs/tutorial/role_based_access_control.md)
67
+ - [Turn Your Project Into an Agent](docs/tutorial/project_to_agent.md)
67
68
  - Knowledge:
68
69
  - [Data Nodes](docs/knowledge/data_nodes.md)
69
70
  - [Command Center](docs/knowledge/command_center/index.md)
@@ -119,6 +120,7 @@ From there, the normal learning path is:
119
120
  4. understand sharing and RBAC
120
121
  5. schedule jobs
121
122
  6. build dashboards or downstream consumers
123
+ 7. package the project as an agent-facing surface when the repository is ready
122
124
 
123
125
  ## Installation for development
124
126
 
@@ -18,9 +18,9 @@ uses them to update only the Main Sequence section below.
18
18
  ## Main Sequence Instructions
19
19
 
20
20
  Before any non-trivial Main Sequence work, verify that this Main Sequence section matches the
21
- latest installed `agent_scaffold/AGENTS.md` template and that
21
+ latest installed `AGENTS.md` template and that
22
22
  `.agents/skills/mainsequence/project_builder/SKILL.md` matches the latest installed
23
- `agent_scaffold/skills/project_builder/SKILL.md`; if either local file does not match, update it
23
+ `.agents/skills/project_builder/SKILL.md`; if either local file does not match, update it
24
24
  before proceeding.
25
25
 
26
26
  Canonical Main Sequence documentation root:
@@ -69,25 +69,32 @@ This skill must not claim ownership of:
69
69
 
70
70
  0. If widget selection, layout narrative, or visualization strategy is not already decided, use:
71
71
  - `.agents/skills/mainsequence/command_center/workspace_design/SKILL.md`
72
- 1. Verify the widget catalog through the CLI:
72
+ 1. If the workspace may use connection-backed source widgets, inspect the available connections through the CLI first:
73
+ - `mainsequence cc connection list --json`
74
+ - identify the target connection instance `uid`
75
+ - `mainsequence cc connection detail <CONNECTION_UID> --json`
76
+ - `mainsequence cc connection_type list --json`
77
+ - identify the target connection `type_id`
78
+ - `mainsequence cc connection_type detail <TYPE_ID> --json`
79
+ 2. Verify the widget catalog through the CLI:
73
80
  - `mainsequence cc registered_widget_type list --json`
74
81
  - identify the target `widget_id`
75
82
  - `mainsequence cc registered_widget_type detail <WIDGET_ID> --json`
76
- 2. The SDK client models in `mainsequence/client/command_center/`:
83
+ 3. The SDK client models in `mainsequence/client/command_center/`:
77
84
  - `workspace.py`
78
85
  - `connections.py` when source widgets depend on backend-owned connections
79
86
  - `data_models.py`
80
87
  - `app_component.py` when the workspace contains AppComponent widgets or editable form payloads
81
- 3. `docs/knowledge/command_center/workspaces.md`
82
- 4. the local Main Sequence docs/models/examples in this repository that define the widget payloads being mounted
83
- 5. the current CLI docs if the task uses CLI workflow
88
+ 4. `docs/knowledge/command_center/workspaces.md`
89
+ 5. the local Main Sequence docs/models/examples in this repository that define the widget payloads being mounted
90
+ 6. the current CLI docs if the task uses CLI workflow
84
91
 
85
92
  If the workspace contains AppComponent widgets, also read:
86
93
 
87
- 6. `docs/knowledge/command_center/forms.md`
88
- 7. `docs/knowledge/command_center/widget_data_contracts.md`
89
- 8. `.agents/skills/mainsequence/platform_operations/orchestration_and_releases/SKILL.md` when mounted widgets depend on project APIs that must be usable from Command Center
90
- 9. `.agents/skills/mainsequence/command_center/api_mock_prototyping/SKILL.md` when the workspace should validate an AppComponent/API contract in `mock-json` mode before deployment
94
+ 7. `docs/knowledge/command_center/forms.md`
95
+ 8. `docs/knowledge/command_center/widget_data_contracts.md`
96
+ 9. `.agents/skills/mainsequence/platform_operations/orchestration_and_releases/SKILL.md` when mounted widgets depend on project APIs that must be usable from Command Center
97
+ 10. `.agents/skills/mainsequence/command_center/api_mock_prototyping/SKILL.md` when the workspace should validate an AppComponent/API contract in `mock-json` mode before deployment
91
98
 
92
99
  ## Command Center Mental Model
93
100
 
@@ -18,6 +18,7 @@ import importlib
18
18
  import json
19
19
  import os
20
20
  import re
21
+ import shlex
21
22
  from typing import Any
22
23
  from urllib.parse import urlencode
23
24
 
@@ -4679,6 +4680,30 @@ def schedule_batch_project_jobs(
4679
4680
  os.environ[k] = v
4680
4681
 
4681
4682
 
4683
+ def get_project_job(
4684
+ job_id: int | str,
4685
+ *,
4686
+ timeout: int | None = None,
4687
+ ) -> dict[str, Any]:
4688
+ """
4689
+ Retrieve one project job via SDK client model.
4690
+ """
4691
+ try:
4692
+ job = _run_sdk_model_operation(
4693
+ module_name="mainsequence.client.models_helpers",
4694
+ class_name="Job",
4695
+ operation=lambda ClientJob: ClientJob.get(pk=int(job_id), timeout=timeout),
4696
+ )
4697
+ return _sdk_object_to_dict(job)
4698
+ except Exception as e:
4699
+ err_name = type(e).__name__
4700
+ if err_name == "NotFoundError":
4701
+ raise ApiError(f"Job not found: {job_id}") from e
4702
+ if isinstance(e, (ApiError, NotLoggedIn)):
4703
+ raise
4704
+ raise ApiError(f"Project job fetch failed: {e}") from e
4705
+
4706
+
4682
4707
  def run_project_job(
4683
4708
  job_id: int | str,
4684
4709
  *,
@@ -4738,11 +4763,35 @@ def run_project_job(
4738
4763
 
4739
4764
  job = ClientJob.get(pk=int(job_id), timeout=timeout)
4740
4765
  payload = job.run_job(timeout=timeout, command_args=command_args)
4766
+ effective_tokens: list[str] = []
4767
+ execution_path = str(getattr(job, "execution_path", "") or "").strip()
4768
+ app_name = str(getattr(job, "app_name", "") or "").strip()
4769
+ if execution_path:
4770
+ effective_tokens.append(execution_path)
4771
+ elif app_name:
4772
+ effective_tokens.append(f"app:{app_name}")
4773
+ if command_args:
4774
+ effective_tokens.extend(str(arg) for arg in command_args)
4775
+ effective_run = shlex.join(effective_tokens) if effective_tokens else None
4741
4776
  if isinstance(payload, dict):
4777
+ if effective_run:
4778
+ payload.setdefault("effective_run", effective_run)
4779
+ if command_args is not None:
4780
+ payload.setdefault("command_args", list(command_args))
4742
4781
  return payload
4743
4782
  if hasattr(payload, "model_dump"):
4744
- return payload.model_dump()
4745
- return {"job_id": int(job_id)}
4783
+ payload = payload.model_dump()
4784
+ if effective_run:
4785
+ payload.setdefault("effective_run", effective_run)
4786
+ if command_args is not None:
4787
+ payload.setdefault("command_args", list(command_args))
4788
+ return payload
4789
+ out = {"job_id": int(job_id)}
4790
+ if effective_run:
4791
+ out["effective_run"] = effective_run
4792
+ if command_args is not None:
4793
+ out["command_args"] = list(command_args)
4794
+ return out
4746
4795
 
4747
4796
  except Exception as e:
4748
4797
  err_name = type(e).__name__
@@ -31,6 +31,7 @@ import os
31
31
  import pathlib
32
32
  import platform
33
33
  import re
34
+ import shlex
34
35
  import shutil
35
36
  import subprocess
36
37
  import sys
@@ -114,6 +115,7 @@ from .api import (
114
115
  get_project,
115
116
  get_project_data_node_updates,
116
117
  get_project_image,
118
+ get_project_job,
117
119
  get_project_job_run_logs,
118
120
  get_projects,
119
121
  get_registered_widget_type,
@@ -8834,42 +8836,6 @@ def project_project_resource_create_dashboard_cmd(
8834
8836
  )
8835
8837
 
8836
8838
 
8837
- @project_project_resource_group.command("create_agent")
8838
- def project_project_resource_create_agent_cmd(
8839
- project_id: int | None = typer.Argument(None, help="Project ID. Defaults to local .env when omitted."),
8840
- resource_id: int | None = typer.Option(None, "--resource-id", help="Project resource ID."),
8841
- path: str | None = typer.Option(None, "--path", help="Project repository path (default: current project)"),
8842
- related_image_id: int | None = typer.Option(None, "--related-image-id", help="Project image ID."),
8843
- readme_resource_id: int | None = typer.Option(None, "--readme-resource-id", help="Optional README resource ID."),
8844
- cpu_request: str | None = typer.Option(None, "--cpu-request", help="CPU request (accepts 0.5 or 500m; default: 0.25)."),
8845
- memory_request: str | None = typer.Option(None, "--memory-request", help="Memory request (accepts 1 or 1Gi; default: 0.5)."),
8846
- gpu_request: str | None = typer.Option(None, "--gpu-request", help="GPU request count."),
8847
- gpu_type: str | None = typer.Option(None, "--gpu-type", help="GPU accelerator type."),
8848
- spot: bool | None = typer.Option(None, "--spot/--no-spot", help="Whether to prefer spot capacity."),
8849
- timeout: int | None = typer.Option(None, "--timeout", help="Request timeout in seconds"),
8850
- ):
8851
- """
8852
- Create an agent release from a project resource.
8853
-
8854
- The command first lets the user select a project image and then filters resources so
8855
- only resources with `repo_commit_sha == related_image.project_repo_hash` are eligible.
8856
- """
8857
- _project_resource_release_create_impl(
8858
- release_kind="agent",
8859
- project_id=project_id,
8860
- resource_id=resource_id,
8861
- path=path,
8862
- related_image_id=related_image_id,
8863
- readme_resource_id=readme_resource_id,
8864
- cpu_request=cpu_request,
8865
- memory_request=memory_request,
8866
- gpu_request=gpu_request,
8867
- gpu_type=gpu_type,
8868
- spot=spot,
8869
- timeout=timeout,
8870
- )
8871
-
8872
-
8873
8839
  @project_project_resource_group.command("create_fastapi")
8874
8840
  def project_project_resource_create_fastapi_cmd(
8875
8841
  project_id: int | None = typer.Argument(None, help="Project ID. Defaults to local .env when omitted."),
@@ -8977,30 +8943,6 @@ def project_project_resource_delete_dashboard_cmd(
8977
8943
  )
8978
8944
 
8979
8945
 
8980
- @project_project_resource_group.command("delete_agent")
8981
- def project_project_resource_delete_agent_cmd(
8982
- release_id: int = typer.Argument(..., help="Agent resource release ID."),
8983
- yes: bool = typer.Option(False, "--yes", help="Delete without confirmation."),
8984
- timeout: int | None = typer.Option(None, "--timeout", help="Request timeout in seconds"),
8985
- ):
8986
- """
8987
- Delete an agent resource release.
8988
-
8989
- Examples
8990
- --------
8991
- ```bash
8992
- mainsequence project project_resource delete_agent 601
8993
- mainsequence project project_resource delete_agent 601 --yes
8994
- ```
8995
- """
8996
- _project_resource_release_delete_impl(
8997
- release_id=release_id,
8998
- expected_release_kind="agent",
8999
- yes=yes,
9000
- timeout=timeout,
9001
- )
9002
-
9003
-
9004
8946
  @project_project_resource_group.command("delete_fastapi")
9005
8947
  def project_project_resource_delete_fastapi_cmd(
9006
8948
  release_id: int = typer.Argument(..., help="FastAPI resource release ID."),
@@ -9574,10 +9516,14 @@ def project_jobs_list_cmd(
9574
9516
  @project_jobs_group.command("run")
9575
9517
  def project_jobs_run_cmd(
9576
9518
  job_id: int = pydantic_argument(JOB_MODEL_REF, "id", ..., help="Job ID to run."),
9519
+ passthrough_args: list[str] | None = typer.Argument(
9520
+ None,
9521
+ help="Additional per-run args after `--`, for example `mainsequence project jobs run 91 -- --name demo`.",
9522
+ ),
9577
9523
  command_args: list[str] | None = typer.Option(
9578
9524
  None,
9579
- "--command",
9580
- help="Per-run command argument. Repeat to send a list of strings to Job.run_job().",
9525
+ "--arg",
9526
+ help="Append one per-run arg to the saved job entrypoint. Repeatable. Does not replace the saved execution_path or app_name.",
9581
9527
  ),
9582
9528
  timeout: int | None = typer.Option(None, "--timeout", help="Request timeout in seconds"),
9583
9529
  ):
@@ -9585,21 +9531,43 @@ def project_jobs_run_cmd(
9585
9531
  Run a project job immediately.
9586
9532
 
9587
9533
  Uses SDK client `Job.run_job()` as the single source of truth.
9534
+ Per-run args are appended to the saved job entrypoint; they do not replace it.
9588
9535
 
9589
9536
  Examples
9590
9537
  --------
9591
9538
  ```bash
9592
9539
  mainsequence project jobs run 91
9593
- mainsequence project jobs run 91 --command python --command -m --command jobs.daily
9540
+ mainsequence project jobs run 91 --arg demo-from-cli
9541
+ mainsequence project jobs run 91 -- --name demo-from-cli
9594
9542
  mainsequence project jobs run 91 --timeout 60
9595
9543
  ```
9596
9544
  """
9597
9545
  _require_login()
9598
9546
 
9547
+ merged_command_args = list(command_args or [])
9548
+ if passthrough_args:
9549
+ merged_command_args.extend(str(arg) for arg in passthrough_args)
9550
+
9551
+ try:
9552
+ job_payload = get_project_job(job_id, timeout=timeout)
9553
+ except ApiError as e:
9554
+ error(f"Project job fetch failed: {e}")
9555
+ raise typer.Exit(1) from e
9556
+
9557
+ entrypoint = str(job_payload.get("execution_path") or "").strip()
9558
+ if not entrypoint:
9559
+ app_name = str(job_payload.get("app_name") or "").strip()
9560
+ if app_name:
9561
+ entrypoint = f"app:{app_name}"
9562
+
9563
+ if entrypoint:
9564
+ effective_tokens = [entrypoint, *merged_command_args]
9565
+ info(f"Effective run: {shlex.join(effective_tokens)}")
9566
+
9599
9567
  try:
9600
9568
  payload = run_project_job(
9601
9569
  job_id=job_id,
9602
- command_args=list(command_args) if command_args else None,
9570
+ command_args=merged_command_args or None,
9603
9571
  timeout=timeout,
9604
9572
  )
9605
9573
  except ApiError as e:
@@ -10625,7 +10593,7 @@ def project_refresh_token(
10625
10593
  def project_freeze_env(
10626
10594
  project_id: int | None = typer.Argument(None, help="Project ID"),
10627
10595
  path: str | None = typer.Option(None, "--path", help="Project directory"),
10628
- ensure_uv: bool = typer.Option(True, "--ensure-uv/--no-ensure-uv", help="Install uv into .venv if missing"),
10596
+ ensure_uv: bool = typer.Option(True, "--ensure-uv/--no-ensure-uv", help="Allow resolving uv from PATH when it is not present inside .venv."),
10629
10597
  ):
10630
10598
  """
10631
10599
  Export pinned dependencies into `requirements.txt` using `uv`.
@@ -10637,7 +10605,7 @@ def project_freeze_env(
10637
10605
  path:
10638
10606
  Explicit local path.
10639
10607
  ensure_uv:
10640
- Install `uv` in `.venv` if missing.
10608
+ Allow resolving `uv` from PATH when it is not present inside `.venv`.
10641
10609
 
10642
10610
  Examples
10643
10611
  --------
@@ -10736,7 +10704,7 @@ def project_sync(
10736
10704
  raise typer.Exit(1)
10737
10705
 
10738
10706
  steps = [
10739
- "pip install uv (in .venv)",
10707
+ "resolve uv executable",
10740
10708
  f"uv version --bump {bump}",
10741
10709
  "uv lock",
10742
10710
  "uv sync",
@@ -11045,7 +11013,7 @@ def project_update_sdk(
11045
11013
  ensure_venv(project_dir)
11046
11014
 
11047
11015
  steps = [
11048
- "pip install uv (in .venv)",
11016
+ "resolve uv executable",
11049
11017
  "uv lock --upgrade-package mainsequence",
11050
11018
  "uv sync",
11051
11019
  ]
@@ -13,6 +13,7 @@ Local operations shared by several commands:
13
13
 
14
14
  import os
15
15
  import pathlib
16
+ import shutil
16
17
  import subprocess
17
18
  import sys
18
19
  from dataclasses import dataclass
@@ -62,7 +63,7 @@ def ensure_venv(project_dir: pathlib.Path) -> VenvPaths:
62
63
 
63
64
  def ensure_uv_installed(project_dir: pathlib.Path, upgrade: bool = True) -> pathlib.Path:
64
65
  """
65
- Ensure uv is installed inside the project's .venv.
66
+ Resolve a usable uv executable for the project workflow.
66
67
 
67
68
  Returns:
68
69
  Path to uv executable
@@ -71,18 +72,11 @@ def ensure_uv_installed(project_dir: pathlib.Path, upgrade: bool = True) -> path
71
72
  if vp.uv and vp.uv.exists():
72
73
  return vp.uv
73
74
 
74
- args = [str(vp.python), "-m", "pip", "install"]
75
- if upgrade:
76
- args.append("--upgrade")
77
- args.append("uv")
78
- r = subprocess.run(args, cwd=str(project_dir))
79
- if r.returncode != 0:
80
- raise RuntimeError("Failed to install uv into the virtual environment.")
75
+ uv_bin = shutil.which("uv")
76
+ if uv_bin:
77
+ return pathlib.Path(uv_bin)
81
78
 
82
- vp = venv_paths(project_dir)
83
- if not vp.uv or not vp.uv.exists():
84
- raise RuntimeError("uv installed but executable not found in .venv.")
85
- return vp.uv
79
+ raise RuntimeError("uv executable not found. Install uv and ensure it is available in PATH.")
86
80
 
87
81
 
88
82
  def run_cmd(cmd: list[str], cwd: pathlib.Path, env: dict[str, str] | None = None) -> subprocess.CompletedProcess:
@@ -101,7 +101,7 @@ class Workspace(LabelableObjectMixin, ShareableObjectMixin, CommandCenterBaseObj
101
101
  description="Free-form workspace description.",
102
102
  )
103
103
  type: WorkspaceType = Field(default=WorkspaceType.WORKSPACE)
104
- public_url:str=Field(description="Public URL for workspace endpoint if exists.", default="")
104
+ public_url:str| None =Field(description="Public URL for workspace endpoint if exists.", default=None)
105
105
  created_at:datetime=Field()
106
106
  labels: list[str] = Field(
107
107
  default_factory=list,
@@ -728,7 +728,7 @@ class Job(BaseObjectOrm, BasePydanticModel):
728
728
 
729
729
  payload: dict[str, Any] = {}
730
730
  if command_args is not None:
731
- payload["command_args"] = command_args
731
+ payload["json"] = {"command_args": list(command_args)}
732
732
 
733
733
  r = make_request(
734
734
  s=s,
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: mainsequence
3
- Version: 3.19.3
3
+ Version: 3.19.5
4
4
  Summary: Main Sequence SDK
5
5
  Author-email: Main Sequence GmbH <dev@main-sequence.io>
6
6
  License: MainSequence GmbH SDK License Agreement
@@ -154,6 +154,7 @@ Recommended entry points:
154
154
  - [Working With Simple Tables](docs/tutorial/working_with_simple_tables.md)
155
155
  - [Create Your First API](docs/tutorial/create_your_first_api.md)
156
156
  - [Role-Based Access Control](docs/tutorial/role_based_access_control.md)
157
+ - [Turn Your Project Into an Agent](docs/tutorial/project_to_agent.md)
157
158
  - Knowledge:
158
159
  - [Data Nodes](docs/knowledge/data_nodes.md)
159
160
  - [Command Center](docs/knowledge/command_center/index.md)
@@ -209,6 +210,7 @@ From there, the normal learning path is:
209
210
  4. understand sharing and RBAC
210
211
  5. schedule jobs
211
212
  6. build dashboards or downstream consumers
213
+ 7. package the project as an agent-facing surface when the repository is ready
212
214
 
213
215
  ## Installation for development
214
216
 
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "mainsequence"
7
- version = "3.19.3"
7
+ version = "3.19.5"
8
8
  description = "Main Sequence SDK "
9
9
  readme = "README.md"
10
10
  requires-python = ">=3.11"
@@ -5699,13 +5699,15 @@ def test_run_project_job_uses_client_model(cli_mod, monkeypatch):
5699
5699
  def get(cls, pk, timeout=None):
5700
5700
  captured["job_id_arg"] = pk
5701
5701
  return types.SimpleNamespace(
5702
+ execution_path="scripts/test.py",
5703
+ app_name=None,
5702
5704
  run_job=lambda timeout=None, command_args=None: (
5703
5705
  captured.update(command_args=command_args)
5704
5706
  or {
5705
- "id": 501,
5706
- "job": pk,
5707
- "status": "QUEUED",
5708
- "unique_identifier": "jobrun_abc123",
5707
+ "id": 501,
5708
+ "job": pk,
5709
+ "status": "QUEUED",
5710
+ "unique_identifier": "jobrun_abc123",
5709
5711
  }
5710
5712
  )
5711
5713
  )
@@ -5728,6 +5730,8 @@ def test_run_project_job_uses_client_model(cli_mod, monkeypatch):
5728
5730
  "job": 91,
5729
5731
  "status": "QUEUED",
5730
5732
  "unique_identifier": "jobrun_abc123",
5733
+ "effective_run": "scripts/test.py python -m jobs.daily",
5734
+ "command_args": ["python", "-m", "jobs.daily"],
5731
5735
  }
5732
5736
 
5733
5737
 
@@ -7951,6 +7955,16 @@ def test_markets_asset_translation_table_detail(cli_mod, runner, monkeypatch):
7951
7955
  def test_project_jobs_run(cli_mod, runner, monkeypatch):
7952
7956
  monkeypatch.setattr(cli_mod, "_require_login", lambda: {"username": "u"})
7953
7957
  captured = {}
7958
+ monkeypatch.setattr(
7959
+ cli_mod,
7960
+ "get_project_job",
7961
+ lambda job_id, timeout=None: {
7962
+ "id": job_id,
7963
+ "name": "daily-run",
7964
+ "execution_path": "scripts/test.py",
7965
+ "app_name": None,
7966
+ },
7967
+ )
7954
7968
  monkeypatch.setattr(
7955
7969
  cli_mod,
7956
7970
  "run_project_job",
@@ -7963,24 +7977,68 @@ def test_project_jobs_run(cli_mod, runner, monkeypatch):
7963
7977
  "job": job_id,
7964
7978
  "status": "QUEUED",
7965
7979
  "unique_identifier": "jobrun_abc123",
7980
+ "effective_run": "scripts/test.py --name demo-from-cli",
7966
7981
  },
7967
7982
  )
7968
7983
 
7969
7984
  result = runner.invoke(
7970
7985
  cli_mod.app,
7971
- ["project", "jobs", "run", "91", "--command", "python", "--command", "-m", "--command", "jobs.daily"],
7986
+ ["project", "jobs", "run", "91", "--", "--name", "demo-from-cli"],
7972
7987
  )
7973
7988
  assert result.exit_code == 0
7974
7989
  assert captured == {
7975
7990
  "job_id": 91,
7976
- "command_args": ["python", "-m", "jobs.daily"],
7991
+ "command_args": ["--name", "demo-from-cli"],
7977
7992
  "timeout": None,
7978
7993
  }
7994
+ assert "Effective run: scripts/test.py --name demo-from-cli" in result.output
7979
7995
  assert "Project job run requested: job_id=91" in result.output
7980
7996
  assert "jobrun_abc123" in result.output
7981
7997
  assert "QUEUED" in result.output
7982
7998
 
7983
7999
 
8000
+ def test_project_jobs_run_with_arg_option(cli_mod, runner, monkeypatch):
8001
+ monkeypatch.setattr(cli_mod, "_require_login", lambda: {"username": "u"})
8002
+ captured = {}
8003
+ monkeypatch.setattr(
8004
+ cli_mod,
8005
+ "get_project_job",
8006
+ lambda job_id, timeout=None: {
8007
+ "id": job_id,
8008
+ "name": "daily-run",
8009
+ "execution_path": "scripts/test.py",
8010
+ "app_name": None,
8011
+ },
8012
+ )
8013
+ monkeypatch.setattr(
8014
+ cli_mod,
8015
+ "run_project_job",
8016
+ lambda job_id, command_args=None, timeout=None: captured.update(
8017
+ job_id=job_id,
8018
+ command_args=command_args,
8019
+ timeout=timeout,
8020
+ ) or {
8021
+ "id": 501,
8022
+ "job": job_id,
8023
+ "status": "QUEUED",
8024
+ "unique_identifier": "jobrun_abc123",
8025
+ "effective_run": "scripts/test.py demo-from-cli",
8026
+ },
8027
+ )
8028
+
8029
+ result = runner.invoke(
8030
+ cli_mod.app,
8031
+ ["project", "jobs", "run", "91", "--arg", "demo-from-cli"],
8032
+ )
8033
+ assert result.exit_code == 0
8034
+ assert captured == {
8035
+ "job_id": 91,
8036
+ "command_args": ["demo-from-cli"],
8037
+ "timeout": None,
8038
+ }
8039
+ assert "Effective run: scripts/test.py demo-from-cli" in result.output
8040
+
8041
+
7984
8042
  def test_project_job_runs_list(cli_mod, runner, monkeypatch):
7985
8043
  monkeypatch.setattr(cli_mod, "_require_login", lambda: {"username": "u"})
7986
8044
  monkeypatch.setattr(
@@ -177,3 +177,46 @@ def test_job_bulk_get_or_create_rejects_invalid_job_definition(tmp_path):
177
177
 
178
178
  with pytest.raises(ValueError, match=r"jobs\[0\] is invalid"):
179
179
  Job.bulk_get_or_create(yaml_file=jobs_file, project_id=123)
180
+
181
+
182
+ def test_job_run_job_posts_command_args_as_json(monkeypatch):
183
+ models_helpers = _load_models_helpers_module()
184
+ Job = models_helpers.Job
185
+
186
+ captured = {}
187
+
188
+ class FakeResponse:
189
+ status_code = 202
190
+
191
+ def json(self):
192
+ return {"id": 501, "status": "QUEUED"}
193
+
194
+ monkeypatch.setattr(Job, "build_session", classmethod(lambda cls: object()))
195
+ monkeypatch.setattr(
196
+ Job,
197
+ "get_object_url",
198
+ classmethod(lambda cls: "https://backend.test/orm/api/pods/job"),
199
+ )
200
+
201
+ def _fake_make_request(*, s, loaders, r_type, url, payload, time_out=None):
202
+ captured["r_type"] = r_type
203
+ captured["url"] = url
204
+ captured["payload"] = payload
205
+ captured["timeout"] = time_out
206
+ return FakeResponse()
207
+
208
+ monkeypatch.setattr(models_helpers, "make_request", _fake_make_request)
209
+
210
+ job = Job(
211
+ id=91,
212
+ name="Simulated Prices",
213
+ project=123,
214
+ execution_path="scripts/simulated_prices_launcher.py",
215
+ )
216
+ out = job.run_job(timeout=30, command_args=["--name", "demo-from-cli"])
217
+
218
+ assert captured["r_type"] == "POST"
219
+ assert captured["url"] == "https://backend.test/orm/api/pods/job/91/run_job/"
220
+ assert captured["timeout"] == 30
221
+ assert captured["payload"] == {"json": {"command_args": ["--name", "demo-from-cli"]}}
222
+ assert out == {"id": 501, "status": "QUEUED"}
File without changes