ellf-cli 22.0.2__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.
- {ellf_cli-22.0.2/ellf_cli.egg-info → ellf_cli-22.0.4}/PKG-INFO +1 -1
- {ellf_cli-22.0.2 → ellf_cli-22.0.4}/README.md +17 -6
- {ellf_cli-22.0.2 → ellf_cli-22.0.4}/ellf_cli/__main__.py +3 -3
- {ellf_cli-22.0.2 → ellf_cli-22.0.4}/ellf_cli/commands/infra/__init__.py +1 -0
- {ellf_cli-22.0.2 → ellf_cli-22.0.4}/ellf_cli/commands/infra/_helpers.py +7 -12
- {ellf_cli-22.0.2 → ellf_cli-22.0.4}/ellf_cli/commands/infra/deploy.py +94 -2
- {ellf_cli-22.0.2 → ellf_cli-22.0.4}/ellf_cli/commands/infra/init.py +36 -7
- {ellf_cli-22.0.2 → ellf_cli-22.0.4}/ellf_cli/commands/infra/init_values.py +37 -3
- {ellf_cli-22.0.2 → ellf_cli-22.0.4}/ellf_cli/commands/infra/register.py +16 -9
- {ellf_cli-22.0.2 → ellf_cli-22.0.4}/ellf_cli/commands/infra/start.py +20 -66
- ellf_cli-22.0.4/ellf_cli/commands/infra/tls.py +302 -0
- {ellf_cli-22.0.2 → ellf_cli-22.0.4}/ellf_cli/ellf.json +84 -23
- {ellf_cli-22.0.2 → ellf_cli-22.0.4}/ellf_cli/helm.py +9 -3
- {ellf_cli-22.0.2 → ellf_cli-22.0.4}/ellf_cli/messages.py +3 -1
- ellf_cli-22.0.4/ellf_cli/version/22.0.2_2026-10-31-11-13-43_patch_54d644d8-cebd-4b6a-ba8f-685e2ffee481 +3 -0
- 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
- {ellf_cli-22.0.2 → ellf_cli-22.0.4/ellf_cli.egg-info}/PKG-INFO +1 -1
- {ellf_cli-22.0.2 → ellf_cli-22.0.4}/ellf_cli.egg-info/SOURCES.txt +4 -0
- {ellf_cli-22.0.2 → ellf_cli-22.0.4}/tests/test_infra_init.py +67 -2
- ellf_cli-22.0.4/tests/test_infra_tls.py +387 -0
- {ellf_cli-22.0.2 → ellf_cli-22.0.4}/LICENSE +0 -0
- {ellf_cli-22.0.2 → ellf_cli-22.0.4}/MANIFEST.in +0 -0
- {ellf_cli-22.0.2 → ellf_cli-22.0.4}/ellf_cli/__init__.py +0 -0
- {ellf_cli-22.0.2 → ellf_cli-22.0.4}/ellf_cli/about.json +0 -0
- {ellf_cli-22.0.2 → ellf_cli-22.0.4}/ellf_cli/about.py +0 -0
- {ellf_cli-22.0.2 → ellf_cli-22.0.4}/ellf_cli/agents.py +0 -0
- {ellf_cli-22.0.2 → ellf_cli-22.0.4}/ellf_cli/appdirs.py +0 -0
- {ellf_cli-22.0.2 → ellf_cli-22.0.4}/ellf_cli/auth.py +0 -0
- {ellf_cli-22.0.2 → ellf_cli-22.0.4}/ellf_cli/cli.py +0 -0
- {ellf_cli-22.0.2 → ellf_cli-22.0.4}/ellf_cli/cloud/__init__.py +0 -0
- {ellf_cli-22.0.2 → ellf_cli-22.0.4}/ellf_cli/cloud/gcp.py +0 -0
- {ellf_cli-22.0.2 → ellf_cli-22.0.4}/ellf_cli/cluster_config.py +0 -0
- {ellf_cli-22.0.2 → ellf_cli-22.0.4}/ellf_cli/commands/__init__.py +0 -0
- {ellf_cli-22.0.2 → ellf_cli-22.0.4}/ellf_cli/commands/_cluster_select.py +0 -0
- {ellf_cli-22.0.2 → ellf_cli-22.0.4}/ellf_cli/commands/_org_select.py +0 -0
- {ellf_cli-22.0.2 → ellf_cli-22.0.4}/ellf_cli/commands/_project_scope.py +0 -0
- {ellf_cli-22.0.2 → ellf_cli-22.0.4}/ellf_cli/commands/_recipe_file.py +0 -0
- {ellf_cli-22.0.2 → ellf_cli-22.0.4}/ellf_cli/commands/_recipe_subcommand.py +0 -0
- {ellf_cli-22.0.2 → ellf_cli-22.0.4}/ellf_cli/commands/_state.py +0 -0
- {ellf_cli-22.0.2 → ellf_cli-22.0.4}/ellf_cli/commands/_usage.py +0 -0
- {ellf_cli-22.0.2 → ellf_cli-22.0.4}/ellf_cli/commands/actions.py +0 -0
- {ellf_cli-22.0.2 → ellf_cli-22.0.4}/ellf_cli/commands/agents.py +0 -0
- {ellf_cli-22.0.2 → ellf_cli-22.0.4}/ellf_cli/commands/assets.py +0 -0
- {ellf_cli-22.0.2 → ellf_cli-22.0.4}/ellf_cli/commands/clusters.py +0 -0
- {ellf_cli-22.0.2 → ellf_cli-22.0.4}/ellf_cli/commands/config.py +0 -0
- {ellf_cli-22.0.2 → ellf_cli-22.0.4}/ellf_cli/commands/datasets.py +0 -0
- {ellf_cli-22.0.2 → ellf_cli-22.0.4}/ellf_cli/commands/files/__init__.py +0 -0
- {ellf_cli-22.0.2 → ellf_cli-22.0.4}/ellf_cli/commands/files/cp.py +0 -0
- {ellf_cli-22.0.2 → ellf_cli-22.0.4}/ellf_cli/commands/files/ls.py +0 -0
- {ellf_cli-22.0.2 → ellf_cli-22.0.4}/ellf_cli/commands/files/rm.py +0 -0
- {ellf_cli-22.0.2 → ellf_cli-22.0.4}/ellf_cli/commands/files/rsync.py +0 -0
- {ellf_cli-22.0.2 → ellf_cli-22.0.4}/ellf_cli/commands/files/stats.py +0 -0
- {ellf_cli-22.0.2 → ellf_cli-22.0.4}/ellf_cli/commands/general.py +0 -0
- {ellf_cli-22.0.2 → ellf_cli-22.0.4}/ellf_cli/commands/import_export.py +0 -0
- {ellf_cli-22.0.2 → ellf_cli-22.0.4}/ellf_cli/commands/infra/_config_io.py +0 -0
- {ellf_cli-22.0.2 → ellf_cli-22.0.4}/ellf_cli/commands/infra/configure.py +0 -0
- {ellf_cli-22.0.2 → ellf_cli-22.0.4}/ellf_cli/commands/infra/hibernate.py +0 -0
- {ellf_cli-22.0.2 → ellf_cli-22.0.4}/ellf_cli/commands/infra/pause.py +0 -0
- {ellf_cli-22.0.2 → ellf_cli-22.0.4}/ellf_cli/commands/infra/provision.py +0 -0
- {ellf_cli-22.0.2 → ellf_cli-22.0.4}/ellf_cli/commands/infra/setup.py +0 -0
- {ellf_cli-22.0.2 → ellf_cli-22.0.4}/ellf_cli/commands/infra/terraform.py +0 -0
- {ellf_cli-22.0.2 → ellf_cli-22.0.4}/ellf_cli/commands/jobs.py +0 -0
- {ellf_cli-22.0.2 → ellf_cli-22.0.4}/ellf_cli/commands/mcp.py +0 -0
- {ellf_cli-22.0.2 → ellf_cli-22.0.4}/ellf_cli/commands/packages.py +0 -0
- {ellf_cli-22.0.2 → ellf_cli-22.0.4}/ellf_cli/commands/paths.py +0 -0
- {ellf_cli-22.0.2 → ellf_cli-22.0.4}/ellf_cli/commands/plans.py +0 -0
- {ellf_cli-22.0.2 → ellf_cli-22.0.4}/ellf_cli/commands/projects.py +0 -0
- {ellf_cli-22.0.2 → ellf_cli-22.0.4}/ellf_cli/commands/publish_code.py +0 -0
- {ellf_cli-22.0.2 → ellf_cli-22.0.4}/ellf_cli/commands/publish_data.py +0 -0
- {ellf_cli-22.0.2 → ellf_cli-22.0.4}/ellf_cli/commands/recipes.py +0 -0
- {ellf_cli-22.0.2 → ellf_cli-22.0.4}/ellf_cli/commands/secrets.py +0 -0
- {ellf_cli-22.0.2 → ellf_cli-22.0.4}/ellf_cli/commands/services.py +0 -0
- {ellf_cli-22.0.2 → ellf_cli-22.0.4}/ellf_cli/commands/sessions.py +0 -0
- {ellf_cli-22.0.2 → ellf_cli-22.0.4}/ellf_cli/commands/support.py +0 -0
- {ellf_cli-22.0.2 → ellf_cli-22.0.4}/ellf_cli/commands/tasks.py +0 -0
- {ellf_cli-22.0.2 → ellf_cli-22.0.4}/ellf_cli/commands/todos.py +0 -0
- {ellf_cli-22.0.2 → ellf_cli-22.0.4}/ellf_cli/commands/users.py +0 -0
- {ellf_cli-22.0.2 → ellf_cli-22.0.4}/ellf_cli/config.py +0 -0
- {ellf_cli-22.0.2 → ellf_cli-22.0.4}/ellf_cli/ellf_skills/.gitignore +0 -0
- {ellf_cli-22.0.2 → ellf_cli-22.0.4}/ellf_cli/ellf_skills/render.py +0 -0
- {ellf_cli-22.0.2 → ellf_cli-22.0.4}/ellf_cli/ellf_skills/skills/ellf-annotate/SKILL.assistant.md +0 -0
- {ellf_cli-22.0.2 → ellf_cli-22.0.4}/ellf_cli/ellf_skills/skills/ellf-annotate/SKILL.coding.md +0 -0
- {ellf_cli-22.0.2 → ellf_cli-22.0.4}/ellf_cli/ellf_skills/skills/ellf-annotate/references/annotation_audit.assistant.md +0 -0
- {ellf_cli-22.0.2 → ellf_cli-22.0.4}/ellf_cli/ellf_skills/skills/ellf-annotate/references/annotation_audit.coding.md +0 -0
- {ellf_cli-22.0.2 → ellf_cli-22.0.4}/ellf_cli/ellf_skills/skills/ellf-annotate/references/builtin_ellf_annotation_recipes.assistant.md +0 -0
- {ellf_cli-22.0.2 → ellf_cli-22.0.4}/ellf_cli/ellf_skills/skills/ellf-annotate/references/builtin_ellf_annotation_recipes.coding.md +0 -0
- {ellf_cli-22.0.2 → ellf_cli-22.0.4}/ellf_cli/ellf_skills/skills/ellf-annotate/references/builtin_prodigy_recipes.coding.md +0 -0
- {ellf_cli-22.0.2 → ellf_cli-22.0.4}/ellf_cli/ellf_skills/skills/ellf-annotate/references/label_schemes.assistant.md +0 -0
- {ellf_cli-22.0.2 → ellf_cli-22.0.4}/ellf_cli/ellf_skills/skills/ellf-annotate/references/label_schemes.coding.md +0 -0
- {ellf_cli-22.0.2 → ellf_cli-22.0.4}/ellf_cli/ellf_skills/skills/ellf-ask/SKILL.md +0 -0
- {ellf_cli-22.0.2 → ellf_cli-22.0.4}/ellf_cli/ellf_skills/skills/ellf-handoff/SKILL.assistant.md +0 -0
- {ellf_cli-22.0.2 → ellf_cli-22.0.4}/ellf_cli/ellf_skills/skills/ellf-monitor/SKILL.assistant.md +0 -0
- {ellf_cli-22.0.2 → ellf_cli-22.0.4}/ellf_cli/ellf_skills/skills/ellf-monitor/SKILL.coding.md +0 -0
- {ellf_cli-22.0.2 → ellf_cli-22.0.4}/ellf_cli/ellf_skills/skills/ellf-monitor/references/annotation_metrics.assistant.md +0 -0
- {ellf_cli-22.0.2 → ellf_cli-22.0.4}/ellf_cli/ellf_skills/skills/ellf-monitor/references/annotation_metrics.coding.md +0 -0
- {ellf_cli-22.0.2 → ellf_cli-22.0.4}/ellf_cli/ellf_skills/skills/ellf-monitor/references/training_monitoring.assistant.md +0 -0
- {ellf_cli-22.0.2 → ellf_cli-22.0.4}/ellf_cli/ellf_skills/skills/ellf-monitor/references/training_monitoring.coding.md +0 -0
- {ellf_cli-22.0.2 → ellf_cli-22.0.4}/ellf_cli/ellf_skills/skills/ellf-monitor/scripts.coding/check_training.py +0 -0
- {ellf_cli-22.0.2 → ellf_cli-22.0.4}/ellf_cli/ellf_skills/skills/ellf-ops/SKILL.assistant.md +0 -0
- {ellf_cli-22.0.2 → ellf_cli-22.0.4}/ellf_cli/ellf_skills/skills/ellf-ops/SKILL.coding.md +0 -0
- {ellf_cli-22.0.2 → ellf_cli-22.0.4}/ellf_cli/ellf_skills/skills/ellf-ops/references.coding/data_infra_cli.md +0 -0
- {ellf_cli-22.0.2 → ellf_cli-22.0.4}/ellf_cli/ellf_skills/skills/ellf-ops/scripts.coding/run_job.py +0 -0
- {ellf_cli-22.0.2 → ellf_cli-22.0.4}/ellf_cli/ellf_skills/skills/ellf-patterns/SKILL.coding.md +0 -0
- {ellf_cli-22.0.2 → ellf_cli-22.0.4}/ellf_cli/ellf_skills/skills/ellf-patterns/references/pattern_strategies.md +0 -0
- {ellf_cli-22.0.2 → ellf_cli-22.0.4}/ellf_cli/ellf_skills/skills/ellf-prodigy/SKILL.coding.md +0 -0
- {ellf_cli-22.0.2 → ellf_cli-22.0.4}/ellf_cli/ellf_skills/skills/ellf-prodigy/assets/templates/template_action_recipe.py +0 -0
- {ellf_cli-22.0.2 → ellf_cli-22.0.4}/ellf_cli/ellf_skills/skills/ellf-prodigy/assets/templates/template_agent_recipe.py +0 -0
- {ellf_cli-22.0.2 → ellf_cli-22.0.4}/ellf_cli/ellf_skills/skills/ellf-prodigy/assets/templates/template_blocks_ui.py +0 -0
- {ellf_cli-22.0.2 → ellf_cli-22.0.4}/ellf_cli/ellf_skills/skills/ellf-prodigy/assets/templates/template_correct.py +0 -0
- {ellf_cli-22.0.2 → ellf_cli-22.0.4}/ellf_cli/ellf_skills/skills/ellf-prodigy/assets/templates/template_custom_ui.py +0 -0
- {ellf_cli-22.0.2 → ellf_cli-22.0.4}/ellf_cli/ellf_skills/skills/ellf-prodigy/assets/templates/template_manual.py +0 -0
- {ellf_cli-22.0.2 → ellf_cli-22.0.4}/ellf_cli/ellf_skills/skills/ellf-prodigy/assets/templates/template_pages_ui.py +0 -0
- {ellf_cli-22.0.2 → ellf_cli-22.0.4}/ellf_cli/ellf_skills/skills/ellf-prodigy/assets/templates/template_routing.py +0 -0
- {ellf_cli-22.0.2 → ellf_cli-22.0.4}/ellf_cli/ellf_skills/skills/ellf-prodigy/assets/templates/template_service_recipe.py +0 -0
- {ellf_cli-22.0.2 → ellf_cli-22.0.4}/ellf_cli/ellf_skills/skills/ellf-prodigy/assets/templates/template_task_recipe.py +0 -0
- {ellf_cli-22.0.2 → ellf_cli-22.0.4}/ellf_cli/ellf_skills/skills/ellf-prodigy/assets/templates/template_teach.py +0 -0
- {ellf_cli-22.0.2 → ellf_cli-22.0.4}/ellf_cli/ellf_skills/skills/ellf-prodigy/references/builtin_recipes.md +0 -0
- {ellf_cli-22.0.2 → ellf_cli-22.0.4}/ellf_cli/ellf_skills/skills/ellf-prodigy/references/ellf_recipe_sdk.md +0 -0
- {ellf_cli-22.0.2 → ellf_cli-22.0.4}/ellf_cli/ellf_skills/skills/ellf-prodigy/references/lint_recipe.py +0 -0
- {ellf_cli-22.0.2 → ellf_cli-22.0.4}/ellf_cli/ellf_skills/skills/ellf-prodigy/references/prodigy_recipe_api.md +0 -0
- {ellf_cli-22.0.2 → ellf_cli-22.0.4}/ellf_cli/ellf_skills/skills/ellf-prodigy/references/template_index.md +0 -0
- {ellf_cli-22.0.2 → ellf_cli-22.0.4}/ellf_cli/ellf_skills/skills/ellf-project/SKILL.assistant.md +0 -0
- {ellf_cli-22.0.2 → ellf_cli-22.0.4}/ellf_cli/ellf_skills/skills/ellf-project/SKILL.coding.md +0 -0
- {ellf_cli-22.0.2 → ellf_cli-22.0.4}/ellf_cli/ellf_skills/skills/ellf-project/references/annotation_agents.md +0 -0
- {ellf_cli-22.0.2 → ellf_cli-22.0.4}/ellf_cli/ellf_skills/skills/ellf-project/references/consulting_patterns.md +0 -0
- {ellf_cli-22.0.2 → ellf_cli-22.0.4}/ellf_cli/ellf_skills/skills/ellf-project/references/explosion_strategy.md +0 -0
- {ellf_cli-22.0.2 → ellf_cli-22.0.4}/ellf_cli/ellf_skills/skills/ellf-project/references/jev.coding.md +0 -0
- {ellf_cli-22.0.2 → ellf_cli-22.0.4}/ellf_cli/ellf_skills/skills/ellf-project/references/label_schemes.assistant.md +0 -0
- {ellf_cli-22.0.2 → ellf_cli-22.0.4}/ellf_cli/ellf_skills/skills/ellf-project/references/label_schemes.coding.md +0 -0
- {ellf_cli-22.0.2 → ellf_cli-22.0.4}/ellf_cli/ellf_skills/skills/ellf-support/SKILL.assistant.md +0 -0
- {ellf_cli-22.0.2 → ellf_cli-22.0.4}/ellf_cli/ellf_skills/skills/ellf-support/SKILL.coding.md +0 -0
- {ellf_cli-22.0.2 → ellf_cli-22.0.4}/ellf_cli/ellf_skills/skills/ellf-template/SKILL.coding.md +0 -0
- {ellf_cli-22.0.2 → ellf_cli-22.0.4}/ellf_cli/ellf_skills/skills/ellf-template/references/template_format.md +0 -0
- {ellf_cli-22.0.2 → ellf_cli-22.0.4}/ellf_cli/ellf_skills/skills/ellf-template/scripts/placeholders.py +0 -0
- {ellf_cli-22.0.2 → ellf_cli-22.0.4}/ellf_cli/ellf_skills/skills/ellf-todo/SKILL.coding.md +0 -0
- {ellf_cli-22.0.2 → ellf_cli-22.0.4}/ellf_cli/ellf_skills/skills/ellf-train/SKILL.assistant.md +0 -0
- {ellf_cli-22.0.2 → ellf_cli-22.0.4}/ellf_cli/ellf_skills/skills/ellf-train/SKILL.coding.md +0 -0
- {ellf_cli-22.0.2 → ellf_cli-22.0.4}/ellf_cli/ellf_skills/skills/ellf-train/references/config_advanced.coding.md +0 -0
- {ellf_cli-22.0.2 → ellf_cli-22.0.4}/ellf_cli/ellf_skills/skills/ellf-train/references/config_architectures.coding.md +0 -0
- {ellf_cli-22.0.2 → ellf_cli-22.0.4}/ellf_cli/ellf_skills/skills/ellf-train/references/config_training.coding.md +0 -0
- {ellf_cli-22.0.2 → ellf_cli-22.0.4}/ellf_cli/ellf_skills/skills/ellf-train/references/diagnostics.md +0 -0
- {ellf_cli-22.0.2 → ellf_cli-22.0.4}/ellf_cli/ellf_skills/skills/ellf-train/references/evaluation_guide.md +0 -0
- {ellf_cli-22.0.2 → ellf_cli-22.0.4}/ellf_cli/ellf_skills/skills/ellf-train/references/experiment_patterns.coding.md +0 -0
- {ellf_cli-22.0.2 → ellf_cli-22.0.4}/ellf_cli/ellf_skills/skills/ellf-train/references/model_selection.md +0 -0
- {ellf_cli-22.0.2 → ellf_cli-22.0.4}/ellf_cli/ellf_skills/skills/ellf-train/references/training_paradigms.md +0 -0
- {ellf_cli-22.0.2 → ellf_cli-22.0.4}/ellf_cli/ellf_skills/skills/ellf-train/references/training_troubleshooting.coding.md +0 -0
- {ellf_cli-22.0.2 → ellf_cli-22.0.4}/ellf_cli/ellf_skills/skills/ellf-train/references/workflow.assistant.md +0 -0
- {ellf_cli-22.0.2 → ellf_cli-22.0.4}/ellf_cli/ellf_skills/skills/ellf-train/references/workflow.coding.md +0 -0
- {ellf_cli-22.0.2 → ellf_cli-22.0.4}/ellf_cli/ellf_skills/skills/ellf-train/scripts.coding/ellf_logger.py +0 -0
- {ellf_cli-22.0.2 → ellf_cli-22.0.4}/ellf_cli/errors.py +0 -0
- {ellf_cli-22.0.2 → ellf_cli-22.0.4}/ellf_cli/interactivity.py +0 -0
- {ellf_cli-22.0.2 → ellf_cli-22.0.4}/ellf_cli/key_pair.py +0 -0
- {ellf_cli-22.0.2 → ellf_cli-22.0.4}/ellf_cli/main.py +0 -0
- {ellf_cli-22.0.2 → ellf_cli-22.0.4}/ellf_cli/mcp_proxy.py +0 -0
- {ellf_cli-22.0.2 → ellf_cli-22.0.4}/ellf_cli/pypi_auth.py +0 -0
- {ellf_cli-22.0.2 → ellf_cli-22.0.4}/ellf_cli/query.py +0 -0
- {ellf_cli-22.0.2 → ellf_cli-22.0.4}/ellf_cli/recipes_cookiecutter/cookiecutter.json +0 -0
- {ellf_cli-22.0.2 → ellf_cli-22.0.4}/ellf_cli/recipes_cookiecutter/{{cookiecutter.package_dir}}/.gitignore +0 -0
- {ellf_cli-22.0.2 → ellf_cli-22.0.4}/ellf_cli/recipes_cookiecutter/{{cookiecutter.package_dir}}/README.md.tmpl +0 -0
- {ellf_cli-22.0.2 → ellf_cli-22.0.4}/ellf_cli/recipes_cookiecutter/{{cookiecutter.package_dir}}/requirements-dev.in +0 -0
- {ellf_cli-22.0.2 → ellf_cli-22.0.4}/ellf_cli/recipes_cookiecutter/{{cookiecutter.package_dir}}/requirements.in +0 -0
- {ellf_cli-22.0.2 → ellf_cli-22.0.4}/ellf_cli/recipes_cookiecutter/{{cookiecutter.package_dir}}/setup.py.tmpl +0 -0
- {ellf_cli-22.0.2 → ellf_cli-22.0.4}/ellf_cli/recipes_cookiecutter/{{cookiecutter.package_dir}}/{{cookiecutter.package_name}}/__init__.py +0 -0
- {ellf_cli-22.0.2 → ellf_cli-22.0.4}/ellf_cli/recipes_cookiecutter/{{cookiecutter.package_dir}}/{{cookiecutter.package_name}}/about.py +0 -0
- {ellf_cli-22.0.2 → ellf_cli-22.0.4}/ellf_cli/recipes_cookiecutter/{{cookiecutter.package_dir}}/{{cookiecutter.package_name}}/recipes/__init__.py +0 -0
- {ellf_cli-22.0.2 → ellf_cli-22.0.4}/ellf_cli/recipes_cookiecutter/{{cookiecutter.package_dir}}/{{cookiecutter.package_name}}/recipes/example_task.py +0 -0
- {ellf_cli-22.0.2 → ellf_cli-22.0.4}/ellf_cli/testing/__init__.py +0 -0
- {ellf_cli-22.0.2 → ellf_cli-22.0.4}/ellf_cli/ty.py +0 -0
- {ellf_cli-22.0.2 → ellf_cli-22.0.4}/ellf_cli/ui.py +0 -0
- {ellf_cli-22.0.2 → ellf_cli-22.0.4}/ellf_cli/url.py +0 -0
- {ellf_cli-22.0.2 → ellf_cli-22.0.4}/ellf_cli/util.py +0 -0
- {ellf_cli-22.0.2 → ellf_cli-22.0.4}/ellf_cli/version/17.0.114_2026-08-19-08-36-57__1a4e37c1-471e-4a81-b4d5-1ffe7dec3a61 +0 -0
- {ellf_cli-22.0.2 → 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
- {ellf_cli-22.0.2 → 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
- {ellf_cli-22.0.2 → 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
- {ellf_cli-22.0.2 → 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
- {ellf_cli-22.0.2 → 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
- {ellf_cli-22.0.2 → 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
- {ellf_cli-22.0.2 → 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
- {ellf_cli-22.0.2 → 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
- {ellf_cli-22.0.2 → 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
- {ellf_cli-22.0.2 → 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
- {ellf_cli-22.0.2 → 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
- {ellf_cli-22.0.2 → 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
- {ellf_cli-22.0.2 → 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
- {ellf_cli-22.0.2 → 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
- {ellf_cli-22.0.2 → 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
- {ellf_cli-22.0.2 → 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
- {ellf_cli-22.0.2 → 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
- {ellf_cli-22.0.2 → 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
- {ellf_cli-22.0.2 → 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
- {ellf_cli-22.0.2 → 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
- {ellf_cli-22.0.2 → 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
- {ellf_cli-22.0.2 → 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
- {ellf_cli-22.0.2 → 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
- {ellf_cli-22.0.2 → 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
- {ellf_cli-22.0.2 → 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
- {ellf_cli-22.0.2 → 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
- {ellf_cli-22.0.2 → 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
- {ellf_cli-22.0.2 → 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
- {ellf_cli-22.0.2 → 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
- {ellf_cli-22.0.2 → 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
- {ellf_cli-22.0.2 → 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
- {ellf_cli-22.0.2 → 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
- {ellf_cli-22.0.2 → 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
- {ellf_cli-22.0.2 → 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
- {ellf_cli-22.0.2 → 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
- {ellf_cli-22.0.2 → 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
- {ellf_cli-22.0.2 → 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
- {ellf_cli-22.0.2 → 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
- {ellf_cli-22.0.2 → 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
- {ellf_cli-22.0.2 → 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
- {ellf_cli-22.0.2 → 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
- {ellf_cli-22.0.2 → 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
- {ellf_cli-22.0.2 → 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
- {ellf_cli-22.0.2 → 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
- {ellf_cli-22.0.2 → 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
- {ellf_cli-22.0.2 → 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
- {ellf_cli-22.0.2 → 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
- {ellf_cli-22.0.2 → 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
- {ellf_cli-22.0.2 → 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
- {ellf_cli-22.0.2 → 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
- {ellf_cli-22.0.2 → 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
- {ellf_cli-22.0.2 → 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
- {ellf_cli-22.0.2 → 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
- {ellf_cli-22.0.2 → 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
- {ellf_cli-22.0.2 → 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
- {ellf_cli-22.0.2 → 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
- {ellf_cli-22.0.2 → 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
- {ellf_cli-22.0.2 → 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
- {ellf_cli-22.0.2 → 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
- {ellf_cli-22.0.2 → 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
- {ellf_cli-22.0.2 → 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
- {ellf_cli-22.0.2 → 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
- {ellf_cli-22.0.2 → 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
- {ellf_cli-22.0.2 → 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
- {ellf_cli-22.0.2 → 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
- {ellf_cli-22.0.2 → 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
- {ellf_cli-22.0.2 → 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
- {ellf_cli-22.0.2 → 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
- {ellf_cli-22.0.2 → 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
- {ellf_cli-22.0.2 → 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
- {ellf_cli-22.0.2 → 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
- {ellf_cli-22.0.2 → 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
- {ellf_cli-22.0.2 → 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
- {ellf_cli-22.0.2 → 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
- {ellf_cli-22.0.2 → 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
- {ellf_cli-22.0.2 → 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
- {ellf_cli-22.0.2 → 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
- {ellf_cli-22.0.2 → 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
- {ellf_cli-22.0.2 → 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
- {ellf_cli-22.0.2 → 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
- {ellf_cli-22.0.2 → 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
- {ellf_cli-22.0.2 → 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
- {ellf_cli-22.0.2 → 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
- {ellf_cli-22.0.2 → 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
- {ellf_cli-22.0.2 → 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
- {ellf_cli-22.0.2 → 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
- {ellf_cli-22.0.2 → 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
- {ellf_cli-22.0.2 → 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
- {ellf_cli-22.0.2 → 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
- {ellf_cli-22.0.2 → 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
- {ellf_cli-22.0.2 → 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
- {ellf_cli-22.0.2 → 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
- {ellf_cli-22.0.2 → 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
- {ellf_cli-22.0.2 → 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
- {ellf_cli-22.0.2 → 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
- {ellf_cli-22.0.2 → 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
- {ellf_cli-22.0.2 → 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
- {ellf_cli-22.0.2 → 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
- {ellf_cli-22.0.2 → 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
- {ellf_cli-22.0.2 → 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
- {ellf_cli-22.0.2 → 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
- {ellf_cli-22.0.2 → 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
- {ellf_cli-22.0.2 → 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
- {ellf_cli-22.0.2 → 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
- {ellf_cli-22.0.2 → 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
- {ellf_cli-22.0.2 → 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
- {ellf_cli-22.0.2 → 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
- {ellf_cli-22.0.2 → 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
- {ellf_cli-22.0.2 → 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
- {ellf_cli-22.0.2 → 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
- {ellf_cli-22.0.2 → 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
- {ellf_cli-22.0.2 → 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
- {ellf_cli-22.0.2 → 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
- {ellf_cli-22.0.2 → 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
- {ellf_cli-22.0.2 → 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
- {ellf_cli-22.0.2 → 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
- {ellf_cli-22.0.2 → 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
- {ellf_cli-22.0.2 → 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
- {ellf_cli-22.0.2 → 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
- {ellf_cli-22.0.2 → 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
- {ellf_cli-22.0.2 → 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
- {ellf_cli-22.0.2 → 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
- {ellf_cli-22.0.2 → 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
- {ellf_cli-22.0.2 → 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
- {ellf_cli-22.0.2 → 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
- {ellf_cli-22.0.2 → 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
- {ellf_cli-22.0.2 → 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
- {ellf_cli-22.0.2 → 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
- {ellf_cli-22.0.2 → 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
- {ellf_cli-22.0.2 → 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
- {ellf_cli-22.0.2 → 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
- {ellf_cli-22.0.2 → 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
- {ellf_cli-22.0.2 → 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
- {ellf_cli-22.0.2 → 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
- {ellf_cli-22.0.2 → 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
- {ellf_cli-22.0.2 → 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
- {ellf_cli-22.0.2 → 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
- {ellf_cli-22.0.2 → 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
- {ellf_cli-22.0.2 → 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
- {ellf_cli-22.0.2 → 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
- {ellf_cli-22.0.2 → 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
- {ellf_cli-22.0.2 → 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
- {ellf_cli-22.0.2 → 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
- {ellf_cli-22.0.2 → 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
- {ellf_cli-22.0.2 → 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
- {ellf_cli-22.0.2 → 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
- {ellf_cli-22.0.2 → 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
- {ellf_cli-22.0.2 → 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
- {ellf_cli-22.0.2 → 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
- {ellf_cli-22.0.2 → 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
- {ellf_cli-22.0.2 → 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
- {ellf_cli-22.0.2 → 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
- {ellf_cli-22.0.2 → 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
- {ellf_cli-22.0.2 → 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
- {ellf_cli-22.0.2 → 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
- {ellf_cli-22.0.2 → 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
- {ellf_cli-22.0.2 → 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
- {ellf_cli-22.0.2 → 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
- {ellf_cli-22.0.2 → 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
- {ellf_cli-22.0.2 → 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
- {ellf_cli-22.0.2 → 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
- {ellf_cli-22.0.2 → 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
- {ellf_cli-22.0.2 → 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
- {ellf_cli-22.0.2 → 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
- {ellf_cli-22.0.2 → 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
- {ellf_cli-22.0.2 → 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
- {ellf_cli-22.0.2 → 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
- {ellf_cli-22.0.2 → 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
- {ellf_cli-22.0.2 → 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
- {ellf_cli-22.0.2 → 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
- {ellf_cli-22.0.2 → 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
- {ellf_cli-22.0.2 → 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
- {ellf_cli-22.0.2 → 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
- {ellf_cli-22.0.2 → 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
- {ellf_cli-22.0.2 → 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
- {ellf_cli-22.0.2 → 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
- {ellf_cli-22.0.2 → 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
- {ellf_cli-22.0.2 → 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
- {ellf_cli-22.0.2 → 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
- {ellf_cli-22.0.2 → 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
- {ellf_cli-22.0.2 → ellf_cli-22.0.4}/ellf_cli/version/__init__.py +0 -0
- {ellf_cli-22.0.2 → ellf_cli-22.0.4}/ellf_cli/version/read_version.py +0 -0
- {ellf_cli-22.0.2 → ellf_cli-22.0.4}/ellf_cli.egg-info/dependency_links.txt +0 -0
- {ellf_cli-22.0.2 → ellf_cli-22.0.4}/ellf_cli.egg-info/entry_points.txt +0 -0
- {ellf_cli-22.0.2 → ellf_cli-22.0.4}/ellf_cli.egg-info/not-zip-safe +0 -0
- {ellf_cli-22.0.2 → ellf_cli-22.0.4}/ellf_cli.egg-info/requires.txt +0 -0
- {ellf_cli-22.0.2 → ellf_cli-22.0.4}/ellf_cli.egg-info/top_level.txt +0 -0
- {ellf_cli-22.0.2 → ellf_cli-22.0.4}/pyproject.toml +0 -0
- {ellf_cli-22.0.2 → ellf_cli-22.0.4}/setup.cfg +0 -0
- {ellf_cli-22.0.2 → ellf_cli-22.0.4}/setup.py +0 -0
- {ellf_cli-22.0.2 → ellf_cli-22.0.4}/tests/test_agents.py +0 -0
- {ellf_cli-22.0.2 → ellf_cli-22.0.4}/tests/test_agents_info_stats.py +0 -0
- {ellf_cli-22.0.2 → ellf_cli-22.0.4}/tests/test_appdirs.py +0 -0
- {ellf_cli-22.0.2 → ellf_cli-22.0.4}/tests/test_auth.py +0 -0
- {ellf_cli-22.0.2 → ellf_cli-22.0.4}/tests/test_clusters_info_stats.py +0 -0
- {ellf_cli-22.0.2 → ellf_cli-22.0.4}/tests/test_config.py +0 -0
- {ellf_cli-22.0.2 → ellf_cli-22.0.4}/tests/test_datasets_export.py +0 -0
- {ellf_cli-22.0.2 → ellf_cli-22.0.4}/tests/test_errors.py +0 -0
- {ellf_cli-22.0.2 → ellf_cli-22.0.4}/tests/test_files_cp.py +0 -0
- {ellf_cli-22.0.2 → ellf_cli-22.0.4}/tests/test_files_cp_helpers.py +0 -0
- {ellf_cli-22.0.2 → ellf_cli-22.0.4}/tests/test_helm.py +0 -0
- {ellf_cli-22.0.2 → ellf_cli-22.0.4}/tests/test_import_export.py +0 -0
- {ellf_cli-22.0.2 → ellf_cli-22.0.4}/tests/test_info.py +0 -0
- {ellf_cli-22.0.2 → ellf_cli-22.0.4}/tests/test_infra_config_io.py +0 -0
- {ellf_cli-22.0.2 → ellf_cli-22.0.4}/tests/test_infra_configure.py +0 -0
- {ellf_cli-22.0.2 → ellf_cli-22.0.4}/tests/test_infra_deploy_node_labels.py +0 -0
- {ellf_cli-22.0.2 → ellf_cli-22.0.4}/tests/test_infra_deploy_preflight.py +0 -0
- {ellf_cli-22.0.2 → ellf_cli-22.0.4}/tests/test_infra_deploy_registry_key.py +0 -0
- {ellf_cli-22.0.2 → ellf_cli-22.0.4}/tests/test_infra_destroy.py +0 -0
- {ellf_cli-22.0.2 → ellf_cli-22.0.4}/tests/test_infra_get_terraform.py +0 -0
- {ellf_cli-22.0.2 → ellf_cli-22.0.4}/tests/test_infra_hibernate.py +0 -0
- {ellf_cli-22.0.2 → ellf_cli-22.0.4}/tests/test_infra_pause.py +0 -0
- {ellf_cli-22.0.2 → ellf_cli-22.0.4}/tests/test_infra_provision.py +0 -0
- {ellf_cli-22.0.2 → ellf_cli-22.0.4}/tests/test_infra_register.py +0 -0
- {ellf_cli-22.0.2 → ellf_cli-22.0.4}/tests/test_infra_start_fixes.py +0 -0
- {ellf_cli-22.0.2 → ellf_cli-22.0.4}/tests/test_infra_start_validation.py +0 -0
- {ellf_cli-22.0.2 → ellf_cli-22.0.4}/tests/test_infra_state_bucket.py +0 -0
- {ellf_cli-22.0.2 → ellf_cli-22.0.4}/tests/test_infra_terraform_init.py +0 -0
- {ellf_cli-22.0.2 → ellf_cli-22.0.4}/tests/test_invalid_secrets.py +0 -0
- {ellf_cli-22.0.2 → ellf_cli-22.0.4}/tests/test_job_list_project_filter.py +0 -0
- {ellf_cli-22.0.2 → ellf_cli-22.0.4}/tests/test_jobs_info_hides_creator.py +0 -0
- {ellf_cli-22.0.2 → ellf_cli-22.0.4}/tests/test_jobs_list_unmatched.py +0 -0
- {ellf_cli-22.0.2 → ellf_cli-22.0.4}/tests/test_key_pair.py +0 -0
- {ellf_cli-22.0.2 → ellf_cli-22.0.4}/tests/test_login.py +0 -0
- {ellf_cli-22.0.2 → ellf_cli-22.0.4}/tests/test_logout.py +0 -0
- {ellf_cli-22.0.2 → ellf_cli-22.0.4}/tests/test_main.py +0 -0
- {ellf_cli-22.0.2 → ellf_cli-22.0.4}/tests/test_mcp_proxy.py +0 -0
- {ellf_cli-22.0.2 → ellf_cli-22.0.4}/tests/test_org_select.py +0 -0
- {ellf_cli-22.0.2 → ellf_cli-22.0.4}/tests/test_plans.py +0 -0
- {ellf_cli-22.0.2 → ellf_cli-22.0.4}/tests/test_projects.py +0 -0
- {ellf_cli-22.0.2 → ellf_cli-22.0.4}/tests/test_projects_delete_confirm.py +0 -0
- {ellf_cli-22.0.2 → ellf_cli-22.0.4}/tests/test_publish_build_poll.py +0 -0
- {ellf_cli-22.0.2 → ellf_cli-22.0.4}/tests/test_publish_data.py +0 -0
- {ellf_cli-22.0.2 → ellf_cli-22.0.4}/tests/test_publish_import_root.py +0 -0
- {ellf_cli-22.0.2 → ellf_cli-22.0.4}/tests/test_publish_requirements.py +0 -0
- {ellf_cli-22.0.2 → ellf_cli-22.0.4}/tests/test_publish_versions.py +0 -0
- {ellf_cli-22.0.2 → ellf_cli-22.0.4}/tests/test_pypi_auth.py +0 -0
- {ellf_cli-22.0.2 → ellf_cli-22.0.4}/tests/test_query.py +0 -0
- {ellf_cli-22.0.2 → ellf_cli-22.0.4}/tests/test_recipe_file.py +0 -0
- {ellf_cli-22.0.2 → ellf_cli-22.0.4}/tests/test_recipe_help.py +0 -0
- {ellf_cli-22.0.2 → ellf_cli-22.0.4}/tests/test_recipe_required_args.py +0 -0
- {ellf_cli-22.0.2 → ellf_cli-22.0.4}/tests/test_recipes.py +0 -0
- {ellf_cli-22.0.2 → ellf_cli-22.0.4}/tests/test_recipes_init.py +0 -0
- {ellf_cli-22.0.2 → ellf_cli-22.0.4}/tests/test_sessions.py +0 -0
- {ellf_cli-22.0.2 → ellf_cli-22.0.4}/tests/test_state.py +0 -0
- {ellf_cli-22.0.2 → ellf_cli-22.0.4}/tests/test_support.py +0 -0
- {ellf_cli-22.0.2 → ellf_cli-22.0.4}/tests/test_todos.py +0 -0
- {ellf_cli-22.0.2 → ellf_cli-22.0.4}/tests/test_ty.py +0 -0
- {ellf_cli-22.0.2 → ellf_cli-22.0.4}/tests/test_ui.py +0 -0
- {ellf_cli-22.0.2 → ellf_cli-22.0.4}/tests/test_ui_extras.py +0 -0
- {ellf_cli-22.0.2 → ellf_cli-22.0.4}/tests/test_usage_counts.py +0 -0
- {ellf_cli-22.0.2 → ellf_cli-22.0.4}/tests/test_users_assign.py +0 -0
- {ellf_cli-22.0.2 → ellf_cli-22.0.4}/tests/test_users_info_stats.py +0 -0
- {ellf_cli-22.0.2 → ellf_cli-22.0.4}/tests/test_users_list.py +0 -0
- {ellf_cli-22.0.2 → ellf_cli-22.0.4}/tests/test_util.py +0 -0
|
@@ -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
|
|
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). 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` |
|
|
@@ -720,6 +720,17 @@ Destroy the cluster infrastructure created by 'infra terraform', including every
|
|
|
720
720
|
| `--delete-data` | `bool` | Required: confirms deleting the cluster's database, data bucket and shared volume along with the cluster | `False` |
|
|
721
721
|
| `--tf-auto-accept` | `bool` | Accept the destroy plan without interaction | `False` |
|
|
722
722
|
|
|
723
|
+
#### `ellf infra tls`
|
|
724
|
+
|
|
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
|
+
|
|
727
|
+
| Argument | Type | Description | Default |
|
|
728
|
+
| --- | --- | --- | --- |
|
|
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` |
|
|
732
|
+
| `--output` | `str` | Output format for trust commands: human, json, shell | `'human'` |
|
|
733
|
+
|
|
723
734
|
#### `ellf infra init`
|
|
724
735
|
|
|
725
736
|
Initialize a cluster: register with PAM (allocating a managed domain unless --domain is given), download the terraform files, and generate config.yaml — the input every later phase (configure, provision, start) reads. Idempotent and safe to re-run. After init, the directory is a complete hand-off bundle (terraform files + terraform.tfvars.json + config.yaml) for teams that apply terraform through their own pipeline; they later sync outputs back with 'ellf infra configure --from-terraform' instead of 'ellf infra provision'.
|
|
@@ -838,9 +849,10 @@ Generate a values.yaml for Helm deployment from credentials and provider default
|
|
|
838
849
|
| --- | --- | --- | --- |
|
|
839
850
|
| `--creds` | `Path` | Path to cluster-creds.json from 'infra register' | |
|
|
840
851
|
| `--cloud-provider` | `str` | Cloud provider: gcp, k3s, aws, azure | |
|
|
841
|
-
| `--domain` | `str` | Public address the cluster is reached on, host or host:port (e.g. 'cluster.example.com'
|
|
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. | |
|
|
842
853
|
| `--out` | `Path` | Output path for generated values.yaml | `'values.yaml'` |
|
|
843
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` |
|
|
844
856
|
|
|
845
857
|
#### `ellf infra provision`
|
|
846
858
|
|
|
@@ -874,7 +886,6 @@ Start a cluster on infrastructure configured under your own cloud account.
|
|
|
874
886
|
| `src` | `Path` | Path to cluster definition files | `'.'` |
|
|
875
887
|
| `config_path` | `Path` | Path to config yaml | `None` |
|
|
876
888
|
| `--config-key` | `str` | Top-level key for the cluster config. | `None` |
|
|
877
|
-
| `--offline` | `Path` | Path to a credentials JSON file. Deploys without connecting to PAM. | `None` |
|
|
878
889
|
|
|
879
890
|
### `ellf jobs`
|
|
880
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,
|
|
11
|
-
# trusted
|
|
12
|
-
# 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
|
|
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
|
|
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
|
|
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
|
|
512
|
-
f"{cluster.address}
|
|
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,6 +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
|
|
32
|
+
from .tls import MANAGED_TLS_SECRET_NAME
|
|
31
33
|
|
|
32
34
|
DEFAULT_NAMESPACE = "ellf"
|
|
33
35
|
DEFAULT_RELEASE = "ellf"
|
|
@@ -190,7 +192,12 @@ def deploy(
|
|
|
190
192
|
# 5. Without an ingress controller nothing serves the cluster's domain.
|
|
191
193
|
# k3s bundles Traefik, but Colima (and k3s --disable traefik) turn it off.
|
|
192
194
|
_ensure_ingress_controller(overrides)
|
|
193
|
-
# 6.
|
|
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)
|
|
200
|
+
# 7. Helm upgrade --install
|
|
194
201
|
if chart is None:
|
|
195
202
|
# Use OCI chart from the registry
|
|
196
203
|
if not registry:
|
|
@@ -206,7 +213,7 @@ def deploy(
|
|
|
206
213
|
else:
|
|
207
214
|
msg.info(f"Installing from local chart {chart}...")
|
|
208
215
|
upgrade(chart, overrides, namespace=namespace, wait=wait)
|
|
209
|
-
#
|
|
216
|
+
# 8. Every worker type pins jobs to nodes labeled ellf/node-class=<node_class>.
|
|
210
217
|
# Cloud node pools get that label from terraform; a hand-built cluster (k3s
|
|
211
218
|
# on a workstation) doesn't, and every job then sits Pending with "didn't
|
|
212
219
|
# match Pod's node affinity/selector". Read the types the chart actually
|
|
@@ -214,11 +221,96 @@ def deploy(
|
|
|
214
221
|
# default) and label the sole node on single-node clusters.
|
|
215
222
|
_ensure_worker_type_labels(namespace)
|
|
216
223
|
msg.good("Deployment complete")
|
|
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
|
+
)
|
|
217
231
|
if wait:
|
|
218
232
|
msg.info("Running verification checks...")
|
|
219
233
|
preflight_test(release=DEFAULT_RELEASE, namespace=namespace)
|
|
220
234
|
|
|
221
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
|
+
|
|
222
314
|
def _placeholder_keys(values: object, prefix: str = "") -> list[str]:
|
|
223
315
|
"""Dotted paths of the values ``infra init-values`` left as FILL-IN."""
|
|
224
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,6 +25,7 @@ from .terraform import (
|
|
|
24
25
|
ensure_ingress_ip,
|
|
25
26
|
record_backend,
|
|
26
27
|
)
|
|
28
|
+
from .tls import address_reachability, pam_certifies
|
|
27
29
|
|
|
28
30
|
# Per-provider defaults mirroring infra/cluster/config-*-example.yaml.
|
|
29
31
|
_SYSTEM_POOL_MACHINE = {
|
|
@@ -169,20 +171,47 @@ def init(
|
|
|
169
171
|
# Stage 1 covers the cloud path; the k3s deployment isn't
|
|
170
172
|
# representable in ClusterConfig yet, so hand over to the documented
|
|
171
173
|
# local flow after registration.
|
|
172
|
-
# A cluster on http://localhost needs no certificate
|
|
173
|
-
#
|
|
174
|
-
|
|
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":
|
|
175
193
|
steps = [
|
|
176
|
-
"
|
|
177
|
-
|
|
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",
|
|
178
198
|
]
|
|
179
199
|
else:
|
|
180
200
|
steps = [
|
|
181
201
|
"ellf infra setup --cert-manager",
|
|
182
|
-
"
|
|
183
|
-
f"--cloud-provider k3s --domain {address} --acme-email <email>",
|
|
202
|
+
f"{init_values} --acme-email <email>",
|
|
184
203
|
]
|
|
185
204
|
steps.append("ellf infra deploy --values values.yaml --wait")
|
|
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
|
+
)
|
|
186
215
|
msg.info("Local (k3s) cluster registered. Continue with the local flow:")
|
|
187
216
|
# Plain prints: wasabi re-wraps its messages, which ran the steps
|
|
188
217
|
# together into one paragraph.
|
|
@@ -12,6 +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 MANAGED_TLS_SECRET_NAME, pam_certifies
|
|
15
16
|
|
|
16
17
|
CLOUD_PROVIDERS = ("gcp", "k3s", "aws", "azure")
|
|
17
18
|
|
|
@@ -28,15 +29,29 @@ CLOUD_PROVIDERS = ("gcp", "k3s", "aws", "azure")
|
|
|
28
29
|
"--domain",
|
|
29
30
|
help=(
|
|
30
31
|
"Public address the cluster is reached on, host or host:port "
|
|
31
|
-
"(e.g. 'cluster.example.com'
|
|
32
|
-
"
|
|
33
|
-
"
|
|
32
|
+
"(e.g. 'cluster.example.com'). Use 'http://localhost' for a cluster "
|
|
33
|
+
"on this machine, which needs no certificate and works in every "
|
|
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' — "
|
|
34
39
|
"it flows into ELLF_BROKER_HOST so the cluster emits matching "
|
|
35
40
|
"OIDC / token URLs."
|
|
36
41
|
),
|
|
37
42
|
),
|
|
38
43
|
out=Arg("--out", help="Output path for generated values.yaml"),
|
|
39
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
|
+
),
|
|
40
55
|
)
|
|
41
56
|
def init_values(
|
|
42
57
|
creds: Path,
|
|
@@ -44,6 +59,7 @@ def init_values(
|
|
|
44
59
|
domain: str,
|
|
45
60
|
out: Union[Path, str] = "values.yaml",
|
|
46
61
|
acme_email: Optional[str] = None,
|
|
62
|
+
tls_secret_name: Optional[str] = None,
|
|
47
63
|
) -> None:
|
|
48
64
|
"""Generate a values.yaml for Helm deployment from credentials and provider defaults."""
|
|
49
65
|
from .._state import get_auth_state
|
|
@@ -54,6 +70,18 @@ def init_values(
|
|
|
54
70
|
f"Choose from: {', '.join(CLOUD_PROVIDERS)}"
|
|
55
71
|
)
|
|
56
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)
|
|
57
85
|
if isinstance(out, str):
|
|
58
86
|
out = Path(out)
|
|
59
87
|
creds_data = srsly.read_json(creds)
|
|
@@ -95,6 +123,12 @@ def init_values(
|
|
|
95
123
|
if acme_email:
|
|
96
124
|
values["ingress"] = {"tls": {"secretName": "ellf-tls"}}
|
|
97
125
|
values["certManager"] = {"acmeEmail": acme_email}
|
|
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}
|
|
98
132
|
if cloud_provider == "k3s":
|
|
99
133
|
# State the selector explicitly rather than relying on the chart
|
|
100
134
|
# default — deploy-time node labeling reads this file, not the chart.
|
|
@@ -50,7 +50,11 @@ class Registration(NamedTuple):
|
|
|
50
50
|
"Public address the cluster is reached on, host or host:port "
|
|
51
51
|
"(e.g. 'cluster.example.com' for a hosted cluster on port 443, "
|
|
52
52
|
"or 'http://localhost' for a cluster on this machine, which needs "
|
|
53
|
-
"no certificate and is what the free Solo plan uses
|
|
53
|
+
"no certificate and is what the free Solo plan uses by default; "
|
|
54
|
+
"Safari can't reach it there, so for Safari use "
|
|
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'). "
|
|
54
58
|
"Stored verbatim as the cluster address; the chart derives the "
|
|
55
59
|
"bare hostname for K8s ingress / cert SAN where a port isn't allowed. "
|
|
56
60
|
"Omit to get a managed subdomain under an Explosion-controlled zone."
|
|
@@ -67,9 +71,10 @@ class Registration(NamedTuple):
|
|
|
67
71
|
public_ip=Arg(
|
|
68
72
|
"--public-ip",
|
|
69
73
|
help=(
|
|
70
|
-
"Cluster ingress IP to point the managed subdomain at
|
|
71
|
-
"reserve the name now and re-run with
|
|
72
|
-
"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."
|
|
73
78
|
),
|
|
74
79
|
),
|
|
75
80
|
cloud_provider=Arg(
|
|
@@ -107,7 +112,7 @@ def register(
|
|
|
107
112
|
"""Register a new cluster with PAM and write credentials to cluster-creds.json.
|
|
108
113
|
|
|
109
114
|
Pass --domain to use your own DNS, or omit it to be allocated a managed
|
|
110
|
-
subdomain (with
|
|
115
|
+
subdomain (with an A record) under an Explosion-controlled
|
|
111
116
|
zone. An org on the free Solo plan has no managed subdomain: omitting
|
|
112
117
|
--domain registers it at http://localhost.
|
|
113
118
|
"""
|
|
@@ -183,11 +188,13 @@ def ensure_registered(
|
|
|
183
188
|
),
|
|
184
189
|
None,
|
|
185
190
|
)
|
|
186
|
-
# Solo's one cluster is on localhost
|
|
191
|
+
# Solo's one cluster is on localhost or a private network, and PAM
|
|
192
|
+
# refuses it anything else; localhost is the default.
|
|
187
193
|
if domain is None and _org_is_solo(client, org_id):
|
|
188
194
|
msg.info(Messages.T074)
|
|
189
|
-
# Plain http
|
|
190
|
-
#
|
|
195
|
+
# Plain http needs no certificate, and every browser but Safari lets
|
|
196
|
+
# an https page call http://localhost. Safari users opt into
|
|
197
|
+
# https://localhost with 'ellf infra tls'.
|
|
191
198
|
domain = "http://localhost"
|
|
192
199
|
# With no --domain, register under a placeholder first; the managed
|
|
193
200
|
# subdomain allocation below replaces it with the real FQDN. On
|
|
@@ -249,7 +256,7 @@ def ensure_registered(
|
|
|
249
256
|
allocated_fqdn = allocated.fqdn
|
|
250
257
|
if public_ip:
|
|
251
258
|
msg.good(f"Managed subdomain {allocated.fqdn} -> {public_ip}")
|
|
252
|
-
msg.info(f" Created A
|
|
259
|
+
msg.info(f" Created an A record for {allocated.fqdn}")
|
|
253
260
|
else:
|
|
254
261
|
msg.good(f"Managed subdomain {allocated.fqdn} reserved")
|
|
255
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
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
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
|
-
|
|
108
|
+
auth.client, cluster_detail, ip=lb_ip, hostname=lb_hostname
|
|
155
109
|
)
|
|
156
110
|
else:
|
|
157
111
|
msg.info(
|