ellf-cli 22.0.3__tar.gz → 22.0.4__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 (429) hide show
  1. {ellf_cli-22.0.3/ellf_cli.egg-info → ellf_cli-22.0.4}/PKG-INFO +1 -1
  2. {ellf_cli-22.0.3 → ellf_cli-22.0.4}/README.md +10 -11
  3. {ellf_cli-22.0.3 → ellf_cli-22.0.4}/ellf_cli/__main__.py +3 -3
  4. {ellf_cli-22.0.3 → ellf_cli-22.0.4}/ellf_cli/commands/infra/_helpers.py +7 -12
  5. {ellf_cli-22.0.3 → ellf_cli-22.0.4}/ellf_cli/commands/infra/deploy.py +92 -17
  6. {ellf_cli-22.0.3 → ellf_cli-22.0.4}/ellf_cli/commands/infra/init.py +36 -13
  7. {ellf_cli-22.0.3 → ellf_cli-22.0.4}/ellf_cli/commands/infra/init_values.py +35 -8
  8. {ellf_cli-22.0.3 → ellf_cli-22.0.4}/ellf_cli/commands/infra/register.py +11 -7
  9. {ellf_cli-22.0.3 → ellf_cli-22.0.4}/ellf_cli/commands/infra/start.py +20 -66
  10. ellf_cli-22.0.4/ellf_cli/commands/infra/tls.py +302 -0
  11. {ellf_cli-22.0.3 → ellf_cli-22.0.4}/ellf_cli/ellf.json +33 -46
  12. {ellf_cli-22.0.3 → ellf_cli-22.0.4}/ellf_cli/helm.py +9 -3
  13. {ellf_cli-22.0.3 → ellf_cli-22.0.4}/ellf_cli/messages.py +3 -1
  14. ellf_cli-22.0.4/ellf_cli/version/22.0.3_2026-10-31-16-57-27_patch_e818d7fa-a4ba-4041-abdd-c6363fcc0a30 +3 -0
  15. {ellf_cli-22.0.3 → ellf_cli-22.0.4/ellf_cli.egg-info}/PKG-INFO +1 -1
  16. {ellf_cli-22.0.3 → ellf_cli-22.0.4}/ellf_cli.egg-info/SOURCES.txt +1 -0
  17. {ellf_cli-22.0.3 → ellf_cli-22.0.4}/tests/test_infra_init.py +67 -2
  18. ellf_cli-22.0.4/tests/test_infra_tls.py +387 -0
  19. ellf_cli-22.0.3/ellf_cli/commands/infra/tls.py +0 -571
  20. ellf_cli-22.0.3/tests/test_infra_tls.py +0 -262
  21. {ellf_cli-22.0.3 → ellf_cli-22.0.4}/LICENSE +0 -0
  22. {ellf_cli-22.0.3 → ellf_cli-22.0.4}/MANIFEST.in +0 -0
  23. {ellf_cli-22.0.3 → ellf_cli-22.0.4}/ellf_cli/__init__.py +0 -0
  24. {ellf_cli-22.0.3 → ellf_cli-22.0.4}/ellf_cli/about.json +0 -0
  25. {ellf_cli-22.0.3 → ellf_cli-22.0.4}/ellf_cli/about.py +0 -0
  26. {ellf_cli-22.0.3 → ellf_cli-22.0.4}/ellf_cli/agents.py +0 -0
  27. {ellf_cli-22.0.3 → ellf_cli-22.0.4}/ellf_cli/appdirs.py +0 -0
  28. {ellf_cli-22.0.3 → ellf_cli-22.0.4}/ellf_cli/auth.py +0 -0
  29. {ellf_cli-22.0.3 → ellf_cli-22.0.4}/ellf_cli/cli.py +0 -0
  30. {ellf_cli-22.0.3 → ellf_cli-22.0.4}/ellf_cli/cloud/__init__.py +0 -0
  31. {ellf_cli-22.0.3 → ellf_cli-22.0.4}/ellf_cli/cloud/gcp.py +0 -0
  32. {ellf_cli-22.0.3 → ellf_cli-22.0.4}/ellf_cli/cluster_config.py +0 -0
  33. {ellf_cli-22.0.3 → ellf_cli-22.0.4}/ellf_cli/commands/__init__.py +0 -0
  34. {ellf_cli-22.0.3 → ellf_cli-22.0.4}/ellf_cli/commands/_cluster_select.py +0 -0
  35. {ellf_cli-22.0.3 → ellf_cli-22.0.4}/ellf_cli/commands/_org_select.py +0 -0
  36. {ellf_cli-22.0.3 → ellf_cli-22.0.4}/ellf_cli/commands/_project_scope.py +0 -0
  37. {ellf_cli-22.0.3 → ellf_cli-22.0.4}/ellf_cli/commands/_recipe_file.py +0 -0
  38. {ellf_cli-22.0.3 → ellf_cli-22.0.4}/ellf_cli/commands/_recipe_subcommand.py +0 -0
  39. {ellf_cli-22.0.3 → ellf_cli-22.0.4}/ellf_cli/commands/_state.py +0 -0
  40. {ellf_cli-22.0.3 → ellf_cli-22.0.4}/ellf_cli/commands/_usage.py +0 -0
  41. {ellf_cli-22.0.3 → ellf_cli-22.0.4}/ellf_cli/commands/actions.py +0 -0
  42. {ellf_cli-22.0.3 → ellf_cli-22.0.4}/ellf_cli/commands/agents.py +0 -0
  43. {ellf_cli-22.0.3 → ellf_cli-22.0.4}/ellf_cli/commands/assets.py +0 -0
  44. {ellf_cli-22.0.3 → ellf_cli-22.0.4}/ellf_cli/commands/clusters.py +0 -0
  45. {ellf_cli-22.0.3 → ellf_cli-22.0.4}/ellf_cli/commands/config.py +0 -0
  46. {ellf_cli-22.0.3 → ellf_cli-22.0.4}/ellf_cli/commands/datasets.py +0 -0
  47. {ellf_cli-22.0.3 → ellf_cli-22.0.4}/ellf_cli/commands/files/__init__.py +0 -0
  48. {ellf_cli-22.0.3 → ellf_cli-22.0.4}/ellf_cli/commands/files/cp.py +0 -0
  49. {ellf_cli-22.0.3 → ellf_cli-22.0.4}/ellf_cli/commands/files/ls.py +0 -0
  50. {ellf_cli-22.0.3 → ellf_cli-22.0.4}/ellf_cli/commands/files/rm.py +0 -0
  51. {ellf_cli-22.0.3 → ellf_cli-22.0.4}/ellf_cli/commands/files/rsync.py +0 -0
  52. {ellf_cli-22.0.3 → ellf_cli-22.0.4}/ellf_cli/commands/files/stats.py +0 -0
  53. {ellf_cli-22.0.3 → ellf_cli-22.0.4}/ellf_cli/commands/general.py +0 -0
  54. {ellf_cli-22.0.3 → ellf_cli-22.0.4}/ellf_cli/commands/import_export.py +0 -0
  55. {ellf_cli-22.0.3 → ellf_cli-22.0.4}/ellf_cli/commands/infra/__init__.py +0 -0
  56. {ellf_cli-22.0.3 → ellf_cli-22.0.4}/ellf_cli/commands/infra/_config_io.py +0 -0
  57. {ellf_cli-22.0.3 → ellf_cli-22.0.4}/ellf_cli/commands/infra/configure.py +0 -0
  58. {ellf_cli-22.0.3 → ellf_cli-22.0.4}/ellf_cli/commands/infra/hibernate.py +0 -0
  59. {ellf_cli-22.0.3 → ellf_cli-22.0.4}/ellf_cli/commands/infra/pause.py +0 -0
  60. {ellf_cli-22.0.3 → ellf_cli-22.0.4}/ellf_cli/commands/infra/provision.py +0 -0
  61. {ellf_cli-22.0.3 → ellf_cli-22.0.4}/ellf_cli/commands/infra/setup.py +0 -0
  62. {ellf_cli-22.0.3 → ellf_cli-22.0.4}/ellf_cli/commands/infra/terraform.py +0 -0
  63. {ellf_cli-22.0.3 → ellf_cli-22.0.4}/ellf_cli/commands/jobs.py +0 -0
  64. {ellf_cli-22.0.3 → ellf_cli-22.0.4}/ellf_cli/commands/mcp.py +0 -0
  65. {ellf_cli-22.0.3 → ellf_cli-22.0.4}/ellf_cli/commands/packages.py +0 -0
  66. {ellf_cli-22.0.3 → ellf_cli-22.0.4}/ellf_cli/commands/paths.py +0 -0
  67. {ellf_cli-22.0.3 → ellf_cli-22.0.4}/ellf_cli/commands/plans.py +0 -0
  68. {ellf_cli-22.0.3 → ellf_cli-22.0.4}/ellf_cli/commands/projects.py +0 -0
  69. {ellf_cli-22.0.3 → ellf_cli-22.0.4}/ellf_cli/commands/publish_code.py +0 -0
  70. {ellf_cli-22.0.3 → ellf_cli-22.0.4}/ellf_cli/commands/publish_data.py +0 -0
  71. {ellf_cli-22.0.3 → ellf_cli-22.0.4}/ellf_cli/commands/recipes.py +0 -0
  72. {ellf_cli-22.0.3 → ellf_cli-22.0.4}/ellf_cli/commands/secrets.py +0 -0
  73. {ellf_cli-22.0.3 → ellf_cli-22.0.4}/ellf_cli/commands/services.py +0 -0
  74. {ellf_cli-22.0.3 → ellf_cli-22.0.4}/ellf_cli/commands/sessions.py +0 -0
  75. {ellf_cli-22.0.3 → ellf_cli-22.0.4}/ellf_cli/commands/support.py +0 -0
  76. {ellf_cli-22.0.3 → ellf_cli-22.0.4}/ellf_cli/commands/tasks.py +0 -0
  77. {ellf_cli-22.0.3 → ellf_cli-22.0.4}/ellf_cli/commands/todos.py +0 -0
  78. {ellf_cli-22.0.3 → ellf_cli-22.0.4}/ellf_cli/commands/users.py +0 -0
  79. {ellf_cli-22.0.3 → ellf_cli-22.0.4}/ellf_cli/config.py +0 -0
  80. {ellf_cli-22.0.3 → ellf_cli-22.0.4}/ellf_cli/ellf_skills/.gitignore +0 -0
  81. {ellf_cli-22.0.3 → ellf_cli-22.0.4}/ellf_cli/ellf_skills/render.py +0 -0
  82. {ellf_cli-22.0.3 → ellf_cli-22.0.4}/ellf_cli/ellf_skills/skills/ellf-annotate/SKILL.assistant.md +0 -0
  83. {ellf_cli-22.0.3 → ellf_cli-22.0.4}/ellf_cli/ellf_skills/skills/ellf-annotate/SKILL.coding.md +0 -0
  84. {ellf_cli-22.0.3 → ellf_cli-22.0.4}/ellf_cli/ellf_skills/skills/ellf-annotate/references/annotation_audit.assistant.md +0 -0
  85. {ellf_cli-22.0.3 → ellf_cli-22.0.4}/ellf_cli/ellf_skills/skills/ellf-annotate/references/annotation_audit.coding.md +0 -0
  86. {ellf_cli-22.0.3 → ellf_cli-22.0.4}/ellf_cli/ellf_skills/skills/ellf-annotate/references/builtin_ellf_annotation_recipes.assistant.md +0 -0
  87. {ellf_cli-22.0.3 → ellf_cli-22.0.4}/ellf_cli/ellf_skills/skills/ellf-annotate/references/builtin_ellf_annotation_recipes.coding.md +0 -0
  88. {ellf_cli-22.0.3 → ellf_cli-22.0.4}/ellf_cli/ellf_skills/skills/ellf-annotate/references/builtin_prodigy_recipes.coding.md +0 -0
  89. {ellf_cli-22.0.3 → ellf_cli-22.0.4}/ellf_cli/ellf_skills/skills/ellf-annotate/references/label_schemes.assistant.md +0 -0
  90. {ellf_cli-22.0.3 → ellf_cli-22.0.4}/ellf_cli/ellf_skills/skills/ellf-annotate/references/label_schemes.coding.md +0 -0
  91. {ellf_cli-22.0.3 → ellf_cli-22.0.4}/ellf_cli/ellf_skills/skills/ellf-ask/SKILL.md +0 -0
  92. {ellf_cli-22.0.3 → ellf_cli-22.0.4}/ellf_cli/ellf_skills/skills/ellf-handoff/SKILL.assistant.md +0 -0
  93. {ellf_cli-22.0.3 → ellf_cli-22.0.4}/ellf_cli/ellf_skills/skills/ellf-monitor/SKILL.assistant.md +0 -0
  94. {ellf_cli-22.0.3 → ellf_cli-22.0.4}/ellf_cli/ellf_skills/skills/ellf-monitor/SKILL.coding.md +0 -0
  95. {ellf_cli-22.0.3 → ellf_cli-22.0.4}/ellf_cli/ellf_skills/skills/ellf-monitor/references/annotation_metrics.assistant.md +0 -0
  96. {ellf_cli-22.0.3 → ellf_cli-22.0.4}/ellf_cli/ellf_skills/skills/ellf-monitor/references/annotation_metrics.coding.md +0 -0
  97. {ellf_cli-22.0.3 → ellf_cli-22.0.4}/ellf_cli/ellf_skills/skills/ellf-monitor/references/training_monitoring.assistant.md +0 -0
  98. {ellf_cli-22.0.3 → ellf_cli-22.0.4}/ellf_cli/ellf_skills/skills/ellf-monitor/references/training_monitoring.coding.md +0 -0
  99. {ellf_cli-22.0.3 → ellf_cli-22.0.4}/ellf_cli/ellf_skills/skills/ellf-monitor/scripts.coding/check_training.py +0 -0
  100. {ellf_cli-22.0.3 → ellf_cli-22.0.4}/ellf_cli/ellf_skills/skills/ellf-ops/SKILL.assistant.md +0 -0
  101. {ellf_cli-22.0.3 → ellf_cli-22.0.4}/ellf_cli/ellf_skills/skills/ellf-ops/SKILL.coding.md +0 -0
  102. {ellf_cli-22.0.3 → ellf_cli-22.0.4}/ellf_cli/ellf_skills/skills/ellf-ops/references.coding/data_infra_cli.md +0 -0
  103. {ellf_cli-22.0.3 → ellf_cli-22.0.4}/ellf_cli/ellf_skills/skills/ellf-ops/scripts.coding/run_job.py +0 -0
  104. {ellf_cli-22.0.3 → ellf_cli-22.0.4}/ellf_cli/ellf_skills/skills/ellf-patterns/SKILL.coding.md +0 -0
  105. {ellf_cli-22.0.3 → ellf_cli-22.0.4}/ellf_cli/ellf_skills/skills/ellf-patterns/references/pattern_strategies.md +0 -0
  106. {ellf_cli-22.0.3 → ellf_cli-22.0.4}/ellf_cli/ellf_skills/skills/ellf-prodigy/SKILL.coding.md +0 -0
  107. {ellf_cli-22.0.3 → ellf_cli-22.0.4}/ellf_cli/ellf_skills/skills/ellf-prodigy/assets/templates/template_action_recipe.py +0 -0
  108. {ellf_cli-22.0.3 → ellf_cli-22.0.4}/ellf_cli/ellf_skills/skills/ellf-prodigy/assets/templates/template_agent_recipe.py +0 -0
  109. {ellf_cli-22.0.3 → ellf_cli-22.0.4}/ellf_cli/ellf_skills/skills/ellf-prodigy/assets/templates/template_blocks_ui.py +0 -0
  110. {ellf_cli-22.0.3 → ellf_cli-22.0.4}/ellf_cli/ellf_skills/skills/ellf-prodigy/assets/templates/template_correct.py +0 -0
  111. {ellf_cli-22.0.3 → ellf_cli-22.0.4}/ellf_cli/ellf_skills/skills/ellf-prodigy/assets/templates/template_custom_ui.py +0 -0
  112. {ellf_cli-22.0.3 → ellf_cli-22.0.4}/ellf_cli/ellf_skills/skills/ellf-prodigy/assets/templates/template_manual.py +0 -0
  113. {ellf_cli-22.0.3 → ellf_cli-22.0.4}/ellf_cli/ellf_skills/skills/ellf-prodigy/assets/templates/template_pages_ui.py +0 -0
  114. {ellf_cli-22.0.3 → ellf_cli-22.0.4}/ellf_cli/ellf_skills/skills/ellf-prodigy/assets/templates/template_routing.py +0 -0
  115. {ellf_cli-22.0.3 → ellf_cli-22.0.4}/ellf_cli/ellf_skills/skills/ellf-prodigy/assets/templates/template_service_recipe.py +0 -0
  116. {ellf_cli-22.0.3 → ellf_cli-22.0.4}/ellf_cli/ellf_skills/skills/ellf-prodigy/assets/templates/template_task_recipe.py +0 -0
  117. {ellf_cli-22.0.3 → ellf_cli-22.0.4}/ellf_cli/ellf_skills/skills/ellf-prodigy/assets/templates/template_teach.py +0 -0
  118. {ellf_cli-22.0.3 → ellf_cli-22.0.4}/ellf_cli/ellf_skills/skills/ellf-prodigy/references/builtin_recipes.md +0 -0
  119. {ellf_cli-22.0.3 → ellf_cli-22.0.4}/ellf_cli/ellf_skills/skills/ellf-prodigy/references/ellf_recipe_sdk.md +0 -0
  120. {ellf_cli-22.0.3 → ellf_cli-22.0.4}/ellf_cli/ellf_skills/skills/ellf-prodigy/references/lint_recipe.py +0 -0
  121. {ellf_cli-22.0.3 → ellf_cli-22.0.4}/ellf_cli/ellf_skills/skills/ellf-prodigy/references/prodigy_recipe_api.md +0 -0
  122. {ellf_cli-22.0.3 → ellf_cli-22.0.4}/ellf_cli/ellf_skills/skills/ellf-prodigy/references/template_index.md +0 -0
  123. {ellf_cli-22.0.3 → ellf_cli-22.0.4}/ellf_cli/ellf_skills/skills/ellf-project/SKILL.assistant.md +0 -0
  124. {ellf_cli-22.0.3 → ellf_cli-22.0.4}/ellf_cli/ellf_skills/skills/ellf-project/SKILL.coding.md +0 -0
  125. {ellf_cli-22.0.3 → ellf_cli-22.0.4}/ellf_cli/ellf_skills/skills/ellf-project/references/annotation_agents.md +0 -0
  126. {ellf_cli-22.0.3 → ellf_cli-22.0.4}/ellf_cli/ellf_skills/skills/ellf-project/references/consulting_patterns.md +0 -0
  127. {ellf_cli-22.0.3 → ellf_cli-22.0.4}/ellf_cli/ellf_skills/skills/ellf-project/references/explosion_strategy.md +0 -0
  128. {ellf_cli-22.0.3 → ellf_cli-22.0.4}/ellf_cli/ellf_skills/skills/ellf-project/references/jev.coding.md +0 -0
  129. {ellf_cli-22.0.3 → ellf_cli-22.0.4}/ellf_cli/ellf_skills/skills/ellf-project/references/label_schemes.assistant.md +0 -0
  130. {ellf_cli-22.0.3 → ellf_cli-22.0.4}/ellf_cli/ellf_skills/skills/ellf-project/references/label_schemes.coding.md +0 -0
  131. {ellf_cli-22.0.3 → ellf_cli-22.0.4}/ellf_cli/ellf_skills/skills/ellf-support/SKILL.assistant.md +0 -0
  132. {ellf_cli-22.0.3 → ellf_cli-22.0.4}/ellf_cli/ellf_skills/skills/ellf-support/SKILL.coding.md +0 -0
  133. {ellf_cli-22.0.3 → ellf_cli-22.0.4}/ellf_cli/ellf_skills/skills/ellf-template/SKILL.coding.md +0 -0
  134. {ellf_cli-22.0.3 → ellf_cli-22.0.4}/ellf_cli/ellf_skills/skills/ellf-template/references/template_format.md +0 -0
  135. {ellf_cli-22.0.3 → ellf_cli-22.0.4}/ellf_cli/ellf_skills/skills/ellf-template/scripts/placeholders.py +0 -0
  136. {ellf_cli-22.0.3 → ellf_cli-22.0.4}/ellf_cli/ellf_skills/skills/ellf-todo/SKILL.coding.md +0 -0
  137. {ellf_cli-22.0.3 → ellf_cli-22.0.4}/ellf_cli/ellf_skills/skills/ellf-train/SKILL.assistant.md +0 -0
  138. {ellf_cli-22.0.3 → ellf_cli-22.0.4}/ellf_cli/ellf_skills/skills/ellf-train/SKILL.coding.md +0 -0
  139. {ellf_cli-22.0.3 → ellf_cli-22.0.4}/ellf_cli/ellf_skills/skills/ellf-train/references/config_advanced.coding.md +0 -0
  140. {ellf_cli-22.0.3 → ellf_cli-22.0.4}/ellf_cli/ellf_skills/skills/ellf-train/references/config_architectures.coding.md +0 -0
  141. {ellf_cli-22.0.3 → ellf_cli-22.0.4}/ellf_cli/ellf_skills/skills/ellf-train/references/config_training.coding.md +0 -0
  142. {ellf_cli-22.0.3 → ellf_cli-22.0.4}/ellf_cli/ellf_skills/skills/ellf-train/references/diagnostics.md +0 -0
  143. {ellf_cli-22.0.3 → ellf_cli-22.0.4}/ellf_cli/ellf_skills/skills/ellf-train/references/evaluation_guide.md +0 -0
  144. {ellf_cli-22.0.3 → ellf_cli-22.0.4}/ellf_cli/ellf_skills/skills/ellf-train/references/experiment_patterns.coding.md +0 -0
  145. {ellf_cli-22.0.3 → ellf_cli-22.0.4}/ellf_cli/ellf_skills/skills/ellf-train/references/model_selection.md +0 -0
  146. {ellf_cli-22.0.3 → ellf_cli-22.0.4}/ellf_cli/ellf_skills/skills/ellf-train/references/training_paradigms.md +0 -0
  147. {ellf_cli-22.0.3 → ellf_cli-22.0.4}/ellf_cli/ellf_skills/skills/ellf-train/references/training_troubleshooting.coding.md +0 -0
  148. {ellf_cli-22.0.3 → ellf_cli-22.0.4}/ellf_cli/ellf_skills/skills/ellf-train/references/workflow.assistant.md +0 -0
  149. {ellf_cli-22.0.3 → ellf_cli-22.0.4}/ellf_cli/ellf_skills/skills/ellf-train/references/workflow.coding.md +0 -0
  150. {ellf_cli-22.0.3 → ellf_cli-22.0.4}/ellf_cli/ellf_skills/skills/ellf-train/scripts.coding/ellf_logger.py +0 -0
  151. {ellf_cli-22.0.3 → ellf_cli-22.0.4}/ellf_cli/errors.py +0 -0
  152. {ellf_cli-22.0.3 → ellf_cli-22.0.4}/ellf_cli/interactivity.py +0 -0
  153. {ellf_cli-22.0.3 → ellf_cli-22.0.4}/ellf_cli/key_pair.py +0 -0
  154. {ellf_cli-22.0.3 → ellf_cli-22.0.4}/ellf_cli/main.py +0 -0
  155. {ellf_cli-22.0.3 → ellf_cli-22.0.4}/ellf_cli/mcp_proxy.py +0 -0
  156. {ellf_cli-22.0.3 → ellf_cli-22.0.4}/ellf_cli/pypi_auth.py +0 -0
  157. {ellf_cli-22.0.3 → ellf_cli-22.0.4}/ellf_cli/query.py +0 -0
  158. {ellf_cli-22.0.3 → ellf_cli-22.0.4}/ellf_cli/recipes_cookiecutter/cookiecutter.json +0 -0
  159. {ellf_cli-22.0.3 → ellf_cli-22.0.4}/ellf_cli/recipes_cookiecutter/{{cookiecutter.package_dir}}/.gitignore +0 -0
  160. {ellf_cli-22.0.3 → ellf_cli-22.0.4}/ellf_cli/recipes_cookiecutter/{{cookiecutter.package_dir}}/README.md.tmpl +0 -0
  161. {ellf_cli-22.0.3 → ellf_cli-22.0.4}/ellf_cli/recipes_cookiecutter/{{cookiecutter.package_dir}}/requirements-dev.in +0 -0
  162. {ellf_cli-22.0.3 → ellf_cli-22.0.4}/ellf_cli/recipes_cookiecutter/{{cookiecutter.package_dir}}/requirements.in +0 -0
  163. {ellf_cli-22.0.3 → ellf_cli-22.0.4}/ellf_cli/recipes_cookiecutter/{{cookiecutter.package_dir}}/setup.py.tmpl +0 -0
  164. {ellf_cli-22.0.3 → ellf_cli-22.0.4}/ellf_cli/recipes_cookiecutter/{{cookiecutter.package_dir}}/{{cookiecutter.package_name}}/__init__.py +0 -0
  165. {ellf_cli-22.0.3 → ellf_cli-22.0.4}/ellf_cli/recipes_cookiecutter/{{cookiecutter.package_dir}}/{{cookiecutter.package_name}}/about.py +0 -0
  166. {ellf_cli-22.0.3 → ellf_cli-22.0.4}/ellf_cli/recipes_cookiecutter/{{cookiecutter.package_dir}}/{{cookiecutter.package_name}}/recipes/__init__.py +0 -0
  167. {ellf_cli-22.0.3 → ellf_cli-22.0.4}/ellf_cli/recipes_cookiecutter/{{cookiecutter.package_dir}}/{{cookiecutter.package_name}}/recipes/example_task.py +0 -0
  168. {ellf_cli-22.0.3 → ellf_cli-22.0.4}/ellf_cli/testing/__init__.py +0 -0
  169. {ellf_cli-22.0.3 → ellf_cli-22.0.4}/ellf_cli/ty.py +0 -0
  170. {ellf_cli-22.0.3 → ellf_cli-22.0.4}/ellf_cli/ui.py +0 -0
  171. {ellf_cli-22.0.3 → ellf_cli-22.0.4}/ellf_cli/url.py +0 -0
  172. {ellf_cli-22.0.3 → ellf_cli-22.0.4}/ellf_cli/util.py +0 -0
  173. {ellf_cli-22.0.3 → ellf_cli-22.0.4}/ellf_cli/version/17.0.114_2026-08-19-08-36-57__1a4e37c1-471e-4a81-b4d5-1ffe7dec3a61 +0 -0
  174. {ellf_cli-22.0.3 → ellf_cli-22.0.4}/ellf_cli/version/17.0.114_2026-08-19-09-25-21_patch_33ff1e26-0e5e-46d8-8052-ab1d032e8b6c +0 -0
  175. {ellf_cli-22.0.3 → ellf_cli-22.0.4}/ellf_cli/version/17.0.115_2026-08-19-09-27-13_patch_4ac728c3-0342-4a36-adbc-e28d2d99b1ba +0 -0
  176. {ellf_cli-22.0.3 → ellf_cli-22.0.4}/ellf_cli/version/17.0.116_2026-08-19-09-30-47_patch_c706fef6-6813-4183-b268-12e7b00d7d0b +0 -0
  177. {ellf_cli-22.0.3 → ellf_cli-22.0.4}/ellf_cli/version/17.0.117_2026-08-19-09-39-14_patch_c6b0495f-2f88-4aa8-9ebf-0a235b4c407e +0 -0
  178. {ellf_cli-22.0.3 → ellf_cli-22.0.4}/ellf_cli/version/17.0.118_2026-08-19-09-52-00_patch_b395655a-8d78-4344-9266-6901c20f6517 +0 -0
  179. {ellf_cli-22.0.3 → ellf_cli-22.0.4}/ellf_cli/version/17.0.119_2026-08-19-10-22-38_patch_8f95c49d-aedc-41b8-816d-ccb95f048831 +0 -0
  180. {ellf_cli-22.0.3 → ellf_cli-22.0.4}/ellf_cli/version/17.0.120_2026-08-19-10-26-58_patch_7fbf35e6-cc01-4b75-8c9a-f5d054bcaf93 +0 -0
  181. {ellf_cli-22.0.3 → ellf_cli-22.0.4}/ellf_cli/version/17.0.121_2026-08-19-10-42-02_patch_31a81b02-a886-4dea-84e0-67cc6ca412d5 +0 -0
  182. {ellf_cli-22.0.3 → ellf_cli-22.0.4}/ellf_cli/version/17.0.122_2026-08-19-11-16-49_patch_1c4d7307-48d4-493e-936d-80af799d644b +0 -0
  183. {ellf_cli-22.0.3 → ellf_cli-22.0.4}/ellf_cli/version/17.0.123_2026-08-19-11-47-32_patch_d3d78d04-9989-45b1-91b1-93b571d8814a +0 -0
  184. {ellf_cli-22.0.3 → ellf_cli-22.0.4}/ellf_cli/version/17.0.124_2026-08-20-08-03-17_patch_57f4c389-2d19-41ce-aca4-13b61c62f387 +0 -0
  185. {ellf_cli-22.0.3 → ellf_cli-22.0.4}/ellf_cli/version/17.0.125_2026-08-20-08-25-05_patch_7f368148-0d1c-4525-bbdf-c6d257ad861a +0 -0
  186. {ellf_cli-22.0.3 → ellf_cli-22.0.4}/ellf_cli/version/17.0.126_2026-08-20-14-51-36_patch_a99abcc8-3371-4c63-a759-bb7ea5b32432 +0 -0
  187. {ellf_cli-22.0.3 → ellf_cli-22.0.4}/ellf_cli/version/17.0.127_2026-08-21-05-48-58_patch_34b4ccee-7596-4f57-93e9-774973cca58e +0 -0
  188. {ellf_cli-22.0.3 → ellf_cli-22.0.4}/ellf_cli/version/17.0.128_2026-08-21-10-47-20_patch_693112b9-927c-4a8f-8553-b06eef86079d +0 -0
  189. {ellf_cli-22.0.3 → ellf_cli-22.0.4}/ellf_cli/version/17.0.129_2026-08-21-11-16-54_patch_7a981666-01d9-4d1b-9ba3-2129744e3bd5 +0 -0
  190. {ellf_cli-22.0.3 → ellf_cli-22.0.4}/ellf_cli/version/17.0.130_2026-08-21-13-09-36_patch_b7ad1a76-ce70-4d70-9d1a-7c414eba5bb4 +0 -0
  191. {ellf_cli-22.0.3 → ellf_cli-22.0.4}/ellf_cli/version/17.0.131_2026-08-21-14-31-10_patch_9128ef66-193e-4caf-8710-afd86128e638 +0 -0
  192. {ellf_cli-22.0.3 → ellf_cli-22.0.4}/ellf_cli/version/17.0.132_2026-08-24-08-59-34_patch_c600fc14-86a5-460f-9c4e-f0177056f756 +0 -0
  193. {ellf_cli-22.0.3 → ellf_cli-22.0.4}/ellf_cli/version/17.0.133_2026-08-24-10-28-29_patch_b6ce3e32-9d7b-48ac-981a-270c22c180dd +0 -0
  194. {ellf_cli-22.0.3 → ellf_cli-22.0.4}/ellf_cli/version/17.0.134_2026-08-24-11-00-51_patch_a4fc0484-4a64-46a2-94f8-aa805254c7b0 +0 -0
  195. {ellf_cli-22.0.3 → ellf_cli-22.0.4}/ellf_cli/version/17.0.135_2026-08-24-12-12-41_patch_a870a56f-9a65-49eb-92da-f572b26667d8 +0 -0
  196. {ellf_cli-22.0.3 → ellf_cli-22.0.4}/ellf_cli/version/17.0.136_2026-08-24-12-42-46_patch_9b399356-6482-4291-8da7-e748a99c4b53 +0 -0
  197. {ellf_cli-22.0.3 → ellf_cli-22.0.4}/ellf_cli/version/17.0.137_2026-08-24-12-53-57_patch_f7979348-1910-40cd-a535-90299eca84ee +0 -0
  198. {ellf_cli-22.0.3 → ellf_cli-22.0.4}/ellf_cli/version/17.0.138_2026-08-24-14-36-30_patch_ed0f8750-f663-498b-85e7-0d3f2213fcfe +0 -0
  199. {ellf_cli-22.0.3 → ellf_cli-22.0.4}/ellf_cli/version/17.0.139_2026-08-25-08-37-26_patch_3ba87e19-902b-4f76-b64c-90e5c0ca1fc6 +0 -0
  200. {ellf_cli-22.0.3 → ellf_cli-22.0.4}/ellf_cli/version/17.0.140_2026-08-25-09-52-45_patch_abc830f2-df15-4385-b797-cdaf72c10752 +0 -0
  201. {ellf_cli-22.0.3 → ellf_cli-22.0.4}/ellf_cli/version/17.0.141_2026-08-25-11-37-20_patch_1d9ec01e-06f6-4c8e-b095-af17d5b62b00 +0 -0
  202. {ellf_cli-22.0.3 → ellf_cli-22.0.4}/ellf_cli/version/17.0.142_2026-08-26-07-38-49_patch_3dd23e36-e7df-494c-986f-a412861a8d40 +0 -0
  203. {ellf_cli-22.0.3 → ellf_cli-22.0.4}/ellf_cli/version/17.0.143_2026-08-26-09-32-32_patch_7b7ebd34-de27-4b73-98a7-3e1501e16cd5 +0 -0
  204. {ellf_cli-22.0.3 → ellf_cli-22.0.4}/ellf_cli/version/17.0.144_2026-08-26-10-56-43_patch_166064f2-2208-4c9b-9e25-25dc4cd32084 +0 -0
  205. {ellf_cli-22.0.3 → ellf_cli-22.0.4}/ellf_cli/version/17.0.145_2026-08-26-11-37-29_patch_b997f3ee-d7d3-4408-9f2f-c8b6d08bdb3b +0 -0
  206. {ellf_cli-22.0.3 → ellf_cli-22.0.4}/ellf_cli/version/17.0.146_2026-08-26-12-15-20_patch_afbbb893-25db-468c-81db-2663ca373068 +0 -0
  207. {ellf_cli-22.0.3 → ellf_cli-22.0.4}/ellf_cli/version/17.0.147_2026-08-26-14-10-50_patch_fd753b1f-2fa1-4ddb-a9e7-de690da85254 +0 -0
  208. {ellf_cli-22.0.3 → ellf_cli-22.0.4}/ellf_cli/version/17.0.148_2026-08-27-09-08-35_patch_23814c0b-7f77-489c-b4a8-ceadd18146e3 +0 -0
  209. {ellf_cli-22.0.3 → ellf_cli-22.0.4}/ellf_cli/version/17.0.149_2026-08-27-09-40-17_patch_8879d72e-a3d8-4abc-a16a-d9dcf75f0fa1 +0 -0
  210. {ellf_cli-22.0.3 → ellf_cli-22.0.4}/ellf_cli/version/17.0.150_2026-08-27-10-57-25_patch_afbe60ed-3e23-4d88-be3c-f800d7002a7d +0 -0
  211. {ellf_cli-22.0.3 → ellf_cli-22.0.4}/ellf_cli/version/17.0.151_2026-08-28-09-02-46_patch_1d4ca23b-aeae-4ea8-96b1-8cf57371f29d +0 -0
  212. {ellf_cli-22.0.3 → ellf_cli-22.0.4}/ellf_cli/version/17.0.152_2026-08-28-15-37-24_patch_c9d0bdc1-a7e1-432b-92b9-92dc76fe0c78 +0 -0
  213. {ellf_cli-22.0.3 → ellf_cli-22.0.4}/ellf_cli/version/17.0.153_2026-08-29-09-03-27_patch_7531573a-1b33-4092-aa9c-0e65f47b201f +0 -0
  214. {ellf_cli-22.0.3 → ellf_cli-22.0.4}/ellf_cli/version/17.0.154_2026-08-29-11-56-35_patch_919f5787-39c8-4dc1-ae6a-f27c28380f21 +0 -0
  215. {ellf_cli-22.0.3 → ellf_cli-22.0.4}/ellf_cli/version/17.0.155_2026-08-29-15-15-59_patch_822f9ed8-c366-4e5f-9791-dee8467cf3ce +0 -0
  216. {ellf_cli-22.0.3 → ellf_cli-22.0.4}/ellf_cli/version/17.0.156_2026-08-29-16-19-01_patch_3ac2bf36-20f7-4c9f-9fdb-295d4466d59e +0 -0
  217. {ellf_cli-22.0.3 → ellf_cli-22.0.4}/ellf_cli/version/17.0.157_2026-08-30-17-32-17_patch_4302354c-c9c5-4be7-acd3-542872a748d8 +0 -0
  218. {ellf_cli-22.0.3 → ellf_cli-22.0.4}/ellf_cli/version/17.0.158_2026-08-31-09-28-33_patch_3523774f-c25d-4102-8c1a-92fff046a48d +0 -0
  219. {ellf_cli-22.0.3 → ellf_cli-22.0.4}/ellf_cli/version/17.0.159_2026-08-31-10-31-01_patch_fe3e6290-2553-4154-a0a0-8b8a418280ed +0 -0
  220. {ellf_cli-22.0.3 → ellf_cli-22.0.4}/ellf_cli/version/17.0.160_2026-08-31-11-23-39_patch_e701a085-3d23-4212-b0af-73b1d419a4c1 +0 -0
  221. {ellf_cli-22.0.3 → ellf_cli-22.0.4}/ellf_cli/version/17.0.161_2026-08-31-12-41-01_patch_074d7bde-c493-44db-bc15-e81fd91e2921 +0 -0
  222. {ellf_cli-22.0.3 → ellf_cli-22.0.4}/ellf_cli/version/17.0.162_2026-08-31-13-58-06_patch_84db1d60-4280-433b-bd3a-22e5611cd315 +0 -0
  223. {ellf_cli-22.0.3 → ellf_cli-22.0.4}/ellf_cli/version/17.0.163_2026-08-31-15-25-54_patch_1931c5ba-73df-48cb-8dcb-555ace0af267 +0 -0
  224. {ellf_cli-22.0.3 → ellf_cli-22.0.4}/ellf_cli/version/17.0.163_2026-08-31-15-44-37_patch_3962e805-2a92-494a-a87b-b6e6d66c40a0 +0 -0
  225. {ellf_cli-22.0.3 → ellf_cli-22.0.4}/ellf_cli/version/17.0.165_2026-08-31-19-48-59_patch_86e5b923-2b99-4b86-b542-469b197614be +0 -0
  226. {ellf_cli-22.0.3 → ellf_cli-22.0.4}/ellf_cli/version/17.0.166_2026-09-01-07-39-57_patch_c344892f-d542-4e7b-8f27-02365a411e15 +0 -0
  227. {ellf_cli-22.0.3 → ellf_cli-22.0.4}/ellf_cli/version/17.0.167_2026-09-01-08-21-53_patch_e199d419-282a-4dbb-8756-cf98b7037e2b +0 -0
  228. {ellf_cli-22.0.3 → ellf_cli-22.0.4}/ellf_cli/version/17.0.168_2026-09-01-09-12-31_patch_235c9fe0-da15-431d-bb56-01e2b9b15f4b +0 -0
  229. {ellf_cli-22.0.3 → ellf_cli-22.0.4}/ellf_cli/version/17.0.169_2026-09-01-11-54-19_patch_98b617ab-d130-42b5-93f2-19cae56d4c4c +0 -0
  230. {ellf_cli-22.0.3 → ellf_cli-22.0.4}/ellf_cli/version/17.0.170_2026-09-02-09-27-28_patch_e448d2e6-b7ec-4235-96bf-0173b3623c87 +0 -0
  231. {ellf_cli-22.0.3 → ellf_cli-22.0.4}/ellf_cli/version/17.0.171_2026-09-02-12-52-44_patch_e6a241c2-1d62-482c-a9eb-9e3373395c50 +0 -0
  232. {ellf_cli-22.0.3 → ellf_cli-22.0.4}/ellf_cli/version/17.0.172_2026-09-04-08-47-04_patch_daae079f-c104-4fcd-bb57-7bff68e80ad9 +0 -0
  233. {ellf_cli-22.0.3 → ellf_cli-22.0.4}/ellf_cli/version/17.0.173_2026-09-04-12-34-42_patch_399c4cbd-0c28-4dfd-a4ec-e569420a36e4 +0 -0
  234. {ellf_cli-22.0.3 → ellf_cli-22.0.4}/ellf_cli/version/17.0.174_2026-09-04-13-08-48_patch_f5cfe3d5-808e-45e4-bafc-00d0554838ee +0 -0
  235. {ellf_cli-22.0.3 → ellf_cli-22.0.4}/ellf_cli/version/17.0.175_2026-09-04-17-20-46_patch_1ef542fa-5d8e-4d90-aa15-9fc4cea3dfec +0 -0
  236. {ellf_cli-22.0.3 → ellf_cli-22.0.4}/ellf_cli/version/17.0.176_2026-09-05-08-25-05_patch_ced5521f-f9da-4640-a921-95fcb5cd2e6d +0 -0
  237. {ellf_cli-22.0.3 → ellf_cli-22.0.4}/ellf_cli/version/17.0.177_2026-09-05-09-00-16_patch_803c1ad4-a9af-4953-92c5-d882dec03ea2 +0 -0
  238. {ellf_cli-22.0.3 → ellf_cli-22.0.4}/ellf_cli/version/17.0.178_2026-09-05-09-30-34_patch_012f6259-2e3f-4591-9806-1920fd06cb1f +0 -0
  239. {ellf_cli-22.0.3 → ellf_cli-22.0.4}/ellf_cli/version/17.0.179_2026-09-05-09-33-43_patch_f4bdf28c-d584-47a5-b699-8c883330162c +0 -0
  240. {ellf_cli-22.0.3 → ellf_cli-22.0.4}/ellf_cli/version/17.0.180_2026-09-05-12-02-21_patch_34a14b01-d22d-44a8-ac63-be0370a61422 +0 -0
  241. {ellf_cli-22.0.3 → ellf_cli-22.0.4}/ellf_cli/version/17.0.181_2026-09-05-12-47-46_patch_56b18867-99f6-4d5c-8215-7b9d60d34010 +0 -0
  242. {ellf_cli-22.0.3 → ellf_cli-22.0.4}/ellf_cli/version/17.0.182_2026-09-05-13-35-18_patch_61b7d4d1-73c8-4603-9b0c-593e7405587f +0 -0
  243. {ellf_cli-22.0.3 → ellf_cli-22.0.4}/ellf_cli/version/17.0.183_2026-09-06-07-27-09_patch_d76331b2-e3a3-4c0b-8412-ecd131b6ac02 +0 -0
  244. {ellf_cli-22.0.3 → ellf_cli-22.0.4}/ellf_cli/version/17.0.184_2026-09-06-10-21-47_patch_7680b7a1-49f0-4eef-b12e-4c854407448c +0 -0
  245. {ellf_cli-22.0.3 → ellf_cli-22.0.4}/ellf_cli/version/17.0.185_2026-09-06-10-30-40_patch_e56c2183-77f8-44ef-a92e-7377ea6ce8c1 +0 -0
  246. {ellf_cli-22.0.3 → ellf_cli-22.0.4}/ellf_cli/version/17.0.186_2026-09-07-10-00-51_patch_67de5683-d208-4bc0-8fb6-7677377908e7 +0 -0
  247. {ellf_cli-22.0.3 → ellf_cli-22.0.4}/ellf_cli/version/17.0.187_2026-09-07-10-18-13_patch_2086cee2-276b-48f7-80ce-6a17397b9541 +0 -0
  248. {ellf_cli-22.0.3 → ellf_cli-22.0.4}/ellf_cli/version/17.0.188_2026-09-07-12-51-28_patch_d6590822-068f-4a19-814d-34bbf204ba6e +0 -0
  249. {ellf_cli-22.0.3 → ellf_cli-22.0.4}/ellf_cli/version/17.0.189_2026-09-07-12-55-23_patch_7e0fb04b-4d40-4490-974f-c74ba2ae55a1 +0 -0
  250. {ellf_cli-22.0.3 → ellf_cli-22.0.4}/ellf_cli/version/17.0.190_2026-09-08-08-58-03_patch_15e3ac1e-4475-4272-bdd8-d00bb0db8f17 +0 -0
  251. {ellf_cli-22.0.3 → ellf_cli-22.0.4}/ellf_cli/version/17.0.191_2026-09-08-09-55-58_patch_3d350e50-ee53-4282-bc75-d2251142c46e +0 -0
  252. {ellf_cli-22.0.3 → ellf_cli-22.0.4}/ellf_cli/version/17.0.192_2026-09-08-11-12-21_patch_0733223b-1ae2-4b5b-897f-dfc57574ff83 +0 -0
  253. {ellf_cli-22.0.3 → ellf_cli-22.0.4}/ellf_cli/version/17.0.193_2026-09-08-21-48-17_patch_8d07d4e6-3555-4720-85c9-9452570fae2d +0 -0
  254. {ellf_cli-22.0.3 → ellf_cli-22.0.4}/ellf_cli/version/17.0.194_2026-09-09-07-53-43_patch_69559429-aa42-4b62-9bd7-cdfded1c00d0 +0 -0
  255. {ellf_cli-22.0.3 → ellf_cli-22.0.4}/ellf_cli/version/17.0.195_2026-09-09-09-55-40_patch_bbf67f1d-3b9a-43a2-b1bc-22df3fed8a34 +0 -0
  256. {ellf_cli-22.0.3 → ellf_cli-22.0.4}/ellf_cli/version/17.0.196_2026-09-09-09-59-47_patch_70d76812-7b3c-4192-ad49-fcaccc29b0da +0 -0
  257. {ellf_cli-22.0.3 → ellf_cli-22.0.4}/ellf_cli/version/17.0.197_2026-09-09-13-06-06_patch_d455be0b-5421-492a-b952-fab0fd8110f8 +0 -0
  258. {ellf_cli-22.0.3 → ellf_cli-22.0.4}/ellf_cli/version/17.0.198_2026-09-09-13-14-34_patch_267f54e5-311e-435e-882f-8e19191325ea +0 -0
  259. {ellf_cli-22.0.3 → ellf_cli-22.0.4}/ellf_cli/version/17.0.199_2026-09-09-22-24-18_patch_81e3e453-ca0e-42fa-9c21-8f94d43c05f2 +0 -0
  260. {ellf_cli-22.0.3 → ellf_cli-22.0.4}/ellf_cli/version/17.0.200_2026-09-10-07-19-50_patch_911714fa-1fa3-4165-bfe6-d10c650bf5bf +0 -0
  261. {ellf_cli-22.0.3 → ellf_cli-22.0.4}/ellf_cli/version/17.0.201_2026-09-10-10-14-19_patch_864015f2-b0b0-4563-a1c8-155629cb9126 +0 -0
  262. {ellf_cli-22.0.3 → ellf_cli-22.0.4}/ellf_cli/version/17.0.202_2026-09-10-11-00-05_patch_18270ecf-e770-4e69-8bcf-e5143c39929b +0 -0
  263. {ellf_cli-22.0.3 → ellf_cli-22.0.4}/ellf_cli/version/17.0.203_2026-09-10-11-10-41_patch_ff273ab1-44f3-4bc0-b184-3396e943df04 +0 -0
  264. {ellf_cli-22.0.3 → ellf_cli-22.0.4}/ellf_cli/version/17.0.204_2026-09-10-13-02-02_patch_d4ee41e2-d13c-4941-8605-3f1a42b56b22 +0 -0
  265. {ellf_cli-22.0.3 → ellf_cli-22.0.4}/ellf_cli/version/17.0.205_2026-09-10-13-57-50_patch_284df67e-5957-4b0c-bc64-5581db6ddecc +0 -0
  266. {ellf_cli-22.0.3 → ellf_cli-22.0.4}/ellf_cli/version/17.0.206_2026-09-11-10-18-50_patch_1ac7e410-0e62-4eef-bbe4-ebf93c9d0869 +0 -0
  267. {ellf_cli-22.0.3 → ellf_cli-22.0.4}/ellf_cli/version/17.0.207_2026-09-11-15-26-02_major_3f1b3ed7-eaac-41f5-b740-0af3331595d9 +0 -0
  268. {ellf_cli-22.0.3 → ellf_cli-22.0.4}/ellf_cli/version/18.0.0_2026-09-12-10-01-10_patch_8995783a-a9fa-4c4e-90b3-6386a4c481eb +0 -0
  269. {ellf_cli-22.0.3 → ellf_cli-22.0.4}/ellf_cli/version/18.0.10_2026-09-14-16-09-38_patch_9403d562-eed0-4125-a8b3-09cd92a13298 +0 -0
  270. {ellf_cli-22.0.3 → ellf_cli-22.0.4}/ellf_cli/version/18.0.11_2026-09-15-09-05-57_patch_8b22dba2-d931-4c4d-ae60-db3238d5e73d +0 -0
  271. {ellf_cli-22.0.3 → ellf_cli-22.0.4}/ellf_cli/version/18.0.12_2026-09-15-11-18-52_patch_81314afa-e70d-4ad8-b45b-3f8d62b983e7 +0 -0
  272. {ellf_cli-22.0.3 → ellf_cli-22.0.4}/ellf_cli/version/18.0.13_2026-09-16-08-00-55_patch_bfa30fc0-3281-4de6-bb56-d7290c98d1c3 +0 -0
  273. {ellf_cli-22.0.3 → ellf_cli-22.0.4}/ellf_cli/version/18.0.14_2026-09-16-14-38-26_patch_aae6e9b0-88f4-4bcf-b611-3308c7fcd6eb +0 -0
  274. {ellf_cli-22.0.3 → ellf_cli-22.0.4}/ellf_cli/version/18.0.15_2026-09-17-07-58-54_patch_43d40582-effd-4925-a8bd-e70fecf1faab +0 -0
  275. {ellf_cli-22.0.3 → ellf_cli-22.0.4}/ellf_cli/version/18.0.16_2026-09-17-08-12-37_patch_b4db2d23-88f1-45c1-82d9-78d38014fcb6 +0 -0
  276. {ellf_cli-22.0.3 → ellf_cli-22.0.4}/ellf_cli/version/18.0.17_2026-09-18-09-38-19_patch_25697fc3-e9a8-4980-962b-e6cb0a4760c5 +0 -0
  277. {ellf_cli-22.0.3 → ellf_cli-22.0.4}/ellf_cli/version/18.0.18_2026-09-18-09-54-33_patch_d37d9c71-c469-4d94-90d7-4898915ee991 +0 -0
  278. {ellf_cli-22.0.3 → ellf_cli-22.0.4}/ellf_cli/version/18.0.19_2026-09-18-10-47-33_patch_47607b3c-d1b6-4864-b9b0-00a2a8d2ac32 +0 -0
  279. {ellf_cli-22.0.3 → ellf_cli-22.0.4}/ellf_cli/version/18.0.1_2026-09-12-17-33-30_patch_f6ea2c99-a715-4c4d-876a-23ed8c62a83d +0 -0
  280. {ellf_cli-22.0.3 → ellf_cli-22.0.4}/ellf_cli/version/18.0.20_2026-09-18-11-07-21_patch_5a336f91-63a6-4877-97ed-da89395cc8f5 +0 -0
  281. {ellf_cli-22.0.3 → ellf_cli-22.0.4}/ellf_cli/version/18.0.21_2026-09-18-13-34-52_patch_c56462e1-63d9-498f-a912-c80c5f0171d1 +0 -0
  282. {ellf_cli-22.0.3 → ellf_cli-22.0.4}/ellf_cli/version/18.0.22_2026-09-20-08-10-45_patch_6afa2c20-bcef-4b7d-8685-2c9815d8683f +0 -0
  283. {ellf_cli-22.0.3 → ellf_cli-22.0.4}/ellf_cli/version/18.0.23_2026-09-20-08-38-35_patch_a7f169e5-37ab-4ec0-bf89-d14770181303 +0 -0
  284. {ellf_cli-22.0.3 → ellf_cli-22.0.4}/ellf_cli/version/18.0.24_2026-09-20-15-49-27_patch_c1222081-ff44-4f20-aea2-5840b6d76efd +0 -0
  285. {ellf_cli-22.0.3 → ellf_cli-22.0.4}/ellf_cli/version/18.0.25_2026-09-21-06-11-40_patch_6df46dd9-e7d3-4e71-bec4-e5c93b4ac863 +0 -0
  286. {ellf_cli-22.0.3 → ellf_cli-22.0.4}/ellf_cli/version/18.0.26_2026-09-21-07-54-11_patch_cc52dd36-5eba-4094-a1b0-d6f79f103441 +0 -0
  287. {ellf_cli-22.0.3 → ellf_cli-22.0.4}/ellf_cli/version/18.0.27_2026-09-21-08-36-49_patch_a25fa125-9bcd-4317-8da5-851c1c329631 +0 -0
  288. {ellf_cli-22.0.3 → ellf_cli-22.0.4}/ellf_cli/version/18.0.28_2026-09-21-09-44-01_patch_4430a721-0b3c-4f6a-b9bc-a53e667161bb +0 -0
  289. {ellf_cli-22.0.3 → ellf_cli-22.0.4}/ellf_cli/version/18.0.29_2026-09-21-10-58-07_patch_83b2b942-60a4-4bef-afd1-99690ba52b69 +0 -0
  290. {ellf_cli-22.0.3 → ellf_cli-22.0.4}/ellf_cli/version/18.0.2_2026-09-13-06-36-33_patch_176bb944-1c49-4dd3-a001-8fce908825a0 +0 -0
  291. {ellf_cli-22.0.3 → ellf_cli-22.0.4}/ellf_cli/version/18.0.30_2026-09-21-12-51-16_patch_519b9a65-d400-4234-b113-36b46e315507 +0 -0
  292. {ellf_cli-22.0.3 → ellf_cli-22.0.4}/ellf_cli/version/18.0.31_2026-09-22-06-53-35_patch_5c45a235-81b6-4d05-801a-07dc7db61400 +0 -0
  293. {ellf_cli-22.0.3 → ellf_cli-22.0.4}/ellf_cli/version/18.0.32_2026-09-22-07-42-04_patch_f12c6415-8166-4063-9e5d-26601b929d63 +0 -0
  294. {ellf_cli-22.0.3 → ellf_cli-22.0.4}/ellf_cli/version/18.0.33_2026-09-22-08-12-32_patch_cec31bb0-a6cb-43e6-8407-25aba322b54c +0 -0
  295. {ellf_cli-22.0.3 → ellf_cli-22.0.4}/ellf_cli/version/18.0.34_2026-09-23-07-09-16_patch_659fccbb-c0a0-4f6f-bc87-e6abaa8815a7 +0 -0
  296. {ellf_cli-22.0.3 → ellf_cli-22.0.4}/ellf_cli/version/18.0.35_2026-09-23-09-38-52_patch_023f904d-807a-4833-8a40-570365e8ec06 +0 -0
  297. {ellf_cli-22.0.3 → ellf_cli-22.0.4}/ellf_cli/version/18.0.36_2026-09-23-10-05-33_patch_846fb491-2b1b-47ed-ac1f-ba40e744e199 +0 -0
  298. {ellf_cli-22.0.3 → ellf_cli-22.0.4}/ellf_cli/version/18.0.37_2026-09-23-13-08-30_patch_8e4b1d24-156e-4ffe-8e4a-b8740ec18588 +0 -0
  299. {ellf_cli-22.0.3 → ellf_cli-22.0.4}/ellf_cli/version/18.0.38_2026-09-24-06-41-37_patch_67adb1d6-3f49-4357-a67f-b1d48f23a972 +0 -0
  300. {ellf_cli-22.0.3 → ellf_cli-22.0.4}/ellf_cli/version/18.0.39_2026-09-24-15-24-32_patch_e5dd375d-5643-4514-9b65-8d877c4a2bb2 +0 -0
  301. {ellf_cli-22.0.3 → ellf_cli-22.0.4}/ellf_cli/version/18.0.3_2026-09-13-12-32-46_patch_58e5ce3a-f2a7-4789-b2cd-5cc75c72b6ec +0 -0
  302. {ellf_cli-22.0.3 → ellf_cli-22.0.4}/ellf_cli/version/18.0.40_2026-09-24-19-58-14_patch_ff18824b-be51-4a3d-a621-e96279e53c3e +0 -0
  303. {ellf_cli-22.0.3 → ellf_cli-22.0.4}/ellf_cli/version/18.0.41_2026-09-26-09-24-52_patch_b9979267-30f9-440a-a4ce-c611991d4886 +0 -0
  304. {ellf_cli-22.0.3 → ellf_cli-22.0.4}/ellf_cli/version/18.0.42_2026-09-28-12-39-34_patch_dda535d9-8922-4eb9-ab51-6ecff0650c10 +0 -0
  305. {ellf_cli-22.0.3 → ellf_cli-22.0.4}/ellf_cli/version/18.0.43_2026-09-28-13-35-27_minor_aca1168a-1286-4326-9d0c-2f2587c9fe92 +0 -0
  306. {ellf_cli-22.0.3 → ellf_cli-22.0.4}/ellf_cli/version/18.0.4_2026-09-13-12-45-14_patch_b6c79d55-8f85-4f47-a304-df52b122c38c +0 -0
  307. {ellf_cli-22.0.3 → ellf_cli-22.0.4}/ellf_cli/version/18.0.5_2026-09-13-14-11-18_patch_9b111116-5e62-472a-b72c-d1bbafa22c97 +0 -0
  308. {ellf_cli-22.0.3 → ellf_cli-22.0.4}/ellf_cli/version/18.0.5_2026-09-13-17-01-11_patch_d4bf61c0-83ac-4dd3-b512-a65805d298c8 +0 -0
  309. {ellf_cli-22.0.3 → ellf_cli-22.0.4}/ellf_cli/version/18.0.6_2026-09-14-11-31-04_patch_fd2ebda6-22cb-4e7d-9452-2f6f2640e828 +0 -0
  310. {ellf_cli-22.0.3 → ellf_cli-22.0.4}/ellf_cli/version/18.0.7_2026-09-13-17-06-08_patch_428af161-0b27-43c2-8751-e5f7874df120 +0 -0
  311. {ellf_cli-22.0.3 → ellf_cli-22.0.4}/ellf_cli/version/18.0.9_2026-09-14-12-37-42_patch_b2ca0529-4b67-4fa4-8fdc-5f5e9f5d5166 +0 -0
  312. {ellf_cli-22.0.3 → ellf_cli-22.0.4}/ellf_cli/version/18.1.0_2026-09-28-14-27-54_patch_324444f7-d24e-4189-b21f-1188894a3109 +0 -0
  313. {ellf_cli-22.0.3 → ellf_cli-22.0.4}/ellf_cli/version/18.1.1_2026-09-28-21-03-07_major_e266a14a-7d96-4ece-92a3-8f34e64438a5 +0 -0
  314. {ellf_cli-22.0.3 → ellf_cli-22.0.4}/ellf_cli/version/19.0.0_2026-09-29-11-05-31_patch_77d6f9f8-ead3-4d79-a4b2-b084b2dffc98 +0 -0
  315. {ellf_cli-22.0.3 → ellf_cli-22.0.4}/ellf_cli/version/19.0.10_2026-10-02-12-56-52_patch_14e97576-73ca-44b8-82c9-7dad6f1789ab +0 -0
  316. {ellf_cli-22.0.3 → ellf_cli-22.0.4}/ellf_cli/version/19.0.11_2026-10-04-10-31-19_patch_088eb371-1799-4949-a119-286cf1cd55d1 +0 -0
  317. {ellf_cli-22.0.3 → ellf_cli-22.0.4}/ellf_cli/version/19.0.12_2026-10-06-18-00-25_patch_36554bed-49c5-41cb-9fd4-48cc6af74c09 +0 -0
  318. {ellf_cli-22.0.3 → ellf_cli-22.0.4}/ellf_cli/version/19.0.13_2026-10-07-12-24-08_patch_a540a364-3a0d-4960-b278-06ba2c7cc258 +0 -0
  319. {ellf_cli-22.0.3 → ellf_cli-22.0.4}/ellf_cli/version/19.0.14_2026-10-07-12-52-39_patch_e3a6fc5c-2ee4-414c-a2dd-4910a655a7dd +0 -0
  320. {ellf_cli-22.0.3 → ellf_cli-22.0.4}/ellf_cli/version/19.0.15_2026-10-09-04-15-56_patch_bd7a9b06-867b-4fea-9678-973a05e3022d +0 -0
  321. {ellf_cli-22.0.3 → ellf_cli-22.0.4}/ellf_cli/version/19.0.16_2026-10-09-16-11-56_patch_a1135435-7dea-4f00-8c98-203ba3974382 +0 -0
  322. {ellf_cli-22.0.3 → ellf_cli-22.0.4}/ellf_cli/version/19.0.17_2026-10-09-17-44-55_patch_faeacad8-541a-40f2-ab9c-abcd66fcd00e +0 -0
  323. {ellf_cli-22.0.3 → ellf_cli-22.0.4}/ellf_cli/version/19.0.18_2026-10-10-07-29-38_major_02601a1e-9bdb-45bf-ad67-a07b7dcd0002 +0 -0
  324. {ellf_cli-22.0.3 → ellf_cli-22.0.4}/ellf_cli/version/19.0.1_2026-09-29-11-59-38_patch_bbf4753c-d641-4c2f-ac7c-02be6bd8e7c2 +0 -0
  325. {ellf_cli-22.0.3 → ellf_cli-22.0.4}/ellf_cli/version/19.0.2_2026-09-29-12-44-07_patch_19fdaa94-60dc-464b-93b8-c17607ebc7b6 +0 -0
  326. {ellf_cli-22.0.3 → ellf_cli-22.0.4}/ellf_cli/version/19.0.3_2026-09-29-14-16-00_patch_3cb60272-95cf-4311-8897-721e1b5c2c88 +0 -0
  327. {ellf_cli-22.0.3 → ellf_cli-22.0.4}/ellf_cli/version/19.0.4_2026-09-29-16-24-53_patch_9fe8ad2b-e3ec-4558-a5fc-9c44c5c266dc +0 -0
  328. {ellf_cli-22.0.3 → ellf_cli-22.0.4}/ellf_cli/version/19.0.5_2026-09-29-17-13-16_patch_e48d2dd2-f41a-402e-a441-756b23c01dcb +0 -0
  329. {ellf_cli-22.0.3 → ellf_cli-22.0.4}/ellf_cli/version/19.0.6_2026-09-29-17-22-11_patch_3b1c79e9-9002-4e96-ba78-10668c33be6a +0 -0
  330. {ellf_cli-22.0.3 → ellf_cli-22.0.4}/ellf_cli/version/19.0.7_2026-09-30-07-50-58_patch_e8b1f046-d819-499c-9cd5-3e1a20fc9acf +0 -0
  331. {ellf_cli-22.0.3 → ellf_cli-22.0.4}/ellf_cli/version/19.0.8_2026-10-01-13-41-44_patch_7d63af18-e0af-4127-ab58-aede2407490f +0 -0
  332. {ellf_cli-22.0.3 → ellf_cli-22.0.4}/ellf_cli/version/19.0.9_2026-10-01-15-57-35_patch_143863cb-8f63-4ef7-bd78-c1114fc93a2d +0 -0
  333. {ellf_cli-22.0.3 → ellf_cli-22.0.4}/ellf_cli/version/20.0.0_2026-10-10-09-36-17_patch_d2618f39-cdc9-4d3a-ba40-1a0ad6265f3b +0 -0
  334. {ellf_cli-22.0.3 → ellf_cli-22.0.4}/ellf_cli/version/20.0.1_2026-10-11-10-24-33_patch_e29a8713-eecb-4eb1-9641-789d3cfab21a +0 -0
  335. {ellf_cli-22.0.3 → ellf_cli-22.0.4}/ellf_cli/version/20.0.2_2026-10-12-07-22-24_patch_f4053ad1-8fcd-4591-a68d-bf1e23485ba7 +0 -0
  336. {ellf_cli-22.0.3 → ellf_cli-22.0.4}/ellf_cli/version/20.0.3_2026-10-14-14-29-16_patch_070af1c4-f9d9-4f97-8112-058de90d3ea8 +0 -0
  337. {ellf_cli-22.0.3 → ellf_cli-22.0.4}/ellf_cli/version/20.0.4_2026-10-14-16-20-24_patch_c6672844-8227-4e0a-9fb5-36af7e7141dc +0 -0
  338. {ellf_cli-22.0.3 → ellf_cli-22.0.4}/ellf_cli/version/20.0.5_2026-10-15-08-53-28_patch_10154146-8afd-4632-b250-9b4b9f5bf67c +0 -0
  339. {ellf_cli-22.0.3 → ellf_cli-22.0.4}/ellf_cli/version/20.0.6_2026-10-15-14-17-40_patch_df3a5d3c-f0e0-46cb-aeaf-15b51c8a0f13 +0 -0
  340. {ellf_cli-22.0.3 → ellf_cli-22.0.4}/ellf_cli/version/20.0.7_2026-10-16-07-09-40_major_3e5dfba3-2810-47b4-85a9-938bfff72a04 +0 -0
  341. {ellf_cli-22.0.3 → ellf_cli-22.0.4}/ellf_cli/version/21.0.0_2026-10-16-10-44-29_patch_1058e453-613f-492b-a3c7-f3ca2abe8c2d +0 -0
  342. {ellf_cli-22.0.3 → ellf_cli-22.0.4}/ellf_cli/version/21.0.10_2026-10-30-14-29-34_major_4fd535f6-6e41-4f34-b29c-543e1a9bac51 +0 -0
  343. {ellf_cli-22.0.3 → ellf_cli-22.0.4}/ellf_cli/version/21.0.1_2026-10-16-16-06-10_patch_e20b4ad2-b87f-478e-b4f4-323c8bcc5ea9 +0 -0
  344. {ellf_cli-22.0.3 → ellf_cli-22.0.4}/ellf_cli/version/21.0.2_2026-10-19-13-54-31_patch_bf091df4-00bb-43c8-9117-51047944e305 +0 -0
  345. {ellf_cli-22.0.3 → ellf_cli-22.0.4}/ellf_cli/version/21.0.3_2026-10-19-14-58-29_patch_9b985381-1b1d-4531-b242-b02cea2484c5 +0 -0
  346. {ellf_cli-22.0.3 → ellf_cli-22.0.4}/ellf_cli/version/21.0.4_2026-10-20-17-08-51_patch_38d02583-cb5c-4adc-b5d7-614b483acf19 +0 -0
  347. {ellf_cli-22.0.3 → ellf_cli-22.0.4}/ellf_cli/version/21.0.5_2026-10-21-09-28-47_patch_fabd5cfe-2d04-4215-abf1-58fba226acb7 +0 -0
  348. {ellf_cli-22.0.3 → ellf_cli-22.0.4}/ellf_cli/version/21.0.6_2026-10-22-16-07-37_patch_d2bb27d7-6304-4c99-abbc-46d3dd7e363b +0 -0
  349. {ellf_cli-22.0.3 → ellf_cli-22.0.4}/ellf_cli/version/21.0.7_2026-10-27-11-41-55_patch_6b50567f-209d-42c9-a2cd-d251a2bc8fbf +0 -0
  350. {ellf_cli-22.0.3 → ellf_cli-22.0.4}/ellf_cli/version/21.0.8_2026-10-29-13-48-18_patch_bdab3ee6-fba8-4625-9243-c4cbe839ba32 +0 -0
  351. {ellf_cli-22.0.3 → ellf_cli-22.0.4}/ellf_cli/version/21.0.9_2026-10-29-17-51-00_patch_803a4c70-2e2b-4024-8598-c7d91c9db0d2 +0 -0
  352. {ellf_cli-22.0.3 → ellf_cli-22.0.4}/ellf_cli/version/22.0.0_2026-10-30-15-25-42_patch_1f39caf4-aadc-4d26-9124-a854d945b32a +0 -0
  353. {ellf_cli-22.0.3 → ellf_cli-22.0.4}/ellf_cli/version/22.0.1_2026-10-30-17-30-37_patch_4a6e6cfb-d86e-4f1e-84c5-02d4c071f75b +0 -0
  354. {ellf_cli-22.0.3 → ellf_cli-22.0.4}/ellf_cli/version/22.0.2_2026-10-31-11-13-43_patch_54d644d8-cebd-4b6a-ba8f-685e2ffee481 +0 -0
  355. {ellf_cli-22.0.3 → ellf_cli-22.0.4}/ellf_cli/version/__init__.py +0 -0
  356. {ellf_cli-22.0.3 → ellf_cli-22.0.4}/ellf_cli/version/read_version.py +0 -0
  357. {ellf_cli-22.0.3 → ellf_cli-22.0.4}/ellf_cli.egg-info/dependency_links.txt +0 -0
  358. {ellf_cli-22.0.3 → ellf_cli-22.0.4}/ellf_cli.egg-info/entry_points.txt +0 -0
  359. {ellf_cli-22.0.3 → ellf_cli-22.0.4}/ellf_cli.egg-info/not-zip-safe +0 -0
  360. {ellf_cli-22.0.3 → ellf_cli-22.0.4}/ellf_cli.egg-info/requires.txt +0 -0
  361. {ellf_cli-22.0.3 → ellf_cli-22.0.4}/ellf_cli.egg-info/top_level.txt +0 -0
  362. {ellf_cli-22.0.3 → ellf_cli-22.0.4}/pyproject.toml +0 -0
  363. {ellf_cli-22.0.3 → ellf_cli-22.0.4}/setup.cfg +0 -0
  364. {ellf_cli-22.0.3 → ellf_cli-22.0.4}/setup.py +0 -0
  365. {ellf_cli-22.0.3 → ellf_cli-22.0.4}/tests/test_agents.py +0 -0
  366. {ellf_cli-22.0.3 → ellf_cli-22.0.4}/tests/test_agents_info_stats.py +0 -0
  367. {ellf_cli-22.0.3 → ellf_cli-22.0.4}/tests/test_appdirs.py +0 -0
  368. {ellf_cli-22.0.3 → ellf_cli-22.0.4}/tests/test_auth.py +0 -0
  369. {ellf_cli-22.0.3 → ellf_cli-22.0.4}/tests/test_clusters_info_stats.py +0 -0
  370. {ellf_cli-22.0.3 → ellf_cli-22.0.4}/tests/test_config.py +0 -0
  371. {ellf_cli-22.0.3 → ellf_cli-22.0.4}/tests/test_datasets_export.py +0 -0
  372. {ellf_cli-22.0.3 → ellf_cli-22.0.4}/tests/test_errors.py +0 -0
  373. {ellf_cli-22.0.3 → ellf_cli-22.0.4}/tests/test_files_cp.py +0 -0
  374. {ellf_cli-22.0.3 → ellf_cli-22.0.4}/tests/test_files_cp_helpers.py +0 -0
  375. {ellf_cli-22.0.3 → ellf_cli-22.0.4}/tests/test_helm.py +0 -0
  376. {ellf_cli-22.0.3 → ellf_cli-22.0.4}/tests/test_import_export.py +0 -0
  377. {ellf_cli-22.0.3 → ellf_cli-22.0.4}/tests/test_info.py +0 -0
  378. {ellf_cli-22.0.3 → ellf_cli-22.0.4}/tests/test_infra_config_io.py +0 -0
  379. {ellf_cli-22.0.3 → ellf_cli-22.0.4}/tests/test_infra_configure.py +0 -0
  380. {ellf_cli-22.0.3 → ellf_cli-22.0.4}/tests/test_infra_deploy_node_labels.py +0 -0
  381. {ellf_cli-22.0.3 → ellf_cli-22.0.4}/tests/test_infra_deploy_preflight.py +0 -0
  382. {ellf_cli-22.0.3 → ellf_cli-22.0.4}/tests/test_infra_deploy_registry_key.py +0 -0
  383. {ellf_cli-22.0.3 → ellf_cli-22.0.4}/tests/test_infra_destroy.py +0 -0
  384. {ellf_cli-22.0.3 → ellf_cli-22.0.4}/tests/test_infra_get_terraform.py +0 -0
  385. {ellf_cli-22.0.3 → ellf_cli-22.0.4}/tests/test_infra_hibernate.py +0 -0
  386. {ellf_cli-22.0.3 → ellf_cli-22.0.4}/tests/test_infra_pause.py +0 -0
  387. {ellf_cli-22.0.3 → ellf_cli-22.0.4}/tests/test_infra_provision.py +0 -0
  388. {ellf_cli-22.0.3 → ellf_cli-22.0.4}/tests/test_infra_register.py +0 -0
  389. {ellf_cli-22.0.3 → ellf_cli-22.0.4}/tests/test_infra_start_fixes.py +0 -0
  390. {ellf_cli-22.0.3 → ellf_cli-22.0.4}/tests/test_infra_start_validation.py +0 -0
  391. {ellf_cli-22.0.3 → ellf_cli-22.0.4}/tests/test_infra_state_bucket.py +0 -0
  392. {ellf_cli-22.0.3 → ellf_cli-22.0.4}/tests/test_infra_terraform_init.py +0 -0
  393. {ellf_cli-22.0.3 → ellf_cli-22.0.4}/tests/test_invalid_secrets.py +0 -0
  394. {ellf_cli-22.0.3 → ellf_cli-22.0.4}/tests/test_job_list_project_filter.py +0 -0
  395. {ellf_cli-22.0.3 → ellf_cli-22.0.4}/tests/test_jobs_info_hides_creator.py +0 -0
  396. {ellf_cli-22.0.3 → ellf_cli-22.0.4}/tests/test_jobs_list_unmatched.py +0 -0
  397. {ellf_cli-22.0.3 → ellf_cli-22.0.4}/tests/test_key_pair.py +0 -0
  398. {ellf_cli-22.0.3 → ellf_cli-22.0.4}/tests/test_login.py +0 -0
  399. {ellf_cli-22.0.3 → ellf_cli-22.0.4}/tests/test_logout.py +0 -0
  400. {ellf_cli-22.0.3 → ellf_cli-22.0.4}/tests/test_main.py +0 -0
  401. {ellf_cli-22.0.3 → ellf_cli-22.0.4}/tests/test_mcp_proxy.py +0 -0
  402. {ellf_cli-22.0.3 → ellf_cli-22.0.4}/tests/test_org_select.py +0 -0
  403. {ellf_cli-22.0.3 → ellf_cli-22.0.4}/tests/test_plans.py +0 -0
  404. {ellf_cli-22.0.3 → ellf_cli-22.0.4}/tests/test_projects.py +0 -0
  405. {ellf_cli-22.0.3 → ellf_cli-22.0.4}/tests/test_projects_delete_confirm.py +0 -0
  406. {ellf_cli-22.0.3 → ellf_cli-22.0.4}/tests/test_publish_build_poll.py +0 -0
  407. {ellf_cli-22.0.3 → ellf_cli-22.0.4}/tests/test_publish_data.py +0 -0
  408. {ellf_cli-22.0.3 → ellf_cli-22.0.4}/tests/test_publish_import_root.py +0 -0
  409. {ellf_cli-22.0.3 → ellf_cli-22.0.4}/tests/test_publish_requirements.py +0 -0
  410. {ellf_cli-22.0.3 → ellf_cli-22.0.4}/tests/test_publish_versions.py +0 -0
  411. {ellf_cli-22.0.3 → ellf_cli-22.0.4}/tests/test_pypi_auth.py +0 -0
  412. {ellf_cli-22.0.3 → ellf_cli-22.0.4}/tests/test_query.py +0 -0
  413. {ellf_cli-22.0.3 → ellf_cli-22.0.4}/tests/test_recipe_file.py +0 -0
  414. {ellf_cli-22.0.3 → ellf_cli-22.0.4}/tests/test_recipe_help.py +0 -0
  415. {ellf_cli-22.0.3 → ellf_cli-22.0.4}/tests/test_recipe_required_args.py +0 -0
  416. {ellf_cli-22.0.3 → ellf_cli-22.0.4}/tests/test_recipes.py +0 -0
  417. {ellf_cli-22.0.3 → ellf_cli-22.0.4}/tests/test_recipes_init.py +0 -0
  418. {ellf_cli-22.0.3 → ellf_cli-22.0.4}/tests/test_sessions.py +0 -0
  419. {ellf_cli-22.0.3 → ellf_cli-22.0.4}/tests/test_state.py +0 -0
  420. {ellf_cli-22.0.3 → ellf_cli-22.0.4}/tests/test_support.py +0 -0
  421. {ellf_cli-22.0.3 → ellf_cli-22.0.4}/tests/test_todos.py +0 -0
  422. {ellf_cli-22.0.3 → ellf_cli-22.0.4}/tests/test_ty.py +0 -0
  423. {ellf_cli-22.0.3 → ellf_cli-22.0.4}/tests/test_ui.py +0 -0
  424. {ellf_cli-22.0.3 → ellf_cli-22.0.4}/tests/test_ui_extras.py +0 -0
  425. {ellf_cli-22.0.3 → ellf_cli-22.0.4}/tests/test_usage_counts.py +0 -0
  426. {ellf_cli-22.0.3 → ellf_cli-22.0.4}/tests/test_users_assign.py +0 -0
  427. {ellf_cli-22.0.3 → ellf_cli-22.0.4}/tests/test_users_info_stats.py +0 -0
  428. {ellf_cli-22.0.3 → ellf_cli-22.0.4}/tests/test_users_list.py +0 -0
  429. {ellf_cli-22.0.3 → ellf_cli-22.0.4}/tests/test_util.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: ellf-cli
3
- Version: 22.0.3
3
+ Version: 22.0.4
4
4
  Summary: Ellf Command Line Interface
5
5
  Home-page: https://prodi.gy
6
6
  Author: ExplosionAI GmbH
@@ -358,7 +358,7 @@ Get detailed info for a cluster
358
358
  | Argument | Type | Description | Default |
359
359
  | --- | --- | --- | --- |
360
360
  | `name_or_id` | `Union[str, UUID]` | Name or ID of the cluster | |
361
- | `--select` | `list[str]` | Comma-separated fields to select and show in output. Available: ['id', 'created', 'updated', 'org_id', 'name', 'address', 'state', 'cloud_provider', 'cloud', 'managed_subdomain', 'gcp_project', 'is_hosted'] | `None` |
361
+ | `--select` | `list[str]` | Comma-separated fields to select and show in output. Available: ['id', 'created', 'updated', 'org_id', 'name', 'address', 'state', 'cloud_provider', 'cloud', 'managed_subdomain', 'gcp_project', 'is_hosted', 'ca_certificate'] | `None` |
362
362
  | `--json` | `bool` | Output the result as JSON | `False` |
363
363
 
364
364
  #### `ellf clusters update`
@@ -659,14 +659,14 @@ Deploy and manage cluster infrastructure (Kubernetes, TLS, Terraform)
659
659
 
660
660
  #### `ellf infra register`
661
661
 
662
- Register a new cluster with PAM and write credentials to cluster-creds.json. Pass --domain to use your own DNS, or omit it to be allocated a managed subdomain (with base + wildcard A records) under an Explosion-controlled zone. An org on the free Solo plan has no managed subdomain: omitting --domain registers it at http://localhost.
662
+ Register a new cluster with PAM and write credentials to cluster-creds.json. Pass --domain to use your own DNS, or omit it to be allocated a managed subdomain (with an A record) under an Explosion-controlled zone. An org on the free Solo plan has no managed subdomain: omitting --domain registers it at http://localhost.
663
663
 
664
664
  | Argument | Type | Description | Default |
665
665
  | --- | --- | --- | --- |
666
666
  | `--name` | `str` | Human-readable name for the cluster | |
667
- | `--domain` | `str` | Public address the cluster is reached on, host or host:port (e.g. 'cluster.example.com' for a hosted cluster on port 443, or 'http://localhost' for a cluster on this machine, which needs no certificate and is what the free Solo plan uses by default; Safari can't reach it there, so for Safari use 'https://localhost[:port]', see 'ellf infra tls'). Stored verbatim as the cluster address; the chart derives the bare hostname for K8s ingress / cert SAN where a port isn't allowed. Omit to get a managed subdomain under an Explosion-controlled zone. | `None` |
667
+ | `--domain` | `str` | Public address the cluster is reached on, host or host:port (e.g. 'cluster.example.com' for a hosted cluster on port 443, or 'http://localhost' for a cluster on this machine, which needs no certificate and is what the free Solo plan uses by default; Safari can't reach it there, so for Safari use 'https://localhost[:port]', and to reach it from other devices 'https://<its IP address on your network>'; Ellf issues those a certificate, see 'ellf infra tls'). Stored verbatim as the cluster address; the chart derives the bare hostname for K8s ingress / cert SAN where a port isn't allowed. Omit to get a managed subdomain under an Explosion-controlled zone. | `None` |
668
668
  | `--subdomain` | `str` | Requested label for a managed subdomain (e.g. 'acme' for acme.<zone>). Only valid without --domain; defaults to a slug derived from your org/cluster name. | `None` |
669
- | `--public-ip` | `str` | Cluster ingress IP to point the managed subdomain at. Omit to reserve the name now and re-run with --public-ip once the load balancer IP is known. | `None` |
669
+ | `--public-ip` | `str` | Cluster ingress IP to point the managed subdomain at: a public IPv4 address. Omit to reserve the name now and re-run with --public-ip once the load balancer IP is known. A cluster on a private network uses --domain with your own DNS instead. | `None` |
670
670
  | `--cloud-provider` | `str` | Cloud provider: gcp, k3s, aws, azure | |
671
671
  | `--gcp-project` | `str` | GCP project the cluster is billed under. Recorded on the cluster record as the precondition for cloud-cost collection (see docs/drafts/cloud-costs.md). Re-running register updates it. | `None` |
672
672
  | `--hosted` | `bool` | Mark the cluster as Explosion-hosted. Hosted clusters get their metered cloud costs charged back with a margin — Explosion-operators only, not for BYO clusters. | `False` |
@@ -722,14 +722,13 @@ Destroy the cluster infrastructure created by 'infra terraform', including every
722
722
 
723
723
  #### `ellf infra tls`
724
724
 
725
- Serve a cluster on this machine at https://localhost. Only Safari needs this: it blocks Ellf from calling a cluster at http://localhost, which every other browser allows. Register and deploy the cluster at https://localhost (or https://localhost:<port>) and 'infra deploy' issues its certificate from a CA the CLI keeps, which can only sign for localhost. Then trust that CA once with --trust. Run on the machine the cluster runs on, with kubectl pointed at it.
725
+ Trust the certificate Ellf issues a cluster on localhost or a private network. Such a cluster is served over https with a certificate from a CA Ellf keeps for it, which can only certify the cluster's own address. Run this with --trust on each device you open Ellf on, once. It needs only your Ellf login, not kubectl.
726
726
 
727
727
  | Argument | Type | Description | Default |
728
728
  | --- | --- | --- | --- |
729
- | `--self-signed` | `bool` | Issue the cluster a certificate from the localhost-only CA | `False` |
730
- | `--trust` | `bool` | Print the commands to trust the localhost-only CA | `False` |
731
- | `--status` | `bool` | Show the cluster's TLS setup | `False` |
732
- | `--namespace` | `str` | Kubernetes namespace | `'ellf'` |
729
+ | `--trust` | `bool` | Fetch the cluster's certificate authority from Ellf and print the commands to trust it on this device | `False` |
730
+ | `--status` | `bool` | Show the certificate authority Ellf keeps for the cluster | `False` |
731
+ | `--cluster` | `str` | Name or ID of the cluster (default: the current one) | `None` |
733
732
  | `--output` | `str` | Output format for trust commands: human, json, shell | `'human'` |
734
733
 
735
734
  #### `ellf infra init`
@@ -850,9 +849,10 @@ Generate a values.yaml for Helm deployment from credentials and provider default
850
849
  | --- | --- | --- | --- |
851
850
  | `--creds` | `Path` | Path to cluster-creds.json from 'infra register' | |
852
851
  | `--cloud-provider` | `str` | Cloud provider: gcp, k3s, aws, azure | |
853
- | `--domain` | `str` | Public address the cluster is reached on, host or host:port (e.g. 'cluster.example.com'). Use 'http://localhost' for a cluster on this machine, which needs no certificate and works in every browser but Safari; 'https://localhost[:port]' serves it with a certificate from a localhost-only CA the CLI keeps, for Safari (see 'ellf infra tls'). Match what was passed to 'infra register' — it flows into ELLF_BROKER_HOST so the cluster emits matching OIDC / token URLs. | |
852
+ | `--domain` | `str` | Public address the cluster is reached on, host or host:port (e.g. 'cluster.example.com'). Use 'http://localhost' for a cluster on this machine, which needs no certificate and works in every browser but Safari. 'https://localhost[:port]', or https at an IP address on a private network (for other devices to reach), is served with a certificate Ellf issues the cluster, which each device trusts once (see 'ellf infra tls'). Match what was passed to 'infra register' — it flows into ELLF_BROKER_HOST so the cluster emits matching OIDC / token URLs. | |
854
853
  | `--out` | `Path` | Output path for generated values.yaml | `'values.yaml'` |
855
854
  | `--acme-email` | `str` | Email for Let's Encrypt certificates | `None` |
855
+ | `--tls-secret-name` | `str` | Serve the cluster with a certificate you provide: the name of a kubernetes.io/tls Secret in the cluster's namespace, made however you like (your own cert-manager issuer, a company CA, by hand). For a cluster on a private network or at an IP address, which Let's Encrypt can't certify. | `None` |
856
856
 
857
857
  #### `ellf infra provision`
858
858
 
@@ -886,7 +886,6 @@ Start a cluster on infrastructure configured under your own cloud account.
886
886
  | `src` | `Path` | Path to cluster definition files | `'.'` |
887
887
  | `config_path` | `Path` | Path to config yaml | `None` |
888
888
  | `--config-key` | `str` | Top-level key for the cluster config. | `None` |
889
- | `--offline` | `Path` | Path to a credentials JSON file. Deploys without connecting to PAM. | `None` |
890
889
 
891
890
  ### `ellf jobs`
892
891
 
@@ -7,9 +7,9 @@ import truststore
7
7
  # Use the OS native trust store (macOS Keychain, /etc/ssl/certs on Linux,
8
8
  # Windows certificate store) for every httpx/urllib/requests call this
9
9
  # CLI makes — instead of httpx's default certifi bundle — so a CA the
10
- # machine already trusts (a corporate proxy's, a dev stack's, or the
11
- # localhost CA from `ellf infra tls`) is trusted by the browser, curl and
12
- # the CLI alike, with no SSL_CERT_FILE or separate bundle to maintain.
10
+ # machine already trusts (a corporate proxy's, a dev stack's, or a cluster's
11
+ # CA trusted with `ellf infra tls --trust`) is trusted by the browser, curl
12
+ # and the CLI alike, with no SSL_CERT_FILE or separate bundle to maintain.
13
13
  # Must run before any code constructs an ssl.SSLContext, so it sits at
14
14
  # module top.
15
15
  truststore.inject_into_ssl()
@@ -23,7 +23,7 @@ NonEmptyStr = Annotated[str, StringConstraints(min_length=1)]
23
23
 
24
24
  class ClusterCredsFile(BaseModel):
25
25
  """Schema for cluster-creds.json, written by `infra register` and read by
26
- `infra init-values` and `infra start --offline`.
26
+ `infra init-values`.
27
27
 
28
28
  cluster_id/upstream_registry are required — nothing downstream can work
29
29
  without them. The rest are legitimately blank on a fresh cluster PAM
@@ -482,7 +482,7 @@ def point_managed_dns(
482
482
  ip: "str | None",
483
483
  hostname: "str | None" = None,
484
484
  ) -> None:
485
- """Point a cluster's managed subdomain (base + wildcard) at its ingress.
485
+ """Point a cluster's managed subdomain at its ingress.
486
486
 
487
487
  No-op for clusters without a managed subdomain (bring-your-own DNS).
488
488
  Idempotent server-side, so safe to call on every provision/start.
@@ -491,25 +491,20 @@ def point_managed_dns(
491
491
  return
492
492
  if ip:
493
493
  allocated = client.cluster.allocate_domain(cluster.id, ip=ip)
494
- msg.good(
495
- f"Pointed managed domain {allocated.fqdn} (and *.{allocated.fqdn}) at {ip}"
496
- )
494
+ msg.good(f"Pointed managed domain {allocated.fqdn} at {ip}")
497
495
  elif hostname:
498
- # AWS ELBs expose a hostname, never an IP — the records become CNAMEs.
496
+ # AWS ELBs expose a hostname, never an IP — the record becomes a CNAME.
499
497
  allocated = client.cluster.allocate_domain(cluster.id, hostname=hostname)
500
498
  if allocated.hostname:
501
- msg.good(
502
- f"Pointed managed domain {allocated.fqdn} (and "
503
- f"*.{allocated.fqdn}) at {hostname} (CNAME)"
504
- )
499
+ msg.good(f"Pointed managed domain {allocated.fqdn} at {hostname} (CNAME)")
505
500
  else:
506
501
  # A PAM that predates CNAME support silently ignores the
507
502
  # hostname field. Never fail a deploy over this — tell the user
508
503
  # how to point DNS themselves instead.
509
504
  msg.warn(
510
505
  f"This PAM deployment's managed DNS does not support "
511
- f"hostname (CNAME) targets yet. Create CNAME records for "
512
- f"{cluster.address} and *.{cluster.address} pointing at "
506
+ f"hostname (CNAME) targets yet. Create a CNAME record for "
507
+ f"{cluster.address} pointing at "
513
508
  f"{hostname} in your own DNS, or register the cluster with "
514
509
  f"--domain to use bring-your-own DNS."
515
510
  )
@@ -2,6 +2,7 @@ import base64
2
2
  import json
3
3
  import subprocess
4
4
  import tempfile
5
+ import time
5
6
  from pathlib import Path
6
7
  from typing import Optional
7
8
  from uuid import UUID
@@ -28,12 +29,7 @@ from ...url import URL
28
29
  from .._state import get_auth_state
29
30
  from ._config_io import FILL_IN
30
31
  from ._helpers import create_image_pull_secret, ensure_traefik, helm_registry_login
31
- from .tls import (
32
- LocalTlsError,
33
- ensure_cluster_certificate,
34
- manages_local_tls,
35
- report_trust,
36
- )
32
+ from .tls import MANAGED_TLS_SECRET_NAME
37
33
 
38
34
  DEFAULT_NAMESPACE = "ellf"
39
35
  DEFAULT_RELEASE = "ellf"
@@ -196,15 +192,11 @@ def deploy(
196
192
  # 5. Without an ingress controller nothing serves the cluster's domain.
197
193
  # k3s bundles Traefik, but Colima (and k3s --disable traefik) turn it off.
198
194
  _ensure_ingress_controller(overrides)
199
- # 6. A cluster at https://localhost is served a certificate from the CLI's
200
- # localhost-only CA, issued (or renewed) before the chart points at it.
201
- local_tls_cert = None
202
- if manages_local_tls(overrides):
203
- try:
204
- local_tls_cert = ensure_cluster_certificate(namespace)
205
- except LocalTlsError as e:
206
- msg.fail(str(e))
207
- raise SystemExit(1)
195
+ # 6. A certificate neither cert-manager nor Ellf (managedTls) issues is
196
+ # the operator's own, and has to be in place already.
197
+ operator_tls_secret = provided_tls_secret(overrides)
198
+ if operator_tls_secret:
199
+ _require_tls_secret(operator_tls_secret, namespace)
208
200
  # 7. Helm upgrade --install
209
201
  if chart is None:
210
202
  # Use OCI chart from the registry
@@ -229,13 +221,96 @@ def deploy(
229
221
  # default) and label the sole node on single-node clusters.
230
222
  _ensure_worker_type_labels(namespace)
231
223
  msg.good("Deployment complete")
232
- if local_tls_cert is not None:
233
- report_trust(local_tls_cert)
224
+ if uses_managed_tls(overrides):
225
+ if wait:
226
+ _wait_for_managed_certificate(namespace)
227
+ msg.info(
228
+ "The cluster is served with a certificate Ellf issues it. On each "
229
+ "device you open Ellf on, trust it once with: ellf infra tls --trust"
230
+ )
234
231
  if wait:
235
232
  msg.info("Running verification checks...")
236
233
  preflight_test(release=DEFAULT_RELEASE, namespace=namespace)
237
234
 
238
235
 
236
+ def uses_managed_tls(overrides: HelmChartOverrides) -> bool:
237
+ """Whether the cluster's certificate comes from Ellf (managedTls)."""
238
+ return bool(overrides.managed_tls and overrides.managed_tls.enabled)
239
+
240
+
241
+ def provided_tls_secret(overrides: HelmChartOverrides) -> Optional[str]:
242
+ """The TLS Secret the operator provides, or None when cert-manager,
243
+ Ellf (managedTls) or the chart (dev clusters' local.tls) makes it."""
244
+ tls = overrides.ingress.tls if overrides.ingress else None
245
+ if not (tls and tls.secret_name) or overrides.cert_manager:
246
+ return None
247
+ if uses_managed_tls(overrides):
248
+ return None
249
+ if overrides.local and overrides.local.tls:
250
+ return None
251
+ return tls.secret_name
252
+
253
+
254
+ def _wait_for_managed_certificate(
255
+ namespace: str, timeout: float = 120.0, interval: float = 3.0
256
+ ) -> None:
257
+ """Wait for CPL to write the first certificate from Ellf. Until it does,
258
+ Traefik serves its own self-signed one."""
259
+ deadline = time.monotonic() + timeout
260
+ while True:
261
+ result = subprocess.run(
262
+ ["kubectl", "get", "secret", MANAGED_TLS_SECRET_NAME, "-n", namespace],
263
+ capture_output=True,
264
+ text=True,
265
+ )
266
+ if result.returncode == 0:
267
+ msg.good("The cluster has its certificate from Ellf")
268
+ return
269
+ if time.monotonic() >= deadline:
270
+ msg.warn(
271
+ f"The cluster hasn't received its certificate from Ellf yet "
272
+ f"(Secret {MANAGED_TLS_SECRET_NAME!r}). Check the cpl container's "
273
+ "logs: kubectl logs -n "
274
+ f"{namespace} deploy/ellf-broker -c cpl"
275
+ )
276
+ return
277
+ time.sleep(interval)
278
+
279
+
280
+ def _require_tls_secret(secret_name: str, namespace: str) -> None:
281
+ """Fail unless the operator's TLS Secret exists. Without it the ingress
282
+ would fall back to Traefik's self-signed certificate."""
283
+ result = subprocess.run(
284
+ [
285
+ "kubectl",
286
+ "get",
287
+ "secret",
288
+ secret_name,
289
+ "-n",
290
+ namespace,
291
+ "-o",
292
+ "jsonpath={.type}",
293
+ ],
294
+ capture_output=True,
295
+ text=True,
296
+ )
297
+ if result.returncode != 0:
298
+ msg.fail(
299
+ f"The cluster's certificate Secret {secret_name!r} doesn't exist in "
300
+ f"namespace {namespace!r}. Create it from your certificate and key:\n"
301
+ f" kubectl -n {namespace} create secret tls {secret_name} "
302
+ "--cert=<certificate.pem> --key=<key.pem>"
303
+ )
304
+ raise SystemExit(1)
305
+ if result.stdout.strip() != "kubernetes.io/tls":
306
+ msg.fail(
307
+ f"Secret {secret_name!r} has type {result.stdout.strip()!r}; the "
308
+ "cluster's certificate needs a kubernetes.io/tls Secret "
309
+ "('kubectl create secret tls')."
310
+ )
311
+ raise SystemExit(1)
312
+
313
+
239
314
  def _placeholder_keys(values: object, prefix: str = "") -> list[str]:
240
315
  """Dotted paths of the values ``infra init-values`` left as FILL-IN."""
241
316
  if isinstance(values, dict):
@@ -15,6 +15,7 @@ from ...cluster_config import ClusterConfig
15
15
  from ...errors import CLIError
16
16
  from ...helm import HelmChartValues, NodePool, PerJob
17
17
  from ...interactivity import stdin_isatty
18
+ from ...url import URL
18
19
  from ._config_io import FILL_IN, is_placeholder
19
20
  from .register import CLOUD_PROVIDERS, ensure_registered
20
21
  from .terraform import (
@@ -24,7 +25,7 @@ from .terraform import (
24
25
  ensure_ingress_ip,
25
26
  record_backend,
26
27
  )
27
- from .tls import is_local_https
28
+ from .tls import address_reachability, pam_certifies
28
29
 
29
30
  # Per-provider defaults mirroring infra/cluster/config-*-example.yaml.
30
31
  _SYSTEM_POOL_MACHINE = {
@@ -170,25 +171,47 @@ def init(
170
171
  # Stage 1 covers the cloud path; the k3s deployment isn't
171
172
  # representable in ClusterConfig yet, so hand over to the documented
172
173
  # local flow after registration.
173
- # A cluster on http://localhost needs no certificate. One at
174
- # https://localhost (for Safari) gets one from the CLI's localhost
175
- # CA at deploy time, which this machine then has to trust. One on a
176
- # public domain gets Let's Encrypt through cert-manager.
177
- local_https = is_local_https(address)
178
- if address.startswith("http://") or local_https:
174
+ # A cluster on http://localhost needs no certificate. One over https
175
+ # on localhost or at an IP address on a private network gets one
176
+ # from Ellf (managedTls), which each device then trusts once. One
177
+ # Let's Encrypt can reach gets its certificate through cert-manager.
178
+ # Any other (a name the operator's DNS points at a private network,
179
+ # or a public IP) is served a certificate the operator provides.
180
+ certified = pam_certifies(address)
181
+ reachability = (
182
+ None
183
+ if address.startswith("http://") or certified
184
+ else address_reachability(address)
185
+ )
186
+ init_values = (
187
+ "ellf infra init-values --creds cluster-creds.json "
188
+ f"--cloud-provider k3s --domain {address}"
189
+ )
190
+ if address.startswith("http://") or certified:
191
+ steps = [init_values]
192
+ elif reachability != "public":
179
193
  steps = [
180
- "ellf infra init-values --creds cluster-creds.json "
181
- f"--cloud-provider k3s --domain {address}",
194
+ "kubectl create namespace ellf",
195
+ "kubectl -n ellf create secret tls ellf-tls "
196
+ "--cert=<certificate.pem> --key=<key.pem>",
197
+ f"{init_values} --tls-secret-name ellf-tls",
182
198
  ]
183
199
  else:
184
200
  steps = [
185
201
  "ellf infra setup --cert-manager",
186
- "ellf infra init-values --creds cluster-creds.json "
187
- f"--cloud-provider k3s --domain {address} --acme-email <email>",
202
+ f"{init_values} --acme-email <email>",
188
203
  ]
189
204
  steps.append("ellf infra deploy --values values.yaml --wait")
190
- if local_https:
191
- steps.append("ellf infra tls --trust")
205
+ if certified:
206
+ steps.append("ellf infra tls --trust # on each device you open Ellf on")
207
+ if reachability == "unresolved":
208
+ msg.warn(
209
+ f"{URL.parse(address, scheme='https').hostname} doesn't resolve "
210
+ "yet. Point it at the cluster in your DNS. If that's a public "
211
+ "address, Let's Encrypt can issue the certificate instead: run "
212
+ "'ellf infra setup --cert-manager' and pass --acme-email "
213
+ "<email> to init-values in place of --tls-secret-name."
214
+ )
192
215
  msg.info("Local (k3s) cluster registered. Continue with the local flow:")
193
216
  # Plain prints: wasabi re-wraps its messages, which ran the steps
194
217
  # together into one paragraph.
@@ -12,7 +12,7 @@ from ...cli import cli
12
12
  from ...helm import IMAGE_PULL_SECRET_NAME
13
13
  from ...url import URL
14
14
  from ._helpers import ClusterCredsFile
15
- from .tls import LOCAL_TLS_SECRET_NAME, is_local_https
15
+ from .tls import MANAGED_TLS_SECRET_NAME, pam_certifies
16
16
 
17
17
  CLOUD_PROVIDERS = ("gcp", "k3s", "aws", "azure")
18
18
 
@@ -31,15 +31,27 @@ CLOUD_PROVIDERS = ("gcp", "k3s", "aws", "azure")
31
31
  "Public address the cluster is reached on, host or host:port "
32
32
  "(e.g. 'cluster.example.com'). Use 'http://localhost' for a cluster "
33
33
  "on this machine, which needs no certificate and works in every "
34
- "browser but Safari; 'https://localhost[:port]' serves it with a "
35
- "certificate from a localhost-only CA the CLI keeps, for Safari "
36
- "(see 'ellf infra tls'). Match what was passed to 'infra register' — "
34
+ "browser but Safari. 'https://localhost[:port]', or https at an IP "
35
+ "address on a private network (for other devices to reach), is "
36
+ "served with a certificate Ellf issues the cluster, which each "
37
+ "device trusts once (see 'ellf infra tls'). Match what was passed "
38
+ "to 'infra register' — "
37
39
  "it flows into ELLF_BROKER_HOST so the cluster emits matching "
38
40
  "OIDC / token URLs."
39
41
  ),
40
42
  ),
41
43
  out=Arg("--out", help="Output path for generated values.yaml"),
42
44
  acme_email=Arg("--acme-email", help="Email for Let's Encrypt certificates"),
45
+ tls_secret_name=Arg(
46
+ "--tls-secret-name",
47
+ help=(
48
+ "Serve the cluster with a certificate you provide: the name of a "
49
+ "kubernetes.io/tls Secret in the cluster's namespace, made however "
50
+ "you like (your own cert-manager issuer, a company CA, by hand). "
51
+ "For a cluster on a private network or at an IP address, which "
52
+ "Let's Encrypt can't certify."
53
+ ),
54
+ ),
43
55
  )
44
56
  def init_values(
45
57
  creds: Path,
@@ -47,6 +59,7 @@ def init_values(
47
59
  domain: str,
48
60
  out: Union[Path, str] = "values.yaml",
49
61
  acme_email: Optional[str] = None,
62
+ tls_secret_name: Optional[str] = None,
50
63
  ) -> None:
51
64
  """Generate a values.yaml for Helm deployment from credentials and provider defaults."""
52
65
  from .._state import get_auth_state
@@ -57,6 +70,18 @@ def init_values(
57
70
  f"Choose from: {', '.join(CLOUD_PROVIDERS)}"
58
71
  )
59
72
  raise SystemExit(1)
73
+ if acme_email and tls_secret_name:
74
+ msg.fail(
75
+ "Pass either --acme-email or --tls-secret-name, not both: Let's "
76
+ "Encrypt issues the certificate, or you provide it."
77
+ )
78
+ raise SystemExit(1)
79
+ if tls_secret_name == MANAGED_TLS_SECRET_NAME:
80
+ msg.fail(
81
+ f"'{MANAGED_TLS_SECRET_NAME}' is the Secret the cluster keeps the "
82
+ "certificate Ellf issues it in. Name yours something else."
83
+ )
84
+ raise SystemExit(1)
60
85
  if isinstance(out, str):
61
86
  out = Path(out)
62
87
  creds_data = srsly.read_json(creds)
@@ -98,10 +123,12 @@ def init_values(
98
123
  if acme_email:
99
124
  values["ingress"] = {"tls": {"secretName": "ellf-tls"}}
100
125
  values["certManager"] = {"acmeEmail": acme_email}
101
- elif is_local_https(domain):
102
- # Let's Encrypt can't issue for localhost: 'infra deploy' issues the
103
- # certificate from the CLI's localhost-only CA instead.
104
- values["ingress"] = {"tls": {"secretName": LOCAL_TLS_SECRET_NAME}}
126
+ elif tls_secret_name:
127
+ values["ingress"] = {"tls": {"secretName": tls_secret_name}}
128
+ elif pam_certifies(domain):
129
+ # No public CA certifies localhost or a private IP: the cluster gets
130
+ # its certificate from Ellf, which its CPL sidecar keeps current.
131
+ values["managedTls"] = {"enabled": True}
105
132
  if cloud_provider == "k3s":
106
133
  # State the selector explicitly rather than relying on the chart
107
134
  # default — deploy-time node labeling reads this file, not the chart.
@@ -52,7 +52,9 @@ class Registration(NamedTuple):
52
52
  "or 'http://localhost' for a cluster on this machine, which needs "
53
53
  "no certificate and is what the free Solo plan uses by default; "
54
54
  "Safari can't reach it there, so for Safari use "
55
- "'https://localhost[:port]', see 'ellf infra tls'). "
55
+ "'https://localhost[:port]', and to reach it from other devices "
56
+ "'https://<its IP address on your network>'; Ellf issues those a "
57
+ "certificate, see 'ellf infra tls'). "
56
58
  "Stored verbatim as the cluster address; the chart derives the "
57
59
  "bare hostname for K8s ingress / cert SAN where a port isn't allowed. "
58
60
  "Omit to get a managed subdomain under an Explosion-controlled zone."
@@ -69,9 +71,10 @@ class Registration(NamedTuple):
69
71
  public_ip=Arg(
70
72
  "--public-ip",
71
73
  help=(
72
- "Cluster ingress IP to point the managed subdomain at. Omit to "
73
- "reserve the name now and re-run with --public-ip once the "
74
- "load balancer IP is known."
74
+ "Cluster ingress IP to point the managed subdomain at: a public "
75
+ "IPv4 address. Omit to reserve the name now and re-run with "
76
+ "--public-ip once the load balancer IP is known. A cluster on a "
77
+ "private network uses --domain with your own DNS instead."
75
78
  ),
76
79
  ),
77
80
  cloud_provider=Arg(
@@ -109,7 +112,7 @@ def register(
109
112
  """Register a new cluster with PAM and write credentials to cluster-creds.json.
110
113
 
111
114
  Pass --domain to use your own DNS, or omit it to be allocated a managed
112
- subdomain (with base + wildcard A records) under an Explosion-controlled
115
+ subdomain (with an A record) under an Explosion-controlled
113
116
  zone. An org on the free Solo plan has no managed subdomain: omitting
114
117
  --domain registers it at http://localhost.
115
118
  """
@@ -185,7 +188,8 @@ def ensure_registered(
185
188
  ),
186
189
  None,
187
190
  )
188
- # Solo's one cluster is on localhost, and PAM refuses it anything else.
191
+ # Solo's one cluster is on localhost or a private network, and PAM
192
+ # refuses it anything else; localhost is the default.
189
193
  if domain is None and _org_is_solo(client, org_id):
190
194
  msg.info(Messages.T074)
191
195
  # Plain http needs no certificate, and every browser but Safari lets
@@ -252,7 +256,7 @@ def ensure_registered(
252
256
  allocated_fqdn = allocated.fqdn
253
257
  if public_ip:
254
258
  msg.good(f"Managed subdomain {allocated.fqdn} -> {public_ip}")
255
- msg.info(f" Created A records for {allocated.fqdn} and *.{allocated.fqdn}")
259
+ msg.info(f" Created an A record for {allocated.fqdn}")
256
260
  else:
257
261
  msg.good(f"Managed subdomain {allocated.fqdn} reserved")
258
262
  msg.info(
@@ -3,7 +3,6 @@
3
3
  from pathlib import Path
4
4
  from typing import Optional, Union
5
5
 
6
- import srsly
7
6
  from radicli import Arg
8
7
  from wasabi import msg
9
8
 
@@ -17,7 +16,6 @@ from ...helm import (
17
16
  )
18
17
  from ._config_io import check_deploy_ready, load_cluster_config
19
18
  from ._helpers import (
20
- ClusterCredsFile,
21
19
  create_image_pull_secret,
22
20
  ensure_cert_manager,
23
21
  ensure_cluster_keypair,
@@ -42,16 +40,11 @@ DEFAULT_NAMESPACE = "ellf"
42
40
  "--config-key",
43
41
  help="Top-level key for the cluster config.",
44
42
  ),
45
- offline=Arg(
46
- "--offline",
47
- help="Path to a credentials JSON file. Deploys without connecting to PAM.",
48
- ),
49
43
  )
50
44
  def start(
51
45
  src: Union[Path, str] = ".",
52
46
  config_path: Optional[Path] = None,
53
47
  config_key: Optional[str] = None,
54
- offline: Optional[Path] = None,
55
48
  ) -> None:
56
49
  """Start a cluster on infrastructure configured under your own cloud account."""
57
50
  from .._state import get_auth_state
@@ -65,59 +58,24 @@ def start(
65
58
  cloud_provider = config.deployment.cloud_provider
66
59
  use_cluster_credentials(config)
67
60
  public_key = ensure_cluster_keypair(config.deployment.infra_secret_name)
68
- cluster_detail = None
69
- dns_client = None
70
- if offline is not None:
71
- from uuid import UUID
72
-
73
- from ellf_pam_sdk.models import ClusterCredentials, Image, Secrets
74
-
75
- if not config.deployment.pam_domain:
76
- from ...errors import CLIError
77
-
78
- raise CLIError(
79
- "deployment.pam_domain is required with --offline",
80
- "The chart needs the PAM host (e.g. 'app.ellf.ai'). "
81
- "Online runs derive it from your login; offline runs must "
82
- "set it in config.yaml.",
83
- )
84
- creds_data = srsly.read_json(offline)
85
- assert isinstance(creds_data, dict)
86
- creds_file = ClusterCredsFile(**creds_data)
87
- creds = ClusterCredentials(
88
- cluster_id=UUID(creds_file.cluster_id),
89
- public_key=public_key,
90
- image=Image(
91
- registry=creds_file.upstream_registry,
92
- broker_version=creds_file.target_broker_version,
93
- cpl_version=creds_file.target_cpl_version,
94
- ),
95
- secrets=Secrets(
96
- container_sa_key=creds_file.container_sa_key,
97
- prodigy_license_key=creds_file.prodigy_license_key,
98
- ),
99
- )
100
- values = build_values(config, creds)
101
- else:
102
- auth = get_auth_state()
103
- # The chart hard-requires cluster.pamFqdn; it's not information the
104
- # operator should have to supply, so default it from the PAM host
105
- # they're already logged in to (mirrors init_values).
106
- if not config.deployment.pam_domain:
107
- config.deployment.pam_domain = auth.pam_url.netloc
108
- ensure_cluster_record(
109
- auth.client,
110
- auth.org_id,
111
- config.deployment.domain,
112
- public_key,
113
- cluster_name=config.name,
114
- cloud_provider=cloud_provider,
115
- )
116
- cluster_auth, cluster_detail = get_cluster_auth(
117
- auth.client, config.deployment.domain
118
- )
119
- dns_client = auth.client
120
- values = build_values(config, cluster_auth)
61
+ auth = get_auth_state()
62
+ # The chart hard-requires cluster.pamFqdn; it's not information the
63
+ # operator should have to supply, so default it from the PAM host
64
+ # they're already logged in to (mirrors init_values).
65
+ if not config.deployment.pam_domain:
66
+ config.deployment.pam_domain = auth.pam_url.netloc
67
+ ensure_cluster_record(
68
+ auth.client,
69
+ auth.org_id,
70
+ config.deployment.domain,
71
+ public_key,
72
+ cluster_name=config.name,
73
+ cloud_provider=cloud_provider,
74
+ )
75
+ cluster_auth, cluster_detail = get_cluster_auth(
76
+ auth.client, config.deployment.domain
77
+ )
78
+ values = build_values(config, cluster_auth)
121
79
  assert values.secrets is not None
122
80
  assert values.image is not None
123
81
  if values.image_pull_secrets and values.secrets.container_sa_key:
@@ -131,11 +89,7 @@ def start(
131
89
  ensure_traefik(public_ip=config.deployment.public_ip)
132
90
  if config.deployment.acme_email:
133
91
  ensure_cert_manager()
134
- if (
135
- dns_client is not None
136
- and cluster_detail is not None
137
- and cluster_detail.managed_subdomain
138
- ):
92
+ if cluster_detail.managed_subdomain:
139
93
  # Point the managed subdomain at the ingress now that traefik (and
140
94
  # so the LoadBalancer) exists — before the chart upgrade, so a chart
141
95
  # failure doesn't leave DNS unpointed. On GCP the IP is usually
@@ -151,7 +105,7 @@ def start(
151
105
  lb_ip, lb_hostname = get_traefik_lb_address()
152
106
  if lb_ip or lb_hostname:
153
107
  point_managed_dns(
154
- dns_client, cluster_detail, ip=lb_ip, hostname=lb_hostname
108
+ auth.client, cluster_detail, ip=lb_ip, hostname=lb_hostname
155
109
  )
156
110
  else:
157
111
  msg.info(