ellf-cli 22.0.4__tar.gz → 22.0.6__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.4/ellf_cli.egg-info → ellf_cli-22.0.6}/PKG-INFO +1 -1
- {ellf_cli-22.0.4 → ellf_cli-22.0.6}/README.md +15 -15
- {ellf_cli-22.0.4 → ellf_cli-22.0.6}/ellf_cli/cli.py +40 -3
- {ellf_cli-22.0.4 → ellf_cli-22.0.6}/ellf_cli/cluster_config.py +1 -13
- {ellf_cli-22.0.4 → ellf_cli-22.0.6}/ellf_cli/commands/clusters.py +31 -6
- {ellf_cli-22.0.4 → ellf_cli-22.0.6}/ellf_cli/commands/infra/_helpers.py +51 -44
- {ellf_cli-22.0.4 → ellf_cli-22.0.6}/ellf_cli/commands/infra/deploy.py +119 -50
- {ellf_cli-22.0.4 → ellf_cli-22.0.6}/ellf_cli/commands/infra/init.py +3 -5
- {ellf_cli-22.0.4 → ellf_cli-22.0.6}/ellf_cli/commands/infra/init_values.py +47 -53
- {ellf_cli-22.0.4 → ellf_cli-22.0.6}/ellf_cli/commands/infra/register.py +19 -27
- {ellf_cli-22.0.4 → ellf_cli-22.0.6}/ellf_cli/commands/infra/start.py +8 -12
- {ellf_cli-22.0.4 → ellf_cli-22.0.6}/ellf_cli/commands/plans.py +3 -2
- {ellf_cli-22.0.4 → ellf_cli-22.0.6}/ellf_cli/commands/sessions.py +5 -1
- {ellf_cli-22.0.4 → ellf_cli-22.0.6}/ellf_cli/ellf.json +55 -55
- {ellf_cli-22.0.4 → ellf_cli-22.0.6}/ellf_cli/helm.py +21 -28
- {ellf_cli-22.0.4 → ellf_cli-22.0.6}/ellf_cli/messages.py +2 -1
- {ellf_cli-22.0.4 → ellf_cli-22.0.6}/ellf_cli/ty.py +2 -0
- ellf_cli-22.0.6/ellf_cli/version/22.0.4_2026-11-01-12-41-57_patch_1af4ec08-f257-4eb8-99ba-3ad69b89f4e7 +3 -0
- ellf_cli-22.0.6/ellf_cli/version/22.0.5_2026-11-02-09-07-46_patch_9f15d4c1-bb21-4582-bdc7-b950c6d02f92 +3 -0
- {ellf_cli-22.0.4 → ellf_cli-22.0.6/ellf_cli.egg-info}/PKG-INFO +1 -1
- {ellf_cli-22.0.4 → ellf_cli-22.0.6}/ellf_cli.egg-info/SOURCES.txt +3 -0
- ellf_cli-22.0.6/tests/test_cluster_status.py +46 -0
- ellf_cli-22.0.6/tests/test_infra_deploy_registry_key.py +243 -0
- {ellf_cli-22.0.4 → ellf_cli-22.0.6}/tests/test_infra_init.py +6 -5
- {ellf_cli-22.0.4 → ellf_cli-22.0.6}/tests/test_infra_register.py +16 -20
- {ellf_cli-22.0.4 → ellf_cli-22.0.6}/tests/test_infra_start_fixes.py +10 -14
- {ellf_cli-22.0.4 → ellf_cli-22.0.6}/tests/test_infra_tls.py +10 -18
- {ellf_cli-22.0.4 → ellf_cli-22.0.6}/tests/test_sessions.py +10 -6
- ellf_cli-22.0.4/tests/test_infra_deploy_registry_key.py +0 -46
- {ellf_cli-22.0.4 → ellf_cli-22.0.6}/LICENSE +0 -0
- {ellf_cli-22.0.4 → ellf_cli-22.0.6}/MANIFEST.in +0 -0
- {ellf_cli-22.0.4 → ellf_cli-22.0.6}/ellf_cli/__init__.py +0 -0
- {ellf_cli-22.0.4 → ellf_cli-22.0.6}/ellf_cli/__main__.py +0 -0
- {ellf_cli-22.0.4 → ellf_cli-22.0.6}/ellf_cli/about.json +0 -0
- {ellf_cli-22.0.4 → ellf_cli-22.0.6}/ellf_cli/about.py +0 -0
- {ellf_cli-22.0.4 → ellf_cli-22.0.6}/ellf_cli/agents.py +0 -0
- {ellf_cli-22.0.4 → ellf_cli-22.0.6}/ellf_cli/appdirs.py +0 -0
- {ellf_cli-22.0.4 → ellf_cli-22.0.6}/ellf_cli/auth.py +0 -0
- {ellf_cli-22.0.4 → ellf_cli-22.0.6}/ellf_cli/cloud/__init__.py +0 -0
- {ellf_cli-22.0.4 → ellf_cli-22.0.6}/ellf_cli/cloud/gcp.py +0 -0
- {ellf_cli-22.0.4 → ellf_cli-22.0.6}/ellf_cli/commands/__init__.py +0 -0
- {ellf_cli-22.0.4 → ellf_cli-22.0.6}/ellf_cli/commands/_cluster_select.py +0 -0
- {ellf_cli-22.0.4 → ellf_cli-22.0.6}/ellf_cli/commands/_org_select.py +0 -0
- {ellf_cli-22.0.4 → ellf_cli-22.0.6}/ellf_cli/commands/_project_scope.py +0 -0
- {ellf_cli-22.0.4 → ellf_cli-22.0.6}/ellf_cli/commands/_recipe_file.py +0 -0
- {ellf_cli-22.0.4 → ellf_cli-22.0.6}/ellf_cli/commands/_recipe_subcommand.py +0 -0
- {ellf_cli-22.0.4 → ellf_cli-22.0.6}/ellf_cli/commands/_state.py +0 -0
- {ellf_cli-22.0.4 → ellf_cli-22.0.6}/ellf_cli/commands/_usage.py +0 -0
- {ellf_cli-22.0.4 → ellf_cli-22.0.6}/ellf_cli/commands/actions.py +0 -0
- {ellf_cli-22.0.4 → ellf_cli-22.0.6}/ellf_cli/commands/agents.py +0 -0
- {ellf_cli-22.0.4 → ellf_cli-22.0.6}/ellf_cli/commands/assets.py +0 -0
- {ellf_cli-22.0.4 → ellf_cli-22.0.6}/ellf_cli/commands/config.py +0 -0
- {ellf_cli-22.0.4 → ellf_cli-22.0.6}/ellf_cli/commands/datasets.py +0 -0
- {ellf_cli-22.0.4 → ellf_cli-22.0.6}/ellf_cli/commands/files/__init__.py +0 -0
- {ellf_cli-22.0.4 → ellf_cli-22.0.6}/ellf_cli/commands/files/cp.py +0 -0
- {ellf_cli-22.0.4 → ellf_cli-22.0.6}/ellf_cli/commands/files/ls.py +0 -0
- {ellf_cli-22.0.4 → ellf_cli-22.0.6}/ellf_cli/commands/files/rm.py +0 -0
- {ellf_cli-22.0.4 → ellf_cli-22.0.6}/ellf_cli/commands/files/rsync.py +0 -0
- {ellf_cli-22.0.4 → ellf_cli-22.0.6}/ellf_cli/commands/files/stats.py +0 -0
- {ellf_cli-22.0.4 → ellf_cli-22.0.6}/ellf_cli/commands/general.py +0 -0
- {ellf_cli-22.0.4 → ellf_cli-22.0.6}/ellf_cli/commands/import_export.py +0 -0
- {ellf_cli-22.0.4 → ellf_cli-22.0.6}/ellf_cli/commands/infra/__init__.py +0 -0
- {ellf_cli-22.0.4 → ellf_cli-22.0.6}/ellf_cli/commands/infra/_config_io.py +0 -0
- {ellf_cli-22.0.4 → ellf_cli-22.0.6}/ellf_cli/commands/infra/configure.py +0 -0
- {ellf_cli-22.0.4 → ellf_cli-22.0.6}/ellf_cli/commands/infra/hibernate.py +0 -0
- {ellf_cli-22.0.4 → ellf_cli-22.0.6}/ellf_cli/commands/infra/pause.py +0 -0
- {ellf_cli-22.0.4 → ellf_cli-22.0.6}/ellf_cli/commands/infra/provision.py +0 -0
- {ellf_cli-22.0.4 → ellf_cli-22.0.6}/ellf_cli/commands/infra/setup.py +0 -0
- {ellf_cli-22.0.4 → ellf_cli-22.0.6}/ellf_cli/commands/infra/terraform.py +0 -0
- {ellf_cli-22.0.4 → ellf_cli-22.0.6}/ellf_cli/commands/infra/tls.py +0 -0
- {ellf_cli-22.0.4 → ellf_cli-22.0.6}/ellf_cli/commands/jobs.py +0 -0
- {ellf_cli-22.0.4 → ellf_cli-22.0.6}/ellf_cli/commands/mcp.py +0 -0
- {ellf_cli-22.0.4 → ellf_cli-22.0.6}/ellf_cli/commands/packages.py +0 -0
- {ellf_cli-22.0.4 → ellf_cli-22.0.6}/ellf_cli/commands/paths.py +0 -0
- {ellf_cli-22.0.4 → ellf_cli-22.0.6}/ellf_cli/commands/projects.py +0 -0
- {ellf_cli-22.0.4 → ellf_cli-22.0.6}/ellf_cli/commands/publish_code.py +0 -0
- {ellf_cli-22.0.4 → ellf_cli-22.0.6}/ellf_cli/commands/publish_data.py +0 -0
- {ellf_cli-22.0.4 → ellf_cli-22.0.6}/ellf_cli/commands/recipes.py +0 -0
- {ellf_cli-22.0.4 → ellf_cli-22.0.6}/ellf_cli/commands/secrets.py +0 -0
- {ellf_cli-22.0.4 → ellf_cli-22.0.6}/ellf_cli/commands/services.py +0 -0
- {ellf_cli-22.0.4 → ellf_cli-22.0.6}/ellf_cli/commands/support.py +0 -0
- {ellf_cli-22.0.4 → ellf_cli-22.0.6}/ellf_cli/commands/tasks.py +0 -0
- {ellf_cli-22.0.4 → ellf_cli-22.0.6}/ellf_cli/commands/todos.py +0 -0
- {ellf_cli-22.0.4 → ellf_cli-22.0.6}/ellf_cli/commands/users.py +0 -0
- {ellf_cli-22.0.4 → ellf_cli-22.0.6}/ellf_cli/config.py +0 -0
- {ellf_cli-22.0.4 → ellf_cli-22.0.6}/ellf_cli/ellf_skills/.gitignore +0 -0
- {ellf_cli-22.0.4 → ellf_cli-22.0.6}/ellf_cli/ellf_skills/render.py +0 -0
- {ellf_cli-22.0.4 → ellf_cli-22.0.6}/ellf_cli/ellf_skills/skills/ellf-annotate/SKILL.assistant.md +0 -0
- {ellf_cli-22.0.4 → ellf_cli-22.0.6}/ellf_cli/ellf_skills/skills/ellf-annotate/SKILL.coding.md +0 -0
- {ellf_cli-22.0.4 → ellf_cli-22.0.6}/ellf_cli/ellf_skills/skills/ellf-annotate/references/annotation_audit.assistant.md +0 -0
- {ellf_cli-22.0.4 → ellf_cli-22.0.6}/ellf_cli/ellf_skills/skills/ellf-annotate/references/annotation_audit.coding.md +0 -0
- {ellf_cli-22.0.4 → ellf_cli-22.0.6}/ellf_cli/ellf_skills/skills/ellf-annotate/references/builtin_ellf_annotation_recipes.assistant.md +0 -0
- {ellf_cli-22.0.4 → ellf_cli-22.0.6}/ellf_cli/ellf_skills/skills/ellf-annotate/references/builtin_ellf_annotation_recipes.coding.md +0 -0
- {ellf_cli-22.0.4 → ellf_cli-22.0.6}/ellf_cli/ellf_skills/skills/ellf-annotate/references/builtin_prodigy_recipes.coding.md +0 -0
- {ellf_cli-22.0.4 → ellf_cli-22.0.6}/ellf_cli/ellf_skills/skills/ellf-annotate/references/label_schemes.assistant.md +0 -0
- {ellf_cli-22.0.4 → ellf_cli-22.0.6}/ellf_cli/ellf_skills/skills/ellf-annotate/references/label_schemes.coding.md +0 -0
- {ellf_cli-22.0.4 → ellf_cli-22.0.6}/ellf_cli/ellf_skills/skills/ellf-ask/SKILL.md +0 -0
- {ellf_cli-22.0.4 → ellf_cli-22.0.6}/ellf_cli/ellf_skills/skills/ellf-handoff/SKILL.assistant.md +0 -0
- {ellf_cli-22.0.4 → ellf_cli-22.0.6}/ellf_cli/ellf_skills/skills/ellf-monitor/SKILL.assistant.md +0 -0
- {ellf_cli-22.0.4 → ellf_cli-22.0.6}/ellf_cli/ellf_skills/skills/ellf-monitor/SKILL.coding.md +0 -0
- {ellf_cli-22.0.4 → ellf_cli-22.0.6}/ellf_cli/ellf_skills/skills/ellf-monitor/references/annotation_metrics.assistant.md +0 -0
- {ellf_cli-22.0.4 → ellf_cli-22.0.6}/ellf_cli/ellf_skills/skills/ellf-monitor/references/annotation_metrics.coding.md +0 -0
- {ellf_cli-22.0.4 → ellf_cli-22.0.6}/ellf_cli/ellf_skills/skills/ellf-monitor/references/training_monitoring.assistant.md +0 -0
- {ellf_cli-22.0.4 → ellf_cli-22.0.6}/ellf_cli/ellf_skills/skills/ellf-monitor/references/training_monitoring.coding.md +0 -0
- {ellf_cli-22.0.4 → ellf_cli-22.0.6}/ellf_cli/ellf_skills/skills/ellf-monitor/scripts.coding/check_training.py +0 -0
- {ellf_cli-22.0.4 → ellf_cli-22.0.6}/ellf_cli/ellf_skills/skills/ellf-ops/SKILL.assistant.md +0 -0
- {ellf_cli-22.0.4 → ellf_cli-22.0.6}/ellf_cli/ellf_skills/skills/ellf-ops/SKILL.coding.md +0 -0
- {ellf_cli-22.0.4 → ellf_cli-22.0.6}/ellf_cli/ellf_skills/skills/ellf-ops/references.coding/data_infra_cli.md +0 -0
- {ellf_cli-22.0.4 → ellf_cli-22.0.6}/ellf_cli/ellf_skills/skills/ellf-ops/scripts.coding/run_job.py +0 -0
- {ellf_cli-22.0.4 → ellf_cli-22.0.6}/ellf_cli/ellf_skills/skills/ellf-patterns/SKILL.coding.md +0 -0
- {ellf_cli-22.0.4 → ellf_cli-22.0.6}/ellf_cli/ellf_skills/skills/ellf-patterns/references/pattern_strategies.md +0 -0
- {ellf_cli-22.0.4 → ellf_cli-22.0.6}/ellf_cli/ellf_skills/skills/ellf-prodigy/SKILL.coding.md +0 -0
- {ellf_cli-22.0.4 → ellf_cli-22.0.6}/ellf_cli/ellf_skills/skills/ellf-prodigy/assets/templates/template_action_recipe.py +0 -0
- {ellf_cli-22.0.4 → ellf_cli-22.0.6}/ellf_cli/ellf_skills/skills/ellf-prodigy/assets/templates/template_agent_recipe.py +0 -0
- {ellf_cli-22.0.4 → ellf_cli-22.0.6}/ellf_cli/ellf_skills/skills/ellf-prodigy/assets/templates/template_blocks_ui.py +0 -0
- {ellf_cli-22.0.4 → ellf_cli-22.0.6}/ellf_cli/ellf_skills/skills/ellf-prodigy/assets/templates/template_correct.py +0 -0
- {ellf_cli-22.0.4 → ellf_cli-22.0.6}/ellf_cli/ellf_skills/skills/ellf-prodigy/assets/templates/template_custom_ui.py +0 -0
- {ellf_cli-22.0.4 → ellf_cli-22.0.6}/ellf_cli/ellf_skills/skills/ellf-prodigy/assets/templates/template_manual.py +0 -0
- {ellf_cli-22.0.4 → ellf_cli-22.0.6}/ellf_cli/ellf_skills/skills/ellf-prodigy/assets/templates/template_pages_ui.py +0 -0
- {ellf_cli-22.0.4 → ellf_cli-22.0.6}/ellf_cli/ellf_skills/skills/ellf-prodigy/assets/templates/template_routing.py +0 -0
- {ellf_cli-22.0.4 → ellf_cli-22.0.6}/ellf_cli/ellf_skills/skills/ellf-prodigy/assets/templates/template_service_recipe.py +0 -0
- {ellf_cli-22.0.4 → ellf_cli-22.0.6}/ellf_cli/ellf_skills/skills/ellf-prodigy/assets/templates/template_task_recipe.py +0 -0
- {ellf_cli-22.0.4 → ellf_cli-22.0.6}/ellf_cli/ellf_skills/skills/ellf-prodigy/assets/templates/template_teach.py +0 -0
- {ellf_cli-22.0.4 → ellf_cli-22.0.6}/ellf_cli/ellf_skills/skills/ellf-prodigy/references/builtin_recipes.md +0 -0
- {ellf_cli-22.0.4 → ellf_cli-22.0.6}/ellf_cli/ellf_skills/skills/ellf-prodigy/references/ellf_recipe_sdk.md +0 -0
- {ellf_cli-22.0.4 → ellf_cli-22.0.6}/ellf_cli/ellf_skills/skills/ellf-prodigy/references/lint_recipe.py +0 -0
- {ellf_cli-22.0.4 → ellf_cli-22.0.6}/ellf_cli/ellf_skills/skills/ellf-prodigy/references/prodigy_recipe_api.md +0 -0
- {ellf_cli-22.0.4 → ellf_cli-22.0.6}/ellf_cli/ellf_skills/skills/ellf-prodigy/references/template_index.md +0 -0
- {ellf_cli-22.0.4 → ellf_cli-22.0.6}/ellf_cli/ellf_skills/skills/ellf-project/SKILL.assistant.md +0 -0
- {ellf_cli-22.0.4 → ellf_cli-22.0.6}/ellf_cli/ellf_skills/skills/ellf-project/SKILL.coding.md +0 -0
- {ellf_cli-22.0.4 → ellf_cli-22.0.6}/ellf_cli/ellf_skills/skills/ellf-project/references/annotation_agents.md +0 -0
- {ellf_cli-22.0.4 → ellf_cli-22.0.6}/ellf_cli/ellf_skills/skills/ellf-project/references/consulting_patterns.md +0 -0
- {ellf_cli-22.0.4 → ellf_cli-22.0.6}/ellf_cli/ellf_skills/skills/ellf-project/references/explosion_strategy.md +0 -0
- {ellf_cli-22.0.4 → ellf_cli-22.0.6}/ellf_cli/ellf_skills/skills/ellf-project/references/jev.coding.md +0 -0
- {ellf_cli-22.0.4 → ellf_cli-22.0.6}/ellf_cli/ellf_skills/skills/ellf-project/references/label_schemes.assistant.md +0 -0
- {ellf_cli-22.0.4 → ellf_cli-22.0.6}/ellf_cli/ellf_skills/skills/ellf-project/references/label_schemes.coding.md +0 -0
- {ellf_cli-22.0.4 → ellf_cli-22.0.6}/ellf_cli/ellf_skills/skills/ellf-support/SKILL.assistant.md +0 -0
- {ellf_cli-22.0.4 → ellf_cli-22.0.6}/ellf_cli/ellf_skills/skills/ellf-support/SKILL.coding.md +0 -0
- {ellf_cli-22.0.4 → ellf_cli-22.0.6}/ellf_cli/ellf_skills/skills/ellf-template/SKILL.coding.md +0 -0
- {ellf_cli-22.0.4 → ellf_cli-22.0.6}/ellf_cli/ellf_skills/skills/ellf-template/references/template_format.md +0 -0
- {ellf_cli-22.0.4 → ellf_cli-22.0.6}/ellf_cli/ellf_skills/skills/ellf-template/scripts/placeholders.py +0 -0
- {ellf_cli-22.0.4 → ellf_cli-22.0.6}/ellf_cli/ellf_skills/skills/ellf-todo/SKILL.coding.md +0 -0
- {ellf_cli-22.0.4 → ellf_cli-22.0.6}/ellf_cli/ellf_skills/skills/ellf-train/SKILL.assistant.md +0 -0
- {ellf_cli-22.0.4 → ellf_cli-22.0.6}/ellf_cli/ellf_skills/skills/ellf-train/SKILL.coding.md +0 -0
- {ellf_cli-22.0.4 → ellf_cli-22.0.6}/ellf_cli/ellf_skills/skills/ellf-train/references/config_advanced.coding.md +0 -0
- {ellf_cli-22.0.4 → ellf_cli-22.0.6}/ellf_cli/ellf_skills/skills/ellf-train/references/config_architectures.coding.md +0 -0
- {ellf_cli-22.0.4 → ellf_cli-22.0.6}/ellf_cli/ellf_skills/skills/ellf-train/references/config_training.coding.md +0 -0
- {ellf_cli-22.0.4 → ellf_cli-22.0.6}/ellf_cli/ellf_skills/skills/ellf-train/references/diagnostics.md +0 -0
- {ellf_cli-22.0.4 → ellf_cli-22.0.6}/ellf_cli/ellf_skills/skills/ellf-train/references/evaluation_guide.md +0 -0
- {ellf_cli-22.0.4 → ellf_cli-22.0.6}/ellf_cli/ellf_skills/skills/ellf-train/references/experiment_patterns.coding.md +0 -0
- {ellf_cli-22.0.4 → ellf_cli-22.0.6}/ellf_cli/ellf_skills/skills/ellf-train/references/model_selection.md +0 -0
- {ellf_cli-22.0.4 → ellf_cli-22.0.6}/ellf_cli/ellf_skills/skills/ellf-train/references/training_paradigms.md +0 -0
- {ellf_cli-22.0.4 → ellf_cli-22.0.6}/ellf_cli/ellf_skills/skills/ellf-train/references/training_troubleshooting.coding.md +0 -0
- {ellf_cli-22.0.4 → ellf_cli-22.0.6}/ellf_cli/ellf_skills/skills/ellf-train/references/workflow.assistant.md +0 -0
- {ellf_cli-22.0.4 → ellf_cli-22.0.6}/ellf_cli/ellf_skills/skills/ellf-train/references/workflow.coding.md +0 -0
- {ellf_cli-22.0.4 → ellf_cli-22.0.6}/ellf_cli/ellf_skills/skills/ellf-train/scripts.coding/ellf_logger.py +0 -0
- {ellf_cli-22.0.4 → ellf_cli-22.0.6}/ellf_cli/errors.py +0 -0
- {ellf_cli-22.0.4 → ellf_cli-22.0.6}/ellf_cli/interactivity.py +0 -0
- {ellf_cli-22.0.4 → ellf_cli-22.0.6}/ellf_cli/key_pair.py +0 -0
- {ellf_cli-22.0.4 → ellf_cli-22.0.6}/ellf_cli/main.py +0 -0
- {ellf_cli-22.0.4 → ellf_cli-22.0.6}/ellf_cli/mcp_proxy.py +0 -0
- {ellf_cli-22.0.4 → ellf_cli-22.0.6}/ellf_cli/pypi_auth.py +0 -0
- {ellf_cli-22.0.4 → ellf_cli-22.0.6}/ellf_cli/query.py +0 -0
- {ellf_cli-22.0.4 → ellf_cli-22.0.6}/ellf_cli/recipes_cookiecutter/cookiecutter.json +0 -0
- {ellf_cli-22.0.4 → ellf_cli-22.0.6}/ellf_cli/recipes_cookiecutter/{{cookiecutter.package_dir}}/.gitignore +0 -0
- {ellf_cli-22.0.4 → ellf_cli-22.0.6}/ellf_cli/recipes_cookiecutter/{{cookiecutter.package_dir}}/README.md.tmpl +0 -0
- {ellf_cli-22.0.4 → ellf_cli-22.0.6}/ellf_cli/recipes_cookiecutter/{{cookiecutter.package_dir}}/requirements-dev.in +0 -0
- {ellf_cli-22.0.4 → ellf_cli-22.0.6}/ellf_cli/recipes_cookiecutter/{{cookiecutter.package_dir}}/requirements.in +0 -0
- {ellf_cli-22.0.4 → ellf_cli-22.0.6}/ellf_cli/recipes_cookiecutter/{{cookiecutter.package_dir}}/setup.py.tmpl +0 -0
- {ellf_cli-22.0.4 → ellf_cli-22.0.6}/ellf_cli/recipes_cookiecutter/{{cookiecutter.package_dir}}/{{cookiecutter.package_name}}/__init__.py +0 -0
- {ellf_cli-22.0.4 → ellf_cli-22.0.6}/ellf_cli/recipes_cookiecutter/{{cookiecutter.package_dir}}/{{cookiecutter.package_name}}/about.py +0 -0
- {ellf_cli-22.0.4 → ellf_cli-22.0.6}/ellf_cli/recipes_cookiecutter/{{cookiecutter.package_dir}}/{{cookiecutter.package_name}}/recipes/__init__.py +0 -0
- {ellf_cli-22.0.4 → ellf_cli-22.0.6}/ellf_cli/recipes_cookiecutter/{{cookiecutter.package_dir}}/{{cookiecutter.package_name}}/recipes/example_task.py +0 -0
- {ellf_cli-22.0.4 → ellf_cli-22.0.6}/ellf_cli/testing/__init__.py +0 -0
- {ellf_cli-22.0.4 → ellf_cli-22.0.6}/ellf_cli/ui.py +0 -0
- {ellf_cli-22.0.4 → ellf_cli-22.0.6}/ellf_cli/url.py +0 -0
- {ellf_cli-22.0.4 → ellf_cli-22.0.6}/ellf_cli/util.py +0 -0
- {ellf_cli-22.0.4 → ellf_cli-22.0.6}/ellf_cli/version/17.0.114_2026-08-19-08-36-57__1a4e37c1-471e-4a81-b4d5-1ffe7dec3a61 +0 -0
- {ellf_cli-22.0.4 → ellf_cli-22.0.6}/ellf_cli/version/17.0.114_2026-08-19-09-25-21_patch_33ff1e26-0e5e-46d8-8052-ab1d032e8b6c +0 -0
- {ellf_cli-22.0.4 → ellf_cli-22.0.6}/ellf_cli/version/17.0.115_2026-08-19-09-27-13_patch_4ac728c3-0342-4a36-adbc-e28d2d99b1ba +0 -0
- {ellf_cli-22.0.4 → ellf_cli-22.0.6}/ellf_cli/version/17.0.116_2026-08-19-09-30-47_patch_c706fef6-6813-4183-b268-12e7b00d7d0b +0 -0
- {ellf_cli-22.0.4 → ellf_cli-22.0.6}/ellf_cli/version/17.0.117_2026-08-19-09-39-14_patch_c6b0495f-2f88-4aa8-9ebf-0a235b4c407e +0 -0
- {ellf_cli-22.0.4 → ellf_cli-22.0.6}/ellf_cli/version/17.0.118_2026-08-19-09-52-00_patch_b395655a-8d78-4344-9266-6901c20f6517 +0 -0
- {ellf_cli-22.0.4 → ellf_cli-22.0.6}/ellf_cli/version/17.0.119_2026-08-19-10-22-38_patch_8f95c49d-aedc-41b8-816d-ccb95f048831 +0 -0
- {ellf_cli-22.0.4 → ellf_cli-22.0.6}/ellf_cli/version/17.0.120_2026-08-19-10-26-58_patch_7fbf35e6-cc01-4b75-8c9a-f5d054bcaf93 +0 -0
- {ellf_cli-22.0.4 → ellf_cli-22.0.6}/ellf_cli/version/17.0.121_2026-08-19-10-42-02_patch_31a81b02-a886-4dea-84e0-67cc6ca412d5 +0 -0
- {ellf_cli-22.0.4 → ellf_cli-22.0.6}/ellf_cli/version/17.0.122_2026-08-19-11-16-49_patch_1c4d7307-48d4-493e-936d-80af799d644b +0 -0
- {ellf_cli-22.0.4 → ellf_cli-22.0.6}/ellf_cli/version/17.0.123_2026-08-19-11-47-32_patch_d3d78d04-9989-45b1-91b1-93b571d8814a +0 -0
- {ellf_cli-22.0.4 → ellf_cli-22.0.6}/ellf_cli/version/17.0.124_2026-08-20-08-03-17_patch_57f4c389-2d19-41ce-aca4-13b61c62f387 +0 -0
- {ellf_cli-22.0.4 → ellf_cli-22.0.6}/ellf_cli/version/17.0.125_2026-08-20-08-25-05_patch_7f368148-0d1c-4525-bbdf-c6d257ad861a +0 -0
- {ellf_cli-22.0.4 → ellf_cli-22.0.6}/ellf_cli/version/17.0.126_2026-08-20-14-51-36_patch_a99abcc8-3371-4c63-a759-bb7ea5b32432 +0 -0
- {ellf_cli-22.0.4 → ellf_cli-22.0.6}/ellf_cli/version/17.0.127_2026-08-21-05-48-58_patch_34b4ccee-7596-4f57-93e9-774973cca58e +0 -0
- {ellf_cli-22.0.4 → ellf_cli-22.0.6}/ellf_cli/version/17.0.128_2026-08-21-10-47-20_patch_693112b9-927c-4a8f-8553-b06eef86079d +0 -0
- {ellf_cli-22.0.4 → ellf_cli-22.0.6}/ellf_cli/version/17.0.129_2026-08-21-11-16-54_patch_7a981666-01d9-4d1b-9ba3-2129744e3bd5 +0 -0
- {ellf_cli-22.0.4 → ellf_cli-22.0.6}/ellf_cli/version/17.0.130_2026-08-21-13-09-36_patch_b7ad1a76-ce70-4d70-9d1a-7c414eba5bb4 +0 -0
- {ellf_cli-22.0.4 → ellf_cli-22.0.6}/ellf_cli/version/17.0.131_2026-08-21-14-31-10_patch_9128ef66-193e-4caf-8710-afd86128e638 +0 -0
- {ellf_cli-22.0.4 → ellf_cli-22.0.6}/ellf_cli/version/17.0.132_2026-08-24-08-59-34_patch_c600fc14-86a5-460f-9c4e-f0177056f756 +0 -0
- {ellf_cli-22.0.4 → ellf_cli-22.0.6}/ellf_cli/version/17.0.133_2026-08-24-10-28-29_patch_b6ce3e32-9d7b-48ac-981a-270c22c180dd +0 -0
- {ellf_cli-22.0.4 → ellf_cli-22.0.6}/ellf_cli/version/17.0.134_2026-08-24-11-00-51_patch_a4fc0484-4a64-46a2-94f8-aa805254c7b0 +0 -0
- {ellf_cli-22.0.4 → ellf_cli-22.0.6}/ellf_cli/version/17.0.135_2026-08-24-12-12-41_patch_a870a56f-9a65-49eb-92da-f572b26667d8 +0 -0
- {ellf_cli-22.0.4 → ellf_cli-22.0.6}/ellf_cli/version/17.0.136_2026-08-24-12-42-46_patch_9b399356-6482-4291-8da7-e748a99c4b53 +0 -0
- {ellf_cli-22.0.4 → ellf_cli-22.0.6}/ellf_cli/version/17.0.137_2026-08-24-12-53-57_patch_f7979348-1910-40cd-a535-90299eca84ee +0 -0
- {ellf_cli-22.0.4 → ellf_cli-22.0.6}/ellf_cli/version/17.0.138_2026-08-24-14-36-30_patch_ed0f8750-f663-498b-85e7-0d3f2213fcfe +0 -0
- {ellf_cli-22.0.4 → ellf_cli-22.0.6}/ellf_cli/version/17.0.139_2026-08-25-08-37-26_patch_3ba87e19-902b-4f76-b64c-90e5c0ca1fc6 +0 -0
- {ellf_cli-22.0.4 → ellf_cli-22.0.6}/ellf_cli/version/17.0.140_2026-08-25-09-52-45_patch_abc830f2-df15-4385-b797-cdaf72c10752 +0 -0
- {ellf_cli-22.0.4 → ellf_cli-22.0.6}/ellf_cli/version/17.0.141_2026-08-25-11-37-20_patch_1d9ec01e-06f6-4c8e-b095-af17d5b62b00 +0 -0
- {ellf_cli-22.0.4 → ellf_cli-22.0.6}/ellf_cli/version/17.0.142_2026-08-26-07-38-49_patch_3dd23e36-e7df-494c-986f-a412861a8d40 +0 -0
- {ellf_cli-22.0.4 → ellf_cli-22.0.6}/ellf_cli/version/17.0.143_2026-08-26-09-32-32_patch_7b7ebd34-de27-4b73-98a7-3e1501e16cd5 +0 -0
- {ellf_cli-22.0.4 → ellf_cli-22.0.6}/ellf_cli/version/17.0.144_2026-08-26-10-56-43_patch_166064f2-2208-4c9b-9e25-25dc4cd32084 +0 -0
- {ellf_cli-22.0.4 → ellf_cli-22.0.6}/ellf_cli/version/17.0.145_2026-08-26-11-37-29_patch_b997f3ee-d7d3-4408-9f2f-c8b6d08bdb3b +0 -0
- {ellf_cli-22.0.4 → ellf_cli-22.0.6}/ellf_cli/version/17.0.146_2026-08-26-12-15-20_patch_afbbb893-25db-468c-81db-2663ca373068 +0 -0
- {ellf_cli-22.0.4 → ellf_cli-22.0.6}/ellf_cli/version/17.0.147_2026-08-26-14-10-50_patch_fd753b1f-2fa1-4ddb-a9e7-de690da85254 +0 -0
- {ellf_cli-22.0.4 → ellf_cli-22.0.6}/ellf_cli/version/17.0.148_2026-08-27-09-08-35_patch_23814c0b-7f77-489c-b4a8-ceadd18146e3 +0 -0
- {ellf_cli-22.0.4 → ellf_cli-22.0.6}/ellf_cli/version/17.0.149_2026-08-27-09-40-17_patch_8879d72e-a3d8-4abc-a16a-d9dcf75f0fa1 +0 -0
- {ellf_cli-22.0.4 → ellf_cli-22.0.6}/ellf_cli/version/17.0.150_2026-08-27-10-57-25_patch_afbe60ed-3e23-4d88-be3c-f800d7002a7d +0 -0
- {ellf_cli-22.0.4 → ellf_cli-22.0.6}/ellf_cli/version/17.0.151_2026-08-28-09-02-46_patch_1d4ca23b-aeae-4ea8-96b1-8cf57371f29d +0 -0
- {ellf_cli-22.0.4 → ellf_cli-22.0.6}/ellf_cli/version/17.0.152_2026-08-28-15-37-24_patch_c9d0bdc1-a7e1-432b-92b9-92dc76fe0c78 +0 -0
- {ellf_cli-22.0.4 → ellf_cli-22.0.6}/ellf_cli/version/17.0.153_2026-08-29-09-03-27_patch_7531573a-1b33-4092-aa9c-0e65f47b201f +0 -0
- {ellf_cli-22.0.4 → ellf_cli-22.0.6}/ellf_cli/version/17.0.154_2026-08-29-11-56-35_patch_919f5787-39c8-4dc1-ae6a-f27c28380f21 +0 -0
- {ellf_cli-22.0.4 → ellf_cli-22.0.6}/ellf_cli/version/17.0.155_2026-08-29-15-15-59_patch_822f9ed8-c366-4e5f-9791-dee8467cf3ce +0 -0
- {ellf_cli-22.0.4 → ellf_cli-22.0.6}/ellf_cli/version/17.0.156_2026-08-29-16-19-01_patch_3ac2bf36-20f7-4c9f-9fdb-295d4466d59e +0 -0
- {ellf_cli-22.0.4 → ellf_cli-22.0.6}/ellf_cli/version/17.0.157_2026-08-30-17-32-17_patch_4302354c-c9c5-4be7-acd3-542872a748d8 +0 -0
- {ellf_cli-22.0.4 → ellf_cli-22.0.6}/ellf_cli/version/17.0.158_2026-08-31-09-28-33_patch_3523774f-c25d-4102-8c1a-92fff046a48d +0 -0
- {ellf_cli-22.0.4 → ellf_cli-22.0.6}/ellf_cli/version/17.0.159_2026-08-31-10-31-01_patch_fe3e6290-2553-4154-a0a0-8b8a418280ed +0 -0
- {ellf_cli-22.0.4 → ellf_cli-22.0.6}/ellf_cli/version/17.0.160_2026-08-31-11-23-39_patch_e701a085-3d23-4212-b0af-73b1d419a4c1 +0 -0
- {ellf_cli-22.0.4 → ellf_cli-22.0.6}/ellf_cli/version/17.0.161_2026-08-31-12-41-01_patch_074d7bde-c493-44db-bc15-e81fd91e2921 +0 -0
- {ellf_cli-22.0.4 → ellf_cli-22.0.6}/ellf_cli/version/17.0.162_2026-08-31-13-58-06_patch_84db1d60-4280-433b-bd3a-22e5611cd315 +0 -0
- {ellf_cli-22.0.4 → ellf_cli-22.0.6}/ellf_cli/version/17.0.163_2026-08-31-15-25-54_patch_1931c5ba-73df-48cb-8dcb-555ace0af267 +0 -0
- {ellf_cli-22.0.4 → ellf_cli-22.0.6}/ellf_cli/version/17.0.163_2026-08-31-15-44-37_patch_3962e805-2a92-494a-a87b-b6e6d66c40a0 +0 -0
- {ellf_cli-22.0.4 → ellf_cli-22.0.6}/ellf_cli/version/17.0.165_2026-08-31-19-48-59_patch_86e5b923-2b99-4b86-b542-469b197614be +0 -0
- {ellf_cli-22.0.4 → ellf_cli-22.0.6}/ellf_cli/version/17.0.166_2026-09-01-07-39-57_patch_c344892f-d542-4e7b-8f27-02365a411e15 +0 -0
- {ellf_cli-22.0.4 → ellf_cli-22.0.6}/ellf_cli/version/17.0.167_2026-09-01-08-21-53_patch_e199d419-282a-4dbb-8756-cf98b7037e2b +0 -0
- {ellf_cli-22.0.4 → ellf_cli-22.0.6}/ellf_cli/version/17.0.168_2026-09-01-09-12-31_patch_235c9fe0-da15-431d-bb56-01e2b9b15f4b +0 -0
- {ellf_cli-22.0.4 → ellf_cli-22.0.6}/ellf_cli/version/17.0.169_2026-09-01-11-54-19_patch_98b617ab-d130-42b5-93f2-19cae56d4c4c +0 -0
- {ellf_cli-22.0.4 → ellf_cli-22.0.6}/ellf_cli/version/17.0.170_2026-09-02-09-27-28_patch_e448d2e6-b7ec-4235-96bf-0173b3623c87 +0 -0
- {ellf_cli-22.0.4 → ellf_cli-22.0.6}/ellf_cli/version/17.0.171_2026-09-02-12-52-44_patch_e6a241c2-1d62-482c-a9eb-9e3373395c50 +0 -0
- {ellf_cli-22.0.4 → ellf_cli-22.0.6}/ellf_cli/version/17.0.172_2026-09-04-08-47-04_patch_daae079f-c104-4fcd-bb57-7bff68e80ad9 +0 -0
- {ellf_cli-22.0.4 → ellf_cli-22.0.6}/ellf_cli/version/17.0.173_2026-09-04-12-34-42_patch_399c4cbd-0c28-4dfd-a4ec-e569420a36e4 +0 -0
- {ellf_cli-22.0.4 → ellf_cli-22.0.6}/ellf_cli/version/17.0.174_2026-09-04-13-08-48_patch_f5cfe3d5-808e-45e4-bafc-00d0554838ee +0 -0
- {ellf_cli-22.0.4 → ellf_cli-22.0.6}/ellf_cli/version/17.0.175_2026-09-04-17-20-46_patch_1ef542fa-5d8e-4d90-aa15-9fc4cea3dfec +0 -0
- {ellf_cli-22.0.4 → ellf_cli-22.0.6}/ellf_cli/version/17.0.176_2026-09-05-08-25-05_patch_ced5521f-f9da-4640-a921-95fcb5cd2e6d +0 -0
- {ellf_cli-22.0.4 → ellf_cli-22.0.6}/ellf_cli/version/17.0.177_2026-09-05-09-00-16_patch_803c1ad4-a9af-4953-92c5-d882dec03ea2 +0 -0
- {ellf_cli-22.0.4 → ellf_cli-22.0.6}/ellf_cli/version/17.0.178_2026-09-05-09-30-34_patch_012f6259-2e3f-4591-9806-1920fd06cb1f +0 -0
- {ellf_cli-22.0.4 → ellf_cli-22.0.6}/ellf_cli/version/17.0.179_2026-09-05-09-33-43_patch_f4bdf28c-d584-47a5-b699-8c883330162c +0 -0
- {ellf_cli-22.0.4 → ellf_cli-22.0.6}/ellf_cli/version/17.0.180_2026-09-05-12-02-21_patch_34a14b01-d22d-44a8-ac63-be0370a61422 +0 -0
- {ellf_cli-22.0.4 → ellf_cli-22.0.6}/ellf_cli/version/17.0.181_2026-09-05-12-47-46_patch_56b18867-99f6-4d5c-8215-7b9d60d34010 +0 -0
- {ellf_cli-22.0.4 → ellf_cli-22.0.6}/ellf_cli/version/17.0.182_2026-09-05-13-35-18_patch_61b7d4d1-73c8-4603-9b0c-593e7405587f +0 -0
- {ellf_cli-22.0.4 → ellf_cli-22.0.6}/ellf_cli/version/17.0.183_2026-09-06-07-27-09_patch_d76331b2-e3a3-4c0b-8412-ecd131b6ac02 +0 -0
- {ellf_cli-22.0.4 → ellf_cli-22.0.6}/ellf_cli/version/17.0.184_2026-09-06-10-21-47_patch_7680b7a1-49f0-4eef-b12e-4c854407448c +0 -0
- {ellf_cli-22.0.4 → ellf_cli-22.0.6}/ellf_cli/version/17.0.185_2026-09-06-10-30-40_patch_e56c2183-77f8-44ef-a92e-7377ea6ce8c1 +0 -0
- {ellf_cli-22.0.4 → ellf_cli-22.0.6}/ellf_cli/version/17.0.186_2026-09-07-10-00-51_patch_67de5683-d208-4bc0-8fb6-7677377908e7 +0 -0
- {ellf_cli-22.0.4 → ellf_cli-22.0.6}/ellf_cli/version/17.0.187_2026-09-07-10-18-13_patch_2086cee2-276b-48f7-80ce-6a17397b9541 +0 -0
- {ellf_cli-22.0.4 → ellf_cli-22.0.6}/ellf_cli/version/17.0.188_2026-09-07-12-51-28_patch_d6590822-068f-4a19-814d-34bbf204ba6e +0 -0
- {ellf_cli-22.0.4 → ellf_cli-22.0.6}/ellf_cli/version/17.0.189_2026-09-07-12-55-23_patch_7e0fb04b-4d40-4490-974f-c74ba2ae55a1 +0 -0
- {ellf_cli-22.0.4 → ellf_cli-22.0.6}/ellf_cli/version/17.0.190_2026-09-08-08-58-03_patch_15e3ac1e-4475-4272-bdd8-d00bb0db8f17 +0 -0
- {ellf_cli-22.0.4 → ellf_cli-22.0.6}/ellf_cli/version/17.0.191_2026-09-08-09-55-58_patch_3d350e50-ee53-4282-bc75-d2251142c46e +0 -0
- {ellf_cli-22.0.4 → ellf_cli-22.0.6}/ellf_cli/version/17.0.192_2026-09-08-11-12-21_patch_0733223b-1ae2-4b5b-897f-dfc57574ff83 +0 -0
- {ellf_cli-22.0.4 → ellf_cli-22.0.6}/ellf_cli/version/17.0.193_2026-09-08-21-48-17_patch_8d07d4e6-3555-4720-85c9-9452570fae2d +0 -0
- {ellf_cli-22.0.4 → ellf_cli-22.0.6}/ellf_cli/version/17.0.194_2026-09-09-07-53-43_patch_69559429-aa42-4b62-9bd7-cdfded1c00d0 +0 -0
- {ellf_cli-22.0.4 → ellf_cli-22.0.6}/ellf_cli/version/17.0.195_2026-09-09-09-55-40_patch_bbf67f1d-3b9a-43a2-b1bc-22df3fed8a34 +0 -0
- {ellf_cli-22.0.4 → ellf_cli-22.0.6}/ellf_cli/version/17.0.196_2026-09-09-09-59-47_patch_70d76812-7b3c-4192-ad49-fcaccc29b0da +0 -0
- {ellf_cli-22.0.4 → ellf_cli-22.0.6}/ellf_cli/version/17.0.197_2026-09-09-13-06-06_patch_d455be0b-5421-492a-b952-fab0fd8110f8 +0 -0
- {ellf_cli-22.0.4 → ellf_cli-22.0.6}/ellf_cli/version/17.0.198_2026-09-09-13-14-34_patch_267f54e5-311e-435e-882f-8e19191325ea +0 -0
- {ellf_cli-22.0.4 → ellf_cli-22.0.6}/ellf_cli/version/17.0.199_2026-09-09-22-24-18_patch_81e3e453-ca0e-42fa-9c21-8f94d43c05f2 +0 -0
- {ellf_cli-22.0.4 → ellf_cli-22.0.6}/ellf_cli/version/17.0.200_2026-09-10-07-19-50_patch_911714fa-1fa3-4165-bfe6-d10c650bf5bf +0 -0
- {ellf_cli-22.0.4 → ellf_cli-22.0.6}/ellf_cli/version/17.0.201_2026-09-10-10-14-19_patch_864015f2-b0b0-4563-a1c8-155629cb9126 +0 -0
- {ellf_cli-22.0.4 → ellf_cli-22.0.6}/ellf_cli/version/17.0.202_2026-09-10-11-00-05_patch_18270ecf-e770-4e69-8bcf-e5143c39929b +0 -0
- {ellf_cli-22.0.4 → ellf_cli-22.0.6}/ellf_cli/version/17.0.203_2026-09-10-11-10-41_patch_ff273ab1-44f3-4bc0-b184-3396e943df04 +0 -0
- {ellf_cli-22.0.4 → ellf_cli-22.0.6}/ellf_cli/version/17.0.204_2026-09-10-13-02-02_patch_d4ee41e2-d13c-4941-8605-3f1a42b56b22 +0 -0
- {ellf_cli-22.0.4 → ellf_cli-22.0.6}/ellf_cli/version/17.0.205_2026-09-10-13-57-50_patch_284df67e-5957-4b0c-bc64-5581db6ddecc +0 -0
- {ellf_cli-22.0.4 → ellf_cli-22.0.6}/ellf_cli/version/17.0.206_2026-09-11-10-18-50_patch_1ac7e410-0e62-4eef-bbe4-ebf93c9d0869 +0 -0
- {ellf_cli-22.0.4 → ellf_cli-22.0.6}/ellf_cli/version/17.0.207_2026-09-11-15-26-02_major_3f1b3ed7-eaac-41f5-b740-0af3331595d9 +0 -0
- {ellf_cli-22.0.4 → ellf_cli-22.0.6}/ellf_cli/version/18.0.0_2026-09-12-10-01-10_patch_8995783a-a9fa-4c4e-90b3-6386a4c481eb +0 -0
- {ellf_cli-22.0.4 → ellf_cli-22.0.6}/ellf_cli/version/18.0.10_2026-09-14-16-09-38_patch_9403d562-eed0-4125-a8b3-09cd92a13298 +0 -0
- {ellf_cli-22.0.4 → ellf_cli-22.0.6}/ellf_cli/version/18.0.11_2026-09-15-09-05-57_patch_8b22dba2-d931-4c4d-ae60-db3238d5e73d +0 -0
- {ellf_cli-22.0.4 → ellf_cli-22.0.6}/ellf_cli/version/18.0.12_2026-09-15-11-18-52_patch_81314afa-e70d-4ad8-b45b-3f8d62b983e7 +0 -0
- {ellf_cli-22.0.4 → ellf_cli-22.0.6}/ellf_cli/version/18.0.13_2026-09-16-08-00-55_patch_bfa30fc0-3281-4de6-bb56-d7290c98d1c3 +0 -0
- {ellf_cli-22.0.4 → ellf_cli-22.0.6}/ellf_cli/version/18.0.14_2026-09-16-14-38-26_patch_aae6e9b0-88f4-4bcf-b611-3308c7fcd6eb +0 -0
- {ellf_cli-22.0.4 → ellf_cli-22.0.6}/ellf_cli/version/18.0.15_2026-09-17-07-58-54_patch_43d40582-effd-4925-a8bd-e70fecf1faab +0 -0
- {ellf_cli-22.0.4 → ellf_cli-22.0.6}/ellf_cli/version/18.0.16_2026-09-17-08-12-37_patch_b4db2d23-88f1-45c1-82d9-78d38014fcb6 +0 -0
- {ellf_cli-22.0.4 → ellf_cli-22.0.6}/ellf_cli/version/18.0.17_2026-09-18-09-38-19_patch_25697fc3-e9a8-4980-962b-e6cb0a4760c5 +0 -0
- {ellf_cli-22.0.4 → ellf_cli-22.0.6}/ellf_cli/version/18.0.18_2026-09-18-09-54-33_patch_d37d9c71-c469-4d94-90d7-4898915ee991 +0 -0
- {ellf_cli-22.0.4 → ellf_cli-22.0.6}/ellf_cli/version/18.0.19_2026-09-18-10-47-33_patch_47607b3c-d1b6-4864-b9b0-00a2a8d2ac32 +0 -0
- {ellf_cli-22.0.4 → ellf_cli-22.0.6}/ellf_cli/version/18.0.1_2026-09-12-17-33-30_patch_f6ea2c99-a715-4c4d-876a-23ed8c62a83d +0 -0
- {ellf_cli-22.0.4 → ellf_cli-22.0.6}/ellf_cli/version/18.0.20_2026-09-18-11-07-21_patch_5a336f91-63a6-4877-97ed-da89395cc8f5 +0 -0
- {ellf_cli-22.0.4 → ellf_cli-22.0.6}/ellf_cli/version/18.0.21_2026-09-18-13-34-52_patch_c56462e1-63d9-498f-a912-c80c5f0171d1 +0 -0
- {ellf_cli-22.0.4 → ellf_cli-22.0.6}/ellf_cli/version/18.0.22_2026-09-20-08-10-45_patch_6afa2c20-bcef-4b7d-8685-2c9815d8683f +0 -0
- {ellf_cli-22.0.4 → ellf_cli-22.0.6}/ellf_cli/version/18.0.23_2026-09-20-08-38-35_patch_a7f169e5-37ab-4ec0-bf89-d14770181303 +0 -0
- {ellf_cli-22.0.4 → ellf_cli-22.0.6}/ellf_cli/version/18.0.24_2026-09-20-15-49-27_patch_c1222081-ff44-4f20-aea2-5840b6d76efd +0 -0
- {ellf_cli-22.0.4 → ellf_cli-22.0.6}/ellf_cli/version/18.0.25_2026-09-21-06-11-40_patch_6df46dd9-e7d3-4e71-bec4-e5c93b4ac863 +0 -0
- {ellf_cli-22.0.4 → ellf_cli-22.0.6}/ellf_cli/version/18.0.26_2026-09-21-07-54-11_patch_cc52dd36-5eba-4094-a1b0-d6f79f103441 +0 -0
- {ellf_cli-22.0.4 → ellf_cli-22.0.6}/ellf_cli/version/18.0.27_2026-09-21-08-36-49_patch_a25fa125-9bcd-4317-8da5-851c1c329631 +0 -0
- {ellf_cli-22.0.4 → ellf_cli-22.0.6}/ellf_cli/version/18.0.28_2026-09-21-09-44-01_patch_4430a721-0b3c-4f6a-b9bc-a53e667161bb +0 -0
- {ellf_cli-22.0.4 → ellf_cli-22.0.6}/ellf_cli/version/18.0.29_2026-09-21-10-58-07_patch_83b2b942-60a4-4bef-afd1-99690ba52b69 +0 -0
- {ellf_cli-22.0.4 → ellf_cli-22.0.6}/ellf_cli/version/18.0.2_2026-09-13-06-36-33_patch_176bb944-1c49-4dd3-a001-8fce908825a0 +0 -0
- {ellf_cli-22.0.4 → ellf_cli-22.0.6}/ellf_cli/version/18.0.30_2026-09-21-12-51-16_patch_519b9a65-d400-4234-b113-36b46e315507 +0 -0
- {ellf_cli-22.0.4 → ellf_cli-22.0.6}/ellf_cli/version/18.0.31_2026-09-22-06-53-35_patch_5c45a235-81b6-4d05-801a-07dc7db61400 +0 -0
- {ellf_cli-22.0.4 → ellf_cli-22.0.6}/ellf_cli/version/18.0.32_2026-09-22-07-42-04_patch_f12c6415-8166-4063-9e5d-26601b929d63 +0 -0
- {ellf_cli-22.0.4 → ellf_cli-22.0.6}/ellf_cli/version/18.0.33_2026-09-22-08-12-32_patch_cec31bb0-a6cb-43e6-8407-25aba322b54c +0 -0
- {ellf_cli-22.0.4 → ellf_cli-22.0.6}/ellf_cli/version/18.0.34_2026-09-23-07-09-16_patch_659fccbb-c0a0-4f6f-bc87-e6abaa8815a7 +0 -0
- {ellf_cli-22.0.4 → ellf_cli-22.0.6}/ellf_cli/version/18.0.35_2026-09-23-09-38-52_patch_023f904d-807a-4833-8a40-570365e8ec06 +0 -0
- {ellf_cli-22.0.4 → ellf_cli-22.0.6}/ellf_cli/version/18.0.36_2026-09-23-10-05-33_patch_846fb491-2b1b-47ed-ac1f-ba40e744e199 +0 -0
- {ellf_cli-22.0.4 → ellf_cli-22.0.6}/ellf_cli/version/18.0.37_2026-09-23-13-08-30_patch_8e4b1d24-156e-4ffe-8e4a-b8740ec18588 +0 -0
- {ellf_cli-22.0.4 → ellf_cli-22.0.6}/ellf_cli/version/18.0.38_2026-09-24-06-41-37_patch_67adb1d6-3f49-4357-a67f-b1d48f23a972 +0 -0
- {ellf_cli-22.0.4 → ellf_cli-22.0.6}/ellf_cli/version/18.0.39_2026-09-24-15-24-32_patch_e5dd375d-5643-4514-9b65-8d877c4a2bb2 +0 -0
- {ellf_cli-22.0.4 → ellf_cli-22.0.6}/ellf_cli/version/18.0.3_2026-09-13-12-32-46_patch_58e5ce3a-f2a7-4789-b2cd-5cc75c72b6ec +0 -0
- {ellf_cli-22.0.4 → ellf_cli-22.0.6}/ellf_cli/version/18.0.40_2026-09-24-19-58-14_patch_ff18824b-be51-4a3d-a621-e96279e53c3e +0 -0
- {ellf_cli-22.0.4 → ellf_cli-22.0.6}/ellf_cli/version/18.0.41_2026-09-26-09-24-52_patch_b9979267-30f9-440a-a4ce-c611991d4886 +0 -0
- {ellf_cli-22.0.4 → ellf_cli-22.0.6}/ellf_cli/version/18.0.42_2026-09-28-12-39-34_patch_dda535d9-8922-4eb9-ab51-6ecff0650c10 +0 -0
- {ellf_cli-22.0.4 → ellf_cli-22.0.6}/ellf_cli/version/18.0.43_2026-09-28-13-35-27_minor_aca1168a-1286-4326-9d0c-2f2587c9fe92 +0 -0
- {ellf_cli-22.0.4 → ellf_cli-22.0.6}/ellf_cli/version/18.0.4_2026-09-13-12-45-14_patch_b6c79d55-8f85-4f47-a304-df52b122c38c +0 -0
- {ellf_cli-22.0.4 → ellf_cli-22.0.6}/ellf_cli/version/18.0.5_2026-09-13-14-11-18_patch_9b111116-5e62-472a-b72c-d1bbafa22c97 +0 -0
- {ellf_cli-22.0.4 → ellf_cli-22.0.6}/ellf_cli/version/18.0.5_2026-09-13-17-01-11_patch_d4bf61c0-83ac-4dd3-b512-a65805d298c8 +0 -0
- {ellf_cli-22.0.4 → ellf_cli-22.0.6}/ellf_cli/version/18.0.6_2026-09-14-11-31-04_patch_fd2ebda6-22cb-4e7d-9452-2f6f2640e828 +0 -0
- {ellf_cli-22.0.4 → ellf_cli-22.0.6}/ellf_cli/version/18.0.7_2026-09-13-17-06-08_patch_428af161-0b27-43c2-8751-e5f7874df120 +0 -0
- {ellf_cli-22.0.4 → ellf_cli-22.0.6}/ellf_cli/version/18.0.9_2026-09-14-12-37-42_patch_b2ca0529-4b67-4fa4-8fdc-5f5e9f5d5166 +0 -0
- {ellf_cli-22.0.4 → ellf_cli-22.0.6}/ellf_cli/version/18.1.0_2026-09-28-14-27-54_patch_324444f7-d24e-4189-b21f-1188894a3109 +0 -0
- {ellf_cli-22.0.4 → ellf_cli-22.0.6}/ellf_cli/version/18.1.1_2026-09-28-21-03-07_major_e266a14a-7d96-4ece-92a3-8f34e64438a5 +0 -0
- {ellf_cli-22.0.4 → ellf_cli-22.0.6}/ellf_cli/version/19.0.0_2026-09-29-11-05-31_patch_77d6f9f8-ead3-4d79-a4b2-b084b2dffc98 +0 -0
- {ellf_cli-22.0.4 → ellf_cli-22.0.6}/ellf_cli/version/19.0.10_2026-10-02-12-56-52_patch_14e97576-73ca-44b8-82c9-7dad6f1789ab +0 -0
- {ellf_cli-22.0.4 → ellf_cli-22.0.6}/ellf_cli/version/19.0.11_2026-10-04-10-31-19_patch_088eb371-1799-4949-a119-286cf1cd55d1 +0 -0
- {ellf_cli-22.0.4 → ellf_cli-22.0.6}/ellf_cli/version/19.0.12_2026-10-06-18-00-25_patch_36554bed-49c5-41cb-9fd4-48cc6af74c09 +0 -0
- {ellf_cli-22.0.4 → ellf_cli-22.0.6}/ellf_cli/version/19.0.13_2026-10-07-12-24-08_patch_a540a364-3a0d-4960-b278-06ba2c7cc258 +0 -0
- {ellf_cli-22.0.4 → ellf_cli-22.0.6}/ellf_cli/version/19.0.14_2026-10-07-12-52-39_patch_e3a6fc5c-2ee4-414c-a2dd-4910a655a7dd +0 -0
- {ellf_cli-22.0.4 → ellf_cli-22.0.6}/ellf_cli/version/19.0.15_2026-10-09-04-15-56_patch_bd7a9b06-867b-4fea-9678-973a05e3022d +0 -0
- {ellf_cli-22.0.4 → ellf_cli-22.0.6}/ellf_cli/version/19.0.16_2026-10-09-16-11-56_patch_a1135435-7dea-4f00-8c98-203ba3974382 +0 -0
- {ellf_cli-22.0.4 → ellf_cli-22.0.6}/ellf_cli/version/19.0.17_2026-10-09-17-44-55_patch_faeacad8-541a-40f2-ab9c-abcd66fcd00e +0 -0
- {ellf_cli-22.0.4 → ellf_cli-22.0.6}/ellf_cli/version/19.0.18_2026-10-10-07-29-38_major_02601a1e-9bdb-45bf-ad67-a07b7dcd0002 +0 -0
- {ellf_cli-22.0.4 → ellf_cli-22.0.6}/ellf_cli/version/19.0.1_2026-09-29-11-59-38_patch_bbf4753c-d641-4c2f-ac7c-02be6bd8e7c2 +0 -0
- {ellf_cli-22.0.4 → ellf_cli-22.0.6}/ellf_cli/version/19.0.2_2026-09-29-12-44-07_patch_19fdaa94-60dc-464b-93b8-c17607ebc7b6 +0 -0
- {ellf_cli-22.0.4 → ellf_cli-22.0.6}/ellf_cli/version/19.0.3_2026-09-29-14-16-00_patch_3cb60272-95cf-4311-8897-721e1b5c2c88 +0 -0
- {ellf_cli-22.0.4 → ellf_cli-22.0.6}/ellf_cli/version/19.0.4_2026-09-29-16-24-53_patch_9fe8ad2b-e3ec-4558-a5fc-9c44c5c266dc +0 -0
- {ellf_cli-22.0.4 → ellf_cli-22.0.6}/ellf_cli/version/19.0.5_2026-09-29-17-13-16_patch_e48d2dd2-f41a-402e-a441-756b23c01dcb +0 -0
- {ellf_cli-22.0.4 → ellf_cli-22.0.6}/ellf_cli/version/19.0.6_2026-09-29-17-22-11_patch_3b1c79e9-9002-4e96-ba78-10668c33be6a +0 -0
- {ellf_cli-22.0.4 → ellf_cli-22.0.6}/ellf_cli/version/19.0.7_2026-09-30-07-50-58_patch_e8b1f046-d819-499c-9cd5-3e1a20fc9acf +0 -0
- {ellf_cli-22.0.4 → ellf_cli-22.0.6}/ellf_cli/version/19.0.8_2026-10-01-13-41-44_patch_7d63af18-e0af-4127-ab58-aede2407490f +0 -0
- {ellf_cli-22.0.4 → ellf_cli-22.0.6}/ellf_cli/version/19.0.9_2026-10-01-15-57-35_patch_143863cb-8f63-4ef7-bd78-c1114fc93a2d +0 -0
- {ellf_cli-22.0.4 → ellf_cli-22.0.6}/ellf_cli/version/20.0.0_2026-10-10-09-36-17_patch_d2618f39-cdc9-4d3a-ba40-1a0ad6265f3b +0 -0
- {ellf_cli-22.0.4 → ellf_cli-22.0.6}/ellf_cli/version/20.0.1_2026-10-11-10-24-33_patch_e29a8713-eecb-4eb1-9641-789d3cfab21a +0 -0
- {ellf_cli-22.0.4 → ellf_cli-22.0.6}/ellf_cli/version/20.0.2_2026-10-12-07-22-24_patch_f4053ad1-8fcd-4591-a68d-bf1e23485ba7 +0 -0
- {ellf_cli-22.0.4 → ellf_cli-22.0.6}/ellf_cli/version/20.0.3_2026-10-14-14-29-16_patch_070af1c4-f9d9-4f97-8112-058de90d3ea8 +0 -0
- {ellf_cli-22.0.4 → ellf_cli-22.0.6}/ellf_cli/version/20.0.4_2026-10-14-16-20-24_patch_c6672844-8227-4e0a-9fb5-36af7e7141dc +0 -0
- {ellf_cli-22.0.4 → ellf_cli-22.0.6}/ellf_cli/version/20.0.5_2026-10-15-08-53-28_patch_10154146-8afd-4632-b250-9b4b9f5bf67c +0 -0
- {ellf_cli-22.0.4 → ellf_cli-22.0.6}/ellf_cli/version/20.0.6_2026-10-15-14-17-40_patch_df3a5d3c-f0e0-46cb-aeaf-15b51c8a0f13 +0 -0
- {ellf_cli-22.0.4 → ellf_cli-22.0.6}/ellf_cli/version/20.0.7_2026-10-16-07-09-40_major_3e5dfba3-2810-47b4-85a9-938bfff72a04 +0 -0
- {ellf_cli-22.0.4 → ellf_cli-22.0.6}/ellf_cli/version/21.0.0_2026-10-16-10-44-29_patch_1058e453-613f-492b-a3c7-f3ca2abe8c2d +0 -0
- {ellf_cli-22.0.4 → ellf_cli-22.0.6}/ellf_cli/version/21.0.10_2026-10-30-14-29-34_major_4fd535f6-6e41-4f34-b29c-543e1a9bac51 +0 -0
- {ellf_cli-22.0.4 → ellf_cli-22.0.6}/ellf_cli/version/21.0.1_2026-10-16-16-06-10_patch_e20b4ad2-b87f-478e-b4f4-323c8bcc5ea9 +0 -0
- {ellf_cli-22.0.4 → ellf_cli-22.0.6}/ellf_cli/version/21.0.2_2026-10-19-13-54-31_patch_bf091df4-00bb-43c8-9117-51047944e305 +0 -0
- {ellf_cli-22.0.4 → ellf_cli-22.0.6}/ellf_cli/version/21.0.3_2026-10-19-14-58-29_patch_9b985381-1b1d-4531-b242-b02cea2484c5 +0 -0
- {ellf_cli-22.0.4 → ellf_cli-22.0.6}/ellf_cli/version/21.0.4_2026-10-20-17-08-51_patch_38d02583-cb5c-4adc-b5d7-614b483acf19 +0 -0
- {ellf_cli-22.0.4 → ellf_cli-22.0.6}/ellf_cli/version/21.0.5_2026-10-21-09-28-47_patch_fabd5cfe-2d04-4215-abf1-58fba226acb7 +0 -0
- {ellf_cli-22.0.4 → ellf_cli-22.0.6}/ellf_cli/version/21.0.6_2026-10-22-16-07-37_patch_d2bb27d7-6304-4c99-abbc-46d3dd7e363b +0 -0
- {ellf_cli-22.0.4 → ellf_cli-22.0.6}/ellf_cli/version/21.0.7_2026-10-27-11-41-55_patch_6b50567f-209d-42c9-a2cd-d251a2bc8fbf +0 -0
- {ellf_cli-22.0.4 → ellf_cli-22.0.6}/ellf_cli/version/21.0.8_2026-10-29-13-48-18_patch_bdab3ee6-fba8-4625-9243-c4cbe839ba32 +0 -0
- {ellf_cli-22.0.4 → ellf_cli-22.0.6}/ellf_cli/version/21.0.9_2026-10-29-17-51-00_patch_803a4c70-2e2b-4024-8598-c7d91c9db0d2 +0 -0
- {ellf_cli-22.0.4 → ellf_cli-22.0.6}/ellf_cli/version/22.0.0_2026-10-30-15-25-42_patch_1f39caf4-aadc-4d26-9124-a854d945b32a +0 -0
- {ellf_cli-22.0.4 → ellf_cli-22.0.6}/ellf_cli/version/22.0.1_2026-10-30-17-30-37_patch_4a6e6cfb-d86e-4f1e-84c5-02d4c071f75b +0 -0
- {ellf_cli-22.0.4 → ellf_cli-22.0.6}/ellf_cli/version/22.0.2_2026-10-31-11-13-43_patch_54d644d8-cebd-4b6a-ba8f-685e2ffee481 +0 -0
- {ellf_cli-22.0.4 → ellf_cli-22.0.6}/ellf_cli/version/22.0.3_2026-10-31-16-57-27_patch_e818d7fa-a4ba-4041-abdd-c6363fcc0a30 +0 -0
- {ellf_cli-22.0.4 → ellf_cli-22.0.6}/ellf_cli/version/__init__.py +0 -0
- {ellf_cli-22.0.4 → ellf_cli-22.0.6}/ellf_cli/version/read_version.py +0 -0
- {ellf_cli-22.0.4 → ellf_cli-22.0.6}/ellf_cli.egg-info/dependency_links.txt +0 -0
- {ellf_cli-22.0.4 → ellf_cli-22.0.6}/ellf_cli.egg-info/entry_points.txt +0 -0
- {ellf_cli-22.0.4 → ellf_cli-22.0.6}/ellf_cli.egg-info/not-zip-safe +0 -0
- {ellf_cli-22.0.4 → ellf_cli-22.0.6}/ellf_cli.egg-info/requires.txt +0 -0
- {ellf_cli-22.0.4 → ellf_cli-22.0.6}/ellf_cli.egg-info/top_level.txt +0 -0
- {ellf_cli-22.0.4 → ellf_cli-22.0.6}/pyproject.toml +0 -0
- {ellf_cli-22.0.4 → ellf_cli-22.0.6}/setup.cfg +0 -0
- {ellf_cli-22.0.4 → ellf_cli-22.0.6}/setup.py +0 -0
- {ellf_cli-22.0.4 → ellf_cli-22.0.6}/tests/test_agents.py +0 -0
- {ellf_cli-22.0.4 → ellf_cli-22.0.6}/tests/test_agents_info_stats.py +0 -0
- {ellf_cli-22.0.4 → ellf_cli-22.0.6}/tests/test_appdirs.py +0 -0
- {ellf_cli-22.0.4 → ellf_cli-22.0.6}/tests/test_auth.py +0 -0
- {ellf_cli-22.0.4 → ellf_cli-22.0.6}/tests/test_clusters_info_stats.py +0 -0
- {ellf_cli-22.0.4 → ellf_cli-22.0.6}/tests/test_config.py +0 -0
- {ellf_cli-22.0.4 → ellf_cli-22.0.6}/tests/test_datasets_export.py +0 -0
- {ellf_cli-22.0.4 → ellf_cli-22.0.6}/tests/test_errors.py +0 -0
- {ellf_cli-22.0.4 → ellf_cli-22.0.6}/tests/test_files_cp.py +0 -0
- {ellf_cli-22.0.4 → ellf_cli-22.0.6}/tests/test_files_cp_helpers.py +0 -0
- {ellf_cli-22.0.4 → ellf_cli-22.0.6}/tests/test_helm.py +0 -0
- {ellf_cli-22.0.4 → ellf_cli-22.0.6}/tests/test_import_export.py +0 -0
- {ellf_cli-22.0.4 → ellf_cli-22.0.6}/tests/test_info.py +0 -0
- {ellf_cli-22.0.4 → ellf_cli-22.0.6}/tests/test_infra_config_io.py +0 -0
- {ellf_cli-22.0.4 → ellf_cli-22.0.6}/tests/test_infra_configure.py +0 -0
- {ellf_cli-22.0.4 → ellf_cli-22.0.6}/tests/test_infra_deploy_node_labels.py +0 -0
- {ellf_cli-22.0.4 → ellf_cli-22.0.6}/tests/test_infra_deploy_preflight.py +0 -0
- {ellf_cli-22.0.4 → ellf_cli-22.0.6}/tests/test_infra_destroy.py +0 -0
- {ellf_cli-22.0.4 → ellf_cli-22.0.6}/tests/test_infra_get_terraform.py +0 -0
- {ellf_cli-22.0.4 → ellf_cli-22.0.6}/tests/test_infra_hibernate.py +0 -0
- {ellf_cli-22.0.4 → ellf_cli-22.0.6}/tests/test_infra_pause.py +0 -0
- {ellf_cli-22.0.4 → ellf_cli-22.0.6}/tests/test_infra_provision.py +0 -0
- {ellf_cli-22.0.4 → ellf_cli-22.0.6}/tests/test_infra_start_validation.py +0 -0
- {ellf_cli-22.0.4 → ellf_cli-22.0.6}/tests/test_infra_state_bucket.py +0 -0
- {ellf_cli-22.0.4 → ellf_cli-22.0.6}/tests/test_infra_terraform_init.py +0 -0
- {ellf_cli-22.0.4 → ellf_cli-22.0.6}/tests/test_invalid_secrets.py +0 -0
- {ellf_cli-22.0.4 → ellf_cli-22.0.6}/tests/test_job_list_project_filter.py +0 -0
- {ellf_cli-22.0.4 → ellf_cli-22.0.6}/tests/test_jobs_info_hides_creator.py +0 -0
- {ellf_cli-22.0.4 → ellf_cli-22.0.6}/tests/test_jobs_list_unmatched.py +0 -0
- {ellf_cli-22.0.4 → ellf_cli-22.0.6}/tests/test_key_pair.py +0 -0
- {ellf_cli-22.0.4 → ellf_cli-22.0.6}/tests/test_login.py +0 -0
- {ellf_cli-22.0.4 → ellf_cli-22.0.6}/tests/test_logout.py +0 -0
- {ellf_cli-22.0.4 → ellf_cli-22.0.6}/tests/test_main.py +0 -0
- {ellf_cli-22.0.4 → ellf_cli-22.0.6}/tests/test_mcp_proxy.py +0 -0
- {ellf_cli-22.0.4 → ellf_cli-22.0.6}/tests/test_org_select.py +0 -0
- {ellf_cli-22.0.4 → ellf_cli-22.0.6}/tests/test_plans.py +0 -0
- {ellf_cli-22.0.4 → ellf_cli-22.0.6}/tests/test_projects.py +0 -0
- {ellf_cli-22.0.4 → ellf_cli-22.0.6}/tests/test_projects_delete_confirm.py +0 -0
- {ellf_cli-22.0.4 → ellf_cli-22.0.6}/tests/test_publish_build_poll.py +0 -0
- {ellf_cli-22.0.4 → ellf_cli-22.0.6}/tests/test_publish_data.py +0 -0
- {ellf_cli-22.0.4 → ellf_cli-22.0.6}/tests/test_publish_import_root.py +0 -0
- {ellf_cli-22.0.4 → ellf_cli-22.0.6}/tests/test_publish_requirements.py +0 -0
- {ellf_cli-22.0.4 → ellf_cli-22.0.6}/tests/test_publish_versions.py +0 -0
- {ellf_cli-22.0.4 → ellf_cli-22.0.6}/tests/test_pypi_auth.py +0 -0
- {ellf_cli-22.0.4 → ellf_cli-22.0.6}/tests/test_query.py +0 -0
- {ellf_cli-22.0.4 → ellf_cli-22.0.6}/tests/test_recipe_file.py +0 -0
- {ellf_cli-22.0.4 → ellf_cli-22.0.6}/tests/test_recipe_help.py +0 -0
- {ellf_cli-22.0.4 → ellf_cli-22.0.6}/tests/test_recipe_required_args.py +0 -0
- {ellf_cli-22.0.4 → ellf_cli-22.0.6}/tests/test_recipes.py +0 -0
- {ellf_cli-22.0.4 → ellf_cli-22.0.6}/tests/test_recipes_init.py +0 -0
- {ellf_cli-22.0.4 → ellf_cli-22.0.6}/tests/test_state.py +0 -0
- {ellf_cli-22.0.4 → ellf_cli-22.0.6}/tests/test_support.py +0 -0
- {ellf_cli-22.0.4 → ellf_cli-22.0.6}/tests/test_todos.py +0 -0
- {ellf_cli-22.0.4 → ellf_cli-22.0.6}/tests/test_ty.py +0 -0
- {ellf_cli-22.0.4 → ellf_cli-22.0.6}/tests/test_ui.py +0 -0
- {ellf_cli-22.0.4 → ellf_cli-22.0.6}/tests/test_ui_extras.py +0 -0
- {ellf_cli-22.0.4 → ellf_cli-22.0.6}/tests/test_usage_counts.py +0 -0
- {ellf_cli-22.0.4 → ellf_cli-22.0.6}/tests/test_users_assign.py +0 -0
- {ellf_cli-22.0.4 → ellf_cli-22.0.6}/tests/test_users_info_stats.py +0 -0
- {ellf_cli-22.0.4 → ellf_cli-22.0.6}/tests/test_users_list.py +0 -0
- {ellf_cli-22.0.4 → ellf_cli-22.0.6}/tests/test_util.py +0 -0
|
@@ -32,7 +32,7 @@ List the actions in a project. Uses your default project unless --project is giv
|
|
|
32
32
|
| Argument | Type | Description | Default |
|
|
33
33
|
| --- | --- | --- | --- |
|
|
34
34
|
| `--project` | `str` | Name, slug, or ID of action's project or the last project if not set | `None` |
|
|
35
|
-
| `--select` | `list[str]` | Comma-separated fields to select and show in output. Available: ['id', 'created', 'updated', 'cluster_id', 'name', 'project_id', 'job_type', 'plan', 'cli_command', 'stats', 'created_by_user', 'state'] | `['id', 'name', 'state']` |
|
|
35
|
+
| `--select` | `list[str]` | Comma-separated fields to select and show in output. Available: ['id', 'created', 'updated', 'cluster_id', 'name', 'project_id', 'job_type', 'plan', 'cli_command', 'stats', 'created_by_user', 'created_by_deleted', 'state'] | `['id', 'name', 'state']` |
|
|
36
36
|
| `--json` | `bool` | Output the result as JSON | `False` |
|
|
37
37
|
|
|
38
38
|
#### `ellf actions info`
|
|
@@ -44,7 +44,7 @@ Print information about an action on the cluster
|
|
|
44
44
|
| `name_or_id` | `Union[str, UUID]` | Name or ID of the action | |
|
|
45
45
|
| `--project` | `str` | Name, slug, or ID of action's project or the last project if not set | `None` |
|
|
46
46
|
| `--cluster` | `str` | Name, slug, or ID of the cluster to search for action name (or the last cluster if not set) | `None` |
|
|
47
|
-
| `--select` | `list[str]` | Comma-separated fields to select and show in output. Available: ['id', 'created', 'updated', 'cluster_id', 'name', 'project_id', 'job_type', 'plan', 'cli_command', 'stats', 'created_by_user', 'recipe_name', 'recipe_title', 'evaluation', 'project_name', 'error', 'executions', 'last_execution_id', 'url_logs', 'url', 'related_tasks'] | `None` |
|
|
47
|
+
| `--select` | `list[str]` | Comma-separated fields to select and show in output. Available: ['id', 'created', 'updated', 'cluster_id', 'name', 'project_id', 'job_type', 'plan', 'cli_command', 'stats', 'created_by_user', 'created_by_deleted', 'recipe_name', 'recipe_title', 'evaluation', 'project_name', 'error', 'executions', 'last_execution_id', 'url_logs', 'url', 'related_tasks'] | `None` |
|
|
48
48
|
| `--json` | `bool` | Output the result as JSON | `False` |
|
|
49
49
|
|
|
50
50
|
#### `ellf actions logs`
|
|
@@ -215,7 +215,7 @@ List the agents in a project. Uses your default project unless --project is give
|
|
|
215
215
|
| Argument | Type | Description | Default |
|
|
216
216
|
| --- | --- | --- | --- |
|
|
217
217
|
| `--project` | `str` | Name, slug, or ID of agent's project or the last project if not set | `None` |
|
|
218
|
-
| `--select` | `list[str]` | Comma-separated fields to select and show in output. Available: ['id', 'created', 'updated', 'cluster_id', 'name', 'project_id', 'job_type', 'plan', 'cli_command', 'last_execution_started', 'icon', 'created_by_user', 'live_sessions', 'state'] | `['id', 'name', 'state']` |
|
|
218
|
+
| `--select` | `list[str]` | Comma-separated fields to select and show in output. Available: ['id', 'created', 'updated', 'cluster_id', 'name', 'project_id', 'job_type', 'plan', 'cli_command', 'last_execution_started', 'icon', 'created_by_user', 'created_by_deleted', 'live_sessions', 'state'] | `['id', 'name', 'state']` |
|
|
219
219
|
| `--json` | `bool` | Output the result as JSON | `False` |
|
|
220
220
|
|
|
221
221
|
#### `ellf agents info`
|
|
@@ -227,7 +227,7 @@ Print information about an agent on the cluster
|
|
|
227
227
|
| `name_or_id` | `Union[str, UUID]` | Name or ID of the agent | |
|
|
228
228
|
| `--project` | `str` | Name, slug, or ID of agent's project or the last project if not set | `None` |
|
|
229
229
|
| `--cluster` | `str` | Name, slug, or ID of the cluster to search for agent name (or the last cluster if not set) | `None` |
|
|
230
|
-
| `--select` | `list[str]` | Comma-separated fields to select and show in output. Available: ['id', 'created', 'updated', 'cluster_id', 'name', 'project_id', 'job_type', 'plan', 'cli_command', 'last_execution_started', 'icon', 'created_by_user', 'live_sessions', 'recipe_name', 'recipe_title', 'project_name', 'error', 'last_execution_id', 'related_tasks'] | `None` |
|
|
230
|
+
| `--select` | `list[str]` | Comma-separated fields to select and show in output. Available: ['id', 'created', 'updated', 'cluster_id', 'name', 'project_id', 'job_type', 'plan', 'cli_command', 'last_execution_started', 'icon', 'created_by_user', 'created_by_deleted', 'live_sessions', 'recipe_name', 'recipe_title', 'project_name', 'error', 'last_execution_id', 'related_tasks'] | `None` |
|
|
231
231
|
| `--json` | `bool` | Output the result as JSON | `False` |
|
|
232
232
|
|
|
233
233
|
#### `ellf agents logs`
|
|
@@ -659,12 +659,12 @@ Deploy and manage cluster infrastructure (Kubernetes, TLS, Terraform)
|
|
|
659
659
|
|
|
660
660
|
#### `ellf infra register`
|
|
661
661
|
|
|
662
|
-
Register a new cluster with PAM
|
|
662
|
+
Register a new cluster with PAM. 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]', and to reach it from other devices 'https://<its IP address on your network>'; Ellf issues those a certificate, see 'ellf infra tls'). Stored
|
|
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 as the cluster address without its scheme, which is https unless you give http://; 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
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 | |
|
|
@@ -843,13 +843,13 @@ Recreate a cluster hibernated with 'ellf infra hibernate'. Starts the database,
|
|
|
843
843
|
|
|
844
844
|
#### `ellf infra init-values`
|
|
845
845
|
|
|
846
|
-
Generate a values.yaml for Helm deployment from
|
|
846
|
+
Generate a values.yaml for Helm deployment of a registered cluster. The file holds no secrets, so it's safe to keep with the rest of your configuration: 'infra deploy' hands the cluster its registry and licence keys from PAM, and keeps its database password in the cluster.
|
|
847
847
|
|
|
848
848
|
| Argument | Type | Description | Default |
|
|
849
849
|
| --- | --- | --- | --- |
|
|
850
|
-
| `--creds` | `Path` | Path to cluster-creds.json from 'infra register' | |
|
|
851
850
|
| `--cloud-provider` | `str` | Cloud provider: gcp, k3s, aws, azure | |
|
|
852
|
-
| `--
|
|
851
|
+
| `--cluster` | `str` | Name or ID of the registered cluster (default: the one 'infra register' registered last) | `None` |
|
|
852
|
+
| `--domain` | `str` | Public address the cluster is reached on, host or host:port (default: the address it was registered at). 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'). It flows into ELLF_BROKER_HOST so the cluster emits matching OIDC / token URLs. | `None` |
|
|
853
853
|
| `--out` | `Path` | Output path for generated values.yaml | `'values.yaml'` |
|
|
854
854
|
| `--acme-email` | `str` | Email for Let's Encrypt certificates | `None` |
|
|
855
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` |
|
|
@@ -1255,7 +1255,7 @@ List the services in a project. Uses your default project unless --project is gi
|
|
|
1255
1255
|
| Argument | Type | Description | Default |
|
|
1256
1256
|
| --- | --- | --- | --- |
|
|
1257
1257
|
| `--project` | `str` | Name, slug, or ID of service's project or the last project if not set | `None` |
|
|
1258
|
-
| `--select` | `list[str]` | Comma-separated fields to select and show in output. Available: ['id', 'created', 'updated', 'cluster_id', 'name', 'project_id', 'job_type', 'plan', 'cli_command', 'last_execution_started', 'created_by_user', 'state'] | `['id', 'name', 'state']` |
|
|
1258
|
+
| `--select` | `list[str]` | Comma-separated fields to select and show in output. Available: ['id', 'created', 'updated', 'cluster_id', 'name', 'project_id', 'job_type', 'plan', 'cli_command', 'last_execution_started', 'created_by_user', 'created_by_deleted', 'state'] | `['id', 'name', 'state']` |
|
|
1259
1259
|
| `--json` | `bool` | Output the result as JSON | `False` |
|
|
1260
1260
|
|
|
1261
1261
|
#### `ellf services info`
|
|
@@ -1267,7 +1267,7 @@ Print information about a service on the cluster.
|
|
|
1267
1267
|
| `name_or_id` | `Union[str, UUID]` | Name or ID of the service | |
|
|
1268
1268
|
| `--project` | `str` | Name, slug, or ID of service's project or the last project if not set | `None` |
|
|
1269
1269
|
| `--cluster` | `str` | Name, slug, or ID of the cluster to search for service name (or the last cluster if not set) | `None` |
|
|
1270
|
-
| `--select` | `list[str]` | Comma-separated fields to select and show in output. Available: ['id', 'created', 'updated', 'cluster_id', 'name', 'project_id', 'job_type', 'plan', 'cli_command', 'last_execution_started', 'created_by_user', 'recipe_name', 'recipe_title', 'project_name', 'error', 'last_execution_id', 'url', 'url_ui', 'url_logs'] | `None` |
|
|
1270
|
+
| `--select` | `list[str]` | Comma-separated fields to select and show in output. Available: ['id', 'created', 'updated', 'cluster_id', 'name', 'project_id', 'job_type', 'plan', 'cli_command', 'last_execution_started', 'created_by_user', 'created_by_deleted', 'recipe_name', 'recipe_title', 'project_name', 'error', 'last_execution_id', 'url', 'url_ui', 'url_logs'] | `None` |
|
|
1271
1271
|
| `--json` | `bool` | Output the result as JSON | `False` |
|
|
1272
1272
|
|
|
1273
1273
|
#### `ellf services logs`
|
|
@@ -1418,7 +1418,7 @@ List the tasks in a project. Uses your default project unless --project is given
|
|
|
1418
1418
|
| Argument | Type | Description | Default |
|
|
1419
1419
|
| --- | --- | --- | --- |
|
|
1420
1420
|
| `--project` | `str` | Name, slug, or ID of task's project or the last project if not set | `None` |
|
|
1421
|
-
| `--select` | `list[str]` | Comma-separated fields to select and show in output. Available: ['id', 'created', 'updated', 'project_id', 'cluster_id', 'name', 'recipe_name', 'recipe_title', 'project_name', 'error', 'plan', 'job_type', 'cli_command', 'last_execution_started', 'created_by_user', 'state'] | `['id', 'name', 'state']` |
|
|
1421
|
+
| `--select` | `list[str]` | Comma-separated fields to select and show in output. Available: ['id', 'created', 'updated', 'project_id', 'cluster_id', 'name', 'recipe_name', 'recipe_title', 'project_name', 'error', 'plan', 'job_type', 'cli_command', 'last_execution_started', 'created_by_user', 'created_by_deleted', 'state'] | `['id', 'name', 'state']` |
|
|
1422
1422
|
| `--json` | `bool` | Output the result as JSON | `False` |
|
|
1423
1423
|
|
|
1424
1424
|
#### `ellf tasks info`
|
|
@@ -1430,7 +1430,7 @@ Print information about a task on the cluster
|
|
|
1430
1430
|
| `name_or_id` | `Union[str, UUID]` | Name or ID of the task | |
|
|
1431
1431
|
| `--project` | `str` | Name, slug, or ID of task's project or the last project if not set | `None` |
|
|
1432
1432
|
| `--cluster` | `str` | Name, slug, or ID of the cluster to search for task name (or the last cluster if not set) | `None` |
|
|
1433
|
-
| `--select` | `list[str]` | Comma-separated fields to select and show in output. Available: ['id', 'created', 'updated', 'project_id', 'cluster_id', 'name', 'recipe_name', 'recipe_title', 'project_name', 'error', 'plan', 'job_type', 'cli_command', 'last_execution_started', 'created_by_user', 'last_execution_id', 'related_actions', 'url_logs', 'url'] | `None` |
|
|
1433
|
+
| `--select` | `list[str]` | Comma-separated fields to select and show in output. Available: ['id', 'created', 'updated', 'project_id', 'cluster_id', 'name', 'recipe_name', 'recipe_title', 'project_name', 'error', 'plan', 'job_type', 'cli_command', 'last_execution_started', 'created_by_user', 'created_by_deleted', 'last_execution_id', 'related_actions', 'url_logs', 'url'] | `None` |
|
|
1434
1434
|
| `--json` | `bool` | Output the result as JSON | `False` |
|
|
1435
1435
|
|
|
1436
1436
|
#### `ellf tasks logs`
|
|
@@ -1546,7 +1546,7 @@ Remove a user from the organization. By default the user is deactivated: they lo
|
|
|
1546
1546
|
| Argument | Type | Description | Default |
|
|
1547
1547
|
| --- | --- | --- | --- |
|
|
1548
1548
|
| `email_name_or_id` | `Union[str, UUID]` | Email, name, or ID of the user | |
|
|
1549
|
-
| `--permanent` | `bool` | Permanently delete the user's record instead of deactivating them.
|
|
1549
|
+
| `--permanent` | `bool` | Permanently delete the user's record instead of deactivating them. Their annotations, sessions and plans are kept but attributed to a deleted user. This can't be undone | `False` |
|
|
1550
1550
|
| `--json` | `bool` | Output the result as JSON | `False` |
|
|
1551
1551
|
|
|
1552
1552
|
#### `ellf users reactivate`
|
|
@@ -1576,7 +1576,7 @@ List pending invitations
|
|
|
1576
1576
|
|
|
1577
1577
|
| Argument | Type | Description | Default |
|
|
1578
1578
|
| --- | --- | --- | --- |
|
|
1579
|
-
| `--select` | `list[str]` | Comma-separated fields to select and show in output. Available: ['id', 'created', 'updated', 'org_id', 'email', 'avatar_color', 'avatar_url', 'claimed', 'vars', 'created_by_id', 'role'] | `['id', 'email', 'role', 'claimed']` |
|
|
1579
|
+
| `--select` | `list[str]` | Comma-separated fields to select and show in output. Available: ['id', 'created', 'updated', 'org_id', 'email', 'avatar_color', 'avatar_url', 'claimed', 'vars', 'created_by_id', 'created_by_deleted', 'role'] | `['id', 'email', 'role', 'claimed']` |
|
|
1580
1580
|
| `--all` | `bool` | Also include invitations that have already been claimed | `False` |
|
|
1581
1581
|
| `--json` | `bool` | Output the result as JSON | `False` |
|
|
1582
1582
|
|
|
@@ -1,11 +1,13 @@
|
|
|
1
1
|
from __future__ import annotations
|
|
2
2
|
|
|
3
|
+
import dataclasses
|
|
3
4
|
import json
|
|
4
5
|
import sys
|
|
5
6
|
from pathlib import Path
|
|
6
|
-
from typing import Any
|
|
7
|
+
from typing import Any, Callable, TypeVar
|
|
7
8
|
|
|
8
|
-
from radicli import Radicli, get_list_converter
|
|
9
|
+
from radicli import Arg, Command, Radicli, get_list_converter
|
|
10
|
+
from radicli.util import DEFAULT_PLACEHOLDER, ArgparseArg
|
|
9
11
|
from wasabi import msg
|
|
10
12
|
|
|
11
13
|
from ellf_broker_sdk.errors import BrokerError
|
|
@@ -18,6 +20,8 @@ from .messages import Messages
|
|
|
18
20
|
|
|
19
21
|
HELP = """Ellf Command Line Interface."""
|
|
20
22
|
|
|
23
|
+
_F = TypeVar("_F", bound=Callable[..., Any])
|
|
24
|
+
|
|
21
25
|
|
|
22
26
|
def _emit_error(title: str, detail: str | None = None) -> int:
|
|
23
27
|
if not isatty(sys.stderr):
|
|
@@ -95,7 +99,40 @@ ERRORS = {
|
|
|
95
99
|
}
|
|
96
100
|
|
|
97
101
|
|
|
98
|
-
|
|
102
|
+
class _Radicli(Radicli):
|
|
103
|
+
"""Radicli that says which options are required in their help.
|
|
104
|
+
|
|
105
|
+
radicli checks required options itself rather than through argparse,
|
|
106
|
+
which would trip over subcommands, so argparse's usage line shows every
|
|
107
|
+
option in brackets, as if optional. Marked when the command registers,
|
|
108
|
+
so the static ellf.json the CLI runs from carries it too.
|
|
109
|
+
"""
|
|
110
|
+
|
|
111
|
+
def _command(
|
|
112
|
+
self,
|
|
113
|
+
name: str,
|
|
114
|
+
args: dict[str, Arg],
|
|
115
|
+
registry: dict[str, Command],
|
|
116
|
+
**kwargs: Any,
|
|
117
|
+
) -> Callable[[_F], _F]:
|
|
118
|
+
register = super()._command(name, args, registry, **kwargs)
|
|
119
|
+
|
|
120
|
+
def cli_wrapper(cli_func: _F) -> _F:
|
|
121
|
+
register(cli_func)
|
|
122
|
+
command = registry[name]
|
|
123
|
+
command.args = [_mark_required(arg) for arg in command.args]
|
|
124
|
+
return cli_func
|
|
125
|
+
|
|
126
|
+
return cli_wrapper
|
|
127
|
+
|
|
128
|
+
|
|
129
|
+
def _mark_required(arg: ArgparseArg) -> ArgparseArg:
|
|
130
|
+
if not arg.arg.option or arg.default is not DEFAULT_PLACEHOLDER:
|
|
131
|
+
return arg
|
|
132
|
+
return dataclasses.replace(arg, help=f"{arg.help or ''} (required)".lstrip())
|
|
133
|
+
|
|
134
|
+
|
|
135
|
+
cli = _Radicli(
|
|
99
136
|
prog=about.__prog__,
|
|
100
137
|
help=HELP,
|
|
101
138
|
version=about.__version__,
|
|
@@ -1,9 +1,6 @@
|
|
|
1
1
|
from __future__ import annotations
|
|
2
2
|
|
|
3
|
-
import
|
|
4
|
-
import tempfile
|
|
5
|
-
from contextlib import contextmanager
|
|
6
|
-
from typing import Annotated, Iterator, Literal
|
|
3
|
+
from typing import Annotated, Literal
|
|
7
4
|
|
|
8
5
|
from pydantic import AliasChoices, BaseModel, Field, model_validator
|
|
9
6
|
|
|
@@ -249,14 +246,5 @@ class ClusterConfigOverrides(BaseModel):
|
|
|
249
246
|
node_pools: list[NodePool] | None = None
|
|
250
247
|
default_worker_type: str | None = None
|
|
251
248
|
|
|
252
|
-
@contextmanager
|
|
253
|
-
def write_to_temp(self) -> Iterator[str]:
|
|
254
|
-
"""Write override values to a temp JSON file, yielding its path."""
|
|
255
|
-
data = self.model_dump(by_alias=True, exclude_none=True)
|
|
256
|
-
with tempfile.NamedTemporaryFile(mode="w", suffix=".json", delete=True) as tmp:
|
|
257
|
-
tmp.write(json.dumps(data, indent=2))
|
|
258
|
-
tmp.flush()
|
|
259
|
-
yield tmp.name
|
|
260
|
-
|
|
261
249
|
|
|
262
250
|
_check_overrides_match(ClusterConfig, ClusterConfigOverrides)
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import base64
|
|
2
2
|
import builtins
|
|
3
3
|
import json
|
|
4
|
+
import ssl
|
|
4
5
|
import subprocess
|
|
5
6
|
import time
|
|
6
7
|
import uuid
|
|
@@ -49,6 +50,24 @@ DEFAULT_NAMESPACE = "ellf"
|
|
|
49
50
|
DEFAULT_RELEASE = "ellf"
|
|
50
51
|
|
|
51
52
|
|
|
53
|
+
def is_untrusted_certificate(exc: BaseException) -> bool:
|
|
54
|
+
"""Whether a connection failed because this device doesn't trust the
|
|
55
|
+
cluster's certificate, rather than because nothing answered.
|
|
56
|
+
|
|
57
|
+
That's how a cluster served with a certificate Ellf issues it (on
|
|
58
|
+
localhost or a private network) looks until 'ellf infra tls --trust'.
|
|
59
|
+
httpx reports it as a ConnectError, with the SSL error in its chain.
|
|
60
|
+
"""
|
|
61
|
+
seen: set[int] = set()
|
|
62
|
+
current: Optional[BaseException] = exc
|
|
63
|
+
while current is not None and id(current) not in seen:
|
|
64
|
+
if isinstance(current, ssl.SSLCertVerificationError):
|
|
65
|
+
return True
|
|
66
|
+
seen.add(id(current))
|
|
67
|
+
current = current.__cause__ or current.__context__
|
|
68
|
+
return "CERTIFICATE_VERIFY_FAILED" in str(exc)
|
|
69
|
+
|
|
70
|
+
|
|
52
71
|
def check_cluster_status(
|
|
53
72
|
cluster: ellf_pam_sdk_models.ClusterSummary,
|
|
54
73
|
) -> ClusterStatusCheck:
|
|
@@ -68,7 +87,9 @@ def check_cluster_status(
|
|
|
68
87
|
return ClusterStatusCheck.ISSUES
|
|
69
88
|
except InvalidURL:
|
|
70
89
|
return ClusterStatusCheck.INVALID_ADDRESS
|
|
71
|
-
except (ConnectTimeout, ConnectError):
|
|
90
|
+
except (ConnectTimeout, ConnectError) as e:
|
|
91
|
+
if is_untrusted_certificate(e):
|
|
92
|
+
return ClusterStatusCheck.UNTRUSTED_CERTIFICATE
|
|
72
93
|
# This could mean the cluster is down, but could also mean we have no network access
|
|
73
94
|
return ClusterStatusCheck.NOT_FOUND
|
|
74
95
|
except RequestError as e:
|
|
@@ -312,13 +333,12 @@ def _check_result(
|
|
|
312
333
|
label: str, ok: bool, detail: str = "", *, quiet: bool = False
|
|
313
334
|
) -> tuple[str, bool, str]:
|
|
314
335
|
"""Print a check result line (unless quiet) and return (label, ok, detail)."""
|
|
315
|
-
symbol = "\u2714" if ok else "\u2718"
|
|
316
336
|
suffix = f" ({detail})" if detail else ""
|
|
317
337
|
if not quiet:
|
|
318
338
|
if ok:
|
|
319
|
-
msg.good(f"{
|
|
339
|
+
msg.good(f"{label}{suffix}")
|
|
320
340
|
else:
|
|
321
|
-
msg.fail(f"{
|
|
341
|
+
msg.fail(f"{label}{suffix}")
|
|
322
342
|
return (label, ok, detail)
|
|
323
343
|
|
|
324
344
|
|
|
@@ -387,12 +407,17 @@ def check(
|
|
|
387
407
|
quiet=as_json,
|
|
388
408
|
)
|
|
389
409
|
)
|
|
390
|
-
except (httpx.ConnectError, httpx.ConnectTimeout):
|
|
410
|
+
except (httpx.ConnectError, httpx.ConnectTimeout) as exc:
|
|
391
411
|
results.append(
|
|
392
412
|
_check_result(
|
|
393
413
|
f"Cluster API responding on {broker_url}",
|
|
394
414
|
False,
|
|
395
|
-
detail=
|
|
415
|
+
detail=(
|
|
416
|
+
"this device doesn't trust the cluster's certificate: "
|
|
417
|
+
"run 'ellf infra tls --trust'"
|
|
418
|
+
if is_untrusted_certificate(exc)
|
|
419
|
+
else "connection failed"
|
|
420
|
+
),
|
|
396
421
|
quiet=as_json,
|
|
397
422
|
)
|
|
398
423
|
)
|
|
@@ -2,13 +2,14 @@
|
|
|
2
2
|
|
|
3
3
|
import base64
|
|
4
4
|
import json
|
|
5
|
+
import os
|
|
5
6
|
import subprocess
|
|
6
7
|
import tempfile
|
|
8
|
+
from contextlib import contextmanager
|
|
7
9
|
from pathlib import Path
|
|
8
|
-
from typing import TYPE_CHECKING,
|
|
10
|
+
from typing import TYPE_CHECKING, ContextManager, Iterator, Optional
|
|
9
11
|
from uuid import UUID
|
|
10
12
|
|
|
11
|
-
from pydantic import AliasChoices, BaseModel, Field, StringConstraints
|
|
12
13
|
from wasabi import msg
|
|
13
14
|
|
|
14
15
|
from ellf_pam_sdk import Client
|
|
@@ -17,32 +18,6 @@ from ellf_pam_sdk.models import ClusterCredentials, ClusterDetail
|
|
|
17
18
|
if TYPE_CHECKING:
|
|
18
19
|
from ...cluster_config import ClusterConfig
|
|
19
20
|
|
|
20
|
-
# Matches pam/ellf_pam/config.py's NonEmptyStr convention.
|
|
21
|
-
NonEmptyStr = Annotated[str, StringConstraints(min_length=1)]
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
class ClusterCredsFile(BaseModel):
|
|
25
|
-
"""Schema for cluster-creds.json, written by `infra register` and read by
|
|
26
|
-
`infra init-values`.
|
|
27
|
-
|
|
28
|
-
cluster_id/upstream_registry are required — nothing downstream can work
|
|
29
|
-
without them. The rest are legitimately blank on a fresh cluster PAM
|
|
30
|
-
hasn't released anything to yet (callers warn, not fail, on those).
|
|
31
|
-
"""
|
|
32
|
-
|
|
33
|
-
cluster_id: NonEmptyStr
|
|
34
|
-
# The upstream Ellf registry the cluster pulls release images from.
|
|
35
|
-
# Renamed from the ambiguous 'container_registry' (which read like the
|
|
36
|
-
# cluster's own registry); creds files written before the rename are
|
|
37
|
-
# still read via the alias.
|
|
38
|
-
upstream_registry: NonEmptyStr = Field(
|
|
39
|
-
validation_alias=AliasChoices("upstream_registry", "container_registry")
|
|
40
|
-
)
|
|
41
|
-
container_sa_key: str = ""
|
|
42
|
-
prodigy_license_key: str = ""
|
|
43
|
-
target_broker_version: str = ""
|
|
44
|
-
target_cpl_version: str = ""
|
|
45
|
-
|
|
46
21
|
|
|
47
22
|
# Tolerations for the system node-pool taint the terraform modules apply:
|
|
48
23
|
# ellf/role=system on GKE and EKS, and AKS's own CriticalAddonsOnly on AKS,
|
|
@@ -419,25 +394,57 @@ def ensure_cert_manager() -> None:
|
|
|
419
394
|
Path(tmp_path).unlink(missing_ok=True)
|
|
420
395
|
|
|
421
396
|
|
|
422
|
-
|
|
423
|
-
|
|
397
|
+
# A registry config helm reads no credentials from but still treats as
|
|
398
|
+
# configured. Given a config with no credentials at all, helm falls back to the
|
|
399
|
+
# OS credential store (the macOS keychain, Docker's credsStore), reading
|
|
400
|
+
# whatever is stored there for the host and saving any login into it.
|
|
401
|
+
_NO_CREDENTIALS_HOST = "ellf.invalid"
|
|
402
|
+
|
|
403
|
+
|
|
404
|
+
@contextmanager
|
|
405
|
+
def helm_registry_config(
|
|
406
|
+
registry: Optional[str] = None, sa_key_b64: Optional[str] = None
|
|
407
|
+
) -> Iterator[Path]:
|
|
408
|
+
"""A throwaway helm registry config, deleted on exit.
|
|
424
409
|
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
410
|
+
Pass it to helm with --registry-config. Without *registry* and
|
|
411
|
+
*sa_key_b64* it holds no credentials, for a chart anyone can pull, and
|
|
412
|
+
keeps helm away from the OS credential store. With them it holds the
|
|
413
|
+
registry key for the registry's host: the cluster's own credential, which
|
|
414
|
+
otherwise would stay in the installer's keychain, and replace whatever
|
|
415
|
+
they had stored for that host, long after the deploy.
|
|
428
416
|
"""
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
417
|
+
auths: dict[str, dict[str, str]] = {_NO_CREDENTIALS_HOST: {}}
|
|
418
|
+
if registry and sa_key_b64:
|
|
419
|
+
host = registry.split("/", 1)[0]
|
|
420
|
+
sa_key_json = base64.b64decode(sa_key_b64, validate=True).decode("utf-8")
|
|
421
|
+
token = base64.b64encode(f"_json_key:{sa_key_json}".encode()).decode()
|
|
422
|
+
auths[host] = {"auth": token}
|
|
423
|
+
with tempfile.TemporaryDirectory(prefix="ellf-helm-") as tmp:
|
|
424
|
+
path = Path(tmp) / "registry.json"
|
|
425
|
+
fd = os.open(path, os.O_WRONLY | os.O_CREAT | os.O_EXCL, 0o600)
|
|
426
|
+
with os.fdopen(fd, "w") as f:
|
|
427
|
+
json.dump({"auths": auths}, f)
|
|
428
|
+
yield path
|
|
429
|
+
|
|
430
|
+
|
|
431
|
+
def chart_source(
|
|
432
|
+
creds: ClusterCredentials, registry: Optional[str], sa_key_b64: Optional[str]
|
|
433
|
+
) -> "tuple[str, ContextManager[Path]]":
|
|
434
|
+
"""The chart to install and the helm registry config to pull it with.
|
|
435
|
+
|
|
436
|
+
PAM names a chart anyone can pull, which needs no credentials. A PAM
|
|
437
|
+
that predates the public chart repository names none: the chart is then
|
|
438
|
+
only in the upstream registry, behind the registry key.
|
|
439
|
+
"""
|
|
440
|
+
if creds.image.chart:
|
|
441
|
+
return creds.image.chart, helm_registry_config()
|
|
442
|
+
if not registry:
|
|
443
|
+
msg.fail(
|
|
444
|
+
"registries.upstream (or legacy image.registry) is empty in values.yaml"
|
|
445
|
+
)
|
|
439
446
|
raise SystemExit(1)
|
|
440
|
-
|
|
447
|
+
return f"oci://{registry}/ellf", helm_registry_config(registry, sa_key_b64)
|
|
441
448
|
|
|
442
449
|
|
|
443
450
|
def get_cluster_auth(
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import base64
|
|
2
2
|
import json
|
|
3
|
+
import secrets
|
|
3
4
|
import subprocess
|
|
4
|
-
import tempfile
|
|
5
5
|
import time
|
|
6
6
|
from pathlib import Path
|
|
7
7
|
from typing import Optional
|
|
@@ -28,7 +28,7 @@ from ...key_pair import generate_key_pair
|
|
|
28
28
|
from ...url import URL
|
|
29
29
|
from .._state import get_auth_state
|
|
30
30
|
from ._config_io import FILL_IN
|
|
31
|
-
from ._helpers import create_image_pull_secret, ensure_traefik
|
|
31
|
+
from ._helpers import chart_source, create_image_pull_secret, ensure_traefik
|
|
32
32
|
from .tls import MANAGED_TLS_SECRET_NAME
|
|
33
33
|
|
|
34
34
|
DEFAULT_NAMESPACE = "ellf"
|
|
@@ -102,12 +102,22 @@ def deploy(
|
|
|
102
102
|
)
|
|
103
103
|
overrides.image.broker_version = creds.image.broker_version
|
|
104
104
|
overrides.image.cpl_version = creds.image.cpl_version
|
|
105
|
-
# The registry key
|
|
106
|
-
#
|
|
107
|
-
#
|
|
108
|
-
#
|
|
105
|
+
# The registry key and licence key are PAM's too, and are handed to the
|
|
106
|
+
# cluster straight from PAM on every deploy rather than kept in
|
|
107
|
+
# values.yaml: each org has its own registry key, which PAM may replace
|
|
108
|
+
# (a leaked key is deleted and the org given a fresh one), and keeping
|
|
109
|
+
# them in a file leaves them on the installer's disk.
|
|
110
|
+
stored = _secrets_in_values(values_dict)
|
|
111
|
+
if stored:
|
|
112
|
+
msg.warn(
|
|
113
|
+
f"{values_file} holds secrets ({', '.join(stored)}). Deploy no "
|
|
114
|
+
"longer needs them there: the registry and licence keys come from "
|
|
115
|
+
"PAM and the database password from the cluster. Delete them "
|
|
116
|
+
"from the file."
|
|
117
|
+
)
|
|
109
118
|
if overrides.secrets is not None:
|
|
110
119
|
overrides.secrets.container_sa_key = creds.secrets.container_sa_key
|
|
120
|
+
overrides.secrets.prodigy_license_key = creds.secrets.prodigy_license_key
|
|
111
121
|
# 1. Create namespace
|
|
112
122
|
msg.info(f"Ensuring namespace {namespace}...")
|
|
113
123
|
subprocess.run(
|
|
@@ -159,17 +169,24 @@ def deploy(
|
|
|
159
169
|
infra_secret_name = (
|
|
160
170
|
overrides.secrets.infra_secret_name if overrides.secrets else None
|
|
161
171
|
) or ""
|
|
172
|
+
postgresql = overrides.postgresql
|
|
173
|
+
if postgresql and postgresql.enabled:
|
|
174
|
+
# The bundled database's password lives in the cluster, not in
|
|
175
|
+
# values.yaml: reuse the one it was installed with, or make one up
|
|
176
|
+
# on the first install. A new one on upgrade would lock the
|
|
177
|
+
# cluster out of its existing database.
|
|
178
|
+
if postgresql.auth is None:
|
|
179
|
+
postgresql.auth = HelmChartOverrides.Postgresql.Auth()
|
|
180
|
+
if not postgresql.auth.password:
|
|
181
|
+
postgresql.auth.password = _installed_database_password(
|
|
182
|
+
infra_secret_name, namespace
|
|
183
|
+
) or secrets.token_urlsafe(24)
|
|
184
|
+
database_password = postgresql.auth.password
|
|
185
|
+
else:
|
|
186
|
+
database_password = (
|
|
187
|
+
overrides.secrets.database_password if overrides.secrets else None
|
|
188
|
+
) or ""
|
|
162
189
|
if infra_secret_name:
|
|
163
|
-
# Resolve the database password from postgresql subchart or explicit config.
|
|
164
|
-
postgresql = overrides.postgresql
|
|
165
|
-
if postgresql and postgresql.enabled:
|
|
166
|
-
database_password = (
|
|
167
|
-
postgresql.auth.password if postgresql.auth else None
|
|
168
|
-
) or ""
|
|
169
|
-
else:
|
|
170
|
-
database_password = (
|
|
171
|
-
overrides.secrets.database_password if overrides.secrets else None
|
|
172
|
-
) or ""
|
|
173
190
|
public_key_pem = _ensure_cluster_keypair_standalone(
|
|
174
191
|
infra_secret_name, namespace, database_password=database_password
|
|
175
192
|
)
|
|
@@ -199,17 +216,16 @@ def deploy(
|
|
|
199
216
|
_require_tls_secret(operator_tls_secret, namespace)
|
|
200
217
|
# 7. Helm upgrade --install
|
|
201
218
|
if chart is None:
|
|
202
|
-
|
|
203
|
-
if not registry:
|
|
204
|
-
msg.fail(
|
|
205
|
-
"registries.upstream (or legacy image.registry) is empty in values.yaml"
|
|
206
|
-
)
|
|
207
|
-
raise SystemExit(1)
|
|
208
|
-
if container_sa_key:
|
|
209
|
-
helm_registry_login(registry, container_sa_key)
|
|
210
|
-
chart_ref = f"oci://{registry}/ellf"
|
|
219
|
+
chart_ref, registry_config = chart_source(creds, registry, container_sa_key)
|
|
211
220
|
msg.info(f"Installing from {chart_ref}...")
|
|
212
|
-
|
|
221
|
+
with registry_config as registry_config_path:
|
|
222
|
+
upgrade_from_ref(
|
|
223
|
+
chart_ref,
|
|
224
|
+
overrides,
|
|
225
|
+
namespace=namespace,
|
|
226
|
+
wait=wait,
|
|
227
|
+
registry_config=registry_config_path,
|
|
228
|
+
)
|
|
213
229
|
else:
|
|
214
230
|
msg.info(f"Installing from local chart {chart}...")
|
|
215
231
|
upgrade(chart, overrides, namespace=namespace, wait=wait)
|
|
@@ -439,32 +455,85 @@ def _ensure_cluster_keypair_standalone(
|
|
|
439
455
|
def _patch_secret_field(secret_name: str, namespace: str, key: str, value: str) -> None:
|
|
440
456
|
"""Ensure a single field exists in an existing K8s secret.
|
|
441
457
|
|
|
442
|
-
The
|
|
443
|
-
|
|
444
|
-
expose it.
|
|
458
|
+
The updated secret goes to kubectl on stdin, so the value appears neither
|
|
459
|
+
on the command line, where ``ps`` would expose it, nor in a file.
|
|
445
460
|
"""
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
461
|
+
current = subprocess.run(
|
|
462
|
+
[
|
|
463
|
+
"kubectl",
|
|
464
|
+
"get",
|
|
465
|
+
"secret",
|
|
466
|
+
secret_name,
|
|
467
|
+
f"--namespace={namespace}",
|
|
468
|
+
"-o",
|
|
469
|
+
"json",
|
|
470
|
+
],
|
|
471
|
+
check=True,
|
|
472
|
+
text=True,
|
|
473
|
+
capture_output=True,
|
|
474
|
+
)
|
|
475
|
+
secret = json.loads(current.stdout)
|
|
476
|
+
secret.setdefault("data", {})[key] = base64.b64encode(value.encode()).decode()
|
|
477
|
+
subprocess.run(
|
|
478
|
+
["kubectl", "replace", "-f", "-"],
|
|
479
|
+
input=json.dumps(secret),
|
|
480
|
+
check=True,
|
|
481
|
+
text=True,
|
|
482
|
+
capture_output=True,
|
|
483
|
+
)
|
|
484
|
+
|
|
485
|
+
|
|
486
|
+
def _read_secret_field(secret_name: str, namespace: str, key: str) -> str:
|
|
487
|
+
"""A field of a K8s secret, decoded; empty when the secret or field is missing."""
|
|
488
|
+
jsonpath_key = key.replace(".", r"\.")
|
|
489
|
+
result = subprocess.run(
|
|
490
|
+
[
|
|
491
|
+
"kubectl",
|
|
492
|
+
"get",
|
|
493
|
+
"secret",
|
|
494
|
+
secret_name,
|
|
495
|
+
f"--namespace={namespace}",
|
|
496
|
+
"-o",
|
|
497
|
+
f"jsonpath={{.data.{jsonpath_key}}}",
|
|
498
|
+
],
|
|
499
|
+
text=True,
|
|
500
|
+
capture_output=True,
|
|
501
|
+
)
|
|
502
|
+
if result.returncode != 0 or not result.stdout.strip():
|
|
503
|
+
return ""
|
|
504
|
+
return base64.b64decode(result.stdout.strip()).decode("utf-8")
|
|
505
|
+
|
|
506
|
+
|
|
507
|
+
def _installed_database_password(infra_secret_name: str, namespace: str) -> str:
|
|
508
|
+
"""The bundled database's password, from an earlier install of the cluster.
|
|
509
|
+
|
|
510
|
+
Deploy keeps it in the infra secret. Clusters installed before that have
|
|
511
|
+
it only in the database chart's own secret. Empty on a first install.
|
|
512
|
+
"""
|
|
513
|
+
if infra_secret_name:
|
|
514
|
+
password = _read_secret_field(
|
|
515
|
+
infra_secret_name, namespace, "ELLF_DATABASE_PASSWORD"
|
|
467
516
|
)
|
|
517
|
+
if password:
|
|
518
|
+
return password
|
|
519
|
+
return _read_secret_field(f"{DEFAULT_RELEASE}-postgresql", namespace, "password")
|
|
520
|
+
|
|
521
|
+
|
|
522
|
+
def _secrets_in_values(values: dict) -> list[str]:
|
|
523
|
+
"""Dotted keys of the secrets a values.yaml still holds.
|
|
524
|
+
|
|
525
|
+
'infra init-values' used to write them there; deploy now takes them from
|
|
526
|
+
PAM and the cluster instead.
|
|
527
|
+
"""
|
|
528
|
+
found = []
|
|
529
|
+
secrets_block = values.get("secrets") or {}
|
|
530
|
+
for key in ("containerSaKey", "prodigyLicenseKey"):
|
|
531
|
+
if secrets_block.get(key):
|
|
532
|
+
found.append(f"secrets.{key}")
|
|
533
|
+
postgresql_auth = (values.get("postgresql") or {}).get("auth") or {}
|
|
534
|
+
if postgresql_auth.get("password"):
|
|
535
|
+
found.append("postgresql.auth.password")
|
|
536
|
+
return found
|
|
468
537
|
|
|
469
538
|
|
|
470
539
|
WORKER_TYPES_CONFIGMAP = "ellf-worker-types"
|