freesolo-flash-dev 0.2.29__tar.gz → 0.2.30__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 (274) hide show
  1. {freesolo_flash_dev-0.2.29 → freesolo_flash_dev-0.2.30}/.github/workflows/bake-kernel-cache.yml +16 -29
  2. {freesolo_flash_dev-0.2.29 → freesolo_flash_dev-0.2.30}/PKG-INFO +1 -1
  3. {freesolo_flash_dev-0.2.29 → freesolo_flash_dev-0.2.30}/flash/cli/__init__.py +8 -2
  4. {freesolo_flash_dev-0.2.29 → freesolo_flash_dev-0.2.30}/flash/server/auth.py +14 -4
  5. {freesolo_flash_dev-0.2.29 → freesolo_flash_dev-0.2.30}/flash/server/envs.py +18 -23
  6. {freesolo_flash_dev-0.2.29 → freesolo_flash_dev-0.2.30}/flash/server/routes/meta.py +2 -0
  7. {freesolo_flash_dev-0.2.29 → freesolo_flash_dev-0.2.30}/pyproject.toml +2 -2
  8. {freesolo_flash_dev-0.2.29 → freesolo_flash_dev-0.2.30}/tests/test_billing_retry.py +6 -1
  9. {freesolo_flash_dev-0.2.29 → freesolo_flash_dev-0.2.30}/tests/test_cli_help.py +31 -2
  10. {freesolo_flash_dev-0.2.29 → freesolo_flash_dev-0.2.30}/tests/test_client.py +3 -3
  11. {freesolo_flash_dev-0.2.29 → freesolo_flash_dev-0.2.30}/tests/test_client_server_integration.py +2 -0
  12. {freesolo_flash_dev-0.2.29 → freesolo_flash_dev-0.2.30}/tests/test_env_publish.py +53 -59
  13. {freesolo_flash_dev-0.2.29 → freesolo_flash_dev-0.2.30}/tests/test_server_api.py +52 -19
  14. {freesolo_flash_dev-0.2.29 → freesolo_flash_dev-0.2.30}/tests/test_server_billing.py +11 -2
  15. {freesolo_flash_dev-0.2.29 → freesolo_flash_dev-0.2.30}/tests/test_server_db.py +3 -1
  16. {freesolo_flash_dev-0.2.29 → freesolo_flash_dev-0.2.30}/.dockerignore +0 -0
  17. {freesolo_flash_dev-0.2.29 → freesolo_flash_dev-0.2.30}/.env.example +0 -0
  18. {freesolo_flash_dev-0.2.29 → freesolo_flash_dev-0.2.30}/.github/workflows/auto-rebake.yml +0 -0
  19. {freesolo_flash_dev-0.2.29 → freesolo_flash_dev-0.2.30}/.github/workflows/ci.yml +0 -0
  20. {freesolo_flash_dev-0.2.29 → freesolo_flash_dev-0.2.30}/.github/workflows/main-source-guard.yml +0 -0
  21. {freesolo_flash_dev-0.2.29 → freesolo_flash_dev-0.2.30}/.github/workflows/publish-dev.yml +0 -0
  22. {freesolo_flash_dev-0.2.29 → freesolo_flash_dev-0.2.30}/.github/workflows/publish-image.yml +0 -0
  23. {freesolo_flash_dev-0.2.29 → freesolo_flash_dev-0.2.30}/.github/workflows/publish.yml +0 -0
  24. {freesolo_flash_dev-0.2.29 → freesolo_flash_dev-0.2.30}/.github/workflows/version-parity.yml +0 -0
  25. {freesolo_flash_dev-0.2.29 → freesolo_flash_dev-0.2.30}/.github/workflows/worker-image.yml +0 -0
  26. {freesolo_flash_dev-0.2.29 → freesolo_flash_dev-0.2.30}/.gitignore +0 -0
  27. {freesolo_flash_dev-0.2.29 → freesolo_flash_dev-0.2.30}/Dockerfile +0 -0
  28. {freesolo_flash_dev-0.2.29 → freesolo_flash_dev-0.2.30}/Dockerfile.worker +0 -0
  29. {freesolo_flash_dev-0.2.29 → freesolo_flash_dev-0.2.30}/LICENSE +0 -0
  30. {freesolo_flash_dev-0.2.29 → freesolo_flash_dev-0.2.30}/README.md +0 -0
  31. {freesolo_flash_dev-0.2.29 → freesolo_flash_dev-0.2.30}/build/kernel_cache/.gitignore +0 -0
  32. {freesolo_flash_dev-0.2.29 → freesolo_flash_dev-0.2.30}/build/kernel_cache/.keep +0 -0
  33. {freesolo_flash_dev-0.2.29 → freesolo_flash_dev-0.2.30}/docker/Dockerfile.kernelcache +0 -0
  34. {freesolo_flash_dev-0.2.29 → freesolo_flash_dev-0.2.30}/docker/Dockerfile.kernelcache.relayer +0 -0
  35. {freesolo_flash_dev-0.2.29 → freesolo_flash_dev-0.2.30}/docker/bake_kernel_cache.py +0 -0
  36. {freesolo_flash_dev-0.2.29 → freesolo_flash_dev-0.2.30}/docker/bake_pod_entry.py +0 -0
  37. {freesolo_flash_dev-0.2.29 → freesolo_flash_dev-0.2.30}/docker/kernel_fingerprint.py +0 -0
  38. {freesolo_flash_dev-0.2.29 → freesolo_flash_dev-0.2.30}/docker/make_rp_handler.py +0 -0
  39. {freesolo_flash_dev-0.2.29 → freesolo_flash_dev-0.2.30}/docs/cli-style/README.md +0 -0
  40. {freesolo_flash_dev-0.2.29 → freesolo_flash_dev-0.2.30}/docs/cli-style/generate.py +0 -0
  41. {freesolo_flash_dev-0.2.29 → freesolo_flash_dev-0.2.30}/docs/cli-style/index.html +0 -0
  42. {freesolo_flash_dev-0.2.29 → freesolo_flash_dev-0.2.30}/docs/cli-style/preview.png +0 -0
  43. {freesolo_flash_dev-0.2.29 → freesolo_flash_dev-0.2.30}/docs/cli-style/themed-errors.png +0 -0
  44. {freesolo_flash_dev-0.2.29 → freesolo_flash_dev-0.2.30}/docs/cli-theme/README.md +0 -0
  45. {freesolo_flash_dev-0.2.29 → freesolo_flash_dev-0.2.30}/docs/cli-theme/before-after-dark.png +0 -0
  46. {freesolo_flash_dev-0.2.29 → freesolo_flash_dev-0.2.30}/docs/cli-theme/before-after-light.png +0 -0
  47. {freesolo_flash_dev-0.2.29 → freesolo_flash_dev-0.2.30}/docs/cli-theme/gallery-dark.png +0 -0
  48. {freesolo_flash_dev-0.2.29 → freesolo_flash_dev-0.2.30}/docs/cli-theme/gallery-light.png +0 -0
  49. {freesolo_flash_dev-0.2.29 → freesolo_flash_dev-0.2.30}/docs/cli-theme/help-dark.png +0 -0
  50. {freesolo_flash_dev-0.2.29 → freesolo_flash_dev-0.2.30}/docs/cli-theme/help-light.png +0 -0
  51. {freesolo_flash_dev-0.2.29 → freesolo_flash_dev-0.2.30}/docs/kernel-cache.md +0 -0
  52. {freesolo_flash_dev-0.2.29 → freesolo_flash_dev-0.2.30}/flash/__init__.py +0 -0
  53. {freesolo_flash_dev-0.2.29 → freesolo_flash_dev-0.2.30}/flash/_channel.py +0 -0
  54. {freesolo_flash_dev-0.2.29 → freesolo_flash_dev-0.2.30}/flash/_fileio.py +0 -0
  55. {freesolo_flash_dev-0.2.29 → freesolo_flash_dev-0.2.30}/flash/_logging.py +0 -0
  56. {freesolo_flash_dev-0.2.29 → freesolo_flash_dev-0.2.30}/flash/_update_check.py +0 -0
  57. {freesolo_flash_dev-0.2.29 → freesolo_flash_dev-0.2.30}/flash/catalog.py +0 -0
  58. {freesolo_flash_dev-0.2.29 → freesolo_flash_dev-0.2.30}/flash/cli/__main__.py +0 -0
  59. {freesolo_flash_dev-0.2.29 → freesolo_flash_dev-0.2.30}/flash/cli/commands.py +0 -0
  60. {freesolo_flash_dev-0.2.29 → freesolo_flash_dev-0.2.30}/flash/cli/envpush.py +0 -0
  61. {freesolo_flash_dev-0.2.29 → freesolo_flash_dev-0.2.30}/flash/cli/render.py +0 -0
  62. {freesolo_flash_dev-0.2.29 → freesolo_flash_dev-0.2.30}/flash/cli/training_doc.py +0 -0
  63. {freesolo_flash_dev-0.2.29 → freesolo_flash_dev-0.2.30}/flash/client/__init__.py +0 -0
  64. {freesolo_flash_dev-0.2.29 → freesolo_flash_dev-0.2.30}/flash/client/config.py +0 -0
  65. {freesolo_flash_dev-0.2.29 → freesolo_flash_dev-0.2.30}/flash/client/http.py +0 -0
  66. {freesolo_flash_dev-0.2.29 → freesolo_flash_dev-0.2.30}/flash/client/runtime_secrets.py +0 -0
  67. {freesolo_flash_dev-0.2.29 → freesolo_flash_dev-0.2.30}/flash/client/specs.py +0 -0
  68. {freesolo_flash_dev-0.2.29 → freesolo_flash_dev-0.2.30}/flash/cost/__init__.py +0 -0
  69. {freesolo_flash_dev-0.2.29 → freesolo_flash_dev-0.2.30}/flash/cost/analytical.py +0 -0
  70. {freesolo_flash_dev-0.2.29 → freesolo_flash_dev-0.2.30}/flash/cost/facts.py +0 -0
  71. {freesolo_flash_dev-0.2.29 → freesolo_flash_dev-0.2.30}/flash/cost/spec.py +0 -0
  72. {freesolo_flash_dev-0.2.29 → freesolo_flash_dev-0.2.30}/flash/cost/types.py +0 -0
  73. {freesolo_flash_dev-0.2.29 → freesolo_flash_dev-0.2.30}/flash/engine/__init__.py +0 -0
  74. {freesolo_flash_dev-0.2.29 → freesolo_flash_dev-0.2.30}/flash/engine/accounting.py +0 -0
  75. {freesolo_flash_dev-0.2.29 → freesolo_flash_dev-0.2.30}/flash/engine/chalk_kernels.py +0 -0
  76. {freesolo_flash_dev-0.2.29 → freesolo_flash_dev-0.2.30}/flash/engine/multiturn_rollout.py +0 -0
  77. {freesolo_flash_dev-0.2.29 → freesolo_flash_dev-0.2.30}/flash/engine/recipe.py +0 -0
  78. {freesolo_flash_dev-0.2.29 → freesolo_flash_dev-0.2.30}/flash/engine/vram.py +0 -0
  79. {freesolo_flash_dev-0.2.29 → freesolo_flash_dev-0.2.30}/flash/engine/worker/__init__.py +0 -0
  80. {freesolo_flash_dev-0.2.29 → freesolo_flash_dev-0.2.30}/flash/engine/worker/__main__.py +0 -0
  81. {freesolo_flash_dev-0.2.29 → freesolo_flash_dev-0.2.30}/flash/engine/worker/_pkg.py +0 -0
  82. {freesolo_flash_dev-0.2.29 → freesolo_flash_dev-0.2.30}/flash/engine/worker/adapter.py +0 -0
  83. {freesolo_flash_dev-0.2.29 → freesolo_flash_dev-0.2.30}/flash/engine/worker/decoding.py +0 -0
  84. {freesolo_flash_dev-0.2.29 → freesolo_flash_dev-0.2.30}/flash/engine/worker/finalize.py +0 -0
  85. {freesolo_flash_dev-0.2.29 → freesolo_flash_dev-0.2.30}/flash/engine/worker/gpu_setup.py +0 -0
  86. {freesolo_flash_dev-0.2.29 → freesolo_flash_dev-0.2.30}/flash/engine/worker/grpo.py +0 -0
  87. {freesolo_flash_dev-0.2.29 → freesolo_flash_dev-0.2.30}/flash/engine/worker/heartbeat.py +0 -0
  88. {freesolo_flash_dev-0.2.29 → freesolo_flash_dev-0.2.30}/flash/engine/worker/hf.py +0 -0
  89. {freesolo_flash_dev-0.2.29 → freesolo_flash_dev-0.2.30}/flash/engine/worker/kernel_warmup.py +0 -0
  90. {freesolo_flash_dev-0.2.29 → freesolo_flash_dev-0.2.30}/flash/engine/worker/lora.py +0 -0
  91. {freesolo_flash_dev-0.2.29 → freesolo_flash_dev-0.2.30}/flash/engine/worker/packing.py +0 -0
  92. {freesolo_flash_dev-0.2.29 → freesolo_flash_dev-0.2.30}/flash/engine/worker/perf/__init__.py +0 -0
  93. {freesolo_flash_dev-0.2.29 → freesolo_flash_dev-0.2.30}/flash/engine/worker/perf/attn.py +0 -0
  94. {freesolo_flash_dev-0.2.29 → freesolo_flash_dev-0.2.30}/flash/engine/worker/perf/diagnostics.py +0 -0
  95. {freesolo_flash_dev-0.2.29 → freesolo_flash_dev-0.2.30}/flash/engine/worker/perf/lifecycle.py +0 -0
  96. {freesolo_flash_dev-0.2.29 → freesolo_flash_dev-0.2.30}/flash/engine/worker/perf/liger.py +0 -0
  97. {freesolo_flash_dev-0.2.29 → freesolo_flash_dev-0.2.30}/flash/engine/worker/perf/loraplus.py +0 -0
  98. {freesolo_flash_dev-0.2.29 → freesolo_flash_dev-0.2.30}/flash/engine/worker/perf/memory.py +0 -0
  99. {freesolo_flash_dev-0.2.29 → freesolo_flash_dev-0.2.30}/flash/engine/worker/rl.py +0 -0
  100. {freesolo_flash_dev-0.2.29 → freesolo_flash_dev-0.2.30}/flash/engine/worker/sft.py +0 -0
  101. {freesolo_flash_dev-0.2.29 → freesolo_flash_dev-0.2.30}/flash/engine/worker/wandb_log.py +0 -0
  102. {freesolo_flash_dev-0.2.29 → freesolo_flash_dev-0.2.30}/flash/envs/__init__.py +0 -0
  103. {freesolo_flash_dev-0.2.29 → freesolo_flash_dev-0.2.30}/flash/envs/adapter.py +0 -0
  104. {freesolo_flash_dev-0.2.29 → freesolo_flash_dev-0.2.30}/flash/envs/base.py +0 -0
  105. {freesolo_flash_dev-0.2.29 → freesolo_flash_dev-0.2.30}/flash/envs/pull.py +0 -0
  106. {freesolo_flash_dev-0.2.29 → freesolo_flash_dev-0.2.30}/flash/envs/registry.py +0 -0
  107. {freesolo_flash_dev-0.2.29 → freesolo_flash_dev-0.2.30}/flash/providers/__init__.py +0 -0
  108. {freesolo_flash_dev-0.2.29 → freesolo_flash_dev-0.2.30}/flash/providers/_auth.py +0 -0
  109. {freesolo_flash_dev-0.2.29 → freesolo_flash_dev-0.2.30}/flash/providers/_http.py +0 -0
  110. {freesolo_flash_dev-0.2.29 → freesolo_flash_dev-0.2.30}/flash/providers/_instance.py +0 -0
  111. {freesolo_flash_dev-0.2.29 → freesolo_flash_dev-0.2.30}/flash/providers/_instance_bootstrap.py +0 -0
  112. {freesolo_flash_dev-0.2.29 → freesolo_flash_dev-0.2.30}/flash/providers/_poll.py +0 -0
  113. {freesolo_flash_dev-0.2.29 → freesolo_flash_dev-0.2.30}/flash/providers/allocator.py +0 -0
  114. {freesolo_flash_dev-0.2.29 → freesolo_flash_dev-0.2.30}/flash/providers/base.py +0 -0
  115. {freesolo_flash_dev-0.2.29 → freesolo_flash_dev-0.2.30}/flash/providers/lambdalabs/__init__.py +0 -0
  116. {freesolo_flash_dev-0.2.29 → freesolo_flash_dev-0.2.30}/flash/providers/lambdalabs/api.py +0 -0
  117. {freesolo_flash_dev-0.2.29 → freesolo_flash_dev-0.2.30}/flash/providers/lambdalabs/auth.py +0 -0
  118. {freesolo_flash_dev-0.2.29 → freesolo_flash_dev-0.2.30}/flash/providers/lambdalabs/gpus.py +0 -0
  119. {freesolo_flash_dev-0.2.29 → freesolo_flash_dev-0.2.30}/flash/providers/lambdalabs/jobs/__init__.py +0 -0
  120. {freesolo_flash_dev-0.2.29 → freesolo_flash_dev-0.2.30}/flash/providers/lambdalabs/jobs/builders.py +0 -0
  121. {freesolo_flash_dev-0.2.29 → freesolo_flash_dev-0.2.30}/flash/providers/lambdalabs/preflight.py +0 -0
  122. {freesolo_flash_dev-0.2.29 → freesolo_flash_dev-0.2.30}/flash/providers/lambdalabs/pricing.py +0 -0
  123. {freesolo_flash_dev-0.2.29 → freesolo_flash_dev-0.2.30}/flash/providers/lambdalabs/train.py +0 -0
  124. {freesolo_flash_dev-0.2.29 → freesolo_flash_dev-0.2.30}/flash/providers/preflight.py +0 -0
  125. {freesolo_flash_dev-0.2.29 → freesolo_flash_dev-0.2.30}/flash/providers/realized.py +0 -0
  126. {freesolo_flash_dev-0.2.29 → freesolo_flash_dev-0.2.30}/flash/providers/runpod/__init__.py +0 -0
  127. {freesolo_flash_dev-0.2.29 → freesolo_flash_dev-0.2.30}/flash/providers/runpod/api.py +0 -0
  128. {freesolo_flash_dev-0.2.29 → freesolo_flash_dev-0.2.30}/flash/providers/runpod/auth.py +0 -0
  129. {freesolo_flash_dev-0.2.29 → freesolo_flash_dev-0.2.30}/flash/providers/runpod/cost.py +0 -0
  130. {freesolo_flash_dev-0.2.29 → freesolo_flash_dev-0.2.30}/flash/providers/runpod/gpus.py +0 -0
  131. {freesolo_flash_dev-0.2.29 → freesolo_flash_dev-0.2.30}/flash/providers/runpod/jobs.py +0 -0
  132. {freesolo_flash_dev-0.2.29 → freesolo_flash_dev-0.2.30}/flash/providers/runpod/keys.py +0 -0
  133. {freesolo_flash_dev-0.2.29 → freesolo_flash_dev-0.2.30}/flash/providers/runpod/preflight.py +0 -0
  134. {freesolo_flash_dev-0.2.29 → freesolo_flash_dev-0.2.30}/flash/providers/runpod/preload.py +0 -0
  135. {freesolo_flash_dev-0.2.29 → freesolo_flash_dev-0.2.30}/flash/providers/runpod/pricing.py +0 -0
  136. {freesolo_flash_dev-0.2.29 → freesolo_flash_dev-0.2.30}/flash/providers/runpod/slots.py +0 -0
  137. {freesolo_flash_dev-0.2.29 → freesolo_flash_dev-0.2.30}/flash/providers/runpod/train/__init__.py +0 -0
  138. {freesolo_flash_dev-0.2.29 → freesolo_flash_dev-0.2.30}/flash/providers/runpod/train/deps.py +0 -0
  139. {freesolo_flash_dev-0.2.29 → freesolo_flash_dev-0.2.30}/flash/providers/runpod/train/endpoints.py +0 -0
  140. {freesolo_flash_dev-0.2.29 → freesolo_flash_dev-0.2.30}/flash/py.typed +0 -0
  141. {freesolo_flash_dev-0.2.29 → freesolo_flash_dev-0.2.30}/flash/runner/__init__.py +0 -0
  142. {freesolo_flash_dev-0.2.29 → freesolo_flash_dev-0.2.30}/flash/runner/checkpoints.py +0 -0
  143. {freesolo_flash_dev-0.2.29 → freesolo_flash_dev-0.2.30}/flash/runner/deploy.py +0 -0
  144. {freesolo_flash_dev-0.2.29 → freesolo_flash_dev-0.2.30}/flash/runner/lifecycle.py +0 -0
  145. {freesolo_flash_dev-0.2.29 → freesolo_flash_dev-0.2.30}/flash/schema/__init__.py +0 -0
  146. {freesolo_flash_dev-0.2.29 → freesolo_flash_dev-0.2.30}/flash/schema/fields.py +0 -0
  147. {freesolo_flash_dev-0.2.29 → freesolo_flash_dev-0.2.30}/flash/serve/__init__.py +0 -0
  148. {freesolo_flash_dev-0.2.29 → freesolo_flash_dev-0.2.30}/flash/serve/deploy.py +0 -0
  149. {freesolo_flash_dev-0.2.29 → freesolo_flash_dev-0.2.30}/flash/serve/export.py +0 -0
  150. {freesolo_flash_dev-0.2.29 → freesolo_flash_dev-0.2.30}/flash/serve/pricing.py +0 -0
  151. {freesolo_flash_dev-0.2.29 → freesolo_flash_dev-0.2.30}/flash/server/__init__.py +0 -0
  152. {freesolo_flash_dev-0.2.29 → freesolo_flash_dev-0.2.30}/flash/server/__main__.py +0 -0
  153. {freesolo_flash_dev-0.2.29 → freesolo_flash_dev-0.2.30}/flash/server/_deps.py +0 -0
  154. {freesolo_flash_dev-0.2.29 → freesolo_flash_dev-0.2.30}/flash/server/_internal_client.py +0 -0
  155. {freesolo_flash_dev-0.2.29 → freesolo_flash_dev-0.2.30}/flash/server/_locks.py +0 -0
  156. {freesolo_flash_dev-0.2.29 → freesolo_flash_dev-0.2.30}/flash/server/_runtime.py +0 -0
  157. {freesolo_flash_dev-0.2.29 → freesolo_flash_dev-0.2.30}/flash/server/app.py +0 -0
  158. {freesolo_flash_dev-0.2.29 → freesolo_flash_dev-0.2.30}/flash/server/billing.py +0 -0
  159. {freesolo_flash_dev-0.2.29 → freesolo_flash_dev-0.2.30}/flash/server/billing_retry.py +0 -0
  160. {freesolo_flash_dev-0.2.29 → freesolo_flash_dev-0.2.30}/flash/server/checkpoints.py +0 -0
  161. {freesolo_flash_dev-0.2.29 → freesolo_flash_dev-0.2.30}/flash/server/db.py +0 -0
  162. {freesolo_flash_dev-0.2.29 → freesolo_flash_dev-0.2.30}/flash/server/environment_registry.py +0 -0
  163. {freesolo_flash_dev-0.2.29 → freesolo_flash_dev-0.2.30}/flash/server/reconcile.py +0 -0
  164. {freesolo_flash_dev-0.2.29 → freesolo_flash_dev-0.2.30}/flash/server/repo_cleanup.py +0 -0
  165. {freesolo_flash_dev-0.2.29 → freesolo_flash_dev-0.2.30}/flash/server/routes/__init__.py +0 -0
  166. {freesolo_flash_dev-0.2.29 → freesolo_flash_dev-0.2.30}/flash/server/routes/envs.py +0 -0
  167. {freesolo_flash_dev-0.2.29 → freesolo_flash_dev-0.2.30}/flash/server/routes/runs.py +0 -0
  168. {freesolo_flash_dev-0.2.29 → freesolo_flash_dev-0.2.30}/flash/server/routes/serving.py +0 -0
  169. {freesolo_flash_dev-0.2.29 → freesolo_flash_dev-0.2.30}/flash/server/run_registry.py +0 -0
  170. {freesolo_flash_dev-0.2.29 → freesolo_flash_dev-0.2.30}/flash/spec.py +0 -0
  171. {freesolo_flash_dev-0.2.29 → freesolo_flash_dev-0.2.30}/infisical-entrypoint.sh +0 -0
  172. {freesolo_flash_dev-0.2.29 → freesolo_flash_dev-0.2.30}/scripts/build_dev_dist.py +0 -0
  173. {freesolo_flash_dev-0.2.29 → freesolo_flash_dev-0.2.30}/tests/__init__.py +0 -0
  174. {freesolo_flash_dev-0.2.29 → freesolo_flash_dev-0.2.30}/tests/_helpers/__init__.py +0 -0
  175. {freesolo_flash_dev-0.2.29 → freesolo_flash_dev-0.2.30}/tests/_helpers/runner.py +0 -0
  176. {freesolo_flash_dev-0.2.29 → freesolo_flash_dev-0.2.30}/tests/_helpers/specs.py +0 -0
  177. {freesolo_flash_dev-0.2.29 → freesolo_flash_dev-0.2.30}/tests/conftest.py +0 -0
  178. {freesolo_flash_dev-0.2.29 → freesolo_flash_dev-0.2.30}/tests/fixtures/math_eval.jsonl +0 -0
  179. {freesolo_flash_dev-0.2.29 → freesolo_flash_dev-0.2.30}/tests/fixtures/math_train.jsonl +0 -0
  180. {freesolo_flash_dev-0.2.29 → freesolo_flash_dev-0.2.30}/tests/live/__init__.py +0 -0
  181. {freesolo_flash_dev-0.2.29 → freesolo_flash_dev-0.2.30}/tests/live/conftest.py +0 -0
  182. {freesolo_flash_dev-0.2.29 → freesolo_flash_dev-0.2.30}/tests/live/test_lambda_live.py +0 -0
  183. {freesolo_flash_dev-0.2.29 → freesolo_flash_dev-0.2.30}/tests/live/test_runpod_live.py +0 -0
  184. {freesolo_flash_dev-0.2.29 → freesolo_flash_dev-0.2.30}/tests/test_agent_flash_cli_contract.py +0 -0
  185. {freesolo_flash_dev-0.2.29 → freesolo_flash_dev-0.2.30}/tests/test_algorithms.py +0 -0
  186. {freesolo_flash_dev-0.2.29 → freesolo_flash_dev-0.2.30}/tests/test_allocator.py +0 -0
  187. {freesolo_flash_dev-0.2.29 → freesolo_flash_dev-0.2.30}/tests/test_b200_rollout_opt.py +0 -0
  188. {freesolo_flash_dev-0.2.29 → freesolo_flash_dev-0.2.30}/tests/test_backend_jobspec_contract.py +0 -0
  189. {freesolo_flash_dev-0.2.29 → freesolo_flash_dev-0.2.30}/tests/test_cancel_remote.py +0 -0
  190. {freesolo_flash_dev-0.2.29 → freesolo_flash_dev-0.2.30}/tests/test_catalog_consistency.py +0 -0
  191. {freesolo_flash_dev-0.2.29 → freesolo_flash_dev-0.2.30}/tests/test_chalk_kernels.py +0 -0
  192. {freesolo_flash_dev-0.2.29 → freesolo_flash_dev-0.2.30}/tests/test_checkpoints.py +0 -0
  193. {freesolo_flash_dev-0.2.29 → freesolo_flash_dev-0.2.30}/tests/test_cli_commands.py +0 -0
  194. {freesolo_flash_dev-0.2.29 → freesolo_flash_dev-0.2.30}/tests/test_cli_errors.py +0 -0
  195. {freesolo_flash_dev-0.2.29 → freesolo_flash_dev-0.2.30}/tests/test_cli_estimate.py +0 -0
  196. {freesolo_flash_dev-0.2.29 → freesolo_flash_dev-0.2.30}/tests/test_cli_managed.py +0 -0
  197. {freesolo_flash_dev-0.2.29 → freesolo_flash_dev-0.2.30}/tests/test_cli_render_theme.py +0 -0
  198. {freesolo_flash_dev-0.2.29 → freesolo_flash_dev-0.2.30}/tests/test_config_overrides.py +0 -0
  199. {freesolo_flash_dev-0.2.29 → freesolo_flash_dev-0.2.30}/tests/test_cost_analytical.py +0 -0
  200. {freesolo_flash_dev-0.2.29 → freesolo_flash_dev-0.2.30}/tests/test_cost_equation.py +0 -0
  201. {freesolo_flash_dev-0.2.29 → freesolo_flash_dev-0.2.30}/tests/test_cost_estimate.py +0 -0
  202. {freesolo_flash_dev-0.2.29 → freesolo_flash_dev-0.2.30}/tests/test_cost_hardware.py +0 -0
  203. {freesolo_flash_dev-0.2.29 → freesolo_flash_dev-0.2.30}/tests/test_cost_models.py +0 -0
  204. {freesolo_flash_dev-0.2.29 → freesolo_flash_dev-0.2.30}/tests/test_cost_rewards.py +0 -0
  205. {freesolo_flash_dev-0.2.29 → freesolo_flash_dev-0.2.30}/tests/test_dev_channel.py +0 -0
  206. {freesolo_flash_dev-0.2.29 → freesolo_flash_dev-0.2.30}/tests/test_disk_gb.py +0 -0
  207. {freesolo_flash_dev-0.2.29 → freesolo_flash_dev-0.2.30}/tests/test_endpoint_name.py +0 -0
  208. {freesolo_flash_dev-0.2.29 → freesolo_flash_dev-0.2.30}/tests/test_env_delete.py +0 -0
  209. {freesolo_flash_dev-0.2.29 → freesolo_flash_dev-0.2.30}/tests/test_env_no_install.py +0 -0
  210. {freesolo_flash_dev-0.2.29 → freesolo_flash_dev-0.2.30}/tests/test_env_pull.py +0 -0
  211. {freesolo_flash_dev-0.2.29 → freesolo_flash_dev-0.2.30}/tests/test_env_push.py +0 -0
  212. {freesolo_flash_dev-0.2.29 → freesolo_flash_dev-0.2.30}/tests/test_env_rate_limit_resolve.py +0 -0
  213. {freesolo_flash_dev-0.2.29 → freesolo_flash_dev-0.2.30}/tests/test_envs_coverage.py +0 -0
  214. {freesolo_flash_dev-0.2.29 → freesolo_flash_dev-0.2.30}/tests/test_export.py +0 -0
  215. {freesolo_flash_dev-0.2.29 → freesolo_flash_dev-0.2.30}/tests/test_flash_mvp.py +0 -0
  216. {freesolo_flash_dev-0.2.29 → freesolo_flash_dev-0.2.30}/tests/test_flash_worker.py +0 -0
  217. {freesolo_flash_dev-0.2.29 → freesolo_flash_dev-0.2.30}/tests/test_flashinfer_cache_dirs.py +0 -0
  218. {freesolo_flash_dev-0.2.29 → freesolo_flash_dev-0.2.30}/tests/test_github_urlopen_retry.py +0 -0
  219. {freesolo_flash_dev-0.2.29 → freesolo_flash_dev-0.2.30}/tests/test_gpus.py +0 -0
  220. {freesolo_flash_dev-0.2.29 → freesolo_flash_dev-0.2.30}/tests/test_grpo_mask_aware.py +0 -0
  221. {freesolo_flash_dev-0.2.29 → freesolo_flash_dev-0.2.30}/tests/test_grpo_params.py +0 -0
  222. {freesolo_flash_dev-0.2.29 → freesolo_flash_dev-0.2.30}/tests/test_grpo_sleep_gate.py +0 -0
  223. {freesolo_flash_dev-0.2.29 → freesolo_flash_dev-0.2.30}/tests/test_idle_endpoint_reaper.py +0 -0
  224. {freesolo_flash_dev-0.2.29 → freesolo_flash_dev-0.2.30}/tests/test_internal_client.py +0 -0
  225. {freesolo_flash_dev-0.2.29 → freesolo_flash_dev-0.2.30}/tests/test_jobs.py +0 -0
  226. {freesolo_flash_dev-0.2.29 → freesolo_flash_dev-0.2.30}/tests/test_kernel_cache.py +0 -0
  227. {freesolo_flash_dev-0.2.29 → freesolo_flash_dev-0.2.30}/tests/test_kernel_fingerprint.py +0 -0
  228. {freesolo_flash_dev-0.2.29 → freesolo_flash_dev-0.2.30}/tests/test_kv_util.py +0 -0
  229. {freesolo_flash_dev-0.2.29 → freesolo_flash_dev-0.2.30}/tests/test_lambda_runner.py +0 -0
  230. {freesolo_flash_dev-0.2.29 → freesolo_flash_dev-0.2.30}/tests/test_logging.py +0 -0
  231. {freesolo_flash_dev-0.2.29 → freesolo_flash_dev-0.2.30}/tests/test_login_perms.py +0 -0
  232. {freesolo_flash_dev-0.2.29 → freesolo_flash_dev-0.2.30}/tests/test_managed_hf_repo.py +0 -0
  233. {freesolo_flash_dev-0.2.29 → freesolo_flash_dev-0.2.30}/tests/test_metrics_schema_agent_contract.py +0 -0
  234. {freesolo_flash_dev-0.2.29 → freesolo_flash_dev-0.2.30}/tests/test_mig_guard.py +0 -0
  235. {freesolo_flash_dev-0.2.29 → freesolo_flash_dev-0.2.30}/tests/test_multiturn_rollout.py +0 -0
  236. {freesolo_flash_dev-0.2.29 → freesolo_flash_dev-0.2.30}/tests/test_oom_escalate_gpu.py +0 -0
  237. {freesolo_flash_dev-0.2.29 → freesolo_flash_dev-0.2.30}/tests/test_open_model_policy.py +0 -0
  238. {freesolo_flash_dev-0.2.29 → freesolo_flash_dev-0.2.30}/tests/test_orchestrator_flash.py +0 -0
  239. {freesolo_flash_dev-0.2.29 → freesolo_flash_dev-0.2.30}/tests/test_packing.py +0 -0
  240. {freesolo_flash_dev-0.2.29 → freesolo_flash_dev-0.2.30}/tests/test_poll_helpers.py +0 -0
  241. {freesolo_flash_dev-0.2.29 → freesolo_flash_dev-0.2.30}/tests/test_preflight.py +0 -0
  242. {freesolo_flash_dev-0.2.29 → freesolo_flash_dev-0.2.30}/tests/test_provider_routing.py +0 -0
  243. {freesolo_flash_dev-0.2.29 → freesolo_flash_dev-0.2.30}/tests/test_provider_teardown_robustness.py +0 -0
  244. {freesolo_flash_dev-0.2.29 → freesolo_flash_dev-0.2.30}/tests/test_providers_symmetry.py +0 -0
  245. {freesolo_flash_dev-0.2.29 → freesolo_flash_dev-0.2.30}/tests/test_realized_cost.py +0 -0
  246. {freesolo_flash_dev-0.2.29 → freesolo_flash_dev-0.2.30}/tests/test_repo_cleanup.py +0 -0
  247. {freesolo_flash_dev-0.2.29 → freesolo_flash_dev-0.2.30}/tests/test_resolve_params_b.py +0 -0
  248. {freesolo_flash_dev-0.2.29 → freesolo_flash_dev-0.2.30}/tests/test_resume_on_retry.py +0 -0
  249. {freesolo_flash_dev-0.2.29 → freesolo_flash_dev-0.2.30}/tests/test_runmgmt.py +0 -0
  250. {freesolo_flash_dev-0.2.29 → freesolo_flash_dev-0.2.30}/tests/test_runpod_api_delete.py +0 -0
  251. {freesolo_flash_dev-0.2.29 → freesolo_flash_dev-0.2.30}/tests/test_runpod_key_fingerprint.py +0 -0
  252. {freesolo_flash_dev-0.2.29 → freesolo_flash_dev-0.2.30}/tests/test_runpod_key_waterfall.py +0 -0
  253. {freesolo_flash_dev-0.2.29 → freesolo_flash_dev-0.2.30}/tests/test_runpod_slots.py +0 -0
  254. {freesolo_flash_dev-0.2.29 → freesolo_flash_dev-0.2.30}/tests/test_serve.py +0 -0
  255. {freesolo_flash_dev-0.2.29 → freesolo_flash_dev-0.2.30}/tests/test_serving_contract.py +0 -0
  256. {freesolo_flash_dev-0.2.29 → freesolo_flash_dev-0.2.30}/tests/test_sft_gc_off.py +0 -0
  257. {freesolo_flash_dev-0.2.29 → freesolo_flash_dev-0.2.30}/tests/test_spec_and_validation.py +0 -0
  258. {freesolo_flash_dev-0.2.29 → freesolo_flash_dev-0.2.30}/tests/test_thinking_config.py +0 -0
  259. {freesolo_flash_dev-0.2.29 → freesolo_flash_dev-0.2.30}/tests/test_update_check.py +0 -0
  260. {freesolo_flash_dev-0.2.29 → freesolo_flash_dev-0.2.30}/tests/test_verifiers.py +0 -0
  261. {freesolo_flash_dev-0.2.29 → freesolo_flash_dev-0.2.30}/tests/test_version.py +0 -0
  262. {freesolo_flash_dev-0.2.29 → freesolo_flash_dev-0.2.30}/tests/test_vl_warmstart_adapter_keys.py +0 -0
  263. {freesolo_flash_dev-0.2.29 → freesolo_flash_dev-0.2.30}/tests/test_vl_warmstart_recombine.py +0 -0
  264. {freesolo_flash_dev-0.2.29 → freesolo_flash_dev-0.2.30}/tests/test_vl_weight_sync.py +0 -0
  265. {freesolo_flash_dev-0.2.29 → freesolo_flash_dev-0.2.30}/tests/test_wandb_naming.py +0 -0
  266. {freesolo_flash_dev-0.2.29 → freesolo_flash_dev-0.2.30}/tests/test_warmstart_cross_repo.py +0 -0
  267. {freesolo_flash_dev-0.2.29 → freesolo_flash_dev-0.2.30}/tests/test_weight_cache.py +0 -0
  268. {freesolo_flash_dev-0.2.29 → freesolo_flash_dev-0.2.30}/tests/test_worker_dryrun.py +0 -0
  269. {freesolo_flash_dev-0.2.29 → freesolo_flash_dev-0.2.30}/tests/test_worker_hardexit.py +0 -0
  270. {freesolo_flash_dev-0.2.29 → freesolo_flash_dev-0.2.30}/tests/test_worker_image.py +0 -0
  271. {freesolo_flash_dev-0.2.29 → freesolo_flash_dev-0.2.30}/tests/test_worker_init_heartbeat.py +0 -0
  272. {freesolo_flash_dev-0.2.29 → freesolo_flash_dev-0.2.30}/tests/test_worker_stack.py +0 -0
  273. {freesolo_flash_dev-0.2.29 → freesolo_flash_dev-0.2.30}/tests/test_worker_thinking.py +0 -0
  274. {freesolo_flash_dev-0.2.29 → freesolo_flash_dev-0.2.30}/uv.lock +0 -0
@@ -11,8 +11,8 @@ name: flash worker kernel-cache (per-arch)
11
11
  # warmup runs INSIDE it so the cache matches the image's pinned torch/triton/fla/liger toolchain.
12
12
  #
13
13
  # Secrets: RUNPOD_API_KEY (the warmup GPU pod) + HF_TOKEN (the temp dataset that ferries code + the
14
- # cache artifact) are pulled from Infisical (/flash, prod) via the bootstrap INFISICAL_CLIENT_ID /
15
- # CLIENT_SECRET / PROJECT_ID repo secrets; the GHCR push uses the default GITHUB_TOKEN. Heavy + paid,
14
+ # cache artifact) are native repo Actions secrets, synced from Infisical (prod:/flash-ci) by the
15
+ # github-ci-flash Secret Sync; the GHCR push uses the default GITHUB_TOKEN. Heavy + paid,
16
16
  # but UNGATED: the fingerprint detection in auto-rebake.yml decides WHEN this runs (only on a real
17
17
  # kernel-toolchain change), so there is no manual approval step. Invoked by hand (workflow_dispatch)
18
18
  # OR automatically by auto-rebake.yml (workflow_call) when the kernel toolchain changed.
@@ -179,38 +179,25 @@ jobs:
179
179
  if: steps.gate.outputs.run == 'true'
180
180
  run: uv sync --extra server
181
181
 
182
- - name: Install Infisical CLI
183
- if: steps.gate.outputs.run == 'true'
184
- run: |
185
- curl -1sLf 'https://dl.cloudsmith.io/public/infisical/infisical-cli/setup.deb.sh' | sudo -E bash
186
- sudo apt-get update && sudo apt-get install -y infisical
187
-
188
- # RUNPOD_API_KEY + HF_TOKEN come from Infisical (/flash, prod) -- mirrors infisical-entrypoint.sh;
189
- # the CLI takes the project UUID directly (the secrets-action wants a slug). `env -u
190
- # FLASH_CHALK_SPEC` drops any chalk-spec override living in /flash so the bake always warms the
191
- # repo-default chalk (the value the fingerprint hashed) -- otherwise the image could be warmed with
192
- # one chalk and labeled with another. BASE_IMAGE is digest-pinned so the warm runs inside the EXACT
193
- # base the per-arch tag will sit on.
182
+ # RUNPOD_API_KEY (the warmup GPU pod) + HF_TOKEN (the temp dataset that ferries code/cache) are
183
+ # native repo Actions secrets, synced from Infisical (prod:/flash-ci) by the github-ci-flash
184
+ # Secret Sync. No Infisical CLI/login at job time -- Infisical stays the source of truth and
185
+ # rotations propagate via the sync. FLASH_CHALK_SPEC isn't synced, so bake_kernel_cache.py uses
186
+ # the repo-default DEFAULT_CHALK_SPEC (the value the fingerprint hashed) automatically. BASE_IMAGE
187
+ # is digest-pinned so the warm runs inside the EXACT base the per-arch tag will sit on.
194
188
  - name: Warm kernels on a RunPod ${{ matrix.sm }} GPU -> build/kernel_cache
195
189
  if: steps.gate.outputs.run == 'true'
196
190
  env:
197
- INFISICAL_CLIENT_ID: ${{ secrets.INFISICAL_CLIENT_ID }}
198
- INFISICAL_CLIENT_SECRET: ${{ secrets.INFISICAL_CLIENT_SECRET }}
199
- INFISICAL_PROJECT_ID: ${{ secrets.INFISICAL_PROJECT_ID }}
191
+ RUNPOD_API_KEY: ${{ secrets.RUNPOD_API_KEY }}
192
+ HF_TOKEN: ${{ secrets.HF_TOKEN }}
200
193
  BASE_IMAGE: ${{ steps.base.outputs.ref }}
201
194
  run: |
202
- INFISICAL_TOKEN="$(infisical login --method=universal-auth \
203
- --client-id="$INFISICAL_CLIENT_ID" --client-secret="$INFISICAL_CLIENT_SECRET" \
204
- --silent --plain)"
205
- export INFISICAL_TOKEN
206
- infisical run --projectId "$INFISICAL_PROJECT_ID" --env prod --path /flash -- \
207
- env -u FLASH_CHALK_SPEC \
208
- uv run python docker/bake_kernel_cache.py \
209
- --arch "${{ matrix.arch }}" --sm "${{ matrix.sm }}" \
210
- --gpu-type-id "${{ matrix.gpu_type_id }}" \
211
- --allowed-cuda "${{ matrix.allowed_cuda }}" \
212
- --image "$BASE_IMAGE" \
213
- --out build/kernel_cache
195
+ uv run python docker/bake_kernel_cache.py \
196
+ --arch "${{ matrix.arch }}" --sm "${{ matrix.sm }}" \
197
+ --gpu-type-id "${{ matrix.gpu_type_id }}" \
198
+ --allowed-cuda "${{ matrix.allowed_cuda }}" \
199
+ --image "$BASE_IMAGE" \
200
+ --out build/kernel_cache
214
201
 
215
202
  - name: Free disk space
216
203
  if: steps.gate.outputs.run == 'true'
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: freesolo-flash-dev
3
- Version: 0.2.29
3
+ Version: 0.2.30
4
4
  Summary: Flash — managed LoRA post-training (SFT/GRPO) for Freesolo environments, driven by the `flash` CLI
5
5
  Project-URL: Homepage, https://github.com/freesolo-co/flash
6
6
  Project-URL: Repository, https://github.com/freesolo-co/flash
@@ -72,7 +72,11 @@ _HELP_GROUPS: list[tuple[str, list[tuple[str, str]]]] = [
72
72
  (
73
73
  "environments",
74
74
  [
75
- ("env", "manage Freesolo environments"),
75
+ ("env setup", "scaffold a starter Freesolo environment"),
76
+ ("env list", "list local environment sources"),
77
+ ("env push", "upload a local environment"),
78
+ ("env pull", "download a published environment or file"),
79
+ ("env delete", "delete a published environment"),
76
80
  ],
77
81
  ),
78
82
  (
@@ -160,7 +164,9 @@ class _FlashParser(_ThemedParser):
160
164
  return super().format_help()
161
165
  usage = f"{CLI_NAME} [--debug] [-v] <command> [args]"
162
166
  footers = [
163
- f"new here? run `{CLI_NAME} login`, then `{CLI_NAME} train configs/rl.toml`",
167
+ f"new here? run `{CLI_NAME} login`, then `{CLI_NAME} env setup`",
168
+ f"train after publishing: `{CLI_NAME} env push --name my-env .`, "
169
+ f"then `{CLI_NAME} train configs/rl.toml`",
164
170
  f"any command in depth: `{CLI_NAME} <command> --help`",
165
171
  "docs: https://freesolo.co/docs",
166
172
  ]
@@ -85,6 +85,8 @@ def _identity_from_verify_body(raw: bytes) -> dict[str, Any]:
85
85
  or _str_field(user.get("id"))
86
86
  ),
87
87
  "org_id": _str_field(body.get("org_id")) or _str_field(org.get("id")),
88
+ "org_slug": _str_field(body.get("org_slug")) or _str_field(org.get("slug")),
89
+ "org_name": _str_field(body.get("org_name")) or _str_field(org.get("name")),
88
90
  "api_key_id": (
89
91
  _str_field(body.get("api_key_id"))
90
92
  or _str_field(body.get("key_id"))
@@ -124,7 +126,15 @@ def _external_row(row: dict, token: str, identity: dict[str, Any]) -> dict:
124
126
  out["key_prefix"] = _external_key_prefix(token, identity)
125
127
  if identity.get("email"):
126
128
  out["email"] = identity["email"]
127
- for key in ("user_id", "org_id", "api_key_id", "training_agent_job_id", "project_id"):
129
+ for key in (
130
+ "user_id",
131
+ "org_id",
132
+ "org_slug",
133
+ "org_name",
134
+ "api_key_id",
135
+ "training_agent_job_id",
136
+ "project_id",
137
+ ):
128
138
  if identity.get(key):
129
139
  out[key] = identity[key]
130
140
  return out
@@ -184,13 +194,13 @@ def authenticate(authorization: str | None) -> dict | None:
184
194
  return out
185
195
  if _freesolo_verify(token):
186
196
  identity = _cached_identity(token)
187
- email = _identity_email(identity)
188
- if not email:
197
+ if not identity.get("org_slug"):
189
198
  return None
199
+ email = _identity_email(identity)
190
200
  row = db.lookup_key(token) or db.ensure_external_key(
191
201
  token,
192
202
  key_prefix=_external_key_prefix(token, identity),
193
- email=email,
203
+ email=email or None,
194
204
  )
195
205
  return _external_row(row, token, identity) if row is not None else None
196
206
  return None
@@ -23,18 +23,17 @@ _DEFAULT_GITHUB_REPO = "freesolo-co/environment-hub"
23
23
  _GITHUB_BRANCH = "main"
24
24
  _DEFAULT_ENVIRONMENT_FILE = "environment.py"
25
25
  # Genuine repo-control directories that live at the ROOT of the environment-hub checkout. A DELETE
26
- # runs `git rm -r -- <namespace>/<name>` directly against that checkout, so a slug whose namespace is
26
+ # runs `git rm -r -- <org-slug>/<name>` directly against that checkout, so a slug whose namespace is
27
27
  # one of these would target tracked repo infrastructure (e.g. `DELETE /v1/envs/.github/workflows`).
28
- # `namespace_for` slugifies an email and can NEVER emit a dot-prefixed namespace, so these are never
29
- # publishable namespaces and are always safe to reject on delete.
28
+ # Org slugs can never be dot-prefixed, so these are never publishable namespaces and are always safe
29
+ # to reject on delete.
30
30
  _REPO_CONTROL_TOP_LEVEL_PATHS = {
31
31
  ".github",
32
32
  ".git",
33
33
  }
34
34
  # Top-level segments barred from env-package CONTENTS by `_safe_extract`. A strict SUPERSET of the
35
35
  # repo-control set: it also bars a top-level `source/` dir inside a package. `source` is intentionally
36
- # NOT a repo-control namespace `namespace_for` can slugify a user email to the `source` namespace
37
- # and publish writes `source/<name>` without objection — so the delete validator must reject only
36
+ # NOT a repo-control namespace, so the delete validator must reject only
38
37
  # `_REPO_CONTROL_TOP_LEVEL_PATHS`, not this set, or `source/<name>` envs become undeletable.
39
38
  _BLOCKED_TOP_LEVEL_PATHS = _REPO_CONTROL_TOP_LEVEL_PATHS | {"source"}
40
39
  _TAR_METADATA_TYPES = {
@@ -57,21 +56,18 @@ class EnvPublishError(Exception):
57
56
  self.status = status
58
57
 
59
58
 
60
- # Internal service key has no per-user email; fixed namespace keeps it stable.
61
- _INTERNAL_NAMESPACE = "internal-freesolo-co"
62
-
63
-
64
59
  def namespace_for(key: dict) -> str:
65
- if key.get("auth_kind") == "internal":
66
- return _INTERNAL_NAMESPACE
67
- email = str(key.get("email") or "")
68
- if "@" not in email:
60
+ org = key.get("org") if isinstance(key.get("org"), dict) else {}
61
+ raw = key.get("org_slug") or org.get("slug")
62
+ slug = str(raw or "").strip()
63
+ if not slug:
69
64
  raise EnvPublishError(
70
- "authenticated Freesolo key must include an email (used to derive the hub namespace) — "
65
+ "authenticated Freesolo key must include an org slug (used to derive the hub namespace) — "
71
66
  "publish with a key created at https://freesolo.co/sign-in (`flash login`)"
72
67
  )
73
- slug = re.sub(r"[^a-z0-9]+", "-", email.lower()).strip("-")
74
- return slug or "user"
68
+ if not re.fullmatch(r"[a-z0-9][a-z0-9._-]*", slug):
69
+ raise EnvPublishError("authenticated Freesolo key has an invalid org slug")
70
+ return slug
75
71
 
76
72
 
77
73
  def _sanitize_name(name: str) -> str:
@@ -458,9 +454,8 @@ def _validate_slug(slug: str) -> tuple[str, str]:
458
454
  # An internal-key delete bypasses the namespace-ownership check in delete_package, so this
459
455
  # validator is the only barrier, and a request like `DELETE /v1/envs/.github/workflows` would
460
456
  # otherwise remove tracked repo infrastructure. Reject ONLY `_REPO_CONTROL_TOP_LEVEL_PATHS`, NOT
461
- # the wider publish-content blocklist: `source` is a legitimately publishable namespace
462
- # (`namespace_for` can slugify an email to it and publish writes `source/<name>` without
463
- # objection), so blocking it here would leave those envs publishable-but-undeletable.
457
+ # the wider publish-content blocklist: `source` is a legitimately publishable org slug, so
458
+ # blocking it here would leave those envs publishable-but-undeletable.
464
459
  if namespace in _REPO_CONTROL_TOP_LEVEL_PATHS:
465
460
  raise EnvPublishError(f"invalid env id segment: {namespace!r}")
466
461
  return namespace, name
@@ -587,13 +582,13 @@ def delete_package(*, slug: str, key: dict) -> bool:
587
582
 
588
583
  Returns ``True`` when a package was removed and ``False`` when it was already absent
589
584
  (idempotent). Authorization mirrors publish's namespace isolation: a user key may delete
590
- only environments in its own ``namespace_for(key)`` namespace, while the internal service
591
- key (``auth_kind == "internal"``) may delete any environment.
585
+ only environments in its own org-slug namespace, while the internal service key
586
+ (``auth_kind == "internal"``) may delete any environment.
592
587
  """
593
588
  namespace, name = _validate_slug(slug)
594
589
  canonical = f"{namespace}/{name}"
595
- caller_namespace = namespace_for(key)
596
- if key.get("auth_kind") != "internal" and namespace != caller_namespace:
590
+ caller_namespace = None if key.get("auth_kind") == "internal" else namespace_for(key)
591
+ if caller_namespace is not None and namespace != caller_namespace:
597
592
  raise EnvPublishError(
598
593
  "you can only delete environments in your own namespace "
599
594
  f"({caller_namespace}/…); got {canonical!r}",
@@ -28,6 +28,8 @@ def me(key: Annotated[dict, Depends(require_key)]):
28
28
  "email",
29
29
  "user_id",
30
30
  "org_id",
31
+ "org_slug",
32
+ "org_name",
31
33
  "api_key_id",
32
34
  "training_agent_job_id",
33
35
  "project_id",
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "freesolo-flash-dev"
3
- version = "0.2.29"
3
+ version = "0.2.30"
4
4
  description = "Flash — managed LoRA post-training (SFT/GRPO) for Freesolo environments, driven by the `flash` CLI"
5
5
  readme = "README.md"
6
6
  # RunPod Flash supports Python 3.11-3.12 (not 3.13 yet).
@@ -109,7 +109,7 @@ artifacts = ["flash/py.typed"]
109
109
  # auto-published by .github/workflows/publish-dev.yml: a push to `dev` whose version isn't yet on
110
110
  # PyPI publishes it; pushes that don't change it find it already published and no-op. So cutting a
111
111
  # dev-channel release = bump this (and [project].version) and merge to `dev`.
112
- version = "0.2.29"
112
+ version = "0.2.30"
113
113
 
114
114
  [tool.pytest.ini_options]
115
115
  pythonpath = ["."]
@@ -591,7 +591,12 @@ def _identity_for_token(token: str) -> dict[str, str]:
591
591
  if not token.startswith(_USER_PREFIX):
592
592
  return {}
593
593
  suffix = token.removeprefix(_USER_PREFIX)
594
- return {"email": f"u-{suffix}@x", "key_prefix": "fslo_test", "org_id": f"org-{suffix}"}
594
+ return {
595
+ "email": f"u-{suffix}@x",
596
+ "key_prefix": "fslo_test",
597
+ "org_id": f"org-{suffix}",
598
+ "org_slug": f"org-{suffix}",
599
+ }
595
600
 
596
601
 
597
602
  def test_startup_runs_completion_charge_sweep(monkeypatch, tmp_path):
@@ -21,11 +21,34 @@ def _registered_subcommands() -> set[str]:
21
21
  return set(sub.choices)
22
22
 
23
23
 
24
+ def _registered_env_subcommands() -> set[str]:
25
+ """The nested environment commands argparse registers under `flash env`."""
26
+ parser = cli._build_parser()
27
+ sub = next(a for a in parser._actions if isinstance(a, argparse._SubParsersAction))
28
+ env_parser = sub.choices["env"]
29
+ env_sub = next(a for a in env_parser._actions if isinstance(a, argparse._SubParsersAction))
30
+ return {f"env {name}" for name in env_sub.choices}
31
+
32
+
24
33
  def _catalog_commands() -> set[str]:
25
- """The commands listed in the themed help grid (_HELP_GROUPS)."""
34
+ """The root commands represented by the themed help grid (_HELP_GROUPS).
35
+
36
+ Rows may show nested commands such as `env push`, but argparse only registers `env`
37
+ at the root level.
38
+ """
39
+ return {cmd.split()[0] for _, rows in cli._HELP_GROUPS for cmd, _ in rows}
40
+
41
+
42
+ def _catalog_rows() -> set[str]:
43
+ """The display rows listed in the themed help grid (_HELP_GROUPS)."""
26
44
  return {cmd for _, rows in cli._HELP_GROUPS for cmd, _ in rows}
27
45
 
28
46
 
47
+ def _catalog_env_rows() -> set[str]:
48
+ """The nested environment rows listed in the themed help grid."""
49
+ return {cmd for cmd in _catalog_rows() if cmd.startswith("env ")}
50
+
51
+
29
52
  def test_help_catalog_matches_registered_subcommands() -> None:
30
53
  """Every real subcommand appears once in the themed help, and the help lists no command that
31
54
  isn't real — so the grouped `flash --help` can't drift from the actual CLI surface."""
@@ -41,6 +64,10 @@ def test_help_catalog_matches_registered_subcommands() -> None:
41
64
  assert len(flat) == len(set(flat)), "a command is listed under more than one help group"
42
65
 
43
66
 
67
+ def test_help_catalog_matches_registered_env_subcommands() -> None:
68
+ assert _catalog_env_rows() == _registered_env_subcommands()
69
+
70
+
44
71
  def test_help_styled_is_themed_and_exits_zero(monkeypatch, capsys) -> None:
45
72
  monkeypatch.setenv("FLASH_STYLE", "1")
46
73
  monkeypatch.setenv("NO_COLOR", "1") # layout kept, color dropped — assert on contiguous text
@@ -53,8 +80,10 @@ def test_help_styled_is_themed_and_exits_zero(monkeypatch, capsys) -> None:
53
80
  assert "managed LoRA post-training" in out # themed banner (lowercase) vs argparse description
54
81
  for title in ("getting started", "catalog", "environments", "training", "serving & export"):
55
82
  assert title in out # grouped, not a flat dump
56
- for cmd in _catalog_commands():
83
+ for cmd in _catalog_rows():
57
84
  assert cmd in out
85
+ assert f"{cli.CLI_NAME} env setup" in out
86
+ assert f"{cli.CLI_NAME} env push --name my-env ." in out
58
87
  assert "usage:" in out
59
88
  assert f"{cli.CLI_NAME} <command> --help" in out # next-step hint
60
89
 
@@ -186,11 +186,11 @@ def test_publish_env_plain_without_progress(stub):
186
186
  def test_delete_env_sends_delete_to_slug_path(stub):
187
187
  url, seen = stub
188
188
  client = ApiClient(url, "fslo-user-test")
189
- out = client.delete_env("dev-clado-ai/my-env")
190
- assert out == {"id": "dev-clado-ai/my-env", "deleted": True}
189
+ out = client.delete_env("acme/my-env")
190
+ assert out == {"id": "acme/my-env", "deleted": True}
191
191
  assert seen["method"] == "DELETE"
192
192
  # the namespace/name slug (with its slash) goes straight into the path
193
- assert seen["path"] == "/v1/envs/dev-clado-ai/my-env"
193
+ assert seen["path"] == "/v1/envs/acme/my-env"
194
194
  assert seen["auth"] == "Bearer fslo-user-test"
195
195
 
196
196
 
@@ -63,6 +63,7 @@ def _identity_for_token(token: str) -> dict[str, str]:
63
63
  "email": f"user-{suffix}@example.com",
64
64
  "key_prefix": "fslo_test",
65
65
  "org_id": f"org-{suffix}",
66
+ "org_slug": f"org-{suffix}",
66
67
  }
67
68
 
68
69
 
@@ -176,6 +177,7 @@ def test_health_and_identity_roundtrip(make_client) -> None:
176
177
  # the JSON body back into a dict the CLI prints.
177
178
  assert me["key_prefix"]
178
179
  assert "email" in me
180
+ assert "org_slug" in me
179
181
 
180
182
 
181
183
  def test_create_status_list_cancel_lifecycle(make_client) -> None:
@@ -73,34 +73,31 @@ def _pkg_b64(files: dict[str, str]) -> str:
73
73
  return base64.b64encode(buf.getvalue()).decode()
74
74
 
75
75
 
76
- def test_namespace_is_stable_and_repo_safe():
77
- assert envs.namespace_for({"email": "Dev@Clado.ai"}) == "dev-clado-ai"
78
- assert envs.namespace_for({"email": "dev@clado.ai"}) == "dev-clado-ai"
79
-
80
-
81
- def test_namespace_requires_email():
82
- for key in ({"id": 7}, {"key_prefix": "fslo-abc"}, {}, {"email": "missing-email"}):
83
- with pytest.raises(envs.EnvPublishError, match="email"):
76
+ def test_namespace_uses_org_slug():
77
+ assert envs.namespace_for({"org_slug": "acme"}) == "acme"
78
+ assert envs.namespace_for({"org": {"slug": "freesolo-co"}}) == "freesolo-co"
79
+ assert envs.namespace_for({"email": "dev@clado.ai", "org_slug": "acme"}) == "acme"
80
+
81
+
82
+ def test_namespace_requires_org_slug():
83
+ for key in (
84
+ {"id": 7},
85
+ {"key_prefix": "fslo-abc"},
86
+ {},
87
+ {"email": "dev@clado.ai"},
88
+ {"org_name": "Acme"},
89
+ {"org_slug": "Bad Slug"},
90
+ ):
91
+ with pytest.raises(envs.EnvPublishError, match="org slug"):
84
92
  envs.namespace_for(key)
85
93
 
86
94
 
87
- def test_internal_key_gets_reserved_namespace_without_email():
88
- # The internal/operator service key has a synthetic/shared identity (no per-user email) but is
89
- # trusted to submit runs and read everything, so it must be able to publish too. It gets a
90
- # fixed reserved namespace instead of raising — regardless of whether its row carries an email.
91
- assert envs.namespace_for({"auth_kind": "internal"}) == envs._INTERNAL_NAMESPACE
92
- assert envs.namespace_for({"auth_kind": "internal", "email": ""}) == envs._INTERNAL_NAMESPACE
93
- assert (
94
- envs.namespace_for({"auth_kind": "internal", "email": "missing-email"})
95
- == envs._INTERNAL_NAMESPACE
96
- )
97
-
98
-
99
- def test_internal_special_case_does_not_loosen_user_keys():
100
- # The bypass is reserved for auth_kind == "internal" ONLY: a user key (any other auth_kind, or
101
- # none) without a valid email must still be rejected so two users can't collide on a namespace.
102
- for key in ({"auth_kind": "external"}, {"auth_kind": "user", "email": "no-at"}, {"email": ""}):
103
- with pytest.raises(envs.EnvPublishError, match="email"):
95
+ def test_internal_key_does_not_get_publish_namespace_fallback():
96
+ for key in (
97
+ {"auth_kind": "internal"},
98
+ {"auth_kind": "internal", "email": "internal@freesolo.co"},
99
+ ):
100
+ with pytest.raises(envs.EnvPublishError, match="org slug"):
104
101
  envs.namespace_for(key)
105
102
 
106
103
 
@@ -130,15 +127,15 @@ def test_publish_uploads_to_github_and_returns_slug(monkeypatch):
130
127
  ref = envs.publish_package(
131
128
  package_b64=_pkg_b64(_MINIMAL),
132
129
  name="My Env!",
133
- key={"email": "dev@clado.ai"},
130
+ key={"email": "dev@clado.ai", "org_slug": "acme"},
134
131
  )
135
132
 
136
- root = "dev-clado-ai/my-env"
133
+ root = "acme/my-env"
137
134
  assert ref == root
138
135
  assert captured["repo"] == "freesolo-co/environment-hub"
139
136
  assert captured["token"] == "ghp-test"
140
137
  assert captured["publish_root"] == root
141
- assert captured["message"] == "Upload Flash environment dev-clado-ai/my-env"
138
+ assert captured["message"] == "Upload Flash environment acme/my-env"
142
139
  assert captured["files"] == ["environment.py", "pyproject.toml"]
143
140
 
144
141
 
@@ -155,7 +152,7 @@ def test_publish_rejects_bad_input(monkeypatch):
155
152
  envs.publish_package(
156
153
  package_b64=_pkg_b64({"pyproject.toml": "[project]\nname='e'\n"}),
157
154
  name="e",
158
- key={"email": "dev@clado.ai"},
155
+ key={"org_slug": "acme"},
159
156
  )
160
157
 
161
158
 
@@ -201,7 +198,7 @@ def test_record_published_environment_posts_to_backend(monkeypatch):
201
198
  monkeypatch.setattr(environment_registry.urllib.request, "urlopen", fake_urlopen)
202
199
 
203
200
  ok = environment_registry.record_published_environment(
204
- slug="dev-clado-ai/my-env",
201
+ slug="acme/my-env",
205
202
  name="My Env",
206
203
  key={"org_id": "org-1", "user_id": "user-1", "api_key_id": "key-1"},
207
204
  )
@@ -212,11 +209,11 @@ def test_record_published_environment_posts_to_backend(monkeypatch):
212
209
  body = json.loads(seen["body"])
213
210
  assert body == {
214
211
  "orgId": "org-1",
215
- "slug": "dev-clado-ai/my-env",
212
+ "slug": "acme/my-env",
216
213
  "name": "My Env",
217
214
  "hubRepo": "freesolo-co/environment-hub",
218
215
  "hubRef": "main",
219
- "hubPath": "dev-clado-ai/my-env/environment.py",
216
+ "hubPath": "acme/my-env/environment.py",
220
217
  "publishedByUserId": "user-1",
221
218
  "apiKeyId": "key-1",
222
219
  "metadata": {"source": "flash.env.push"},
@@ -229,7 +226,7 @@ def test_record_published_environment_is_best_effort(monkeypatch):
229
226
  monkeypatch.delenv("FREESOLO_INTERNAL_KEY", raising=False)
230
227
  assert (
231
228
  environment_registry.record_published_environment(
232
- slug="dev-clado-ai/my-env",
229
+ slug="acme/my-env",
233
230
  name="My Env",
234
231
  key={"org_id": "org-1"},
235
232
  )
@@ -262,7 +259,7 @@ def test_record_environment_use_posts_to_backend(monkeypatch):
262
259
  monkeypatch.setattr(environment_registry.urllib.request, "urlopen", fake_urlopen)
263
260
 
264
261
  ok = environment_registry.record_environment_use(
265
- slug="dev-clado-ai/my-env",
262
+ slug="acme/my-env",
266
263
  run_id="flash-1",
267
264
  key={"org_id": "org-1"},
268
265
  )
@@ -272,7 +269,7 @@ def test_record_environment_use_posts_to_backend(monkeypatch):
272
269
  assert seen["headers"]["Authorization"] == "Bearer internal-test"
273
270
  assert json.loads(seen["body"]) == {
274
271
  "orgId": "org-1",
275
- "slug": "dev-clado-ai/my-env",
272
+ "slug": "acme/my-env",
276
273
  "runId": "flash-1",
277
274
  }
278
275
 
@@ -310,7 +307,7 @@ def test_record_training_run_posts_to_backend(monkeypatch):
310
307
  "model": "Qwen/Qwen3.5-4B",
311
308
  "algorithm": "grpo",
312
309
  "phase": "rl",
313
- "environment": {"id": "dev-clado-ai/my-env"},
310
+ "environment": {"id": "acme/my-env"},
314
311
  "gpu": {"type": "RTX 5090"},
315
312
  },
316
313
  platform_context={
@@ -328,7 +325,7 @@ def test_record_training_run_posts_to_backend(monkeypatch):
328
325
  assert body["orgId"] == "org-1"
329
326
  assert body["runId"] == "flash-1"
330
327
  assert body["status"] == "running"
331
- assert body["environmentSlug"] == "dev-clado-ai/my-env"
328
+ assert body["environmentSlug"] == "acme/my-env"
332
329
  assert body["model"] == "Qwen/Qwen3.5-4B"
333
330
 
334
331
 
@@ -571,10 +568,10 @@ def test_github_publish_retries_concurrent_push(monkeypatch, tmp_path):
571
568
  monkeypatch.setattr(envs, "_github_publish_once", fake_publish_once)
572
569
  monkeypatch.setattr(envs.time, "sleep", lambda _seconds: None)
573
570
 
574
- ref = envs._github_publish(tmp_path, name="e", key={"email": "dev@clado.ai"})
571
+ ref = envs._github_publish(tmp_path, name="e", key={"org_slug": "acme"})
575
572
 
576
573
  assert calls["count"] == 2
577
- assert ref == "dev-clado-ai/e"
574
+ assert ref == "acme/e"
578
575
 
579
576
 
580
577
  def _git(cwd: Path, *args: str) -> None:
@@ -631,9 +628,9 @@ def test_delete_package_user_can_delete_own_namespace(monkeypatch):
631
628
 
632
629
  monkeypatch.setattr(envs, "_github_delete", fake_github_delete)
633
630
  assert (
634
- envs.delete_package(slug="dev-clado-ai/my-env", key={"email": "dev@clado.ai"}) is True
631
+ envs.delete_package(slug="acme/my-env", key={"org_slug": "acme"}) is True
635
632
  )
636
- assert seen == {"slug": "dev-clado-ai/my-env", "token": "ghp-test"}
633
+ assert seen == {"slug": "acme/my-env", "token": "ghp-test"}
637
634
 
638
635
 
639
636
  def test_delete_package_rejects_other_users_namespace(monkeypatch):
@@ -642,7 +639,7 @@ def test_delete_package_rejects_other_users_namespace(monkeypatch):
642
639
  envs, "_github_delete", lambda *a, **k: pytest.fail("storage must not be touched")
643
640
  )
644
641
  with pytest.raises(envs.EnvPublishError) as excinfo:
645
- envs.delete_package(slug="someone-else/env", key={"email": "dev@clado.ai"})
642
+ envs.delete_package(slug="someone-else/env", key={"org_slug": "acme"})
646
643
  assert excinfo.value.status == 403
647
644
 
648
645
 
@@ -652,18 +649,16 @@ def test_delete_package_internal_key_can_delete_any_namespace(monkeypatch):
652
649
  monkeypatch.setattr(
653
650
  envs, "_github_delete", lambda slug, *, token: seen.update(slug=slug) or True
654
651
  )
655
- assert (
656
- envs.delete_package(slug="dev-clado-ai/paper-foo", key={"auth_kind": "internal"}) is True
657
- )
658
- assert seen["slug"] == "dev-clado-ai/paper-foo"
652
+ assert envs.delete_package(slug="acme/paper-foo", key={"auth_kind": "internal"}) is True
653
+ assert seen["slug"] == "acme/paper-foo"
659
654
 
660
655
 
661
656
  def test_delete_package_internal_key_rejects_repo_control_namespace(monkeypatch):
662
657
  # The internal key bypasses the namespace-ownership check, so _validate_slug is the ONLY barrier
663
658
  # before `git rm -r -- <namespace>/<name>`. A GENUINE repo-control top-level path (a dir at the
664
659
  # root of the hub checkout) must be rejected so e.g. DELETE /v1/envs/.github/workflows can't
665
- # remove tracked repo infrastructure. Only `.git`/`.github` qualify `namespace_for` can never
666
- # slugify an email to a dot-prefixed namespace, so these are never publishable.
660
+ # remove tracked repo infrastructure. Only `.git`/`.github` qualify because org slugs can never
661
+ # be dot-prefixed, so these are never publishable.
667
662
  monkeypatch.setenv("GITHUB_TOKEN", "ghp-test")
668
663
  monkeypatch.setattr(
669
664
  envs, "_github_delete", lambda *a, **k: pytest.fail("storage must not be touched")
@@ -677,10 +672,9 @@ def test_delete_package_internal_key_rejects_repo_control_namespace(monkeypatch)
677
672
 
678
673
  def test_delete_package_allows_publishable_source_namespace(monkeypatch):
679
674
  # Regression guard for publish/delete symmetry: `source` is in publish's package-CONTENT
680
- # blocklist (_BLOCKED_TOP_LEVEL_PATHS via _safe_extract) but is a legitimate user NAMESPACE —
681
- # `namespace_for` slugifies an email like `source@` to `source` and publish writes `source/<name>`
682
- # without objection. Delete must therefore reach storage for `source/<name>` (not 400), or those
683
- # envs would be publishable-but-undeletable.
675
+ # blocklist (_BLOCKED_TOP_LEVEL_PATHS via _safe_extract) but is a legitimate org namespace.
676
+ # Delete must therefore reach storage for `source/<name>` (not 400), or those envs would be
677
+ # publishable-but-undeletable.
684
678
  monkeypatch.setenv("GITHUB_TOKEN", "ghp-test")
685
679
  seen: dict[str, str] = {}
686
680
 
@@ -689,8 +683,8 @@ def test_delete_package_allows_publishable_source_namespace(monkeypatch):
689
683
  return True
690
684
 
691
685
  monkeypatch.setattr(envs, "_github_delete", fake_delete)
692
- # A user key whose email normalizes to the `source` namespace deletes its OWN `source/<name>`.
693
- source_key = {"email": "source@"}
686
+ # A user key whose org slug is `source` deletes its OWN `source/<name>`.
687
+ source_key = {"org_slug": "source"}
694
688
  assert envs.namespace_for(source_key) == "source"
695
689
  assert "source" in envs._BLOCKED_TOP_LEVEL_PATHS # still barred from package CONTENTS
696
690
  assert envs.delete_package(slug="source/my-env", key=source_key) is True
@@ -718,7 +712,7 @@ def test_delete_package_validates_slug(monkeypatch):
718
712
 
719
713
 
720
714
  def test_canonical_env_id_accepts_only_canonical_form():
721
- assert envs.canonical_env_id("dev-clado-ai/my-env") == "dev-clado-ai/my-env"
715
+ assert envs.canonical_env_id("acme/my-env") == "acme/my-env"
722
716
  for bad in ("ns/env/", " ns/env", "ns/env ", "ns/env%20", "Ns/Env", "noslash"):
723
717
  with pytest.raises(envs.EnvPublishError):
724
718
  envs.canonical_env_id(bad)
@@ -727,7 +721,7 @@ def test_canonical_env_id_accepts_only_canonical_form():
727
721
  def test_delete_package_requires_github_token(monkeypatch):
728
722
  monkeypatch.delenv("GITHUB_TOKEN", raising=False)
729
723
  with pytest.raises(envs.EnvPublishError) as excinfo:
730
- envs.delete_package(slug="internal-freesolo-co/x", key={"auth_kind": "internal"})
724
+ envs.delete_package(slug="acme/x", key={"auth_kind": "internal"})
731
725
  assert excinfo.value.status == 503
732
726
  assert "GITHUB_TOKEN" in str(excinfo.value)
733
727
 
@@ -908,7 +902,7 @@ def test_record_deleted_environment_sends_delete(monkeypatch):
908
902
  monkeypatch.setattr(environment_registry.urllib.request, "urlopen", fake_urlopen)
909
903
 
910
904
  ok = environment_registry.record_deleted_environment(
911
- slug="dev-clado-ai/my-env",
905
+ slug="acme/my-env",
912
906
  key={"org_id": "org-1"},
913
907
  )
914
908
 
@@ -916,7 +910,7 @@ def test_record_deleted_environment_sends_delete(monkeypatch):
916
910
  assert seen["url"] == "https://backend.test/api/flash/environments/internal"
917
911
  assert seen["method"] == "DELETE"
918
912
  assert seen["headers"]["Authorization"] == "Bearer internal-test"
919
- assert json.loads(seen["body"]) == {"orgId": "org-1", "slug": "dev-clado-ai/my-env"}
913
+ assert json.loads(seen["body"]) == {"orgId": "org-1", "slug": "acme/my-env"}
920
914
 
921
915
 
922
916
  def test_record_deleted_environment_is_best_effort(monkeypatch):
@@ -925,7 +919,7 @@ def test_record_deleted_environment_is_best_effort(monkeypatch):
925
919
  monkeypatch.delenv("FREESOLO_INTERNAL_KEY", raising=False)
926
920
  assert (
927
921
  environment_registry.record_deleted_environment(
928
- slug="dev-clado-ai/my-env",
922
+ slug="acme/my-env",
929
923
  key={"org_id": "org-1"},
930
924
  )
931
925
  is False