openrunner-sdk 2.41.6__tar.gz → 2.41.7__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 (231) hide show
  1. {openrunner_sdk-2.41.6 → openrunner_sdk-2.41.7}/PKG-INFO +1 -1
  2. {openrunner_sdk-2.41.6 → openrunner_sdk-2.41.7}/openrunner/CLAUDE.md +42 -0
  3. {openrunner_sdk-2.41.6 → openrunner_sdk-2.41.7}/openrunner/__init__.py +9 -0
  4. {openrunner_sdk-2.41.6 → openrunner_sdk-2.41.7}/openrunner/cli.py +38 -6
  5. {openrunner_sdk-2.41.6 → openrunner_sdk-2.41.7}/openrunner/datasets.py +47 -2
  6. {openrunner_sdk-2.41.6 → openrunner_sdk-2.41.7}/openrunner/gpu/__init__.py +55 -18
  7. {openrunner_sdk-2.41.6 → openrunner_sdk-2.41.7}/openrunner/install_commands.py +89 -0
  8. openrunner_sdk-2.41.7/openrunner/llms-full.txt +258 -0
  9. openrunner_sdk-2.41.7/openrunner/llms.py +49 -0
  10. openrunner_sdk-2.41.7/openrunner/llms.txt +42 -0
  11. {openrunner_sdk-2.41.6 → openrunner_sdk-2.41.7}/openrunner/sweep.py +31 -27
  12. {openrunner_sdk-2.41.6 → openrunner_sdk-2.41.7}/pyproject.toml +7 -1
  13. {openrunner_sdk-2.41.6 → openrunner_sdk-2.41.7}/tests/test_datasets_module.py +31 -1
  14. {openrunner_sdk-2.41.6 → openrunner_sdk-2.41.7}/tests/test_gpu/test_gpu_cluster.py +46 -8
  15. {openrunner_sdk-2.41.6 → openrunner_sdk-2.41.7}/tests/test_gpu/test_sdk_gpu_api.py +20 -3
  16. openrunner_sdk-2.41.7/tests/test_llms.py +43 -0
  17. {openrunner_sdk-2.41.6 → openrunner_sdk-2.41.7}/tests/test_sweep.py +55 -2
  18. {openrunner_sdk-2.41.6 → openrunner_sdk-2.41.7}/.gitignore +0 -0
  19. {openrunner_sdk-2.41.6 → openrunner_sdk-2.41.7}/=6.0 +0 -0
  20. {openrunner_sdk-2.41.6 → openrunner_sdk-2.41.7}/=8.1 +0 -0
  21. {openrunner_sdk-2.41.6 → openrunner_sdk-2.41.7}/README.md +0 -0
  22. {openrunner_sdk-2.41.6 → openrunner_sdk-2.41.7}/openrunner/_native.py +0 -0
  23. {openrunner_sdk-2.41.6 → openrunner_sdk-2.41.7}/openrunner/_retry_transport.py +0 -0
  24. {openrunner_sdk-2.41.6 → openrunner_sdk-2.41.7}/openrunner/_safepath.py +0 -0
  25. {openrunner_sdk-2.41.6 → openrunner_sdk-2.41.7}/openrunner/_secret_rules.py +0 -0
  26. {openrunner_sdk-2.41.6 → openrunner_sdk-2.41.7}/openrunner/api_client.py +0 -0
  27. {openrunner_sdk-2.41.6 → openrunner_sdk-2.41.7}/openrunner/artifact.py +0 -0
  28. {openrunner_sdk-2.41.6 → openrunner_sdk-2.41.7}/openrunner/autoresearch.py +0 -0
  29. {openrunner_sdk-2.41.6 → openrunner_sdk-2.41.7}/openrunner/buffer.py +0 -0
  30. {openrunner_sdk-2.41.6 → openrunner_sdk-2.41.7}/openrunner/cache.py +0 -0
  31. {openrunner_sdk-2.41.6 → openrunner_sdk-2.41.7}/openrunner/config.py +0 -0
  32. {openrunner_sdk-2.41.6 → openrunner_sdk-2.41.7}/openrunner/cost.py +0 -0
  33. {openrunner_sdk-2.41.6 → openrunner_sdk-2.41.7}/openrunner/dataset.py +0 -0
  34. {openrunner_sdk-2.41.6 → openrunner_sdk-2.41.7}/openrunner/environment.py +0 -0
  35. {openrunner_sdk-2.41.6 → openrunner_sdk-2.41.7}/openrunner/evaluation.py +0 -0
  36. {openrunner_sdk-2.41.6 → openrunner_sdk-2.41.7}/openrunner/feedback.py +0 -0
  37. {openrunner_sdk-2.41.6 → openrunner_sdk-2.41.7}/openrunner/git_info.py +0 -0
  38. {openrunner_sdk-2.41.6 → openrunner_sdk-2.41.7}/openrunner/gpu/api_integration.py +0 -0
  39. {openrunner_sdk-2.41.6 → openrunner_sdk-2.41.7}/openrunner/gpu/cost_guard.py +0 -0
  40. {openrunner_sdk-2.41.6 → openrunner_sdk-2.41.7}/openrunner/gpu/gpu_types.py +0 -0
  41. {openrunner_sdk-2.41.6 → openrunner_sdk-2.41.7}/openrunner/gpu/price_compare.py +0 -0
  42. {openrunner_sdk-2.41.6 → openrunner_sdk-2.41.7}/openrunner/gpu/provider_base.py +0 -0
  43. {openrunner_sdk-2.41.6 → openrunner_sdk-2.41.7}/openrunner/gpu/providers/__init__.py +0 -0
  44. {openrunner_sdk-2.41.6 → openrunner_sdk-2.41.7}/openrunner/gpu/providers/forge_provider.py +0 -0
  45. {openrunner_sdk-2.41.6 → openrunner_sdk-2.41.7}/openrunner/gpu/providers/lambda_cloud.py +0 -0
  46. {openrunner_sdk-2.41.6 → openrunner_sdk-2.41.7}/openrunner/gpu/providers/modal_provider.py +0 -0
  47. {openrunner_sdk-2.41.6 → openrunner_sdk-2.41.7}/openrunner/gpu/providers/ovhcloud.py +0 -0
  48. {openrunner_sdk-2.41.6 → openrunner_sdk-2.41.7}/openrunner/gpu/providers/runpod.py +0 -0
  49. {openrunner_sdk-2.41.6 → openrunner_sdk-2.41.7}/openrunner/gpu/providers/vastai.py +0 -0
  50. {openrunner_sdk-2.41.6 → openrunner_sdk-2.41.7}/openrunner/gpu/runner.py +0 -0
  51. {openrunner_sdk-2.41.6 → openrunner_sdk-2.41.7}/openrunner/gpu/workspace_sync.py +0 -0
  52. {openrunner_sdk-2.41.6 → openrunner_sdk-2.41.7}/openrunner/guardrails.py +0 -0
  53. {openrunner_sdk-2.41.6 → openrunner_sdk-2.41.7}/openrunner/handover.py +0 -0
  54. {openrunner_sdk-2.41.6 → openrunner_sdk-2.41.7}/openrunner/hooks/patent-lab-post-commit +0 -0
  55. {openrunner_sdk-2.41.6 → openrunner_sdk-2.41.7}/openrunner/integration/__init__.py +0 -0
  56. {openrunner_sdk-2.41.6 → openrunner_sdk-2.41.7}/openrunner/integration/accelerate.py +0 -0
  57. {openrunner_sdk-2.41.6 → openrunner_sdk-2.41.7}/openrunner/integration/anthropic_tracer.py +0 -0
  58. {openrunner_sdk-2.41.6 → openrunner_sdk-2.41.7}/openrunner/integration/catboost.py +0 -0
  59. {openrunner_sdk-2.41.6 → openrunner_sdk-2.41.7}/openrunner/integration/diffusers.py +0 -0
  60. {openrunner_sdk-2.41.6 → openrunner_sdk-2.41.7}/openrunner/integration/fastai.py +0 -0
  61. {openrunner_sdk-2.41.6 → openrunner_sdk-2.41.7}/openrunner/integration/forced_alignment.py +0 -0
  62. {openrunner_sdk-2.41.6 → openrunner_sdk-2.41.7}/openrunner/integration/gladia.py +0 -0
  63. {openrunner_sdk-2.41.6 → openrunner_sdk-2.41.7}/openrunner/integration/gymnasium.py +0 -0
  64. {openrunner_sdk-2.41.6 → openrunner_sdk-2.41.7}/openrunner/integration/huggingface.py +0 -0
  65. {openrunner_sdk-2.41.6 → openrunner_sdk-2.41.7}/openrunner/integration/hydra.py +0 -0
  66. {openrunner_sdk-2.41.6 → openrunner_sdk-2.41.7}/openrunner/integration/ignite.py +0 -0
  67. {openrunner_sdk-2.41.6 → openrunner_sdk-2.41.7}/openrunner/integration/jax.py +0 -0
  68. {openrunner_sdk-2.41.6 → openrunner_sdk-2.41.7}/openrunner/integration/keras.py +0 -0
  69. {openrunner_sdk-2.41.6 → openrunner_sdk-2.41.7}/openrunner/integration/langchain.py +0 -0
  70. {openrunner_sdk-2.41.6 → openrunner_sdk-2.41.7}/openrunner/integration/lightgbm.py +0 -0
  71. {openrunner_sdk-2.41.6 → openrunner_sdk-2.41.7}/openrunner/integration/lightning.py +0 -0
  72. {openrunner_sdk-2.41.6 → openrunner_sdk-2.41.7}/openrunner/integration/llamaindex.py +0 -0
  73. {openrunner_sdk-2.41.6 → openrunner_sdk-2.41.7}/openrunner/integration/openai_finetune.py +0 -0
  74. {openrunner_sdk-2.41.6 → openrunner_sdk-2.41.7}/openrunner/integration/openai_tracer.py +0 -0
  75. {openrunner_sdk-2.41.6 → openrunner_sdk-2.41.7}/openrunner/integration/optuna.py +0 -0
  76. {openrunner_sdk-2.41.6 → openrunner_sdk-2.41.7}/openrunner/integration/pytorch.py +0 -0
  77. {openrunner_sdk-2.41.6 → openrunner_sdk-2.41.7}/openrunner/integration/sb3.py +0 -0
  78. {openrunner_sdk-2.41.6 → openrunner_sdk-2.41.7}/openrunner/integration/sklearn.py +0 -0
  79. {openrunner_sdk-2.41.6 → openrunner_sdk-2.41.7}/openrunner/integration/skypilot.py +0 -0
  80. {openrunner_sdk-2.41.6 → openrunner_sdk-2.41.7}/openrunner/integration/tensorflow.py +0 -0
  81. {openrunner_sdk-2.41.6 → openrunner_sdk-2.41.7}/openrunner/integration/trl.py +0 -0
  82. {openrunner_sdk-2.41.6 → openrunner_sdk-2.41.7}/openrunner/integration/tts.py +0 -0
  83. {openrunner_sdk-2.41.6 → openrunner_sdk-2.41.7}/openrunner/integration/ultralytics.py +0 -0
  84. {openrunner_sdk-2.41.6 → openrunner_sdk-2.41.7}/openrunner/integration/voice_agent.py +0 -0
  85. {openrunner_sdk-2.41.6 → openrunner_sdk-2.41.7}/openrunner/integration/whisper.py +0 -0
  86. {openrunner_sdk-2.41.6 → openrunner_sdk-2.41.7}/openrunner/integration/xgboost.py +0 -0
  87. {openrunner_sdk-2.41.6 → openrunner_sdk-2.41.7}/openrunner/launch.py +0 -0
  88. {openrunner_sdk-2.41.6 → openrunner_sdk-2.41.7}/openrunner/machine.py +0 -0
  89. {openrunner_sdk-2.41.6 → openrunner_sdk-2.41.7}/openrunner/mcp_server.py +0 -0
  90. {openrunner_sdk-2.41.6 → openrunner_sdk-2.41.7}/openrunner/media.py +0 -0
  91. {openrunner_sdk-2.41.6 → openrunner_sdk-2.41.7}/openrunner/migrate.py +0 -0
  92. {openrunner_sdk-2.41.6 → openrunner_sdk-2.41.7}/openrunner/model.py +0 -0
  93. {openrunner_sdk-2.41.6 → openrunner_sdk-2.41.7}/openrunner/model_card.py +0 -0
  94. {openrunner_sdk-2.41.6 → openrunner_sdk-2.41.7}/openrunner/offline.py +0 -0
  95. {openrunner_sdk-2.41.6 → openrunner_sdk-2.41.7}/openrunner/patent_scan.py +0 -0
  96. {openrunner_sdk-2.41.6 → openrunner_sdk-2.41.7}/openrunner/pii.py +0 -0
  97. {openrunner_sdk-2.41.6 → openrunner_sdk-2.41.7}/openrunner/plot.py +0 -0
  98. {openrunner_sdk-2.41.6 → openrunner_sdk-2.41.7}/openrunner/projects.py +0 -0
  99. {openrunner_sdk-2.41.6 → openrunner_sdk-2.41.7}/openrunner/prompt.py +0 -0
  100. {openrunner_sdk-2.41.6 → openrunner_sdk-2.41.7}/openrunner/query_api.py +0 -0
  101. {openrunner_sdk-2.41.6 → openrunner_sdk-2.41.7}/openrunner/redact.py +0 -0
  102. {openrunner_sdk-2.41.6 → openrunner_sdk-2.41.7}/openrunner/reload.py +0 -0
  103. {openrunner_sdk-2.41.6 → openrunner_sdk-2.41.7}/openrunner/research.py +0 -0
  104. {openrunner_sdk-2.41.6 → openrunner_sdk-2.41.7}/openrunner/research_policy.py +0 -0
  105. {openrunner_sdk-2.41.6 → openrunner_sdk-2.41.7}/openrunner/run.py +0 -0
  106. {openrunner_sdk-2.41.6 → openrunner_sdk-2.41.7}/openrunner/scorers.py +0 -0
  107. {openrunner_sdk-2.41.6 → openrunner_sdk-2.41.7}/openrunner/sender.py +0 -0
  108. {openrunner_sdk-2.41.6 → openrunner_sdk-2.41.7}/openrunner/session.py +0 -0
  109. {openrunner_sdk-2.41.6 → openrunner_sdk-2.41.7}/openrunner/settings.py +0 -0
  110. {openrunner_sdk-2.41.6 → openrunner_sdk-2.41.7}/openrunner/summary.py +0 -0
  111. {openrunner_sdk-2.41.6 → openrunner_sdk-2.41.7}/openrunner/sync.py +0 -0
  112. {openrunner_sdk-2.41.6 → openrunner_sdk-2.41.7}/openrunner/system_metrics.py +0 -0
  113. {openrunner_sdk-2.41.6 → openrunner_sdk-2.41.7}/openrunner/tensorboard.py +0 -0
  114. {openrunner_sdk-2.41.6 → openrunner_sdk-2.41.7}/openrunner/tools/__init__.py +0 -0
  115. {openrunner_sdk-2.41.6 → openrunner_sdk-2.41.7}/openrunner/tools/patent_report.py +0 -0
  116. {openrunner_sdk-2.41.6 → openrunner_sdk-2.41.7}/openrunner/trace.py +0 -0
  117. {openrunner_sdk-2.41.6 → openrunner_sdk-2.41.7}/openrunner/transcript_formatter.py +0 -0
  118. {openrunner_sdk-2.41.6 → openrunner_sdk-2.41.7}/openrunner/wal.py +0 -0
  119. {openrunner_sdk-2.41.6 → openrunner_sdk-2.41.7}/openrunner/wandb_compat/__init__.py +0 -0
  120. {openrunner_sdk-2.41.6 → openrunner_sdk-2.41.7}/openrunner/wandb_compat/_shim.py +0 -0
  121. {openrunner_sdk-2.41.6 → openrunner_sdk-2.41.7}/openrunner/wer.py +0 -0
  122. {openrunner_sdk-2.41.6 → openrunner_sdk-2.41.7}/scripts/gen_betterleaks_rules.py +0 -0
  123. {openrunner_sdk-2.41.6 → openrunner_sdk-2.41.7}/tests/__init__.py +0 -0
  124. {openrunner_sdk-2.41.6 → openrunner_sdk-2.41.7}/tests/conftest.py +0 -0
  125. {openrunner_sdk-2.41.6 → openrunner_sdk-2.41.7}/tests/fixtures/wandb-export-mini/run-aaa/config.json +0 -0
  126. {openrunner_sdk-2.41.6 → openrunner_sdk-2.41.7}/tests/fixtures/wandb-export-mini/run-aaa/metadata.json +0 -0
  127. {openrunner_sdk-2.41.6 → openrunner_sdk-2.41.7}/tests/fixtures/wandb-export-mini/run-aaa/metrics.jsonl +0 -0
  128. {openrunner_sdk-2.41.6 → openrunner_sdk-2.41.7}/tests/fixtures/wandb-export-mini/run-aaa/summary.json +0 -0
  129. {openrunner_sdk-2.41.6 → openrunner_sdk-2.41.7}/tests/fixtures/wandb-export-mini/run-bbb/artifacts/model-checkpoint/manifest.json +0 -0
  130. {openrunner_sdk-2.41.6 → openrunner_sdk-2.41.7}/tests/fixtures/wandb-export-mini/run-bbb/artifacts/model-checkpoint/weights.bin +0 -0
  131. {openrunner_sdk-2.41.6 → openrunner_sdk-2.41.7}/tests/fixtures/wandb-export-mini/run-bbb/artifacts/sample-image/manifest.json +0 -0
  132. {openrunner_sdk-2.41.6 → openrunner_sdk-2.41.7}/tests/fixtures/wandb-export-mini/run-bbb/artifacts/sample-image/preview.png +0 -0
  133. {openrunner_sdk-2.41.6 → openrunner_sdk-2.41.7}/tests/fixtures/wandb-export-mini/run-bbb/config.json +0 -0
  134. {openrunner_sdk-2.41.6 → openrunner_sdk-2.41.7}/tests/fixtures/wandb-export-mini/run-bbb/metadata.json +0 -0
  135. {openrunner_sdk-2.41.6 → openrunner_sdk-2.41.7}/tests/fixtures/wandb-export-mini/run-bbb/metrics.jsonl +0 -0
  136. {openrunner_sdk-2.41.6 → openrunner_sdk-2.41.7}/tests/fixtures/wandb-export-mini/run-bbb/summary.json +0 -0
  137. {openrunner_sdk-2.41.6 → openrunner_sdk-2.41.7}/tests/integration/__init__.py +0 -0
  138. {openrunner_sdk-2.41.6 → openrunner_sdk-2.41.7}/tests/integration/conftest.py +0 -0
  139. {openrunner_sdk-2.41.6 → openrunner_sdk-2.41.7}/tests/integration/test_artifact_round_trip.py +0 -0
  140. {openrunner_sdk-2.41.6 → openrunner_sdk-2.41.7}/tests/test_alert.py +0 -0
  141. {openrunner_sdk-2.41.6 → openrunner_sdk-2.41.7}/tests/test_aliases.py +0 -0
  142. {openrunner_sdk-2.41.6 → openrunner_sdk-2.41.7}/tests/test_api_client.py +0 -0
  143. {openrunner_sdk-2.41.6 → openrunner_sdk-2.41.7}/tests/test_api_client_methods.py +0 -0
  144. {openrunner_sdk-2.41.6 → openrunner_sdk-2.41.7}/tests/test_api_client_transport.py +0 -0
  145. {openrunner_sdk-2.41.6 → openrunner_sdk-2.41.7}/tests/test_artifact.py +0 -0
  146. {openrunner_sdk-2.41.6 → openrunner_sdk-2.41.7}/tests/test_buffer.py +0 -0
  147. {openrunner_sdk-2.41.6 → openrunner_sdk-2.41.7}/tests/test_cache.py +0 -0
  148. {openrunner_sdk-2.41.6 → openrunner_sdk-2.41.7}/tests/test_class_scorers.py +0 -0
  149. {openrunner_sdk-2.41.6 → openrunner_sdk-2.41.7}/tests/test_cli.py +0 -0
  150. {openrunner_sdk-2.41.6 → openrunner_sdk-2.41.7}/tests/test_cli_artifact_sweep.py +0 -0
  151. {openrunner_sdk-2.41.6 → openrunner_sdk-2.41.7}/tests/test_cli_datasets.py +0 -0
  152. {openrunner_sdk-2.41.6 → openrunner_sdk-2.41.7}/tests/test_cli_gpu_registry_deploy.py +0 -0
  153. {openrunner_sdk-2.41.6 → openrunner_sdk-2.41.7}/tests/test_cli_infra.py +0 -0
  154. {openrunner_sdk-2.41.6 → openrunner_sdk-2.41.7}/tests/test_cli_remainder.py +0 -0
  155. {openrunner_sdk-2.41.6 → openrunner_sdk-2.41.7}/tests/test_cli_topup.py +0 -0
  156. {openrunner_sdk-2.41.6 → openrunner_sdk-2.41.7}/tests/test_config.py +0 -0
  157. {openrunner_sdk-2.41.6 → openrunner_sdk-2.41.7}/tests/test_evaluation.py +0 -0
  158. {openrunner_sdk-2.41.6 → openrunner_sdk-2.41.7}/tests/test_finish.py +0 -0
  159. {openrunner_sdk-2.41.6 → openrunner_sdk-2.41.7}/tests/test_git_info.py +0 -0
  160. {openrunner_sdk-2.41.6 → openrunner_sdk-2.41.7}/tests/test_gpu/__init__.py +0 -0
  161. {openrunner_sdk-2.41.6 → openrunner_sdk-2.41.7}/tests/test_gpu/conftest.py +0 -0
  162. {openrunner_sdk-2.41.6 → openrunner_sdk-2.41.7}/tests/test_gpu/test_cost_guard.py +0 -0
  163. {openrunner_sdk-2.41.6 → openrunner_sdk-2.41.7}/tests/test_gpu/test_engine_serverside.py +0 -0
  164. {openrunner_sdk-2.41.6 → openrunner_sdk-2.41.7}/tests/test_gpu/test_forge.py +0 -0
  165. {openrunner_sdk-2.41.6 → openrunner_sdk-2.41.7}/tests/test_gpu/test_governed_jobs.py +0 -0
  166. {openrunner_sdk-2.41.6 → openrunner_sdk-2.41.7}/tests/test_gpu/test_gpu_types.py +0 -0
  167. {openrunner_sdk-2.41.6 → openrunner_sdk-2.41.7}/tests/test_gpu/test_lambda.py +0 -0
  168. {openrunner_sdk-2.41.6 → openrunner_sdk-2.41.7}/tests/test_gpu/test_modal.py +0 -0
  169. {openrunner_sdk-2.41.6 → openrunner_sdk-2.41.7}/tests/test_gpu/test_ovh_training.py +0 -0
  170. {openrunner_sdk-2.41.6 → openrunner_sdk-2.41.7}/tests/test_gpu/test_ovhcloud.py +0 -0
  171. {openrunner_sdk-2.41.6 → openrunner_sdk-2.41.7}/tests/test_gpu/test_price_compare.py +0 -0
  172. {openrunner_sdk-2.41.6 → openrunner_sdk-2.41.7}/tests/test_gpu/test_provider_base.py +0 -0
  173. {openrunner_sdk-2.41.6 → openrunner_sdk-2.41.7}/tests/test_gpu/test_runner.py +0 -0
  174. {openrunner_sdk-2.41.6 → openrunner_sdk-2.41.7}/tests/test_gpu/test_runpod.py +0 -0
  175. {openrunner_sdk-2.41.6 → openrunner_sdk-2.41.7}/tests/test_gpu/test_vastai.py +0 -0
  176. {openrunner_sdk-2.41.6 → openrunner_sdk-2.41.7}/tests/test_gpu/test_workspace_sync.py +0 -0
  177. {openrunner_sdk-2.41.6 → openrunner_sdk-2.41.7}/tests/test_gpu_modal_creds.py +0 -0
  178. {openrunner_sdk-2.41.6 → openrunner_sdk-2.41.7}/tests/test_init.py +0 -0
  179. {openrunner_sdk-2.41.6 → openrunner_sdk-2.41.7}/tests/test_init_facade.py +0 -0
  180. {openrunner_sdk-2.41.6 → openrunner_sdk-2.41.7}/tests/test_install_commands.py +0 -0
  181. {openrunner_sdk-2.41.6 → openrunner_sdk-2.41.7}/tests/test_integration_fastai.py +0 -0
  182. {openrunner_sdk-2.41.6 → openrunner_sdk-2.41.7}/tests/test_integration_huggingface.py +0 -0
  183. {openrunner_sdk-2.41.6 → openrunner_sdk-2.41.7}/tests/test_integration_keras.py +0 -0
  184. {openrunner_sdk-2.41.6 → openrunner_sdk-2.41.7}/tests/test_integration_langchain.py +0 -0
  185. {openrunner_sdk-2.41.6 → openrunner_sdk-2.41.7}/tests/test_integration_lightning.py +0 -0
  186. {openrunner_sdk-2.41.6 → openrunner_sdk-2.41.7}/tests/test_integration_pytorch.py +0 -0
  187. {openrunner_sdk-2.41.6 → openrunner_sdk-2.41.7}/tests/test_integration_setA.py +0 -0
  188. {openrunner_sdk-2.41.6 → openrunner_sdk-2.41.7}/tests/test_integration_setB.py +0 -0
  189. {openrunner_sdk-2.41.6 → openrunner_sdk-2.41.7}/tests/test_integration_setC.py +0 -0
  190. {openrunner_sdk-2.41.6 → openrunner_sdk-2.41.7}/tests/test_integration_sklearn.py +0 -0
  191. {openrunner_sdk-2.41.6 → openrunner_sdk-2.41.7}/tests/test_integration_xgboost.py +0 -0
  192. {openrunner_sdk-2.41.6 → openrunner_sdk-2.41.7}/tests/test_launch.py +0 -0
  193. {openrunner_sdk-2.41.6 → openrunner_sdk-2.41.7}/tests/test_log.py +0 -0
  194. {openrunner_sdk-2.41.6 → openrunner_sdk-2.41.7}/tests/test_log_code.py +0 -0
  195. {openrunner_sdk-2.41.6 → openrunner_sdk-2.41.7}/tests/test_machine_migrate.py +0 -0
  196. {openrunner_sdk-2.41.6 → openrunner_sdk-2.41.7}/tests/test_mcp_decisions.py +0 -0
  197. {openrunner_sdk-2.41.6 → openrunner_sdk-2.41.7}/tests/test_mcp_monitor.py +0 -0
  198. {openrunner_sdk-2.41.6 → openrunner_sdk-2.41.7}/tests/test_mcp_predictions.py +0 -0
  199. {openrunner_sdk-2.41.6 → openrunner_sdk-2.41.7}/tests/test_mcp_server.py +0 -0
  200. {openrunner_sdk-2.41.6 → openrunner_sdk-2.41.7}/tests/test_media.py +0 -0
  201. {openrunner_sdk-2.41.6 → openrunner_sdk-2.41.7}/tests/test_media_extra.py +0 -0
  202. {openrunner_sdk-2.41.6 → openrunner_sdk-2.41.7}/tests/test_migrate.py +0 -0
  203. {openrunner_sdk-2.41.6 → openrunner_sdk-2.41.7}/tests/test_offline.py +0 -0
  204. {openrunner_sdk-2.41.6 → openrunner_sdk-2.41.7}/tests/test_offline_sync.py +0 -0
  205. {openrunner_sdk-2.41.6 → openrunner_sdk-2.41.7}/tests/test_patent_scan.py +0 -0
  206. {openrunner_sdk-2.41.6 → openrunner_sdk-2.41.7}/tests/test_pii.py +0 -0
  207. {openrunner_sdk-2.41.6 → openrunner_sdk-2.41.7}/tests/test_plot.py +0 -0
  208. {openrunner_sdk-2.41.6 → openrunner_sdk-2.41.7}/tests/test_plot_media_extra2.py +0 -0
  209. {openrunner_sdk-2.41.6 → openrunner_sdk-2.41.7}/tests/test_prompt_module.py +0 -0
  210. {openrunner_sdk-2.41.6 → openrunner_sdk-2.41.7}/tests/test_pure_modules.py +0 -0
  211. {openrunner_sdk-2.41.6 → openrunner_sdk-2.41.7}/tests/test_query_api.py +0 -0
  212. {openrunner_sdk-2.41.6 → openrunner_sdk-2.41.7}/tests/test_redact.py +0 -0
  213. {openrunner_sdk-2.41.6 → openrunner_sdk-2.41.7}/tests/test_reload_module.py +0 -0
  214. {openrunner_sdk-2.41.6 → openrunner_sdk-2.41.7}/tests/test_research_module.py +0 -0
  215. {openrunner_sdk-2.41.6 → openrunner_sdk-2.41.7}/tests/test_resume.py +0 -0
  216. {openrunner_sdk-2.41.6 → openrunner_sdk-2.41.7}/tests/test_retry_transport.py +0 -0
  217. {openrunner_sdk-2.41.6 → openrunner_sdk-2.41.7}/tests/test_run_module.py +0 -0
  218. {openrunner_sdk-2.41.6 → openrunner_sdk-2.41.7}/tests/test_sdk_cluster_j1.py +0 -0
  219. {openrunner_sdk-2.41.6 → openrunner_sdk-2.41.7}/tests/test_sdk_cluster_j2.py +0 -0
  220. {openrunner_sdk-2.41.6 → openrunner_sdk-2.41.7}/tests/test_sdk_cluster_j3.py +0 -0
  221. {openrunner_sdk-2.41.6 → openrunner_sdk-2.41.7}/tests/test_sdk_cluster_j4.py +0 -0
  222. {openrunner_sdk-2.41.6 → openrunner_sdk-2.41.7}/tests/test_sdk_core_edges.py +0 -0
  223. {openrunner_sdk-2.41.6 → openrunner_sdk-2.41.7}/tests/test_sdk_features.py +0 -0
  224. {openrunner_sdk-2.41.6 → openrunner_sdk-2.41.7}/tests/test_sender.py +0 -0
  225. {openrunner_sdk-2.41.6 → openrunner_sdk-2.41.7}/tests/test_session_module.py +0 -0
  226. {openrunner_sdk-2.41.6 → openrunner_sdk-2.41.7}/tests/test_session_sync.py +0 -0
  227. {openrunner_sdk-2.41.6 → openrunner_sdk-2.41.7}/tests/test_summary.py +0 -0
  228. {openrunner_sdk-2.41.6 → openrunner_sdk-2.41.7}/tests/test_system_metrics.py +0 -0
  229. {openrunner_sdk-2.41.6 → openrunner_sdk-2.41.7}/tests/test_topup_hi.py +0 -0
  230. {openrunner_sdk-2.41.6 → openrunner_sdk-2.41.7}/tests/test_trace.py +0 -0
  231. {openrunner_sdk-2.41.6 → openrunner_sdk-2.41.7}/tests/test_wandb_compat.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: openrunner-sdk
3
- Version: 2.41.6
3
+ Version: 2.41.7
4
4
  Summary: OpenRunner SDK - W&B-compatible ML experiment tracking client
5
5
  Project-URL: Homepage, https://github.com/jqueguiner/openrunner
6
6
  Project-URL: Repository, https://github.com/jqueguiner/openrunner
@@ -176,3 +176,45 @@ When the user asks about papers, research, or sessions:
176
176
  - Use `mcp__openrunner__openrunner_search_research` for research plans
177
177
  - Use `mcp__openrunner__openrunner_search_sessions` for AI session history
178
178
  - Use `mcp__openrunner__openrunner_update_paper` to modify paper sections
179
+
180
+ ## Container registry & serverless deployment
181
+
182
+ Register a container registry (Docker Hub, GHCR, Google Artifact Registry, AWS
183
+ ECR, Azure ACR, OVH/Harbor) once; the upstream credential is stored encrypted
184
+ server-side and the OpenRunner **broker** proxies pull/push — the client never
185
+ holds the upstream key.
186
+
187
+ ```
188
+ openrunner registry add --name "Harbor ML" --url REG_URL --username U --token T --namespace NS
189
+ openrunner registry set-default <id> # broker fallback when the repo path is ambiguous
190
+ openrunner registry login # docker login the broker (short-lived token)
191
+ docker push <registry_host>/<namespace>/<project-slug>:<user>-<name>-<tag>
192
+ ```
193
+
194
+ Repo convention (`canonical_image_name`): `{namespace}/{project_slug}`, tag leads
195
+ with the user — e.g. `model-team/ner-email:jl-int8-v1`.
196
+
197
+ **Images link to a project = the deploy gate.** An image pushed **through the
198
+ broker** is **auto-linked** to its project on the manifest PUT (project resolved
199
+ from the repo's project-slug segment), so it appears under the project's
200
+ **Registry** tab (artifact page) and the project-overview **Registry** panel with
201
+ no manual step. Direct-to-upstream pushes (or registries that hide their catalog,
202
+ e.g. OVH Harbor robots can't list `_catalog`) must be linked explicitly:
203
+
204
+ ```
205
+ openrunner deploy link IMAGE -p ORG/NAME [--registry-id UUID]
206
+ openrunner deploy images -p ORG/NAME # list linked images
207
+ openrunner deploy gpus -p ORG/NAME # deployable GPU offers
208
+ openrunner deploy create ... ; openrunner deploy scale <id> N ; openrunner deploy call <id> ...
209
+ ```
210
+ API: `GET/POST /projects/{id}/images`, `.../deployments`. SDK:
211
+ `client.list_project_images(pid)`, `client.link_project_image(pid, image, registry_id=)`.
212
+ Slash: `/openrunner:registry`, `/openrunner:deploy`, `/openrunner:build-api`.
213
+
214
+ ## Full LLM map: llms.txt
215
+
216
+ The complete end-to-end, LLM-oriented map of OpenRunner (dataset → run
217
+ instrumentation → GPU training → sweep → registry → deploy → handover) lives at
218
+ **https://openrun.gladia.io/llms-full.txt** (source: `src/web/public/llms-full.txt`),
219
+ with a short index at **/llms.txt**. Read the full doc to ground yourself in the
220
+ real commands + conventions before driving OpenRunner end-to-end for a new project.
@@ -112,11 +112,20 @@ from openrunner.datasets import (
112
112
  word_lookup,
113
113
  set_dataset_card,
114
114
  set_storage_policy,
115
+ update_dataset,
115
116
  upload_file,
116
117
  upload_sharded,
117
118
  upload_folder,
118
119
  )
119
120
  from openrunner.config import Config
121
+ from openrunner.llms import (
122
+ llms_txt,
123
+ llms_txt_path,
124
+ LLMS_TXT_URL,
125
+ LLMS_FULL_TXT_URL,
126
+ LLMS_TXT_PATH,
127
+ LLMS_FULL_TXT_PATH,
128
+ )
120
129
  from openrunner.model import Model
121
130
  from openrunner.prompt import MessagesPrompt, Prompt, StringPrompt
122
131
  from openrunner.launch import LaunchJob, launch, from_run as _launch_from_run
@@ -863,8 +863,11 @@ def artifact_list(project: str | None, project_opt: str | None) -> None:
863
863
 
864
864
  @artifact.command("get")
865
865
  @click.argument("name")
866
- @click.option("--root", "-r", default=".", help="Download directory (default: .)")
867
- def artifact_get(name: str, root: str) -> None:
866
+ @click.option("--root", "-r", "--output", "-o", "root", default=".",
867
+ help="Download directory (default: .). --output/-o is an alias.")
868
+ @click.option("--project", "-p", default=None,
869
+ help="Scope the lookup to ORG/PROJECT (else searches all accessible projects)")
870
+ def artifact_get(name: str, root: str, project: str | None = None) -> None:
868
871
  """Download an artifact to a local directory.
869
872
 
870
873
  NAME can use colon syntax: my-dataset:latest or my-model:v3.
@@ -886,8 +889,16 @@ def artifact_get(name: str, root: str) -> None:
886
889
  else:
887
890
  alias = ref
888
891
 
889
- # Find artifact by listing all accessible projects
892
+ # Find artifact by listing accessible projects. When --project is given we
893
+ # only search that one (faster, and disambiguates a name shared across
894
+ # projects); otherwise fall back to scanning every accessible project.
890
895
  projects = client.list_projects()
896
+ if project:
897
+ projects = [
898
+ p for p in projects
899
+ if project in (p.get("id"), p.get("name"), p.get("slug"),
900
+ f"{p.get('org_slug')}/{p.get('slug')}")
901
+ ] or projects
891
902
  artifact_detail = None
892
903
  target_version_id = None
893
904
 
@@ -1135,9 +1146,12 @@ def cache_cleanup(max_size: float | None) -> None:
1135
1146
 
1136
1147
  @main.command("download-artifact")
1137
1148
  @click.argument("name")
1138
- @click.option("--root", "-r", default=".", help="Download directory (default: .)")
1149
+ @click.option("--root", "-r", "--output", "-o", "root", default=".",
1150
+ help="Download directory (default: .). --output/-o is an alias.")
1151
+ @click.option("--project", "-p", default=None,
1152
+ help="Scope the lookup to ORG/PROJECT (else searches all accessible projects)")
1139
1153
  @click.pass_context
1140
- def download_artifact_cmd(ctx: click.Context, name: str, root: str) -> None:
1154
+ def download_artifact_cmd(ctx: click.Context, name: str, root: str, project: str | None) -> None:
1141
1155
  """Download an artifact to a local directory.
1142
1156
 
1143
1157
  NAME can use colon syntax: my-dataset:latest or my-model:v3.
@@ -1145,7 +1159,7 @@ def download_artifact_cmd(ctx: click.Context, name: str, root: str) -> None:
1145
1159
 
1146
1160
  This is a top-level alias for `openrunner artifact get`.
1147
1161
  """
1148
- ctx.invoke(artifact_get, name=name, root=root)
1162
+ ctx.invoke(artifact_get, name=name, root=root, project=project)
1149
1163
 
1150
1164
 
1151
1165
  @main.command("upload-artifact")
@@ -3221,6 +3235,24 @@ def version_cmd(check: bool) -> None:
3221
3235
  click.echo("Up to date.")
3222
3236
 
3223
3237
 
3238
+ @main.command("llms")
3239
+ @click.option("--full", is_flag=True, help="Print the complete guide (llms-full.txt) instead of the index")
3240
+ @click.option("--path", "show_path", is_flag=True, help="Print the bundled file path instead of its content")
3241
+ def llms_cmd(full: bool, show_path: bool) -> None:
3242
+ """Print the bundled LLM-oriented OpenRunner doc.
3243
+
3244
+ The short index by default, or the complete end-to-end guide with --full.
3245
+ Bundled in the package (works offline); also served at /llms.txt and
3246
+ /llms-full.txt on the instance.
3247
+ """
3248
+ from openrunner import llms as _llms
3249
+
3250
+ if show_path:
3251
+ click.echo(_llms.llms_txt_path(full=full))
3252
+ else:
3253
+ click.echo(_llms.llms_txt(full=full))
3254
+
3255
+
3224
3256
  @main.command("_update_nudge", hidden=True)
3225
3257
  def update_nudge_cmd() -> None:
3226
3258
  """Print ONE update-hint line if a newer SDK exists, else nothing. Cheap:
@@ -453,11 +453,24 @@ class DatasetsClient:
453
453
  complete — so the dataset's file count grows live and an interrupted run
454
454
  is genuinely resumable (re-run with ``skip_existing`` to continue). The
455
455
  API stays out of the byte path, so throughput is bandwidth-bound.
456
+
457
+ ``skip_existing`` skips a file only when the remote copy has the SAME
458
+ path AND the SAME content (sha256). A file whose bytes changed since the
459
+ last upload is re-uploaded — a pure path match would silently drop
460
+ edits, making a dataset update look successful while sending nothing.
456
461
  """
462
+ import hashlib
457
463
  import os
458
464
  import sys
459
465
  from concurrent.futures import ThreadPoolExecutor, as_completed
460
466
 
467
+ def _sha256(fp: str) -> str:
468
+ h = hashlib.sha256()
469
+ with open(fp, "rb") as fh:
470
+ for chunk in iter(lambda: fh.read(1024 * 1024), b""):
471
+ h.update(chunk)
472
+ return h.hexdigest()
473
+
461
474
  ds = self._resolve(repo_id)
462
475
  items: list[tuple[str, str]] = []
463
476
  for root, _dirs, names in os.walk(folder):
@@ -469,8 +482,16 @@ class DatasetsClient:
469
482
  items.append((full, rel))
470
483
  if skip_existing:
471
484
  try:
472
- have = {f["path"] for f in self.list_files(repo_id)}
473
- items = [it for it in items if it[1] not in have]
485
+ # Map remote path -> sha256. Skip a local file only when both the
486
+ # path and the content hash match; changed files are re-uploaded.
487
+ remote = {f["path"]: (f.get("sha256") or "") for f in self.list_files(repo_id)}
488
+ kept: list[tuple[str, str]] = []
489
+ for full, rel in items:
490
+ rsha = remote.get(rel)
491
+ if rsha and rsha == _sha256(full):
492
+ continue
493
+ kept.append((full, rel))
494
+ items = kept
474
495
  except Exception:
475
496
  pass
476
497
  if not items:
@@ -1008,6 +1029,24 @@ class DatasetsClient:
1008
1029
  _check(r)
1009
1030
  return r.json().get("columns", [])
1010
1031
 
1032
+ def delete_column(self, repo_id: str, name: str) -> list[str]:
1033
+ """Delete an editable column from an indexed dataset.
1034
+
1035
+ Miroir exact de :meth:`add_column` — retire la colonne overlay (ou de la
1036
+ members table d'un dérivé) ``name`` ; les colonnes source indexées
1037
+ ("parent") restent **en lecture seule** et ne peuvent pas être supprimées.
1038
+
1039
+ Exact mirror of :meth:`add_column`: removes the overlay (or derived-member)
1040
+ column ``name`` — the indexed source ("parent") columns stay **read-only**
1041
+ and cannot be deleted. A read-only source column (or the primary key) is
1042
+ rejected (400); an unknown column returns 404. Returns the remaining
1043
+ editable column names.
1044
+ """
1045
+ ds = self._resolve(repo_id)
1046
+ r = self._http.delete(f"/datasets/{ds.id}/columns/{name}")
1047
+ _check(r)
1048
+ return r.json().get("columns", [])
1049
+
1011
1050
  def set_cell(self, repo_id: str, debug_id: str, column: str,
1012
1051
  value: str | None = None) -> dict:
1013
1052
  """Set ONE editable cell (creates the column on first write).
@@ -1261,6 +1300,12 @@ def add_column(repo_id: str, name: str, *, default: str | None = None) -> list[s
1261
1300
  return _c().add_column(repo_id, name, default=default)
1262
1301
 
1263
1302
 
1303
+ def delete_column(repo_id: str, name: str) -> list[str]:
1304
+ """Delete an editable column from an indexed dataset (source columns stay
1305
+ read-only). Module-level mirror of DatasetsClient.delete_column."""
1306
+ return _c().delete_column(repo_id, name)
1307
+
1308
+
1264
1309
  def set_cell(repo_id: str, debug_id: str, column: str, value: str | None = None) -> dict:
1265
1310
  """Set one editable cell of a dataset row (creates the column if new)."""
1266
1311
  return _c().set_cell(repo_id, debug_id, column, value)
@@ -534,25 +534,65 @@ async def terminate(
534
534
  provider: str | None = None,
535
535
  api_key: str | None = None,
536
536
  ) -> bool:
537
- """Terminate a GPU instance on the provider and update the platform.
538
-
539
- 1. Looks up instance details from the backend (to find provider if
540
- not specified).
541
- 2. Terminates the instance via the provider API.
542
- 3. Updates the backend status to ``"terminated"`` via PATCH.
543
- 4. Stops the cost guard thread if one is running.
537
+ """Terminate a GPU instance.
538
+
539
+ Preferred path (governed): when no explicit ``api_key`` is passed, the
540
+ platform terminates the pod SERVER-SIDE — it decrypts the org's stored
541
+ provider key (which never leaves the API), kills the pod, VERIFIES it is
542
+ gone, and only then marks the instance ``terminated``. This is the only
543
+ path that works when the provider key lives on the platform (the common
544
+ case): a client-side terminate cannot see that key and would fail with
545
+ "No API key found for provider …".
546
+
547
+ Fallback (BYO key): when an explicit ``api_key`` is given, terminate the
548
+ pod directly via the provider API. The backend status is marked
549
+ ``terminated`` ONLY if the provider kill actually succeeded — never
550
+ unconditionally, since a falsely-"terminated" record strands a BILLING pod
551
+ that our records claim is dead.
544
552
 
545
553
  Args:
546
554
  instance_id: Platform instance ID.
547
555
  provider: Provider name (auto-detected from backend if None).
548
- api_key: Explicit provider API key.
556
+ api_key: Explicit provider API key (opts into the client-side path).
549
557
 
550
558
  Returns:
551
- True if termination succeeded.
559
+ True if termination is confirmed.
552
560
  """
553
561
  api_client = _get_api_client()
554
562
 
555
- # Resolve provider and provider_instance_id from backend
563
+ def _stop_guard() -> None:
564
+ guard_entry = _active_guards.pop(instance_id, None)
565
+ if guard_entry:
566
+ guard_entry["stop"].set()
567
+ logger.info("Cost guard stopped for instance %s", instance_id)
568
+
569
+ # Governed server-side terminate: kills + verifies with the org's stored
570
+ # key. Used whenever we aren't handed an explicit BYO key.
571
+ if api_key is None and api_client is not None:
572
+ ok = api_client.terminate_instance(instance_id)
573
+ if ok:
574
+ # The API only reports success once the pod is confirmed gone.
575
+ inst = api_client.get_instance(instance_id) or {}
576
+ confirmed = inst.get("status") == "terminated"
577
+ if not confirmed:
578
+ # Non-terminal ("terminating") — the reconciler keeps retrying;
579
+ # surface that the pod is not yet confirmed dead.
580
+ logger.warning(
581
+ "terminate %s: server accepted but pod not yet confirmed "
582
+ "gone (status=%s) — the reconciler will keep retrying",
583
+ instance_id, inst.get("status"),
584
+ )
585
+ else:
586
+ _stop_guard()
587
+ return confirmed
588
+ logger.error(
589
+ "terminate %s: governed server-side termination failed — the pod "
590
+ "may still be BILLING; check `openrunner gpu ls` / provider console",
591
+ instance_id,
592
+ )
593
+ return False
594
+
595
+ # BYO-key path: resolve provider + provider_instance_id, then kill directly.
556
596
  provider_instance_id = instance_id
557
597
  if provider is None and api_client is not None:
558
598
  instances = api_client.list_instances()
@@ -569,7 +609,6 @@ async def terminate(
569
609
  logger.error("terminate: cannot determine provider for instance %s", instance_id)
570
610
  return False
571
611
 
572
- # Get provider instance and terminate
573
612
  try:
574
613
  prov = _get_provider_instance(provider, api_key=api_key, api_client=api_client)
575
614
  result = await prov.terminate_instance(provider_instance_id)
@@ -577,18 +616,16 @@ async def terminate(
577
616
  logger.warning("terminate: provider termination failed: %s", exc)
578
617
  result = False
579
618
 
580
- # Update backend
581
- if api_client is not None:
619
+ # Only mark terminated when the provider kill actually succeeded — a
620
+ # falsely-"terminated" record strands a billing pod.
621
+ if result and api_client is not None:
582
622
  try:
583
623
  api_client.update_instance_status(instance_id, "terminated")
584
624
  except Exception as exc:
585
625
  logger.warning("terminate: backend update failed: %s", exc)
586
626
 
587
- # Stop cost guard thread
588
- guard_entry = _active_guards.pop(instance_id, None)
589
- if guard_entry:
590
- guard_entry["stop"].set()
591
- logger.info("Cost guard stopped for instance %s", instance_id)
627
+ if result:
628
+ _stop_guard()
592
629
 
593
630
  return result
594
631
 
@@ -3254,6 +3254,93 @@ directly SSH-reachable** (`publicIp:22`): SSH is only via the **proxy**
3254
3254
  """
3255
3255
 
3256
3256
 
3257
+ REPORT_BUG_CMD = """---
3258
+ name: {prefix}report-bug
3259
+ description: Open a GitHub issue on the OpenRunner open-source repo for a bug you hit
3260
+ allowed-tools:
3261
+ - Bash
3262
+ - Read
3263
+ - Grep
3264
+ - AskUserQuestion
3265
+ ---
3266
+ <objective>
3267
+ File a high-quality bug report as a GitHub issue on the OpenRunner open-source
3268
+ repository when the user hits a bug (or asks to report one). Gather REAL context
3269
+ from this session, avoid duplicates, confirm before creating (an issue is public),
3270
+ and return the issue URL.
3271
+ </objective>
3272
+
3273
+ <preconditions>
3274
+ - The `gh` CLI must be installed and authenticated. Check with `gh auth status`.
3275
+ If it is not authenticated, tell the user to run it themselves -- suggest they
3276
+ type `! gh auth login` in the prompt -- then stop.
3277
+ - Target repo = the OpenRunner open-source repository. Detect it (below); if it
3278
+ cannot be resolved, fall back to `jqueguiner/openrunner`.
3279
+ </preconditions>
3280
+
3281
+ <process>
3282
+ 1. Verify gh is ready: `gh auth status`. If not, stop and ask the user to
3283
+ `! gh auth login`.
3284
+ 2. Resolve the target repo. Prefer an explicit OpenRunner repo. If the current
3285
+ directory is the OpenRunner checkout, use its origin:
3286
+ `gh repo view --json nameWithOwner -q .nameWithOwner`. Otherwise default to
3287
+ `jqueguiner/openrunner`. Store it as REPO.
3288
+ 3. Collect context (do NOT ask the user for what you can already observe):
3289
+ - SDK version: `openrunner version`.
3290
+ - OS + Python: `python3 -V` and `uname -sr`.
3291
+ - The failing command and its EXACT error output (quote verbatim), the repro
3292
+ steps, and expected vs actual behaviour -- reconstruct these from the current
3293
+ session. Use AskUserQuestion ONLY for the pieces you genuinely cannot infer.
3294
+ 4. De-dupe: search existing issues before opening a new one:
3295
+ `gh issue list --repo REPO --state all --search "<key error phrase>"`.
3296
+ If a clear match exists, show it and ask whether to comment on it
3297
+ (`gh issue comment <n> --repo REPO --body ...`) instead of opening a new one.
3298
+ 5. Draft the issue using the body template below. SHOW the full title + body to
3299
+ the user and get EXPLICIT confirmation before creating -- an issue is public
3300
+ and outward-facing. Do not create it on your own initiative.
3301
+ 6. Create it:
3302
+ `gh issue create --repo REPO --title "<title>" --body "<body>" --label bug`.
3303
+ If the `bug` label does not exist the call fails -- retry without `--label`.
3304
+ 7. Print the returned issue URL to the user.
3305
+ </process>
3306
+
3307
+ <body-template>
3308
+ Compose the issue body as markdown with these sections (fill from real data;
3309
+ write "unknown" where you truly cannot determine a value -- never invent):
3310
+
3311
+ ### Summary
3312
+ One or two sentences: what broke.
3313
+
3314
+ ### Environment
3315
+ - openrunner-sdk: <version>
3316
+ - OS / Python: <uname> / <python -V>
3317
+ - Instance base_url: <from ~/.openrunner/settings.json, host only -- no api_key>
3318
+
3319
+ ### Steps to reproduce
3320
+ 1. ...
3321
+
3322
+ ### Expected
3323
+ ...
3324
+
3325
+ ### Actual
3326
+ ... (paste the exact error, in a fenced code block)
3327
+
3328
+ ### Notes
3329
+ Anything else relevant (workaround found, related area of the codebase).
3330
+ </body-template>
3331
+
3332
+ <safety>
3333
+ - NEVER create the issue (or a comment) without explicit user confirmation. It is
3334
+ public and cannot be un-published cleanly.
3335
+ - REDACT secrets before including anything: strip api keys, tokens, passwords,
3336
+ bearer headers, `Authorization:` values, and the api_key from settings.json.
3337
+ Include only the base_url host, never credentials.
3338
+ - Quote error text verbatim (do not paraphrase the failure) -- but with secrets
3339
+ stripped.
3340
+ </safety>
3341
+ """
3342
+
3343
+
3257
3344
  FORGE_CMD = """---
3258
3345
  name: {prefix}forge
3259
3346
  description: Run or write a Python function on the Forge (Shadow-PaaS) serverless GPU fleet. Use for "run/launch/train on forge", "forge GPU", or writing an @app.function. Forge is a serverless PROVIDER (like Modal) -- NOT an ssh host or runner, so don't hunt ~/.ssh/config or `hosts ls`.
@@ -3577,6 +3664,7 @@ def install_claude_code() -> list[str]:
3577
3664
  "unregister-machine.md": UNREGISTER_MACHINE_CMD,
3578
3665
  "train.md": TRAIN_CMD,
3579
3666
  "deploy.md": DEPLOY_CMD,
3667
+ "report-bug.md": REPORT_BUG_CMD,
3580
3668
  "forge.md": FORGE_CMD,
3581
3669
  }
3582
3670
 
@@ -3812,6 +3900,7 @@ def _install_all_commands_to_dir(cmd_dir: Path, prefix: str = "openrunner:") ->
3812
3900
  "unregister-machine.md": UNREGISTER_MACHINE_CMD,
3813
3901
  "train.md": TRAIN_CMD,
3814
3902
  "deploy.md": DEPLOY_CMD,
3903
+ "report-bug.md": REPORT_BUG_CMD,
3815
3904
  "forge.md": FORGE_CMD,
3816
3905
  }
3817
3906